From: Po Lu Date: Fri, 25 Mar 2022 03:09:43 +0000 (+0800) Subject: Use _NET_CLIENT_LIST_STACKING to optimize drag and drop window discovery X-Git-Tag: emacs-29.0.90~1931^2~929 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=b4fc5bedb8fa3fbc48731da6fced2ba04efad449;p=emacs.git 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. --- diff --git a/src/xterm.c b/src/xterm.c index 7a16704d6e0..0267ba7ec1b 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);