]> git.eshelyaron.com Git - emacs.git/commitdiff
Make drag-and-drop wheel movement work locally too on X
authorPo Lu <luangruo@yahoo.com>
Tue, 19 Jul 2022 01:33:09 +0000 (09:33 +0800)
committerPo Lu <luangruo@yahoo.com>
Tue, 19 Jul 2022 01:33:09 +0000 (09:33 +0800)
This is provided by the XDND protocol, so it isn't fair for it
to not work with local drag-and-drop.

* lisp/x-dnd.el (x-dnd-note-wheel-movement): New function.  Set
it as the `x-dnd-wheel-function'.
* src/xterm.c (x_dnd_cleanup_drag_and_drop): Clear new flags.
(x_dnd_note_self_wheel): New function.  Set some flags.
(x_dnd_process_quit, x_dnd_begin_drag_and_drop, handle_one_xevent)
(x_connection_closed, x_delete_terminal, mark_xterm): Handle and
set new wheel movement flags
(syms_of_xterm): New variable `x-dnd-wheel-function'.

lisp/x-dnd.el
src/xterm.c

index ffb94c18bb4418e36a380a8251893eacf5f0917a..62a5bbdfc3dde4f5e5887359ededbe1fbcd16e0c 100644 (file)
@@ -1648,6 +1648,24 @@ VERSION is the version of the XDND protocol understood by SOURCE."
                                          0
                                        "XdndDirectSave0")))))))
 
+;; Internal wheel movement.
+
+(defvar x-dnd-wheel-function)
+
+(defun x-dnd-note-wheel-movement (position button state time)
+  "Note wheel movement at POSITION.
+POSITION is a mouse position list describing the position of the
+wheel movement.
+BUTTON is the wheel button that was pressed.
+STATE is the X modifier state at the time of the wheel movement.
+TIME is the X server time at which the wheel moved."
+  (when (posn-window position)
+    (with-selected-window (posn-window position)
+      (let ((count (x-dnd-note-click button time)))
+        (x-dnd-mwheel-scroll button count state)))))
+
+(setq x-dnd-wheel-function #'x-dnd-note-wheel-movement)
+
 (provide 'x-dnd)
 
 ;;; x-dnd.el ends here
index 0436d3c00c0c03dd94536a42102a950d274812aa..88028948ba3cc492c2fcc5a1ac08945320682f1d 100644 (file)
@@ -1353,6 +1353,21 @@ static struct frame *x_dnd_movement_frame;
    with.  */
 static int x_dnd_movement_x, x_dnd_movement_y;
 
+/* The frame for which `x-dnd-wheel-function' should be called.  */
+static struct frame *x_dnd_wheel_frame;
+
+/* The coordinates which the wheel function should be called with.  */
+static int x_dnd_wheel_x, x_dnd_wheel_y;
+
+/* The button that was pressed.  */
+static int x_dnd_wheel_button;
+
+/* The modifier state when the button was pressed.  */
+static int x_dnd_wheel_state;
+
+/* When the button was pressed.  */
+static Time x_dnd_wheel_time;
+
 #ifdef HAVE_XKB
 /* The keyboard state during the drag-and-drop operation.  */
 static unsigned int x_dnd_keyboard_state;
@@ -4734,6 +4749,7 @@ x_dnd_cleanup_drag_and_drop (void *frame)
 #endif
   x_dnd_return_frame_object = NULL;
   x_dnd_movement_frame = NULL;
+  x_dnd_wheel_frame = NULL;
   x_dnd_frame = NULL;
 
   x_restore_events_after_dnd (f, &x_dnd_old_window_attrs);
@@ -4763,6 +4779,37 @@ x_dnd_note_self_position (struct x_display_info *dpyinfo, Window target,
     }
 }
 
+static void
+x_dnd_note_self_wheel (struct x_display_info *dpyinfo, Window target,
+                      unsigned short root_x, unsigned short root_y,
+                      int button, unsigned int state, Time time)
+{
+  struct frame *f;
+  int dest_x, dest_y;
+  Window child_return;
+
+  if (button < 4 || button > 7)
+    return;
+
+  f = x_top_window_to_frame (dpyinfo, target);
+
+  if (f && XTranslateCoordinates (dpyinfo->display,
+                                 dpyinfo->root_window,
+                                 FRAME_X_WINDOW (f),
+                                 root_x, root_y, &dest_x,
+                                 &dest_y, &child_return))
+    {
+      x_dnd_wheel_frame = f;
+      x_dnd_wheel_x = dest_x;
+      x_dnd_wheel_y = dest_y;
+      x_dnd_wheel_button = button;
+      x_dnd_wheel_state = state;
+      x_dnd_wheel_time = time;
+
+      return;
+    }
+}
+
 static void
 x_dnd_note_self_drop (struct x_display_info *dpyinfo, Window target,
                      unsigned short root_x, unsigned short root_y,
@@ -11395,6 +11442,7 @@ x_dnd_process_quit (struct frame *f, Time timestamp)
   x_dnd_waiting_for_finish = false;
   x_dnd_return_frame_object = NULL;
   x_dnd_movement_frame = NULL;
+  x_dnd_wheel_frame = NULL;
 }
 
 /* This function is defined far away from the rest of the XDND code so
@@ -11618,6 +11666,7 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction,
   x_dnd_toplevels = NULL;
   x_dnd_allow_current_frame = allow_current_frame;
   x_dnd_movement_frame = NULL;
+  x_dnd_wheel_frame = NULL;
   x_dnd_init_type_lists = false;
   x_dnd_need_send_drop = false;
 #ifdef HAVE_XKB
@@ -11787,6 +11836,43 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction,
                }
            }
 
+         if (x_dnd_wheel_frame
+             && (x_dnd_in_progress || x_dnd_waiting_for_finish))
+           {
+             XSETFRAME (frame_object, x_dnd_wheel_frame);
+             XSETINT (x, x_dnd_wheel_x);
+             XSETINT (y, x_dnd_wheel_y);
+             x_dnd_wheel_frame = NULL;
+
+             if (!NILP (Vx_dnd_wheel_function)
+                 && FRAME_LIVE_P (XFRAME (frame_object))
+                 && !FRAME_TOOLTIP_P (XFRAME (frame_object))
+                 && x_dnd_movement_x >= 0
+                 && x_dnd_movement_y >= 0
+                 && x_dnd_frame
+                 && (XFRAME (frame_object) != x_dnd_frame
+                     || x_dnd_allow_current_frame))
+               {
+                 x_dnd_old_window_attrs = root_window_attrs;
+                 x_dnd_unwind_flag = true;
+
+                 ref = SPECPDL_INDEX ();
+                 record_unwind_protect_ptr (x_dnd_cleanup_drag_and_drop, f);
+                 call4 (Vx_dnd_wheel_function,
+                        Fposn_at_x_y (x, y, frame_object, Qnil),
+                        make_fixnum (x_dnd_wheel_button),
+                        make_uint (x_dnd_wheel_state),
+                        make_uint (x_dnd_wheel_time));
+                 x_dnd_unwind_flag = false;
+                 unbind_to (ref, Qnil);
+
+                 /* Redisplay this way to preserve the echo area.
+                    Otherwise, the contents will abruptly disappear
+                    when the mouse moves over a frame.  */
+                 redisplay_preserve_echo_area (33);
+               }
+           }
+
          if (hold_quit.kind != NO_EVENT)
            {
              x_dnd_process_quit (f, hold_quit.timestamp);
@@ -11890,6 +11976,9 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction,
          if (x_dnd_movement_frame)
            x_dnd_movement_frame = NULL;
 
+         if (x_dnd_wheel_frame)
+           x_dnd_wheel_frame = NULL;
+
          if (hold_quit.kind != NO_EVENT)
            EVENT_INIT (hold_quit);
        }
@@ -11902,6 +11991,7 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction,
   current_hold_quit = NULL;
 #endif
   x_dnd_movement_frame = NULL;
+  x_dnd_wheel_frame = NULL;
   x_restore_events_after_dnd (f, &root_window_attrs);
 
   if (x_dnd_return_frame == 3
@@ -18914,18 +19004,26 @@ handle_one_xevent (struct x_display_info *dpyinfo,
              dpyinfo->grabbed &= ~(1 << event->xbutton.button);
 
            if (event->xbutton.type == ButtonPress
-               && x_dnd_last_seen_window != None
-               && x_dnd_last_protocol_version != -1)
+               && x_dnd_last_seen_window != None)
              {
-               x_dnd_send_position (x_dnd_frame,
-                                    x_dnd_last_seen_window,
-                                    x_dnd_last_protocol_version,
-                                    event->xbutton.x_root,
-                                    event->xbutton.y_root,
-                                    x_dnd_selection_timestamp,
-                                    x_dnd_wanted_action,
-                                    event->xbutton.button,
-                                    event->xbutton.state);
+               if (x_dnd_last_window_is_frame)
+                 x_dnd_note_self_wheel (dpyinfo,
+                                        x_dnd_last_seen_window,
+                                        event->xbutton.x_root,
+                                        event->xbutton.y_root,
+                                        event->xbutton.button,
+                                        event->xbutton.state,
+                                        event->xbutton.time);
+               else if (x_dnd_last_protocol_version != -1)
+                 x_dnd_send_position (x_dnd_frame,
+                                      x_dnd_last_seen_window,
+                                      x_dnd_last_protocol_version,
+                                      event->xbutton.x_root,
+                                      event->xbutton.y_root,
+                                      x_dnd_selection_timestamp,
+                                      x_dnd_wanted_action,
+                                      event->xbutton.button,
+                                      event->xbutton.state);
 
                goto OTHER;
              }
@@ -20315,15 +20413,21 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 #endif
 
                  if (xev->evtype == XI_ButtonPress
-                     && x_dnd_last_seen_window != None
-                     && x_dnd_last_protocol_version != -1)
+                     && x_dnd_last_seen_window != None)
                    {
                      dnd_state = xi_convert_event_state (xev);
 
-                     x_dnd_send_position (x_dnd_frame, x_dnd_last_seen_window,
-                                          x_dnd_last_protocol_version, xev->root_x,
-                                          xev->root_y, x_dnd_selection_timestamp,
-                                          x_dnd_wanted_action, xev->detail, dnd_state);
+                     if (x_dnd_last_window_is_frame)
+                       x_dnd_note_self_wheel (dpyinfo,
+                                              x_dnd_last_seen_window,
+                                              xev->root_x, xev->root_y,
+                                              xev->detail, dnd_state,
+                                              xev->time);
+                     else
+                       x_dnd_send_position (x_dnd_frame, x_dnd_last_seen_window,
+                                            x_dnd_last_protocol_version, xev->root_x,
+                                            xev->root_y, x_dnd_selection_timestamp,
+                                            x_dnd_wanted_action, xev->detail, dnd_state);
 
                      goto XI_OTHER;
                    }
@@ -23482,6 +23586,7 @@ x_connection_closed (Display *dpy, const char *error_message, bool ioerror)
 
       x_dnd_return_frame_object = NULL;
       x_dnd_movement_frame = NULL;
+      x_dnd_wheel_frame = NULL;
       x_dnd_frame = NULL;
     }
 
@@ -27750,6 +27855,7 @@ x_delete_terminal (struct terminal *terminal)
 
          x_dnd_return_frame_object = NULL;
          x_dnd_movement_frame = NULL;
+         x_dnd_wheel_frame = NULL;
          x_dnd_frame = NULL;
        }
 
@@ -28008,6 +28114,12 @@ mark_xterm (void)
       mark_object (val);
     }
 
+  if (x_dnd_wheel_frame)
+    {
+      XSETFRAME (val, x_dnd_wheel_frame);
+      mark_object (val);
+    }
+
 #if defined HAVE_XINPUT2 || defined USE_TOOLKIT_SCROLL_BARS \
   || defined HAVE_XRANDR || defined USE_GTK
   for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
@@ -28454,6 +28566,15 @@ where FRAME is the frame the mouse is on top of, and POSITION is a
 mouse position list.  */);
   Vx_dnd_movement_function = Qnil;
 
+  DEFVAR_LISP ("x-dnd-wheel-function", Vx_dnd_wheel_function,
+    doc: /* Function called upon wheel movement on a frame during drag-and-drop.
+It should either be nil, or accept four arguments POSITION, BUTTON,
+STATE and TIME, where POSITION is a mouse position list describing
+where the wheel moved, BUTTON is the wheel button that was pressed,
+STATE is the X modifier state at the time of the wheel movement, and
+TIME is the X server time at which the wheel moved.  */);
+  Vx_dnd_wheel_function = Qnil;
+
   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