]> git.eshelyaron.com Git - emacs.git/commitdiff
Improve input extension focus handling with multiple master devices
authorPo Lu <luangruo@yahoo.com>
Fri, 5 Aug 2022 02:18:18 +0000 (10:18 +0800)
committerPo Lu <luangruo@yahoo.com>
Fri, 5 Aug 2022 02:18:18 +0000 (10:18 +0800)
* 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.

src/xterm.c
src/xterm.h

index 2a453099ee79eb052624b3518ae336a320c1bb30..ebd27aea0691a8336a770aec256f8a3f1ebf987c 100644 (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)
index c1a944d3cd6bc2cbb09ae46d83c8ecfefe6694d2..7be0f2ede65d9a67454e10173b9a589846d68bfb 100644 (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.  */