From: Po Lu Date: Wed, 8 Jun 2022 07:08:09 +0000 (+0800) Subject: Make responding to selection requests work inside popups X-Git-Tag: emacs-29.0.90~1910^2~158 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=22d3f0e95a5602b2bde763cff185f5b4fed6e53e;p=emacs.git 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. --- diff --git a/src/xfns.c b/src/xfns.c index cffb4a5d96c..f0a2ec666c9 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -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 diff --git a/src/xmenu.c b/src/xmenu.c index e9601981edd..7134bf22c83 100644 --- a/src/xmenu.c +++ b/src/xmenu.c @@ -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); diff --git a/src/xselect.c b/src/xselect.c index d184489cbd8..40b6571e0ad 100644 --- a/src/xselect.c +++ b/src/xselect.c @@ -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)); } diff --git a/src/xterm.c b/src/xterm.c index 444adcf94f1..1f4d301e6a5 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -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; diff --git a/src/xterm.h b/src/xterm.h index 1ab65f15d10..7e91e28ed13 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -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,