diff --git a/gdk/wayland/gdkdevice-wayland.c b/gdk/wayland/gdkdevice-wayland.c index dd64f2fcd2..74142f308b 100644 --- a/gdk/wayland/gdkdevice-wayland.c +++ b/gdk/wayland/gdkdevice-wayland.c @@ -89,6 +89,7 @@ struct _GdkWaylandDeviceData guint cursor_timeout_id; guint cursor_image_index; + GdkDragContext *drop_context; struct wl_surface *pointer_surface; }; @@ -522,21 +523,50 @@ data_device_enter (void *data, struct wl_data_offer *offer) { GdkWaylandDeviceData *device = (GdkWaylandDeviceData *)data; + GdkWindow *dest_window; + + dest_window = wl_surface_get_user_data (surface); + + if (!GDK_IS_WINDOW (dest_window)) + return; g_debug (G_STRLOC ": %s data_device = %p serial = %u, surface = %p, x = %d y = %d, offer = %p", G_STRFUNC, data_device, serial, surface, x, y, offer); + /* Update pointer state, so device state queries work during DnD */ + device->pointer_focus = g_object_ref (dest_window); + device->surface_x = wl_fixed_to_double (x); + device->surface_y = wl_fixed_to_double (y); + + gdk_wayland_drop_context_update_targets (device->drop_context); + _gdk_wayland_drag_context_set_dest_window (device->drop_context, + dest_window, serial); + _gdk_wayland_drag_context_set_coords (device->drop_context, + wl_fixed_to_double (x), + wl_fixed_to_double (y)); + _gdk_wayland_drag_context_emit_event (device->drop_context, GDK_DRAG_ENTER, + GDK_CURRENT_TIME); + gdk_wayland_selection_set_offer (offer); } static void data_device_leave (void *data, struct wl_data_device *data_device) { - GdkWaylandDeviceData *device = (GdkWaylandDeviceData *)data; + GdkWaylandDeviceData *device = (GdkWaylandDeviceData *) data; g_debug (G_STRLOC ": %s data_device = %p", G_STRFUNC, data_device); + if (!gdk_drag_context_get_dest_window (device->drop_context)) + return; + + device->pointer_focus = NULL; + + _gdk_wayland_drag_context_set_coords (device->drop_context, -1, -1); + _gdk_wayland_drag_context_emit_event (device->drop_context, GDK_DRAG_LEAVE, + GDK_CURRENT_TIME); + _gdk_wayland_drag_context_set_dest_window (device->drop_context, NULL, 0); } static void @@ -546,16 +576,37 @@ data_device_motion (void *data, wl_fixed_t x, wl_fixed_t y) { + GdkWaylandDeviceData *device = (GdkWaylandDeviceData *) data; + g_debug (G_STRLOC ": %s data_device = %p, time = %d, x = %d, y = %d", G_STRFUNC, data_device, time, x, y); + + if (!gdk_drag_context_get_dest_window (device->drop_context)) + return; + + /* Update pointer state, so device state queries work during DnD */ + device->surface_x = wl_fixed_to_double (x); + device->surface_y = wl_fixed_to_double (y); + + gdk_wayland_drop_context_update_targets (device->drop_context); + _gdk_wayland_drag_context_set_coords (device->drop_context, + wl_fixed_to_double (x), + wl_fixed_to_double (y)); + _gdk_wayland_drag_context_emit_event (device->drop_context, + GDK_DRAG_MOTION, time); } static void data_device_drop (void *data, struct wl_data_device *data_device) { + GdkWaylandDeviceData *device = (GdkWaylandDeviceData *) data; + g_debug (G_STRLOC ": %s data_device = %p", G_STRFUNC, data_device); + + _gdk_wayland_drag_context_emit_event (device->drop_context, + GDK_DROP_START, GDK_CURRENT_TIME); } static void @@ -1450,6 +1501,9 @@ seat_handle_capabilities (void *data, device_manager->devices = g_list_prepend (device_manager->devices, device->pointer); + device->drop_context = _gdk_wayland_drop_context_new (device->pointer, + device->data_device); + g_signal_emit_by_name (device_manager, "device-added", device->pointer); } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && device->wl_pointer) @@ -1461,6 +1515,8 @@ seat_handle_capabilities (void *data, device_manager->devices = g_list_remove (device_manager->devices, device->pointer); + g_clear_object (&device->drop_context); + g_signal_emit_by_name (device_manager, "device-removed", device->pointer); g_object_unref (device->pointer); device->pointer = NULL; diff --git a/gdk/wayland/gdkdnd-wayland.c b/gdk/wayland/gdkdnd-wayland.c index 34ea3cddb5..099a95ae1b 100644 --- a/gdk/wayland/gdkdnd-wayland.c +++ b/gdk/wayland/gdkdnd-wayland.c @@ -40,6 +40,10 @@ typedef struct _GdkWaylandDragContextClass GdkWaylandDragContextClass; struct _GdkWaylandDragContext { GdkDragContext context; + struct wl_data_offer *offer; + uint32_t serial; + gdouble x; + gdouble y; }; struct _GdkWaylandDragContextClass @@ -54,13 +58,58 @@ G_DEFINE_TYPE (GdkWaylandDragContext, gdk_wayland_drag_context, GDK_TYPE_DRAG_CO static void gdk_wayland_drag_context_finalize (GObject *object) { + GdkWaylandDragContext *wayland_context = GDK_WAYLAND_DRAG_CONTEXT (object); GdkDragContext *context = GDK_DRAG_CONTEXT (object); contexts = g_list_remove (contexts, context); + if (wayland_context->data_source) + wl_data_source_destroy (wayland_context->data_source); + + if (wayland_context->dnd_window) + gdk_window_destroy (wayland_context->dnd_window); + G_OBJECT_CLASS (gdk_wayland_drag_context_parent_class)->finalize (object); } +void +_gdk_wayland_drag_context_emit_event (GdkDragContext *context, + GdkEventType type, + guint32 time_) +{ + GdkWindow *window; + GdkEvent *event; + + switch (type) + { + case GDK_DRAG_ENTER: + case GDK_DRAG_LEAVE: + case GDK_DRAG_MOTION: + case GDK_DRAG_STATUS: + case GDK_DROP_START: + case GDK_DROP_FINISHED: + break; + default: + return; + } + + if (context->is_source) + window = gdk_drag_context_get_source_window (context); + else + window = gdk_drag_context_get_dest_window (context); + + event = gdk_event_new (type); + event->dnd.window = g_object_ref (window); + event->dnd.context = g_object_ref (context); + event->dnd.time = time_; + event->dnd.x_root = GDK_WAYLAND_DRAG_CONTEXT (context)->x; + event->dnd.y_root = GDK_WAYLAND_DRAG_CONTEXT (context)->y; + gdk_event_set_device (event, gdk_drag_context_get_device (context)); + + gdk_event_put (event); + gdk_event_free (event); +} + static GdkWindow * gdk_wayland_drag_context_find_window (GdkDragContext *context, GdkWindow *drag_window, @@ -69,9 +118,29 @@ gdk_wayland_drag_context_find_window (GdkDragContext *context, gint y_root, GdkDragProtocol *protocol) { + GdkDevice *device; + GdkWindow *window; + + device = gdk_drag_context_get_device (context); + window = gdk_device_get_window_at_position (device, NULL, NULL); + + if (window) + { + window = gdk_window_get_toplevel (window); + *protocol = GDK_DRAG_PROTO_WAYLAND; + return g_object_ref (window); + } + return NULL; } +void +gdk_wayland_drag_context_set_action (GdkDragContext *context, + GdkDragAction action) +{ + context->suggested_action = context->action = action; +} + static gboolean gdk_wayland_drag_context_drag_motion (GdkDragContext *context, GdkWindow *dest_window, @@ -82,7 +151,16 @@ gdk_wayland_drag_context_drag_motion (GdkDragContext *context, GdkDragAction possible_actions, guint32 time) { - return FALSE; + if (context->dest_window != dest_window) + { + context->dest_window = dest_window ? g_object_ref (dest_window) : NULL; + _gdk_wayland_drag_context_set_coords (context, x_root, y_root); + _gdk_wayland_drag_context_emit_event (context, GDK_DRAG_STATUS, time); + } + + gdk_wayland_drag_context_set_action (context, suggested_action); + + return context->dest_window != NULL; } static void @@ -99,11 +177,46 @@ gdk_wayland_drag_context_drag_drop (GdkDragContext *context, /* Destination side */ +static void +gdk_wayland_drop_context_set_status (GdkDragContext *context, + gboolean accepted) +{ + GdkWaylandDragContext *context_wayland; + struct wl_data_offer *wl_offer; + + context_wayland = GDK_WAYLAND_DRAG_CONTEXT (context); + wl_offer = gdk_wayland_selection_get_offer (); + + if (!wl_offer) + return; + + if (accepted) + { + GList *l; + + for (l = context->targets; l; l = l->next) + { + if (l->data != gdk_atom_intern_static_string ("DELETE")) + break; + } + + if (l) + { + wl_data_offer_accept (wl_offer, context_wayland->serial, + gdk_atom_name (l->data)); + return; + } + } + + wl_data_offer_accept (wl_offer, context_wayland->serial, NULL); +} + static void gdk_wayland_drag_context_drag_status (GdkDragContext *context, GdkDragAction action, guint32 time_) { + gdk_wayland_drop_context_set_status (context, action != 0); } static void @@ -111,6 +224,7 @@ gdk_wayland_drag_context_drop_reply (GdkDragContext *context, gboolean accepted, guint32 time_) { + gdk_wayland_drop_context_set_status (context, accepted); } static void @@ -129,13 +243,20 @@ gdk_wayland_drag_context_drop_status (GdkDragContext *context) static GdkAtom gdk_wayland_drag_context_get_selection (GdkDragContext *context) { - return GDK_NONE; + return gdk_atom_intern_static_string ("GdkWaylandSelection"); } static void -gdk_wayland_drag_context_init (GdkWaylandDragContext *context) +gdk_wayland_drag_context_init (GdkWaylandDragContext *context_wayland) { + GdkDragContext *context; + + context = GDK_DRAG_CONTEXT (context_wayland); contexts = g_list_prepend (contexts, context); + + context->action = GDK_ACTION_COPY; + context->suggested_action = GDK_ACTION_COPY; + context->actions = GDK_ACTION_COPY | GDK_ACTION_MOVE; } static void @@ -160,7 +281,7 @@ gdk_wayland_drag_context_class_init (GdkWaylandDragContextClass *klass) GdkDragProtocol _gdk_wayland_window_get_drag_protocol (GdkWindow *window, GdkWindow **target) { - return 0; + return GDK_DRAG_PROTO_WAYLAND; } void @@ -183,3 +304,58 @@ _gdk_wayland_window_drag_begin (GdkWindow *window, return context; } + +GdkDragContext * +_gdk_wayland_drop_context_new (GdkDevice *device, + struct wl_data_device *data_device) +{ + GdkWaylandDragContext *context_wayland; + GdkDragContext *context; + + context_wayland = g_object_new (GDK_TYPE_WAYLAND_DRAG_CONTEXT, NULL); + context = GDK_DRAG_CONTEXT (context_wayland); + context->is_source = FALSE; + + gdk_drag_context_set_device (context, device); + + return context; +} + +void +gdk_wayland_drop_context_update_targets (GdkDragContext *context) +{ + g_list_free (context->targets); + context->targets = g_list_copy (gdk_wayland_selection_get_targets ()); +} + +void +_gdk_wayland_drag_context_set_coords (GdkDragContext *context, + gdouble x, + gdouble y) +{ + GdkWaylandDragContext *context_wayland; + + context_wayland = GDK_WAYLAND_DRAG_CONTEXT (context); + context_wayland->x = x; + context_wayland->y = y; +} + +void +_gdk_wayland_drag_context_set_source_window (GdkDragContext *context, + GdkWindow *window) +{ + if (context->source_window) + g_object_unref (context->source_window); + + context->source_window = window ? g_object_ref (window) : NULL; +} + +void +_gdk_wayland_drag_context_set_dest_window (GdkDragContext *context, + GdkWindow *dest_window, + uint32_t serial) +{ + context->dest_window = dest_window ? g_object_ref (dest_window) : NULL; + GDK_WAYLAND_DRAG_CONTEXT (context)->serial = serial; + gdk_wayland_drop_context_update_targets (context); +} diff --git a/gdk/wayland/gdkprivate-wayland.h b/gdk/wayland/gdkprivate-wayland.h index b72c7a64d7..0d5eebc18a 100644 --- a/gdk/wayland/gdkprivate-wayland.h +++ b/gdk/wayland/gdkprivate-wayland.h @@ -92,6 +92,24 @@ void _gdk_wayland_window_register_dnd (GdkWindow *window); GdkDragContext *_gdk_wayland_window_drag_begin (GdkWindow *window, GdkDevice *device, GList *targets); +GdkDragContext * _gdk_wayland_drop_context_new (GdkDevice *device, + struct wl_data_device *data_device); +void _gdk_wayland_drag_context_set_source_window (GdkDragContext *context, + GdkWindow *window); +void _gdk_wayland_drag_context_set_dest_window (GdkDragContext *context, + GdkWindow *dest_window, + uint32_t serial); +void _gdk_wayland_drag_context_emit_event (GdkDragContext *context, + GdkEventType type, + guint32 time_); +void _gdk_wayland_drag_context_set_coords (GdkDragContext *context, + gdouble x, + gdouble y); + +void gdk_wayland_drag_context_set_action (GdkDragContext *context, + GdkDragAction action); + +void gdk_wayland_drop_context_update_targets (GdkDragContext *context); void _gdk_wayland_display_create_window_impl (GdkDisplay *display, GdkWindow *window,