Parent document is top of "Motif FAQ (Part 3 of 9)"
Previous document is "75) How can my application know when the user has quit Mwm?"
76) How can I tell if the user has selected "Close" from the system
menu? How do I catch the "Close"? I need to do some clean up before exiting.
[Last modified: Aug 95]
Answer: Catching the mwm Close involves using XmAddWMProtocolCallback and
possibly setting the XmNdeleteResponse resource. Note that whether your
application involves multiple applicationShells vs. a single applicationShell
and multiple toplevelShells is significant. Following the two older code
fragments is a complete test application which can be compiled with different
#defines to alter the behavior. This works with R4 Intrinsics
#include <Xm/Protocols.h>
void FinalCleanupCB(w, client_data, call_data)
Widget w;
caddr_t client_data, call_data;
{
/* tidy up stuff here */
...
/* exit if you want to */
exit (0);
}
main()
{
Atom wm_delete_window;
...
XtRealizeWidget(toplevel);
...
wm_delete_window =
XmInternAtom(XtDisplay(toplevel),
"WM_DELETE_WINDOW", False);
XmAddWMProtocolCallback(toplevel, wm_delete_window,
FinalCleanupCB, NULL);
XtMainLoop();
}
This will still kill the application. To turn this behaviour off so that the
application is not killed, set the shell resource XmNdeleteResponse to
XmDO_NOTHING. This means that users cannot kill your application via the
system menu, and may be a bad thing.
If you are running R3, Bob Hays (bobhays@spss.com) has suggested this:
"Trapping on the delete window atom does not work as I cannot force my action
routine to the top of the action list for the activity desired, so the window
manager kills my window anyway BEFORE I can do anything about it. And, to
make matters worse, the window manager (Motif in this case) tacks its atoms
and handlers onto the window at some unknown point down the line after the
creation of the shell widget as far as I can tell. So....
I have a procedure as an action routine for ClientMessage. Then, if I get a
property change event on the window manager protocols, I then tack on
WM_SAVE_YOURSELF. If I get this request, I clean up (it seems to happen on
WM_DELETE_WINDOW, BTW, if you remove WM_DELETE_WINDOW from the WM protocols
atom) and exit. Works great and is less filling overall:-)."
The following similar code fragment is from Dave Mink
(mink@cadcam.pms.ford.com):
void setupCloseCallback(Widget shell, XtCallbackProc closeProc)
{
/* get window manager delete protocol atom */
Atom deletewin_protocol = XmInternAtom(
XtDisplay(shell), "WM_DELETE_WINDOW", True
);
/* turn off default delete response */
XtVaSetValues( shell,
XmNdeleteResponse, XmDO_NOTHING,
NULL);
/* add callback for window manager delete protocol */
XmAddWMProtocolCallback(shell, deletewin_protocol, closeProc, NULL);
}
Here is a complete code example which can be compiled several different ways,
as per the comments in the code.
/*
* MWM Close test program.
*
* Creates 4 shells, testing each of 3 different values of XmNdeleteResponse.
* Compile will -DMULTIPLE_APP_SHELLS to make all 4 shells of type
* applicationShellWidgetClass. Otherwise, first shell created is
* applicationShellWidgetClass, but other 3 are topLevelShellWidgetClass.
* Results differ. You can also experiment with #defining POPUP_SHELL,
* BEFORE_CREATE, or AFTER_CREATE.
*
* Ken Sall (ksall@cen.com), Motif FAQ maintainer, Century Computing, Inc.
*/
#include <stdio.h>
#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <Xm/Xm.h>
#include <Xm/XmP.h>
#include <Xm/RowColumn.h> /* for popup */
#include <Xm/Label.h>
#include <X11/Protocols.h>
#include <X11/AtomMgr.h>
#include <X11/MwmUtil.h>
void CloseCB();
void popup_handler();
#ifdef MULTIPLE_APP_SHELLS
#define P1_TITLE "P1: applicationShell: XmDO_NOTHING"
#define P2_TITLE "P2: applicationShell: XmDESTROY"
#define P3_TITLE "P3: applicationShell: XmUNMAP"
#define P4_TITLE "P4: applicationShell: default"
#else
#define P1_TITLE "P1: applicationShell: XmDO_NOTHING"
#define P2_TITLE "P2: topLevelShell: XmDESTROY"
#define P3_TITLE "P3: topLevelShell: XmUNMAP"
#define P4_TITLE "P4: topLevelShell: XmDO_NOTHING"
#endif
void CloseCB (w, client_data, call_data)
Widget w; /* widget id */
caddr_t client_data; /* data from application */
caddr_t call_data; /* data from widget class */
{
XmAnyCallbackStruct *cb = (XmAnyCallbackStruct *) call_data;
printf ("caught Close from: %s\n", (char *)client_data );
if (strcmp ( P1_TITLE, (char *)client_data ) == 0 )
{
/* do something */
}
else if (strcmp ( P2_TITLE, (char *)client_data ) == 0 )
{
/* do something else */
}
else if (strcmp ( P3_TITLE, (char *)client_data ) == 0 )
{
/* do something else */
}
else if (strcmp ( P4_TITLE, (char *)client_data ) == 0 )
{
/* do something else */
}
else /* unreachable */
{
printf ("oops\n");
}
}
void popup_handler()
{
printf ("popup handler\n");
}
int main (argc,argv, envp)
int argc;
char **argv;
char **envp;
{
XtAppContext app_context;
Display *theDisplay;
Widget shell1, shell2, shell3, shell4;
Widget label, DrawWindow, WindowPopupMenu;
Arg al[10];
int ac;
Atom delwinAtom1, delwinAtom2, delwinAtom3, delwinAtom4;
XmString xms;
#ifdef MULTIPLE_APP_SHELLS
printf ("This version will demonstrate a problem if you Close P2.\n");
printf ("Since there are multiple appshells, closing (destroying) P2 cause the app to exit.\n");
#else
#ifdef POPUP_SHELL
printf ("This version uses XtCreatePopupShell rather than XtAppCreateShell \n");
#else
printf ("Compile with '-DMULTIPLE_APP_SHELLS' to demonstrate a problem.\n");
#endif
#endif
#ifdef BEFORE_CREATE
printf ("This version adds the XmNdeleteResponse _before_ the shell is created.\n");
#else
printf ("This version adds the XmNdeleteResponse _after the shell is created.\n");
#endif
XtToolkitInitialize ();
app_context = XtCreateApplicationContext ();
theDisplay = XtOpenDisplay ( app_context, NULL,
"my_program", "ProgramClass",
NULL, 0, &argc, argv);
/* --------------------- BEGIN P1 -------------------- */
ac = 0;
XtSetArg(al[ac], XmNx, 0); ac++;
XtSetArg(al[ac], XmNy, 0); ac++;
XtSetArg(al[ac], XmNwidth, 350); ac++;
XtSetArg(al[ac], XmNheight, 200); ac++;
XtSetArg (al[ac], XmNtitle, P1_TITLE); ac++;
#ifdef BEFORE_CREATE
XtSetArg (al[ac], XmNdeleteResponse, XmDO_NOTHING); ac++;
#endif
/* The ONLY applicationShell unless MULTIPLE_APP_SHELLS is defined. */
shell1 = XtAppCreateShell ("shell1", "ProgramClass",
applicationShellWidgetClass, theDisplay, al, ac);
/* Tell mwm to exec CloseCB when close is detected. */
delwinAtom1 = XmInternAtom (XtDisplay(shell1),
"WM_DELETE_WINDOW", False);
XmAddWMProtocolCallback (shell1, delwinAtom1, CloseCB, P1_TITLE);
#ifndef BEFORE_CREATE
XtVaSetValues( shell1, XmNdeleteResponse, XmDO_NOTHING, NULL);
#endif
/* --------------------- BEGIN P2 -------------------- */
ac = 0;
XtSetArg(al[ac], XmNx, 375); ac++;
XtSetArg(al[ac], XmNy, 0); ac++;
XtSetArg(al[ac], XmNwidth, 350); ac++;
XtSetArg(al[ac], XmNheight, 200); ac++;
XtSetArg (al[ac], XmNtitle, P2_TITLE); ac++;
#ifdef BEFORE_CREATE
XtSetArg (al[ac], XmNdeleteResponse, XmDESTROY); ac++;
#endif
#ifdef MULTIPLE_APP_SHELLS
shell2 = XtAppCreateShell ("shell2", "ProgramClass",
applicationShellWidgetClass, theDisplay, al, ac);
#else
#ifdef POPUP_SHELL
/*
* NOTE use of XtCreatePopupShell (not XtCreateMAnagedWidget) and
* topLevelShellWidgetClass (not applicationShellWidgetClass).
* Parent of topLevelShell is applicationShell.
* Use XtPopup rather than XtRealize for topLevelShell.
*/
shell2 = XtCreatePopupShell ("shell2",
topLevelShellWidgetClass, shell1, al, ac);
#else
shell2 = XtAppCreateShell ("shell2", "ProgramClass",
topLevelShellWidgetClass, theDisplay, al, ac);
#endif
#endif
/* Tell mwm to exec CloseCB when close is detected. */
delwinAtom2 = XmInternAtom (XtDisplay(shell2),
"WM_DELETE_WINDOW", False);
XmAddWMProtocolCallback (shell2, delwinAtom2, CloseCB, P2_TITLE);
#ifndef BEFORE_CREATE
XtVaSetValues( shell2, XmNdeleteResponse, XmDESTROY, NULL);
#endif
/* --------------------- BEGIN P3 -------------------- */
ac = 0;
XtSetArg(al[ac], XmNx, 750); ac++;
XtSetArg(al[ac], XmNy, 0); ac++;
XtSetArg(al[ac], XmNwidth, 350); ac++;
XtSetArg(al[ac], XmNheight, 200); ac++;
XtSetArg (al[ac], XmNtitle, P3_TITLE); ac++;
#ifdef BEFORE_CREATE
XtSetArg (al[ac], XmNdeleteResponse, XmUNMAP); ac++;
#endif
#ifdef MULTIPLE_APP_SHELLS
shell3 = XtAppCreateShell ("shell3", "ProgramClass",
applicationShellWidgetClass, theDisplay, al, ac);
#else
#ifdef POPUP_SHELL
/* See comments for shell2 */
shell3 = XtCreatePopupShell ("shell3",
topLevelShellWidgetClass, shell1, al, ac);
#else
shell3 = XtAppCreateShell ("shell3", "ProgramClass",
topLevelShellWidgetClass, theDisplay, al, ac);
#endif
#endif
/* Tell mwm to exec CloseCB when close is detected. */
delwinAtom3 = XmInternAtom (XtDisplay(shell3),
"WM_DELETE_WINDOW", False);
XmAddWMProtocolCallback (shell3, delwinAtom3, CloseCB, P3_TITLE);
#ifndef BEFORE_CREATE
XtVaSetValues( shell3, XmNdeleteResponse, XmUNMAP, NULL);
#endif
/* --------------------- BEGIN P4 -------------------- */
ac = 0;
XtSetArg(al[ac], XmNx, 0); ac++;
XtSetArg(al[ac], XmNy, 250); ac++;
XtSetArg(al[ac], XmNwidth, 350); ac++;
XtSetArg(al[ac], XmNheight, 200); ac++;
XtSetArg (al[ac], XmNtitle, P4_TITLE); ac++;
#ifdef BEFORE_CREATE
XtSetArg (al[ac], XmNdeleteResponse, XmDO_NOTHING); ac++;
#endif
#ifdef MULTIPLE_APP_SHELLS
shell4 = XtAppCreateShell ("shell4", "ProgramClass",
applicationShellWidgetClass, theDisplay, al, ac);
#else
#ifdef POPUP_SHELL
/* See comments for shell2 */
shell4 = XtCreatePopupShell ("shell4",
topLevelShellWidgetClass, shell1, al, ac);
#else
shell4 = XtAppCreateShell ("shell4", "ProgramClass",
topLevelShellWidgetClass, theDisplay, al, ac);
#endif
#endif
/* Tell mwm to exec CloseCB when close is detected. */
delwinAtom4 = XmInternAtom (XtDisplay(shell4),
"WM_DELETE_WINDOW", False);
XmAddWMProtocolCallback (shell4, delwinAtom4, CloseCB, P4_TITLE);
#ifndef BEFORE_CREATE
XtVaSetValues( shell4, XmNdeleteResponse, XmDO_NOTHING, NULL);
#endif
/* just for fun */
ac = 0;
WindowPopupMenu = XmCreatePopupMenu(shell1, "PopupMenu", al, ac);
XtAddEventHandler( shell1, ButtonPressMask, FALSE, popup_handler,
WindowPopupMenu);
ac = 0;
xms = (XmString) XmStringCreateLocalized ( "Button3 = popup; Button2 = DnD.");
XtSetArg(al[ac], XmNlabelString, xms); ac++;
XtSetArg(al[ac], XmNshadowThickness, 2); ac++;
label = XmCreateLabel (shell1, "label", al, ac);
XtManageChild ( label );
XtRealizeWidget( shell1 );
/* NOTE use of XtPopup rather than XtRealizeWidget for topLevels */
#ifdef MULTIPLE_APP_SHELLS
XtRealizeWidget( shell2 );
XtRealizeWidget( shell3 );
XtRealizeWidget( shell4 );
#else
#ifdef POPUP_SHELL
XtPopup ( shell2, XtGrabNone );
XtPopup ( shell3, XtGrabNone );
XtPopup ( shell4, XtGrabNone );
#else
XtRealizeWidget( shell2 );
XtRealizeWidget( shell3 );
XtRealizeWidget( shell4 );
#endif
#endif
XtAppMainLoop (app_context);
}
-----------------------------------------------------------------------------
END OF PART THREE
Parent document is top of "Motif FAQ (Part 3 of 9)"
Previous document is "75) How can my application know when the user has quit Mwm?"