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.
This commit is contained in:
Po Lu 2022-04-23 19:34:46 +08:00
parent 132fa71615
commit 759d337b0d
2 changed files with 104 additions and 22 deletions

View file

@ -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;

View file

@ -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 ? &copy : (XEvent *) event);
#else
XtDispatchEvent ((XEvent *) event);