fbc45b
commit 88bdce139baf89839b6e13d698576fc56211e845
fbc45b
Author: Oleksiy Chernyavskyy <ochern@ics.com>
fbc45b
Date:   Wed Mar 16 00:46:49 2016 +0200
fbc45b
fbc45b
    Reimplemented bugfix 1565
fbc45b
    
fbc45b
    Signed-off-by: Oleksiy Chernyavskyy <ochern@ics.com>
fbc45b
fbc45b
diff --git a/lib/Xm/ComboBox.c b/lib/Xm/ComboBox.c
fbc45b
index 1472e45..cf507da 100644
fbc45b
--- a/lib/Xm/ComboBox.c
fbc45b
+++ b/lib/Xm/ComboBox.c
fbc45b
@@ -3164,6 +3164,9 @@ CreatePulldown(Widget    parent,
fbc45b
   Arg      args[4];
fbc45b
   ArgList  merged_args;
fbc45b
   Cardinal n;
fbc45b
+#ifdef FIX_1565
fbc45b
+  XmGrabShellWidget grabsh;
fbc45b
+#endif
fbc45b
 
fbc45b
   n = 0;
fbc45b
   XtSetArg(args[n], XmNlayoutDirection, LayoutM(parent)), n++;
fbc45b
@@ -3175,6 +3178,11 @@ CreatePulldown(Widget    parent,
fbc45b
 			     merged_args, n + *num_args);
fbc45b
   XtFree((char*)merged_args);
fbc45b
 
fbc45b
+#ifdef FIX_1565
fbc45b
+  grabsh = (XmGrabShellWidget) shell;
fbc45b
+  grabsh->grab_shell.set_input_focus = False;
fbc45b
+#endif
fbc45b
+
fbc45b
   return shell;
fbc45b
 }
fbc45b
 
fbc45b
diff --git a/lib/Xm/DropDown.c b/lib/Xm/DropDown.c
fbc45b
index 37fec03..5cd15ca 100644
fbc45b
--- a/lib/Xm/DropDown.c
fbc45b
+++ b/lib/Xm/DropDown.c
fbc45b
@@ -2027,6 +2027,9 @@ CreatePopup(Widget w, ArgList args, Cardinal num_args)
fbc45b
     Arg *new_list, largs[10];
fbc45b
     Cardinal num_largs;
fbc45b
     Widget sb;
fbc45b
+#ifdef FIX_1565
fbc45b
+    XmGrabShellWidget grabsh;
fbc45b
+#endif
fbc45b
 
fbc45b
     num_largs = 0;
fbc45b
     XtSetArg(largs[num_largs], XmNoverrideRedirect, True); num_largs++;
fbc45b
@@ -2040,6 +2043,10 @@ CreatePopup(Widget w, ArgList args, Cardinal num_args)
fbc45b
 						xmGrabShellWidgetClass, w,
fbc45b
 						new_list,
fbc45b
 						num_largs + num_args);
fbc45b
+#ifdef FIX_1565
fbc45b
+    grabsh = (XmGrabShellWidget) XmDropDown_popup_shell(cbw);
fbc45b
+    grabsh->grab_shell.set_input_focus = False;
fbc45b
+#endif
fbc45b
     XtFree((char *) new_list);
fbc45b
 
fbc45b
 #ifdef FIX_1446
fbc45b
diff --git a/lib/Xm/GrabShell.c b/lib/Xm/GrabShell.c
fbc45b
index 88f3154..af13e0b 100644
fbc45b
--- a/lib/Xm/GrabShell.c
fbc45b
+++ b/lib/Xm/GrabShell.c
fbc45b
@@ -283,6 +283,10 @@ Initialize(Widget req,		/* unused */
fbc45b
 
fbc45b
   /* CR 9920:  Popdown may be requested before MapNotify. */
fbc45b
   grabsh->grab_shell.mapped = False;
fbc45b
+
fbc45b
+#ifdef FIX_1565
fbc45b
+  grabsh->grab_shell.set_input_focus = True;
fbc45b
+#endif
fbc45b
 }
fbc45b
 
fbc45b
 /*
fbc45b
@@ -395,8 +399,16 @@ MapNotifyHandler(Widget shell, XtPointer client_data,
fbc45b
   XGetInputFocus(XtDisplay(shell), &grabshell->grab_shell.old_focus,
fbc45b
 		 &grabshell->grab_shell.old_revert_to);
fbc45b
   old_handler = XSetErrorHandler(IgnoreXErrors);
fbc45b
-  XSetInputFocus(XtDisplay(shell), XtWindow(shell), RevertToParent, time);
fbc45b
-  XSync(XtDisplay(shell), False);
fbc45b
+#ifdef FIX_1565
fbc45b
+  if (! grabshell->grab_shell.set_input_focus) {
fbc45b
+    XmForceGrabKeyboard(shell, time);
fbc45b
+  } else {
fbc45b
+#endif
fbc45b
+    XSetInputFocus(XtDisplay(shell), XtWindow(shell), RevertToParent, time);
fbc45b
+    XSync(XtDisplay(shell), False);
fbc45b
+#ifdef FIX_1565
fbc45b
+  }
fbc45b
+#endif
fbc45b
   XSetErrorHandler(old_handler);
fbc45b
 }
fbc45b
 
fbc45b
diff --git a/lib/Xm/GrabShellP.h b/lib/Xm/GrabShellP.h
fbc45b
index 92fe508..025f001 100644
fbc45b
--- a/lib/Xm/GrabShellP.h
fbc45b
+++ b/lib/Xm/GrabShellP.h
fbc45b
@@ -30,6 +30,7 @@
fbc45b
 #include <Xm/GrabShell.h>
fbc45b
 #include <Xm/XmP.h>
fbc45b
 #include <X11/ShellP.h>
fbc45b
+#include "XmI.h"
fbc45b
 
fbc45b
 #ifdef __cplusplus
fbc45b
 extern "C" {
fbc45b
@@ -55,6 +56,9 @@ typedef struct
fbc45b
   Boolean	mapped;
fbc45b
   Window	old_focus;
fbc45b
   int		old_revert_to;
fbc45b
+#ifdef FIX_1565
fbc45b
+  Boolean	set_input_focus;
fbc45b
+#endif
fbc45b
 } XmGrabShellPart;
fbc45b
 
fbc45b
 
fbc45b
diff --git a/lib/Xm/MenuShell.c b/lib/Xm/MenuShell.c
fbc45b
index 2ed3dd8..9887087 100644
fbc45b
--- a/lib/Xm/MenuShell.c
fbc45b
+++ b/lib/Xm/MenuShell.c
fbc45b
@@ -1514,9 +1514,7 @@ ChangeManaged(
fbc45b
 
fbc45b
 	       /** the real grab ***/
fbc45b
          _XmMenuGrabKeyboardAndPointer((Widget)rowcol, _time);
fbc45b
-#ifndef FIX_1565
fbc45b
 	 _XmMenuFocus(XtParent(rowcol), XmMENU_BEGIN, _time);
fbc45b
-#endif
fbc45b
 
fbc45b
 	 /* To support menu replay, keep the pointer in sync mode */
fbc45b
 	 XAllowEvents(XtDisplay(rowcol), SyncPointer, CurrentTime);
fbc45b
diff --git a/lib/Xm/MenuUtil.c b/lib/Xm/MenuUtil.c
fbc45b
index 1d88390..2fb6a27 100644
fbc45b
--- a/lib/Xm/MenuUtil.c
fbc45b
+++ b/lib/Xm/MenuUtil.c
fbc45b
@@ -1053,11 +1053,7 @@ _XmMenuGrabKeyboardAndPointer(
fbc45b
 
fbc45b
    register int status =
fbc45b
            (_XmGrabKeyboard(widget,
fbc45b
-#ifdef FIX_1565
fbc45b
-                            False,
fbc45b
-#else
fbc45b
                             True,
fbc45b
-#endif
fbc45b
                             GrabModeSync,
fbc45b
                             GrabModeAsync,
fbc45b
                             time) != GrabSuccess);
fbc45b
diff --git a/lib/Xm/RCMenu.c b/lib/Xm/RCMenu.c
fbc45b
index 2c698d4..8b156da 100644
fbc45b
--- a/lib/Xm/RCMenu.c
fbc45b
+++ b/lib/Xm/RCMenu.c
fbc45b
@@ -85,6 +85,9 @@ static char *rcsid = "$TOG: RCMenu.c /main/25 1999/05/24 18:06:57 samborn $";
fbc45b
 #include "TraversalI.h"
fbc45b
 #include "UniqueEvnI.h"
fbc45b
 #include "VendorSI.h"
fbc45b
+#ifdef FIX_1565
fbc45b
+#include <Xm/GrabShell.h>
fbc45b
+#endif
fbc45b
 
fbc45b
 #define FIX_1535
fbc45b
 
fbc45b
@@ -943,6 +946,13 @@ _XmMenuFocus(
fbc45b
    XmMenuState mst = _XmGetMenuState((Widget)w);
fbc45b
    Window tmpWindow;
fbc45b
    int tmpRevert;
fbc45b
+#ifdef FIX_1565
fbc45b
+   Widget shell;
fbc45b
+
fbc45b
+   shell = w;
fbc45b
+   while (! XtIsSubclass(shell, shellWidgetClass))
fbc45b
+     shell = XtParent(shell);
fbc45b
+#endif
fbc45b
  
fbc45b
    if (_time == CurrentTime) 
fbc45b
      _time = XtLastTimestampProcessed(XtDisplay(w));
fbc45b
@@ -983,6 +993,11 @@ _XmMenuFocus(
fbc45b
 			  shell.popped_up))
fbc45b
 **/
fbc45b
 		     {
fbc45b
+#ifdef FIX_1565
fbc45b
+               if (XtIsSubclass(shell, xmGrabShellWidgetClass) || XtIsSubclass(shell, xmMenuShellWidgetClass))
fbc45b
+                 XmForceGrabKeyboard(w, _time);
fbc45b
+               else
fbc45b
+#endif
fbc45b
 		       SetInputFocus(XtDisplay(w), mst->RC_menuFocus.oldFocus,
fbc45b
 				     mst->RC_menuFocus.oldRevert,
fbc45b
 				     mst->RC_menuFocus.oldTime);
fbc45b
@@ -996,6 +1011,11 @@ _XmMenuFocus(
fbc45b
  	      */
fbc45b
  	     else
fbc45b
  	       {
fbc45b
+#ifdef FIX_1565
fbc45b
+               if (XtIsSubclass(shell, xmGrabShellWidgetClass) || XtIsSubclass(shell, xmMenuShellWidgetClass))
fbc45b
+                 XmForceGrabKeyboard(w, _time);
fbc45b
+               else
fbc45b
+#endif
fbc45b
  		  SetInputFocus(XtDisplay(w), mst->RC_menuFocus.oldFocus,
fbc45b
 				mst->RC_menuFocus.oldRevert,
fbc45b
 				mst->RC_menuFocus.oldTime);
fbc45b
@@ -1014,6 +1034,11 @@ _XmMenuFocus(
fbc45b
 	     RC_menuFocus.oldFocus);
fbc45b
 	  mst->RC_menuFocus.oldTime = _time - 1;
fbc45b
 
fbc45b
+#ifdef FIX_1565
fbc45b
+      if (XtIsSubclass(shell, xmGrabShellWidgetClass) || XtIsSubclass(shell, xmMenuShellWidgetClass))
fbc45b
+        XmForceGrabKeyboard(w, _time);
fbc45b
+      else
fbc45b
+#endif
fbc45b
 	  SetInputFocus(XtDisplay(w), XtWindow(w), mst->RC_menuFocus.oldRevert,
fbc45b
 			mst->RC_menuFocus.oldTime);
fbc45b
 
fbc45b
@@ -1027,6 +1052,11 @@ _XmMenuFocus(
fbc45b
 	  XGetInputFocus(XtDisplay(w), &tmpWindow, &tmpRevert);
fbc45b
 	  if (tmpWindow != XtWindow(w))
fbc45b
 	  {
fbc45b
+#ifdef FIX_1565
fbc45b
+        if (XtIsSubclass(shell, xmGrabShellWidgetClass) || XtIsSubclass(shell, xmMenuShellWidgetClass))
fbc45b
+          XmForceGrabKeyboard(w, _time);
fbc45b
+        else
fbc45b
+#endif
fbc45b
 	    SetInputFocus(XtDisplay(w), XtWindow(w), tmpRevert, _time);
fbc45b
 
fbc45b
 	    mst->RC_menuFocus.oldRevert = tmpRevert;
fbc45b
@@ -1048,6 +1078,11 @@ _XmMenuFocus(
fbc45b
 
fbc45b
 	  break;
fbc45b
 	case XmMENU_MIDDLE:
fbc45b
+#ifdef FIX_1565
fbc45b
+      if (XtIsSubclass(shell, xmGrabShellWidgetClass) || XtIsSubclass(shell, xmMenuShellWidgetClass))
fbc45b
+        XmForceGrabKeyboard(w, _time);
fbc45b
+      else
fbc45b
+#endif
fbc45b
 	  SetInputFocus(XtDisplay(w), XtWindow(w),
fbc45b
 			mst->RC_menuFocus.oldRevert,
fbc45b
 			mst->RC_menuFocus.oldTime);
fbc45b
@@ -1062,6 +1097,11 @@ _XmMenuFocus(
fbc45b
 	  if ((tmpWindow != XtWindow(w)) &&
fbc45b
 	      (_time > mst->RC_menuFocus.oldTime))
fbc45b
 	  {
fbc45b
+#ifdef FIX_1565
fbc45b
+        if (XtIsSubclass(shell, xmGrabShellWidgetClass) || XtIsSubclass(shell, xmMenuShellWidgetClass))
fbc45b
+          XmForceGrabKeyboard(w, _time);
fbc45b
+        else
fbc45b
+#endif
fbc45b
 	    SetInputFocus(XtDisplay(w), XtWindow(w), tmpRevert, _time);
fbc45b
 
fbc45b
 	    mst->RC_menuFocus.oldRevert = tmpRevert;
fbc45b
diff --git a/lib/Xm/Xm.c b/lib/Xm/Xm.c
fbc45b
index 3dfd794..45d48b6 100644
fbc45b
--- a/lib/Xm/Xm.c
fbc45b
+++ b/lib/Xm/Xm.c
fbc45b
@@ -40,6 +40,10 @@
fbc45b
 #ifdef FIX_345
fbc45b
 #include <X11/keysym.h>
fbc45b
 #endif
fbc45b
+#ifdef FIX_1565
fbc45b
+#include <Xm/GrabShell.h>
fbc45b
+#include <Xm/MenuShell.h>
fbc45b
+#endif
fbc45b
 
fbc45b
 
fbc45b
 /**************************************************************************
fbc45b
@@ -530,3 +534,173 @@ _XmAssignInsensitiveColor(Widget w)
fbc45b
 	return p;
fbc45b
 }
fbc45b
 #endif
fbc45b
+
fbc45b
+#ifdef FIX_1565
fbc45b
+
fbc45b
+typedef struct _GrabData GrabData;
fbc45b
+struct _GrabData {
fbc45b
+  Widget w;
fbc45b
+  GrabData *next;
fbc45b
+};
fbc45b
+
fbc45b
+static void _XmSendFocusEvent(Widget child, int type);
fbc45b
+static void _XmStartDispatcher(Display *display);
fbc45b
+static Boolean _XmEventDispatcher(XEvent *event);
fbc45b
+static void UnmapHandler(Widget w, XtPointer client_data, XEvent *event, Boolean *cont);
fbc45b
+static Boolean _UngrabKeyboard(Widget w);
fbc45b
+
fbc45b
+static GrabData *grabw_top = NULL;
fbc45b
+static int xm_dispatcher_on = 0;
fbc45b
+static XtEventDispatchProc saved_dispatcher_proc = NULL;
fbc45b
+static XtEventDispatchProc xt_dispatcher_proc = NULL;
fbc45b
+
fbc45b
+/*
fbc45b
+   XmForceGrabKeyboard function is defined to be a substitutor of XSetInputFocus calls
fbc45b
+   for popup and pulldown menus that should grab keyboard focus yet main window at the
fbc45b
+   same time should visually stay in focus for window manager. This resolves focus flip
fbc45b
+   issue when popup or pulldown menu is raised. ~ochern
fbc45b
+ */
fbc45b
+void XmForceGrabKeyboard(Widget w, Time time)
fbc45b
+{
fbc45b
+  GrabData *grabw;
fbc45b
+
fbc45b
+  if (!w)
fbc45b
+    return;
fbc45b
+
fbc45b
+  while (! XtIsSubclass(w, shellWidgetClass))
fbc45b
+    w = XtParent(w);
fbc45b
+
fbc45b
+  if (! (XtIsSubclass(w, xmGrabShellWidgetClass) || XtIsSubclass(w, xmMenuShellWidgetClass)))
fbc45b
+    return;
fbc45b
+
fbc45b
+  _XmStartDispatcher(XtDisplay(w));
fbc45b
+
fbc45b
+  _UngrabKeyboard(w);
fbc45b
+
fbc45b
+  grabw = (GrabData *) XtMalloc(sizeof(GrabData));
fbc45b
+  grabw->w = w;
fbc45b
+  _XmProcessLock();
fbc45b
+  grabw->next = grabw_top;
fbc45b
+  grabw_top = grabw;
fbc45b
+  _XmProcessUnlock();
fbc45b
+
fbc45b
+  XtInsertEventHandler(w, StructureNotifyMask, False, UnmapHandler, NULL, XtListHead);
fbc45b
+
fbc45b
+  _XmSendFocusEvent(w, FocusIn);
fbc45b
+
fbc45b
+  /* Following the XSetInputFocus behaviour we force sending FocusOut (see XGrabKeyboard(3))
fbc45b
+     event to a previous keyboard holder */
fbc45b
+  XtGrabKeyboard(w, True, GrabModeAsync, GrabModeAsync, time);
fbc45b
+}
fbc45b
+
fbc45b
+static void _XmStartDispatcher(Display *display)
fbc45b
+{
fbc45b
+  if (!display)
fbc45b
+    return;
fbc45b
+
fbc45b
+  _XmProcessLock();
fbc45b
+
fbc45b
+  if (xm_dispatcher_on) {
fbc45b
+    _XmProcessUnlock();
fbc45b
+    return;
fbc45b
+  }
fbc45b
+
fbc45b
+  saved_dispatcher_proc = XtSetEventDispatcher(display, KeyPress, _XmEventDispatcher);
fbc45b
+  if (! xt_dispatcher_proc)
fbc45b
+    xt_dispatcher_proc = saved_dispatcher_proc;
fbc45b
+  XtSetEventDispatcher(display, KeyRelease, _XmEventDispatcher);
fbc45b
+  xm_dispatcher_on = 1;
fbc45b
+
fbc45b
+  _XmProcessUnlock();
fbc45b
+}
fbc45b
+
fbc45b
+static Boolean _XmEventDispatcher(XEvent *event)
fbc45b
+{
fbc45b
+  _XmProcessLock();
fbc45b
+  if (grabw_top) {
fbc45b
+    if (event->type == KeyPress || event->type == KeyRelease)
fbc45b
+      event->xany.window = XtWindow(grabw_top->w);
fbc45b
+  }
fbc45b
+  _XmProcessUnlock();
fbc45b
+
fbc45b
+  if (saved_dispatcher_proc) {
fbc45b
+    return (*saved_dispatcher_proc)(event);
fbc45b
+  } else if (xt_dispatcher_proc) {
fbc45b
+    return (*xt_dispatcher_proc)(event);
fbc45b
+  } else {
fbc45b
+    if (grabw_top)
fbc45b
+      XtSetEventDispatcher(XtDisplay(grabw_top->w), event->type, NULL);
fbc45b
+    return XtDispatchEvent(event);
fbc45b
+  }
fbc45b
+}
fbc45b
+
fbc45b
+static void UnmapHandler(Widget w, XtPointer client_data, XEvent *event, Boolean *cont)
fbc45b
+{
fbc45b
+  if (event->type == UnmapNotify)
fbc45b
+    _UngrabKeyboard(w);
fbc45b
+  if (! grabw_top) {
fbc45b
+    XtSetEventDispatcher(XtDisplay(w), KeyPress, saved_dispatcher_proc);
fbc45b
+    XtSetEventDispatcher(XtDisplay(w), KeyRelease, saved_dispatcher_proc);
fbc45b
+    xm_dispatcher_on = 0;
fbc45b
+  }
fbc45b
+
fbc45b
+  /* we do not call XtUngrabKeyboard since X server automatically performs an
fbc45b
+     UngrabKeyboard request if the event window for an active keyboard grab becomes
fbc45b
+     not viewable. ~ochern */
fbc45b
+}
fbc45b
+
fbc45b
+static Boolean _UngrabKeyboard(Widget w)
fbc45b
+{
fbc45b
+  GrabData *grabw, *grabw_prev;
fbc45b
+
fbc45b
+  _XmProcessLock();
fbc45b
+  if (! grabw_top) {
fbc45b
+    _XmProcessUnlock();
fbc45b
+    return False;
fbc45b
+  }
fbc45b
+
fbc45b
+  grabw = grabw_top;
fbc45b
+  grabw_prev = NULL;
fbc45b
+  while(grabw && grabw->w != w) {
fbc45b
+    grabw_prev = grabw;
fbc45b
+    grabw = grabw->next;
fbc45b
+  }
fbc45b
+  if (grabw) {
fbc45b
+    if (grabw_prev)
fbc45b
+      grabw_prev->next = grabw->next;
fbc45b
+    else
fbc45b
+      grabw_top = grabw->next;
fbc45b
+    XtFree((char*) grabw);
fbc45b
+
fbc45b
+    _XmProcessUnlock();
fbc45b
+    return True;
fbc45b
+  }
fbc45b
+
fbc45b
+  _XmProcessUnlock();
fbc45b
+  return False;
fbc45b
+}
fbc45b
+
fbc45b
+static void _XmSendFocusEvent(Widget child, int type)
fbc45b
+{
fbc45b
+  child = XtIsWidget(child) ? child : _XtWindowedAncestor(child);
fbc45b
+  if (XtIsSensitive(child) && !child->core.being_destroyed
fbc45b
+      && XtIsRealized(child) && (XtBuildEventMask(child) & FocusChangeMask))
fbc45b
+  {
fbc45b
+    XFocusChangeEvent event;
fbc45b
+    Display* dpy = XtDisplay (child);
fbc45b
+
fbc45b
+    event.type = type;
fbc45b
+    event.serial = LastKnownRequestProcessed(dpy);
fbc45b
+    event.send_event = True;
fbc45b
+    event.display = dpy;
fbc45b
+    event.window = XtWindow(child);
fbc45b
+    event.mode = NotifyNormal;
fbc45b
+    event.detail = NotifyAncestor;
fbc45b
+    if (XFilterEvent((XEvent*)&event, XtWindow(child)))
fbc45b
+      return;
fbc45b
+    XtDispatchEventToWidget(child, (XEvent*)&event);
fbc45b
+  }
fbc45b
+}
fbc45b
+
fbc45b
+#endif
fbc45b
+
fbc45b
diff --git a/lib/Xm/XmI.h b/lib/Xm/XmI.h
fbc45b
index b4420d3..c2b819e 100644
fbc45b
--- a/lib/Xm/XmI.h
fbc45b
+++ b/lib/Xm/XmI.h
fbc45b
@@ -242,7 +242,9 @@ extern Boolean _XmIsISO10646(Display *dpy,
fbc45b
 extern XChar2b* _XmUtf8ToUcs2(char *draw_text,
fbc45b
                               size_t seg_len,
fbc45b
 			      size_t *ret_str_len);
fbc45b
-
fbc45b
+#ifdef FIX_1565
fbc45b
+extern void XmForceGrabKeyboard(Widget w, Time time);
fbc45b
+#endif
fbc45b
 
fbc45b
 /********    End Private Function Declarations    ********/
fbc45b