From be06b40ebdd6cfbd23558ce210a8a7ad2b4c7a40 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sat, 12 Feb 2022 20:25:00 +0800 Subject: [PATCH] Fix entry and exit events with XI2 grabs on X toolkit menus * 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 | 53 +++++++++++++++++++++++++++++++++++++++++++---------- src/xterm.c | 11 +++++++++++ 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/src/xmenu.c b/src/xmenu.c index eaec6efc26c..28fafa108da 100644 --- a/src/xmenu.c +++ b/src/xmenu.c @@ -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 (); { diff --git a/src/xterm.c b/src/xterm.c index 22c14380767..20ea7468202 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -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 -- 2.39.5