mirror of
https://github.com/brl/mutter.git
synced 2024-11-26 01:50:42 -05:00
xwayland: Implement wayland-to-X11 DnD
X11 client windows now hook a X11-specific MetaWaylandDragDestFuncs that converts these into Xdnd* messages, and an additional selection bridge has been added to take care of XdndSelection, and the data transfers done through it. https://bugzilla.gnome.org/show_bug.cgi?id=738312
This commit is contained in:
parent
b449ba942a
commit
ccb7833e99
@ -52,6 +52,7 @@
|
|||||||
|
|
||||||
#include "meta-surface-actor.h"
|
#include "meta-surface-actor.h"
|
||||||
#include "meta-surface-actor-wayland.h"
|
#include "meta-surface-actor-wayland.h"
|
||||||
|
#include "meta-xwayland-private.h"
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
@ -751,6 +752,10 @@ sync_reactive (MetaWaylandSurface *surface)
|
|||||||
static void
|
static void
|
||||||
sync_drag_dest_funcs (MetaWaylandSurface *surface)
|
sync_drag_dest_funcs (MetaWaylandSurface *surface)
|
||||||
{
|
{
|
||||||
|
if (surface->window &&
|
||||||
|
surface->window->client_type == META_WINDOW_CLIENT_TYPE_X11)
|
||||||
|
surface->dnd.funcs = meta_xwayland_selection_get_drag_dest_funcs ();
|
||||||
|
else
|
||||||
surface->dnd.funcs = meta_wayland_data_device_get_drag_dest_funcs ();
|
surface->dnd.funcs = meta_wayland_data_device_get_drag_dest_funcs ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,4 +39,6 @@ void meta_xwayland_init_selection (void);
|
|||||||
void meta_xwayland_shutdown_selection (void);
|
void meta_xwayland_shutdown_selection (void);
|
||||||
gboolean meta_xwayland_selection_handle_event (XEvent *xevent);
|
gboolean meta_xwayland_selection_handle_event (XEvent *xevent);
|
||||||
|
|
||||||
|
const MetaWaylandDragDestFuncs * meta_xwayland_selection_get_drag_dest_funcs (void);
|
||||||
|
|
||||||
#endif /* META_XWAYLAND_PRIVATE_H */
|
#endif /* META_XWAYLAND_PRIVATE_H */
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
#include "meta-wayland-data-device.h"
|
#include "meta-wayland-data-device.h"
|
||||||
|
|
||||||
#define INCR_CHUNK_SIZE (128 * 1024)
|
#define INCR_CHUNK_SIZE (128 * 1024)
|
||||||
|
#define XDND_VERSION 5
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
MetaXWaylandSelection *selection_data;
|
MetaXWaylandSelection *selection_data;
|
||||||
@ -70,10 +71,184 @@ typedef struct {
|
|||||||
struct wl_listener ownership_listener;
|
struct wl_listener ownership_listener;
|
||||||
} MetaSelectionBridge;
|
} MetaSelectionBridge;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
MetaSelectionBridge selection;
|
||||||
|
Window dnd_dest;
|
||||||
|
} MetaDndBridge;
|
||||||
|
|
||||||
struct _MetaXWaylandSelection {
|
struct _MetaXWaylandSelection {
|
||||||
MetaSelectionBridge clipboard;
|
MetaSelectionBridge clipboard;
|
||||||
|
MetaDndBridge dnd;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ATOM_DND_SELECTION,
|
||||||
|
ATOM_DND_AWARE,
|
||||||
|
ATOM_DND_STATUS,
|
||||||
|
ATOM_DND_POSITION,
|
||||||
|
ATOM_DND_ENTER,
|
||||||
|
ATOM_DND_LEAVE,
|
||||||
|
ATOM_DND_DROP,
|
||||||
|
ATOM_DND_FINISHED,
|
||||||
|
ATOM_DND_PROXY,
|
||||||
|
ATOM_DND_TYPE_LIST,
|
||||||
|
ATOM_DND_ACTION_MOVE,
|
||||||
|
ATOM_DND_ACTION_COPY,
|
||||||
|
ATOM_DND_ACTION_ASK,
|
||||||
|
N_DND_ATOMS
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Matches order in enum above */
|
||||||
|
const gchar *atom_names[] = {
|
||||||
|
"XdndSelection",
|
||||||
|
"XdndAware",
|
||||||
|
"XdndStatus",
|
||||||
|
"XdndPosition",
|
||||||
|
"XdndEnter",
|
||||||
|
"XdndLeave",
|
||||||
|
"XdndDrop",
|
||||||
|
"XdndFinished",
|
||||||
|
"XdndProxy",
|
||||||
|
"XdndTypeList",
|
||||||
|
"XdndActionMove",
|
||||||
|
"XdndActionCopy",
|
||||||
|
"XdndActionAsk",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
Atom xdnd_atoms[N_DND_ATOMS];
|
||||||
|
|
||||||
|
/* XDND helpers */
|
||||||
|
static void
|
||||||
|
xdnd_send_enter (MetaXWaylandSelection *selection_data,
|
||||||
|
Window dest)
|
||||||
|
{
|
||||||
|
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
|
||||||
|
MetaSelectionBridge *selection = &selection_data->dnd.selection;
|
||||||
|
Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
|
||||||
|
MetaWaylandDataSource *data_source;
|
||||||
|
XEvent xev = { 0 };
|
||||||
|
gchar **p;
|
||||||
|
|
||||||
|
data_source = compositor->seat->data_device.dnd_data_source;
|
||||||
|
xev.xclient.type = ClientMessage;
|
||||||
|
xev.xclient.message_type = xdnd_atoms[ATOM_DND_ENTER];
|
||||||
|
xev.xclient.format = 32;
|
||||||
|
xev.xclient.window = dest;
|
||||||
|
|
||||||
|
xev.xclient.data.l[0] = selection->window;
|
||||||
|
xev.xclient.data.l[1] = XDND_VERSION << 24; /* version */
|
||||||
|
xev.xclient.data.l[2] = xev.xclient.data.l[3] = xev.xclient.data.l[4] = 0;
|
||||||
|
|
||||||
|
if (data_source->mime_types.size <= 3)
|
||||||
|
{
|
||||||
|
/* The mimetype atoms fit in this same message */
|
||||||
|
gchar **p;
|
||||||
|
gint i = 2;
|
||||||
|
|
||||||
|
wl_array_for_each (p, &data_source->mime_types)
|
||||||
|
{
|
||||||
|
xev.xclient.data.l[i++] = gdk_x11_get_xatom_by_name (*p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* We have more than 3 mimetypes, we must set up
|
||||||
|
* the mimetype list as a XdndTypeList property.
|
||||||
|
*/
|
||||||
|
Atom *atomlist;
|
||||||
|
gint i = 0;
|
||||||
|
|
||||||
|
xev.xclient.data.l[1] |= 1;
|
||||||
|
atomlist = g_new0 (Atom, data_source->mime_types.size);
|
||||||
|
|
||||||
|
wl_array_for_each (p, &data_source->mime_types)
|
||||||
|
{
|
||||||
|
atomlist[i++] = gdk_x11_get_xatom_by_name (*p);
|
||||||
|
}
|
||||||
|
|
||||||
|
XChangeProperty (xdisplay, selection->window,
|
||||||
|
xdnd_atoms[ATOM_DND_TYPE_LIST],
|
||||||
|
XA_ATOM, 32, PropModeReplace,
|
||||||
|
(guchar *) atomlist, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
XSendEvent (xdisplay, dest, False, NoEventMask, &xev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
xdnd_send_leave (MetaXWaylandSelection *selection_data,
|
||||||
|
Window dest)
|
||||||
|
{
|
||||||
|
MetaSelectionBridge *selection = &selection_data->dnd.selection;
|
||||||
|
Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
|
||||||
|
XEvent xev = { 0 };
|
||||||
|
|
||||||
|
xev.xclient.type = ClientMessage;
|
||||||
|
xev.xclient.message_type = xdnd_atoms[ATOM_DND_LEAVE];
|
||||||
|
xev.xclient.format = 32;
|
||||||
|
xev.xclient.window = dest;
|
||||||
|
xev.xclient.data.l[0] = selection->window;
|
||||||
|
|
||||||
|
XSendEvent (xdisplay, dest, False, NoEventMask, &xev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
xdnd_send_position (MetaXWaylandSelection *selection_data,
|
||||||
|
Window dest,
|
||||||
|
uint32_t time,
|
||||||
|
int x,
|
||||||
|
int y)
|
||||||
|
{
|
||||||
|
MetaSelectionBridge *selection = &selection_data->dnd.selection;
|
||||||
|
Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
|
||||||
|
XEvent xev = { 0 };
|
||||||
|
|
||||||
|
xev.xclient.type = ClientMessage;
|
||||||
|
xev.xclient.message_type = xdnd_atoms[ATOM_DND_POSITION];
|
||||||
|
xev.xclient.format = 32;
|
||||||
|
xev.xclient.window = dest;
|
||||||
|
|
||||||
|
xev.xclient.data.l[0] = selection->window;
|
||||||
|
xev.xclient.data.l[1] = 0;
|
||||||
|
xev.xclient.data.l[2] = (x << 16) | y;
|
||||||
|
xev.xclient.data.l[3] = time;
|
||||||
|
xev.xclient.data.l[4] = xdnd_atoms[ATOM_DND_ACTION_COPY];
|
||||||
|
|
||||||
|
XSendEvent (xdisplay, dest, False, NoEventMask, &xev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
xdnd_send_drop (MetaXWaylandSelection *selection_data,
|
||||||
|
Window dest,
|
||||||
|
uint32_t time)
|
||||||
|
{
|
||||||
|
MetaSelectionBridge *selection = &selection_data->dnd.selection;
|
||||||
|
Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
|
||||||
|
XEvent xev = { 0 };
|
||||||
|
|
||||||
|
xev.xclient.type = ClientMessage;
|
||||||
|
xev.xclient.message_type = xdnd_atoms[ATOM_DND_DROP];
|
||||||
|
xev.xclient.format = 32;
|
||||||
|
xev.xclient.window = dest;
|
||||||
|
|
||||||
|
xev.xclient.data.l[0] = selection->window;
|
||||||
|
xev.xclient.data.l[2] = time;
|
||||||
|
|
||||||
|
XSendEvent (xdisplay, dest, False, NoEventMask, &xev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_xwayland_init_dnd (void)
|
||||||
|
{
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
for (i = 0; i < N_DND_ATOMS; i++)
|
||||||
|
xdnd_atoms[i] = gdk_x11_get_xatom_by_name (atom_names[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* X11/Wayland data bridges */
|
||||||
|
|
||||||
static MetaSelectionBridge *
|
static MetaSelectionBridge *
|
||||||
atom_to_selection_bridge (MetaWaylandCompositor *compositor,
|
atom_to_selection_bridge (MetaWaylandCompositor *compositor,
|
||||||
Atom selection_atom)
|
Atom selection_atom)
|
||||||
@ -82,6 +257,8 @@ atom_to_selection_bridge (MetaWaylandCompositor *compositor,
|
|||||||
|
|
||||||
if (selection_atom == selection_data->clipboard.selection_atom)
|
if (selection_atom == selection_data->clipboard.selection_atom)
|
||||||
return &selection_data->clipboard;
|
return &selection_data->clipboard;
|
||||||
|
else if (selection_atom == selection_data->dnd.selection.selection_atom)
|
||||||
|
return &selection_data->dnd.selection;
|
||||||
else
|
else
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -164,6 +341,8 @@ data_device_get_active_source_for_atom (MetaWaylandDataDevice *data_device,
|
|||||||
{
|
{
|
||||||
if (selection_atom == gdk_x11_get_xatom_by_name ("CLIPBOARD"))
|
if (selection_atom == gdk_x11_get_xatom_by_name ("CLIPBOARD"))
|
||||||
return data_device->selection_data_source;
|
return data_device->selection_data_source;
|
||||||
|
else if (selection_atom == xdnd_atoms[ATOM_DND_SELECTION])
|
||||||
|
return data_device->dnd_data_source;
|
||||||
else
|
else
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -457,6 +636,69 @@ static const MetaWaylandDataSourceFuncs meta_x11_source_funcs = {
|
|||||||
meta_x11_source_cancel
|
meta_x11_source_cancel
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_x11_drag_dest_focus_in (MetaWaylandDataDevice *data_device,
|
||||||
|
MetaWaylandSurface *surface,
|
||||||
|
MetaWaylandDataOffer *offer)
|
||||||
|
{
|
||||||
|
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
|
||||||
|
|
||||||
|
compositor->xwayland_manager.selection_data->dnd.dnd_dest = surface->window->xwindow;
|
||||||
|
xdnd_send_enter (compositor->xwayland_manager.selection_data,
|
||||||
|
compositor->xwayland_manager.selection_data->dnd.dnd_dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_x11_drag_dest_focus_out (MetaWaylandDataDevice *data_device,
|
||||||
|
MetaWaylandSurface *surface)
|
||||||
|
{
|
||||||
|
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
|
||||||
|
|
||||||
|
xdnd_send_leave (compositor->xwayland_manager.selection_data,
|
||||||
|
compositor->xwayland_manager.selection_data->dnd.dnd_dest);
|
||||||
|
compositor->xwayland_manager.selection_data->dnd.dnd_dest = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_x11_drag_dest_motion (MetaWaylandDataDevice *data_device,
|
||||||
|
MetaWaylandSurface *surface,
|
||||||
|
const ClutterEvent *event)
|
||||||
|
{
|
||||||
|
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
|
||||||
|
guint32 time;
|
||||||
|
gfloat x, y;
|
||||||
|
|
||||||
|
time = clutter_event_get_time (event);
|
||||||
|
clutter_event_get_coords (event, &x, &y);
|
||||||
|
xdnd_send_position (compositor->xwayland_manager.selection_data,
|
||||||
|
compositor->xwayland_manager.selection_data->dnd.dnd_dest,
|
||||||
|
time, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_x11_drag_dest_drop (MetaWaylandDataDevice *data_device,
|
||||||
|
MetaWaylandSurface *surface)
|
||||||
|
{
|
||||||
|
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
|
||||||
|
|
||||||
|
xdnd_send_drop (compositor->xwayland_manager.selection_data,
|
||||||
|
compositor->xwayland_manager.selection_data->dnd.dnd_dest,
|
||||||
|
meta_display_get_current_time_roundtrip (meta_get_display ()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MetaWaylandDragDestFuncs meta_x11_drag_dest_funcs = {
|
||||||
|
meta_x11_drag_dest_focus_in,
|
||||||
|
meta_x11_drag_dest_focus_out,
|
||||||
|
meta_x11_drag_dest_motion,
|
||||||
|
meta_x11_drag_dest_drop
|
||||||
|
};
|
||||||
|
|
||||||
|
const MetaWaylandDragDestFuncs *
|
||||||
|
meta_xwayland_selection_get_drag_dest_funcs (void)
|
||||||
|
{
|
||||||
|
return &meta_x11_drag_dest_funcs;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
meta_xwayland_data_source_fetch_mimetype_list (MetaWaylandDataSource *source,
|
meta_xwayland_data_source_fetch_mimetype_list (MetaWaylandDataSource *source,
|
||||||
Window window,
|
Window window,
|
||||||
@ -752,6 +994,45 @@ meta_xwayland_selection_handle_selection_request (MetaWaylandCompositor *composi
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
meta_xwayland_selection_handle_client_message (MetaWaylandCompositor *compositor,
|
||||||
|
XEvent *xevent)
|
||||||
|
{
|
||||||
|
XClientMessageEvent *event = (XClientMessageEvent *) xevent;
|
||||||
|
MetaSelectionBridge *selection = &compositor->xwayland_manager.selection_data->dnd.selection;
|
||||||
|
|
||||||
|
/* Source side messages */
|
||||||
|
if (event->window == selection->window)
|
||||||
|
{
|
||||||
|
MetaWaylandDataSource *data_source;
|
||||||
|
|
||||||
|
data_source = compositor->seat->data_device.dnd_data_source;
|
||||||
|
|
||||||
|
if (!data_source)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (event->message_type == xdnd_atoms[ATOM_DND_STATUS])
|
||||||
|
{
|
||||||
|
/* The first bit in data.l[1] is set if the drag was accepted */
|
||||||
|
data_source->has_target = (event->data.l[1] & 1) != 0;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
else if (event->message_type == xdnd_atoms[ATOM_DND_FINISHED])
|
||||||
|
{
|
||||||
|
/* Reject messages mid-grab */
|
||||||
|
if (compositor->seat->data_device.current_grab)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
meta_wayland_data_device_set_dnd_source (&compositor->seat->data_device,
|
||||||
|
NULL);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
meta_xwayland_selection_handle_xfixes_selection_notify (MetaWaylandCompositor *compositor,
|
meta_xwayland_selection_handle_xfixes_selection_notify (MetaWaylandCompositor *compositor,
|
||||||
XEvent *xevent)
|
XEvent *xevent)
|
||||||
@ -824,6 +1105,8 @@ meta_xwayland_selection_handle_event (XEvent *xevent)
|
|||||||
return meta_xwayland_selection_handle_property_notify (compositor, xevent);
|
return meta_xwayland_selection_handle_property_notify (compositor, xevent);
|
||||||
case SelectionRequest:
|
case SelectionRequest:
|
||||||
return meta_xwayland_selection_handle_selection_request (compositor, xevent);
|
return meta_xwayland_selection_handle_selection_request (compositor, xevent);
|
||||||
|
case ClientMessage:
|
||||||
|
return meta_xwayland_selection_handle_client_message (compositor, xevent);
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
MetaDisplay *display = meta_get_display ();
|
MetaDisplay *display = meta_get_display ();
|
||||||
@ -916,9 +1199,13 @@ meta_xwayland_init_selection (void)
|
|||||||
|
|
||||||
manager->selection_data = g_slice_new0 (MetaXWaylandSelection);
|
manager->selection_data = g_slice_new0 (MetaXWaylandSelection);
|
||||||
|
|
||||||
|
meta_xwayland_init_dnd ();
|
||||||
init_selection_bridge (&manager->selection_data->clipboard,
|
init_selection_bridge (&manager->selection_data->clipboard,
|
||||||
gdk_x11_get_xatom_by_name ("CLIPBOARD"),
|
gdk_x11_get_xatom_by_name ("CLIPBOARD"),
|
||||||
&compositor->seat->data_device.selection_ownership_signal);
|
&compositor->seat->data_device.selection_ownership_signal);
|
||||||
|
init_selection_bridge (&manager->selection_data->dnd.selection,
|
||||||
|
xdnd_atoms[ATOM_DND_SELECTION],
|
||||||
|
&compositor->seat->data_device.dnd_ownership_signal);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -937,6 +1224,7 @@ meta_xwayland_shutdown_selection (void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
shutdown_selection_bridge (&selection->clipboard);
|
shutdown_selection_bridge (&selection->clipboard);
|
||||||
|
shutdown_selection_bridge (&selection->dnd.selection);
|
||||||
|
|
||||||
g_slice_free (MetaXWaylandSelection, selection);
|
g_slice_free (MetaXWaylandSelection, selection);
|
||||||
manager->selection_data = NULL;
|
manager->selection_data = NULL;
|
||||||
|
Loading…
Reference in New Issue
Block a user