/* * Copyright © 2012 Intel Corporation * * 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 (including the * next paragraph) 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 "config.h" #include #include #include #include #include #include #include #include "compositor.h" #include "shared/helpers.h" struct clipboard_source { struct weston_data_source base; struct wl_array contents; struct clipboard *clipboard; struct wl_event_source *event_source; uint32_t serial; int refcount; int fd; }; 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); close(source->fd); } wl_signal_emit(&source->base.destroy_signal, &source->base); 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); close(fd); 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 weston_data_source *source, uint32_t time, const char *mime_type) { } static void clipboard_source_send(struct weston_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 weston_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 = zalloc(sizeof *source); if (source == NULL) return NULL; wl_array_init(&source->contents); wl_array_init(&source->base.mime_types); source->base.resource = NULL; source->base.accept = clipboard_source_accept; source->base.send = clipboard_source_send; source->base.cancel = clipboard_source_cancel; wl_signal_init(&source->base.destroy_signal); source->refcount = 1; source->clipboard = clipboard; source->serial = serial; source->fd = fd; s = wl_array_add(&source->base.mime_types, sizeof *s); if (s == NULL) goto err_add; *s = strdup(mime_type); if (*s == NULL) goto err_strdup; source->event_source = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE, clipboard_source_data, source); if (source->event_source == NULL) goto err_source; return source; err_source: free(*s); err_strdup: wl_array_release(&source->base.mime_types); err_add: free(source); return NULL; } 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 = zalloc(sizeof *client); if (client == NULL) return; 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 weston_data_source *source = seat->selection_data_source; const char **mime_types; int p[2]; if (source == NULL) { if (clipboard->source) weston_seat_set_selection(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 (!mime_types || pipe2(p, O_CLOEXEC) == -1) return; source->send(source, mime_types[0], p[1]); clipboard->source = clipboard_source_create(clipboard, mime_types[0], seat->selection_serial, p[0]); if (clipboard->source == NULL) { close(p[0]); return; } } static void clipboard_destroy(struct wl_listener *listener, void *data) { struct clipboard *clipboard = container_of(listener, struct clipboard, destroy_listener); wl_list_remove(&clipboard->selection_listener.link); wl_list_remove(&clipboard->destroy_listener.link); free(clipboard); } struct clipboard * clipboard_create(struct weston_seat *seat) { struct clipboard *clipboard; clipboard = zalloc(sizeof *clipboard); if (clipboard == NULL) return NULL; clipboard->seat = seat; clipboard->selection_listener.notify = clipboard_set_selection; clipboard->destroy_listener.notify = clipboard_destroy; wl_signal_add(&seat->selection_signal, &clipboard->selection_listener); wl_signal_add(&seat->destroy_signal, &clipboard->destroy_listener); return clipboard; }