From b432fb6c86b922bf1e8bfa8ae59e0dc80cb37eb0 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Mon, 7 Feb 2022 16:22:06 +0800 Subject: [PATCH] Make menus work better on X toolkit builds with XInput 2 * src/xmenu.c (popup_get_selection): Translate some important XI2 events into events the toolkit can understand. (x_activate_menubar): (create_and_show_popup_menu): Clear grab regardless of reported status on Motif. * src/xterm.c (xi_device_from_id): Export function. * src/xterm.h: Update prototypes. --- src/xmenu.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/xterm.c | 2 +- src/xterm.h | 4 ++ 3 files changed, 124 insertions(+), 4 deletions(-) diff --git a/src/xmenu.c b/src/xmenu.c index 9e4e6b62fce..745a80ade1b 100644 --- a/src/xmenu.c +++ b/src/xmenu.c @@ -52,6 +52,7 @@ along with GNU Emacs. If not, see . */ #endif #ifdef HAVE_XINPUT2 +#include #include #endif @@ -240,18 +241,25 @@ popup_get_selection (XEvent *initial_event, struct x_display_info *dpyinfo, LWLIB_ID id, bool do_timers) { XEvent event; + XEvent copy; +#ifdef HAVE_XINPUT2 + bool cookie_claimed_p = false; + XIDeviceEvent *xev; + struct xi_device_t *device; +#endif while (popup_activated_flag) { if (initial_event) { - event = *initial_event; + copy = event = *initial_event; initial_event = 0; } else { if (do_timers) x_menu_wait_for_event (0); XtAppNextEvent (Xt_app_con, &event); + copy = event; } /* Make sure we don't consider buttons grabbed after menu goes. @@ -271,6 +279,7 @@ popup_get_selection (XEvent *initial_event, struct x_display_info *dpyinfo, so Motif thinks this is the case. */ event.xbutton.state = 0; #endif + copy = event; } /* Pop down on C-g and Escape. */ else if (event.type == KeyPress @@ -281,9 +290,110 @@ popup_get_selection (XEvent *initial_event, struct x_display_info *dpyinfo, if ((keysym == XK_g && (event.xkey.state & ControlMask) != 0) || keysym == XK_Escape) /* Any escape, ignore modifiers. */ popup_activated_flag = 0; + + copy = event; } +#ifdef HAVE_XINPUT2 + else if (event.type == GenericEvent + && dpyinfo->supports_xi2 + && 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: + { + xev = (XIDeviceEvent *) event.xcookie.data; + device = xi_device_from_id (dpyinfo, xev->deviceid); + + dpyinfo->grabbed &= ~(1 << xev->detail); + device->grab &= ~(1 << xev->detail); + + copy.xbutton.type = ButtonRelease; + copy.xbutton.serial = xev->serial; + copy.xbutton.send_event = xev->send_event; + copy.xbutton.display = xev->display; + copy.xbutton.window = xev->event; + copy.xbutton.root = xev->root; + copy.xbutton.subwindow = xev->child; + copy.xbutton.time = xev->time; + copy.xbutton.x = lrint (xev->event_x); + copy.xbutton.y = lrint (xev->event_y); + copy.xbutton.x_root = lrint (xev->root_x); + copy.xbutton.y_root = lrint (xev->root_y); + copy.xbutton.state = xev->mods.effective; + copy.xbutton.button = xev->detail; + copy.xbutton.same_screen = True; + +#ifdef USE_MOTIF /* Pretending that the event came from a + Btn1Down seems the only way to convince Motif to + activate its callbacks; setting the XmNmenuPost + isn't working. --marcus@sysc.pdx.edu. */ + copy.xbutton.button = 1; + /* Motif only pops down menus when no Ctrl, Alt or Mod + key is pressed and the button is released. So reset key state + so Motif thinks this is the case. */ + copy.xbutton.state = 0; +#endif + + if (xev->buttons.mask_len) + { + if (XIMaskIsSet (xev->buttons.mask, 1)) + copy.xbutton.state |= Button1Mask; + if (XIMaskIsSet (xev->buttons.mask, 2)) + copy.xbutton.state |= Button2Mask; + if (XIMaskIsSet (xev->buttons.mask, 3)) + copy.xbutton.state |= Button3Mask; + } + + break; + } + case XI_KeyPress: + { + KeySym keysym; + + xev = (XIDeviceEvent *) event.xcookie.data; + + copy.xkey.type = KeyPress; + copy.xkey.serial = xev->serial; + copy.xkey.send_event = xev->send_event; + copy.xkey.display = xev->display; + copy.xkey.window = xev->event; + copy.xkey.root = xev->root; + copy.xkey.subwindow = xev->child; + copy.xkey.time = xev->time; + copy.xkey.x = lrint (xev->event_x); + copy.xkey.y = lrint (xev->event_y); + copy.xkey.x_root = lrint (xev->root_x); + copy.xkey.y_root = lrint (xev->root_y); + copy.xkey.state = xev->mods.effective; + copy.xkey.keycode = xev->detail; + copy.xkey.same_screen = True; + + keysym = XLookupKeysym (©.xkey, 0); + + if ((keysym == XK_g + && (copy.xkey.state & ControlMask) != 0) + || keysym == XK_Escape) /* Any escape, ignore modifiers. */ + popup_activated_flag = 0; + + break; + } + } + } + } - x_dispatch_event (&event, event.xany.display); + if (cookie_claimed_p) + XFreeEventData (dpyinfo->display, &event.xcookie); +#endif + + x_dispatch_event (©, copy.xany.display); } } @@ -458,7 +568,9 @@ x_activate_menubar (struct frame *f) { 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); } @@ -1465,7 +1577,8 @@ create_and_show_popup_menu (struct frame *f, widget_value *first_wv, /* Don't allow any geometry request from the user. */ XtSetArg (av[ac], (char *) XtNgeometry, 0); ac++; XtSetValues (menu, av, ac); -#if defined HAVE_XINPUT2 && defined USE_LUCID + +#if defined HAVE_XINPUT2 && defined USE_X_TOOLKIT struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f); /* Clear the XI2 grab so lwlib can set a core grab. */ @@ -1473,12 +1586,15 @@ create_and_show_popup_menu (struct frame *f, widget_value *first_wv, { 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); } } #endif + /* Display the menu. */ lw_popup_menu (menu, &dummy); popup_activated_flag = 1; diff --git a/src/xterm.c b/src/xterm.c index 49fc2b1bb74..940ee347d5a 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -698,7 +698,7 @@ x_get_scroll_valuator_delta (struct x_display_info *dpyinfo, int device_id, return DBL_MAX; } -static struct xi_device_t * +struct xi_device_t * xi_device_from_id (struct x_display_info *dpyinfo, int deviceid) { for (int i = 0; i < dpyinfo->num_devices; ++i) diff --git a/src/xterm.h b/src/xterm.h index d3678054a82..63956fd6434 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -1444,6 +1444,10 @@ extern void x_session_close (void); extern struct input_event xg_pending_quit_event; #endif +#ifdef HAVE_XINPUT2 +struct xi_device_t *xi_device_from_id (struct x_display_info *, int); +#endif + /* Is the frame embedded into another application? */ #define FRAME_X_EMBEDDED_P(f) (FRAME_X_OUTPUT(f)->explicit_parent != 0) -- 2.39.5