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.
This commit is contained in:
Po Lu 2022-05-30 14:04:43 +08:00
parent 52d41f2750
commit fd510f1239
3 changed files with 85 additions and 6 deletions

View 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)))

View 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)

View 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 *);