Description: Updated fix for upstream bug #1565 This patch fixes the problem introduced by fix #1565, included in Motif 2.3.4, which breaks keyboard navigation in menus. Author: Olexiy Chernyavskyy Origin: upstream, http://bugs.motifzone.net/show_bug.cgi?id=1565 Bug: http://bugs.motifzone.net/show_bug.cgi?id=1630 Bug-Debian: http://bugs.debian.org/730026 Last-Update: 2013-11-29 --- a/lib/Xm/ComboBox.c +++ b/lib/Xm/ComboBox.c @@ -3164,6 +3164,9 @@ Arg args[4]; ArgList merged_args; Cardinal n; +#ifdef FIX_1565 + XmGrabShellWidget grabsh; +#endif n = 0; XtSetArg(args[n], XmNlayoutDirection, LayoutM(parent)), n++; @@ -3175,6 +3178,11 @@ merged_args, n + *num_args); XtFree((char*)merged_args); +#ifdef FIX_1565 + grabsh = (XmGrabShellWidget) shell; + grabsh->grab_shell.set_input_focus = False; +#endif + return shell; } --- a/lib/Xm/DropDown.c +++ b/lib/Xm/DropDown.c @@ -2027,6 +2027,9 @@ Arg *new_list, largs[10]; Cardinal num_largs; Widget sb; +#ifdef FIX_1565 + XmGrabShellWidget grabsh; +#endif num_largs = 0; XtSetArg(largs[num_largs], XmNoverrideRedirect, True); num_largs++; @@ -2040,6 +2043,10 @@ xmGrabShellWidgetClass, w, new_list, num_largs + num_args); +#ifdef FIX_1565 + grabsh = (XmGrabShellWidget) XmDropDown_popup_shell(cbw); + grabsh->grab_shell.set_input_focus = False; +#endif XtFree((char *) new_list); #ifdef FIX_1446 --- a/lib/Xm/GrabShell.c +++ b/lib/Xm/GrabShell.c @@ -283,6 +283,10 @@ /* CR 9920: Popdown may be requested before MapNotify. */ grabsh->grab_shell.mapped = False; + +#ifdef FIX_1565 + grabsh->grab_shell.set_input_focus = True; +#endif } /* @@ -395,8 +399,16 @@ XGetInputFocus(XtDisplay(shell), &grabshell->grab_shell.old_focus, &grabshell->grab_shell.old_revert_to); old_handler = XSetErrorHandler(IgnoreXErrors); - XSetInputFocus(XtDisplay(shell), XtWindow(shell), RevertToParent, time); - XSync(XtDisplay(shell), False); +#ifdef FIX_1565 + if (! grabshell->grab_shell.set_input_focus) { + XmForceGrabKeyboard(shell, time); + } else { +#endif + XSetInputFocus(XtDisplay(shell), XtWindow(shell), RevertToParent, time); + XSync(XtDisplay(shell), False); +#ifdef FIX_1565 + } +#endif XSetErrorHandler(old_handler); } --- a/lib/Xm/GrabShellP.h +++ b/lib/Xm/GrabShellP.h @@ -30,6 +30,7 @@ #include #include #include +#include "XmI.h" #ifdef __cplusplus extern "C" { @@ -55,6 +56,9 @@ Boolean mapped; Window old_focus; int old_revert_to; +#ifdef FIX_1565 + Boolean set_input_focus; +#endif } XmGrabShellPart; --- a/lib/Xm/MenuShell.c +++ b/lib/Xm/MenuShell.c @@ -1514,9 +1514,7 @@ /** the real grab ***/ _XmMenuGrabKeyboardAndPointer((Widget)rowcol, _time); -#ifndef FIX_1565 _XmMenuFocus(XtParent(rowcol), XmMENU_BEGIN, _time); -#endif /* To support menu replay, keep the pointer in sync mode */ XAllowEvents(XtDisplay(rowcol), SyncPointer, CurrentTime); --- a/lib/Xm/MenuUtil.c +++ b/lib/Xm/MenuUtil.c @@ -1053,11 +1053,7 @@ register int status = (_XmGrabKeyboard(widget, -#ifdef FIX_1565 - False, -#else True, -#endif GrabModeSync, GrabModeAsync, time) != GrabSuccess); --- a/lib/Xm/RCMenu.c +++ b/lib/Xm/RCMenu.c @@ -85,6 +85,9 @@ #include "TraversalI.h" #include "UniqueEvnI.h" #include "VendorSI.h" +#ifdef FIX_1565 +#include +#endif #define FIX_1535 @@ -943,6 +946,13 @@ XmMenuState mst = _XmGetMenuState((Widget)w); Window tmpWindow; int tmpRevert; +#ifdef FIX_1565 + Widget shell; + + shell = w; + while (! XtIsSubclass(shell, shellWidgetClass)) + shell = XtParent(shell); +#endif if (_time == CurrentTime) _time = XtLastTimestampProcessed(XtDisplay(w)); @@ -983,6 +993,11 @@ shell.popped_up)) **/ { +#ifdef FIX_1565 + if (XtIsSubclass(shell, xmGrabShellWidgetClass) || XtIsSubclass(shell, xmMenuShellWidgetClass)) + XmForceGrabKeyboard(w, _time); + else +#endif SetInputFocus(XtDisplay(w), mst->RC_menuFocus.oldFocus, mst->RC_menuFocus.oldRevert, mst->RC_menuFocus.oldTime); @@ -996,6 +1011,11 @@ */ else { +#ifdef FIX_1565 + if (XtIsSubclass(shell, xmGrabShellWidgetClass) || XtIsSubclass(shell, xmMenuShellWidgetClass)) + XmForceGrabKeyboard(w, _time); + else +#endif SetInputFocus(XtDisplay(w), mst->RC_menuFocus.oldFocus, mst->RC_menuFocus.oldRevert, mst->RC_menuFocus.oldTime); @@ -1014,6 +1034,11 @@ RC_menuFocus.oldFocus); mst->RC_menuFocus.oldTime = _time - 1; +#ifdef FIX_1565 + if (XtIsSubclass(shell, xmGrabShellWidgetClass) || XtIsSubclass(shell, xmMenuShellWidgetClass)) + XmForceGrabKeyboard(w, _time); + else +#endif SetInputFocus(XtDisplay(w), XtWindow(w), mst->RC_menuFocus.oldRevert, mst->RC_menuFocus.oldTime); @@ -1027,6 +1052,11 @@ XGetInputFocus(XtDisplay(w), &tmpWindow, &tmpRevert); if (tmpWindow != XtWindow(w)) { +#ifdef FIX_1565 + if (XtIsSubclass(shell, xmGrabShellWidgetClass) || XtIsSubclass(shell, xmMenuShellWidgetClass)) + XmForceGrabKeyboard(w, _time); + else +#endif SetInputFocus(XtDisplay(w), XtWindow(w), tmpRevert, _time); mst->RC_menuFocus.oldRevert = tmpRevert; @@ -1048,6 +1078,11 @@ break; case XmMENU_MIDDLE: +#ifdef FIX_1565 + if (XtIsSubclass(shell, xmGrabShellWidgetClass) || XtIsSubclass(shell, xmMenuShellWidgetClass)) + XmForceGrabKeyboard(w, _time); + else +#endif SetInputFocus(XtDisplay(w), XtWindow(w), mst->RC_menuFocus.oldRevert, mst->RC_menuFocus.oldTime); @@ -1062,6 +1097,11 @@ if ((tmpWindow != XtWindow(w)) && (_time > mst->RC_menuFocus.oldTime)) { +#ifdef FIX_1565 + if (XtIsSubclass(shell, xmGrabShellWidgetClass) || XtIsSubclass(shell, xmMenuShellWidgetClass)) + XmForceGrabKeyboard(w, _time); + else +#endif SetInputFocus(XtDisplay(w), XtWindow(w), tmpRevert, _time); mst->RC_menuFocus.oldRevert = tmpRevert; --- a/lib/Xm/Xm.c +++ b/lib/Xm/Xm.c @@ -40,6 +40,10 @@ #ifdef FIX_345 #include #endif +#ifdef FIX_1565 +#include +#include +#endif /************************************************************************** @@ -530,3 +534,173 @@ return p; } #endif + +#ifdef FIX_1565 + +typedef struct _GrabData GrabData; +struct _GrabData { + Widget w; + GrabData *next; +}; + +static void _XmSendFocusEvent(Widget child, int type); +static void _XmStartDispatcher(Display *display); +static Boolean _XmEventDispatcher(XEvent *event); +static void UnmapHandler(Widget w, XtPointer client_data, XEvent *event, Boolean *cont); +static Boolean _UngrabKeyboard(Widget w); + +static GrabData *grabw_top = NULL; +static int xm_dispatcher_on = 0; +static XtEventDispatchProc saved_dispatcher_proc = NULL; +static XtEventDispatchProc xt_dispatcher_proc = NULL; + +/* + XmForceGrabKeyboard function is defined to be a substitutor of XSetInputFocus calls + for popup and pulldown menus that should grab keyboard focus yet main window at the + same time should visually stay in focus for window manager. This resolves focus flip + issue when popup or pulldown menu is raised. ~ochern + */ +void XmForceGrabKeyboard(Widget w, Time time) +{ + GrabData *grabw; + + if (!w) + return; + + while (! XtIsSubclass(w, shellWidgetClass)) + w = XtParent(w); + + if (! (XtIsSubclass(w, xmGrabShellWidgetClass) || XtIsSubclass(w, xmMenuShellWidgetClass))) + return; + + _XmStartDispatcher(XtDisplay(w)); + + _UngrabKeyboard(w); + + grabw = (GrabData *) XtMalloc(sizeof(GrabData)); + grabw->w = w; + _XmProcessLock(); + grabw->next = grabw_top; + grabw_top = grabw; + _XmProcessUnlock(); + + XtInsertEventHandler(w, StructureNotifyMask, False, UnmapHandler, NULL, XtListHead); + + _XmSendFocusEvent(w, FocusIn); + + /* Following the XSetInputFocus behaviour we force sending FocusOut (see XGrabKeyboard(3)) + event to a previous keyboard holder */ + XtGrabKeyboard(w, True, GrabModeAsync, GrabModeAsync, time); +} + +static void _XmStartDispatcher(Display *display) +{ + if (!display) + return; + + _XmProcessLock(); + + if (xm_dispatcher_on) { + _XmProcessUnlock(); + return; + } + + saved_dispatcher_proc = XtSetEventDispatcher(display, KeyPress, _XmEventDispatcher); + if (! xt_dispatcher_proc) + xt_dispatcher_proc = saved_dispatcher_proc; + XtSetEventDispatcher(display, KeyRelease, _XmEventDispatcher); + xm_dispatcher_on = 1; + + _XmProcessUnlock(); +} + +static Boolean _XmEventDispatcher(XEvent *event) +{ + _XmProcessLock(); + if (grabw_top) { + if (event->type == KeyPress || event->type == KeyRelease) + event->xany.window = XtWindow(grabw_top->w); + } + _XmProcessUnlock(); + + if (saved_dispatcher_proc) { + return (*saved_dispatcher_proc)(event); + } else if (xt_dispatcher_proc) { + return (*xt_dispatcher_proc)(event); + } else { + if (grabw_top) + XtSetEventDispatcher(XtDisplay(grabw_top->w), event->type, NULL); + return XtDispatchEvent(event); + } +} + +static void UnmapHandler(Widget w, XtPointer client_data, XEvent *event, Boolean *cont) +{ + if (event->type == UnmapNotify) + _UngrabKeyboard(w); + if (! grabw_top) { + XtSetEventDispatcher(XtDisplay(w), KeyPress, saved_dispatcher_proc); + XtSetEventDispatcher(XtDisplay(w), KeyRelease, saved_dispatcher_proc); + xm_dispatcher_on = 0; + } + + /* we do not call XtUngrabKeyboard since X server automatically performs an + UngrabKeyboard request if the event window for an active keyboard grab becomes + not viewable. ~ochern */ +} + +static Boolean _UngrabKeyboard(Widget w) +{ + GrabData *grabw, *grabw_prev; + + _XmProcessLock(); + if (! grabw_top) { + _XmProcessUnlock(); + return False; + } + + grabw = grabw_top; + grabw_prev = NULL; + while(grabw && grabw->w != w) { + grabw_prev = grabw; + grabw = grabw->next; + } + if (grabw) { + if (grabw_prev) + grabw_prev->next = grabw->next; + else + grabw_top = grabw->next; + XtFree((char*) grabw); + + _XmProcessUnlock(); + return True; + } + + _XmProcessUnlock(); + return False; +} + +static void _XmSendFocusEvent(Widget child, int type) +{ + child = XtIsWidget(child) ? child : _XtWindowedAncestor(child); + if (XtIsSensitive(child) && !child->core.being_destroyed + && XtIsRealized(child) && (XtBuildEventMask(child) & FocusChangeMask)) + { + XFocusChangeEvent event; + Display* dpy = XtDisplay (child); + + event.type = type; + event.serial = LastKnownRequestProcessed(dpy); + event.send_event = True; + event.display = dpy; + event.window = XtWindow(child); + event.mode = NotifyNormal; + event.detail = NotifyAncestor; + if (XFilterEvent((XEvent*)&event, XtWindow(child))) + return; + XtDispatchEventToWidget(child, (XEvent*)&event); + } +} + +#endif + --- a/lib/Xm/XmI.h +++ b/lib/Xm/XmI.h @@ -242,7 +242,9 @@ extern XChar2b* _XmUtf8ToUcs2(char *draw_text, size_t seg_len, size_t *ret_str_len); - +#ifdef FIX_1565 +extern void XmForceGrabKeyboard(Widget w, Time time); +#endif /******** End Private Function Declarations ********/