diff options
| author | Po Lu | 2022-03-25 11:09:43 +0800 |
|---|---|---|
| committer | Po Lu | 2022-03-25 11:09:43 +0800 |
| commit | b4fc5bedb8fa3fbc48731da6fced2ba04efad449 (patch) | |
| tree | 0f3be69b519e52ac8d223b048363e0d682e4a449 | |
| parent | 392d66f6f5d9962d0b0f96decbebd9db00cce1ab (diff) | |
| download | emacs-b4fc5bedb8fa3fbc48731da6fced2ba04efad449.tar.gz emacs-b4fc5bedb8fa3fbc48731da6fced2ba04efad449.zip | |
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.
| -rw-r--r-- | src/xterm.c | 316 |
1 files changed, 313 insertions, 3 deletions
diff --git a/src/xterm.c b/src/xterm.c index 7a16704d6e0..0267ba7ec1b 100644 --- a/src/xterm.c +++ b/src/xterm.c | |||
| @@ -820,12 +820,141 @@ static struct frame *x_dnd_frame; | |||
| 820 | static XWindowAttributes x_dnd_old_window_attrs; | 820 | static XWindowAttributes x_dnd_old_window_attrs; |
| 821 | static bool x_dnd_unwind_flag; | 821 | static bool x_dnd_unwind_flag; |
| 822 | 822 | ||
| 823 | struct x_client_list_window | ||
| 824 | { | ||
| 825 | Window window; | ||
| 826 | Display *dpy; | ||
| 827 | int x, y; | ||
| 828 | int width, height; | ||
| 829 | bool visible_p; | ||
| 830 | long previous_event_mask; | ||
| 831 | |||
| 832 | struct x_client_list_window *next; | ||
| 833 | }; | ||
| 834 | |||
| 835 | static struct x_client_list_window *x_dnd_toplevels = NULL; | ||
| 836 | static bool x_dnd_use_toplevels; | ||
| 837 | |||
| 838 | static void | ||
| 839 | x_dnd_free_toplevels (void) | ||
| 840 | { | ||
| 841 | struct x_client_list_window *last; | ||
| 842 | struct x_client_list_window *tem = x_dnd_toplevels; | ||
| 843 | |||
| 844 | while (tem) | ||
| 845 | { | ||
| 846 | last = tem; | ||
| 847 | tem = tem->next; | ||
| 848 | |||
| 849 | x_catch_errors (last->dpy); | ||
| 850 | XSelectInput (last->dpy, last->window, | ||
| 851 | last->previous_event_mask); | ||
| 852 | x_uncatch_errors (); | ||
| 853 | xfree (last); | ||
| 854 | } | ||
| 855 | |||
| 856 | x_dnd_toplevels = NULL; | ||
| 857 | } | ||
| 858 | |||
| 859 | static int | ||
| 860 | x_dnd_compute_toplevels (struct x_display_info *dpyinfo) | ||
| 861 | { | ||
| 862 | Atom type; | ||
| 863 | Window *toplevels, child; | ||
| 864 | int format, rc, dest_x, dest_y; | ||
| 865 | unsigned long nitems, bytes_after; | ||
| 866 | unsigned char *data = NULL; | ||
| 867 | unsigned long i; | ||
| 868 | XWindowAttributes attrs; | ||
| 869 | struct x_client_list_window *tem; | ||
| 870 | |||
| 871 | rc = XGetWindowProperty (dpyinfo->display, dpyinfo->root_window, | ||
| 872 | dpyinfo->Xatom_net_client_list_stacking, | ||
| 873 | 0, LONG_MAX, False, XA_WINDOW, &type, | ||
| 874 | &format, &nitems, &bytes_after, &data); | ||
| 875 | |||
| 876 | if (rc != Success) | ||
| 877 | return 1; | ||
| 878 | |||
| 879 | if (format != 32 || type != XA_WINDOW) | ||
| 880 | { | ||
| 881 | XFree (data); | ||
| 882 | return 1; | ||
| 883 | } | ||
| 884 | |||
| 885 | toplevels = (Window *) data; | ||
| 886 | |||
| 887 | /* Actually right because _NET_CLIENT_LIST_STACKING has bottom-up | ||
| 888 | order. */ | ||
| 889 | for (i = 0; i < nitems; ++i) | ||
| 890 | { | ||
| 891 | x_catch_errors (dpyinfo->display); | ||
| 892 | rc = (XGetWindowAttributes (dpyinfo->display, | ||
| 893 | toplevels[i], &attrs) | ||
| 894 | && !x_had_errors_p (dpyinfo->display)); | ||
| 895 | |||
| 896 | if (rc) | ||
| 897 | rc = (XTranslateCoordinates (dpyinfo->display, toplevels[i], | ||
| 898 | attrs.root, -attrs.border_width, | ||
| 899 | -attrs.border_width, &dest_x, | ||
| 900 | &dest_y, &child) | ||
| 901 | && !x_had_errors_p (dpyinfo->display)); | ||
| 902 | x_uncatch_errors_after_check (); | ||
| 903 | |||
| 904 | if (rc) | ||
| 905 | { | ||
| 906 | tem = xmalloc (sizeof *tem); | ||
| 907 | tem->window = toplevels[i]; | ||
| 908 | tem->dpy = dpyinfo->display; | ||
| 909 | tem->x = dest_x; | ||
| 910 | tem->y = dest_y; | ||
| 911 | tem->width = attrs.width + attrs.border_width; | ||
| 912 | tem->height = attrs.height + attrs.border_width; | ||
| 913 | tem->visible_p = (attrs.map_state == IsViewable); | ||
| 914 | tem->next = x_dnd_toplevels; | ||
| 915 | tem->previous_event_mask = attrs.your_event_mask; | ||
| 916 | |||
| 917 | x_catch_errors (dpyinfo->display); | ||
| 918 | XSelectInput (dpyinfo->display, toplevels[i], | ||
| 919 | attrs.your_event_mask | StructureNotifyMask); | ||
| 920 | x_uncatch_errors (); | ||
| 921 | |||
| 922 | x_dnd_toplevels = tem; | ||
| 923 | } | ||
| 924 | } | ||
| 925 | |||
| 926 | return 0; | ||
| 927 | } | ||
| 928 | |||
| 823 | #define X_DND_SUPPORTED_VERSION 5 | 929 | #define X_DND_SUPPORTED_VERSION 5 |
| 824 | 930 | ||
| 825 | static int x_dnd_get_window_proto (struct x_display_info *, Window); | 931 | static int x_dnd_get_window_proto (struct x_display_info *, Window); |
| 826 | static Window x_dnd_get_window_proxy (struct x_display_info *, Window); | 932 | static Window x_dnd_get_window_proxy (struct x_display_info *, Window); |
| 827 | 933 | ||
| 828 | static Window | 934 | static Window |
| 935 | x_dnd_get_target_window_1 (struct x_display_info *dpyinfo, | ||
| 936 | int root_x, int root_y) | ||
| 937 | { | ||
| 938 | struct x_client_list_window *tem; | ||
| 939 | |||
| 940 | /* Loop through x_dnd_toplevels until we find the toplevel where | ||
| 941 | root_x and root_y are. */ | ||
| 942 | |||
| 943 | for (tem = x_dnd_toplevels; tem; tem = tem->next) | ||
| 944 | { | ||
| 945 | if (!tem->visible_p) | ||
| 946 | continue; | ||
| 947 | |||
| 948 | if (root_x >= tem->x && root_y >= tem->y | ||
| 949 | && root_x < tem->x + tem->width | ||
| 950 | && root_y < tem->y + tem->height) | ||
| 951 | return tem->window; | ||
| 952 | } | ||
| 953 | |||
| 954 | return None; | ||
| 955 | } | ||
| 956 | |||
| 957 | static Window | ||
| 829 | x_dnd_get_target_window (struct x_display_info *dpyinfo, | 958 | x_dnd_get_target_window (struct x_display_info *dpyinfo, |
| 830 | int root_x, int root_y, int *proto_out) | 959 | int root_x, int root_y, int *proto_out) |
| 831 | { | 960 | { |
| @@ -841,6 +970,76 @@ x_dnd_get_target_window (struct x_display_info *dpyinfo, | |||
| 841 | 970 | ||
| 842 | proto = -1; | 971 | proto = -1; |
| 843 | 972 | ||
| 973 | if (x_dnd_use_toplevels) | ||
| 974 | { | ||
| 975 | child = x_dnd_get_target_window_1 (dpyinfo, root_x, root_y); | ||
| 976 | |||
| 977 | if (child != None) | ||
| 978 | { | ||
| 979 | proxy = x_dnd_get_window_proxy (dpyinfo, child_return); | ||
| 980 | |||
| 981 | if (proxy != None) | ||
| 982 | { | ||
| 983 | proto = x_dnd_get_window_proto (dpyinfo, proxy); | ||
| 984 | |||
| 985 | if (proto != -1) | ||
| 986 | { | ||
| 987 | *proto_out = proto; | ||
| 988 | return proxy; | ||
| 989 | } | ||
| 990 | } | ||
| 991 | |||
| 992 | *proto_out = x_dnd_get_window_proto (dpyinfo, child); | ||
| 993 | return child; | ||
| 994 | } | ||
| 995 | |||
| 996 | /* Then look at the composite overlay window. */ | ||
| 997 | #if defined HAVE_XCOMPOSITE && (XCOMPOSITE_MAJOR > 0 || XCOMPOSITE_MINOR > 2) | ||
| 998 | if (dpyinfo->composite_supported_p | ||
| 999 | && (dpyinfo->composite_major > 0 | ||
| 1000 | || dpyinfo->composite_minor > 2)) | ||
| 1001 | { | ||
| 1002 | if (XGetSelectionOwner (dpyinfo->display, | ||
| 1003 | dpyinfo->Xatom_NET_WM_CM_Sn) != None) | ||
| 1004 | { | ||
| 1005 | x_catch_errors (dpyinfo->display); | ||
| 1006 | overlay_window = XCompositeGetOverlayWindow (dpyinfo->display, | ||
| 1007 | dpyinfo->root_window); | ||
| 1008 | XCompositeReleaseOverlayWindow (dpyinfo->display, | ||
| 1009 | dpyinfo->root_window); | ||
| 1010 | if (!x_had_errors_p (dpyinfo->display)) | ||
| 1011 | { | ||
| 1012 | XGetWindowAttributes (dpyinfo->display, overlay_window, &attrs); | ||
| 1013 | |||
| 1014 | if (attrs.map_state == IsViewable) | ||
| 1015 | { | ||
| 1016 | proxy = x_dnd_get_window_proxy (dpyinfo, overlay_window); | ||
| 1017 | |||
| 1018 | if (proxy != None) | ||
| 1019 | { | ||
| 1020 | proto = x_dnd_get_window_proto (dpyinfo, proxy); | ||
| 1021 | |||
| 1022 | if (proto != -1) | ||
| 1023 | { | ||
| 1024 | *proto_out = proto; | ||
| 1025 | x_uncatch_errors_after_check (); | ||
| 1026 | |||
| 1027 | return proxy; | ||
| 1028 | } | ||
| 1029 | } | ||
| 1030 | } | ||
| 1031 | } | ||
| 1032 | x_uncatch_errors_after_check (); | ||
| 1033 | } | ||
| 1034 | } | ||
| 1035 | #endif | ||
| 1036 | |||
| 1037 | /* No toplevel was found and the overlay window was not a proxy, | ||
| 1038 | so return None. */ | ||
| 1039 | *proto_out = -1; | ||
| 1040 | return None; | ||
| 1041 | } | ||
| 1042 | |||
| 844 | /* Not strictly necessary, but satisfies GCC. */ | 1043 | /* Not strictly necessary, but satisfies GCC. */ |
| 845 | child = dpyinfo->root_window; | 1044 | child = dpyinfo->root_window; |
| 846 | 1045 | ||
| @@ -1005,7 +1204,7 @@ x_dnd_get_window_proto (struct x_display_info *dpyinfo, Window wdesc) | |||
| 1005 | unsigned long n, left; | 1204 | unsigned long n, left; |
| 1006 | bool had_errors; | 1205 | bool had_errors; |
| 1007 | 1206 | ||
| 1008 | if (wdesc == None || wdesc == FRAME_X_WINDOW (x_dnd_frame)) | 1207 | if (wdesc == None || wdesc == FRAME_OUTER_WINDOW (x_dnd_frame)) |
| 1009 | return -1; | 1208 | return -1; |
| 1010 | 1209 | ||
| 1011 | x_catch_errors (dpyinfo->display); | 1210 | x_catch_errors (dpyinfo->display); |
| @@ -1182,6 +1381,9 @@ x_dnd_cleanup_drag_and_drop (void *frame) | |||
| 1182 | 1381 | ||
| 1183 | x_dnd_waiting_for_finish = false; | 1382 | x_dnd_waiting_for_finish = false; |
| 1184 | 1383 | ||
| 1384 | if (x_dnd_use_toplevels) | ||
| 1385 | x_dnd_free_toplevels (); | ||
| 1386 | |||
| 1185 | FRAME_DISPLAY_INFO (f)->grabbed = 0; | 1387 | FRAME_DISPLAY_INFO (f)->grabbed = 0; |
| 1186 | #ifdef USE_GTK | 1388 | #ifdef USE_GTK |
| 1187 | current_hold_quit = NULL; | 1389 | current_hold_quit = NULL; |
| @@ -7036,6 +7238,18 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, | |||
| 7036 | x_dnd_return_frame = 0; | 7238 | x_dnd_return_frame = 0; |
| 7037 | x_dnd_waiting_for_finish = false; | 7239 | x_dnd_waiting_for_finish = false; |
| 7038 | x_dnd_end_window = None; | 7240 | x_dnd_end_window = None; |
| 7241 | x_dnd_use_toplevels | ||
| 7242 | = x_wm_supports (f, FRAME_DISPLAY_INFO (f)->Xatom_net_client_list_stacking); | ||
| 7243 | x_dnd_toplevels = NULL; | ||
| 7244 | |||
| 7245 | if (x_dnd_use_toplevels) | ||
| 7246 | { | ||
| 7247 | if (x_dnd_compute_toplevels (FRAME_DISPLAY_INFO (f))) | ||
| 7248 | { | ||
| 7249 | x_dnd_free_toplevels (); | ||
| 7250 | x_dnd_use_toplevels = false; | ||
| 7251 | } | ||
| 7252 | } | ||
| 7039 | 7253 | ||
| 7040 | if (return_frame_p) | 7254 | if (return_frame_p) |
| 7041 | x_dnd_return_frame = 1; | 7255 | x_dnd_return_frame = 1; |
| @@ -7128,6 +7342,9 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, | |||
| 7128 | x_dnd_waiting_for_finish = false; | 7342 | x_dnd_waiting_for_finish = false; |
| 7129 | } | 7343 | } |
| 7130 | 7344 | ||
| 7345 | if (x_dnd_use_toplevels) | ||
| 7346 | x_dnd_free_toplevels (); | ||
| 7347 | |||
| 7131 | FRAME_DISPLAY_INFO (f)->grabbed = 0; | 7348 | FRAME_DISPLAY_INFO (f)->grabbed = 0; |
| 7132 | #ifdef USE_GTK | 7349 | #ifdef USE_GTK |
| 7133 | current_hold_quit = NULL; | 7350 | current_hold_quit = NULL; |
| @@ -7162,6 +7379,8 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, | |||
| 7162 | return action; | 7379 | return action; |
| 7163 | } | 7380 | } |
| 7164 | 7381 | ||
| 7382 | if (x_dnd_use_toplevels) | ||
| 7383 | x_dnd_free_toplevels (); | ||
| 7165 | FRAME_DISPLAY_INFO (f)->grabbed = 0; | 7384 | FRAME_DISPLAY_INFO (f)->grabbed = 0; |
| 7166 | 7385 | ||
| 7167 | /* Emacs can't respond to DND events inside the nested event | 7386 | /* Emacs can't respond to DND events inside the nested event |
| @@ -11292,8 +11511,22 @@ handle_one_xevent (struct x_display_info *dpyinfo, | |||
| 11292 | if (event->xproperty.window == dpyinfo->root_window | 11511 | if (event->xproperty.window == dpyinfo->root_window |
| 11293 | && (event->xproperty.atom == dpyinfo->Xatom_net_client_list_stacking | 11512 | && (event->xproperty.atom == dpyinfo->Xatom_net_client_list_stacking |
| 11294 | || event->xproperty.atom == dpyinfo->Xatom_net_current_desktop) | 11513 | || event->xproperty.atom == dpyinfo->Xatom_net_current_desktop) |
| 11295 | && x_dnd_in_progress) | 11514 | && x_dnd_in_progress |
| 11296 | x_dnd_update_state (dpyinfo); | 11515 | && dpyinfo == FRAME_DISPLAY_INFO (x_dnd_frame)) |
| 11516 | { | ||
| 11517 | if (x_dnd_use_toplevels) | ||
| 11518 | { | ||
| 11519 | x_dnd_free_toplevels (); | ||
| 11520 | |||
| 11521 | if (x_dnd_compute_toplevels (dpyinfo)) | ||
| 11522 | { | ||
| 11523 | x_dnd_free_toplevels (); | ||
| 11524 | x_dnd_use_toplevels = false; | ||
| 11525 | } | ||
| 11526 | } | ||
| 11527 | |||
| 11528 | x_dnd_update_state (dpyinfo); | ||
| 11529 | } | ||
| 11297 | 11530 | ||
| 11298 | x_handle_property_notify (&event->xproperty); | 11531 | x_handle_property_notify (&event->xproperty); |
| 11299 | xft_settings_event (dpyinfo, event); | 11532 | xft_settings_event (dpyinfo, event); |
| @@ -11464,6 +11697,19 @@ handle_one_xevent (struct x_display_info *dpyinfo, | |||
| 11464 | break; | 11697 | break; |
| 11465 | 11698 | ||
| 11466 | case UnmapNotify: | 11699 | case UnmapNotify: |
| 11700 | if (x_dnd_in_progress && x_dnd_use_toplevels) | ||
| 11701 | { | ||
| 11702 | for (struct x_client_list_window *tem = x_dnd_toplevels; tem; | ||
| 11703 | tem = tem->next) | ||
| 11704 | { | ||
| 11705 | if (tem->window == event->xmap.window) | ||
| 11706 | { | ||
| 11707 | tem->visible_p = false; | ||
| 11708 | break; | ||
| 11709 | } | ||
| 11710 | } | ||
| 11711 | } | ||
| 11712 | |||
| 11467 | /* Redo the mouse-highlight after the tooltip has gone. */ | 11713 | /* Redo the mouse-highlight after the tooltip has gone. */ |
| 11468 | if (event->xunmap.window == tip_window) | 11714 | if (event->xunmap.window == tip_window) |
| 11469 | { | 11715 | { |
| @@ -11511,6 +11757,20 @@ handle_one_xevent (struct x_display_info *dpyinfo, | |||
| 11511 | 11757 | ||
| 11512 | if (x_dnd_in_progress) | 11758 | if (x_dnd_in_progress) |
| 11513 | x_dnd_update_state (dpyinfo); | 11759 | x_dnd_update_state (dpyinfo); |
| 11760 | |||
| 11761 | if (x_dnd_in_progress && x_dnd_use_toplevels) | ||
| 11762 | { | ||
| 11763 | for (struct x_client_list_window *tem = x_dnd_toplevels; tem; | ||
| 11764 | tem = tem->next) | ||
| 11765 | { | ||
| 11766 | if (tem->window == event->xmap.window) | ||
| 11767 | { | ||
| 11768 | tem->visible_p = true; | ||
| 11769 | break; | ||
| 11770 | } | ||
| 11771 | } | ||
| 11772 | } | ||
| 11773 | |||
| 11514 | /* We use x_top_window_to_frame because map events can | 11774 | /* We use x_top_window_to_frame because map events can |
| 11515 | come for sub-windows and they don't mean that the | 11775 | come for sub-windows and they don't mean that the |
| 11516 | frame is visible. */ | 11776 | frame is visible. */ |
| @@ -12287,6 +12547,56 @@ handle_one_xevent (struct x_display_info *dpyinfo, | |||
| 12287 | configureEvent = next_event; | 12547 | configureEvent = next_event; |
| 12288 | } | 12548 | } |
| 12289 | 12549 | ||
| 12550 | if (x_dnd_in_progress && x_dnd_use_toplevels) | ||
| 12551 | { | ||
| 12552 | int rc, dest_x, dest_y; | ||
| 12553 | Window child; | ||
| 12554 | struct x_client_list_window *tem, *last = NULL; | ||
| 12555 | |||
| 12556 | for (tem = x_dnd_toplevels; tem; last = tem, tem = tem->next) | ||
| 12557 | { | ||
| 12558 | /* Not completely right, since the parent could be | ||
| 12559 | unmapped, but good enough. */ | ||
| 12560 | |||
| 12561 | if (tem->window == configureEvent.xconfigure.window) | ||
| 12562 | { | ||
| 12563 | x_catch_errors (dpyinfo->display); | ||
| 12564 | rc = (XTranslateCoordinates (dpyinfo->display, | ||
| 12565 | configureEvent.xconfigure.window, | ||
| 12566 | dpyinfo->root_window, | ||
| 12567 | -configureEvent.xconfigure.border_width, | ||
| 12568 | -configureEvent.xconfigure.border_width, | ||
| 12569 | &dest_x, &dest_y, &child) | ||
| 12570 | && !x_had_errors_p (dpyinfo->display)); | ||
| 12571 | x_uncatch_errors_after_check (); | ||
| 12572 | |||
| 12573 | if (rc) | ||
| 12574 | { | ||
| 12575 | tem->x = dest_x; | ||
| 12576 | tem->y = dest_y; | ||
| 12577 | tem->width = (configureEvent.xconfigure.width | ||
| 12578 | + configureEvent.xconfigure.border_width); | ||
| 12579 | tem->height = (configureEvent.xconfigure.height | ||
| 12580 | + configureEvent.xconfigure.border_width); | ||
| 12581 | } | ||
| 12582 | else | ||
| 12583 | { | ||
| 12584 | /* The window was probably destroyed, so get rid | ||
| 12585 | of it. */ | ||
| 12586 | |||
| 12587 | if (!last) | ||
| 12588 | x_dnd_toplevels = tem->next; | ||
| 12589 | else | ||
| 12590 | last->next = tem->next; | ||
| 12591 | |||
| 12592 | xfree (tem); | ||
| 12593 | } | ||
| 12594 | |||
| 12595 | break; | ||
| 12596 | } | ||
| 12597 | } | ||
| 12598 | } | ||
| 12599 | |||
| 12290 | #if defined HAVE_GTK3 && defined USE_TOOLKIT_SCROLL_BARS | 12600 | #if defined HAVE_GTK3 && defined USE_TOOLKIT_SCROLL_BARS |
| 12291 | struct scroll_bar *bar = x_window_to_scroll_bar (dpyinfo->display, | 12601 | struct scroll_bar *bar = x_window_to_scroll_bar (dpyinfo->display, |
| 12292 | configureEvent.xconfigure.window, 2); | 12602 | configureEvent.xconfigure.window, 2); |