Fix entry and exit events with XI2 grabs on X toolkit menus
authorPo Lu <luangruo@yahoo.com>
Sat, 12 Feb 2022 12:25:00 +0000 (20:25 +0800)
committerPo Lu <luangruo@yahoo.com>
Sat, 12 Feb 2022 12:28:20 +0000 (20:28 +0800)
* src/xmenu.c (popup_get_selection): Only claim cookie if new
event will be generated.
(create_and_show_popup_menu): Ungrab XI2 device, then
immediately set core grab on the frame's edit widget window.

* src/xterm.c (handle_one_xevent): Use x_any_window_to_frame to
find exit event frame if a popup menu is active.

src/xmenu.c
src/xterm.c

index eaec6efc26ca8fa3788f2c6a525662ccaaed587c..28fafa108daa5932525b15e9c3da128181f5e6a2 100644 (file)
@@ -299,16 +299,16 @@ popup_get_selection (XEvent *initial_event, struct x_display_info *dpyinfo,
               && event.xgeneric.display == dpyinfo->display
               && event.xgeneric.extension == dpyinfo->xi2_opcode)
        {
-         if (!event.xcookie.data
-             && XGetEventData (dpyinfo->display, &event.xcookie))
-           cookie_claimed_p = true;
-
          if (event.xcookie.data)
            {
              switch (event.xgeneric.evtype)
                {
                case XI_ButtonRelease:
                  {
+                   if (!event.xcookie.data
+                       && XGetEventData (dpyinfo->display, &event.xcookie))
+                     cookie_claimed_p = true;
+
                    xev = (XIDeviceEvent *) event.xcookie.data;
                    device = xi_device_from_id (dpyinfo, xev->deviceid);
 
@@ -358,6 +358,10 @@ popup_get_selection (XEvent *initial_event, struct x_display_info *dpyinfo,
                  {
                    KeySym keysym;
 
+                   if (!event.xcookie.data
+                       && XGetEventData (dpyinfo->display, &event.xcookie))
+                     cookie_claimed_p = true;
+
                    xev = (XIDeviceEvent *) event.xcookie.data;
 
                    copy.xkey.type = KeyPress;
@@ -1578,26 +1582,55 @@ create_and_show_popup_menu (struct frame *f, widget_value *first_wv,
   XtSetArg (av[ac], (char *) XtNgeometry, 0); ac++;
   XtSetValues (menu, av, ac);
 
-#if defined HAVE_XINPUT2 && defined USE_X_TOOLKIT
+#if defined HAVE_XINPUT2
   struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
-  /* Clear the XI2 grab so lwlib can set a core grab.  */
+  bool any_xi_grab_p = false;
+
+  /* Clear the XI2 grab, and if any XI2 grab was set, place a core
+     grab on the frame's edit widget.  */
+
+  if (dpyinfo->supports_xi2)
+    XGrabServer (dpyinfo->display);
 
   if (dpyinfo->num_devices)
     {
       for (int i = 0; i < dpyinfo->num_devices; ++i)
        {
-#ifndef USE_MOTIF
          if (dpyinfo->devices[i].grab)
-#endif
-           XIUngrabDevice (dpyinfo->display, dpyinfo->devices[i].device_id,
-                           CurrentTime);
+           {
+             any_xi_grab_p = true;
+             dpyinfo->devices[i].grab = 0;
+
+             XIUngrabDevice (dpyinfo->display,
+                             dpyinfo->devices[i].device_id,
+                             CurrentTime);
+           }
        }
     }
+
+  if (any_xi_grab_p)
+    XGrabPointer (dpyinfo->display,
+                 FRAME_X_WINDOW (f),
+                 False, (PointerMotionMask
+                         | PointerMotionHintMask
+                         | ButtonReleaseMask
+                         | ButtonPressMask),
+                 GrabModeSync, GrabModeAsync,
+                 None, None, CurrentTime);
+
+  if (dpyinfo->supports_xi2)
+    XUngrabServer (dpyinfo->display);
 #endif
 
   /* Display the menu.  */
   lw_popup_menu (menu, &dummy);
   popup_activated_flag = 1;
+
+#ifdef HAVE_XINPUT2
+  if (any_xi_grab_p)
+    XAllowEvents (dpyinfo->display, AsyncPointer, CurrentTime);
+#endif
+
   x_activate_timeout_atimer ();
 
   {
index 22c143807675d9480f70f45d5a4fd72355433c82..20ea74682023738e2890d86e774e0cbe31e01fc1 100644 (file)
@@ -10877,6 +10877,17 @@ handle_one_xevent (struct x_display_info *dpyinfo,
            ev.window = leave->event;
            any = x_top_window_to_frame (dpyinfo, leave->event);
 
+           /* This allows us to catch LeaveNotify events generated by
+              popup menu grabs.  FIXME: this is right when there is a
+              focus menu, but implicit focus tracking can get screwed
+              up if we get this and no XI_Enter event later.   */
+
+#ifdef USE_X_TOOLKIT
+           if (popup_activated ()
+               && leave->mode == XINotifyPassiveUngrab)
+             any = x_any_window_to_frame (dpyinfo, leave->event);
+#endif
+
            /* One problem behind the design of XInput 2 scrolling is
               that valuators are not unique to each window, but only
               the window that has grabbed the valuator's device or