]> git.eshelyaron.com Git - emacs.git/commitdiff
Speed up x_real_pos_and_offsets using XCB
authorKen Raeburn <raeburn@raeburn.org>
Sat, 7 Nov 2015 08:06:32 +0000 (03:06 -0500)
committerKen Raeburn <raeburn@raeburn.org>
Thu, 12 Nov 2015 08:58:10 +0000 (03:58 -0500)
* src/xfns.c (x_real_pos_and_offsets) [USE_XCB]: Add XCB flavors of
all X calls, and pipeline requests when possible, collecting results
later.  Eliminate use of x_catch_errors (and thus XSync) in XCB case.

src/xfns.c

index c55e6fee168935aa870f519d5cd91ac8a9a3ab49..9071b89acd97dd579dc7c8d923c4226fe76beb12 100644 (file)
@@ -181,24 +181,38 @@ x_real_pos_and_offsets (struct frame *f,
                         int *yptr,
                         int *outer_border)
 {
-  int win_x, win_y, outer_x IF_LINT (= 0), outer_y IF_LINT (= 0);
+  int win_x = 0, win_y = 0, outer_x = 0, outer_y = 0;
   int real_x = 0, real_y = 0;
   bool had_errors = false;
   Window win = f->output_data.x->parent_desc;
+  struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+  long max_len = 400;
+  Atom target_type = XA_CARDINAL;
+  unsigned int ow = 0, oh = 0;
+  unsigned int fw = 0, fh = 0;
+  unsigned int bw = 0;
+  /* We resort to XCB if possible because there are several X calls
+     here which require responses from the server but do not have data
+     dependencies between them.  Using XCB lets us pipeline requests,
+     whereas with Xlib we must wait for each answer before sending the
+     next request.
+
+     For a non-local display, the round-trip time could be a few tens
+     of milliseconds, depending on the network distance.  It doesn't
+     take a lot of those to add up to a noticeable hesitation in
+     responding to user actions.  */
+#ifdef USE_XCB
+  xcb_connection_t *xcb_conn = dpyinfo->xcb_connection;
+  xcb_get_property_cookie_t prop_cookie;
+  xcb_get_geometry_cookie_t outer_geom_cookie;
+  bool sent_requests = false;
+#else
   Atom actual_type;
   unsigned long actual_size, bytes_remaining;
   int rc, actual_format;
-  struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
-  long max_len = 400;
   Display *dpy = FRAME_X_DISPLAY (f);
   unsigned char *tmp_data = NULL;
-  Atom target_type = XA_CARDINAL;
-  unsigned int ow IF_LINT (= 0), oh IF_LINT (= 0);
-  unsigned int fw, fh;
-
-  block_input ();
-
-  x_catch_errors (dpy);
+#endif
 
   if (x_pixels_diff) *x_pixels_diff = 0;
   if (y_pixels_diff) *y_pixels_diff = 0;
@@ -213,6 +227,13 @@ x_real_pos_and_offsets (struct frame *f,
   if (win == dpyinfo->root_window)
     win = FRAME_OUTER_WINDOW (f);
 
+  block_input ();
+
+#ifndef USE_XCB
+  /* If we're using XCB, all errors are checked for on each call.  */
+  x_catch_errors (dpy);
+#endif
+
   /* This loop traverses up the containment tree until we hit the root
      window.  Window managers may intersect many windows between our window
      and the root window.  The window we find just before the root window
@@ -220,6 +241,22 @@ x_real_pos_and_offsets (struct frame *f,
   for (;;)
     {
       Window wm_window, rootw;
+
+#ifdef USE_XCB
+      xcb_query_tree_cookie_t query_tree_cookie;
+      xcb_query_tree_reply_t *query_tree;
+
+      query_tree_cookie = xcb_query_tree (xcb_conn, win);
+      query_tree = xcb_query_tree_reply (xcb_conn, query_tree_cookie, NULL);
+      if (query_tree == NULL)
+       had_errors = true;
+      else
+       {
+         wm_window = query_tree->parent;
+         rootw = query_tree->root;
+         free (query_tree);
+       }
+#else
       Window *tmp_children;
       unsigned int tmp_nchildren;
       int success;
@@ -234,6 +271,7 @@ x_real_pos_and_offsets (struct frame *f,
        break;
 
       XFree (tmp_children);
+#endif
 
       if (wm_window == rootw || had_errors)
         break;
@@ -243,15 +281,74 @@ x_real_pos_and_offsets (struct frame *f,
 
   if (! had_errors)
     {
-      unsigned int bw, ign;
+#ifdef USE_XCB
+      xcb_get_geometry_cookie_t geom_cookie;
+      xcb_translate_coordinates_cookie_t trans_cookie;
+      xcb_translate_coordinates_cookie_t outer_trans_cookie;
+
+      xcb_translate_coordinates_reply_t *trans;
+      xcb_get_geometry_reply_t *geom;
+#else
       Window child, rootw;
+      unsigned int ign;
+#endif
+
+#ifdef USE_XCB
+      /* Fire off the requests that don't have data dependencies.
+
+         Once we've done this, we must collect the results for each
+         one before returning, even if other errors are detected,
+         making the other responses moot.  */
+      geom_cookie = xcb_get_geometry (xcb_conn, win);
+
+      trans_cookie =
+        xcb_translate_coordinates (xcb_conn,
+                                   /* From-window, to-window.  */
+                                   FRAME_DISPLAY_INFO (f)->root_window,
+                                   FRAME_X_WINDOW (f),
+
+                                   /* From-position.  */
+                                   0, 0);
+      if (FRAME_X_WINDOW (f) != FRAME_OUTER_WINDOW (f))
+        outer_trans_cookie =
+          xcb_translate_coordinates (xcb_conn,
+                                     /* From-window, to-window.  */
+                                     FRAME_DISPLAY_INFO (f)->root_window,
+                                     FRAME_OUTER_WINDOW (f),
+
+                                     /* From-position.  */
+                                     0, 0);
+      if (right_offset_x || bottom_offset_y)
+       outer_geom_cookie = xcb_get_geometry (xcb_conn,
+                                             FRAME_OUTER_WINDOW (f));
+
+      if (dpyinfo->root_window == f->output_data.x->parent_desc)
+       /* Try _NET_FRAME_EXTENTS if our parent is the root window.  */
+       prop_cookie = xcb_get_property (xcb_conn, 0, win,
+                                       dpyinfo->Xatom_net_frame_extents,
+                                       target_type, 0, max_len);
+
+      sent_requests = true;
+#endif
 
       /* Get the real coordinates for the WM window upper left corner */
+#ifdef USE_XCB
+      geom = xcb_get_geometry_reply (xcb_conn, geom_cookie, NULL);
+      if (geom)
+       {
+         real_x = geom->x;
+         real_y = geom->y;
+         ow = geom->width;
+         oh = geom->height;
+         bw = geom->border_width;
+         free (geom);
+       }
+      else
+       had_errors = true;
+#else
       XGetGeometry (dpy, win,
-                    &rootw, &real_x, &real_y, &ow, &oh, &bw, &ign);
-
-      if (outer_border)
-        *outer_border = bw;
+                   &rootw, &real_x, &real_y, &ow, &oh, &bw, &ign);
+#endif
 
       /* Translate real coordinates to coordinates relative to our
          window.  For our window, the upper left corner is 0, 0.
@@ -262,7 +359,23 @@ x_real_pos_and_offsets (struct frame *f,
          |      title                |
          | -----------------         v y
          | |  our window
-      */
+
+         Since we don't care about the child window corresponding to
+         the actual coordinates, we can send zero to get the offsets
+         and compute the resulting coordinates below.  This reduces
+         the data dependencies between calls and lets us pipeline the
+         requests better in the XCB case.  */
+#ifdef USE_XCB
+      trans = xcb_translate_coordinates_reply (xcb_conn, trans_cookie, NULL);
+      if (trans)
+       {
+         win_x = trans->dst_x;
+         win_y = trans->dst_y;
+         free (trans);
+       }
+      else
+       had_errors = true;
+#else
       XTranslateCoordinates (dpy,
 
                             /* From-window, to-window.  */
@@ -274,6 +387,7 @@ x_real_pos_and_offsets (struct frame *f,
 
                             /* Child of win.  */
                             &child);
+#endif
 
       win_x += real_x;
       win_y += real_y;
@@ -285,6 +399,21 @@ x_real_pos_and_offsets (struct frame *f,
        }
       else
         {
+#ifdef USE_XCB
+          xcb_translate_coordinates_reply_t *outer_trans;
+
+          outer_trans = xcb_translate_coordinates_reply (xcb_conn,
+                                                         outer_trans_cookie,
+                                                         NULL);
+          if (outer_trans)
+            {
+              outer_x = outer_trans->dst_x;
+              outer_y = outer_trans->dst_y;
+              free (outer_trans);
+            }
+          else
+           had_errors = true;
+#else
           XTranslateCoordinates (dpy,
 
                                  /* From-window, to-window.  */
@@ -296,17 +425,46 @@ x_real_pos_and_offsets (struct frame *f,
 
                                  /* Child of win.  */
                                  &child);
+#endif
 
          outer_x += real_x;
          outer_y += real_y;
        }
 
+#ifndef USE_XCB
       had_errors = x_had_errors_p (dpy);
+#endif
     }
 
-  if (!had_errors && dpyinfo->root_window == f->output_data.x->parent_desc)
+  if (dpyinfo->root_window == f->output_data.x->parent_desc)
     {
       /* Try _NET_FRAME_EXTENTS if our parent is the root window.  */
+#ifdef USE_XCB
+      /* Make sure we didn't get an X error early and skip sending the
+         request.  */
+      if (sent_requests)
+        {
+          xcb_get_property_reply_t *prop;
+
+          prop = xcb_get_property_reply (xcb_conn, prop_cookie, NULL);
+          if (prop)
+            {
+              if (prop->type == target_type
+                  && xcb_get_property_value_length (prop) == 4
+                  && prop->format == 32)
+                {
+                  long *fe = xcb_get_property_value (prop);
+
+                  outer_x = -fe[0];
+                  outer_y = -fe[2];
+                  real_x -= fe[0];
+                  real_y -= fe[2];
+                }
+              free (prop);
+            }
+          /* Xlib version doesn't set had_errors here.  Intentional or bug?  */
+        }
+#else
       rc = XGetWindowProperty (dpy, win, dpyinfo->Xatom_net_frame_extents,
                                0, max_len, False, target_type,
                                &actual_type, &actual_format, &actual_size,
@@ -324,19 +482,42 @@ x_real_pos_and_offsets (struct frame *f,
         }
 
       if (tmp_data) XFree (tmp_data);
+#endif
     }
 
   if (right_offset_x || bottom_offset_y)
     {
+#ifdef USE_XCB
+      /* Make sure we didn't get an X error early and skip sending the
+         request.  */
+      if (sent_requests)
+        {
+          xcb_get_geometry_reply_t *outer_geom;
+
+          outer_geom = xcb_get_geometry_reply (xcb_conn, outer_geom_cookie,
+                                               NULL);
+          if (outer_geom)
+            {
+              fw = outer_geom->width;
+              fh = outer_geom->height;
+              free (outer_geom);
+            }
+          else
+           had_errors = true;
+        }
+#else
       int xy_ign;
       unsigned int ign;
       Window rootw;
 
       XGetGeometry (dpy, FRAME_OUTER_WINDOW (f),
                    &rootw, &xy_ign, &xy_ign, &fw, &fh, &ign, &ign);
+#endif
     }
 
+#ifndef USE_XCB
   x_uncatch_errors ();
+#endif
 
   unblock_input ();
 
@@ -351,6 +532,8 @@ x_real_pos_and_offsets (struct frame *f,
   if (xptr) *xptr = real_x;
   if (yptr) *yptr = real_y;
 
+  if (outer_border) *outer_border = bw;
+
   if (right_offset_x) *right_offset_x = ow - fw + outer_x;
   if (bottom_offset_y) *bottom_offset_y = oh - fh + outer_y;
 }