Problem description:
Using the supplied program, an application which rapidly pops up and pops
down a window, the window gets lost, such that after a popdown the
subsequent popup is lost.
Details:
In FVWM there was explicit code which checked during unmap if there were
subsequent events in the event queue that would cause the window to be
mapped. If true, this code would pre-empt the event queue by pulling
the event out of the queue and map the window that was being un-mapped.
This code did not work.
Additionally, there was code in the mapping path which would check if
there were subsequent events in the event queue that would cause the
window to be unmapped. If true, this code would pre-empt te event queue
by ignoring the map request.
As a result of the above coding scheme, the event stream would consist
of the following events in the event queue:
(0) unmap window X1
(1) unmap window X1
(2) map window X1
The first event (0) would cause the unmap handler to begin unmap
processing. The peek of the event queue would cause event (2) to be
removed from the queue and the map processing to begin.
During map processing event (1) would cause the event to be ignored, thus
pre-empting the request for a successful unmap and re-map action, and
leaving the window in the unmap state.
After removing the code which 'peeked' ahead and removed future events
in the queue, investigation showed that the real problem was that events
may come in for a client frame base window that no longer exists
(the client window was just unmanaged but the the client did some action
before the un-reparenting was actually done). The code confirms that this
is the case and then handles the request as if it came in as a map window
event.
Other window managers handle this case as coded in the patch.
This problem has a long history. The test case supplied for previous
version of this problem only did one sequence of pop-up, pop-down, pop-up.
The supplied test case does a continuous pop-up, pop-down, repeat ...
AFAIK, fvwm is the only window manager that fails this test.
Correction:
Removed code which erronously removed events from the queue.
Added code to process a map window request for a parented window that
no longer exists as if the parent was really the root window.
Test procedure:
build and run the program 'rapidWin'.
Press the 'Start' button on the application window.
A sub-window of the main application may appear and flicker rapidly.
In the text window, messsages will appear showing the window being mapped
and un-mapped.
If the window does not appear or dis-appear as programmed by the program,
the application will stop displaying messages.
If the program does not stop, because the window is successfully mapped
and un-mapped, then problem is shown as fixed.
************ begin: rapidWin.cxx ***********
/*
* compile with
* g++ -g -o rapidWin rapidWin.o -L/usr/X11R6/lib64 -lXm -lXt -lX11
*/
#include <X11/Intrinsic.h>
#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/PushB.h>
#include <Xm/Frame.h>
#include <Xm/DialogS.h>
#include <Xm/Label.h>
#include <Xm/Text.h>
#include <Xm/ToggleB.h>
#include <unistd.h>
#include <stdlib.h>
#include <iostream>
#include <X11/IntrinsicP.h>
#include <X11/ShellP.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
using namespace std;
XtAppContext app_context;
int Log2Of(int i) {
int j, k;
j = i;
k = 0;
while (j > 1) {
j = j >> 1;
k++;
}
return k;
};
#define RES_CONVERT( res_name, res_value) \
XtVaTypedArg, (res_name), XmRString, (res_value), strlen(res_value) + 1
Widget topLevel;
Widget wizardShell,label,contentForm;
unsigned int seed;
int numberOfLaunches;
int numberOfFastPopups;
bool delayOn;
bool watching;
bool wizardCreated;
int doSafe;
int safeSleepTimens;
FILE *records;
void mapChecker(XtPointer cd, XtIntervalId *id);
void lower(XtPointer cd, XtIntervalId *id);
void lowerRaise_cb(Widget w, void *cbd);
void safePopdownS(Widget w);
void safePopupS(Widget w);
void createWizard();
bool setWidgetPopped(Widget w,int popped)
{
if (! XtIsShell(w))
{
cout << "NOT A SHELL WIDGET!" << endl;
}
else
{
ShellWidget sw = (ShellWidget) w;
sw->shell.popped_up = popped;
}
}
bool isWidgetPopped(Widget w)
{
bool isPopped;
isPopped = false;
if (! XtIsShell(w))
{
cout << "NOT A SHELL WIDGET!" << endl;
}
else
{
ShellWidget sw = (ShellWidget) w;
if (sw->shell.popped_up)
{
isPopped = true;
}
}
return isPopped;
}
void mapWatcher(XtPointer cd, XtIntervalId *id)
{
XWindowAttributes xwa;
Display *dpy;
Window win;
long int upperRange = 199999999;
int val = rand_r(&seed);
int log = rand_r(&seed);
int fullLog = Log2Of(upperRange);
log = (((float) fullLog) * log)/RAND_MAX;
long nanoval = (((float) upperRange)*val)/RAND_MAX;
nanoval = nanoval >> (fullLog-log);
safeSleepTimens = nanoval;
XtAppAddTimeOut(app_context,500,mapChecker,NULL);
cout << "LOWERRAISE START NS:" << safeSleepTimens << endl;
lowerRaise_cb(wizardShell,NULL);
cout << "LOWERRAISE END" << endl;
}
void mapChecker(XtPointer cd, XtIntervalId *id)
{
Display *dpy;
Window win;
int status, cnt = 5;
XWindowAttributes xwa;
dpy = XtDisplay(wizardShell);
win = XtWindow(wizardShell);
status = XGetWindowAttributes(dpy,win,&xwa);
// fprintf(records,"%7d %12d\n",xwa.map_state,safeSleepTimens);
while ((xwa.map_state == 0) && (cnt-- > 0))
{
struct timeval tv;
struct timespec request;
struct timespec remaining;
gettimeofday(&tv, NULL);
// cout << "UNMAP OCCURRED! **************************" << endl;
printf("%d.%06d UNMAP OCCURRED! *****\n", (int) tv.tv_sec, (int)
tv.tv_usec);
request.tv_sec = 0;
request.tv_nsec = 1 * 1000 * 1000;
// nanosleep(&request,&remaining);
break;
}
safeSleepTimens = 500000000;
safePopdownS(wizardShell);
safePopupS(wizardShell);
if (watching)
{
XtAppAddTimeOut(app_context,500,mapWatcher,NULL);
}
}
void safePopupS(Widget w)
{
struct timeval tv;
gettimeofday(&tv, NULL);
XtPopup(w,XtGrabNone);
XFlush(XtDisplay(wizardShell));
printf("%d.%06d Popup\n", (int) tv.tv_sec, (int) tv.tv_usec);
}
void safePopdownS(Widget w)
{
struct timespec request;
struct timespec remaining;
struct timeval tv;
gettimeofday(&tv, NULL);
request.tv_sec = 0;
request.tv_nsec = safeSleepTimens;
XtPopdown(w);
XFlush(XtDisplay(wizardShell));
printf("%d.%06d Popdown\n", (int) tv.tv_sec, (int) tv.tv_usec);
if (safeSleepTimens > 0)
{
nanosleep(&request,&remaining);
}
}
void stop_cb(Widget w, XtPointer cd, XEvent *event, Boolean *cont_disp)
{
watching = false;
}
void start_cb(Widget w, XtPointer cd, XEvent *event, Boolean *cont_disp)
{
if (!wizardCreated)
{
createWizard();
wizardCreated = true;
}
watching = true;
safePopupS(wizardShell);
// mapWatcher(NULL,NULL);
}
void
notify(Widget w, void *, XEvent *event)
{
printf("notify: <%x> event %d\n", XtWindow(w), event->type);
switch (event->type) {
case 19: // Mapnotify
XtAppAddTimeOut(app_context,500,lower,NULL);
break;
case 18: // UnmapNotify
safePopupS(wizardShell);
break;
}
}
void createWizard()
{
Arg arg[10];
int n = 0;
XtSetArg(arg[n],XmNtransientFor,topLevel);n++;
XtSetArg(arg[n],XmNallowShellResize,True);n++;
XtSetArg(arg[n],XmNdefaultPosition,False);n++;
XtSetArg(arg[n],XmNtitle,"FASTPOPSHELL");n++;
XtSetArg(arg[n],XmNminWidth,100);n++;
XtSetArg(arg[n],XmNminHeight,100);n++;
wizardShell=XtCreatePopupShell("FastPopShell",xmDialogShellWidgetClass,topLevel,arg,n);
n = 0;
XtSetArg(arg[n],XmNnoResize,True);n++;
contentForm=XmCreateForm(wizardShell,"form",arg,n);
n = 0;
XtSetArg(arg[n],XmNx,50);n++;
XtSetArg(arg[n],XmNy,50);n++;
label=XmCreateLabel(contentForm,"label",arg,n);
XtManageChild(contentForm);
EventMask em;
em = StructureNotifyMask;
Window shellWindow = XtWindow(wizardShell);
long shellWindowID = (long) shellWindow;
printf(" Window id of wizard: %0lx\n",shellWindowID);
printf(" Window id of wizard decimal: %0ld\n",shellWindowID);
XtAddEventHandler(wizardShell, StructureNotifyMask, False, (XtEventHandler)
notify, (XtPointer) 0);
}
void numberInWizard(int v)
{
Arg arg[10];
char buff[100];
sprintf(buff,"%d",v);
XmString xmString = XmStringCreateLocalized(buff);
int n = 0;
XtSetArg(arg[n],XmNlabelString,xmString);n++;
XtSetValues(label,arg,n);
XtManageChild(label);
}
void raise()
{
// XtPopup(wizardShell,XtGrabNone);
safePopupS(wizardShell);
}
void lower(XtPointer cd, XtIntervalId *id)
{
// XtPopdown(wizardShell);
safePopdownS(wizardShell);
}
// Create Main Shell
// Create a workproc
// create the drawer with the main shell
Widget
form,frame,startWidget,stopWidget,rapidRaise,simpleLower,delayOnToggle,simpleLowerThenRaise;
// start a timer
// start mainloop
void delayChanged_cb(Widget w, void *client, void *cbd)
{
XmToggleButtonCallbackStruct *toggleStruct = (XmToggleButtonCallbackStruct
*) cbd;
if (toggleStruct->set == True)
{
delayOn = true;
}
else
{
delayOn = false;
std::cout << "DELAY OFF " << std::endl;
}
}
void lower_cb(Widget w, void *cbd)
{
lower(NULL, NULL);
}
void raise_cb(Widget w, void *cbd)
{
if (!wizardCreated)
{
createWizard();
wizardCreated = true;
}
numberOfLaunches++;
numberInWizard(numberOfLaunches);
raise();
}
void lowerRaise_cb(Widget w, void *cbd)
{
// raise Shell;
lower_cb(w,cbd);
raise_cb(w,cbd);
}
int main(int argc, char *argv[])
{
numberOfLaunches = 0;
Arg arg[10];
wizardShell = NULL;
int n;
char *doSafeString = getenv("DOSAFE");
if (doSafeString != NULL)
{
doSafe = true;
cout << "Enable DOSAFE" << endl;
}
else
{
doSafe = false;
}
n = 0;
XtSetArg(arg[n],XmNtitle,"FastShellApp");
n++;
topLevel = XtAppInitialize(&app_context,"fastShellApp",
NULL,
0,
(int *) & argc,
argv,
NULL,
arg,
n);
form=XtVaCreateManagedWidget("form",
xmFormWidgetClass,
topLevel,
XmNresizePolicy, XmRESIZE_NONE,
XmNunitType, XmPIXELS,
XmNx, 170,
XmNy, 280,
XmNwidth, 400,
XmNheight, 600,
NULL );
startWidget = XtVaCreateManagedWidget("Start",
xmPushButtonWidgetClass,
form,
XmNx, 10,
XmNy, 50,
RES_CONVERT( XmNlabelString, "Start" ),
NULL );
stopWidget = XtVaCreateManagedWidget("Lower",
xmPushButtonWidgetClass,
form,
XmNx, 10,
XmNy, 75,
RES_CONVERT( XmNlabelString, "Stop" ),
NULL );
XtAddCallback(startWidget,XmNactivateCallback,
(XtCallbackProc) start_cb,
(XtPointer) NULL);
XtAddCallback(stopWidget,XmNactivateCallback,
(XtCallbackProc) stop_cb,
(XtPointer) NULL);
wizardCreated = false;
char *nbFast = getenv("NUMBERFASTPOPUPS");
if (nbFast != NULL)
{
char *lastPos;
numberOfFastPopups = strtol(nbFast,&lastPos,10);
}
else
{
numberOfFastPopups = 2;
}
char *safeSleep = getenv("SAFESLEEPNS");
if (safeSleep != NULL)
{
char *end;
safeSleepTimens = strtol(safeSleep,&end,10);
}
else
{
safeSleepTimens = -1;
}
safeSleepTimens = 0;
XtRealizeWidget(topLevel);
// records = fopen("/tmp/mapStats","w+");
watching = false;
wizardCreated = false;
XtAppMainLoop(app_context);
return 0;
}
************ end: rapidWin.cxx ***********
Index: ChangeLog
===================================================================
RCS file: /home/cvs/fvwm/fvwm/ChangeLog,v
retrieving revision 1.3137
diff -u -3 -p -r1.3137 ChangeLog
--- ChangeLog 14 Feb 2011 21:50:53 -0000 1.3137
+++ ChangeLog 1 Mar 2011 03:46:54 -0000
@@ -1,3 +1,9 @@
+2011-02-28 Rich Coe <[email protected]>
+ * fvwm/add_window.c
+ * fvwm/events.c
+ An application which rapidly pops up and pops down a window, the window
gets lost, such that
+ after a popdown the subsequent popup is lost. This has been fixed before
(and failed).
+
2011-02-14 Thomas Adam <[email protected]>
* libs/PictureImageLoader.c (PImageLoadPng):
Use png_get_color_type() as the data type we were previously using is
Index: fvwm/add_window.c
===================================================================
RCS file: /home/cvs/fvwm/fvwm/fvwm/add_window.c,v
retrieving revision 1.401
diff -u -3 -p -r1.401 add_window.c
--- fvwm/add_window.c 2 Aug 2009 15:04:08 -0000 1.401
+++ fvwm/add_window.c 1 Mar 2011 03:46:55 -0000
@@ -2152,13 +2152,30 @@ FvwmWindow *AddWindow(
(unsigned int*)&JunkWidth, (unsigned int*)&JunkHeight,
(unsigned int*)&JunkBW, (unsigned int*)&JunkDepth) == 0)
{
- if (Scr.bo.do_display_new_window_names)
- {
- fvwm_msg(INFO, "AddWindow", "new window disappeared");
+ /*
+ * Events may come in for a client frame base window that no
+ * longer exists (the client window was just unmanaged but the
+ * the client did some action before the un-reparenting was
+ * actually done). Confirm that this is the case and then
+ * handle the request as if it came in as a root window event.
+ */
+
+ w = exc->x.etrigger->xmaprequest.window;
+ if (XGetGeometry(
+ dpy, w, &JunkRoot, &JunkX, &JunkY,
+ (unsigned int*)&JunkWidth, (unsigned
int*)&JunkHeight,
+ (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth)
== 0) {
+ if (Scr.bo.do_display_new_window_names)
+ {
+ fvwm_msg(INFO, "AddWindow", "new window
disappeared");
+ }
+ free(fw);
+ MyXUngrabServer(dpy);
+ return NULL;
}
free(fw);
- MyXUngrabServer(dpy);
- return NULL;
+ setup_window_structure(&tmp, w, ReuseWin);
+ fw = tmp;
}
/****** window name ******/
Index: fvwm/events.c
===================================================================
RCS file: /home/cvs/fvwm/fvwm/fvwm/events.c,v
retrieving revision 1.568
diff -u -3 -p -r1.568 events.c
--- fvwm/events.c 9 Oct 2010 15:51:29 -0000 1.568
+++ fvwm/events.c 1 Mar 2011 03:46:55 -0000
@@ -2945,59 +2945,6 @@ void HandleMapRequestKeepRaised(
/* If the window has never been mapped before ... */
if (!fw || (fw && DO_REUSE_DESTROYED(fw)))
{
- check_if_event_args args;
- XEvent dummy;
-
- args.w = ew;
- args.do_return_true = True;
- args.do_return_true_cr = False;
- if (
- FCheckIfEvent(
- dpy, &dummy, test_withdraw_request,
- (XPointer)&args)) {
- /* The window is moved back to the WithdrawnState
- * immideately. Don't map it.
- *
- * However, send make sure that a WM_STATE
- * PropertyNotify event is sent to the window.
- * QT needs this.
- */
- Atom atype;
- int aformat;
- unsigned long nitems, bytes_remain;
- unsigned char *prop;
-
- if (
- XGetWindowProperty(
- dpy, ew, _XA_WM_STATE, 0L, 3L, False,
- _XA_WM_STATE, &atype, &aformat,
- &nitems,&bytes_remain,&prop)
- == Success)
- {
- if (prop != NULL)
- {
- XFree(prop);
- XDeleteProperty(dpy, ew, _XA_WM_STATE);
- }
- else
- {
- XPropertyEvent ev;
- ev.type = PropertyNotify;
- ev.display = dpy;
- ev.window = ew;
- ev.atom = _XA_WM_STATE;
- ev.time = fev_get_evtime();
- ev.state = PropertyDelete;
- FSendEvent(
- dpy, ew, True,
- PropertyChangeMask,
- (XEvent*)&ev);
- }
- }
-
- return;
- }
-
/* Add decorations. */
fw = AddWindow(
&initial_map_command, ea->exc, ReuseWin, win_opts);
@@ -3712,10 +3659,8 @@ void HandleUnmapNotify(const evh_args_t
XEvent dummy;
XEvent map_event;
const XEvent *te = ea->exc->x.etrigger;
- int weMustUnmap;
Bool focus_grabbed;
Bool must_return = False;
- Bool do_map = False;
FvwmWindow * const fw = ea->exc->w.fw;
Window pw;
Window cw;
@@ -3736,16 +3681,9 @@ void HandleUnmapNotify(const evh_args_t
* unmapped (which is the case for fvwm for IconicState).
* Unfortunately, we looked for the FvwmContext using that field, so
* try the window field also. */
- weMustUnmap = 0;
- if (!fw)
+ if (!fw && (XFindContext(dpy, te->xunmap.window, FvwmContext, (caddr_t
*)&fw) == XCNOENT))
{
- weMustUnmap = 1;
- if (XFindContext(
- dpy, te->xunmap.window, FvwmContext,
- (caddr_t *)&fw) == XCNOENT)
- {
- return;
- }
+ return;
}
cw = FW_W(fw);
pw = FW_W_PARENT(fw);
@@ -3759,31 +3697,7 @@ void HandleUnmapNotify(const evh_args_t
return;
}
- if (weMustUnmap)
- {
- Bool is_map_request_pending;
- check_if_event_args args;
-
- args.w = te->xunmap.window;
- args.do_return_true = False;
- args.do_return_true_cr = False;
- /* Using FCheckTypedWindowEvent() does not work here. I don't
- * have the slightest idea why, but using FCheckIfEvent() with
- * the appropriate predicate procedure works fine. */
- FCheckIfEvent(dpy, &dummy, test_map_request, (XPointer)&args);
- /* Unfortunately, there is no procedure in X that simply tests
- * if an event of a certain type in on the queue without
- * waiting and without removing it from the queue.
- * XCheck...Event() does not wait but removes the event while
- * XPeek...() does not remove the event but waits. To solve
- * this, the predicate procedure sets a flag in the passed in
- * structure and returns False unconditionally. */
- is_map_request_pending = (args.ret_does_match == True);
- if (!is_map_request_pending)
- {
- XUnmapWindow(dpy, te->xunmap.window);
- }
- }
+ XUnmapWindow(dpy, te->xunmap.window);
if (fw == Scr.Hilite)
{
Scr.Hilite = NULL;
@@ -3803,35 +3717,12 @@ void HandleUnmapNotify(const evh_args_t
* won't cause it to get mapped) and then throw away all state (pretend
* that we've received a DestroyNotify).
*/
- if (!FCheckTypedWindowEvent(
- dpy, te->xunmap.window, DestroyNotify, &dummy) &&
- XTranslateCoordinates(
- dpy, te->xunmap.window, Scr.Root, 0, 0, &dstx, &dsty,
- &dumwin))
+ if (XTranslateCoordinates( dpy, te->xunmap.window, Scr.Root, 0, 0,
&dstx, &dsty, &dumwin))
{
MyXGrabServer(dpy);
SetMapStateProp(fw, WithdrawnState);
EWMH_RestoreInitialStates(fw, te->type);
- if (FCheckTypedWindowEvent(
- dpy, te->xunmap.window, ReparentNotify, &dummy))
- {
- if (fw->attr_backup.border_width)
- {
- XSetWindowBorderWidth(
- dpy, te->xunmap.window,
- fw->attr_backup.border_width);
- }
- if ((!IS_ICON_SUPPRESSED(fw))&&
- (fw->wmhints &&
- (fw->wmhints->flags & IconWindowHint)))
- {
- XUnmapWindow(dpy, fw->wmhints->icon_window);
- }
- }
- else
- {
- RestoreWithdrawnLocation(fw, False, Scr.Root);
- }
+ RestoreWithdrawnLocation(fw, False, Scr.Root);
if (!IS_TEAR_OFF_MENU(fw))
{
XRemoveFromSaveSet(dpy, te->xunmap.window);
@@ -3839,12 +3730,6 @@ void HandleUnmapNotify(const evh_args_t
}
XSync(dpy, 0);
MyXUngrabServer(dpy);
- if (FCheckTypedWindowEvent(dpy, pw, MapRequest, &map_event))
- {
- /* the client tried to map the window again while it
- * was still inside the decoration windows */
- do_map = True;
- }
}
destroy_window(fw);
if (focus_grabbed == True)
@@ -3854,14 +3739,6 @@ void HandleUnmapNotify(const evh_args_t
EWMH_ManageKdeSysTray(te->xunmap.window, te->type);
EWMH_WindowDestroyed();
GNOME_SetClientList();
- if (do_map == True)
- {
- map_event.xmaprequest.window = cw;
- map_event.xmaprequest.parent = Scr.Root;
- dispatch_event(&map_event);
- /* note: we really should handle all map and unmap notify
- * events for that window in a loop here */
- }
return;
}
--
Rich Coe [email protected]