Motif: Intercept close box event and prevent application exit? (C++) - c++

How do I intercept when the user clicks on a motif window's (widget's) close box, and how do I prevent the Motif window manager to close the entire calling application on the close box being clicked (so that my app can close the Motif application context and windows and continue to run)? I've tried to find out myself with Google, tuts and docs, but no dice. C++ required.

This seems to work (found on the inet):
#include <Xm/Protocols.h>
Boolean SetCloseCallBack (Widget shell, void (*callback) (Widget, XtPointer, XtPointer))
{
extern Atom XmInternAtom (Display *, char *, Boolean);
if (!shell)
return False;
Display* disp = XtDisplay (shell);
if (!disp)
return False;
// Retrieve Window Manager Protocol Property
Atom prop = XmInternAtom (disp, const_cast<char*>("WM_PROTOCOLS"), False);
if (!prop)
return False;
// Retrieve Window Manager Delete Window Property
Atom prot = XmInternAtom (disp, const_cast<char*>("WM_DELETE_WINDOW"), True);
if (!prot)
return False;
// Ensure that Shell has the Delete Window Property
// NB: Necessary since some Window managers are not
// Fully XWM Compilant (olwm for instance is not)
XmAddProtocols (shell, prop, &prot, 1);
// Now add our callback into the Protocol Callback List
XmAddProtocolCallback (shell, prop, prot, callback, NULL);
return True;
}
Setting such a callback will prevent the application being closed as a result of the close event being handle be the default event handlers.

Won't vendorShellWidgetClass do the trick for you? As in, closing just the motif app context and not the window..

IIRC, on X11, when you click a window's close box, the window manager sends a signal to your application, which tells it to exit. Whether you use motif or gtk or Qt is irrelevant since the close box belongs to the WM, not your application.
You need to catch the unix signal to prevent your application from closing.

Related

C++ Set window below (or above) the icons on the desktop

I'm trying to place a window either above or below the icons on the desktop. I mostly just want it to stay attached to the desktop at all times. Similar to Rainmeter or Wallpaper engine. So far, everything I tried either disables interaction, or gets minimized when you use the "Show Desktop" button. Any ideas on how to achieve this? I'm using electron and a native module in node to do this.
It's an old subject, but I'll find out how to do it recently and answer it.
The method is to find the handle of SHELLDLL_DefView, the parent of the desktop window, and then make the SHELLDLL_DefView handle the parent of my window to fix it to the desktop.
The method is to find the handle of SHELLDLL_DefView, the owner of the desktop window, and then make the SHELLDLL_DefView handle the owner of my window to fix it to the desktop.
SHELLDLL_DefView is located under the Progma or WorkerW handle. This is a code to prevent ShowDesktop from being used in the Electget package created by ffi-napi to attach the Electron browserWindow to the desktop.
const GWLP_HWNDPARENT = -8;
// find SHELLDLL_DefView in Progma
const progman = user32.FindWindowExA(ref.NULL, ref.NULL, 'Progman', ref.NULL);
let defView = user32.FindWindowExA(progman, ref.NULL, 'SHELLDLL_DefView', ref.NULL );
// find SHELLDLL_DefView in WorkerW
if (!defView) {
const desktopHWnd = user32.GetDesktopWindow();
let workerW = 0;
do {
workerW = user32.FindWindowExA(desktopHWnd, workerW, 'WorkerW', ref.NULL);
defView = user32.FindWindowExA(workerW, ref.NULL, 'SHELLDLL_DefView', ref.NULL );
} while (!defView && workerW);
}
if (!defView) return false;
// make the SHELLDLL_DefView handle the parent of my window
user32.SetWindowLongPtrA(hWnd, GWLP_HWNDPARENT, defView);
This allows you to create a window where you can click and interact without being hidden by ShowDesktop.
2022-03-29
There was a wrong word, so I corrected it. According to doc, it is not a parent window, but an owner window. In the doc, it is strange that the GWLP_HWNDPARENT constant is related to the parent window. However, when tested with Spy++, the corresponding constant changes the owner window.

How to detect if any app goes fullscreen or exiting fullscreen?

I need to disable notifications at my app when there is another app fullscreen like powerpoint or VLC to do not bother a user.
For now, I get to this code, but it always returns true. Whether there is some app fullscreen or not. I am not sure whether this should work only for app bars or also taskbar.
HWND hWnd;
hWnd = FindWindow(L"Shell_TrayWnd", nullptr);
if( hWnd )
{
APPBARDATA apd;
apd.cbSize = sizeof(APPBARDATA);
apd.hWnd = hWnd;
bool uState = bool(SHAppBarMessage(ABN_FULLSCREENAPP , &apd));
if(uState)
qDebug()<<"fullscreen";
else
qDebug()<<"not fullscreen";
}
ABN_FULLSCREENAPP is not a message you send to the shell, it's a message the shell sends to you when an application enters or leaves full screen mode. The message is sent to the owner of an app bar created with ABM_NEW.
Documentation is here.
My reading of the documentation is that you have to create an app bar to receive this message but you may be able to set it to zero height or width with ABM_SETPOS if you want to hide it.

execute program while window handled mfc

I want to make program work while handle is available. I thinking about setting timer on initialization... and insert all source of program in to it, but if I got lost handle when program processing in while loop it will not let me close dialog box immediately.
BOOL WaitProcessDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
SetTimer(IDC_PROGRESS1, 0, NULL);
return TRUE;
}
void WaitProcessDlg::OnTimer(UINT nIDEvent)
{
if(::FindWindow(NULL, str)){
tempHWND = ::FindWindow(NULL, str);
while(tempHWND){
----------------------->>>>>>>>>>>> A LOT OF CODING <<<<<<<<<<<<<<<<----------------------
}
EndDialog( 0 );
KillTimer(IDC_PROGRESS1);
}
CDialog::OnTimer(nIDEvent);
}
So my question is what the best way to close dialog when handle of external application may lost, maybe insert thread or there are any other way to do that?
It sounds like you need a windows hook.
http://msdn.microsoft.com/en-us/library/windows/desktop/ms644959(v=vs.85).aspx#whgetmessagehook
with WH_GETMESSAGE you get to see the windows events being processed by the other application's window, you could then wait for the WM_CLOSE to show up, and kill your dialog.

Passing Data to Windows Console Control Handler

I am working on writing a simple game engine and I am having trouble handling Windows console events; specifically, I cannot figure out how to pass custom data to the callback handler.
I first call this code to specify my callback function:
SetConsoleCtrlHandler((PHANDLER_ROUTINE)WindowsSystemManager::ConsoleControlHandler, true);
My static-member callback function is defined as:
bool WINAPI WindowsSystemManager::ConsoleControlHandler(DWORD controlType){
if(controlType == CTRL_CLOSE_EVENT){
MessageBox(NULL, L"Close Event Captured", L"Close Event Captured", NULL);
}
return true;
}
Everything works fine - when I click on the close button in the console, this MessageBox pops up. Only problem is, I need to call code that flushes a logging buffer to a log file on this type of shutdown (as well as other clean-up), and the Logger instance is a member in my WindowsSystemManager.
I have dealt with a similar problem of passing custom data to window handles by using SetWindowLongPtr and GetWindowLongPtr successfully, but I can't find any information on how to do this type of thing with console control handlers. Any thoughts?
EDIT: I got this functionality working based on MSalters' suggestions. The final code for the console control handler is here:
bool WINAPI WindowsSystemManager::ConsoleControlHandler(DWORD controlType){
BerserkEngine* engine = (BerserkEngine*)GetWindowLongPtr(GetConsoleWindow(), GWLP_USERDATA);
if(controlType == CTRL_CLOSE_EVENT){
engine->~BerserkEngine();
PostQuitMessage(0);
}
return true;
}
Where I set this custom data pointer in the WindowsSystemManager constructor:
SetWindowLongPtr(GetConsoleWindow(), GWL_USERDATA, (LONG_PTR)this->engine);
I'm not sure why you'd need this. You can have multiple windows, but only one console.
However, GetConsoleWindow will give you the console HWND, on which you might call SetWindowLongPtr. Not very clean (you're not supposed to do this on windows that you don't manage), but it might just work.

CDockingManager GetPaneList() causes assertion failure in wincore.cpp?

So I thought this would be pretty simple, but I forgot it's MFC. Instead of registering a notification listener for data model changes that would possibly require a GUI update on each individual control I figure why not register it once and then send a message to all the open dock panes and allow them to update their controls as needed on their own terms for efficiency.
My callback function for handling the notification from the server looks something like this:
void CMainFrame::ChangeCallback(uint32_t nNewVersion, const std::vector<uint32_t>& anChangedObjectTypes)
{
CObList panes;
GetDockingManager()->GetPaneList(panes); // assert failure
if (!panes.IsEmpty())
{
POSITION pos = panes.GetHeadPosition();
while (pos)
{
CDockablePane* pPane = dynamic_cast<CDockablePane*>(panes.GetNext(pos));
if (pPane)
pPane->PostMessage(DM_REFRESH, nNewVersion);
}
}
}
The error I am getting is an assertion failure on line 926 of wincore.cpp
CHandleMap* pMap = afxMapHWND();
ASSERT(pMap != NULL); // right here
There is a comment below this saying this can happen if you pass controls across threads however this is a single threaded MFC application and this is all being done from the main frame.
Does anyone know what else can cause this?
If there is another way to go about sending a message to all the open CDockablePane derived windows in MFC that works as well ...
Here's the obvious workaround that I didn't want to have to do but after hours of debugging and no response here I guess this is a viable answer:
I added std::vector<CDockPane*> m_dockList; to the members of CMainFrame
Now after each call to AddPane in various places that can create and open new dock panes I make a subsequent call to push_back and then I override CDockablePane::OnClose like so:
CMainFrame* pMainFrame = reinterpret_cast<CMainFrame*>(AfxGetMainWnd());
if (pMainFrame)
{
std::vector<CDockPane*>::const_iterator found(
std::find(pMainFrame->DockList()->begin(), pMainFrame->DockList()->end(), this));
if (found != pMainFrame->DockList()->end())
pMainFrame->DockList()->erase(found);
}
CDockablePane::OnClose();
Now this list will only contain pointers to open dock panes which allows me to handle the event notification in my callback and simply do a for loop and PostMessage to each.