Improve input extension focus handling with multiple master devices

* src/xterm.c (x_cache_xi_devices): Initialize device fields to
0.
(xi_handle_focus_change, xi_focus_handle_for_device)
(xi_handle_delete_frame): New functions; store focus information
per-device instead.
(x_detect_focus_change): Handle GenericEvents that way instead.
(handle_one_xevent): Don't cache XI devices on DeviceChanged.
(x_free_frame_resources): Clear any frame focus information.

* src/xterm.h (struct xi_device_t): New fields for focus
tracking.  Add comments describing fields.
This commit is contained in:
Po Lu 2022-08-05 10:18:18 +08:00
parent 8ea7506d72
commit 0dbe0fd410
2 changed files with 226 additions and 45 deletions

View file

@ -5394,7 +5394,7 @@ xi_populate_device_from_info (struct xi_device_t *xi_device,
static void
x_cache_xi_devices (struct x_display_info *dpyinfo)
{
int ndevices, actual_devices;
int ndevices, actual_devices, i;
XIDeviceInfo *infos;
actual_devices = 0;
@ -5411,9 +5411,9 @@ x_cache_xi_devices (struct x_display_info *dpyinfo)
return;
}
dpyinfo->devices = xmalloc (sizeof *dpyinfo->devices * ndevices);
dpyinfo->devices = xzalloc (sizeof *dpyinfo->devices * ndevices);
for (int i = 0; i < ndevices; ++i)
for (i = 0; i < ndevices; ++i)
{
if (infos[i].enabled)
xi_populate_device_from_info (&dpyinfo->devices[actual_devices++],
@ -12446,6 +12446,178 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction,
return unbind_to (base, Qnil);
}
#ifdef HAVE_XINPUT2
/* Since the input extension assigns a keyboard focus to each master
device, there is no longer a 1:1 correspondence between the
selected frame and the focus frame immediately after the keyboard
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. */
static void
xi_handle_focus_change (struct x_display_info *dpyinfo)
{
struct input_event ie;
struct frame *focus, *new;
struct xi_device_t *device, *source;
ptrdiff_t i;
Time time;
#ifdef USE_GTK
struct x_output *output;
GtkWidget *widget;
#endif
focus = dpyinfo->x_focus_frame;
new = NULL;
time = 0;
for (i = 0; i < dpyinfo->num_devices; ++i)
{
device = &dpyinfo->devices[i];
if (device->focus_frame
&& device->focus_frame_time > time)
{
new = device->focus_frame;
time = device->focus_frame_time;
source = device;
}
if (device->focus_implicit_frame
&& device->focus_implicit_time > time)
{
new = device->focus_implicit_frame;
time = device->focus_implicit_time;
source = device;
}
}
if (new != focus && focus)
{
#ifdef HAVE_X_I18N
if (FRAME_XIC (focus))
XUnsetICFocus (FRAME_XIC (focus));
#endif
#ifdef USE_GTK
output = FRAME_X_OUTPUT (focus);
if (x_gtk_use_native_input)
{
gtk_im_context_focus_out (output->im_context);
gtk_im_context_set_client_window (output->im_context,
NULL);
}
#endif
EVENT_INIT (ie);
ie.kind = FOCUS_OUT_EVENT;
XSETFRAME (ie.frame_or_window, focus);
kbd_buffer_store_event (&ie);
}
if (new != focus && new)
{
#ifdef HAVE_X_I18N
if (FRAME_XIC (new))
XSetICFocus (FRAME_XIC (new));
#endif
#ifdef USE_GTK
output = FRAME_X_OUTPUT (new);
if (x_gtk_use_native_input)
{
widget = FRAME_GTK_OUTER_WIDGET (new);
gtk_im_context_focus_in (output->im_context);
gtk_im_context_set_client_window (output->im_context,
gtk_widget_get_window (widget));
}
#endif
EVENT_INIT (ie);
ie.kind = FOCUS_IN_EVENT;
ie.device = source->name;
XSETFRAME (ie.frame_or_window, new);
kbd_buffer_store_event (&ie);
}
x_new_focus_frame (dpyinfo, new);
}
static void
xi_focus_handle_for_device (struct x_display_info *dpyinfo,
struct frame *mentioned_frame,
XIEvent *base_event)
{
struct xi_device_t *device;
XIEnterEvent *event;
/* XILeaveEvent, XIFocusInEvent, etc are just synonyms for
XIEnterEvent. */
event = (XIEnterEvent *) base_event;
device = xi_device_from_id (dpyinfo, event->deviceid);
if (!device)
return;
switch (event->evtype)
{
case XI_FocusIn:
device->focus_frame = mentioned_frame;
device->focus_frame_time = event->time;
break;
case XI_FocusOut:
device->focus_frame = NULL;
break;
case XI_Enter:
if (!event->focus)
break;
device->focus_implicit_frame = mentioned_frame;
device->focus_implicit_time = event->time;
break;
case XI_Leave:
if (!event->focus)
break;
device->focus_implicit_frame = NULL;
break;
}
xi_handle_focus_change (dpyinfo);
}
static void
xi_handle_delete_frame (struct x_display_info *dpyinfo,
struct frame *f)
{
struct xi_device_t *device;
ptrdiff_t i;
for (i = 0; i < dpyinfo->num_devices; ++i)
{
device = &dpyinfo->devices[i];
if (device->focus_frame == f)
device->focus_frame = NULL;
if (device->focus_implicit_frame == f)
device->focus_implicit_frame = NULL;
}
}
#endif
/* The focus may have changed. Figure out if it is a real focus change,
by checking both FocusIn/Out and Enter/LeaveNotify events.
@ -12478,33 +12650,9 @@ x_detect_focus_change (struct x_display_info *dpyinfo, struct frame *frame,
#ifdef HAVE_XINPUT2
case GenericEvent:
{
XIEvent *xi_event = event->xcookie.data;
XIEnterEvent *enter_or_focus = event->xcookie.data;
struct frame *focus_frame = dpyinfo->x_focus_event_frame;
int focus_state
= focus_frame ? focus_frame->output_data.x->focus_state : 0;
if (xi_event->evtype == XI_FocusIn
|| xi_event->evtype == XI_FocusOut)
x_focus_changed ((xi_event->evtype == XI_FocusIn
? FocusIn : FocusOut),
((enter_or_focus->detail
== XINotifyPointer)
? FOCUS_IMPLICIT : FOCUS_EXPLICIT),
dpyinfo, frame, bufp);
else if ((xi_event->evtype == XI_Enter
|| xi_event->evtype == XI_Leave)
&& (enter_or_focus->detail != XINotifyInferior)
&& enter_or_focus->focus
&& !(focus_state & FOCUS_EXPLICIT))
x_focus_changed ((xi_event->evtype == XI_Enter
? FocusIn : FocusOut),
FOCUS_IMPLICIT,
dpyinfo, frame, bufp);
break;
}
xi_focus_handle_for_device (dpyinfo, frame,
event->xcookie.data);
break;
#endif
case FocusIn:
@ -21912,7 +22060,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
if (n_disabled)
{
ndevices = 0;
devices = xmalloc (sizeof *devices * dpyinfo->num_devices);
devices = xzalloc (sizeof *devices * dpyinfo->num_devices);
for (i = 0; i < dpyinfo->num_devices; ++i)
{
@ -21992,7 +22140,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
if (n_disabled)
{
ndevices = 0;
devices = xmalloc (sizeof *devices * dpyinfo->num_devices);
devices = xzalloc (sizeof *devices * dpyinfo->num_devices);
for (i = 0; i < dpyinfo->num_devices; ++i)
{
@ -22027,6 +22175,10 @@ handle_one_xevent (struct x_display_info *dpyinfo,
dpyinfo->num_devices = ndevices;
}
/* Now that the device hierarchy has been changed,
recompute focus. */
xi_handle_focus_change (dpyinfo);
goto XI_OTHER;
}
@ -22044,17 +22196,9 @@ handle_one_xevent (struct x_display_info *dpyinfo,
device = xi_device_from_id (dpyinfo, device_changed->deviceid);
if (!device)
{
/* An existing device might have been enabled. */
x_cache_xi_devices (dpyinfo);
/* Now try to find the device again, in case it was
just enabled. */
device = xi_device_from_id (dpyinfo, device_changed->deviceid);
}
/* If it wasn't enabled, then stop handling this event. */
/* If the device isn't enabled, then stop handling this
event. A HierarchyChanged event will be sent if it
is enabled afterwards. */
if (!device)
goto XI_OTHER;
@ -26219,6 +26363,11 @@ x_free_frame_resources (struct frame *f)
block_input ();
#ifdef HAVE_XINPUT2
/* Remove any record of this frame being focused. */
xi_handle_delete_frame (dpyinfo, f);
#endif
/* If a display connection is dead, don't try sending more
commands to the X server. */
if (dpyinfo->display)

View file

@ -238,23 +238,50 @@ struct xi_touch_point_t
struct xi_device_t
{
/* The numerical ID of this device. */
int device_id;
#ifdef HAVE_XINPUT2_1
/* The number of scroll valuators in `valuators'. */
int scroll_valuator_count;
#endif
/* Whether or not the device is grabbed and its use. */
int grab, use;
#ifdef HAVE_XINPUT2_2
/* Whether or not this device is a direct touch device. */
bool direct_p;
#endif
#ifdef HAVE_XINPUT2_1
/* An array of scroll valuators Emacs knows about. */
struct xi_scroll_valuator_t *valuators;
#endif
#ifdef HAVE_XINPUT2_2
/* An array of in-progress touchscreen events. */
struct xi_touch_point_t *touchpoints;
#endif
/* The name of this device. */
Lisp_Object name;
/* The time at which `focus_frame' became the keyboard focus (only
applies to master devices). */
Time focus_frame_time;
/* The frame that is currently this device's keyboard focus, or
NULL. */
struct frame *focus_frame;
/* The time at which `focus_frame' became the implicit keyboard
focus. */
Time focus_implicit_time;
/* The frame that is currently this device's implicit keyboard
focus, or NULL. */
struct frame *focus_implicit_frame;
};
#endif
@ -482,7 +509,10 @@ struct x_display_info
/* The last frame mentioned in a FocusIn or FocusOut event. This is
separate from x_focus_frame, because whether or not LeaveNotify
events cause us to lose focus depends on whether or not we have
received a FocusIn event for it. */
received a FocusIn event for it.
This field is not used when the input extension is being
utilized. */
struct frame *x_focus_event_frame;
/* The frame which currently has the visual highlight, and should get
@ -1101,7 +1131,9 @@ struct x_output
/* Keep track of focus. May be EXPLICIT if we received a FocusIn for this
frame, or IMPLICIT if we received an EnterNotify.
FocusOut and LeaveNotify clears EXPLICIT/IMPLICIT. */
FocusOut and LeaveNotify clears EXPLICIT/IMPLICIT.
Not used when the input extension is being utilized. */
int focus_state;
/* The offset we need to add to compensate for type A WMs. */