#define LIBDS_DATA_DEVICE_H
#include <wayland-server.h>
+#include <libds/surface.h>
#ifdef __cplusplus
extern "C" {
#endif
+/**
+ * An opaque handle for accessing ds_data_device_manager.
+ * This abstracts wayland data device.
+ */
struct ds_data_device_manager;
+/**
+ * An object represening a wayland client's wl_source_data.
+ *
+ * The request_set_selection event introduces a new ds_data_source object.
+ * You may make this source being a selection of seat by calling
+ * ds_seat_set_selection(). This way, the source will be advertised to the
+ * client that has keyboard focuse of seat.
+ */
struct ds_data_source;
-struct ds_data_deivce_manager *
+/**
+ * An object represening a wayland client's wl_data_offer.
+ *
+ * This object will be introduced by the event request_data_offer_receive.
+ * You may call ds_data_offer_send() with event data to request for data
+ * from the source client.
+ */
+struct ds_data_offer;
+
+struct ds_event_request_set_selection
+{
+ struct ds_seat *seat;
+ struct ds_data_source *source;
+ uint32_t serial;
+};
+
+struct ds_event_request_data_offer_receive
+{
+ struct ds_data_offer *offer;
+ const char *mime_type;
+ int32_t fd;
+};
+
+struct ds_data_device_manager *
ds_data_device_manager_create(struct wl_display *display);
+void ds_data_device_manager_add_destroy_listener(
+ struct ds_data_device_manager *manager, struct wl_listener *listener);
+
+void ds_data_device_manager_add_request_set_selection_listener(
+ struct ds_data_device_manager *manager, struct wl_listener *listener);
+
+void ds_data_device_manager_add_request_data_offer_receive_listener(
+ struct ds_data_device_manager *manager, struct wl_listener *listener);
+
+void ds_data_offer_send(struct ds_data_offer *offer, const char *mime_type,
+ int32_t fd);
+
#ifdef __cplusplus
}
#endif
--- /dev/null
+#ifndef DS_DATA_DEVICE_H
+#define DS_DATA_DEVICE_H
+
+#include <wayland-server.h>
+
+#include "libds/data_device.h"
+#include "surface.h"
+
+struct ds_data_source_interface {
+ void (*destroy)(struct ds_data_source *source);
+ void (*send)(struct ds_data_source *source, const char *mime_type,
+ int32_t fd);
+ void (*accept)(struct ds_data_source *source, uint32_t serial,
+ const char *mime_type);
+};
+
+struct ds_data_source
+{
+ const struct ds_data_source_interface *iface;
+
+ struct wl_array mime_types;
+
+ struct {
+ struct wl_signal destroy;
+ } events;
+
+ bool accepted;
+};
+
+void ds_data_source_init(struct ds_data_source *source,
+ const struct ds_data_source_interface *iface);
+
+void ds_data_source_destroy(struct ds_data_source *source);
+
+void ds_data_source_send(struct ds_data_source *source, const char *mime_type,
+ int32_t fd);
+
+#endif
--- /dev/null
+#include <stdlib.h>
+
+#include "data_device_private.h"
+
+static const struct wl_data_device_interface data_device_iface;
+
+static void data_device_handle_resource_destroy(struct wl_resource *resource);
+static void data_device_handle_seat_client_destroy(
+ struct wl_listener *listener, void *data);
+static void data_device_handle_seat_set_selection(struct wl_listener *listener,
+ void *data);
+static void data_device_handle_keyboard_focus_change(
+ struct wl_listener *listener, void *data);
+static void data_device_update_selection(struct ds_data_device *data_device);
+
+struct ds_data_device *
+create_data_device(struct ds_data_device_manager *manager,
+ struct wl_client *client, uint32_t version,
+ uint32_t id, struct wl_resource *seat_resource)
+{
+ struct ds_data_device *data_device;
+
+ data_device = calloc(1, sizeof *data_device);
+ if (!data_device)
+ return NULL;
+
+ data_device->resource = wl_resource_create(client,
+ &wl_data_device_interface, version, id);
+ if (!data_device->resource) {
+ wl_client_post_no_memory(client);
+ return NULL;
+ }
+
+ wl_resource_set_implementation(data_device->resource, &data_device_iface,
+ data_device, data_device_handle_resource_destroy);
+
+ data_device->manager = manager;
+ data_device->seat_client = ds_seat_client_from_resource(seat_resource);
+ data_device->seat = ds_seat_client_get_seat(data_device->seat_client);
+
+ data_device->seat_client_destroy.notify =
+ data_device_handle_seat_client_destroy;
+ ds_seat_client_add_destroy_listener(data_device->seat_client,
+ &data_device->seat_client_destroy);
+
+ data_device->seat_set_selection.notify =
+ data_device_handle_seat_set_selection;
+ ds_seat_add_set_selection_listener(data_device->seat,
+ &data_device->seat_set_selection);
+
+ data_device->keyboard_focus_change.notify =
+ data_device_handle_keyboard_focus_change;
+ ds_seat_keyboard_add_focus_change_listener(data_device->seat,
+ &data_device->keyboard_focus_change);
+
+ data_device_update_selection(data_device);
+
+ wl_list_insert(&manager->data_devices, &data_device->link);
+
+ return data_device;
+}
+
+void
+data_device_destroy(struct ds_data_device *data_device)
+{
+ wl_list_remove(&data_device->link);
+
+ if (data_device->seat_client) {
+ wl_list_remove(&data_device->seat_client_destroy.link);
+ wl_list_remove(&data_device->seat_set_selection.link);
+ wl_list_remove(&data_device->keyboard_focus_change.link);
+ }
+
+ wl_resource_set_user_data(data_device->resource, NULL);
+
+ free(data_device);
+}
+
+static void
+data_device_handle_resource_destroy(struct wl_resource *resource)
+{
+ struct ds_data_device *data_device;
+
+ data_device = wl_resource_get_user_data(resource);
+ if (!data_device)
+ return;
+
+ data_device_destroy(data_device);
+}
+
+static void
+data_device_handle_seat_client_destroy(struct wl_listener *listener,
+ void *data)
+{
+ struct ds_data_device *data_device;
+
+ data_device = wl_container_of(listener, data_device, seat_client_destroy);
+
+ wl_list_remove(&data_device->seat_client_destroy.link);
+ wl_list_remove(&data_device->seat_set_selection.link);
+ wl_list_remove(&data_device->keyboard_focus_change.link);
+
+ data_device->seat_client = NULL;
+ data_device->seat = NULL;
+}
+
+static void
+data_device_handle_seat_set_selection(struct wl_listener *listener, void *data)
+{
+ struct ds_data_device *data_device;
+
+ data_device = wl_container_of(listener, data_device, seat_set_selection);
+ data_device_update_selection(data_device);
+}
+
+static void
+data_device_handle_keyboard_focus_change(struct wl_listener *listener,
+ void *data)
+{
+ struct ds_data_device *data_device;
+
+ data_device = wl_container_of(listener, data_device,
+ keyboard_focus_change);
+ data_device_update_selection(data_device);
+}
+
+static void
+data_device_handle_start_drag(struct wl_client *client,
+ struct wl_resource *resource, struct wl_resource *source_resource,
+ struct wl_resource *origin_resource, struct wl_resource *icon_resource,
+ uint32_t serial)
+{
+ struct ds_data_device *data_device;
+
+ data_device = wl_resource_get_user_data(resource);
+ if (!data_device)
+ return;
+
+ // TODO
+}
+
+static void
+data_device_handle_set_selection(struct wl_client *client,
+ struct wl_resource *resource, struct wl_resource *source_resource,
+ uint32_t serial)
+{
+ struct ds_data_device *data_device;
+ struct ds_data_source_client *source = NULL;
+
+ data_device = wl_resource_get_user_data(resource);
+ if (!data_device || !data_device->seat_client)
+ return;
+
+ if (source_resource) {
+ source = wl_resource_get_user_data(source_resource);
+
+ if (source)
+ source->finalized = true;
+ }
+
+ struct ds_event_request_set_selection event = {
+ .seat = data_device->seat,
+ .source = &source->base,
+ .serial = serial,
+ };
+ wl_signal_emit(&data_device->manager->events.request_set_selection,
+ &event);
+}
+
+static void
+data_device_handle_release(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static const struct wl_data_device_interface data_device_iface = {
+ .start_drag = data_device_handle_start_drag,
+ .set_selection = data_device_handle_set_selection,
+ .release = data_device_handle_release,
+};
+
+static void
+data_device_update_selection(struct ds_data_device *data_device)
+{
+ struct ds_seat_client *focused_client;
+ struct ds_data_source *source;
+ struct ds_data_offer *offer;
+
+ focused_client = ds_seat_keyboard_get_focused_client(data_device->seat);
+ if (data_device->seat_client != focused_client)
+ return;
+
+ source = ds_seat_get_selection(data_device->seat);
+ if (source) {
+ offer = create_data_offer(data_device, source,
+ DS_DATA_OFFER_SELECTION);
+ if (!offer) {
+ wl_resource_post_no_memory(data_device->resource);
+ return;
+ }
+
+ wl_data_device_send_selection(data_device->resource, offer->resource);
+ }
+ else {
+ wl_data_device_send_selection(data_device->resource, NULL);
+ }
+}
+++ /dev/null
-#ifndef DS_DATA_DEVICE_H
-#define DS_DATA_DEVICE_H
-
-
-
-#endif
--- /dev/null
+#ifndef DS_DATA_DEVICE_PRIVATE_H
+#define DS_DATA_DEVICE_PRIVATE_H
+
+#include <wayland-server.h>
+
+#include "data_device.h"
+#include "seat.h"
+
+enum ds_data_offer_type
+{
+ DS_DATA_OFFER_SELECTION,
+};
+
+struct ds_data_device_manager
+{
+ struct wl_global *global;
+
+ struct wl_list data_devices; // ds_data_device.link
+
+ struct wl_listener display_destroy;
+
+ struct {
+ struct wl_signal destroy;
+ struct wl_signal request_set_selection;
+ struct wl_signal request_data_offer_receive;
+ } events;
+};
+
+struct ds_data_device
+{
+ struct wl_resource *resource;
+ struct ds_data_device_manager *manager;
+ struct ds_seat_client *seat_client;
+ struct ds_seat *seat;
+
+ struct wl_list selection_offers; // ds_data_offer.link
+ struct wl_list link; // ds_data_device_manager.data_devices
+
+ struct wl_listener seat_client_destroy;
+ struct wl_listener seat_set_selection;
+ struct wl_listener keyboard_focus_change;
+};
+
+struct ds_data_source_client
+{
+ struct ds_data_source base;
+ struct ds_data_source_interface iface;
+ struct wl_resource *resource;
+
+ bool finalized;
+};
+
+struct ds_data_offer
+{
+ struct wl_resource *resource;
+ struct ds_data_device *data_device;
+ struct ds_data_source *source;
+ enum ds_data_offer_type type;
+
+ uint32_t actions;
+
+ struct wl_list link; // ds_data_device.data_offers
+
+ struct wl_listener source_destroy;
+};
+
+struct ds_data_device *create_data_device(
+ struct ds_data_device_manager *manager, struct wl_client *client,
+ uint32_t version, uint32_t id, struct wl_resource *seat_resource);
+
+void data_device_destroy(struct ds_data_device *data_device);
+
+struct ds_data_source_client *create_data_source_client(
+ struct wl_client *wl_client, uint32_t version, uint32_t id);
+
+struct ds_data_offer *
+create_data_offer(struct ds_data_device *data_device,
+ struct ds_data_source *source, enum ds_data_offer_type type);
+
+#endif
--- /dev/null
+#include <stdlib.h>
+
+#include "data_device_private.h"
+
+static const struct wl_data_offer_interface data_offer_iface;
+
+static void data_offer_handle_resource_destroy(struct wl_resource *resource);
+static void data_offer_handle_source_destroy(struct wl_listener *listener,
+ void *data);
+
+WL_EXPORT void
+ds_data_offer_send(struct ds_data_offer *offer, const char *mime_type,
+ int32_t fd)
+{
+ ds_data_source_send(offer->source, mime_type, fd);
+}
+
+struct ds_data_offer *
+create_data_offer(struct ds_data_device *data_device,
+ struct ds_data_source *source, enum ds_data_offer_type type)
+{
+ struct ds_data_offer *offer;
+ struct wl_client *client;
+ uint32_t version;
+ char **p;
+
+ offer = calloc(1, sizeof *offer);
+ if (!offer)
+ return NULL;
+
+ offer->source = source;
+ offer->data_device = data_device;
+ offer->type = type;
+
+ client = wl_resource_get_client(data_device->resource);
+ version = wl_resource_get_version(data_device->resource);
+ offer->resource = wl_resource_create(client, &wl_data_offer_interface,
+ version, 0);
+ if (!offer->resource) {
+ free(offer);
+ return NULL;
+ }
+
+ wl_resource_set_implementation(offer->resource, &data_offer_iface, offer,
+ data_offer_handle_resource_destroy);
+
+ offer->source_destroy.notify = data_offer_handle_source_destroy;
+ wl_signal_add(&source->events.destroy, &offer->source_destroy);
+
+ wl_data_device_send_data_offer(data_device->resource, offer->resource);
+
+ wl_array_for_each(p, &source->mime_types)
+ wl_data_offer_send_offer(offer->resource, *p);
+
+ return offer;
+}
+
+static void
+data_offer_destroy(struct ds_data_offer *offer)
+{
+ wl_list_remove(&offer->source_destroy.link);
+
+ wl_resource_set_user_data(offer->resource, NULL);
+
+ free(offer);
+}
+
+static void
+data_offer_handle_resource_destroy(struct wl_resource *resource)
+{
+ struct ds_data_offer *offer;
+
+ offer = wl_resource_get_user_data(resource);
+ if (!offer)
+ return;
+
+ data_offer_destroy(offer);
+}
+
+static void data_offer_handle_source_destroy(struct wl_listener *listener,
+ void *data)
+{
+ struct ds_data_offer *offer;
+
+ offer = wl_container_of(listener, offer, source_destroy);
+ data_offer_destroy(offer);
+}
+
+static void
+data_offer_handle_accept(struct wl_client *client,
+ struct wl_resource *resource, uint32_t serial, const char *mime_type)
+{
+ struct ds_data_offer *offer;
+
+ offer = wl_resource_get_user_data(resource);
+ if (!offer)
+ return;
+
+ // TODO
+}
+
+static void
+data_offer_handle_receive(struct wl_client *client,
+ struct wl_resource *resource, const char *mime_type, int32_t fd)
+{
+ struct ds_data_offer *offer;
+
+ offer = wl_resource_get_user_data(resource);
+ if (!offer)
+ return;
+
+ struct ds_event_request_data_offer_receive event = {
+ .offer = offer,
+ .mime_type = mime_type,
+ .fd = fd,
+ };
+ wl_signal_emit(
+ &offer->data_device->manager->events.request_data_offer_receive,
+ &event);
+}
+
+static void
+data_offer_handle_destroy(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+data_offer_handle_finish(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct ds_data_offer *offer;
+
+ offer = wl_resource_get_user_data(resource);
+ if (!offer)
+ return;
+
+ // TODO
+}
+
+static void
+data_offer_handle_set_actions(struct wl_client *client,
+ struct wl_resource *resource, uint32_t actions,
+ uint32_t preferred_action)
+{
+ struct ds_data_offer *offer;
+
+ offer = wl_resource_get_user_data(resource);
+ if (!offer)
+ return;
+
+ // TODO
+}
+
+static const struct wl_data_offer_interface data_offer_iface = {
+ .accept = data_offer_handle_accept,
+ .receive = data_offer_handle_receive,
+ .destroy = data_offer_handle_destroy,
+ .finish = data_offer_handle_finish,
+ .set_actions = data_offer_handle_set_actions,
+};
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+
+#include "data_device_private.h"
+
+void
+ds_data_source_init(struct ds_data_source *source,
+ const struct ds_data_source_interface *iface)
+{
+ assert(iface->send);
+
+ source->iface = iface;
+ wl_array_init(&source->mime_types);
+ wl_signal_init(&source->events.destroy);
+}
+
+void
+ds_data_source_destroy(struct ds_data_source *source)
+{
+ char **p;
+
+ wl_signal_emit(&source->events.destroy, source);
+
+ wl_array_for_each(p, &source->mime_types)
+ free(*p);
+ wl_array_release(&source->mime_types);
+
+ if (source->iface->destroy)
+ source->iface->destroy(source);
+ else
+ free(source);
+}
+
+void
+ds_data_source_send(struct ds_data_source *source, const char *mime_type,
+ int32_t fd)
+{
+ source->iface->send(source, mime_type, fd);
+}
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+
+#include "libds/log.h"
+#include "data_device_private.h"
+
+static const struct wl_data_source_interface wl_data_source_iface;
+
+static void data_source_client_handle_resource_destroy(
+ struct wl_resource *resource);
+static void data_source_client_iface_accept(struct ds_data_source *source,
+ uint32_t serial, const char *mime_type);
+static void data_handle_client_iface_send(struct ds_data_source *source,
+ const char *mime_type, int32_t fd);
+static void data_source_client_iface_destroy(struct ds_data_source *source);
+
+struct ds_data_source_client *
+create_data_source_client(struct wl_client *client, uint32_t version,
+ uint32_t id)
+{
+ struct ds_data_source_client *source;
+
+ source = calloc(1, sizeof *source);
+ if (!source)
+ return NULL;
+
+ source->resource = wl_resource_create(client, &wl_data_source_interface,
+ version, id);
+ if (!source->resource) {
+ wl_client_post_no_memory(client);
+ free(source);
+ return NULL;
+ }
+
+ wl_resource_set_implementation(source->resource, &wl_data_source_iface,
+ source, data_source_client_handle_resource_destroy);
+
+ source->iface.accept = data_source_client_iface_accept;
+ source->iface.send = data_handle_client_iface_send;
+ source->iface.destroy = data_source_client_iface_destroy;
+
+ ds_data_source_init(&source->base, &source->iface);
+
+ return source;
+};
+
+static void
+data_source_client_handle_resource_destroy(struct wl_resource *resource)
+{
+ struct ds_data_source_client *source;
+
+ source = wl_resource_get_user_data(resource);
+ if (!source)
+ return;
+
+ ds_data_source_destroy(&source->base);
+}
+
+static void
+data_source_client_handle_destroy(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+data_source_client_handle_offer(struct wl_client *client,
+ struct wl_resource *resource, const char *mime_type)
+{
+ struct ds_data_source_client *source;
+ const char **mime_type_ptr;
+ char *dup_mime_type;
+ char **p;
+
+ source = wl_resource_get_user_data(resource);
+ if (!source)
+ return;
+
+ if (source->finalized) {
+ ds_err("Offering additional MIME type after "
+ "wl_data_device.set_selection");
+ }
+
+ wl_array_for_each(mime_type_ptr, &source->base.mime_types) {
+ if (strcmp(*mime_type_ptr, mime_type) == 0) {
+ ds_dbg("Ignoring duplicate MIME type offer %s", mime_type);
+ return;
+ }
+ }
+
+ dup_mime_type = strdup(mime_type);
+ if (!dup_mime_type) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ p = wl_array_add(&source->base.mime_types, sizeof *p);
+ if (!p) {
+ free(dup_mime_type);
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ *p = dup_mime_type;
+}
+
+static void
+data_source_client_handle_set_actions(struct wl_client *client,
+ struct wl_resource *resource, uint32_t dnd_actions)
+{
+ struct ds_data_source_client *source;
+
+ source = wl_resource_get_user_data(resource);
+ if (!source)
+ return;
+
+ // TODO
+}
+
+static const struct wl_data_source_interface wl_data_source_iface = {
+ .destroy = data_source_client_handle_destroy,
+ .offer = data_source_client_handle_offer,
+ .set_actions = data_source_client_handle_set_actions,
+};
+
+static struct ds_data_source_client *
+data_source_client_from_data_source(struct ds_data_source *ds_source)
+{
+ assert(ds_source->iface->accept == data_source_client_iface_accept);
+ return (struct ds_data_source_client *)ds_source;
+}
+
+static void
+data_source_client_iface_accept(struct ds_data_source *ds_source,
+ uint32_t serial, const char *mime_type)
+{
+ struct ds_data_source_client *source;
+
+ source = data_source_client_from_data_source(ds_source);
+ wl_data_source_send_target(source->resource, mime_type);
+}
+
+static void
+data_handle_client_iface_send(struct ds_data_source *ds_source,
+ const char *mime_type, int32_t fd)
+{
+ struct ds_data_source_client *source;
+
+ source = data_source_client_from_data_source(ds_source);
+ wl_data_source_send_send(source->resource, mime_type, fd);
+ close(fd);
+}
+
+static void
+data_source_client_iface_destroy(struct ds_data_source *ds_source)
+{
+ struct ds_data_source_client *source;
+
+ source = data_source_client_from_data_source(ds_source);
+ wl_data_source_send_cancelled(source->resource);
+ wl_resource_set_user_data(source->resource, NULL);
+ free(source);
+}
#include <wayland-server.h>
#include "libds/log.h"
+#include "data_device_private.h"
#define DATA_DEVICE_MANAGER_VERSION 3
-struct ds_data_device_manager
-{
- struct wl_global *global;
-
- struct wl_listener display_destroy;
-
- struct {
- struct wl_signal destroy;
- } events;
-};
-
static void
data_device_manager_bind(struct wl_client *client, void *data,
uint32_t version, uint32_t id);
return NULL;
}
+ wl_list_init(&manager->data_devices);
+
wl_signal_init(&manager->events.destroy);
+ wl_signal_init(&manager->events.request_set_selection);
+ wl_signal_init(&manager->events.request_data_offer_receive);
manager->global = wl_global_create(display,
&wl_data_device_manager_interface, DATA_DEVICE_MANAGER_VERSION,
wl_signal_add(&manager->events.destroy, listener);
}
+WL_EXPORT void
+ds_data_device_manager_add_request_set_selection_listener(
+ struct ds_data_device_manager *manager, struct wl_listener *listener)
+{
+ wl_signal_add(&manager->events.request_set_selection, listener);
+}
+
+WL_EXPORT void
+ds_data_device_manager_add_request_data_offer_receive_listener(
+ struct ds_data_device_manager *manager, struct wl_listener *listener)
+{
+ wl_signal_add(&manager->events.request_data_offer_receive, listener);
+}
+
static const struct wl_data_device_manager_interface data_device_manager_iface;
static void
struct wl_client *client, struct wl_resource *resource,
uint32_t id)
{
+ create_data_source_client(client, wl_resource_get_version(resource), id);
}
static void data_device_manager_handle_get_data_device(
struct wl_client *client, struct wl_resource *resource,
uint32_t id, struct wl_resource *seat_resource)
{
+ struct ds_data_device_manager *manager;
+ uint32_t version;
+
+ manager = wl_resource_get_user_data(resource);
+ version = wl_resource_get_version(resource);
+
+ create_data_device(manager, client, version, id, seat_resource);
}
static const struct wl_data_device_manager_interface
libds_files += files(
'manager.c',
+ 'data_device.c',
+ 'data_source.c',
+ 'data_source_client.c',
+ 'data_offer.c',
)
#include <string.h>
#include "libds/log.h"
+#include "data_device.h"
#include "seat_private.h"
#define SEAT_VERSION 7
return;
if (seat->selection_source) {
- /* TODO
wl_list_remove(&seat->selection_source_destroy.link);
ds_data_source_destroy(seat->selection_source);
- */
seat->selection_source = NULL;
}
if (source) {
- /* TODO
seat->selection_source_destroy.notify =
seat_handle_selection_source_destroy;
wl_signal_add(&source->events.destroy,
&seat->selection_source_destroy);
- */
-
seat->selection_source = source;
}