From 759d337b0d05f3164bf90dd980a079cd412f9de1 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sat, 23 Apr 2022 19:34:46 +0800 Subject: [PATCH] Fix key navigation of Lucid menus on XI2 * src/xmenu.c (popup_get_selection): Fix cookie claiming of input extension events. (Fx_menu_bar_open_internal): Use right timestamps on XI2. * src/xterm.c (handle_one_xevent): Dispatch XI2 key events via Xt when popup is active. --- src/xmenu.c | 47 +++++++++++++++++++++++++------ src/xterm.c | 79 +++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 104 insertions(+), 22 deletions(-) diff --git a/src/xmenu.c b/src/xmenu.c index 94cd9dab69b..316dacee5bf 100644 --- a/src/xmenu.c +++ b/src/xmenu.c @@ -365,16 +365,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); @@ -424,10 +424,6 @@ 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; @@ -473,6 +469,9 @@ DEFUN ("x-menu-bar-open-internal", Fx_menu_bar_open_internal, Sx_menu_bar_open_i { XEvent ev; struct frame *f = decode_window_system_frame (frame); +#if defined USE_X_TOOLKIT && defined HAVE_XINPUT2 + struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f); +#endif Widget menubar; block_input (); @@ -485,12 +484,44 @@ DEFUN ("x-menu-bar-open-internal", Fx_menu_bar_open_internal, Sx_menu_bar_open_i Window child; bool error_p = false; +#if defined USE_X_TOOLKIT && defined HAVE_XINPUT2 + /* Clear the XI2 grab so Motif or lwlib can set a core grab. + Otherwise some versions of Motif will emit a warning and hang, + and lwlib will fail to destroy the menu window. */ + + if (dpyinfo->supports_xi2 + && xi_frame_selected_for (f, XI_ButtonPress)) + { + for (int i = 0; i < dpyinfo->num_devices; ++i) + { + /* The keyboard grab matters too, in this specific + case. */ +#ifndef USE_LUCID + if (dpyinfo->devices[i].grab) +#endif + { + XIUngrabDevice (dpyinfo->display, + dpyinfo->devices[i].device_id, + CurrentTime); + dpyinfo->devices[i].grab = 0; + } + } + } +#endif + x_catch_errors (FRAME_X_DISPLAY (f)); memset (&ev, 0, sizeof ev); ev.xbutton.display = FRAME_X_DISPLAY (f); ev.xbutton.window = XtWindow (menubar); ev.xbutton.root = FRAME_DISPLAY_INFO (f)->root_window; +#ifndef HAVE_XINPUT2 ev.xbutton.time = XtLastTimestampProcessed (FRAME_X_DISPLAY (f)); +#else + ev.xbutton.time = ((dpyinfo->supports_xi2 + && xi_frame_selected_for (f, XI_KeyPress)) + ? dpyinfo->last_user_time + : XtLastTimestampProcessed (dpyinfo->display)); +#endif ev.xbutton.button = Button1; ev.xbutton.x = ev.xbutton.y = FRAME_MENUBAR_HEIGHT (f) / 2; ev.xbutton.same_screen = True; diff --git a/src/xterm.c b/src/xterm.c index 1b1074b2f40..8b813210b76 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -13833,10 +13833,10 @@ handle_one_xevent (struct x_display_info *dpyinfo, XEvent configureEvent; XEvent next_event; Lisp_Object coding; -#if defined USE_MOTIF && defined HAVE_XINPUT2 - /* Some XInput 2 events are important for Motif menu bars to work - correctly, so they must be translated into core events before - being passed to XtDispatchEvent. */ +#if defined USE_X_TOOLKIT && defined HAVE_XINPUT2 + /* Some XInput 2 events are important for Motif and Lucid menu bars + to work correctly, so they must be translated into core events + before being passed to XtDispatchEvent. */ bool use_copy = false; XEvent copy; #elif defined USE_GTK && !defined HAVE_GTK3 && defined HAVE_XINPUT2 @@ -17746,7 +17746,41 @@ handle_one_xevent (struct x_display_info *dpyinfo, #if defined (USE_X_TOOLKIT) || defined (USE_GTK) /* Dispatch XI_KeyPress events when in menu. */ if (popup_activated ()) - goto XI_OTHER; + { +#ifdef USE_LUCID + /* This makes key navigation work inside menus. */ + use_copy = true; + copy.xkey.type = KeyPress; + copy.xkey.serial = xev->serial; + copy.xkey.send_event = xev->send_event; + copy.xkey.display = dpyinfo->display; + copy.xkey.window = xev->event; + copy.xkey.root = xev->root; + copy.xkey.subwindow = xev->child; + copy.xkey.time = xev->time; + copy.xkey.state = ((xev->mods.effective & ~(1 << 13 | 1 << 14)) + | (xev->group.effective << 13)); + + 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); + + if (xev->buttons.mask_len) + { + if (XIMaskIsSet (xev->buttons.mask, 1)) + copy.xkey.state |= Button1Mask; + if (XIMaskIsSet (xev->buttons.mask, 2)) + copy.xkey.state |= Button2Mask; + if (XIMaskIsSet (xev->buttons.mask, 3)) + copy.xkey.state |= Button3Mask; + } + + copy.xkey.keycode = xev->detail; + copy.xkey.same_screen = True; +#endif + goto XI_OTHER; + } #endif x_display_set_last_user_time (dpyinfo, xev->time); @@ -18193,7 +18227,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, #endif case XI_KeyRelease: -#if defined HAVE_X_I18N || defined USE_GTK +#if defined HAVE_X_I18N || defined USE_GTK || defined USE_LUCID { XKeyPressedEvent xkey; @@ -18229,14 +18263,31 @@ handle_one_xevent (struct x_display_info *dpyinfo, xkey.keycode = xev->detail; xkey.same_screen = True; +#ifdef USE_LUCID + if (!popup_activated ()) + { +#endif #ifdef HAVE_X_I18N - if (x_filter_event (dpyinfo, (XEvent *) &xkey)) - *finish = X_EVENT_DROP; -#else - f = x_any_window_to_frame (xkey->event); + if (x_filter_event (dpyinfo, (XEvent *) &xkey)) + *finish = X_EVENT_DROP; +#elif defined USE_GTK + f = x_any_window_to_frame (xkey->event); - if (f && xg_filter_key (f, event)) - *finish = X_EVENT_DROP; + if (f && xg_filter_key (f, event)) + *finish = X_EVENT_DROP; +#endif +#ifdef USE_LUCID + } + else + { + /* FIXME: the Lucid menu bar pops down upon any key + release event, so we don't dispatch these events + at all, which doesn't seem to be the right + solution. + + use_copy = true; + copy.xkey = xkey; */ + } #endif } #endif @@ -19009,12 +19060,12 @@ handle_one_xevent (struct x_display_info *dpyinfo, { /* Ignore some obviously bogus ConfigureNotify events that other clients have been known to send Emacs. - (bug#54051)*/ + (bug#54051) */ if (event->type != ConfigureNotify || (event->xconfigure.width != 0 && event->xconfigure.height != 0)) { -#if defined USE_MOTIF && defined HAVE_XINPUT2 +#if defined USE_X_TOOLKIT && defined HAVE_XINPUT2 XtDispatchEvent (use_copy ? © : (XEvent *) event); #else XtDispatchEvent ((XEvent *) event); -- 2.39.5