summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPo Lu <luangruo@yahoo.com>2022-05-30 14:04:43 +0800
committerPo Lu <luangruo@yahoo.com>2022-05-30 14:05:30 +0800
commitfd510f12392fcb2bf34eb08262ddda20d8a3c221 (patch)
treebd293cd92a1338e9aab3b80d23e26127485f8cce /src
parent52d41f2750c0f66d7f7ba8e198832734fe750fa5 (diff)
downloademacs-fd510f12392fcb2bf34eb08262ddda20d8a3c221.tar.gz
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.
Diffstat (limited to 'src')
-rw-r--r--src/xselect.c22
-rw-r--r--src/xterm.c64
-rw-r--r--src/xterm.h5
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 *);