From fd510f12392fcb2bf34eb08262ddda20d8a3c221 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Mon, 30 May 2022 14:04:43 +0800 Subject: [PATCH] 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 bfd081b1e28..a4148735945 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 eee888f6fe6..777a6c4dafc 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 c8e86d5d094..d7e184ed9f1 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 *); -- 2.39.2