Make responding to selection requests work inside popups

* src/xfns.c (Fx_file_dialog):
* src/xmenu.c (x_menu_wait_for_event, create_and_show_popup_menu)
(create_and_show_dialog, x_menu_show): Defer selection requests.
* src/xselect.c (x_get_foreign_selection)
(x_handle_selection_notify): Add some more info to selection
trace.

* src/xterm.c (x_defer_selection_requests): Make non-static.
(x_release_selection_requests_and_flush): New function.
(x_dnd_begin_drag_and_drop): Use DEFER_SELECTIONS instead.
(x_wait_for_cell_change): Fix initial value of rc for pushed
back events.
(handle_one_xevent): Allow GTK to respond to selections in its
windows too.

* src/xterm.h (DEFER_SELECTIONS): New slug of code.
This commit is contained in:
Po Lu 2022-06-08 15:08:09 +08:00
parent 90f3da0ccd
commit 22d3f0e95a
5 changed files with 86 additions and 18 deletions

View file

@ -8885,6 +8885,9 @@ DEFUN ("x-file-dialog", Fx_file_dialog, Sx_file_dialog, 2, 5, 0,
/* Prevent redisplay. */
specbind (Qinhibit_redisplay, Qt);
/* Defer selection requests. */
DEFER_SELECTIONS;
block_input ();
/* Create the dialog with PROMPT as title, using DIR as initial

View file

@ -198,6 +198,10 @@ x_menu_wait_for_event (void *data)
struct x_display_info *dpyinfo;
int n = 0;
/* ISTM that if timer_check is okay, this should be too, since
both can run random Lisp. */
x_handle_pending_selection_requests ();
FD_ZERO (&read_fds);
for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
{
@ -1579,6 +1583,8 @@ create_and_show_popup_menu (struct frame *f, widget_value *first_wv,
}
#endif
DEFER_SELECTIONS;
/* Display the menu. */
gtk_widget_show_all (menu);
@ -1868,6 +1874,8 @@ create_and_show_popup_menu (struct frame *f, widget_value *first_wv,
{
specpdl_ref specpdl_count = SPECPDL_INDEX ();
DEFER_SELECTIONS;
record_unwind_protect_int (pop_down_menu, (int) menu_id);
#ifdef HAVE_XINPUT2
record_unwind_protect_ptr (leave_toolkit_menu, f);
@ -2199,6 +2207,8 @@ create_and_show_dialog (struct frame *f, widget_value *first_wv)
if (menu)
{
specpdl_ref specpdl_count = SPECPDL_INDEX ();
DEFER_SELECTIONS;
record_unwind_protect_ptr (pop_down_menu, menu);
/* Display the menu. */
@ -2255,6 +2265,8 @@ create_and_show_dialog (struct frame *f, widget_value *first_wv)
{
specpdl_ref count = SPECPDL_INDEX ();
DEFER_SELECTIONS;
/* xdialog_show_unwind is responsible for popping the dialog box down. */
record_unwind_protect_int (pop_down_menu, (int) dialog_id);
@ -2715,18 +2727,18 @@ x_menu_show (struct frame *f, int x, int y, int menuflags,
y = max (y, 1);
XMenuLocate (FRAME_X_DISPLAY (f), menu, 0, 0, x, y,
&ulx, &uly, &width, &height);
if (ulx+width > dispwidth)
if (ulx + width > dispwidth)
{
x -= (ulx + width) - dispwidth;
ulx = dispwidth - width;
}
if (uly+height > dispheight)
if (uly + height > dispheight)
{
y -= (uly + height) - dispheight;
uly = dispheight - height;
}
#ifndef HAVE_X_WINDOWS
if (FRAME_HAS_MINIBUF_P (f) && uly+height > dispheight - 1)
if (FRAME_HAS_MINIBUF_P (f) && uly + height > dispheight - 1)
{
/* Move the menu away of the echo area, to avoid overwriting the
menu with help echo messages or vice versa. */
@ -2750,8 +2762,8 @@ x_menu_show (struct frame *f, int x, int y, int menuflags,
/* If position was not given by a mouse click, adjust so upper left
corner of the menu as a whole ends up at given coordinates. This
is what x-popup-menu says in its documentation. */
x += width/2;
y += 1.5*height/(maxlines+2);
x += width / 2;
y += 1.5 * height/ (maxlines + 2);
}
XMenuSetAEQ (menu, true);
@ -2759,6 +2771,8 @@ x_menu_show (struct frame *f, int x, int y, int menuflags,
pane = selidx = 0;
#ifndef MSDOS
DEFER_SELECTIONS;
XMenuActivateSetWaitFunction (x_menu_wait_for_event, FRAME_X_DISPLAY (f));
#ifdef HAVE_XINPUT2
XMenuActivateSetTranslateFunction (x_menu_translate_generic_event);

View file

@ -1252,7 +1252,11 @@ x_get_foreign_selection (Lisp_Object selection_symbol, Lisp_Object target_type,
else
x_wait_for_cell_change (reading_selection_reply,
make_timespec (secs, nsecs));
TRACE1 (" Got event = %d", !NILP (XCAR (reading_selection_reply)));
TRACE1 (" Got event = %s", (!NILP (XCAR (reading_selection_reply))
? (SYMBOLP (XCAR (reading_selection_reply))
? SSDATA (SYMBOL_NAME (XCAR (reading_selection_reply)))
: "YES")
: "NO"));
if (NILP (XCAR (reading_selection_reply)))
error ("Timed out waiting for reply from selection owner");
@ -1947,7 +1951,7 @@ x_handle_selection_notify (const XSelectionEvent *event)
if (event->selection != reading_which_selection)
return;
TRACE0 ("Received SelectionNotify");
TRACE1 ("Received SelectionNotify: %d", (int) event->property);
XSETCAR (reading_selection_reply,
(event->property != 0 ? Qt : Qlambda));
}

View file

@ -793,10 +793,43 @@ static struct input_event *current_hold_quit;
than 0. */
static int x_use_pending_selection_requests;
static void
static void x_push_selection_request (struct selection_input_event *);
/* Defer selection requests. Any selection requests generated after
this can then be processed by calling
`x_handle_pending_selection_requests'.
Also run through and queue all the selection events already in the
keyboard buffer. */
void
x_defer_selection_requests (void)
{
union buffered_input_event *event;
block_input ();
x_use_pending_selection_requests++;
if (!x_use_pending_selection_requests)
{
event = kbd_fetch_ptr;
while (event != kbd_store_ptr)
{
if (event->ie.kind == SELECTION_REQUEST_EVENT
|| event->ie.kind == SELECTION_CLEAR_EVENT)
{
x_push_selection_request (&event->sie);
/* Mark this selection event as invalid. */
SELECTION_EVENT_DPYINFO (&event->sie) = NULL;
}
event = (event == kbd_buffer + KBD_BUFFER_SIZE - 1
? kbd_buffer : event + 1);
}
}
unblock_input ();
}
static void
@ -805,6 +838,15 @@ x_release_selection_requests (void)
x_use_pending_selection_requests--;
}
void
x_release_selection_requests_and_flush (void)
{
x_release_selection_requests ();
if (!x_use_pending_selection_requests)
x_handle_pending_selection_requests ();
}
struct x_selection_request_event
{
/* The selection request event. */
@ -10764,8 +10806,7 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction,
if (x_dnd_in_progress || x_dnd_waiting_for_finish)
error ("A drag-and-drop session is already in progress");
x_defer_selection_requests ();
record_unwind_protect_void (x_release_selection_requests);
DEFER_SELECTIONS;
/* If local_value is nil, then we lost ownership of XdndSelection.
Signal a more informative error than args-out-of-range. */
@ -10781,8 +10822,8 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction,
if (popup_activated ())
error ("Trying to drag-and-drop from within a menu-entry");
record_unwind_protect_void (x_free_dnd_targets);
x_set_dnd_targets (target_atoms, ntargets);
record_unwind_protect_void (x_free_dnd_targets);
ltimestamp = x_timestamp_for_selection (FRAME_DISPLAY_INFO (f),
QXdndSelection);
@ -15306,7 +15347,7 @@ x_wait_for_cell_change (Lisp_Object cell, struct timespec timeout)
#ifndef USE_GTK
FD_ZERO (&rfds);
rc = 0;
rc = -1;
#endif
while (true)
@ -15892,18 +15933,18 @@ handle_one_xevent (struct x_display_info *dpyinfo,
break;
case SelectionNotify:
#ifdef USE_X_TOOLKIT
if (! x_window_to_frame (dpyinfo, event->xselection.requestor))
#if defined USE_X_TOOLKIT || defined USE_GTK
if (!x_window_to_frame (dpyinfo, event->xselection.requestor))
goto OTHER;
#endif /* not USE_X_TOOLKIT */
#endif /* not USE_X_TOOLKIT and not USE_GTK */
x_handle_selection_notify (&event->xselection);
break;
case SelectionClear: /* Someone has grabbed ownership. */
#ifdef USE_X_TOOLKIT
if (! x_window_to_frame (dpyinfo, event->xselectionclear.window))
#if defined USE_X_TOOLKIT || defined USE_GTK
if (!x_window_to_frame (dpyinfo, event->xselectionclear.window))
goto OTHER;
#endif /* USE_X_TOOLKIT */
#endif /* not USE_X_TOOLKIT and not USE_GTK */
{
const XSelectionClearEvent *eventp = &event->xselectionclear;

View file

@ -1456,6 +1456,12 @@ extern void x_xr_reset_ext_clip (struct frame *f);
extern void x_scroll_bar_configure (GdkEvent *);
#endif
#define DEFER_SELECTIONS \
x_defer_selection_requests (); \
record_unwind_protect_void (x_release_selection_requests_and_flush)
extern void x_defer_selection_requests (void);
extern void x_release_selection_requests_and_flush (void);
extern void x_handle_pending_selection_requests (void);
extern bool x_detect_pending_selection_requests (void);
extern Lisp_Object x_dnd_begin_drag_and_drop (struct frame *, Time, Atom,