]> git.eshelyaron.com Git - emacs.git/commitdiff
Optimize coordinate translation during event handling
authorPo Lu <luangruo@yahoo.com>
Tue, 27 Sep 2022 02:50:34 +0000 (10:50 +0800)
committerPo Lu <luangruo@yahoo.com>
Tue, 27 Sep 2022 02:51:13 +0000 (10:51 +0800)
These changes noticeably improve turning the mouse wheel on top of
scroll bars etc over slow network connections.

* src/xterm.c (x_dnd_note_self_position, x_dnd_note_self_wheel)
(x_dnd_note_self_drop): Use x_translate_coordinates.
(x_compute_root_window_offset): New function for calculating and
caching root window offsets of edit window.
(x_translate_coordinates): New function.  Use cached values
whenever possible.
(xi_compute_root_window_offset)
(xi_compute_root_window_offset_pinch): New wrappers for XI2
events.
(x_construct_mouse_click, handle_one_xevent): Use
x_translate_coordinates wherever appropriate.
* src/xterm.h (struct x_output): New fields for keeping track of
the root window offset of the edit window.

src/xterm.c
src/xterm.h

index 527c26f0da383a11cc3e903aa8e39378799b5453..d7ea63bb4f847441fec72021c4b0b4cce3d32223 100644 (file)
@@ -1143,6 +1143,7 @@ static Window x_get_window_below (Display *, Window, int, int, int *, int *);
 #ifndef USE_TOOLKIT_SCROLL_BARS
 static void x_scroll_bar_redraw (struct scroll_bar *);
 #endif
+static void x_translate_coordinates (struct frame *, int, int, int *, int *);
 
 /* Global state maintained during a drag-and-drop operation.  */
 
@@ -4852,16 +4853,13 @@ x_dnd_note_self_position (struct x_display_info *dpyinfo, Window target,
 {
   struct frame *f;
   int dest_x, dest_y;
-  Window child_return;
 
   f = x_top_window_to_frame (dpyinfo, target);
 
-  if (f && XTranslateCoordinates (dpyinfo->display,
-                                 dpyinfo->root_window,
-                                 FRAME_X_WINDOW (f),
-                                 root_x, root_y, &dest_x,
-                                 &dest_y, &child_return))
+  if (f)
     {
+      x_translate_coordinates (f, root_x, root_y, &dest_x, &dest_y);
+
       x_dnd_movement_frame = f;
       x_dnd_movement_x = dest_x;
       x_dnd_movement_y = dest_y;
@@ -4877,19 +4875,16 @@ x_dnd_note_self_wheel (struct x_display_info *dpyinfo, Window target,
 {
   struct frame *f;
   int dest_x, dest_y;
-  Window child_return;
 
   if (button < 4 || button > 7)
     return;
 
   f = x_top_window_to_frame (dpyinfo, target);
 
-  if (f && XTranslateCoordinates (dpyinfo->display,
-                                 dpyinfo->root_window,
-                                 FRAME_X_WINDOW (f),
-                                 root_x, root_y, &dest_x,
-                                 &dest_y, &child_return))
+  if (f)
     {
+      x_translate_coordinates (f, root_x, root_y, &dest_x, &dest_y);
+
       x_dnd_wheel_frame = f;
       x_dnd_wheel_x = dest_x;
       x_dnd_wheel_y = dest_y;
@@ -4912,7 +4907,6 @@ x_dnd_note_self_drop (struct x_display_info *dpyinfo, Window target,
   char **atom_names;
   char *name;
   int win_x, win_y, i;
-  Window dummy;
 
   if (!x_dnd_allow_current_frame
       && (FRAME_OUTER_WINDOW (x_dnd_frame)
@@ -4927,10 +4921,7 @@ x_dnd_note_self_drop (struct x_display_info *dpyinfo, Window target,
   if (NILP (Vx_dnd_native_test_function))
     return;
 
-  if (!XTranslateCoordinates (dpyinfo->display, dpyinfo->root_window,
-                             FRAME_X_WINDOW (f), root_x, root_y,
-                             &win_x, &win_y, &dummy))
-    return;
+  x_translate_coordinates (f, root_x, root_y, &win_x, &win_y);
 
   /* Emacs can't respond to DND events inside the nested event loop,
      so when dragging items to itself, call the test function
@@ -13523,6 +13514,91 @@ get_keysym_name (int keysym)
   return value;
 }
 
+/* Given the root and event coordinates of an X event destined for F's
+   edit window, compute the offset between that window and F's root
+   window.  This information is then used as an optimization to avoid
+   synchronizing when converting coordinates from some other event to
+   F's edit window.  */
+
+static void
+x_compute_root_window_offset (struct frame *f, int root_x, int root_y,
+                             int event_x, int event_y)
+{
+  FRAME_X_OUTPUT (f)->window_offset_certain_p = true;
+  FRAME_X_OUTPUT (f)->root_x = root_x - event_x;
+  FRAME_X_OUTPUT (f)->root_y = root_y - event_y;
+}
+
+/* Translate the given coordinates from the root window to the edit
+   window of FRAME, taking into account any cached root window
+   offsets.  This allows Emacs to avoid excessive calls to _XReply in
+   many cases while handling events, which would otherwise result in
+   slowdowns over slow network connections.  */
+
+static void
+x_translate_coordinates (struct frame *f, int root_x, int root_y,
+                        int *x_out, int *y_out)
+{
+  struct x_output *output;
+  Window dummy;
+
+  output = FRAME_X_OUTPUT (f);
+
+  if (output->window_offset_certain_p)
+    {
+      /* Use the cached root window offset.  */
+      *x_out = root_x - output->root_x;
+      *y_out = root_y - output->root_y;
+
+      return;
+    }
+
+  /* Otherwise, do the transformation manually.  Then, cache the root
+     window position.  */
+  if (!XTranslateCoordinates (FRAME_X_DISPLAY (f),
+                             FRAME_DISPLAY_INFO (f)->root_window,
+                             FRAME_X_WINDOW (f), root_x, root_y,
+                             x_out, y_out, &dummy))
+    /* Use some dummy values.  This is not supposed to be called with
+       coordinates out of the screen.  */
+    *x_out = 0, *y_out = 0;
+  else
+    {
+      /* Cache the root window offset of the edit window.  */
+      output->window_offset_certain_p = true;
+      output->root_x = root_x - *x_out;
+      output->root_y = root_y - *y_out;
+    }
+}
+
+/* The same, but for an XIDeviceEvent.  */
+
+#ifdef HAVE_XINPUT2
+
+static void
+xi_compute_root_window_offset (struct frame *f, XIDeviceEvent *xev)
+{
+  /* Truncate coordinates instead of rounding them, because that is
+     how the X server handles window hierarchy.  */
+  x_compute_root_window_offset (f, xev->root_x, xev->root_y,
+                               xev->event_x, xev->event_y);
+}
+
+#ifdef HAVE_XINPUT2_4
+
+static void
+xi_compute_root_window_offset_pinch (struct frame *f, XIGesturePinchEvent *pev)
+{
+  /* Truncate coordinates instead of rounding them, because that is
+     how the X server handles window hierarchy.  */
+  x_compute_root_window_offset (f, pev->root_x, pev->root_y,
+                               pev->event_x, pev->event_y);
+}
+
+#endif
+
+#endif
+
 static Bool
 x_query_pointer_1 (struct x_display_info *dpyinfo,
                   int client_pointer_device, Window w,
@@ -13651,10 +13727,10 @@ x_query_pointer (Display *dpy, Window w, Window *root_return,
    X server, and instead be artificially constructed from input
    extension events.  In these special events, the only fields that
    are initialized are `time', `button', `state', `type', `window' and
-   `x' and `y'.  This function should not access any other fields in
-   EVENT without also initializing the corresponding fields in `bv'
-   under the XI_ButtonPress and XI_ButtonRelease labels inside
-   `handle_one_xevent'.  */
+   `x', `y', `x_root' and `y_root'.  This function should not access
+   any other fields in EVENT without also initializing the
+   corresponding fields in `bv' under the XI_ButtonPress and
+   XI_ButtonRelease labels inside `handle_one_xevent'.  */
 
 static Lisp_Object
 x_construct_mouse_click (struct input_event *result,
@@ -13663,7 +13739,6 @@ x_construct_mouse_click (struct input_event *result,
 {
   int x = event->x;
   int y = event->y;
-  Window dummy;
 
   /* Make the event type NO_EVENT; we'll change that when we decide
      otherwise.  */
@@ -13680,9 +13755,8 @@ x_construct_mouse_click (struct input_event *result,
      happen with GTK+ scroll bars, for example), translate the
      coordinates so they appear at the correct position.  */
   if (event->window != FRAME_X_WINDOW (f))
-    XTranslateCoordinates (FRAME_X_DISPLAY (f),
-                          event->window, FRAME_X_WINDOW (f),
-                          x, y, &x, &y, &dummy);
+    x_translate_coordinates (f, event->x_root, event->y_root,
+                            &x, &y);
 
   XSETINT (result->x, x);
   XSETINT (result->y, y);
@@ -18968,6 +19042,13 @@ handle_one_xevent (struct x_display_info *dpyinfo,
             `event' itself.  */
          XKeyEvent xkey = event->xkey;
 
+         if (event->xkey.window == FRAME_X_WINDOW (f))
+           /* See the comment above x_compute_root_window_offset for
+              why this optimization is performed.  */
+           x_compute_root_window_offset (f, event->xkey.x_root,
+                                         event->xkey.y_root,
+                                         event->xkey.x, event->xkey.y);
+
 #ifdef HAVE_XINPUT2
          Time pending_keystroke_time;
          struct xi_device_t *source;
@@ -19601,6 +19682,14 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
        f = mouse_or_wdesc_frame (dpyinfo, event->xmotion.window);
 
+       if (f && event->xmotion.window == FRAME_X_WINDOW (f))
+         /* See the comment above x_compute_root_window_offset for
+            why this optimization is performed.  */
+         x_compute_root_window_offset (f, event->xmotion.x_root,
+                                       event->xmotion.y_root,
+                                       event->xmotion.x,
+                                       event->xmotion.y);
+
        if (x_dnd_in_progress
            /* Handle these events normally if the recursion
               level is higher than when the drag-and-drop
@@ -19865,10 +19954,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
                if (xmotion.window != FRAME_X_WINDOW (f))
                  {
-                   XTranslateCoordinates (FRAME_X_DISPLAY (f),
-                                          xmotion.window, FRAME_X_WINDOW (f),
-                                          xmotion.x, xmotion.y, &xmotion.x,
-                                          &xmotion.y, &xmotion.subwindow);
+                   x_translate_coordinates (f, xmotion.x_root, xmotion.y_root,
+                                            &xmotion.x, &xmotion.y);
                    xmotion.window = FRAME_X_WINDOW (f);
                  }
 
@@ -20091,6 +20178,12 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 #endif
 
       f = x_top_window_to_frame (dpyinfo, configureEvent.xconfigure.window);
+
+      /* This means we can no longer be certain of the root window
+        coordinates of any's edit window.  */
+      if (any)
+       FRAME_X_OUTPUT (any)->window_offset_certain_p = false;
+
       /* Unfortunately, we need to call x_drop_xrender_surfaces for
          _all_ ConfigureNotify events, otherwise we miss some and
          flicker.  Don't try to optimize these calls by looking only
@@ -20320,6 +20413,14 @@ handle_one_xevent (struct x_display_info *dpyinfo,
          {
            f = mouse_or_wdesc_frame (dpyinfo, event->xbutton.window);
 
+           if (f && event->xbutton.window == FRAME_X_WINDOW (f))
+             /* See the comment above x_compute_root_window_offset
+                for why this optimization is performed.  */
+             x_compute_root_window_offset (f, event->xbutton.x_root,
+                                           event->xbutton.y_root,
+                                           event->xbutton.x,
+                                           event->xbutton.y);
+
            if (event->type == ButtonPress)
              {
                x_display_set_last_user_time (dpyinfo, event->xbutton.time,
@@ -20493,6 +20594,15 @@ handle_one_xevent (struct x_display_info *dpyinfo,
        dpyinfo->last_mouse_glyph_frame = NULL;
 
        f = mouse_or_wdesc_frame (dpyinfo, event->xbutton.window);
+
+       if (f && event->xbutton.window == FRAME_X_WINDOW (f))
+         /* See the comment above x_compute_root_window_offset
+            for why this optimization is performed.  */
+         x_compute_root_window_offset (f, event->xbutton.x_root,
+                                       event->xbutton.y_root,
+                                       event->xbutton.x,
+                                       event->xbutton.y);
+
        if (f && event->xbutton.type == ButtonPress
            && !popup_activated ()
            && !x_window_to_scroll_bar (event->xbutton.display,
@@ -21178,8 +21288,6 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                goto XI_OTHER;
 #endif
 
-             Window dummy;
-
 #ifdef HAVE_XINPUT2_1
 #ifdef HAVE_XWIDGETS
              struct xwidget_view *xv = xwidget_view_from_window (xev->event);
@@ -21257,11 +21365,10 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                                  real_y = lrint (xev->event_y + bar->top);
                                }
                              else
-                               XTranslateCoordinates (dpyinfo->display,
-                                                      xev->event, FRAME_X_WINDOW (f),
-                                                      lrint (xev->event_x),
-                                                      lrint (xev->event_y),
-                                                      &real_x, &real_y, &dummy);
+                               x_translate_coordinates (f,
+                                                        lrint (xev->root_x),
+                                                        lrint (xev->root_y),
+                                                        &real_x, &real_y);
                            }
                          else
                            {
@@ -21463,6 +21570,11 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
              f = mouse_or_wdesc_frame (dpyinfo, xev->event);
 
+             if (f && xev->event == FRAME_X_WINDOW (f))
+               /* See the comment above x_compute_root_window_offset
+                  for why this optimization is performed.  */
+               xi_compute_root_window_offset (f, xev);
+
              if (x_dnd_in_progress
                  /* Handle these events normally if the recursion
                     level is higher than when the drag-and-drop
@@ -21711,9 +21823,9 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                {
                  if (xev->event != FRAME_X_WINDOW (f))
                    {
-                     XTranslateCoordinates (FRAME_X_DISPLAY (f),
-                                            xev->event, FRAME_X_WINDOW (f),
-                                            ev.x, ev.y, &ev.x, &ev.y, &dummy);
+                     x_translate_coordinates (f, lrint (xev->root_x),
+                                              lrint (xev->root_y),
+                                              &ev.x, &ev.y);
                      ev.window = FRAME_X_WINDOW (f);
                    }
 
@@ -21819,6 +21931,12 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                  f = mouse_or_wdesc_frame (dpyinfo, xev->event);
                  device = xi_device_from_id (dpyinfo, xev->deviceid);
 
+                 if (f && xev->event == FRAME_X_WINDOW (f))
+                   /* See the comment above
+                      x_compute_root_window_offset for why this
+                      optimization is performed.  */
+                   xi_compute_root_window_offset (f, xev);
+
                  /* Don't track grab status for emulated pointer
                     events, because they are ignored by the regular
                     mouse click processing code.  */
@@ -22150,6 +22268,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
              bv.type = xev->evtype == XI_ButtonPress ? ButtonPress : ButtonRelease;
              bv.x = lrint (xev->event_x);
              bv.y = lrint (xev->event_y);
+             bv.x_root = lrint (xev->root_x);
+             bv.y_root = lrint (xev->root_y);
              bv.window = xev->event;
              bv.state = xi_convert_event_state (xev);
              bv.time = xev->time;
@@ -22158,6 +22278,11 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
              f = mouse_or_wdesc_frame (dpyinfo, xev->event);
 
+             if (f && xev->event == FRAME_X_WINDOW (f))
+               /* See the comment above x_compute_root_window_offset
+                  for why this optimization is performed.  */
+               xi_compute_root_window_offset (f, xev);
+
              if (f && xev->evtype == XI_ButtonPress
                  && !popup_activated ()
                  && !x_window_to_scroll_bar (dpyinfo->display, xev->event, 2)
@@ -22209,9 +22334,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 #ifdef USE_GTK
              if (!f)
                {
-                 int real_x = lrint (xev->event_x);
-                 int real_y = lrint (xev->event_y);
-                 Window child;
+                 int real_x = lrint (xev->root_x);
+                 int real_y = lrint (xev->root_y);
 
                  f = x_any_window_to_frame (dpyinfo, xev->event);
 
@@ -22220,9 +22344,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                      if (xev->evtype == XI_ButtonRelease)
                        {
                          if (FRAME_X_WINDOW (f) != xev->event)
-                           XTranslateCoordinates (dpyinfo->display, xev->event,
-                                                  FRAME_X_WINDOW (f), real_x,
-                                                  real_y, &real_x, &real_y, &child);
+                           x_translate_coordinates (f, real_x, real_y,
+                                                    &real_x, &real_y);
 
                          if (xev->detail <= 5)
                            inev.ie.kind = WHEEL_EVENT;
@@ -22487,6 +22610,11 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
              f = x_any_window_to_frame (dpyinfo, xev->event);
 
+             if (f && xev->event == FRAME_X_WINDOW (f))
+               /* See the comment above x_compute_root_window_offset
+                  for why this optimization is performed.  */
+               xi_compute_root_window_offset (f, xev);
+
              /* GTK handles TAB events in an undesirable manner, so
                 keyboard events are always dropped.  But as a side
                 effect, the user time will no longer be set by GDK,
@@ -23126,6 +23254,11 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
              f = x_window_to_frame (dpyinfo, xev->event);
 
+             if (f)
+               /* See the comment above x_compute_root_window_offset
+                  for why this optimization is performed.  */
+               xi_compute_root_window_offset (f, xev);
+
 #ifdef HAVE_GTK3
              menu_bar_p = (f && FRAME_X_OUTPUT (f)->menubar_widget
                            && xg_event_is_for_menubar (f, event));
@@ -23314,8 +23447,12 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 #endif
 
              any = x_window_to_frame (dpyinfo, pev->event);
+
              if (any)
                {
+                 if (pev->event == FRAME_X_WINDOW (any))
+                   xi_compute_root_window_offset_pinch (any, pev);
+
                  inev.ie.kind = PINCH_EVENT;
                  inev.ie.modifiers = x_x_to_emacs_modifiers (FRAME_DISPLAY_INFO (any),
                                                              pev->mods.effective);
index d6ff15e40f76e52cc4af8f4fd6f39c19627c078c..f3791aa8df9ff1185c42f6895894fdb6231ed26c 100644 (file)
@@ -1196,6 +1196,15 @@ struct x_output
   XIEventMask *xi_masks;
   int num_xi_masks;
 #endif
+
+  /* Whether or not we are certain we know the offset from the root
+     window to this frame.  */
+  bool window_offset_certain_p;
+
+  /* The offset of the edit window from the root window.  This is
+     strictly an optimization to avoid extraneous synchronizing in
+     some cases.  */
+  int root_x, root_y;
 };
 
 enum