diff --git a/lisp/x-dnd.el b/lisp/x-dnd.el index ffb94c18bb4..62a5bbdfc3d 100644 --- a/lisp/x-dnd.el +++ b/lisp/x-dnd.el @@ -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 diff --git a/src/xterm.c b/src/xterm.c index 0436d3c00c0..88028948ba3 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -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