Skip to content

Commit 03106ef

Browse files
committed
meta-xwayland-dnd.c: Use two X11 DnD peer windows.
Drag from Wayland to X11 clients works fine, but with an X11 drag source, dnd only worked if the immediate next Wayland client window focused was the drag target. Moving across multiple clients never re-send XdndEnter. Alternate between two peers on each repick instead. Also trap X errors around the XdndTypeList property read - it reads a window owned by the drag source, which can be gone by then Ref: https://gitlab.gnome.org/GNOME/mutter/-/commit/fe41e69eea9627f80cf793ec58c65cf3ca79a1a6 Ref: https://gitlab.gnome.org/GNOME/mutter/-/commit/1cee0579fa67f427537e0800d67703661b0b4cda
1 parent 02efe06 commit 03106ef

1 file changed

Lines changed: 122 additions & 39 deletions

File tree

src/wayland/meta-xwayland-dnd.c

Lines changed: 122 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,10 @@ struct _MetaXWaylandDnd
5757
Time client_message_timestamp;
5858
MetaWaylandDataSource *source; /* owned by MetaWaylandDataDevice */
5959
MetaWaylandSurface *focus_surface;
60-
Window dnd_window; /* Mutter-internal window, acts as peer on wayland drop sites */
60+
Window dnd_window[2]; /* Muffin-internal windows, act as peers on wayland drop sites */
6161
Window dnd_dest; /* X11 drag dest window */
6262
guint32 last_motion_time;
63+
int current_dnd_window;
6364
};
6465

6566
enum
@@ -133,6 +134,90 @@ atom_to_action (Atom atom)
133134
return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
134135
}
135136

137+
static Window
138+
current_dnd_window (MetaXWaylandDnd *dnd)
139+
{
140+
return dnd->dnd_window[dnd->current_dnd_window];
141+
}
142+
143+
/* X11 drag sources only emit XdndLeave/XdndEnter when the target window
144+
* changes, so moving between two wayland windows has to hand the source a
145+
* different peer window or it never learns the drop site changed.
146+
*/
147+
static Window
148+
next_dnd_window (MetaXWaylandDnd *dnd)
149+
{
150+
dnd->current_dnd_window =
151+
(dnd->current_dnd_window + 1) % G_N_ELEMENTS (dnd->dnd_window);
152+
153+
return current_dnd_window (dnd);
154+
}
155+
156+
static void
157+
create_dnd_windows (MetaXWaylandDnd *dnd,
158+
Display *xdisplay)
159+
{
160+
XSetWindowAttributes attributes;
161+
guint32 version = XDND_VERSION;
162+
guint i;
163+
164+
attributes.event_mask = PropertyChangeMask | SubstructureNotifyMask;
165+
attributes.override_redirect = True;
166+
167+
for (i = 0; i < G_N_ELEMENTS (dnd->dnd_window); i++)
168+
{
169+
dnd->dnd_window[i] =
170+
XCreateWindow (xdisplay,
171+
gdk_x11_window_get_xid (gdk_get_default_root_window ()),
172+
-1, -1, 1, 1,
173+
0, /* border width */
174+
0, /* depth */
175+
InputOnly, /* class */
176+
CopyFromParent, /* visual */
177+
CWEventMask | CWOverrideRedirect,
178+
&attributes);
179+
180+
XChangeProperty (xdisplay, dnd->dnd_window[i],
181+
xdnd_atoms[ATOM_DND_AWARE],
182+
XA_ATOM, 32, PropModeReplace,
183+
(guchar*) &version, 1);
184+
}
185+
}
186+
187+
static void
188+
destroy_dnd_windows (MetaXWaylandDnd *dnd,
189+
Display *xdisplay)
190+
{
191+
guint i;
192+
193+
for (i = 0; i < G_N_ELEMENTS (dnd->dnd_window); i++)
194+
{
195+
XDestroyWindow (xdisplay, dnd->dnd_window[i]);
196+
dnd->dnd_window[i] = None;
197+
}
198+
}
199+
200+
static void
201+
hide_dnd_window (MetaXWaylandDnd *dnd,
202+
Display *xdisplay,
203+
int index)
204+
{
205+
g_assert (index < (int) G_N_ELEMENTS (dnd->dnd_window));
206+
207+
XMoveResizeWindow (xdisplay, dnd->dnd_window[index], -1, -1, 1, 1);
208+
XUnmapWindow (xdisplay, dnd->dnd_window[index]);
209+
}
210+
211+
static void
212+
hide_all_dnd_windows (MetaXWaylandDnd *dnd,
213+
Display *xdisplay)
214+
{
215+
guint i;
216+
217+
for (i = 0; i < G_N_ELEMENTS (dnd->dnd_window); i++)
218+
hide_dnd_window (dnd, xdisplay, i);
219+
}
220+
136221
static void
137222
xdnd_send_enter (MetaXWaylandDnd *dnd,
138223
Window dest)
@@ -295,7 +380,7 @@ xdnd_send_finished (MetaXWaylandDnd *dnd,
295380
xev.xclient.format = 32;
296381
xev.xclient.window = dest;
297382

298-
xev.xclient.data.l[0] = dnd->dnd_window;
383+
xev.xclient.data.l[0] = current_dnd_window (dnd);
299384

300385
if (accepted)
301386
{
@@ -325,7 +410,7 @@ xdnd_send_status (MetaXWaylandDnd *dnd,
325410
xev.xclient.format = 32;
326411
xev.xclient.window = dest;
327412

328-
xev.xclient.data.l[0] = dnd->dnd_window;
413+
xev.xclient.data.l[0] = current_dnd_window (dnd);
329414
xev.xclient.data.l[1] = 1 << 1; /* Bit 2: dest wants XdndPosition messages */
330415
xev.xclient.data.l[4] = action_to_atom (action);
331416

@@ -357,8 +442,7 @@ meta_xwayland_end_dnd_grab (MetaWaylandDataDevice *data_device,
357442
meta_wayland_data_device_end_drag (data_device);
358443
}
359444

360-
XMoveResizeWindow (xdisplay, dnd->dnd_window, -1, -1, 1, 1);
361-
XUnmapWindow (xdisplay, dnd->dnd_window);
445+
hide_all_dnd_windows (dnd, xdisplay);
362446
}
363447

364448
static void
@@ -581,6 +665,7 @@ meta_xwayland_data_source_fetch_mimetype_list (MetaWaylandDataSource *source,
581665
{
582666
MetaWaylandDataSourceXWayland *source_xwayland =
583667
META_WAYLAND_DATA_SOURCE_XWAYLAND (source);
668+
MetaX11Display *x11_display = meta_get_display ()->x11_display;
584669
Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
585670
gulong nitems_ret, bytes_after_ret, i;
586671
Atom *atoms, type_ret, utf8_string;
@@ -591,17 +676,28 @@ meta_xwayland_data_source_fetch_mimetype_list (MetaWaylandDataSource *source,
591676
if (source_mime_types->size != 0)
592677
return TRUE;
593678

679+
/* The drag source can be gone by the time we get here, and an untrapped
680+
* BadWindow is fatal to the compositor. */
681+
meta_x11_error_trap_push (x11_display);
682+
594683
utf8_string = gdk_x11_get_xatom_by_name ("UTF8_STRING");
595-
XGetWindowProperty (xdisplay, window, prop,
596-
0, /* offset */
597-
0x1fffffff, /* length */
598-
False, /* delete */
599-
AnyPropertyType,
600-
&type_ret,
601-
&format_ret,
602-
&nitems_ret,
603-
&bytes_after_ret,
604-
(guchar **) &atoms);
684+
if (XGetWindowProperty (xdisplay, window, prop,
685+
0, /* offset */
686+
0x1fffffff, /* length */
687+
False, /* delete */
688+
AnyPropertyType,
689+
&type_ret,
690+
&format_ret,
691+
&nitems_ret,
692+
&bytes_after_ret,
693+
(guchar **) &atoms) != Success)
694+
{
695+
meta_x11_error_trap_pop (x11_display);
696+
return FALSE;
697+
}
698+
699+
if (meta_x11_error_trap_pop_with_return (x11_display) != Success)
700+
return FALSE;
605701

606702
if (nitems_ret == 0 || type_ret != XA_ATOM)
607703
{
@@ -668,17 +764,21 @@ repick_drop_surface (MetaWaylandCompositor *compositor,
668764
if (focus_window &&
669765
focus_window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND)
670766
{
671-
XMapRaised (xdisplay, dnd->dnd_window);
672-
XMoveResizeWindow (xdisplay, dnd->dnd_window,
767+
Window dnd_window;
768+
769+
hide_dnd_window (dnd, xdisplay, dnd->current_dnd_window);
770+
dnd_window = next_dnd_window (dnd);
771+
772+
XMapRaised (xdisplay, dnd_window);
773+
XMoveResizeWindow (xdisplay, dnd_window,
673774
focus_window->rect.x,
674775
focus_window->rect.y,
675776
focus_window->rect.width,
676777
focus_window->rect.height);
677778
}
678779
else
679780
{
680-
XMoveResizeWindow (xdisplay, dnd->dnd_window, -1, -1, 1, 1);
681-
XUnmapWindow (xdisplay, dnd->dnd_window);
781+
hide_all_dnd_windows (dnd, xdisplay);
682782
}
683783
}
684784

@@ -945,8 +1045,7 @@ meta_xwayland_init_dnd (Display *xdisplay)
9451045
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
9461046
MetaXWaylandManager *manager = &compositor->xwayland_manager;
9471047
MetaXWaylandDnd *dnd = manager->dnd;
948-
XSetWindowAttributes attributes;
949-
guint32 i, version = XDND_VERSION;
1048+
guint32 i;
9501049

9511050
g_assert (manager->dnd == NULL);
9521051

@@ -955,22 +1054,7 @@ meta_xwayland_init_dnd (Display *xdisplay)
9551054
for (i = 0; i < N_DND_ATOMS; i++)
9561055
xdnd_atoms[i] = gdk_x11_get_xatom_by_name (atom_names[i]);
9571056

958-
attributes.event_mask = PropertyChangeMask | SubstructureNotifyMask;
959-
attributes.override_redirect = True;
960-
961-
dnd->dnd_window = XCreateWindow (xdisplay,
962-
gdk_x11_window_get_xid (gdk_get_default_root_window ()),
963-
-1, -1, 1, 1,
964-
0, /* border width */
965-
0, /* depth */
966-
InputOnly, /* class */
967-
CopyFromParent, /* visual */
968-
CWEventMask | CWOverrideRedirect,
969-
&attributes);
970-
XChangeProperty (xdisplay, dnd->dnd_window,
971-
xdnd_atoms[ATOM_DND_AWARE],
972-
XA_ATOM, 32, PropModeReplace,
973-
(guchar*) &version, 1);
1057+
create_dnd_windows (dnd, xdisplay);
9741058
}
9751059

9761060
void
@@ -982,8 +1066,7 @@ meta_xwayland_shutdown_dnd (Display *xdisplay)
9821066

9831067
g_assert (dnd != NULL);
9841068

985-
XDestroyWindow (xdisplay, dnd->dnd_window);
986-
dnd->dnd_window = None;
1069+
destroy_dnd_windows (dnd, xdisplay);
9871070

9881071
g_slice_free (MetaXWaylandDnd, dnd);
9891072
manager->dnd = NULL;

0 commit comments

Comments
 (0)