From 16b8948d79e27a37d223eb171c21e6b78d8b5a7a Mon Sep 17 00:00:00 2001 From: Po Lu Date: Mon, 8 Aug 2022 11:44:53 +0800 Subject: [PATCH] Improve handling of pointer focus under the input extension * src/xfns.c (x_relative_mouse_position) (Fx_mouse_absolute_pixel_position, compute_tip_xy): Use x_query_pointer instead of XQueryPointer. * src/xterm.c (xi_populate_device_from_info): Set new attachment field. (xi_handle_focus_change): Set client pointer. (xi_focus_handle_for_device, xi_handle_interaction): Use attached keyboard device to handle focus. (x_query_pointer): New function. (XTmouse_position, x_scroll_bar_report_motion) (x_horizontal_scroll_bar_report_motion, handle_one_xevent): Use x_query_pointer instead of XQueryPointer. (x_term_init): Initialize client pointer device. * src/xterm.h (struct xi_device_t): New field `attachment'. (struct x_display_info): New field `client_pointer_device'. --- src/xfns.c | 38 ++++++------ src/xterm.c | 171 ++++++++++++++++++++++++++++++++++++++++++---------- src/xterm.h | 31 +++++++++- 3 files changed, 186 insertions(+), 54 deletions(-) diff --git a/src/xfns.c b/src/xfns.c index 672097c0d80..2845ecca6a9 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -609,24 +609,24 @@ x_relative_mouse_position (struct frame *f, int *x, int *y) block_input (); - XQueryPointer (FRAME_X_DISPLAY (f), - FRAME_DISPLAY_INFO (f)->root_window, + x_query_pointer (FRAME_X_DISPLAY (f), + FRAME_DISPLAY_INFO (f)->root_window, - /* The root window which contains the pointer. */ - &root, + /* The root window which contains the pointer. */ + &root, - /* Window pointer is on, not used */ - &dummy_window, + /* Window pointer is on, not used */ + &dummy_window, - /* The position on that root window. */ - x, y, + /* The position on that root window. */ + x, y, - /* x/y in dummy_window coordinates, not used. */ - &dummy, &dummy, + /* x/y in dummy_window coordinates, not used. */ + &dummy, &dummy, - /* Modifier keys and pointer buttons, about which - we don't care. */ - (unsigned int *) &dummy); + /* Modifier keys and pointer buttons, about which + we don't care. */ + (unsigned int *) &dummy); XTranslateCoordinates (FRAME_X_DISPLAY (f), @@ -6823,10 +6823,10 @@ selected frame's display. */) return Qnil; block_input (); - XQueryPointer (FRAME_X_DISPLAY (f), - FRAME_DISPLAY_INFO (f)->root_window, - &root, &dummy_window, &x, &y, &dummy, &dummy, - (unsigned int *) &dummy); + x_query_pointer (FRAME_X_DISPLAY (f), + FRAME_DISPLAY_INFO (f)->root_window, + &root, &dummy_window, &x, &y, &dummy, &dummy, + (unsigned int *) &dummy); unblock_input (); return Fcons (make_fixnum (x), make_fixnum (y)); @@ -8382,8 +8382,8 @@ compute_tip_xy (struct frame *f, Lisp_Object parms, Lisp_Object dx, Lisp_Object frame, attributes, monitor, geometry; block_input (); - XQueryPointer (FRAME_X_DISPLAY (f), FRAME_DISPLAY_INFO (f)->root_window, - &root, &child, root_x, root_y, &win_x, &win_y, &pmask); + x_query_pointer (FRAME_X_DISPLAY (f), FRAME_DISPLAY_INFO (f)->root_window, + &root, &child, root_x, root_y, &win_x, &win_y, &pmask); unblock_input (); XSETFRAME (frame, f); diff --git a/src/xterm.c b/src/xterm.c index 54bf6566572..c1f74f68666 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -5291,6 +5291,7 @@ xi_populate_device_from_info (struct xi_device_t *xi_device, xi_device->direct_p = false; #endif xi_device->name = build_string (device->name); + xi_device->attachment = device->attachment; for (c = 0; c < device->num_classes; ++c) { @@ -12474,7 +12475,9 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, focus is switched to a given frame. This situation is handled by keeping track of each master device's focus frame, the time of the last interaction with that frame, and always keeping the focus on - the most recently selected frame. */ + the most recently selected frame. We also use the pointer of the + device that is keeping the current frame focused in functions like + `mouse-position'. */ static void xi_handle_focus_change (struct x_display_info *dpyinfo) @@ -12493,6 +12496,8 @@ xi_handle_focus_change (struct x_display_info *dpyinfo) new = NULL; time = 0; + dpyinfo->client_pointer_device = -1; + for (i = 0; i < dpyinfo->num_devices; ++i) { device = &dpyinfo->devices[i]; @@ -12503,6 +12508,14 @@ xi_handle_focus_change (struct x_display_info *dpyinfo) new = device->focus_frame; time = device->focus_frame_time; source = device; + + /* Use this device for future calls to `mouse-position' etc. + If it is a keyboard, use its attached pointer. */ + + if (device->use == XIMasterKeyboard) + dpyinfo->client_pointer_device = device->attachment; + else + dpyinfo->client_pointer_device = device->device_id; } if (device->focus_implicit_frame @@ -12511,6 +12524,14 @@ xi_handle_focus_change (struct x_display_info *dpyinfo) new = device->focus_implicit_frame; time = device->focus_implicit_time; source = device; + + /* Use this device for future calls to `mouse-position' etc. + If it is a keyboard, use its attached pointer. */ + + if (device->use == XIMasterKeyboard) + dpyinfo->client_pointer_device = device->attachment; + else + dpyinfo->client_pointer_device = device->device_id; } } @@ -12601,6 +12622,12 @@ xi_focus_handle_for_device (struct x_display_info *dpyinfo, if (!event->focus) break; + if (device->use == XIMasterPointer) + device = xi_device_from_id (dpyinfo, device->attachment); + + if (!device) + break; + device->focus_implicit_frame = mentioned_frame; device->focus_implicit_time = event->time; break; @@ -12609,6 +12636,12 @@ xi_focus_handle_for_device (struct x_display_info *dpyinfo, if (!event->focus) break; + if (device->use == XIMasterPointer) + device = xi_device_from_id (dpyinfo, device->attachment); + + if (!device) + break; + device->focus_implicit_frame = NULL; break; } @@ -12646,6 +12679,13 @@ xi_handle_interaction (struct x_display_info *dpyinfo, { bool change; + /* If DEVICE is a pointer, use its attached keyboard device. */ + if (device->use == XIMasterPointer) + device = xi_device_from_id (dpyinfo, device->attachment); + + if (!device) + return; + change = false; if (device->focus_frame == f) @@ -13042,6 +13082,68 @@ get_keysym_name (int keysym) return value; } +/* Like XQueryPointer, but always use the right client pointer + device. */ + +Bool +x_query_pointer (Display *dpy, Window w, Window *root_return, + Window *child_return, int *root_x_return, + int *root_y_return, int *win_x_return, + int *win_y_return, unsigned int *mask_return) +{ + struct x_display_info *dpyinfo; + Bool rc; + bool had_errors; +#ifdef HAVE_XINPUT2 + XIModifierState modifiers; + XIButtonState buttons; + XIGroupState group; /* Unused. */ + double root_x, root_y, win_x, win_y; + unsigned int state; +#endif + + dpyinfo = x_display_info_for_display (dpy); +#ifdef HAVE_XINPUT2 + if (dpyinfo && dpyinfo->client_pointer_device != -1) + { + /* Catch errors caused by the device going away. This is not + very expensive, since XIQueryPointer will sync anyway. */ + x_catch_errors (dpy); + rc = XIQueryPointer (dpyinfo->display, + dpyinfo->client_pointer_device, + w, root_return, child_return, + &root_x, &root_y, &win_x, &win_y, + &buttons, &modifiers, &group); + had_errors = x_had_errors_p (dpy); + x_uncatch_errors_after_check (); + + if (had_errors) + rc = XQueryPointer (dpyinfo->display, w, root_return, + child_return, root_x_return, + root_y_return, win_x_return, + win_y_return, mask_return); + else + { + state = 0; + + xi_convert_button_state (&buttons, &state); + *mask_return = state | modifiers.effective; + + *root_x_return = lrint (root_x); + *root_y_return = lrint (root_y); + *win_x_return = lrint (win_x); + *win_y_return = lrint (win_y); + } + } + else +#endif + rc = XQueryPointer (dpy, w, root_return, child_return, + root_x_return, root_y_return, win_x_return, + win_y_return, mask_return); + + return rc; +} + /* Mouse clicks and mouse movement. Rah. Formerly, we used PointerMotionHintMask (in standard_event_mask) @@ -13308,20 +13410,20 @@ XTmouse_position (struct frame **fp, int insist, Lisp_Object *bar_window, dpyinfo->last_mouse_scroll_bar = NULL; /* Figure out which root window we're on. */ - XQueryPointer (FRAME_X_DISPLAY (*fp), - DefaultRootWindow (FRAME_X_DISPLAY (*fp)), - /* The root window which contains the pointer. */ - &root, - /* Trash which we can't trust if the pointer is on - a different screen. */ - &dummy_window, - /* The position on that root window. */ - &root_x, &root_y, - /* More trash we can't trust. */ - &dummy, &dummy, - /* Modifier keys and pointer buttons, about which - we don't care. */ - (unsigned int *) &dummy); + x_query_pointer (FRAME_X_DISPLAY (*fp), + DefaultRootWindow (FRAME_X_DISPLAY (*fp)), + /* The root window which contains the pointer. */ + &root, + /* Trash which we can't trust if the pointer is on + a different screen. */ + &dummy_window, + /* The position on that root window. */ + &root_x, &root_y, + /* More trash we can't trust. */ + &dummy, &dummy, + /* Modifier keys and pointer buttons, about which + we don't care. */ + (unsigned int *) &dummy); /* Now we have a position on the root; find the innermost window containing the pointer. */ @@ -15894,17 +15996,17 @@ x_scroll_bar_report_motion (struct frame **fp, Lisp_Object *bar_window, /* Get the mouse's position relative to the scroll bar window, and report that. */ - if (XQueryPointer (FRAME_X_DISPLAY (f), w, + if (x_query_pointer (FRAME_X_DISPLAY (f), w, - /* Root, child, root x and root y. */ - &dummy_window, &dummy_window, - &dummy_coord, &dummy_coord, + /* Root, child, root x and root y. */ + &dummy_window, &dummy_window, + &dummy_coord, &dummy_coord, - /* Position relative to scroll bar. */ - &win_x, &win_y, + /* Position relative to scroll bar. */ + &win_x, &win_y, - /* Mouse buttons and modifier keys. */ - &dummy_mask)) + /* Mouse buttons and modifier keys. */ + &dummy_mask)) { int top_range = VERTICAL_SCROLL_BAR_TOP_RANGE (f, bar->height); @@ -15963,17 +16065,17 @@ x_horizontal_scroll_bar_report_motion (struct frame **fp, Lisp_Object *bar_windo /* Get the mouse's position relative to the scroll bar window, and report that. */ - if (XQueryPointer (FRAME_X_DISPLAY (f), w, + if (x_query_pointer (FRAME_X_DISPLAY (f), w, - /* Root, child, root x and root y. */ - &dummy_window, &dummy_window, - &dummy_coord, &dummy_coord, + /* Root, child, root x and root y. */ + &dummy_window, &dummy_window, + &dummy_coord, &dummy_coord, - /* Position relative to scroll bar. */ - &win_x, &win_y, + /* Position relative to scroll bar. */ + &win_x, &win_y, - /* Mouse buttons and modifier keys. */ - &dummy_mask)) + /* Mouse buttons and modifier keys. */ + &dummy_mask)) { int left_range = HORIZONTAL_SCROLL_BAR_LEFT_RANGE (f, bar->width); @@ -22202,7 +22304,10 @@ handle_one_xevent (struct x_display_info *dpyinfo, if (info) { if (device && info->enabled) - device->use = info->use; + { + device->use = info->use; + device->attachment = info->attachment; + } else if (device) disabled[n_disabled++] = hev->info[i].deviceid; @@ -27816,6 +27921,8 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name) int minor = 0; #endif + dpyinfo->client_pointer_device = -1; + if (XQueryExtension (dpyinfo->display, "XInputExtension", &dpyinfo->xi2_opcode, &xi_first_event, &xi_first_error)) diff --git a/src/xterm.h b/src/xterm.h index 7be0f2ede65..e97f3d4c831 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -249,6 +249,10 @@ struct xi_device_t /* Whether or not the device is grabbed and its use. */ int grab, use; + /* The attached device. Only valid if USE is some kind of master + device. */ + int attachment; + #ifdef HAVE_XINPUT2_2 /* Whether or not this device is a direct touch device. */ bool direct_p; @@ -708,13 +712,27 @@ struct x_display_info #ifdef HAVE_XINPUT2 bool supports_xi2; + + /* The minor version of the input extension. (Major is always + 2.x.) */ int xi2_version; + + /* The generic event opcode of XI2 events. */ int xi2_opcode; + /* The number of devices on this display known to Emacs. */ int num_devices; + + /* Array of all input extension devices on this display known to + Emacs. */ struct xi_device_t *devices; + /* Pending keystroke time. */ Time pending_keystroke_time; + + /* Pending keystroke source. If a core KeyPress event arrives with + the same timestamp as pending_keystroke_time, it will be treated + as originating from this device. */ int pending_keystroke_source; #if defined USE_GTK && !defined HAVE_GTK3 @@ -724,6 +742,10 @@ struct x_display_info input method) core key event. */ bool pending_keystroke_time_special_p; #endif + + /* The client pointer. We keep a record client-side to avoid + calling XISetClientPointer all the time. */ + int client_pointer_device; #endif #ifdef HAVE_XKB @@ -1599,11 +1621,14 @@ extern Lisp_Object x_cr_export_frames (Lisp_Object, cairo_surface_type_t); #ifdef HAVE_XRENDER extern void x_xrender_color_from_gc_background (struct frame *, GC, XRenderColor *, bool); -extern void x_xr_ensure_picture (struct frame *f); -extern void x_xr_apply_ext_clip (struct frame *f, GC gc); -extern void x_xr_reset_ext_clip (struct frame *f); +extern void x_xr_ensure_picture (struct frame *); +extern void x_xr_apply_ext_clip (struct frame *, GC); +extern void x_xr_reset_ext_clip (struct frame *); #endif +extern Bool x_query_pointer (Display *, Window, Window *, Window *, int *, + int *, int *, int *, unsigned int *); + #ifdef HAVE_GTK3 extern void x_scroll_bar_configure (GdkEvent *); #endif -- 2.39.2