[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
master 7f6c925383: Add support for dragging text onto windows that don't
From: |
Po Lu |
Subject: |
master 7f6c925383: Add support for dragging text onto windows that don't support any DND protocol |
Date: |
Mon, 4 Apr 2022 23:40:12 -0400 (EDT) |
branch: master
commit 7f6c9253838baba7cdd38573179bc0d3a724e25b
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>
Add support for dragging text onto windows that don't support any DND
protocol
* src/xselect.c (x_own_selection): Export function.
(x_handle_selection_request): Handle selection requests to the
pending DND time.
(x_set_pending_dnd_time): New function.
* src/xterm.c (x_dnd_send_unsupported_drop): New function.
Implement according to the suggestions in the XDND protocol
specification.
(x_dnd_get_target_window): Return the toplevel window as well.
(x_dnd_cleanup_drag_and_drop, x_dnd_begin_drag_and_drop)
(x_dnd_update_state, handle_one_xevent): Send unsupported drops
and use last seen toplevel instead of target for `return_frame'.
* src/xterm.h: Update prototypes.
---
src/xselect.c | 16 +++-
src/xterm.c | 287 ++++++++++++++++++++++++++++++++++++++++++++--------------
src/xterm.h | 2 +
3 files changed, 236 insertions(+), 69 deletions(-)
diff --git a/src/xselect.c b/src/xselect.c
index 7719876884..2969c816a3 100644
--- a/src/xselect.c
+++ b/src/xselect.c
@@ -39,6 +39,8 @@ along with GNU Emacs. If not, see
<https://www.gnu.org/licenses/>. */
#include <X11/Xproto.h>
+static Time pending_dnd_time;
+
struct prop_location;
struct selection_data;
@@ -304,7 +306,7 @@ x_atom_to_symbol (struct x_display_info *dpyinfo, Atom atom)
Update the Vselection_alist so that we can reply to later requests for
our selection. */
-static void
+void
x_own_selection (Lisp_Object selection_name, Lisp_Object selection_value,
Lisp_Object frame)
{
@@ -771,6 +773,12 @@ x_handle_selection_request (struct selection_input_event
*event)
if (!dpyinfo) goto DONE;
+ /* This is how the XDND protocol recommends dropping text onto a
+ target that doesn't support XDND. */
+ if (SELECTION_EVENT_TIME (event) == pending_dnd_time + 1
+ || SELECTION_EVENT_TIME (event) == pending_dnd_time + 2)
+ selection_symbol = QXdndSelection;
+
local_selection_data = LOCAL_SELECTION (selection_symbol, dpyinfo);
/* Decline if we don't own any selections. */
@@ -2671,6 +2679,12 @@ x_timestamp_for_selection (struct x_display_info
*dpyinfo,
return value;
}
+void
+x_set_pending_dnd_time (Time time)
+{
+ pending_dnd_time = time;
+}
+
static void syms_of_xselect_for_pdumper (void);
void
diff --git a/src/xterm.c b/src/xterm.c
index d29a7a122a..77861c3fc0 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -899,9 +899,14 @@ static int x_dnd_return_frame;
`x_dnd_begin_drag_and_drop'. */
static struct frame *x_dnd_return_frame_object;
-/* The last toplevel window the mouse pointer moved over. */
+/* The last drop target window the mouse pointer moved over. This can
+ be different from `x_dnd_last_seen_toplevel' if that window had an
+ XdndProxy. */
static Window x_dnd_last_seen_window;
+/* The last toplevel the mouse pointer moved over. */
+static Window x_dnd_last_seen_toplevel;
+
/* The window where the drop happened. Normally None, but it is set
when something is actually dropped. */
static Window x_dnd_end_window;
@@ -2747,10 +2752,104 @@ x_dnd_get_wm_state_and_proto (struct x_display_info
*dpyinfo,
return rc;
}
+/* From the XDND protocol specification:
+
+ Dropping on windows that do not support XDND
+
+ Since middle clicking is the universal shortcut for pasting in X,
+ one can drop data into a window that does not support XDND by:
+
+ 1. After the mouse has been released to trigger the drop, obtain
+ ownership of XA_PRIMARY.
+
+ 2. Send a ButtonPress event and then a ButtonRelease event to the
+ deepest subwindow containing the mouse to simulate a middle click.
+ The times for these events should be the time of the actual button
+ release +1 and +2, respectively. These values will not be used by
+ anybody else, so one can unambiguously recognize the resulting
+ XConvertSelection() request.
+
+ 3. If a request for XA_PRIMARY arrives bearing the timestamp of
+ either the ButtonPress or the ButtonRelease event, treat it as a
+ request for XdndSelection. Note that you must use the X data
+ types instead of the MIME types in this case. (e.g. XA_STRING
+ instead of text/plain). */
+static void
+x_dnd_send_unsupported_drop (struct x_display_info *dpyinfo, Window
target_window,
+ int root_x, int root_y, Time before)
+{
+ XEvent event;
+ int dest_x, dest_y;
+ Window child_return, child;
+ Lisp_Object frame;
+ int i;
+
+ for (i = 0; i < x_dnd_n_targets; ++i)
+ {
+ if (x_dnd_targets[i] == XA_STRING
+ || x_dnd_targets[i] == dpyinfo->Xatom_COMPOUND_TEXT
+ || x_dnd_targets[i] == dpyinfo->Xatom_UTF8_STRING)
+ break;
+ }
+
+ if (i == x_dnd_n_targets)
+ return;
+
+ event.xbutton.type = ButtonPress;
+ event.xbutton.serial = 0;
+ event.xbutton.send_event = True;
+ event.xbutton.display = dpyinfo->display;
+ event.xbutton.root = dpyinfo->root_window;
+ event.xbutton.x_root = root_x;
+ event.xbutton.y_root = root_y;
+
+ XSETFRAME (frame, x_dnd_frame);
+
+ x_catch_errors (dpyinfo->display);
+ child = dpyinfo->root_window;
+
+ while (XTranslateCoordinates (dpyinfo->display, child,
+ child, root_x, root_y, &dest_x,
+ &dest_y, &child_return)
+ && child_return != None
+ && XTranslateCoordinates (dpyinfo->display, child,
+ child_return, root_x, root_y,
+ &dest_x, &dest_y, &child))
+ {
+ child = child_return;
+ root_x = dest_x;
+ root_y = dest_y;
+ }
+
+ if (child != dpyinfo->root_window)
+ {
+ x_own_selection (QPRIMARY, Qnil, frame);
+
+ event.xbutton.window = child;
+ event.xbutton.x = dest_x;
+ event.xbutton.y = dest_y;
+ event.xbutton.state = 0;
+ event.xbutton.button = 2;
+ event.xbutton.same_screen = True;
+ event.xbutton.time = before + 1;
+ event.xbutton.time = before + 2;
+
+ x_set_pending_dnd_time (before);
+
+ XSendEvent (dpyinfo->display, child,
+ True, ButtonPressMask, &event);
+ event.xbutton.type = ButtonRelease;
+ XSendEvent (dpyinfo->display, child,
+ True, ButtonReleaseMask, &event);
+ }
+
+ x_uncatch_errors ();
+}
+
static Window
x_dnd_get_target_window (struct x_display_info *dpyinfo,
int root_x, int root_y, int *proto_out,
- int *motif_out)
+ int *motif_out, Window *toplevel_out)
{
Window child_return, child, dummy, proxy;
int dest_x_return, dest_y_return, rc, proto, motif;
@@ -2767,6 +2866,7 @@ x_dnd_get_target_window (struct x_display_info *dpyinfo,
proto = -1;
*motif_out = XM_DRAG_STYLE_NONE;
+ *toplevel_out = None;
if (x_dnd_use_toplevels)
{
@@ -2779,6 +2879,8 @@ x_dnd_get_target_window (struct x_display_info *dpyinfo,
&& FRAME_X_WINDOW (x_dnd_frame) == child)
*motif_out = XM_DRAG_STYLE_NONE;
+ *toplevel_out = child;
+
if (child != None)
{
#ifndef USE_XCB
@@ -2808,6 +2910,7 @@ x_dnd_get_target_window (struct x_display_info *dpyinfo,
{
*proto_out = -1;
*motif_out = XM_DRAG_STYLE_NONE;
+ *toplevel_out = None;
return None;
}
@@ -2841,6 +2944,7 @@ x_dnd_get_target_window (struct x_display_info *dpyinfo,
if (proto != -1)
{
*proto_out = proto;
+ *toplevel_out = overlay_window;
x_uncatch_errors_after_check ();
return proxy;
@@ -2863,6 +2967,7 @@ x_dnd_get_target_window (struct x_display_info *dpyinfo,
if (proto != -1)
{
+ *toplevel_out = dpyinfo->root_window;
*proto_out = proto;
return proxy;
}
@@ -2871,6 +2976,7 @@ x_dnd_get_target_window (struct x_display_info *dpyinfo,
/* No toplevel was found and the overlay and root windows were
not proxies, so return None. */
*proto_out = -1;
+ *toplevel_out = dpyinfo->root_window;
return None;
}
@@ -2894,21 +3000,6 @@ x_dnd_get_target_window (struct x_display_info *dpyinfo,
break;
}
- proxy = x_dnd_get_window_proxy (dpyinfo, child_return);
-
- if (proxy != None)
- {
- proto = x_dnd_get_window_proto (dpyinfo, proxy);
-
- if (proto != -1)
- {
- *proto_out = proto;
-
- x_uncatch_errors ();
- return proxy;
- }
- }
-
if (child_return)
{
if (x_dnd_get_wm_state_and_proto (dpyinfo, child_return,
@@ -2919,11 +3010,28 @@ x_dnd_get_target_window (struct x_display_info *dpyinfo,
{
*proto_out = proto;
*motif_out = motif;
+ *toplevel_out = child_return;
x_uncatch_errors ();
return child_return;
}
+ proxy = x_dnd_get_window_proxy (dpyinfo, child_return);
+
+ if (proxy != None)
+ {
+ proto = x_dnd_get_window_proto (dpyinfo, proxy);
+
+ if (proto != -1)
+ {
+ *proto_out = proto;
+ *toplevel_out = child_return;
+
+ x_uncatch_errors ();
+ return proxy;
+ }
+ }
+
rc = XTranslateCoordinates (dpyinfo->display,
child, child_return,
dest_x_return, dest_y_return,
@@ -2934,6 +3042,7 @@ x_dnd_get_target_window (struct x_display_info *dpyinfo,
{
x_uncatch_errors_after_check ();
*proto_out = -1;
+ *toplevel_out = dpyinfo->root_window;
return None;
}
}
@@ -2956,6 +3065,7 @@ x_dnd_get_target_window (struct x_display_info *dpyinfo,
if (proto != -1)
{
*proto_out = proto;
+ *toplevel_out = child;
return proxy;
}
}
@@ -2993,6 +3103,7 @@ x_dnd_get_target_window (struct x_display_info *dpyinfo,
if (proto != -1)
{
*proto_out = proto;
+ *toplevel_out = overlay_window;
x_uncatch_errors_after_check ();
return proxy;
@@ -3014,6 +3125,7 @@ x_dnd_get_target_window (struct x_display_info *dpyinfo,
if (proto != -1)
{
+ *toplevel_out = child;
*proto_out = proto;
return proxy;
}
@@ -3021,6 +3133,7 @@ x_dnd_get_target_window (struct x_display_info *dpyinfo,
}
*proto_out = x_dnd_get_window_proto (dpyinfo, child);
+ *toplevel_out = child;
return child;
#endif
}
@@ -3370,6 +3483,7 @@ x_dnd_cleanup_drag_and_drop (void *frame)
x_dnd_end_window = x_dnd_last_seen_window;
x_dnd_last_seen_window = None;
+ x_dnd_last_seen_toplevel = None;
x_dnd_in_progress = false;
x_set_dnd_targets (NULL, 0);
}
@@ -9236,6 +9350,7 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time,
Atom xaction,
x_dnd_in_progress = true;
x_dnd_frame = f;
x_dnd_last_seen_window = None;
+ x_dnd_last_seen_toplevel = None;
x_dnd_last_protocol_version = -1;
x_dnd_last_motif_style = XM_DRAG_STYLE_NONE;
x_dnd_mouse_rect_target = None;
@@ -9402,6 +9517,7 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time,
Atom xaction,
x_dnd_end_window = x_dnd_last_seen_window;
x_dnd_last_seen_window = None;
+ x_dnd_last_seen_toplevel = None;
x_dnd_in_progress = false;
x_dnd_frame = NULL;
x_set_dnd_targets (NULL, 0);
@@ -13053,7 +13169,7 @@ x_dnd_update_state (struct x_display_info *dpyinfo,
Time timestamp)
{
int root_x, root_y, dummy_x, dummy_y, target_proto, motif_style;
unsigned int dummy_mask;
- Window dummy, dummy_child, target;
+ Window dummy, dummy_child, target, toplevel;
xm_top_level_leave_message lmsg;
xm_top_level_enter_message emsg;
xm_drag_motion_message dmsg;
@@ -13068,14 +13184,33 @@ x_dnd_update_state (struct x_display_info *dpyinfo,
Time timestamp)
{
target = x_dnd_get_target_window (dpyinfo, root_x,
root_y, &target_proto,
- &motif_style);
+ &motif_style, &toplevel);
- if (target != x_dnd_last_seen_window)
+ if (toplevel != x_dnd_last_seen_toplevel)
{
- if (target != FRAME_OUTER_WINDOW (x_dnd_frame)
+ if (toplevel != FRAME_OUTER_WINDOW (x_dnd_frame)
&& x_dnd_return_frame == 1)
x_dnd_return_frame = 2;
+ if (x_dnd_return_frame == 2
+ && x_any_window_to_frame (dpyinfo, toplevel))
+ {
+ x_dnd_end_window = x_dnd_last_seen_window;
+ x_dnd_last_seen_window = None;
+ x_dnd_last_seen_toplevel = None;
+ x_dnd_in_progress = false;
+ x_dnd_return_frame_object
+ = x_any_window_to_frame (dpyinfo, toplevel);
+ x_dnd_return_frame = 3;
+ x_dnd_waiting_for_finish = false;
+ target = None;
+ }
+
+ x_dnd_last_seen_toplevel = toplevel;
+ }
+
+ if (target != x_dnd_last_seen_window)
+ {
if (x_dnd_last_seen_window != None
&& x_dnd_last_protocol_version != -1
&& x_dnd_last_seen_window != FRAME_OUTER_WINDOW (x_dnd_frame))
@@ -13099,19 +13234,6 @@ x_dnd_update_state (struct x_display_info *dpyinfo,
Time timestamp)
x_dnd_last_seen_window, &lmsg);
}
- if (x_dnd_return_frame == 2
- && x_any_window_to_frame (dpyinfo, target))
- {
- x_dnd_end_window = x_dnd_last_seen_window;
- x_dnd_last_seen_window = None;
- x_dnd_in_progress = false;
- x_dnd_return_frame_object
- = x_any_window_to_frame (dpyinfo, target);
- x_dnd_return_frame = 3;
- x_dnd_waiting_for_finish = false;
- target = None;
- }
-
x_dnd_action = None;
x_dnd_last_seen_window = target;
x_dnd_last_protocol_version = target_proto;
@@ -13208,6 +13330,7 @@ x_dnd_update_state (struct x_display_info *dpyinfo,
Time timestamp)
x_dnd_end_window = x_dnd_last_seen_window;
x_dnd_last_seen_window = None;
+ x_dnd_last_seen_toplevel = None;
x_dnd_in_progress = false;
x_dnd_waiting_for_finish = false;
x_dnd_frame = NULL;
@@ -14681,7 +14804,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
if (x_dnd_in_progress
&& dpyinfo == FRAME_DISPLAY_INFO (x_dnd_frame))
{
- Window target;
+ Window target, toplevel;
int target_proto, motif_style;
xm_top_level_leave_message lmsg;
xm_top_level_enter_message emsg;
@@ -14699,14 +14822,33 @@ handle_one_xevent (struct x_display_info *dpyinfo,
event->xmotion.x_root,
event->xmotion.y_root,
&target_proto,
- &motif_style);
+ &motif_style, &toplevel);
- if (target != x_dnd_last_seen_window)
+ if (toplevel != x_dnd_last_seen_toplevel)
{
- if (target != FRAME_OUTER_WINDOW (x_dnd_frame)
+ if (toplevel != FRAME_OUTER_WINDOW (x_dnd_frame)
&& x_dnd_return_frame == 1)
x_dnd_return_frame = 2;
+ if (x_dnd_return_frame == 2
+ && x_any_window_to_frame (dpyinfo, toplevel))
+ {
+ x_dnd_end_window = x_dnd_last_seen_window;
+ x_dnd_last_seen_window = None;
+ x_dnd_last_seen_toplevel = None;
+ x_dnd_in_progress = false;
+ x_dnd_return_frame_object
+ = x_any_window_to_frame (dpyinfo, toplevel);
+ x_dnd_return_frame = 3;
+ x_dnd_waiting_for_finish = false;
+ target = None;
+ }
+
+ x_dnd_last_seen_toplevel = toplevel;
+ }
+
+ if (target != x_dnd_last_seen_window)
+ {
if (x_dnd_last_seen_window != None
&& x_dnd_last_protocol_version != -1
&& x_dnd_last_seen_window != FRAME_OUTER_WINDOW
(x_dnd_frame))
@@ -14751,19 +14893,6 @@ handle_one_xevent (struct x_display_info *dpyinfo,
}
}
- if (x_dnd_return_frame == 2
- && x_any_window_to_frame (dpyinfo, target))
- {
- x_dnd_end_window = x_dnd_last_seen_window;
- x_dnd_last_seen_window = None;
- x_dnd_in_progress = false;
- x_dnd_return_frame_object
- = x_any_window_to_frame (dpyinfo, target);
- x_dnd_return_frame = 3;
- x_dnd_waiting_for_finish = false;
- target = None;
- }
-
x_dnd_action = None;
x_dnd_last_seen_window = target;
x_dnd_last_protocol_version = target_proto;
@@ -15311,11 +15440,19 @@ handle_one_xevent (struct x_display_info *dpyinfo,
x_dnd_waiting_for_motif_finish = 1;
}
}
+ else
+ {
+ x_set_pending_dnd_time (event->xbutton.time);
+ x_dnd_send_unsupported_drop (dpyinfo,
x_dnd_last_seen_window,
+ event->xbutton.x_root,
event->xbutton.y_root,
+ event->xbutton.time);
+ }
}
x_dnd_last_protocol_version = -1;
x_dnd_last_motif_style = XM_DRAG_STYLE_NONE;
x_dnd_last_seen_window = None;
+ x_dnd_last_seen_toplevel = None;
x_dnd_frame = NULL;
x_set_dnd_targets (NULL, 0);
}
@@ -16136,7 +16273,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
if (x_dnd_in_progress
&& dpyinfo == FRAME_DISPLAY_INFO (x_dnd_frame))
{
- Window target;
+ Window target, toplevel;
int target_proto, motif_style;
/* Sometimes the drag-and-drop operation starts with the
@@ -16151,14 +16288,34 @@ handle_one_xevent (struct x_display_info *dpyinfo,
xev->root_x,
xev->root_y,
&target_proto,
- &motif_style);
+ &motif_style,
+ &toplevel);
- if (target != x_dnd_last_seen_window)
+ if (toplevel != x_dnd_last_seen_toplevel)
{
- if (target != FRAME_OUTER_WINDOW (x_dnd_frame)
+ if (toplevel != FRAME_OUTER_WINDOW (x_dnd_frame)
&& x_dnd_return_frame == 1)
x_dnd_return_frame = 2;
+ if (x_dnd_return_frame == 2
+ && x_any_window_to_frame (dpyinfo, toplevel))
+ {
+ x_dnd_end_window = x_dnd_last_seen_window;
+ x_dnd_last_seen_window = None;
+ x_dnd_last_seen_toplevel = None;
+ x_dnd_in_progress = false;
+ x_dnd_return_frame_object
+ = x_any_window_to_frame (dpyinfo, toplevel);
+ x_dnd_return_frame = 3;
+ x_dnd_waiting_for_finish = false;
+ target = None;
+ }
+
+ x_dnd_last_seen_toplevel = toplevel;
+ }
+
+ if (target != x_dnd_last_seen_window)
+ {
if (x_dnd_last_seen_window != None
&& x_dnd_last_protocol_version != -1
&& x_dnd_last_seen_window != FRAME_OUTER_WINDOW
(x_dnd_frame))
@@ -16205,19 +16362,6 @@ handle_one_xevent (struct x_display_info *dpyinfo,
}
}
- if (x_dnd_return_frame == 2
- && x_any_window_to_frame (dpyinfo, target))
- {
- x_dnd_end_window = x_dnd_last_seen_window;
- x_dnd_last_seen_window = None;
- x_dnd_in_progress = false;
- x_dnd_return_frame_object
- = x_any_window_to_frame (dpyinfo, target);
- x_dnd_return_frame = 3;
- x_dnd_waiting_for_finish = false;
- target = None;
- }
-
x_dnd_action = None;
x_dnd_last_seen_window = target;
x_dnd_last_protocol_version = target_proto;
@@ -16458,11 +16602,18 @@ handle_one_xevent (struct x_display_info *dpyinfo,
x_dnd_waiting_for_motif_finish = 1;
}
}
+ else
+ {
+ x_set_pending_dnd_time (xev->time);
+ x_dnd_send_unsupported_drop (dpyinfo,
x_dnd_last_seen_window,
+ xev->root_x,
xev->root_y, xev->time);
+ }
}
x_dnd_last_protocol_version = -1;
x_dnd_last_motif_style = XM_DRAG_STYLE_NONE;
x_dnd_last_seen_window = None;
+ x_dnd_last_seen_toplevel = None;
x_dnd_frame = NULL;
x_set_dnd_targets (NULL, 0);
diff --git a/src/xterm.h b/src/xterm.h
index 5627fd23c5..79dee6a569 100644
--- a/src/xterm.h
+++ b/src/xterm.h
@@ -1489,6 +1489,8 @@ extern void x_clipboard_manager_save_all (void);
extern Lisp_Object x_timestamp_for_selection (struct x_display_info *,
Lisp_Object);
+extern void x_set_pending_dnd_time (Time);
+extern void x_own_selection (Lisp_Object, Lisp_Object, Lisp_Object);
#ifdef USE_GTK
extern bool xg_set_icon (struct frame *, Lisp_Object);
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- master 7f6c925383: Add support for dragging text onto windows that don't support any DND protocol,
Po Lu <=