xwayland: Split selection code into its own file

parent a9219013
......@@ -15,6 +15,7 @@ xserver_launcher_la_LIBADD = \
xserver_launcher_la_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS)
xserver_launcher_la_SOURCES = \
xserver-launcher.c \
selection.c \
xserver-protocol.c \
xserver-server-protocol.h \
hash.c \
......
/*
* 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 <unistd.h>
#include <fcntl.h>
#include "xwayland.h"
static int
weston_wm_write_property(int fd, uint32_t mask, void *data)
{
struct weston_wm *wm = data;
unsigned char *property;
int len, remainder;
property = xcb_get_property_value(wm->property_reply);
remainder = xcb_get_property_value_length(wm->property_reply) -
wm->property_start;
len = write(fd, property + wm->property_start, remainder);
if (len == -1) {
free(wm->property_reply);
wl_event_source_remove(wm->property_source);
close(fd);
fprintf(stderr, "write error to target fd: %m\n");
return 1;
}
fprintf(stderr, "wrote %d (chunk size %d) of %d bytes\n",
wm->property_start + len,
len, xcb_get_property_value_length(wm->property_reply));
wm->property_start += len;
if (len == remainder) {
free(wm->property_reply);
wl_event_source_remove(wm->property_source);
if (wm->incr) {
xcb_delete_property(wm->conn,
wm->selection_window,
wm->atom.wl_selection);
} else {
fprintf(stderr, "transfer complete\n");
close(fd);
}
}
return 1;
}
static void
weston_wm_get_incr_chunk(struct weston_wm *wm)
{
xcb_get_property_cookie_t cookie;
xcb_get_property_reply_t *reply;
cookie = xcb_get_property(wm->conn,
0, /* delete */
wm->selection_window,
wm->atom.wl_selection,
XCB_GET_PROPERTY_TYPE_ANY,
0, /* offset */
0x1fffffff /* length */);
reply = xcb_get_property_reply(wm->conn, cookie, NULL);
dump_property(wm, wm->atom.wl_selection, reply);
if (xcb_get_property_value_length(reply) > 0) {
wm->property_start = 0;
wm->property_source =
wl_event_loop_add_fd(wm->server->loop,
wm->data_source_fd,
WL_EVENT_WRITABLE,
weston_wm_write_property,
wm);
wm->property_reply = reply;
} else {
fprintf(stderr, "transfer complete\n");
close(wm->data_source_fd);
free(reply);
}
}
static void
data_offer_accept(struct wl_client *client, struct wl_resource *resource,
uint32_t time, const char *mime_type)
{
}
static void
data_offer_receive(struct wl_client *client, struct wl_resource *resource,
const char *mime_type, int32_t fd)
{
struct wl_data_offer *offer = resource->data;
struct weston_wm *wm = offer->source->resource.data;
if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) {
/* Get data for the utf8_string target */
xcb_convert_selection(wm->conn,
wm->selection_window,
wm->atom.clipboard,
wm->atom.utf8_string,
wm->atom.wl_selection,
XCB_TIME_CURRENT_TIME);
xcb_flush(wm->conn);
fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK);
wm->data_source_fd = fd;
} else {
close(fd);
}
}
static void
data_offer_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static const struct wl_data_offer_interface data_offer_interface = {
data_offer_accept,
data_offer_receive,
data_offer_destroy,
};
static void
data_source_cancel(struct wl_data_source *source)
{
}
static void
weston_wm_get_selection_targets(struct weston_wm *wm)
{
struct wl_data_source *source;
struct weston_compositor *compositor;
xcb_get_property_cookie_t cookie;
xcb_get_property_reply_t *reply;
xcb_atom_t *value;
char **p;
uint32_t i;
cookie = xcb_get_property(wm->conn,
1, /* delete */
wm->selection_window,
wm->atom.wl_selection,
XCB_GET_PROPERTY_TYPE_ANY,
0, /* offset */
4096 /* length */);
reply = xcb_get_property_reply(wm->conn, cookie, NULL);
dump_property(wm, wm->atom.wl_selection, reply);
if (reply->type != XCB_ATOM_ATOM) {
free(reply);
return;
}
source = malloc(sizeof *source);
if (source == NULL)
return;
wl_signal_init(&source->resource.destroy_signal);
source->offer_interface = &data_offer_interface;
source->cancel = data_source_cancel;
source->resource.data = wm;
wl_array_init(&source->mime_types);
value = xcb_get_property_value(reply);
for (i = 0; i < reply->value_len; i++) {
if (value[i] == wm->atom.utf8_string) {
p = wl_array_add(&source->mime_types, sizeof *p);
if (p)
*p = strdup("text/plain;charset=utf-8");
}
}
compositor = wm->server->compositor;
wl_seat_set_selection(&compositor->seat->seat, source,
wl_display_next_serial(compositor->wl_display));
free(reply);
}
static void
weston_wm_get_selection_data(struct weston_wm *wm)
{
xcb_get_property_cookie_t cookie;
xcb_get_property_reply_t *reply;
cookie = xcb_get_property(wm->conn,
1, /* delete */
wm->selection_window,
wm->atom.wl_selection,
XCB_GET_PROPERTY_TYPE_ANY,
0, /* offset */
0x1fffffff /* length */);
reply = xcb_get_property_reply(wm->conn, cookie, NULL);
if (reply->type == wm->atom.incr) {
dump_property(wm, wm->atom.wl_selection, reply);
wm->incr = 1;
free(reply);
} else {
dump_property(wm, wm->atom.wl_selection, reply);
wm->incr = 0;
wm->property_start = 0;
wm->property_source =
wl_event_loop_add_fd(wm->server->loop,
wm->data_source_fd,
WL_EVENT_WRITABLE,
weston_wm_write_property,
wm);
wm->property_reply = reply;
}
}
static void
weston_wm_handle_selection_notify(struct weston_wm *wm,
xcb_generic_event_t *event)
{
xcb_selection_notify_event_t *selection_notify =
(xcb_selection_notify_event_t *) event;
if (selection_notify->property == XCB_ATOM_NONE) {
/* convert selection failed */
} else if (selection_notify->target == wm->atom.targets) {
weston_wm_get_selection_targets(wm);
} else {
weston_wm_get_selection_data(wm);
}
}
static const size_t incr_chunk_size = 64 * 1024;
static void
weston_wm_send_selection_notify(struct weston_wm *wm, xcb_atom_t property)
{
xcb_selection_notify_event_t selection_notify;
memset(&selection_notify, 0, sizeof selection_notify);
selection_notify.response_type = XCB_SELECTION_NOTIFY;
selection_notify.sequence = 0;
selection_notify.time = wm->selection_request.time;
selection_notify.requestor = wm->selection_request.requestor;
selection_notify.selection = wm->selection_request.selection;
selection_notify.target = wm->selection_request.target;
selection_notify.property = property;
xcb_send_event(wm->conn, 0, /* propagate */
wm->selection_request.requestor,
XCB_EVENT_MASK_NO_EVENT, (char *) &selection_notify);
}
static void
weston_wm_send_targets(struct weston_wm *wm)
{
xcb_atom_t targets[] = {
wm->atom.timestamp,
wm->atom.targets,
wm->atom.utf8_string,
/* wm->atom.compound_text, */
wm->atom.text,
/* wm->atom.string */
};
xcb_change_property(wm->conn,
XCB_PROP_MODE_REPLACE,
wm->selection_request.requestor,
wm->selection_request.property,
XCB_ATOM_ATOM,
32, /* format */
ARRAY_LENGTH(targets), targets);
weston_wm_send_selection_notify(wm, wm->selection_request.property);
}
static void
weston_wm_send_timestamp(struct weston_wm *wm)
{
xcb_change_property(wm->conn,
XCB_PROP_MODE_REPLACE,
wm->selection_request.requestor,
wm->selection_request.property,
XCB_ATOM_INTEGER,
32, /* format */
1, &wm->selection_timestamp);
weston_wm_send_selection_notify(wm, wm->selection_request.property);
}
static int
weston_wm_flush_source_data(struct weston_wm *wm)
{
int length;
xcb_change_property(wm->conn,
XCB_PROP_MODE_REPLACE,
wm->selection_request.requestor,
wm->selection_request.property,
wm->selection_target,
8, /* format */
wm->source_data.size,
wm->source_data.data);
wm->selection_property_set = 1;
length = wm->source_data.size;
wm->source_data.size = 0;
return length;
}
static int
weston_wm_read_data_source(int fd, uint32_t mask, void *data)
{
struct weston_wm *wm = data;
int len, current, available;
void *p;
current = wm->source_data.size;
if (wm->source_data.size < incr_chunk_size)
p = wl_array_add(&wm->source_data, incr_chunk_size);
else
p = (char *) wm->source_data.data + wm->source_data.size;
available = wm->source_data.alloc - current;
len = read(fd, p, available);
if (len == -1) {
fprintf(stderr, "read error from data source: %m\n");
weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
wl_event_source_remove(wm->property_source);
close(fd);
wl_array_release(&wm->source_data);
}
fprintf(stderr, "read %d (available %d, mask 0x%x) bytes: \"%.*s\"\n",
len, available, mask, len, (char *) p);
wm->source_data.size = current + len;
if (wm->source_data.size >= incr_chunk_size) {
if (!wm->incr) {
fprintf(stderr, "got %zu bytes, starting incr\n",
wm->source_data.size);
wm->incr = 1;
xcb_change_property(wm->conn,
XCB_PROP_MODE_REPLACE,
wm->selection_request.requestor,
wm->selection_request.property,
wm->atom.incr,
32, /* format */
1, &incr_chunk_size);
wm->selection_property_set = 1;
wm->flush_property_on_delete = 1;
wl_event_source_remove(wm->property_source);
weston_wm_send_selection_notify(wm, wm->selection_request.property);
} else if (wm->selection_property_set) {
fprintf(stderr, "got %zu bytes, waiting for "
"property delete\n", wm->source_data.size);
wm->flush_property_on_delete = 1;
wl_event_source_remove(wm->property_source);
} else {
fprintf(stderr, "got %zu bytes, "
"property deleted, seting new property\n",
wm->source_data.size);
weston_wm_flush_source_data(wm);
}
} else if (len == 0 && !wm->incr) {
fprintf(stderr, "non-incr transfer complete\n");
/* Non-incr transfer all done. */
weston_wm_flush_source_data(wm);
weston_wm_send_selection_notify(wm, wm->selection_request.property);
xcb_flush(wm->conn);
wl_event_source_remove(wm->property_source);
close(fd);
wl_array_release(&wm->source_data);
wm->selection_request.requestor = XCB_NONE;
} else if (len == 0 && wm->incr) {
fprintf(stderr, "incr transfer complete\n");
wm->flush_property_on_delete = 1;
if (wm->selection_property_set) {
fprintf(stderr, "got %zu bytes, waiting for "
"property delete\n", wm->source_data.size);
} else {
fprintf(stderr, "got %zu bytes, "
"property deleted, seting new property\n",
wm->source_data.size);
weston_wm_flush_source_data(wm);
}
xcb_flush(wm->conn);
wl_event_source_remove(wm->property_source);
wm->data_source_fd = -1;
close(fd);
} else {
fprintf(stderr, "nothing happened, buffered the bytes\n");
}
return 1;
}
static void
weston_wm_send_data(struct weston_wm *wm, xcb_atom_t target, const char *mime_type)
{
struct wl_seat *seat = &wm->server->compositor->seat->seat;
int p[2];
if (pipe2(p, O_CLOEXEC | O_NONBLOCK) == -1) {
fprintf(stderr, "pipe2 failed: %m\n");
weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
return;
}
wl_array_init(&wm->source_data);
wm->selection_target = target;
wm->data_source_fd = p[0];
wm->property_source = wl_event_loop_add_fd(wm->server->loop,
wm->data_source_fd,
WL_EVENT_READABLE,
weston_wm_read_data_source,
wm);
wl_data_source_send_send(&seat->selection_data_source->resource,
mime_type, p[1]);
close(p[1]);
}
static void
weston_wm_send_incr_chunk(struct weston_wm *wm)
{
int length;
fprintf(stderr, "property deleted\n");
wm->selection_property_set = 0;
if (wm->flush_property_on_delete) {
fprintf(stderr, "setting new property, %zu bytes\n",
wm->source_data.size);
wm->flush_property_on_delete = 0;
length = weston_wm_flush_source_data(wm);
if (wm->data_source_fd >= 0) {
wm->property_source =
wl_event_loop_add_fd(wm->server->loop,
wm->data_source_fd,
WL_EVENT_READABLE,
weston_wm_read_data_source,
wm);
} else if (length > 0) {
/* Transfer is all done, but queue a flush for
* the delete of the last chunk so we can set
* the 0 sized propert to signal the end of
* the transfer. */
wm->flush_property_on_delete = 1;
wl_array_release(&wm->source_data);
} else {
wm->selection_request.requestor = XCB_NONE;
}
}
}
static int
weston_wm_handle_selection_property_notify(struct weston_wm *wm,
xcb_generic_event_t *event)
{
xcb_property_notify_event_t *property_notify =
(xcb_property_notify_event_t *) event;
if (property_notify->window == wm->selection_window) {
if (property_notify->state == XCB_PROPERTY_NEW_VALUE &&
property_notify->atom == wm->atom.wl_selection &&
wm->incr)
weston_wm_get_incr_chunk(wm);
return 1;
} else if (property_notify->window == wm->selection_request.requestor) {
if (property_notify->state == XCB_PROPERTY_DELETE &&
property_notify->atom == wm->selection_request.property &&
wm->incr)
weston_wm_send_incr_chunk(wm);
return 1;
}
return 0;
}
static void
weston_wm_handle_selection_request(struct weston_wm *wm,
xcb_generic_event_t *event)
{
xcb_selection_request_event_t *selection_request =
(xcb_selection_request_event_t *) event;
fprintf(stderr, "selection request, %s, ",
get_atom_name(wm->conn, selection_request->selection));
fprintf(stderr, "target %s, ",
get_atom_name(wm->conn, selection_request->target));
fprintf(stderr, "property %s\n",
get_atom_name(wm->conn, selection_request->property));
wm->selection_request = *selection_request;
wm->incr = 0;
wm->flush_property_on_delete = 0;
if (selection_request->target == wm->atom.targets) {
weston_wm_send_targets(wm);
} else if (selection_request->target == wm->atom.timestamp) {
weston_wm_send_timestamp(wm);
} else if (selection_request->target == wm->atom.utf8_string ||
selection_request->target == wm->atom.text) {
weston_wm_send_data(wm, wm->atom.utf8_string,
"text/plain;charset=utf-8");
} else {
fprintf(stderr, "can only handle UTF8_STRING targets...\n");
weston_wm_send_selection_notify(wm, XCB_ATOM_NONE);
}
}
static void
weston_wm_handle_xfixes_selection_notify(struct weston_wm *wm,
xcb_generic_event_t *event)
{
xcb_xfixes_selection_notify_event_t *xfixes_selection_notify =
(xcb_xfixes_selection_notify_event_t *) event;
printf("xfixes selection notify event: owner %d\n",
xfixes_selection_notify->owner);
/* We have to use XCB_TIME_CURRENT_TIME when we claim the
* selection, so grab the actual timestamp here so we can
* answer TIMESTAMP conversion requests correctly. */
if (xfixes_selection_notify->owner == wm->selection_window) {
wm->selection_timestamp = xfixes_selection_notify->timestamp;
fprintf(stderr, "our window, skipping\n");
return;
}
wm->incr = 0;
xcb_convert_selection(wm->conn, wm->selection_window,
wm->atom.clipboard,
wm->atom.targets,
wm->atom.wl_selection,
xfixes_selection_notify->timestamp);
xcb_flush(wm->conn);
}
int
weston_wm_handle_selection_event(struct weston_wm *wm,
xcb_generic_event_t *event)
{
switch (event->response_type & ~0x80) {
case XCB_SELECTION_NOTIFY:
weston_wm_handle_selection_notify(wm, event);
return 1;
case XCB_PROPERTY_NOTIFY:
return weston_wm_handle_selection_property_notify(wm, event);
case XCB_SELECTION_REQUEST:
weston_wm_handle_selection_request(wm, event);
return 1;
}
switch (event->response_type - wm->xfixes->first_event) {
case XCB_XFIXES_SELECTION_NOTIFY:
weston_wm_handle_xfixes_selection_notify(wm, event);
return 1;
}
return 0;
}
void
weston_wm_set_selection(struct wl_listener *listener, void *data)
{
struct wl_seat *seat = data;
struct weston_wm *wm =
container_of(listener, struct weston_wm, selection_listener);
struct wl_data_source *source = seat->selection_data_source;
const char **p, **end;
int has_text_plain = 0;
if (source->offer_interface == &data_offer_interface)
return;
fprintf(stderr, "set selection\n");
p = source->mime_types.data;
end = (const char **)
((char *) source->mime_types.data + source->mime_types.size);
while (p < end) {
fprintf(stderr, " %s\n", *p);
if (strcmp(*p, "text/plain") == 0 ||
strcmp(*p, "text/plain;charset=utf-8") == 0)
has_text_plain = 1;
p++;
}
if (has_text_plain) {
xcb_set_selection_owner(wm->conn,
wm->selection_window,
wm->atom.clipboard,
XCB_TIME_CURRENT_TIME);
} else {
xcb_set_selection_owner(wm->conn,
XCB_ATOM_NONE,
wm->atom.clipboard,
XCB_TIME_CURRENT_TIME);
}
}
......@@ -32,39 +32,13 @@
#include <unistd.h>
#include <signal.h>
#include <xcb/xcb.h>
#include <xcb/xfixes.h>
#include <cairo/cairo-xcb.h>
#include <wayland-server.h>
#include "xwayland.h"
#include "../../shared/cairo-util.h"
#include "../compositor.h"
#include "xserver-server-protocol.h"
#include "hash.h"
struct xserver {
struct wl_resource resource;
};
struct weston_xserver {
struct wl_display *wl_display;
struct wl_event_loop *loop;
struct wl_event_source *sigchld_source;
int abstract_fd;
struct wl_event_source *abstract_source;
int unix_fd;
struct wl_event_source *unix_source;
int display;
struct weston_process process;
struct wl_resource *resource;
struct wl_client *client;
struct weston_compositor *compositor;
struct weston_wm *wm;
struct wl_listener activate_listener;
struct wl_listener destroy_listener;
};
struct motif_wm_hints {
uint32_t flags;
uint32_t functions;
......@@ -114,75 +88,7 @@ struct motif_wm_hints {
#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */
#define _NET_WM_MOVERESIZE_CANCEL 11 /* cancel operation */
struct weston_wm {
xcb_connection_t *conn;
const xcb_query_extension_reply_t *xfixes;
struct wl_event_source *source;
xcb_screen_t *screen;
struct hash_table *window_hash;
struct weston_xserver *server;
xcb_window_t wm_window;
struct weston_wm_window *focus_window;
struct theme *theme;
xcb_render_pictforminfo_t render_format;
xcb_window_t selection_window;
int incr;
int data_source_fd;
struct wl_event_source *property_source;
xcb_get_property_reply_t *property_reply;
int property_start;
struct wl_array source_data;
xcb_selection_request_event_t selection_request;
xcb_atom_t selection_target;
xcb_timestamp_t selection_timestamp;
int selection_property_set;
int flush_property_on_delete;
struct wl_listener selection_listener;
struct {
xcb_atom_t wm_protocols;
xcb_atom_t wm_take_focus;
xcb_atom_t wm_delete_window;
xcb_atom_t net_wm_name;
xcb_atom_t net_wm_icon;
xcb_atom_t net_wm_state;
xcb_atom_t net_wm_state_fullscreen;
xcb_atom_t net_wm_user_time;
xcb_atom_t net_wm_icon_name;
xcb_atom_t net_wm_window_type;
xcb_atom_t net_wm_window_type_desktop;
xcb_atom_t net_wm_window_type_dock;
xcb_atom_t net_wm_window_type_toolbar;
xcb_atom_t net_wm_window_type_menu;
xcb_atom_t net_wm_window_type_utility;
xcb_atom_t net_wm_window_type_splash;
xcb_atom_t net_wm_window_type_dialog;
xcb_atom_t net_wm_window_type_dropdown;
xcb_atom_t net_wm_window_type_popup;
xcb_atom_t net_wm_window_type_tooltip;
xcb_atom_t net_wm_window_type_notification;
xcb_atom_t net_wm_window_type_combo;
xcb_atom_t net_wm_window_type_dnd;