From a7ceafff9c4fd82910aea2103d6428fb1268165d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Sun, 3 Jun 2012 10:20:13 -0400 Subject: [PATCH] compositor: Add a clipboard manager We use the selection signal to get a callback when somebody sets a selection (including the X server proxy) and then copy the contents of the first mime type. If the selection is cleared (when the client dies), we set a new selection with that contents. --- src/Makefile.am | 1 + src/clipboard.c | 263 +++++++++++++++++++++++++++++++++++++++++++++++ src/compositor.c | 2 + src/compositor.h | 3 + 4 files changed, 269 insertions(+) create mode 100644 src/clipboard.c diff --git a/src/Makefile.am b/src/Makefile.am index 64932a11..51f7ad59 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,6 +18,7 @@ weston_SOURCES = \ screenshooter.c \ screenshooter-protocol.c \ screenshooter-server-protocol.h \ + clipboard.c \ text-cursor-position.c \ text-cursor-position-protocol.c \ text-cursor-position-server-protocol.h \ diff --git a/src/clipboard.c b/src/clipboard.c new file mode 100644 index 00000000..f5caf418 --- /dev/null +++ b/src/clipboard.c @@ -0,0 +1,263 @@ +/* + * Copyright © 2012 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#include "compositor.h" + +struct clipboard_source { + struct wl_data_source base; + struct wl_array contents; + struct clipboard *clipboard; + struct wl_event_source *event_source; + uint32_t serial; + int refcount; +}; + +struct clipboard { + struct weston_seat *seat; + struct wl_listener selection_listener; + struct wl_listener destroy_listener; + struct clipboard_source *source; +}; + +static void clipboard_client_create(struct clipboard_source *source, int fd); + +static void +clipboard_source_unref(struct clipboard_source *source) +{ + char **s; + + source->refcount--; + if (source->refcount > 0) + return; + + if (source->event_source) + wl_event_source_remove(source->event_source); + wl_signal_emit(&source->base.resource.destroy_signal, + &source->base.resource); + s = source->base.mime_types.data; + free(*s); + wl_array_release(&source->base.mime_types); + wl_array_release(&source->contents); + free(source); +} + +static int +clipboard_source_data(int fd, uint32_t mask, void *data) +{ + struct clipboard_source *source = data; + struct clipboard *clipboard = source->clipboard; + char *p; + int len, size; + + if (source->contents.alloc - source->contents.size < 1024) { + wl_array_add(&source->contents, 1024); + source->contents.size -= 1024; + } + + p = source->contents.data + source->contents.size; + size = source->contents.alloc - source->contents.size; + len = read(fd, p, size); + if (len == 0) { + wl_event_source_remove(source->event_source); + source->event_source = NULL; + } else if (len < 0) { + clipboard_source_unref(source); + clipboard->source = NULL; + } else { + source->contents.size += len; + } + + return 1; +} + +static void +clipboard_source_accept(struct wl_data_source *source, + uint32_t time, const char *mime_type) +{ +} + +static void +clipboard_source_send(struct wl_data_source *base, + const char *mime_type, int32_t fd) +{ + struct clipboard_source *source = + container_of(base, struct clipboard_source, base); + char **s; + + s = source->base.mime_types.data; + if (strcmp(mime_type, s[0]) == 0) + clipboard_client_create(source, fd); + else + close(fd); +} + +static void +clipboard_source_cancel(struct wl_data_source *source) +{ +} + +static struct clipboard_source * +clipboard_source_create(struct clipboard *clipboard, + const char *mime_type, uint32_t serial, int fd) +{ + struct wl_display *display = clipboard->seat->compositor->wl_display; + struct wl_event_loop *loop = wl_display_get_event_loop(display); + struct clipboard_source *source; + char **s; + + source = malloc(sizeof *source); + wl_array_init(&source->contents); + wl_array_init(&source->base.mime_types); + source->base.accept = clipboard_source_accept; + source->base.send = clipboard_source_send; + source->base.cancel = clipboard_source_cancel; + source->base.resource.data = &source->base; + wl_signal_init(&source->base.resource.destroy_signal); + source->refcount = 1; + source->clipboard = clipboard; + source->serial = serial; + + s = wl_array_add(&source->base.mime_types, sizeof *s); + *s = strdup(mime_type); + + source->event_source = + wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE, + clipboard_source_data, source); + + return source; +} + +struct clipboard_client { + struct wl_event_source *event_source; + size_t offset; + struct clipboard_source *source; +}; + +static int +clipboard_client_data(int fd, uint32_t mask, void *data) +{ + struct clipboard_client *client = data; + char *p; + size_t size; + int len; + + size = client->source->contents.size; + p = client->source->contents.data; + len = write(fd, p + client->offset, size - client->offset); + if (len > 0) + client->offset += len; + + if (client->offset == size || len <= 0) { + close(fd); + wl_event_source_remove(client->event_source); + clipboard_source_unref(client->source); + free(client); + } + + return 1; +} + +static void +clipboard_client_create(struct clipboard_source *source, int fd) +{ + struct weston_seat *seat = source->clipboard->seat; + struct clipboard_client *client; + struct wl_event_loop *loop = + wl_display_get_event_loop(seat->compositor->wl_display); + + client = malloc(sizeof *client); + + client->offset = 0; + client->source = source; + source->refcount++; + client->event_source = + wl_event_loop_add_fd(loop, fd, WL_EVENT_WRITABLE, + clipboard_client_data, client); +} + +static void +clipboard_set_selection(struct wl_listener *listener, void *data) +{ + struct clipboard *clipboard = + container_of(listener, struct clipboard, selection_listener); + struct weston_seat *seat = data; + struct wl_data_source *source = seat->seat.selection_data_source; + const char **mime_types; + int p[2]; + + if (source == NULL) { + if (clipboard->source) + wl_seat_set_selection(&seat->seat, + &clipboard->source->base, + clipboard->source->serial); + return; + } else if (source->accept == clipboard_source_accept) { + /* Callback for our data source. */ + return; + } + + if (clipboard->source) + clipboard_source_unref(clipboard->source); + + clipboard->source = NULL; + + mime_types = source->mime_types.data; + + if (pipe2(p, O_CLOEXEC) == -1) + return; + + source->send(source, mime_types[0], p[1]); + + clipboard->source = + clipboard_source_create(clipboard, mime_types[0], + seat->seat.selection_serial, p[0]); + if (clipboard->source == NULL) + return; +} + +struct clipboard * +clipboard_create(struct weston_seat *seat) +{ + struct clipboard *clipboard; + + clipboard = malloc(sizeof *clipboard); + if (clipboard == NULL) + return NULL; + + clipboard->seat = seat; + clipboard->selection_listener.notify = clipboard_set_selection; + + wl_signal_add(&seat->seat.selection_signal, + &clipboard->selection_listener); + + return clipboard; +} diff --git a/src/compositor.c b/src/compositor.c index b299e03d..797ab99a 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -2454,6 +2454,8 @@ weston_seat_init(struct weston_seat *seat, struct weston_compositor *ec) seat->new_drag_icon_listener.notify = device_handle_new_drag_icon; wl_signal_add(&seat->seat.drag_icon_signal, &seat->new_drag_icon_listener); + + clipboard_create(seat); } WL_EXPORT void diff --git a/src/compositor.h b/src/compositor.h index d0704710..836f10a0 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -677,6 +677,9 @@ tty_activate_vt(struct tty *tty, int vt); void screenshooter_create(struct weston_compositor *ec); +struct clipboard * +clipboard_create(struct weston_seat *seat); + void text_cursor_position_notifier_create(struct weston_compositor *ec);