From: Po Lu Date: Mon, 9 May 2022 01:17:28 +0000 (+0800) Subject: Fix race conditions in handling of unsupported drops on X X-Git-Tag: emacs-29.0.90~1910^2~892 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=3d846efb857c0ace95d6fe026522fcdbffe04dc3;p=emacs.git Fix race conditions in handling of unsupported drops on X * lisp/x-dnd.el (x-dnd-handle-unsupported-drop): Adjust for new parameters. * src/keyboard.c (kbd_buffer, kbd_fetch_ptr, kbd_store_ptr): Export variables. (kbd_buffer_get_event): Ignore already handled unsupported drops. * src/keyboard.h: Update prototypes. * src/termhooks.h (enum event_kind): Document meaning of `modifiers' in UNSUPPORTED_DROP_EVENTs. * src/xterm.c (x_dnd_send_unsupported_drop): Set event modifiers to current level. (x_toggle_visible_pointer): Fix fixes fallback. (x_dnd_begin_drag_and_drop): Handle UNSUPPORTED_DROP_EVENTs already in the keyboard buffer before starting DND. (syms_of_xterm): Give timestamp to unsupported drop function. * src/xterm.h: Update prototypes. --- diff --git a/lisp/x-dnd.el b/lisp/x-dnd.el index 47d8ae14cfc..c2498a57a13 100644 --- a/lisp/x-dnd.el +++ b/lisp/x-dnd.el @@ -783,7 +783,7 @@ FORMAT is 32 (not used). MESSAGE is the data part of an XClientMessageEvent." ;;; Handling drops. -(defun x-dnd-handle-unsupported-drop (targets _x _y action _window-id _frame) +(defun x-dnd-handle-unsupported-drop (targets _x _y action _window-id _frame _time) "Return non-nil if the drop described by TARGETS and ACTION should not proceeed." (not (and (or (eq action 'XdndActionCopy) (eq action 'XdndActionMove)) diff --git a/src/keyboard.c b/src/keyboard.c index 70908120cb0..e8f51f8a6fe 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -95,8 +95,6 @@ volatile int interrupt_input_blocked; The maybe_quit function checks this. */ volatile bool pending_signals; -enum { KBD_BUFFER_SIZE = 4096 }; - KBOARD *initial_kboard; KBOARD *current_kboard; static KBOARD *all_kboards; @@ -290,14 +288,14 @@ bool input_was_pending; /* Circular buffer for pre-read keyboard input. */ -static union buffered_input_event kbd_buffer[KBD_BUFFER_SIZE]; +union buffered_input_event kbd_buffer[KBD_BUFFER_SIZE]; /* Pointer to next available character in kbd_buffer. If kbd_fetch_ptr == kbd_store_ptr, the buffer is empty. */ -static union buffered_input_event *kbd_fetch_ptr; +union buffered_input_event *kbd_fetch_ptr; /* Pointer to next place to store character in kbd_buffer. */ -static union buffered_input_event *kbd_store_ptr; +union buffered_input_event *kbd_store_ptr; /* The above pair of variables forms a "queue empty" flag. When we enqueue a non-hook event, we increment kbd_store_ptr. When we @@ -4022,6 +4020,11 @@ kbd_buffer_get_event (KBOARD **kbp, kbd_fetch_ptr = next_kbd_event (event); input_pending = readable_events (0); + /* This means this event was already handled in + `x_dnd_begin_drag_and_drop'. */ + if (event->ie.modifiers < x_dnd_unsupported_event_level) + break; + f = XFRAME (event->ie.frame_or_window); if (!FRAME_LIVE_P (f)) @@ -4029,11 +4032,12 @@ kbd_buffer_get_event (KBOARD **kbp, if (!NILP (Vx_dnd_unsupported_drop_function)) { - if (!NILP (call6 (Vx_dnd_unsupported_drop_function, + if (!NILP (call7 (Vx_dnd_unsupported_drop_function, XCAR (XCDR (event->ie.arg)), event->ie.x, event->ie.y, XCAR (XCDR (XCDR (event->ie.arg))), make_uint (event->ie.code), - event->ie.frame_or_window))) + event->ie.frame_or_window, + make_int (event->ie.timestamp)))) break; } diff --git a/src/keyboard.h b/src/keyboard.h index cd5f677b963..a0b7204fa2b 100644 --- a/src/keyboard.h +++ b/src/keyboard.h @@ -358,6 +358,11 @@ enum menu_item_idx MENU_ITEMS_ITEM_LENGTH }; +enum + { + KBD_BUFFER_SIZE = 4096 + }; + extern void unuse_menu_items (void); /* This is how to deal with multibyte text if HAVE_MULTILINGUAL_MENU @@ -419,6 +424,10 @@ extern void unuse_menu_items (void); happens. */ extern struct timespec *input_available_clear_time; +extern union buffered_input_event kbd_buffer[KBD_BUFFER_SIZE]; +extern union buffered_input_event *kbd_fetch_ptr; +extern union buffered_input_event *kbd_store_ptr; + extern bool ignore_mouse_drag_p; extern Lisp_Object parse_modifiers (Lisp_Object); diff --git a/src/termhooks.h b/src/termhooks.h index 8c193914ba8..08bde0aec0d 100644 --- a/src/termhooks.h +++ b/src/termhooks.h @@ -223,6 +223,11 @@ enum event_kind gives the timestamp where the drop happened. + .modifiers gives a number that + determines if an event was already + handled by + `x_dnd_begin_drag_and_drop'. + .x and .y give the coordinates of the drop originating from the root window. */ diff --git a/src/xterm.c b/src/xterm.c index d32bdea843a..2bbc9f3d0c6 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -868,6 +868,10 @@ static int x_filter_event (struct x_display_info *, XEvent *); /* Flag that indicates if a drag-and-drop operation is in progress. */ bool x_dnd_in_progress; +/* Number that indicates the last "generation" of + UNSUPPORTED_DROP_EVENTs handled. */ +unsigned x_dnd_unsupported_event_level; + /* The frame where the drag-and-drop operation originated. */ struct frame *x_dnd_frame; @@ -3070,6 +3074,7 @@ x_dnd_send_unsupported_drop (struct x_display_info *dpyinfo, Window target_windo ie.kind = UNSUPPORTED_DROP_EVENT; ie.code = (unsigned) target_window; + ie.modifiers = x_dnd_unsupported_event_level; ie.arg = list3 (assq_no_quit (QXdndSelection, dpyinfo->terminal->Vselection_alist), targets, arg); @@ -9479,15 +9484,18 @@ x_toggle_visible_pointer (struct frame *f, bool invisible) invisible = false; #else /* But if Xfixes is available, try using it instead. */ - if (x_probe_xfixes_extension (dpyinfo)) + if (dpyinfo->invisible_cursor == None) { - dpyinfo->fixes_pointer_blanking = true; - xfixes_toggle_visible_pointer (f, invisible); + if (x_probe_xfixes_extension (dpyinfo)) + { + dpyinfo->fixes_pointer_blanking = true; + xfixes_toggle_visible_pointer (f, invisible); - return; + return; + } + else + invisible = false; } - else - invisible = false; #endif if (invisible) @@ -9864,6 +9872,64 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, #ifndef USE_GTK struct x_display_info *event_display; #endif + union buffered_input_event *events, *event; + int n_events; + struct frame *event_frame; + + /* Before starting drag-and-drop, walk through the keyboard buffer + to see if there are any UNSUPPORTED_DROP_EVENTs, and run them now + if they exist, to prevent race conditions from happening due to + multiple unsupported drops running at once. */ + + block_input (); + events = alloca (sizeof *events * KBD_BUFFER_SIZE); + n_events = 0; + event = kbd_fetch_ptr; + + while (event != kbd_store_ptr) + { + if (event->ie.kind == UNSUPPORTED_DROP_EVENT + && event->ie.modifiers < x_dnd_unsupported_event_level) + events[n_events++] = *event; + + event = (event == kbd_buffer + KBD_BUFFER_SIZE - 1 + ? kbd_buffer : event + 1); + } + + x_dnd_unsupported_event_level += 1; + unblock_input (); + + for (i = 0; i < n_events; ++i) + { + maybe_quit (); + + event = &events[i]; + event_frame = XFRAME (event->ie.frame_or_window); + + if (!FRAME_LIVE_P (event_frame)) + continue; + + if (!NILP (Vx_dnd_unsupported_drop_function)) + { + if (!NILP (call7 (Vx_dnd_unsupported_drop_function, + XCAR (XCDR (event->ie.arg)), event->ie.x, + event->ie.y, XCAR (XCDR (XCDR (event->ie.arg))), + make_uint (event->ie.code), + event->ie.frame_or_window, + make_int (event->ie.timestamp)))) + continue; + } + + x_dnd_do_unsupported_drop (FRAME_DISPLAY_INFO (event_frame), + event->ie.frame_or_window, + XCAR (event->ie.arg), + XCAR (XCDR (event->ie.arg)), + (Window) event->ie.code, + XFIXNUM (event->ie.x), + XFIXNUM (event->ie.y), + event->ie.timestamp); + break; + } if (!FRAME_VISIBLE_P (f)) { @@ -24849,16 +24915,16 @@ mouse position list. */); DEFVAR_LISP ("x-dnd-unsupported-drop-function", Vx_dnd_unsupported_drop_function, doc: /* Function called when trying to drop on an unsupported window. -This function is called whenever the user tries to drop -something on a window that does not support either the XDND or -Motif protocols for drag-and-drop. It should return a non-nil -value if the drop was handled by the function, and nil if it was -not. It should accept several arguments TARGETS, X, Y, ACTION, -WINDOW-ID and FRAME, where TARGETS is the list of targets that -was passed to `x-begin-drag', WINDOW-ID is the numeric XID of -the window that is being dropped on, X and Y are the root -window-relative coordinates where the drop happened, ACTION -is the action that was passed to `x-begin-drag', and FRAME is -the frame which initiated the drag-and-drop operation. */); +This function is called whenever the user tries to drop something on a +window that does not support either the XDND or Motif protocols for +drag-and-drop. It should return a non-nil value if the drop was +handled by the function, and nil if it was not. It should accept +several arguments TARGETS, X, Y, ACTION, WINDOW-ID, FRAME and TIME, +where TARGETS is the list of targets that was passed to +`x-begin-drag', WINDOW-ID is the numeric XID of the window that is +being dropped on, X and Y are the root window-relative coordinates +where the drop happened, ACTION is the action that was passed to +`x-begin-drag', FRAME is the frame which initiated the drag-and-drop +operation, and TIME is the X server time when the drop happened. */); Vx_dnd_unsupported_drop_function = Qnil; } diff --git a/src/xterm.h b/src/xterm.h index 66c4b178234..16635053be4 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -1586,6 +1586,7 @@ extern struct input_event xg_pending_quit_event; extern bool x_dnd_in_progress; extern struct frame *x_dnd_frame; +extern unsigned x_dnd_unsupported_event_level; #ifdef HAVE_XINPUT2 extern struct xi_device_t *xi_device_from_id (struct x_display_info *, int);