x11: Bridge X11 CLIPBOARD selection to Wayland clients
authorKristian Høgsberg <krh@bitplanet.net>
Tue, 27 Dec 2011 18:50:04 +0000 (13:50 -0500)
committerKristian Høgsberg <krh@bitplanet.net>
Tue, 27 Dec 2011 18:50:04 +0000 (13:50 -0500)
clients/terminal.c
clients/window.c
clients/window.h
compositor/compositor.h
compositor/data-device.c
compositor/xserver-launcher.c
configure.ac

index 472fb2a..d51f107 100644 (file)
@@ -2053,14 +2053,6 @@ static const struct wl_data_source_listener data_source_listener = {
        data_source_cancelled
 };
 
-static void selection_receive_func(void *data, size_t len,
-                                  int32_t x, int32_t y, void *user_data)
-{
-       struct terminal *terminal = user_data;
-
-       write(terminal->master, data, len);
-}
-
 static int
 handle_bound_key(struct terminal *terminal,
                 struct input *input, uint32_t sym, uint32_t time)
@@ -2073,15 +2065,16 @@ handle_bound_key(struct terminal *terminal,
                terminal->selection =
                        display_create_data_source(terminal->display);
                wl_data_source_offer(terminal->selection,
-                                    "text/plain; charset=utf-8");
+                                    "text/plain;charset=utf-8");
                wl_data_source_add_listener(terminal->selection,
                                            &data_source_listener, terminal);
                input_set_selection(input, terminal->selection, time);
                return 1;
        case XK_V:
-               input_receive_selection_data(input,
-                                            "text/plain; charset=utf-8",
-                                            selection_receive_func, terminal);
+               input_receive_selection_data_to_fd(input,
+                                                  "text/plain;charset=utf-8",
+                                                  terminal->master);
+
                return 1;
        default:
                return 0;
index 5931f7b..b031daa 100644 (file)
@@ -1748,6 +1748,15 @@ input_receive_selection_data(struct input *input, const char *mime_type,
        return 0;
 }
 
+int
+input_receive_selection_data_to_fd(struct input *input,
+                                  const char *mime_type, int fd)
+{
+       wl_data_offer_receive(input->selection_offer->offer, mime_type, fd);
+
+       return 0;
+}
+
 void
 window_move(struct window *window, struct input *input, uint32_t time)
 {
index 91f3af1..75bafc1 100644 (file)
@@ -385,6 +385,9 @@ input_receive_drag_data(struct input *input, const char *mime_type,
 int
 input_receive_selection_data(struct input *input, const char *mime_type,
                             data_func_t func, void *data);
+int
+input_receive_selection_data_to_fd(struct input *input,
+                                  const char *mime_type, int fd);
 
 void
 output_set_user_data(struct output *output, void *data);
index 2b1e56d..359c888 100644 (file)
@@ -265,6 +265,26 @@ struct wlsc_surface {
        struct wl_listener buffer_destroy_listener;
 };
 
+struct wlsc_data_source {
+       struct wl_resource resource;
+       struct wl_array mime_types;
+       int refcount;
+       void *data;
+
+       struct wl_resource *(*create_offer)(struct wlsc_data_source *source, 
+                                           struct wl_resource *target);
+
+       void (*cancel)(struct wlsc_data_source *source);
+};
+
+void
+wlsc_data_source_unref(struct wlsc_data_source *source);
+
+void
+wlsc_input_device_set_selection(struct wlsc_input_device *device,
+                               struct wlsc_data_source *source,
+                               uint32_t time);
+
 void
 wlsc_spring_init(struct wlsc_spring *spring,
                 double k, double current, double target);
index c39465f..6410608 100644 (file)
 
 #include "compositor.h"
 
-struct wlsc_data_source {
-       struct wl_resource resource;
-       struct wl_array mime_types;
-       int refcount;
-};
-
-static void
+void
 wlsc_data_source_unref(struct wlsc_data_source *source)
 {
        source->refcount--;
-
-       if (source->refcount == 1 && source->resource.object.id != 0) {
-               wl_resource_post_event(&source->resource,
-                                      WL_DATA_SOURCE_CANCELLED);
-       } else if (source->refcount == 0) {
+       if (source->refcount == 0)
                free(source);
-       }
 }
 
 static void
@@ -93,17 +82,33 @@ static const struct wl_data_offer_interface data_offer_interface = {
 };
 
 static struct wl_resource *
-wlsc_data_source_send_offer(struct wlsc_data_source *source,
-                           struct wl_resource *target)
+data_source_create_offer(struct wlsc_data_source *source, 
+                        struct wl_resource *target)
 {
        struct wl_resource *resource;
-       char **p, **end;
 
        resource = wl_client_new_object(target->client,
                                        &wl_data_offer_interface,
                                        &data_offer_interface, source);
        resource->destroy = destroy_data_offer;
 
+       return resource;
+}
+
+static void
+data_source_cancel(struct wlsc_data_source *source)
+{
+       wl_resource_post_event(&source->resource, WL_DATA_SOURCE_CANCELLED);
+}
+
+static struct wl_resource *
+wlsc_data_source_send_offer(struct wlsc_data_source *source,
+                           struct wl_resource *target)
+{
+       struct wl_resource *resource;
+       char **p, **end;
+
+       resource = source->create_offer(source, target);
        source->refcount++;
 
        wl_resource_post_event(target, WL_DATA_DEVICE_DATA_OFFER, resource);
@@ -112,7 +117,6 @@ wlsc_data_source_send_offer(struct wlsc_data_source *source,
        for (p = source->mime_types.data; p < end; p++)
                wl_resource_post_event(resource, WL_DATA_OFFER_OFFER, *p);
 
-
        return resource;
 }
 
@@ -302,18 +306,14 @@ destroy_selection_data_source(struct wl_listener *listener,
        }
 }
 
-static void
-data_device_set_selection(struct wl_client *client,
-                         struct wl_resource *resource,
-                         struct wl_resource *source_resource, uint32_t time)
+void
+wlsc_input_device_set_selection(struct wlsc_input_device *device,
+                               struct wlsc_data_source *source, uint32_t time)
 {
-       struct wlsc_input_device *device = resource->data;
        struct wl_resource *data_device, *focus, *offer;
 
-       if (!source_resource)
-               return;
-
        if (device->selection_data_source) {
+               device->selection_data_source->cancel(device->selection_data_source);
                /* FIXME: All non-active clients will probably hold a
                 * reference to the selection data source, and thus it
                 * won't get destroyed until every client has been
@@ -323,25 +323,40 @@ data_device_set_selection(struct wl_client *client,
                device->selection_data_source = NULL;
        }
 
-       device->selection_data_source = source_resource->data;
-       device->selection_data_source->refcount++;
+       device->selection_data_source = source;
+       source->refcount++;
 
        focus = device->input_device.keyboard_focus_resource;
        if (focus) {
                data_device = find_resource(&device->drag_resource_list,
                                            focus->client);
-               offer = wlsc_data_source_send_offer(device->selection_data_source,
-                                                   data_device);
-               wl_resource_post_event(data_device,
-                                      WL_DATA_DEVICE_SELECTION, offer);
+               if (data_device) {
+                       offer = wlsc_data_source_send_offer(device->selection_data_source,
+                                                           data_device);
+                       wl_resource_post_event(data_device,
+                                              WL_DATA_DEVICE_SELECTION,
+                                              offer);
+               }
        }
 
        device->selection_data_source_listener.func =
                destroy_selection_data_source;
-       wl_list_insert(source_resource->destroy_listener_list.prev,
+       wl_list_insert(source->resource.destroy_listener_list.prev,
                       &device->selection_data_source_listener.link);
 }
 
+static void
+data_device_set_selection(struct wl_client *client,
+                         struct wl_resource *resource,
+                         struct wl_resource *source_resource, uint32_t time)
+{
+       if (!source_resource)
+               return;
+
+       wlsc_input_device_set_selection(resource->data,
+                                       source_resource->data, time);
+}
+
 static const struct wl_data_device_interface data_device_interface = {
        data_device_start_drag,
        data_device_attach,
@@ -383,6 +398,8 @@ create_data_source(struct wl_client *client,
        source->resource.object.implementation =
                (void (**)(void)) &data_source_interface;
        source->resource.data = source;
+       source->create_offer = data_source_create_offer;
+       source->cancel = data_source_cancel;
        source->refcount = 1;
 
        wl_array_init(&source->mime_types);
index fe4caea..c8c1c97 100644 (file)
@@ -33,6 +33,7 @@
 #include <signal.h>
 
 #include <xcb/xcb.h>
+#include <xcb/xfixes.h>
 
 #include <wayland-server.h>
 
@@ -64,14 +65,25 @@ struct wlsc_xserver {
        struct wlsc_process process;
        struct wl_resource *resource;
        struct wl_client *client;
+       struct wlsc_compositor *compositor;
        struct wlsc_wm *wm;
 };
 
 struct wlsc_wm {
        xcb_connection_t *conn;
+       const xcb_query_extension_reply_t *xfixes;
        struct wl_event_source *source;
        xcb_screen_t *screen;
        struct wl_hash_table *window_hash;
+       struct wlsc_xserver *server;
+
+       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 {
                xcb_atom_t               wm_protocols;
                xcb_atom_t               wm_take_focus;
@@ -82,7 +94,18 @@ struct wlsc_wm {
                xcb_atom_t               net_wm_state_fullscreen;
                xcb_atom_t               net_wm_user_time;
                xcb_atom_t               net_wm_icon_name;
+               xcb_atom_t               clipboard;
+               xcb_atom_t               targets;
                xcb_atom_t               utf8_string;
+               xcb_atom_t               wl_selection;
+               xcb_atom_t               incr;
+               xcb_atom_t               timestamp;
+               xcb_atom_t               multiple;
+               xcb_atom_t               compound_text;
+               xcb_atom_t               text;
+               xcb_atom_t               string;
+               xcb_atom_t               text_plain_utf8;
+               xcb_atom_t               text_plain;
        } atom;
 };
 
@@ -95,6 +118,313 @@ struct wlsc_wm_window {
 static struct wlsc_wm_window *
 get_wm_window(struct wlsc_surface *surface);
 
+static const char *
+get_atom_name(xcb_connection_t *c, xcb_atom_t atom)
+{
+       xcb_get_atom_name_cookie_t cookie;
+       xcb_get_atom_name_reply_t *reply;
+       xcb_generic_error_t *e;
+       static char buffer[64];
+
+       if (atom == XCB_ATOM_NONE)
+               return "None";
+
+       cookie = xcb_get_atom_name (c, atom);
+       reply = xcb_get_atom_name_reply (c, cookie, &e);
+       snprintf(buffer, sizeof buffer, "%.*s",
+                xcb_get_atom_name_name_length (reply),
+                xcb_get_atom_name_name (reply));
+       free(reply);
+
+       return buffer;
+}
+
+static void
+dump_property(struct wlsc_wm *wm, xcb_atom_t property,
+             xcb_get_property_reply_t *reply)
+{
+       int32_t *incr_value;
+       const char *text_value, *name;
+       xcb_atom_t *atom_value;
+       int i, width;
+
+       width = fprintf(stderr, "property %s: ",
+                       get_atom_name(wm->conn, property));
+       width += fprintf(stderr,
+                        "type %s, format %d, length %d (value_len %d): ",
+                        get_atom_name(wm->conn, reply->type),
+                        reply->format,
+                        xcb_get_property_value_length(reply),
+                        reply->value_len);
+
+       if (reply->type == wm->atom.incr) {
+               incr_value = xcb_get_property_value(reply);
+               fprintf(stderr, "%d\n", *incr_value);
+       } if (reply->type == wm->atom.utf8_string ||
+             reply->type == wm->atom.string) {
+               text_value = xcb_get_property_value(reply);
+               fprintf(stderr, "\"%.100s\"\n", text_value);
+       } if (reply->type == XCB_ATOM_ATOM) {
+               atom_value = xcb_get_property_value(reply);
+               for (i = 0; i < reply->value_len; i++) {
+                       name = get_atom_name(wm->conn, atom_value[i]);
+                       if (width + strlen(name) + 2 > 78) {
+                               fprintf(stderr, "\n  ");
+                               width = 2;
+                       } else if (i > 0) {
+                               width += fprintf(stderr, ", ");
+                       }
+
+                       width += fprintf(stderr, "%s", name);
+               }
+               fprintf(stderr, "\n");
+       } else {
+               fprintf(stderr, "huh?\n");
+       }
+}
+
+static void
+data_offer_accept(struct wl_client *client, struct wl_resource *resource,
+                 uint32_t time, const char *mime_type)
+{
+       struct wlsc_data_source *source = resource->data;
+
+       wl_resource_post_event(&source->resource,
+                              WL_DATA_SOURCE_TARGET, mime_type);
+}
+
+static void
+data_offer_receive(struct wl_client *client, struct wl_resource *resource,
+                  const char *mime_type, int32_t fd)
+{
+       struct wlsc_data_source *source = resource->data;
+       struct wlsc_wm *wm = source->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, wlsc_compositor_get_time());
+}
+
+static void
+destroy_data_offer(struct wl_resource *resource)
+{
+       struct wlsc_data_source *source = resource->data;
+
+       wlsc_data_source_unref(source);
+       free(resource);
+}
+
+static const struct wl_data_offer_interface data_offer_interface = {
+       data_offer_accept,
+       data_offer_receive,
+       data_offer_destroy,
+};
+
+static struct wl_resource *
+data_source_create_offer(struct wlsc_data_source *source, 
+                        struct wl_resource *target)
+{
+       struct wl_resource *resource;
+
+       resource = wl_client_new_object(target->client,
+                                       &wl_data_offer_interface,
+                                       &data_offer_interface, source);
+       resource->destroy = destroy_data_offer;
+
+       return resource;
+}
+
+static void
+data_source_cancel(struct wlsc_data_source *source)
+{
+}
+
+static void
+wlsc_wm_get_selection_targets(struct wlsc_wm *wm)
+{
+       struct wlsc_data_source *source;
+       struct wlsc_input_device *device;
+       xcb_get_property_cookie_t cookie;
+       xcb_get_property_reply_t *reply;
+       xcb_atom_t *value;
+       char **p;
+       int 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_list_init(&source->resource.destroy_listener_list);
+       source->create_offer = data_source_create_offer;
+       source->cancel = data_source_cancel;
+       source->data = wm;
+       source->refcount = 1;
+
+       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");
+               }
+       }
+
+       device = (struct wlsc_input_device *)
+               wm->server->compositor->input_device;
+       wlsc_input_device_set_selection(device, source,
+                                       wlsc_compositor_get_time());
+
+       wlsc_data_source_unref(source);
+       free(reply);
+}
+
+static int
+wlsc_wm_write_property(int fd, uint32_t mask, void *data)
+{
+       struct wlsc_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
+wlsc_wm_get_selection_data(struct wlsc_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_WRITEABLE,
+                                            wlsc_wm_write_property,
+                                            wm);
+               wm->property_reply = reply;
+       }
+}
+
+static void
+wlsc_wm_get_incr_chunk(struct wlsc_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_WRITEABLE,
+                                            wlsc_wm_write_property,
+                                            wm);
+               wm->property_reply = reply;
+       } else {
+               fprintf(stderr, "transfer complete\n");
+               close(wm->data_source_fd);
+               free(reply);
+       }
+}
+
 static void
 wlsc_wm_handle_configure_request(struct wlsc_wm *wm, xcb_generic_event_t *event)
 {
@@ -125,30 +455,11 @@ wlsc_wm_handle_configure_request(struct wlsc_wm *wm, xcb_generic_event_t *event)
                             configure_request->value_mask, values);
 }
 
-static const char *
-get_atom_name(xcb_connection_t *c, xcb_atom_t atom)
-{
-       xcb_get_atom_name_cookie_t cookie;
-       xcb_get_atom_name_reply_t *reply;
-       xcb_generic_error_t *e;
-       static char buffer[64];
-
-       cookie = xcb_get_atom_name (c, atom);
-       reply = xcb_get_atom_name_reply (c, cookie, &e);
-       snprintf(buffer, sizeof buffer, "%.*s",
-                xcb_get_atom_name_name_length (reply),
-                xcb_get_atom_name_name (reply));
-       free(reply);
-
-       return buffer;
-}
-
 static void
 wlsc_wm_get_properties(struct wlsc_wm *wm, xcb_window_t window)
 {
        xcb_generic_error_t *e;
        xcb_get_property_reply_t *reply;
-       void *value;
        int i;
 
        struct {
@@ -172,16 +483,7 @@ wlsc_wm_get_properties(struct wlsc_wm *wm, xcb_window_t window)
 
        for (i = 0; i < ARRAY_LENGTH(props); i++)  {
                reply = xcb_get_property_reply(wm->conn, props[i].cookie, &e);
-               value = xcb_get_property_value(reply);
-
-               fprintf(stderr, "property %s, type %d, format %d, "
-                       "length %d (value_len %d), value \"%.*s\"\n",
-                       get_atom_name(wm->conn, props[i].atom),
-                       reply->type, reply->format,
-                       xcb_get_property_value_length(reply),
-                       reply->value_len, reply->value_len,
-                       reply->type ? (char *) value : "(nil)");
-
+               dump_property(wm, props[i].atom, reply);
                free(reply);
        }
 }
@@ -260,7 +562,12 @@ wlsc_wm_handle_property_notify(struct wlsc_wm *wm, xcb_generic_event_t *event)
        xcb_property_notify_event_t *property_notify =
                (xcb_property_notify_event_t *) event;
 
-       if (property_notify->atom == XCB_ATOM_WM_CLASS) {
+       if (property_notify->window == wm->selection_window) {
+               if (property_notify->state == XCB_PROPERTY_NEW_VALUE &&
+                   property_notify->atom == wm->atom.wl_selection &&
+                   wm->incr)
+                       wlsc_wm_get_incr_chunk(wm);
+       } else if (property_notify->atom == XCB_ATOM_WM_CLASS) {
                fprintf(stderr, "wm_class changed\n");
        } else if (property_notify->atom == XCB_ATOM_WM_TRANSIENT_FOR) {
                fprintf(stderr, "wm_transient_for changed\n");
@@ -326,6 +633,41 @@ wlsc_wm_handle_destroy_notify(struct wlsc_wm *wm, xcb_generic_event_t *event)
        free(window);
 }
 
+static void
+wlsc_wm_handle_selection_notify(struct wlsc_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) {
+               wlsc_wm_get_selection_targets(wm);
+       } else {
+               wlsc_wm_get_selection_data(wm);
+       }
+}
+
+static void
+wlsc_wm_handle_xfixes_selection_notify(struct wlsc_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);
+
+       xcb_convert_selection(wm->conn, wm->selection_window,
+                             wm->atom.clipboard,
+                             wm->atom.targets,
+                             wm->atom.wl_selection,
+                             XCB_TIME_CURRENT_TIME);
+
+       xcb_flush(wm->conn);
+}
+
 static int
 wlsc_wm_handle_event(int fd, uint32_t mask, void *data)
 {
@@ -334,7 +676,7 @@ wlsc_wm_handle_event(int fd, uint32_t mask, void *data)
        int count = 0;
 
        while (event = xcb_poll_for_event(wm->conn), event != NULL) {
-               switch (event->response_type) {
+               switch (event->response_type & ~0x80) {
                case XCB_CREATE_NOTIFY:
                        wlsc_wm_handle_create_notify(wm, event);
                        break;
@@ -362,11 +704,18 @@ wlsc_wm_handle_event(int fd, uint32_t mask, void *data)
                case XCB_PROPERTY_NOTIFY:
                        wlsc_wm_handle_property_notify(wm, event);
                        break;
-               default:
-                       fprintf(stderr, "Unhandled event %d\n",
-                               event->response_type);
+               case XCB_SELECTION_NOTIFY:
+                       wlsc_wm_handle_selection_notify(wm, event);
+                       break;
+               }
+
+               switch (event->response_type - wm->xfixes->first_event) {
+               case XCB_XFIXES_SELECTION_NOTIFY:
+                       wlsc_wm_handle_xfixes_selection_notify(wm, event);
                        break;
                }
+
+
                free(event);
                count++;
        }
@@ -392,13 +741,29 @@ wxs_wm_get_resources(struct wlsc_wm *wm)
                { "_NET_WM_STATE_FULLSCREEN", F(atom.net_wm_state_fullscreen) },
                { "_NET_WM_USER_TIME", F(atom.net_wm_user_time) },
                { "_NET_WM_ICON_NAME", F(atom.net_wm_icon_name) },
+               { "CLIPBOARD",          F(atom.clipboard) },
+               { "TARGETS",            F(atom.targets) },
                { "UTF8_STRING",        F(atom.utf8_string) },
+               { "_WL_SELECTION",      F(atom.wl_selection) },
+               { "INCR",               F(atom.incr) },
+               { "TIMESTAMP",          F(atom.timestamp) },
+               { "MULTIPLE",           F(atom.multiple) },
+               { "UTF8_STRING" ,       F(atom.utf8_string) },
+               { "COMPOUND_TEXT",      F(atom.compound_text) },
+               { "TEXT",               F(atom.text) },
+               { "STRING",             F(atom.string) },
+               { "text/plain;charset=utf-8",   F(atom.text_plain_utf8) },
+               { "text/plain",         F(atom.text_plain) },
        };
 
+       xcb_xfixes_query_version_cookie_t xfixes_cookie;
+       xcb_xfixes_query_version_reply_t *xfixes_reply;
        xcb_intern_atom_cookie_t cookies[ARRAY_LENGTH(atoms)];
        xcb_intern_atom_reply_t *reply;
        int i;
 
+       xcb_prefetch_extension_data (wm->conn, &xcb_xfixes_id);
+
        for (i = 0; i < ARRAY_LENGTH(atoms); i++)
                cookies[i] = xcb_intern_atom (wm->conn, 0,
                                              strlen(atoms[i].name),
@@ -409,6 +774,21 @@ wxs_wm_get_resources(struct wlsc_wm *wm)
                *(xcb_atom_t *) ((char *) wm + atoms[i].offset) = reply->atom;
                free(reply);
        }
+
+       wm->xfixes = xcb_get_extension_data(wm->conn, &xcb_xfixes_id);
+       if (!wm->xfixes || !wm->xfixes->present)
+               fprintf(stderr, "xfixes not available\n");
+
+       xfixes_cookie = xcb_xfixes_query_version(wm->conn,
+                                                XCB_XFIXES_MAJOR_VERSION,
+                                                XCB_XFIXES_MINOR_VERSION);
+       xfixes_reply = xcb_xfixes_query_version_reply(wm->conn,
+                                                     xfixes_cookie, NULL);
+
+       printf("xfixes version: %d.%d\n",
+              xfixes_reply->major_version, xfixes_reply->minor_version);
+
+       free(xfixes_reply);
 }
 
 static struct wlsc_wm *
@@ -417,13 +797,14 @@ wlsc_wm_create(struct wlsc_xserver *wxs)
        struct wlsc_wm *wm;
        struct wl_event_loop *loop;
        xcb_screen_iterator_t s;
-       uint32_t values[1];
+       uint32_t values[1], mask;
        int sv[2];
 
        wm = malloc(sizeof *wm);
        if (wm == NULL)
                return NULL;
 
+       wm->server = wxs;
        wm->window_hash = wl_hash_table_create();
        if (wm->window_hash == NULL) {
                free(wm);
@@ -472,6 +853,26 @@ wlsc_wm_create(struct wlsc_xserver *wxs)
        xcb_change_window_attributes(wm->conn, wm->screen->root,
                                     XCB_CW_EVENT_MASK, values);
 
+       wm->selection_window = xcb_generate_id(wm->conn);
+       xcb_create_window(wm->conn,
+                         XCB_COPY_FROM_PARENT,
+                         wm->selection_window,
+                         wm->screen->root,
+                         0, 0,
+                         10, 10,
+                         0,
+                         XCB_WINDOW_CLASS_INPUT_OUTPUT,
+                         wm->screen->root_visual,
+                         XCB_CW_EVENT_MASK, values);
+
+       mask =
+               XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
+               XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
+               XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE;
+
+       xcb_xfixes_select_selection_input(wm->conn, wm->selection_window,
+                                         wm->atom.clipboard, mask);
+
        xcb_flush(wm->conn);
        fprintf(stderr, "created wm\n");
 
@@ -558,10 +959,14 @@ wlsc_xserver_shutdown(struct wlsc_xserver *wxs)
        unlink(path);
        snprintf(path, sizeof path, "/tmp/.X11-unix/X%d", wxs->display);
        unlink(path);
+       if (wxs->process.pid == 0) {
+               wl_event_source_remove(wxs->abstract_source);
+               wl_event_source_remove(wxs->unix_source);
+       }
        close(wxs->abstract_fd);
-       wl_event_source_remove(wxs->abstract_source);
        close(wxs->unix_fd);
-       wl_event_source_remove(wxs->unix_source);
+       if (wxs->wm)
+               wlsc_wm_destroy(wxs->wm);
        wxs->loop = NULL;
 }
 
@@ -813,6 +1218,7 @@ wlsc_xserver_init(struct wlsc_compositor *compositor)
 
        mxs->process.cleanup = wlsc_xserver_cleanup;
        mxs->wl_display = display;
+       mxs->compositor = compositor;
 
        mxs->display = 0;
 
index 428cfaa..0d0d749 100644 (file)
@@ -35,7 +35,7 @@ AC_ARG_ENABLE(xserver-launcher, [  --enable-xserver-launcher],,
              enable_xserver_launcher=yes)
 AM_CONDITIONAL(ENABLE_XSERVER_LAUNCHER, test x$enable_xserver_launcher == xyes)
 if test x$enable_xserver_launcher == xyes; then
-  PKG_CHECK_MODULES([XSERVER_LAUNCHER], xcb)
+  PKG_CHECK_MODULES([XSERVER_LAUNCHER], xcb xcb-xfixes)
   AC_DEFINE([BUILD_XSERVER_LAUNCHER], [1], [Build the X server launcher])
 
   AC_ARG_WITH(xserver-path, AS_HELP_STRING([--with-xserver-path=PATH],