+#include <assert.h>
#include <stdlib.h>
#include "data_device_private.h"
+static const struct ds_pointer_grab_interface drag_pointer_grab_iface;
+static const struct ds_keyboard_grab_interface drag_keyboard_grab_iface;
+
static void drag_destroy(struct ds_drag *drag);
static struct ds_drag_icon *create_drag_icon(struct ds_drag *drag,
struct wl_resource *icon_resource);
static void drag_handle_icon_destroy(struct wl_listener *listener, void *data);
static void drag_handle_source_destroy(struct wl_listener *listener,
void *data);
+static void drag_handle_seat_client_destroy(struct wl_listener *listener,
+ void *data);
+static void drag_start(struct ds_drag *drag, uint32_t serial);
+static void drag_set_focus(struct ds_drag *drag, struct ds_surface *surface,
+ double sx, double sy);
+static void drag_drop(struct ds_drag *drag, uint32_t time);
static void drag_icon_destroy(struct ds_drag_icon *icon);
WL_EXPORT void
drag_destroy(drag);
}
+WL_EXPORT void
+ds_drag_start_pointer_drag(struct ds_drag *drag, uint32_t serial)
+{
+ drag->grab_type = DS_DRAG_GRAB_KEYBOARD_POINTER;
+
+ ds_seat_pointer_clear_focus(drag->seat);
+ ds_seat_pointer_start_grab(drag->seat, &drag->pointer_grab);
+
+ drag_start(drag, serial);
+}
+
+WL_EXPORT void
+ds_drag_start_touch_drag(struct ds_drag *drag, uint32_t serial)
+{
+ drag->grab_type = DS_DRAG_GRAB_KEYBOARD_TOUCH;
+
+ // TODO
+}
+
+WL_EXPORT void
+ds_drag_add_destroy_listener(struct ds_drag *drag,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&drag->events.destroy, listener);
+}
+
+WL_EXPORT void
+ds_drag_add_focus_listener(struct ds_drag *drag, struct wl_listener *listener)
+{
+ wl_signal_add(&drag->events.focus, listener);
+}
+
+WL_EXPORT void
+ds_drag_add_motion_listener(struct ds_drag *drag, struct wl_listener *listener)
+{
+ wl_signal_add(&drag->events.motion, listener);
+}
+
+WL_EXPORT void
+ds_drag_add_drop_listener(struct ds_drag *drag, struct wl_listener *listener)
+{
+ wl_signal_add(&drag->events.drop, listener);
+}
+
+struct ds_data_source *
+ds_drag_get_source(struct ds_drag *drag)
+{
+ return drag->source;
+}
+
struct ds_drag *
-create_drag(struct ds_seat_client *seat_client, struct ds_data_source *source,
+create_drag(struct ds_data_device *data_device, struct ds_data_source *source,
struct wl_resource *icon_resource)
{
struct ds_drag *drag;
if (!drag)
return NULL;
- drag->seat = ds_seat_client_get_seat(seat_client);
- drag->seat_client = seat_client;
+ wl_signal_init(&drag->events.destroy);
+ wl_signal_init(&drag->events.focus);
+ wl_signal_init(&drag->events.motion);
+ wl_signal_init(&drag->events.drop);
+
+ drag->data_device = data_device;
+ drag->seat_client = data_device->seat_client;
+ drag->seat = ds_seat_client_get_seat(drag->seat_client);
// FIXME ds_seat_client_add_destroy_listener()?
wl_signal_add(&source->events.destroy, &drag->source_destroy);
}
- // TODO Add grab interface
+ drag->pointer_grab.data = drag;
+ drag->pointer_grab.iface = &drag_pointer_grab_iface;
+
+ drag->keyboard_grab.data = drag;
+ drag->keyboard_grab.iface = &drag_keyboard_grab_iface;
+
+ // TODO Add touch grab
return drag;
}
static void
drag_destroy(struct ds_drag *drag)
{
+ if (drag->cancelling)
+ return;
+
+ drag->cancelling = true;
+
+ if (drag->started) {
+ ds_seat_keyboard_end_grab(drag->seat);
+ switch (drag->grab_type) {
+ case DS_DRAG_GRAB_KEYBOARD_POINTER:
+ ds_seat_pointer_end_grab(drag->seat);
+ break;
+ case DS_DRAG_GRAB_KEYBOARD_TOUCH:
+ // TODO
+ break;
+ default:
+ case DS_DRAG_GRAB_KEYBOARD:
+ break;
+ }
+ }
+
wl_signal_emit(&drag->events.destroy, drag);
+ if (drag->started)
+ drag_set_focus(drag, NULL, 0, 0);
+
if (drag->source)
wl_list_remove(&drag->source_destroy.link);
icon = wl_container_of(listener, icon, surface_destroy);
drag_icon_destroy(icon);
}
+
+static void
+drag_pointer_grab_iface_enter(struct ds_seat_pointer_grab *grab,
+ struct ds_surface *surface, double sx, double sy)
+{
+ struct ds_drag *drag = grab->data;
+ drag_set_focus(drag, surface, sx, sy);
+}
+
+static void
+drag_pointer_grab_iface_clear_focus(struct ds_seat_pointer_grab *grab)
+{
+ struct ds_drag *drag = grab->data;
+ drag_set_focus(drag, NULL, 0, 0);
+}
+
+static void
+drag_pointer_grab_iface_motion(struct ds_seat_pointer_grab *grab,
+ uint32_t time, double sx, double sy)
+{
+ struct ds_drag *drag = grab->data;
+ struct ds_data_device *data_device;
+ struct wl_resource *resource;
+
+ if (!drag->focused_surface || !drag->focused_client)
+ return;
+
+ data_device = data_device_for_seat_client(drag->data_device->manager,
+ drag->focused_client);
+ if (data_device) {
+ wl_resource_for_each(resource, &data_device->resources) {
+ wl_data_device_send_motion(resource, time,
+ wl_fixed_from_double(sx), wl_fixed_from_double(sy));
+ }
+ }
+
+ struct ds_event_drag_motion event = {
+ .drag = drag,
+ .time = time,
+ .sx = sx,
+ .sy = sy,
+ };
+ wl_signal_emit(&drag->events.motion, &event);
+}
+
+static uint32_t
+drag_pointer_grab_iface_button(struct ds_seat_pointer_grab *grab,
+ uint32_t time, uint32_t button, uint32_t state)
+{
+ struct ds_drag *drag = grab->data;
+ uint32_t grab_button;
+ size_t grab_button_count;
+
+ grab_button = ds_seat_pointer_get_grab_button(grab->seat);
+ if (drag->source && grab_button == button &&
+ state == WL_POINTER_BUTTON_STATE_RELEASED) {
+ if (drag->focused_client && drag->source->current_dnd_action &&
+ drag->source->accepted) {
+ drag_drop(drag, time);
+ }
+ else if (drag->source->iface->dnd_finish) {
+ ds_data_source_destroy(drag->source);
+ return 0;
+ }
+ }
+
+ grab_button_count = ds_seat_pointer_get_grab_button(drag->seat);
+ if (grab_button_count == 0 &&
+ state == WL_POINTER_BUTTON_STATE_RELEASED) {
+ drag_destroy(drag);
+ }
+
+ return 0;
+}
+
+static void
+drag_pointer_grab_iface_axis(struct ds_seat_pointer_grab *grab,
+ uint32_t time, enum ds_axis_orientation orientation, double value,
+ int32_t value_discrete, enum ds_axis_source source)
+{
+ // This space is intentionally left blank
+}
+
+static void
+drag_pointer_grab_iface_cancel(struct ds_seat_pointer_grab *grab)
+{
+ struct ds_drag *drag = grab->data;
+
+ drag_destroy(drag);
+}
+
+static const struct ds_pointer_grab_interface drag_pointer_grab_iface = {
+ .enter = drag_pointer_grab_iface_enter,
+ .clear_focus = drag_pointer_grab_iface_clear_focus,
+ .motion = drag_pointer_grab_iface_motion,
+ .button = drag_pointer_grab_iface_button,
+ .axis = drag_pointer_grab_iface_axis,
+ .cancel = drag_pointer_grab_iface_cancel,
+};
+
+static void
+drag_keyboard_grab_iface_enter(struct ds_seat_keyboard_grab *grab,
+ struct ds_surface *surface, uint32_t keycodes[], size_t num_keycodes,
+ struct ds_keyboard_modifiers *modifiers)
+{
+ // nothing has keyboard focus during drags
+}
+
+static void
+drag_keyboard_grab_iface_clear_focus(struct ds_seat_keyboard_grab *grab)
+{
+ // nothing has keyboard focus during drags
+}
+
+static void
+drag_keyboard_grab_iface_key(struct ds_seat_keyboard_grab *grab,
+ uint32_t time, uint32_t key, uint32_t state)
+{
+ // no keyboard input during drags
+}
+
+static void
+drag_keyboard_grab_iface_modifiers(struct ds_seat_keyboard_grab *grab,
+ struct ds_keyboard_modifiers *modifiers)
+{
+ // TODO change the dnd action based on what modifier is passed on the
+ // keyboard
+}
+
+static void
+drag_keyboard_grab_iface_cancel(struct ds_seat_keyboard_grab *grab)
+{
+ struct ds_drag *drag = grab->data;
+
+ drag_destroy(drag);
+}
+
+static const struct ds_keyboard_grab_interface drag_keyboard_grab_iface = {
+ .enter = drag_keyboard_grab_iface_enter,
+ .clear_focus = drag_keyboard_grab_iface_clear_focus,
+ .key = drag_keyboard_grab_iface_key,
+ .modifiers = drag_keyboard_grab_iface_modifiers,
+ .cancel = drag_keyboard_grab_iface_cancel,
+};
+
+static void
+drag_start(struct ds_drag *drag, uint32_t serial)
+{
+ assert(!drag->started);
+ drag->started = true;
+
+ ds_seat_keyboard_start_grab(drag->seat, &drag->keyboard_grab);
+
+ ds_seat_start_drag(drag->seat, drag->source, serial);
+}
+
+static void
+drag_set_focus(struct ds_drag *drag, struct ds_surface *surface,
+ double sx, double sy)
+{
+ struct ds_seat_client *focused_client;
+ struct ds_data_device *data_device;
+ struct wl_resource *device_resource, *surface_resource;
+ struct ds_data_offer *offer, *tmp;
+ uint32_t serial;
+
+ if (drag->focused_surface == surface)
+ return;
+
+ if (drag->focused_client) {
+ wl_list_remove(&drag->seat_client_destroy.link);
+
+ wl_list_for_each(data_device,
+ &drag->data_device->manager->data_devices, link) {
+ if (data_device->seat !=
+ ds_seat_client_get_seat(drag->focused_client))
+ continue;
+
+ wl_list_for_each_safe(offer, tmp,
+ &data_device->drag_offers, link) {
+ if (!drag->dropped &&
+ offer->source == drag->source &&
+ (wl_resource_get_client(offer->resource) ==
+ ds_seat_client_get_wl_client(drag->focused_client))) {
+ offer->source = NULL;
+ data_offer_destroy(offer);
+ }
+ }
+ }
+
+ data_device = data_device_for_seat_client(drag->data_device->manager,
+ drag->focused_client);
+ if (data_device) {
+ wl_resource_for_each(device_resource, &data_device->resources)
+ wl_data_device_send_leave(device_resource);
+ }
+
+ drag->focused_client = NULL;
+ drag->focused_surface = NULL;
+ }
+
+ if (!surface)
+ goto out;
+
+ surface_resource = ds_surface_get_wl_resource(surface);
+
+ focused_client = ds_seat_client_for_wl_client(drag->seat,
+ wl_resource_get_client(surface_resource));
+ if (!focused_client)
+ goto out;
+
+ if (!drag->source) {
+ if (wl_resource_get_client(surface_resource) !=
+ ds_seat_client_get_wl_client(drag->seat_client))
+ goto out;
+ }
+ else {
+ drag->source->accepted = false;
+
+ data_device = data_device_for_seat_client(drag->data_device->manager,
+ focused_client);
+ if (data_device) {
+ wl_resource_for_each(device_resource, &data_device->resources) {
+ offer = create_data_offer(device_resource, drag->source,
+ DS_DATA_OFFER_DRAG);
+ if (!offer) {
+ wl_resource_post_no_memory(device_resource);
+ return;
+ }
+
+ data_offer_update_action(offer);
+
+ if (wl_resource_get_version(offer->resource) >=
+ WL_DATA_OFFER_SOURCE_ACTIONS_SINCE_VERSION) {
+ wl_data_offer_send_source_actions(offer->resource,
+ drag->source->actions);
+ }
+
+ serial = wl_display_next_serial(
+ drag->data_device->manager->display);
+ wl_data_device_send_enter(device_resource, serial,
+ surface_resource,
+ wl_fixed_from_double(sx), wl_fixed_from_double(sy),
+ offer->resource);
+ }
+ }
+ }
+
+ drag->focused_surface = surface;
+ drag->focused_client = focused_client;
+ drag->seat_client_destroy.notify = drag_handle_seat_client_destroy;
+ ds_seat_client_add_destroy_listener(focused_client,
+ &drag->seat_client_destroy);
+
+out:
+ wl_signal_emit(&drag->events.focus, drag);
+}
+
+static void
+drag_drop(struct ds_drag *drag, uint32_t time)
+{
+ struct ds_data_device *data_device;
+ struct wl_resource *resource;
+
+ assert(drag->focused_client);
+
+ drag->dropped = true;
+
+ data_device = data_device_for_seat_client(drag->data_device->manager,
+ drag->focused_client);
+ if (data_device) {
+ wl_resource_for_each(resource, &data_device->resources)
+ wl_data_device_send_drop(resource);
+ }
+
+ if (drag->source)
+ ds_data_source_dnd_drop(drag->source);
+
+ struct ds_event_drag_drop event = {
+ .drag = drag,
+ .time = time,
+ };
+ wl_signal_emit(&drag->events.drop, &event);
+}
+
+static void
+drag_handle_seat_client_destroy(struct wl_listener *listener, void *data)
+{
+ struct ds_drag *drag;
+
+ drag = wl_container_of(listener, drag, seat_client_destroy);
+
+ drag->focused_client = NULL;
+ wl_list_remove(&drag->seat_client_destroy.link);
+}