1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-01-07 12:20:39 -08:00

Make drag-and-drop wheel movement work locally too on X

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'.
This commit is contained in:
Po Lu 2022-07-19 09:33:09 +08:00
parent 7a93716703
commit bcbd06b4bb
2 changed files with 156 additions and 17 deletions

View 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

View 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