compositor: Add a clipboard manager
authorKristian Høgsberg <krh@bitplanet.net>
Sun, 3 Jun 2012 14:20:13 +0000 (10:20 -0400)
committerKristian Høgsberg <krh@bitplanet.net>
Mon, 4 Jun 2012 02:54:36 +0000 (22:54 -0400)
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
src/clipboard.c [new file with mode: 0644]
src/compositor.c
src/compositor.h

index 64932a1..51f7ad5 100644 (file)
@@ -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 (file)
index 0000000..f5caf41
--- /dev/null
@@ -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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <linux/input.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/uio.h>
+
+#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;
+}
index b299e03..797ab99 100644 (file)
@@ -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
index d070471..836f10a 100644 (file)
@@ -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);