[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
master b4fc5bedb8: Use _NET_CLIENT_LIST_STACKING to optimize drag and dr
From: |
Po Lu |
Subject: |
master b4fc5bedb8: Use _NET_CLIENT_LIST_STACKING to optimize drag and drop window discovery |
Date: |
Thu, 24 Mar 2022 23:10:08 -0400 (EDT) |
branch: master
commit b4fc5bedb8fa3fbc48731da6fced2ba04efad449
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>
Use _NET_CLIENT_LIST_STACKING to optimize drag and drop window discovery
* src/xterm.c (struct x_client_list_window): New struct.
(x_dnd_free_toplevels, x_dnd_compute_toplevels)
(x_dnd_get_target_window_1): New functions.
(x_dnd_get_target_window): Search in the toplevel list if it
exists.
(x_dnd_cleanup_drag_and_drop): Clean up toplevel list.
(x_dnd_begin_drag_and_drop): Compute toplevel list if the
window manager supports it.
(handle_one_xevent): Update the toplevel list if prudent.
---
src/xterm.c | 316 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 313 insertions(+), 3 deletions(-)
diff --git a/src/xterm.c b/src/xterm.c
index 7a16704d6e..0267ba7ec1 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -820,11 +820,140 @@ static struct frame *x_dnd_frame;
static XWindowAttributes x_dnd_old_window_attrs;
static bool x_dnd_unwind_flag;
+struct x_client_list_window
+{
+ Window window;
+ Display *dpy;
+ int x, y;
+ int width, height;
+ bool visible_p;
+ long previous_event_mask;
+
+ struct x_client_list_window *next;
+};
+
+static struct x_client_list_window *x_dnd_toplevels = NULL;
+static bool x_dnd_use_toplevels;
+
+static void
+x_dnd_free_toplevels (void)
+{
+ struct x_client_list_window *last;
+ struct x_client_list_window *tem = x_dnd_toplevels;
+
+ while (tem)
+ {
+ last = tem;
+ tem = tem->next;
+
+ x_catch_errors (last->dpy);
+ XSelectInput (last->dpy, last->window,
+ last->previous_event_mask);
+ x_uncatch_errors ();
+ xfree (last);
+ }
+
+ x_dnd_toplevels = NULL;
+}
+
+static int
+x_dnd_compute_toplevels (struct x_display_info *dpyinfo)
+{
+ Atom type;
+ Window *toplevels, child;
+ int format, rc, dest_x, dest_y;
+ unsigned long nitems, bytes_after;
+ unsigned char *data = NULL;
+ unsigned long i;
+ XWindowAttributes attrs;
+ struct x_client_list_window *tem;
+
+ rc = XGetWindowProperty (dpyinfo->display, dpyinfo->root_window,
+ dpyinfo->Xatom_net_client_list_stacking,
+ 0, LONG_MAX, False, XA_WINDOW, &type,
+ &format, &nitems, &bytes_after, &data);
+
+ if (rc != Success)
+ return 1;
+
+ if (format != 32 || type != XA_WINDOW)
+ {
+ XFree (data);
+ return 1;
+ }
+
+ toplevels = (Window *) data;
+
+ /* Actually right because _NET_CLIENT_LIST_STACKING has bottom-up
+ order. */
+ for (i = 0; i < nitems; ++i)
+ {
+ x_catch_errors (dpyinfo->display);
+ rc = (XGetWindowAttributes (dpyinfo->display,
+ toplevels[i], &attrs)
+ && !x_had_errors_p (dpyinfo->display));
+
+ if (rc)
+ rc = (XTranslateCoordinates (dpyinfo->display, toplevels[i],
+ attrs.root, -attrs.border_width,
+ -attrs.border_width, &dest_x,
+ &dest_y, &child)
+ && !x_had_errors_p (dpyinfo->display));
+ x_uncatch_errors_after_check ();
+
+ if (rc)
+ {
+ tem = xmalloc (sizeof *tem);
+ tem->window = toplevels[i];
+ tem->dpy = dpyinfo->display;
+ tem->x = dest_x;
+ tem->y = dest_y;
+ tem->width = attrs.width + attrs.border_width;
+ tem->height = attrs.height + attrs.border_width;
+ tem->visible_p = (attrs.map_state == IsViewable);
+ tem->next = x_dnd_toplevels;
+ tem->previous_event_mask = attrs.your_event_mask;
+
+ x_catch_errors (dpyinfo->display);
+ XSelectInput (dpyinfo->display, toplevels[i],
+ attrs.your_event_mask | StructureNotifyMask);
+ x_uncatch_errors ();
+
+ x_dnd_toplevels = tem;
+ }
+ }
+
+ return 0;
+}
+
#define X_DND_SUPPORTED_VERSION 5
static int x_dnd_get_window_proto (struct x_display_info *, Window);
static Window x_dnd_get_window_proxy (struct x_display_info *, Window);
+static Window
+x_dnd_get_target_window_1 (struct x_display_info *dpyinfo,
+ int root_x, int root_y)
+{
+ struct x_client_list_window *tem;
+
+ /* Loop through x_dnd_toplevels until we find the toplevel where
+ root_x and root_y are. */
+
+ for (tem = x_dnd_toplevels; tem; tem = tem->next)
+ {
+ if (!tem->visible_p)
+ continue;
+
+ if (root_x >= tem->x && root_y >= tem->y
+ && root_x < tem->x + tem->width
+ && root_y < tem->y + tem->height)
+ return tem->window;
+ }
+
+ return None;
+}
+
static Window
x_dnd_get_target_window (struct x_display_info *dpyinfo,
int root_x, int root_y, int *proto_out)
@@ -841,6 +970,76 @@ x_dnd_get_target_window (struct x_display_info *dpyinfo,
proto = -1;
+ if (x_dnd_use_toplevels)
+ {
+ child = x_dnd_get_target_window_1 (dpyinfo, root_x, root_y);
+
+ if (child != None)
+ {
+ 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;
+ return proxy;
+ }
+ }
+
+ *proto_out = x_dnd_get_window_proto (dpyinfo, child);
+ return child;
+ }
+
+ /* Then look at the composite overlay window. */
+#if defined HAVE_XCOMPOSITE && (XCOMPOSITE_MAJOR > 0 || XCOMPOSITE_MINOR > 2)
+ if (dpyinfo->composite_supported_p
+ && (dpyinfo->composite_major > 0
+ || dpyinfo->composite_minor > 2))
+ {
+ if (XGetSelectionOwner (dpyinfo->display,
+ dpyinfo->Xatom_NET_WM_CM_Sn) != None)
+ {
+ x_catch_errors (dpyinfo->display);
+ overlay_window = XCompositeGetOverlayWindow (dpyinfo->display,
+
dpyinfo->root_window);
+ XCompositeReleaseOverlayWindow (dpyinfo->display,
+ dpyinfo->root_window);
+ if (!x_had_errors_p (dpyinfo->display))
+ {
+ XGetWindowAttributes (dpyinfo->display, overlay_window,
&attrs);
+
+ if (attrs.map_state == IsViewable)
+ {
+ proxy = x_dnd_get_window_proxy (dpyinfo, overlay_window);
+
+ if (proxy != None)
+ {
+ proto = x_dnd_get_window_proto (dpyinfo, proxy);
+
+ if (proto != -1)
+ {
+ *proto_out = proto;
+ x_uncatch_errors_after_check ();
+
+ return proxy;
+ }
+ }
+ }
+ }
+ x_uncatch_errors_after_check ();
+ }
+ }
+#endif
+
+ /* No toplevel was found and the overlay window was not a proxy,
+ so return None. */
+ *proto_out = -1;
+ return None;
+ }
+
/* Not strictly necessary, but satisfies GCC. */
child = dpyinfo->root_window;
@@ -1005,7 +1204,7 @@ x_dnd_get_window_proto (struct x_display_info *dpyinfo,
Window wdesc)
unsigned long n, left;
bool had_errors;
- if (wdesc == None || wdesc == FRAME_X_WINDOW (x_dnd_frame))
+ if (wdesc == None || wdesc == FRAME_OUTER_WINDOW (x_dnd_frame))
return -1;
x_catch_errors (dpyinfo->display);
@@ -1182,6 +1381,9 @@ x_dnd_cleanup_drag_and_drop (void *frame)
x_dnd_waiting_for_finish = false;
+ if (x_dnd_use_toplevels)
+ x_dnd_free_toplevels ();
+
FRAME_DISPLAY_INFO (f)->grabbed = 0;
#ifdef USE_GTK
current_hold_quit = NULL;
@@ -7036,6 +7238,18 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time,
Atom xaction,
x_dnd_return_frame = 0;
x_dnd_waiting_for_finish = false;
x_dnd_end_window = None;
+ x_dnd_use_toplevels
+ = x_wm_supports (f, FRAME_DISPLAY_INFO
(f)->Xatom_net_client_list_stacking);
+ x_dnd_toplevels = NULL;
+
+ if (x_dnd_use_toplevels)
+ {
+ if (x_dnd_compute_toplevels (FRAME_DISPLAY_INFO (f)))
+ {
+ x_dnd_free_toplevels ();
+ x_dnd_use_toplevels = false;
+ }
+ }
if (return_frame_p)
x_dnd_return_frame = 1;
@@ -7128,6 +7342,9 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time,
Atom xaction,
x_dnd_waiting_for_finish = false;
}
+ if (x_dnd_use_toplevels)
+ x_dnd_free_toplevels ();
+
FRAME_DISPLAY_INFO (f)->grabbed = 0;
#ifdef USE_GTK
current_hold_quit = NULL;
@@ -7162,6 +7379,8 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time,
Atom xaction,
return action;
}
+ if (x_dnd_use_toplevels)
+ x_dnd_free_toplevels ();
FRAME_DISPLAY_INFO (f)->grabbed = 0;
/* Emacs can't respond to DND events inside the nested event
@@ -11292,8 +11511,22 @@ handle_one_xevent (struct x_display_info *dpyinfo,
if (event->xproperty.window == dpyinfo->root_window
&& (event->xproperty.atom == dpyinfo->Xatom_net_client_list_stacking
|| event->xproperty.atom == dpyinfo->Xatom_net_current_desktop)
- && x_dnd_in_progress)
- x_dnd_update_state (dpyinfo);
+ && x_dnd_in_progress
+ && dpyinfo == FRAME_DISPLAY_INFO (x_dnd_frame))
+ {
+ if (x_dnd_use_toplevels)
+ {
+ x_dnd_free_toplevels ();
+
+ if (x_dnd_compute_toplevels (dpyinfo))
+ {
+ x_dnd_free_toplevels ();
+ x_dnd_use_toplevels = false;
+ }
+ }
+
+ x_dnd_update_state (dpyinfo);
+ }
x_handle_property_notify (&event->xproperty);
xft_settings_event (dpyinfo, event);
@@ -11464,6 +11697,19 @@ handle_one_xevent (struct x_display_info *dpyinfo,
break;
case UnmapNotify:
+ if (x_dnd_in_progress && x_dnd_use_toplevels)
+ {
+ for (struct x_client_list_window *tem = x_dnd_toplevels; tem;
+ tem = tem->next)
+ {
+ if (tem->window == event->xmap.window)
+ {
+ tem->visible_p = false;
+ break;
+ }
+ }
+ }
+
/* Redo the mouse-highlight after the tooltip has gone. */
if (event->xunmap.window == tip_window)
{
@@ -11511,6 +11757,20 @@ handle_one_xevent (struct x_display_info *dpyinfo,
if (x_dnd_in_progress)
x_dnd_update_state (dpyinfo);
+
+ if (x_dnd_in_progress && x_dnd_use_toplevels)
+ {
+ for (struct x_client_list_window *tem = x_dnd_toplevels; tem;
+ tem = tem->next)
+ {
+ if (tem->window == event->xmap.window)
+ {
+ tem->visible_p = true;
+ break;
+ }
+ }
+ }
+
/* We use x_top_window_to_frame because map events can
come for sub-windows and they don't mean that the
frame is visible. */
@@ -12287,6 +12547,56 @@ handle_one_xevent (struct x_display_info *dpyinfo,
configureEvent = next_event;
}
+ if (x_dnd_in_progress && x_dnd_use_toplevels)
+ {
+ int rc, dest_x, dest_y;
+ Window child;
+ struct x_client_list_window *tem, *last = NULL;
+
+ for (tem = x_dnd_toplevels; tem; last = tem, tem = tem->next)
+ {
+ /* Not completely right, since the parent could be
+ unmapped, but good enough. */
+
+ if (tem->window == configureEvent.xconfigure.window)
+ {
+ x_catch_errors (dpyinfo->display);
+ rc = (XTranslateCoordinates (dpyinfo->display,
+ configureEvent.xconfigure.window,
+ dpyinfo->root_window,
+
-configureEvent.xconfigure.border_width,
+
-configureEvent.xconfigure.border_width,
+ &dest_x, &dest_y, &child)
+ && !x_had_errors_p (dpyinfo->display));
+ x_uncatch_errors_after_check ();
+
+ if (rc)
+ {
+ tem->x = dest_x;
+ tem->y = dest_y;
+ tem->width = (configureEvent.xconfigure.width
+ + configureEvent.xconfigure.border_width);
+ tem->height = (configureEvent.xconfigure.height
+ + configureEvent.xconfigure.border_width);
+ }
+ else
+ {
+ /* The window was probably destroyed, so get rid
+ of it. */
+
+ if (!last)
+ x_dnd_toplevels = tem->next;
+ else
+ last->next = tem->next;
+
+ xfree (tem);
+ }
+
+ break;
+ }
+ }
+ }
+
#if defined HAVE_GTK3 && defined USE_TOOLKIT_SCROLL_BARS
struct scroll_bar *bar = x_window_to_scroll_bar (dpyinfo->display,
configureEvent.xconfigure.window, 2);
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- master b4fc5bedb8: Use _NET_CLIENT_LIST_STACKING to optimize drag and drop window discovery,
Po Lu <=