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.
This commit is contained in:
parent
e220327223
commit
a7ceafff9c
@ -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 \
|
||||
|
263
src/clipboard.c
Normal file
263
src/clipboard.c
Normal file
@ -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;
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user