emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master fd510f1239: Fix hangs when x-get-selection is called inside a pop


From: Po Lu
Subject: master fd510f1239: Fix hangs when x-get-selection is called inside a popup menu
Date: Mon, 30 May 2022 02:05:41 -0400 (EDT)

branch: master
commit fd510f12392fcb2bf34eb08262ddda20d8a3c221
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>

    Fix hangs when x-get-selection is called inside a popup menu
    
    * 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 | 22 ++++++++++++++++----
 src/xterm.c   | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/xterm.h   |  5 +++--
 3 files changed, 85 insertions(+), 6 deletions(-)

diff --git a/src/xselect.c b/src/xselect.c
index bfd081b1e2..a414873594 100644
--- a/src/xselect.c
+++ b/src/xselect.c
@@ -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)))
diff --git a/src/xterm.c b/src/xterm.c
index eee888f6fe..777a6c4daf 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -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)
diff --git a/src/xterm.h b/src/xterm.h
index c8e86d5d09..d7e184ed9f 100644
--- a/src/xterm.h
+++ b/src/xterm.h
@@ -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 *);



reply via email to

[Prev in Thread] Current Thread [Next in Thread]