commit ba0d051b9c19f0c8a382f7854add1b6cb9ce6ede
parent 7c074d1a4ce3e8205f0adb337a3ca1fd061e193b
Author: Michael Forney <mforney@mforney.org>
Date: Mon, 1 Jul 2013 04:37:34 -0700
Implement copy and paste (data_{device{,_manager},source,offer})
Diffstat:
M | Makefile.am | | | 1 | + |
A | data.c | | | 174 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | data.h | | | 39 | +++++++++++++++++++++++++++++++++++++++ |
M | data_device.c | | | 125 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- |
M | data_device.h | | | 50 | +++++++++++++++++++++++++++++++++++++++++++++++++- |
M | data_device_manager.c | | | 38 | +++++++++++++++++++++++++++++++++----- |
M | data_device_manager.h | | | 23 | +++++++++++++++++++++++ |
M | seat.c | | | 56 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | seat.h | | | 6 | ++++++ |
9 files changed, 502 insertions(+), 10 deletions(-)
diff --git a/Makefile.am b/Makefile.am
@@ -19,6 +19,7 @@ libswc_la_SOURCES = \
seat.c seat.h \
data_device_manager.c data_device_manager.h \
data_device.c data_device.h \
+ data.c data.h \
mode.c mode.h \
tty.c tty.h \
evdev_device.c evdev_device.h \
diff --git a/data.c b/data.c
@@ -0,0 +1,174 @@
+/* swc: data.c
+ *
+ * Copyright (c) 2013 Michael Forney
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "data.h"
+#include "util.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+struct data
+{
+ struct wl_array mime_types;
+ struct wl_resource * source;
+ struct wl_list offers;
+};
+
+static void offer_accept(struct wl_client * client,
+ struct wl_resource * offer,
+ uint32_t serial, const char * mime_type)
+{
+ struct data * data = wl_resource_get_user_data(offer);
+
+ /* Protect against expired data_offers being used. */
+ if (data)
+ wl_data_source_send_target(data->source, mime_type);
+}
+
+static void offer_receive(struct wl_client * client,
+ struct wl_resource * offer,
+ const char * mime_type, int fd)
+{
+ struct data * data = wl_resource_get_user_data(offer);
+
+ /* Protect against expired data_offers being used. */
+ if (data)
+ wl_data_source_send_send(data->source, mime_type, fd);
+}
+
+static void offer_destroy(struct wl_client * client,
+ struct wl_resource * offer)
+{
+ wl_resource_destroy(offer);
+}
+
+struct wl_data_offer_interface data_offer_implementation = {
+ .accept = &offer_accept,
+ .receive = &offer_receive,
+ .destroy = &offer_destroy
+};
+
+static void source_offer(struct wl_client * client,
+ struct wl_resource * source,
+ const char * mime_type)
+{
+ struct data * data = wl_resource_get_user_data(source);
+ char ** destination;
+
+ destination = wl_array_add(&data->mime_types, sizeof *destination);
+ *destination = strdup(mime_type);
+}
+
+static void source_destroy(struct wl_client * client,
+ struct wl_resource * source)
+{
+ wl_resource_destroy(source);
+}
+
+struct wl_data_source_interface data_source_implementation = {
+ .offer = &source_offer,
+ .destroy = &source_destroy
+};
+
+static void data_destroy(struct wl_resource * source)
+{
+ struct data * data = wl_resource_get_user_data(source);
+ struct wl_resource * offer;
+ char ** mime_type;
+
+ wl_array_for_each(mime_type, &data->mime_types)
+ free(*mime_type);
+ wl_array_release(&data->mime_types);
+
+ /* After this data_source is destroyed, each of the data_offer objects
+ * associated with the data_source has a pointer to a free'd struct. We
+ * can't destroy the resources because this results in a segfault on the
+ * client when it correctly tries to call data_source.destroy. However, a
+ * misbehaving client could still attempt to call accept or receive on the
+ * data_offer, which would crash the server.
+ *
+ * So, we clear the user data on each of the offers to protect us. */
+ wl_list_for_each(offer, &data->offers, link)
+ wl_resource_set_user_data(offer, NULL);
+
+ free(data);
+}
+
+static struct data * data_new()
+{
+ struct data * data;
+
+ data = malloc(sizeof *data);
+
+ if (!data)
+ return NULL;
+
+ wl_array_init(&data->mime_types);
+ wl_list_init(&data->offers);
+
+ return data;
+}
+
+struct wl_resource * swc_data_source_new(struct wl_client * client, uint32_t id)
+{
+ struct data * data;
+
+ data = data_new();
+
+ if (!data)
+ return NULL;
+
+ /* Add the data source to the client. */
+ data->source = wl_client_add_object(client, &wl_data_source_interface,
+ &data_source_implementation, id, data);
+
+ /* Destroy the data object when the source disappears. */
+ wl_resource_set_destructor(data->source, &data_destroy);
+
+ return data->source;
+}
+
+struct wl_resource * swc_data_offer_new(struct wl_client * client,
+ struct wl_resource * source)
+{
+ struct data * data = wl_resource_get_user_data(source);
+ struct wl_resource * offer;
+
+ offer = wl_client_new_object(client, &wl_data_offer_interface,
+ &data_offer_implementation, data);
+ wl_list_insert(&data->offers, wl_resource_get_link(offer));
+ wl_resource_set_destructor(offer, &swc_remove_resource);
+
+ return offer;
+}
+
+void swc_data_send_mime_types(struct wl_resource * source,
+ struct wl_resource * offer)
+{
+ struct data * data = wl_resource_get_user_data(source);
+ char ** mime_type;
+
+ wl_array_for_each(mime_type, &data->mime_types)
+ wl_data_offer_send_offer(offer, *mime_type);
+}
+
diff --git a/data.h b/data.h
@@ -0,0 +1,39 @@
+/* swc: data.h
+ *
+ * Copyright (c) 2013 Michael Forney
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef SWC_DATA_H
+#define SWC_DATA_H 1
+
+#include <wayland-server.h>
+
+struct wl_resource * swc_data_source_new(struct wl_client * client,
+ uint32_t id);
+
+struct wl_resource * swc_data_offer_new(struct wl_client * client,
+ struct wl_resource * source);
+
+void swc_data_send_mime_types(struct wl_resource * source,
+ struct wl_resource * offer);
+
+#endif
+
diff --git a/data_device.c b/data_device.c
@@ -1,16 +1,66 @@
+/* swc: data_device.c
+ *
+ * Copyright (c) 2013 Michael Forney
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
#include "data_device.h"
+#include "data.h"
+#include "util.h"
static void 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)
{
+ /* XXX: Implement */
}
static void set_selection(struct wl_client * client,
struct wl_resource * resource,
- struct wl_resource * source_resource, uint32_t serial)
+ struct wl_resource * data_source, uint32_t serial)
{
+ struct swc_data_device * data_device = wl_resource_get_user_data(resource);
+ struct swc_event event;
+
+ /* Check if this data source is already the current selection. */
+ if (data_source == data_device->selection)
+ return;
+
+ event.type = SWC_DATA_DEVICE_EVENT_SELECTION_CHANGED;
+
+ if (data_device->selection)
+ {
+ wl_data_source_send_cancelled(data_device->selection);
+ wl_list_remove(&data_device->selection_destroy_listener.link);
+ }
+
+ data_device->selection = data_source;
+
+ if (data_source)
+ {
+ wl_resource_add_destroy_listener
+ (data_source, &data_device->selection_destroy_listener);
+ }
+
+ wl_signal_emit(&data_device->event_signal, &event);
}
struct wl_data_device_interface data_device_implementation = {
@@ -18,9 +68,76 @@ struct wl_data_device_interface data_device_implementation = {
.set_selection = &set_selection
};
-void swc_data_device_new(struct wl_client * client, uint32_t id)
+static void handle_selection_destroy(struct wl_listener * listener, void * data)
+{
+ struct swc_data_device * data_device
+ = wl_container_of(listener, data_device, selection_destroy_listener);
+ struct swc_event event;
+
+ event.type = SWC_DATA_DEVICE_EVENT_SELECTION_CHANGED;
+ data_device->selection = NULL;
+ wl_signal_emit(&data_device->event_signal, &event);
+}
+
+bool swc_data_device_initialize(struct swc_data_device * data_device)
+{
+ data_device->selection_destroy_listener.notify = &handle_selection_destroy;
+ wl_signal_init(&data_device->event_signal);
+ wl_list_init(&data_device->resources);
+
+ return true;
+}
+
+void swc_data_device_finish(struct swc_data_device * data_device)
+{
+ struct wl_resource * resource, * tmp;
+
+ wl_list_for_each_safe(resource, tmp, &data_device->resources, link)
+ wl_resource_destroy(resource);
+}
+
+void swc_data_device_bind(struct swc_data_device * data_device,
+ struct wl_client * client, uint32_t id)
+{
+ struct wl_resource * resource;
+
+ resource = wl_client_add_object(client, &wl_data_device_interface,
+ &data_device_implementation, id,
+ data_device);
+ wl_list_insert(&data_device->resources, &resource->link);
+ wl_resource_set_destructor(resource, &swc_remove_resource);
+}
+
+static struct wl_resource * new_offer(struct wl_resource * resource,
+ struct wl_client * client,
+ struct wl_resource * source)
+{
+ struct wl_resource * offer;
+
+ offer = swc_data_offer_new(client, source);
+ wl_data_device_send_data_offer(resource, offer);
+ swc_data_send_mime_types(source, offer);
+
+ return offer;
+}
+
+void swc_data_device_offer_selection(struct swc_data_device * data_device,
+ struct wl_client * client)
{
- wl_client_add_object(client, &wl_data_device_interface,
- &data_device_implementation, id, NULL);
+ struct wl_resource * resource;
+ struct wl_resource * offer;
+
+ /* Look for the client's data_device resource. */
+ resource = wl_resource_find_for_client(&data_device->resources, client);
+
+ /* If the client does not have a data device, there is nothing to do. */
+ if (!resource)
+ return;
+
+ /* If we don't have a selection, send NULL to the client. */
+ offer = data_device->selection
+ ? new_offer(resource, client, data_device->selection) : NULL;
+
+ wl_data_device_send_selection(resource, offer);
}
diff --git a/data_device.h b/data_device.h
@@ -1,9 +1,57 @@
+/* swc: data_device.h
+ *
+ * Copyright (c) 2013 Michael Forney
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
#ifndef SWC_DATA_DEVICE_H
#define SWC_DATA_DEVICE_H 1
+#include "event.h"
+
+#include <stdbool.h>
#include <wayland-server.h>
-void swc_data_device_new(struct wl_client * client, uint32_t id);
+enum swc_data_device_event_type
+{
+ SWC_DATA_DEVICE_EVENT_SELECTION_CHANGED
+};
+
+struct swc_data_device
+{
+ /* The data source corresponding to the current selection. */
+ struct wl_resource * selection;
+ struct wl_listener selection_destroy_listener;
+
+ struct wl_signal event_signal;
+ struct wl_list resources;
+};
+
+bool swc_data_device_initialize(struct swc_data_device * data_device);
+void swc_data_device_finish(struct swc_data_device * data_device);
+
+void swc_data_device_bind(struct swc_data_device * data_device,
+ struct wl_client * client, uint32_t id);
+
+void swc_data_device_offer_selection(struct swc_data_device * data_device,
+ struct wl_client * client);
#endif
diff --git a/data_device_manager.c b/data_device_manager.c
@@ -1,11 +1,40 @@
+/* swc: data_device_manager.c
+ *
+ * Copyright (c) 2013 Michael Forney
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
#include "data_device_manager.h"
#include "data_device.h"
-
-#include <stdio.h>
+#include "data.h"
+#include "seat.h"
static void create_data_source(struct wl_client * client,
struct wl_resource * resource, uint32_t id)
{
+ struct wl_resource * data_source;
+
+ data_source = swc_data_source_new(client, id);
+
+ if (!data_source)
+ wl_resource_post_no_memory(resource);
}
static void get_data_device(struct wl_client * client,
@@ -14,10 +43,9 @@ static void get_data_device(struct wl_client * client,
{
struct swc_seat * seat = wl_resource_get_user_data(seat_resource);
- printf("get_data_device\n");
+ printf("data_device_manager.get_data_device\n");
- // TODO: keep track of resource?
- swc_data_device_new(client, id);
+ swc_data_device_bind(&seat->data_device, client, id);
}
static struct wl_data_device_manager_interface
diff --git a/data_device_manager.h b/data_device_manager.h
@@ -1,3 +1,26 @@
+/* swc: data_device_manager.h
+ *
+ * Copyright (c) 2013 Michael Forney
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
#ifndef SWC_DATA_DEVICE_MANAGER_H
#define SWC_DATA_DEVICE_MANAGER_H 1
diff --git a/seat.c b/seat.c
@@ -156,6 +156,48 @@ static void handle_evdev_event(struct wl_listener * listener, void * data)
}
}
+static void handle_keyboard_focus_event(struct wl_listener * listener,
+ void * data)
+{
+ struct swc_seat * seat
+ = wl_container_of(listener, seat, keyboard_focus_listener);
+ struct swc_event * event = data;
+ struct swc_input_focus_event_data * event_data = event->data;
+
+ switch (event->type)
+ {
+ case SWC_INPUT_FOCUS_EVENT_CHANGED:
+ if (event_data->new)
+ {
+ struct wl_client * client
+ = wl_resource_get_client(event_data->new->resource);
+
+ /* Offer the selection to the new focus. */
+ swc_data_device_offer_selection(&seat->data_device, client);
+ }
+ break;
+ }
+}
+
+static void handle_data_device_event(struct wl_listener * listener, void * data)
+{
+ struct swc_seat * seat
+ = wl_container_of(listener, seat, data_device_listener);
+ struct swc_event * event = data;
+
+ switch (event->type)
+ {
+ case SWC_DATA_DEVICE_EVENT_SELECTION_CHANGED:
+ if (seat->keyboard.focus.resource)
+ {
+ struct wl_client * client
+ = wl_resource_get_client(seat->keyboard.focus.resource);
+ swc_data_device_offer_selection(&seat->data_device, client);
+ }
+ break;
+ }
+}
+
/* Wayland Seat Interface */
static void get_pointer(struct wl_client * client, struct wl_resource * resource,
uint32_t id)
@@ -260,6 +302,8 @@ static void add_device(struct swc_seat * seat, struct udev_device * udev_device)
{
printf("initializing keyboard\n");
swc_keyboard_initialize(&seat->keyboard);
+ wl_signal_add(&seat->keyboard.focus.event_signal,
+ &seat->keyboard_focus_listener);
seat->capabilities |= WL_SEAT_CAPABILITY_KEYBOARD;
update_capabilities(seat);
}
@@ -274,6 +318,8 @@ bool swc_seat_initialize(struct swc_seat * seat, struct udev * udev,
{
seat->name = strdup(seat_name);
seat->capabilities = 0;
+ seat->keyboard_focus_listener.notify = &handle_keyboard_focus_event;
+ seat->data_device_listener.notify = &handle_data_device_event;
if (!swc_xkb_initialize(&seat->xkb))
{
@@ -281,6 +327,14 @@ bool swc_seat_initialize(struct swc_seat * seat, struct udev * udev,
goto error_name;
}
+ if (!swc_data_device_initialize(&seat->data_device))
+ {
+ printf("could not initialize data device\n");
+ goto error_xkb;
+ }
+
+ wl_signal_add(&seat->data_device.event_signal, &seat->data_device_listener);
+
wl_list_init(&seat->resources);
wl_signal_init(&seat->destroy_signal);
wl_list_init(&seat->devices);
@@ -288,6 +342,8 @@ bool swc_seat_initialize(struct swc_seat * seat, struct udev * udev,
return true;
+ error_xkb:
+ swc_xkb_finish(&seat->xkb);
error_name:
free(seat->name);
error_base:
diff --git a/seat.h b/seat.h
@@ -2,6 +2,7 @@
#define SWC_SEAT_H 1
#include "xkb.h"
+#include "data_device.h"
#include "keyboard.h"
#include "pointer.h"
@@ -20,7 +21,12 @@ struct swc_seat
struct wl_list resources;
struct wl_signal destroy_signal;
+ struct swc_data_device data_device;
+ struct wl_listener data_device_listener;
+
struct swc_keyboard keyboard;
+ struct wl_listener keyboard_focus_listener;
+
struct swc_pointer pointer;
struct wl_list devices;