]> git.eshelyaron.com Git - emacs.git/commitdiff
Fix hangs when x-get-selection is called inside a popup menu
authorPo Lu <luangruo@yahoo.com>
Mon, 30 May 2022 06:04:43 +0000 (14:04 +0800)
committerPo Lu <luangruo@yahoo.com>
Mon, 30 May 2022 06:05:30 +0000 (14:05 +0800)
* src/xselect.c (wait_for_property_change):
(x_get_foreign_selection): Use `x_wait_for_cell_change' if input
is blocked.  (bug#22214)
* src/xterm.c (x_wait_for_cell_change): New function.
* src/xterm.h: Update prototypes.

src/xselect.c
src/xterm.c
src/xterm.h

index bfd081b1e28ccecb7c15b43372ffa9c24de2c608..a4148735945c83eef61833fa206ea3bc4694b606 100644 (file)
@@ -1148,8 +1148,13 @@ wait_for_property_change (struct prop_location *location)
       intmax_t secs = timeout / 1000;
       int nsecs = (timeout % 1000) * 1000000;
       TRACE2 ("  Waiting %"PRIdMAX" secs, %d nsecs", secs, nsecs);
-      wait_reading_process_output (secs, nsecs, 0, false,
-                                  property_change_reply, NULL, 0);
+
+      if (!input_blocked_p ())
+       wait_reading_process_output (secs, nsecs, 0, false,
+                                    property_change_reply, NULL, 0);
+      else
+       x_wait_for_cell_change (property_change_reply,
+                               make_timespec (secs, nsecs));
 
       if (NILP (XCAR (property_change_reply)))
        {
@@ -1256,8 +1261,17 @@ x_get_foreign_selection (Lisp_Object selection_symbol, Lisp_Object target_type,
   intmax_t secs = timeout / 1000;
   int nsecs = (timeout % 1000) * 1000000;
   TRACE1 ("  Start waiting %"PRIdMAX" secs for SelectionNotify", secs);
-  wait_reading_process_output (secs, nsecs, 0, false,
-                              reading_selection_reply, NULL, 0);
+  /* This function can be called with input blocked inside Xt or GTK
+     timeouts run inside popup menus, so use a function that works
+     when input is blocked.  Prefer wait_reading_process_output
+     otherwise, or the toolkit might not get some events.
+     (bug#22214) */
+  if (!input_blocked_p ())
+    wait_reading_process_output (secs, nsecs, 0, false,
+                                reading_selection_reply, NULL, 0);
+  else
+    x_wait_for_cell_change (reading_selection_reply,
+                           make_timespec (secs, nsecs));
   TRACE1 ("  Got event = %d", !NILP (XCAR (reading_selection_reply)));
 
   if (NILP (XCAR (reading_selection_reply)))
index eee888f6fe657321e740652e7594aec2a7742ebd..777a6c4dafc66a0f5e638c17b6dc5930f32ea5a8 100644 (file)
@@ -14836,6 +14836,70 @@ x_display_pixel_width (struct x_display_info *dpyinfo)
   return WidthOfScreen (dpyinfo->screen);
 }
 
+/* Handle events from each display until CELL's car becomes non-nil,
+   or TIMEOUT elapses.  */
+void
+x_wait_for_cell_change (Lisp_Object cell, struct timespec timeout)
+{
+  struct x_display_info *dpyinfo;
+  fd_set fds;
+  int fd, maxfd, finish;
+  XEvent event;
+  struct input_event hold_quit;
+  struct timespec current, at;
+
+  at = timespec_add (current_timespec (), timeout);
+
+  while (true)
+    {
+      FD_ZERO (&fds);
+      maxfd = -1;
+
+      for (dpyinfo = x_display_list; dpyinfo;
+          dpyinfo = dpyinfo->next)
+       {
+         if (XPending (dpyinfo->display))
+           {
+             EVENT_INIT (hold_quit);
+
+             XNextEvent (dpyinfo->display, &event);
+             handle_one_xevent (dpyinfo, &event,
+                                &finish, &hold_quit);
+
+             /* Make us quit now.  */
+             if (hold_quit.kind != NO_EVENT)
+               kbd_buffer_store_event (&hold_quit);
+
+             if (!NILP (XCAR (cell)))
+               return;
+           }
+
+         fd = XConnectionNumber (dpyinfo->display);
+
+         if (fd > maxfd)
+           maxfd = fd;
+
+         eassert (fd < FD_SETSIZE);
+         FD_SET (XConnectionNumber (dpyinfo->display), &fds);
+       }
+
+      eassert (maxfd >= 0);
+
+      current = current_timespec ();
+
+      if (timespec_cmp (at, current) < 0
+         || !NILP (XCAR (cell)))
+       return;
+
+      timeout = timespec_sub (at, current);
+
+      /* We don't have to check the return of pselect, because if an
+        error occurs XPending will call the IO error handler, which
+        then brings us out of this loop.  */
+      pselect (maxfd, &fds, NULL, NULL, &timeout, NULL);
+    }
+}
+
 #ifdef USE_GTK
 static void
 x_monitors_changed_cb (GdkScreen *gscr, gpointer user_data)
index c8e86d5d0940ff0510711bd77bb4afe26a0257a8..d7e184ed9f1ce384054ecc2eef03bccbd15eea83 100644 (file)
@@ -1386,8 +1386,8 @@ extern const char *x_get_string_resource (void *, const char *, const char *);
 
 /* Defined in xterm.c */
 
-typedef void (*x_special_error_handler)(Display *, XErrorEvent *, char *,
-                                       void *);
+typedef void (*x_special_error_handler) (Display *, XErrorEvent *, char *,
+                                        void *);
 
 extern bool x_text_icon (struct frame *, const char *);
 extern void x_catch_errors (Display *);
@@ -1425,6 +1425,7 @@ extern void x_clear_area (struct frame *f, int, int, int, int);
   || (!defined USE_X_TOOLKIT && !defined USE_GTK)
 extern void x_mouse_leave (struct x_display_info *);
 #endif
+extern void x_wait_for_cell_change (Lisp_Object, struct timespec);
 
 #ifndef USE_GTK
 extern int x_dispatch_event (XEvent *, Display *);