emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 3d846efb85: Fix race conditions in handling of unsupported drops


From: Po Lu
Subject: master 3d846efb85: Fix race conditions in handling of unsupported drops on X
Date: Sun, 8 May 2022 21:17:45 -0400 (EDT)

branch: master
commit 3d846efb857c0ace95d6fe026522fcdbffe04dc3
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>

    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.
---
 lisp/x-dnd.el   |   2 +-
 src/keyboard.c  |  18 ++++++----
 src/keyboard.h  |   9 +++++
 src/termhooks.h |   5 +++
 src/xterm.c     | 100 ++++++++++++++++++++++++++++++++++++++++++++++----------
 src/xterm.h     |   1 +
 6 files changed, 110 insertions(+), 25 deletions(-)

diff --git a/lisp/x-dnd.el b/lisp/x-dnd.el
index 47d8ae14cf..c2498a57a1 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 70908120cb..e8f51f8a6f 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 cd5f677b96..a0b7204fa2 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 8c193914ba..08bde0aec0 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 d32bdea843..2bbc9f3d0c 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 66c4b17823..16635053be 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);



reply via email to

[Prev in Thread] Current Thread [Next in Thread]