commit 28dcc02

Michael Forney  ·  2013-11-26 02:54:43 +0000 UTC
parent 4ab4400
Initial xwayland support
9 files changed,  +777, -2
+1, -0
1@@ -14,4 +14,5 @@ WAYLAND_SCANNER ?= wayland-scanner
2 
3 ENABLE_STATIC   = 1
4 ENABLE_SHARED   = 1
5+ENABLE_XWAYLAND = 1
6 
+14, -0
 1@@ -61,12 +61,26 @@ SWC_SOURCES +=                      \
 2     libswc/shell_surface.c          \
 3     libswc/bindings.c
 4 
 5+ifeq ($(ENABLE_XWAYLAND),1)
 6+$(dir)_CFLAGS += -DENABLE_XWAYLAND
 7+$(dir)_PACKAGES +=                  \
 8+    xcb                             \
 9+    xcb-composite                   \
10+    xcb-ewmh
11+
12+SWC_SOURCES +=                      \
13+    libswc/xserver.c                \
14+    libswc/xwm.c                    \
15+    protocol/xserver-protocol.c
16+endif
17+
18 SWC_STATIC_OBJECTS = $(SWC_SOURCES:%.c=%.o)
19 SWC_SHARED_OBJECTS = $(SWC_SOURCES:%.c=%.lo)
20 
21 # Explicitly state dependencies on generated files
22 objects = $(foreach obj,$(1),$(dir)/$(obj).o $(dir)/$(obj.lo))
23 $(call objects,drm drm_buffer): protocol/wayland-drm-server-protocol.h
24+$(call objects,xserver): protocol/xserver-server-protocol.h
25 
26 $(dir)/libswc.a: $(SWC_STATIC_OBJECTS)
27 	$(call quiet,AR) cru $@ $^
+16, -0
 1@@ -30,6 +30,9 @@
 2 #include "seat.h"
 3 #include "shell.h"
 4 #include "window.h"
 5+#ifdef ENABLE_XWAYLAND
 6+# include "xserver.h"
 7+#endif
 8 
 9 #include <libudev.h>
10 
11@@ -94,10 +97,20 @@ bool swc_initialize(struct wl_display * display,
12         goto error4;
13     }
14 
15+#ifdef ENABLE_XWAYLAND
16+    if (!swc_xserver_initialize())
17+    {
18+        fprintf(stderr, "Could not initialize xwayland\n");
19+        goto error5;
20+    }
21+#endif
22+
23     setup_compositor();
24 
25     return true;
26 
27+  error5:
28+    swc_shell_finalize();
29   error4:
30     swc_compositor_finish(&compositor);
31   error3:
32@@ -113,6 +126,9 @@ bool swc_initialize(struct wl_display * display,
33 EXPORT
34 void swc_finalize()
35 {
36+#ifdef ENABLE_XWAYLAND
37+    swc_xserver_finalize();
38+#endif
39     swc_shell_finalize();
40     swc_compositor_finish(&compositor);
41     swc_bindings_finalize();
+293, -0
  1@@ -0,0 +1,293 @@
  2+/* swc: libswc/xserver.c
  3+ *
  4+ * Copyright (c) 2013 Michael Forney
  5+ *
  6+ * Based in part upon xwayland/launcher.c from weston, which is
  7+ *
  8+ *     Copyright © 2011 Intel Corporation
  9+ *
 10+ * Permission is hereby granted, free of charge, to any person obtaining a copy
 11+ * of this software and associated documentation files (the "Software"), to deal
 12+ * in the Software without restriction, including without limitation the rights
 13+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 14+ * copies of the Software, and to permit persons to whom the Software is
 15+ * furnished to do so, subject to the following conditions:
 16+ *
 17+ * The above copyright notice and this permission notice shall be included in
 18+ * all copies or substantial portions of the Software.
 19+ *
 20+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 21+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 22+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 23+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 24+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 25+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 26+ * SOFTWARE.
 27+ */
 28+
 29+#include "xserver.h"
 30+#include "internal.h"
 31+#include "util.h"
 32+#include "xwm.h"
 33+
 34+#include <stdlib.h>
 35+#include <stdio.h>
 36+#include <unistd.h>
 37+#include <fcntl.h>
 38+#include <errno.h>
 39+#include <sys/stat.h>
 40+#include <sys/socket.h>
 41+#include <sys/un.h>
 42+#include <wayland-server.h>
 43+#include "protocol/xserver-server-protocol.h"
 44+
 45+#define LOCK_FMT    "/tmp/.X%d-lock"
 46+#define SOCKET_DIR  "/tmp/.X11-unix"
 47+#define SOCKET_FMT  SOCKET_DIR "/X%d"
 48+
 49+static struct
 50+{
 51+    struct wl_global * global;
 52+    struct wl_client * client;
 53+    struct wl_resource * resource;
 54+    int display;
 55+    char display_name[16];
 56+    int abstract_socket, unix_socket;
 57+} xserver;
 58+
 59+static char * xserver_command[] = {
 60+    "X", "-wayland", "-rootless", "-nolisten", "all", xserver.display_name, NULL
 61+};
 62+
 63+static void set_window_id(struct wl_client * client,
 64+                          struct wl_resource * resource,
 65+                          struct wl_resource * surface_resource, uint32_t id)
 66+{
 67+    struct swc_surface * surface = wl_resource_get_user_data(surface_resource);
 68+
 69+    swc_xwm_manage_window(id, surface);
 70+}
 71+
 72+const static struct xserver_interface xserver_implementation = {
 73+    .set_window_id = &set_window_id
 74+};
 75+
 76+static int open_socket(struct sockaddr_un * addr, size_t path_size)
 77+{
 78+    int fd;
 79+    socklen_t size = OFFSET_OF(typeof(*addr), sun_path) + path_size + 1;
 80+
 81+    if ((fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0)) < 0)
 82+        goto error0;
 83+
 84+    if (bind(fd, (struct sockaddr *) addr, size) < 0)
 85+        goto error1;
 86+
 87+    if (listen(fd, 1) < 0)
 88+        goto error2;
 89+
 90+    return fd;
 91+
 92+  error2:
 93+    if (addr->sun_path[0])
 94+        unlink(addr->sun_path);
 95+  error1:
 96+    close(fd);
 97+  error0:
 98+    return -1;
 99+}
100+
101+static bool open_display()
102+{
103+    char lock_name[64], pid[12];
104+    int lock_fd;
105+    struct sockaddr_un addr = { .sun_family = AF_LOCAL };
106+    size_t path_size;
107+
108+    xserver.display = 0;
109+
110+    /* Create X lockfile and server sockets */
111+    goto begin;
112+
113+  retry2:
114+    close(xserver.abstract_socket);
115+  retry1:
116+    unlink(lock_name);
117+  retry0:
118+    if (++xserver.display > 32)
119+    {
120+        ERROR("No open display in first 32\n");
121+        return false;
122+    }
123+
124+  begin:
125+    snprintf(lock_name, sizeof lock_name, LOCK_FMT, xserver.display);
126+    lock_fd = open(lock_name, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0444);
127+
128+    /* XXX: Stale lockfile handling? */
129+    if (lock_fd < 0)
130+        goto retry0;
131+
132+    snprintf(pid, sizeof pid, "%10d\n", getpid());
133+    if (write(lock_fd, pid, sizeof pid) != sizeof pid)
134+    {
135+        ERROR("Failed to write PID file\n");
136+        unlink(lock_name);
137+        close(lock_fd);
138+        return false;
139+    }
140+
141+    close(lock_fd);
142+
143+    /* Bind to abstract socket */
144+    addr.sun_path[0] = '\0';
145+    path_size = snprintf(addr.sun_path + 1, sizeof addr.sun_path - 1,
146+                         SOCKET_FMT, xserver.display);
147+    if ((xserver.abstract_socket = open_socket(&addr, path_size)) < 0)
148+        goto retry1;
149+
150+    /* Bind to unix socket */
151+    mkdir(SOCKET_DIR, 0777);
152+    path_size = snprintf(addr.sun_path, sizeof addr.sun_path,
153+                         SOCKET_FMT, xserver.display);
154+    if ((xserver.unix_socket = open_socket(&addr, path_size)) < 0)
155+        goto retry2;
156+
157+    snprintf(xserver.display_name, sizeof xserver.display_name,
158+             ":%d", xserver.display);
159+    setenv("DISPLAY", xserver.display_name, true);
160+
161+    return true;
162+}
163+
164+static void close_display()
165+{
166+    char path[64];
167+
168+    close(xserver.abstract_socket);
169+    close(xserver.unix_socket);
170+
171+    snprintf(path, sizeof path, SOCKET_FMT, xserver.display);
172+    unlink(path);
173+    snprintf(path, sizeof path, LOCK_FMT, xserver.display);
174+    unlink(path);
175+
176+    unsetenv("DISPLAY");
177+}
178+
179+static void bind_xserver(struct wl_client * client, void * data,
180+                         uint32_t version, uint32_t id)
181+{
182+    int sv[2];
183+
184+    if (client != xserver.client)
185+        return;
186+
187+    if (version >= 1)
188+        version = 1;
189+
190+    DEBUG("Binding X server\n");
191+
192+    xserver.resource = wl_resource_create(client, &xserver_interface,
193+                                          version, id);
194+    wl_resource_set_implementation(xserver.resource, &xserver_implementation,
195+                                   NULL, NULL);
196+
197+    /* Start the X window manager */
198+    socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
199+    xserver_send_client(xserver.resource, sv[1]);
200+    close(sv[1]);
201+
202+    /* Need to flush the xserver client so the X window manager can connect to
203+     * it's socket. */
204+    wl_client_flush(xserver.client);
205+    swc_xwm_initialize(sv[0]);
206+
207+    xserver_send_listen_socket(xserver.resource, xserver.abstract_socket);
208+    xserver_send_listen_socket(xserver.resource, xserver.unix_socket);
209+}
210+
211+static bool start_xserver()
212+{
213+    int sv[2];
214+
215+    /* Open an X display */
216+    if (!open_display())
217+    {
218+        ERROR("Failed to get X lockfile and sockets\n");
219+        goto error0;
220+    }
221+
222+    /* Start the X server */
223+    if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) == -1)
224+    {
225+        ERROR("Failed to create socketpair: %s\n", strerror(errno));
226+        goto error1;
227+    }
228+
229+    if (!(xserver.client = wl_client_create(swc.display, sv[0])))
230+        goto error2;
231+
232+    switch (fork())
233+    {
234+        case 0:
235+        {
236+            int socket_fd;
237+            char socket_string[32];
238+
239+            if (!(socket_fd = dup(sv[1])))
240+                exit(EXIT_FAILURE);
241+
242+            snprintf(socket_string, sizeof socket_string, "%d", socket_fd);
243+            setenv("WAYLAND_SOCKET", socket_string, true);
244+
245+            execvp(xserver_command[0], xserver_command);
246+            exit(EXIT_FAILURE);
247+
248+            break;
249+        }
250+        case -1:
251+            ERROR("fork() failed when trying to start X server: %s\n",
252+                  strerror(errno));
253+            goto error2;
254+    }
255+
256+    close(sv[1]);
257+
258+    return true;
259+
260+  error2:
261+    close(sv[1]);
262+    close(sv[0]);
263+  error1:
264+    close_display();
265+  error0:
266+    return false;
267+}
268+
269+bool swc_xserver_initialize()
270+{
271+    xserver.global = wl_global_create(swc.display, &xserver_interface, 1,
272+                                      NULL, &bind_xserver);
273+
274+    if (!xserver.global)
275+        goto error0;
276+
277+    if (!start_xserver())
278+        goto error1;
279+
280+    return true;
281+
282+  error1:
283+    wl_global_destroy(xserver.global);
284+  error0:
285+    return false;
286+}
287+
288+void swc_xserver_finalize()
289+{
290+    swc_xwm_finalize();
291+    close_display();
292+    wl_global_destroy(xserver.global);
293+}
294+
+33, -0
 1@@ -0,0 +1,33 @@
 2+/* swc: libswc/xserver.h
 3+ *
 4+ * Copyright (c) 2013 Michael Forney
 5+ *
 6+ * Permission is hereby granted, free of charge, to any person obtaining a copy
 7+ * of this software and associated documentation files (the "Software"), to deal
 8+ * in the Software without restriction, including without limitation the rights
 9+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+ * copies of the Software, and to permit persons to whom the Software is
11+ * furnished to do so, subject to the following conditions:
12+ *
13+ * The above copyright notice and this permission notice shall be included in
14+ * all copies or substantial portions of the Software.
15+ *
16+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+ * SOFTWARE.
23+ */
24+
25+#ifndef SWC_XSERVER_H
26+#define SWC_XSERVER_H
27+
28+#include <stdbool.h>
29+
30+bool swc_xserver_initialize();
31+void swc_xserver_finalize();
32+
33+#endif
34+
+361, -0
  1@@ -0,0 +1,361 @@
  2+/* swc: libswc/xwm.c
  3+ *
  4+ * Copyright (c) 2013 Michael Forney
  5+ *
  6+ * Permission is hereby granted, free of charge, to any person obtaining a copy
  7+ * of this software and associated documentation files (the "Software"), to deal
  8+ * in the Software without restriction, including without limitation the rights
  9+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10+ * copies of the Software, and to permit persons to whom the Software is
 11+ * furnished to do so, subject to the following conditions:
 12+ *
 13+ * The above copyright notice and this permission notice shall be included in
 14+ * all copies or substantial portions of the Software.
 15+ *
 16+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 22+ * SOFTWARE.
 23+ */
 24+
 25+#include "xwm.h"
 26+#include "compositor_surface.h"
 27+#include "internal.h"
 28+#include "surface.h"
 29+#include "swc.h"
 30+#include "util.h"
 31+#include "window.h"
 32+
 33+#include <stdio.h>
 34+#include <xcb/composite.h>
 35+#include <xcb/xcb_ewmh.h>
 36+
 37+struct xwl_window
 38+{
 39+    xcb_window_t id;
 40+    struct swc_window_internal window;
 41+    struct wl_listener surface_destroy_listener;
 42+};
 43+
 44+struct xwl_window_entry
 45+{
 46+    xcb_window_t id;
 47+    bool override_redirect;
 48+    struct xwl_window * xwl_window;
 49+};
 50+
 51+static struct
 52+{
 53+    xcb_connection_t * connection;
 54+    xcb_ewmh_connection_t ewmh;
 55+    xcb_screen_t * screen;
 56+    struct wl_event_source * source;
 57+    struct wl_array windows;
 58+} xwm;
 59+
 60+static void update_name(struct xwl_window * xwl_window)
 61+{
 62+    xcb_get_property_cookie_t wm_name_cookie;
 63+    xcb_ewmh_get_utf8_strings_reply_t wm_name_reply;
 64+
 65+    wm_name_cookie = xcb_ewmh_get_wm_name(&xwm.ewmh, xwl_window->id);
 66+    xcb_ewmh_get_wm_name_reply(&xwm.ewmh, wm_name_cookie,
 67+                               &wm_name_reply, NULL);
 68+
 69+    swc_window_set_title(&xwl_window->window.base,
 70+                         wm_name_reply.strings, wm_name_reply.strings_len);
 71+}
 72+
 73+static struct xwl_window_entry * find_window(xcb_window_t id)
 74+{
 75+    struct xwl_window_entry * entry;
 76+
 77+    wl_array_for_each(entry, &xwm.windows)
 78+    {
 79+        if (entry->id == id)
 80+            return entry;
 81+    }
 82+
 83+    return NULL;
 84+}
 85+
 86+/* X event handlers */
 87+void create_notify(xcb_create_notify_event_t * event)
 88+{
 89+    struct xwl_window_entry * entry;
 90+
 91+    if (!(entry = wl_array_add(&xwm.windows, sizeof *entry)))
 92+        return;
 93+
 94+    entry->id = event->window;
 95+    entry->override_redirect = event->override_redirect;
 96+    entry->xwl_window = NULL;
 97+}
 98+
 99+void destroy_notify(xcb_destroy_notify_event_t * event)
100+{
101+    struct xwl_window_entry * entry;
102+
103+    if (!(entry = find_window(event->window)))
104+        return;
105+
106+    swc_array_remove(&xwm.windows, entry, sizeof *entry);
107+}
108+
109+void map_request(xcb_map_request_event_t * event)
110+{
111+    xcb_map_window(xwm.connection, event->window);
112+}
113+
114+void configure_request(xcb_configure_request_event_t * event)
115+{
116+}
117+
118+void property_notify(xcb_property_notify_event_t * event)
119+{
120+    struct xwl_window_entry * entry;
121+
122+    if (!(entry = find_window(event->window)) || !entry->xwl_window)
123+        return;
124+
125+    if (event->atom == xwm.ewmh._NET_WM_NAME
126+        && event->state == XCB_PROPERTY_NEW_VALUE)
127+    {
128+        update_name(entry->xwl_window);
129+    }
130+}
131+
132+static int connection_data(int fd, uint32_t mask, void * data)
133+{
134+    xcb_generic_event_t * event;
135+    uint32_t count = 0;
136+
137+    while ((event = xcb_poll_for_event(xwm.connection)))
138+    {
139+        switch (event->response_type & ~0x80)
140+        {
141+            case XCB_CREATE_NOTIFY:
142+                create_notify((xcb_create_notify_event_t *) event);
143+                break;
144+            case XCB_DESTROY_NOTIFY:
145+                destroy_notify((xcb_destroy_notify_event_t *) event);
146+                break;
147+            case XCB_MAP_REQUEST:
148+                map_request((xcb_map_request_event_t *) event);
149+                break;
150+            case XCB_CONFIGURE_REQUEST:
151+                configure_request((xcb_configure_request_event_t *) event);
152+                break;
153+            case XCB_PROPERTY_NOTIFY:
154+                property_notify((xcb_property_notify_event_t *) event);
155+        }
156+
157+        ++count;
158+    }
159+
160+    xcb_flush(xwm.connection);
161+
162+    return count;
163+}
164+
165+bool swc_xwm_initialize(int fd)
166+{
167+    const xcb_setup_t * setup;
168+    xcb_screen_iterator_t screen_iterator;
169+    uint32_t mask;
170+    uint32_t values[1];
171+    xcb_void_cookie_t change_attributes_cookie, redirect_subwindows_cookie;
172+    xcb_generic_error_t * error;
173+    xcb_intern_atom_cookie_t * ewmh_cookies;
174+    const xcb_query_extension_reply_t * composite_extension;
175+
176+    xwm.connection = xcb_connect_to_fd(fd, NULL);
177+
178+    if (xcb_connection_has_error(xwm.connection))
179+    {
180+        ERROR("xwm: Could not connect to X server\n");
181+        goto error0;
182+    }
183+
184+    xcb_prefetch_extension_data(xwm.connection, &xcb_composite_id);
185+    ewmh_cookies = xcb_ewmh_init_atoms(xwm.connection, &xwm.ewmh);
186+
187+    if (!ewmh_cookies)
188+    {
189+        ERROR("xwm: Failed to initialize EWMH atoms\n");
190+        goto error1;
191+    }
192+
193+    setup = xcb_get_setup(xwm.connection);
194+    screen_iterator = xcb_setup_roots_iterator(setup);
195+    xwm.screen = screen_iterator.data;
196+
197+    /* Try to select for substructure redirect. */
198+    mask = XCB_CW_EVENT_MASK;
199+    values[0] = XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY
200+              | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT;
201+    change_attributes_cookie = xcb_change_window_attributes
202+        (xwm.connection, xwm.screen->root, mask, values);
203+
204+    xwm.source = wl_event_loop_add_fd(swc.event_loop, fd, WL_EVENT_READABLE,
205+                                      &connection_data, NULL);
206+    wl_array_init(&xwm.windows);
207+
208+    if (!xwm.source)
209+    {
210+        ERROR("xwm: Failed to create X connection event source\n");
211+        goto error2;
212+    }
213+
214+    composite_extension = xcb_get_extension_data(xwm.connection, &xcb_composite_id);
215+
216+    if (!composite_extension->present)
217+    {
218+        ERROR("xwm: X server does not have composite extension\n");
219+        goto error3;
220+    }
221+
222+    redirect_subwindows_cookie = xcb_composite_redirect_subwindows_checked
223+            (xwm.connection, xwm.screen->root, XCB_COMPOSITE_REDIRECT_MANUAL);
224+
225+    if ((error = xcb_request_check(xwm.connection, change_attributes_cookie)))
226+    {
227+        ERROR("xwm: Another window manager is running\n");
228+        free(error);
229+        goto error3;
230+    }
231+
232+    if ((error = xcb_request_check(xwm.connection, redirect_subwindows_cookie)))
233+    {
234+        ERROR("xwm: Could not redirect subwindows of root for compositing\n");
235+        free(error);
236+        goto error3;
237+    }
238+
239+    if (!xcb_ewmh_init_atoms_replies(&xwm.ewmh, ewmh_cookies, NULL))
240+    {
241+        ERROR("xwm: Failed to get EWMH atom replies\n");
242+        goto error3;
243+    }
244+
245+    return true;
246+
247+  error3:
248+    wl_event_source_remove(xwm.source);
249+  error2:
250+    xcb_ewmh_connection_wipe(&xwm.ewmh);
251+  error1:
252+    xcb_disconnect(xwm.connection);
253+  error0:
254+    return false;
255+}
256+
257+void swc_xwm_finalize()
258+{
259+    wl_array_release(&xwm.windows);
260+    wl_event_source_remove(xwm.source);
261+    xcb_ewmh_connection_wipe(&xwm.ewmh);
262+    xcb_disconnect(xwm.connection);
263+}
264+
265+static void configure(struct swc_window * window,
266+                      const struct swc_rectangle * geometry)
267+{
268+    uint32_t mask, values[4];
269+    struct xwl_window * xwl_window
270+        = CONTAINER_OF(window, typeof(*xwl_window), window.base);
271+
272+    mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y
273+         | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
274+    values[0] = geometry->x;
275+    values[1] = geometry->y;
276+    values[2] = geometry->width;
277+    values[3] = geometry->height;
278+
279+    xcb_configure_window(xwm.connection, xwl_window->id, mask, values);
280+    xcb_flush(xwm.connection);
281+}
282+
283+static void focus(struct swc_window * window)
284+{
285+    struct xwl_window * xwl_window
286+        = CONTAINER_OF(window, typeof(*xwl_window), window.base);
287+
288+    xcb_set_input_focus(xwm.connection, XCB_INPUT_FOCUS_NONE,
289+                        xwl_window->id, XCB_CURRENT_TIME);
290+    xcb_flush(xwm.connection);
291+}
292+
293+static const struct swc_window_impl xwl_window_handler = {
294+    .configure = &configure,
295+    .focus = &focus
296+};
297+
298+static void handle_surface_destroy(struct wl_listener * listener, void * data)
299+{
300+    struct xwl_window_entry * entry;
301+    struct xwl_window * xwl_window
302+        = CONTAINER_OF(listener, typeof(*xwl_window), surface_destroy_listener);
303+
304+    swc_window_finalize(&xwl_window->window.base);
305+    free(xwl_window);
306+
307+    wl_array_for_each(entry, &xwm.windows)
308+    {
309+        if (entry->xwl_window == xwl_window)
310+        {
311+            entry->xwl_window = NULL;
312+            break;
313+        }
314+    }
315+}
316+
317+void swc_xwm_manage_window(xcb_window_t id, struct swc_surface * surface)
318+{
319+    struct xwl_window_entry * entry;
320+    struct xwl_window * xwl_window;
321+    xcb_get_geometry_cookie_t geometry_cookie;
322+    xcb_get_geometry_reply_t * geometry_reply;
323+
324+    if (!(entry = find_window(id)))
325+        return;
326+
327+    if (!(xwl_window = malloc(sizeof *xwl_window)))
328+        return;
329+
330+    geometry_cookie = xcb_get_geometry(xwm.connection, id);
331+
332+    xwl_window->id = id;
333+    xwl_window->surface_destroy_listener.notify = &handle_surface_destroy;
334+    wl_resource_add_destroy_listener(surface->resource,
335+                                     &xwl_window->surface_destroy_listener);
336+    swc_window_initialize(&xwl_window->window.base,
337+                          &xwl_window_handler, surface);
338+
339+    entry->xwl_window = xwl_window;
340+
341+    if ((geometry_reply = xcb_get_geometry_reply(xwm.connection,
342+                                                 geometry_cookie, NULL)))
343+    {
344+        swc_surface_move(surface, geometry_reply->x, geometry_reply->y);
345+    }
346+
347+    if (entry->override_redirect)
348+        swc_compositor_surface_show(surface);
349+    else
350+    {
351+        uint32_t mask, values[1];
352+
353+        mask = XCB_CW_EVENT_MASK;
354+        values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
355+        xcb_change_window_attributes(xwm.connection, id, mask, values);
356+        update_name(xwl_window);
357+
358+        swc_window_set_state(&xwl_window->window.base,
359+                             SWC_WINDOW_STATE_TOPLEVEL);
360+    }
361+}
362+
+38, -0
 1@@ -0,0 +1,38 @@
 2+/* swc: libswc/xwm.h
 3+ *
 4+ * Copyright (c) 2013 Michael Forney
 5+ *
 6+ * Permission is hereby granted, free of charge, to any person obtaining a copy
 7+ * of this software and associated documentation files (the "Software"), to deal
 8+ * in the Software without restriction, including without limitation the rights
 9+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+ * copies of the Software, and to permit persons to whom the Software is
11+ * furnished to do so, subject to the following conditions:
12+ *
13+ * The above copyright notice and this permission notice shall be included in
14+ * all copies or substantial portions of the Software.
15+ *
16+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+ * SOFTWARE.
23+ */
24+
25+#ifndef SWC_XWM_H
26+#define SWC_XWM_H
27+
28+#include <stdbool.h>
29+#include <xcb/xcb.h>
30+
31+struct swc_surface;
32+
33+bool swc_xwm_initialize(int fd);
34+void swc_xwm_finalize();
35+
36+void swc_xwm_manage_window(xcb_window_t window, struct swc_surface * surface);
37+
38+#endif
39+
+3, -2
 1@@ -2,8 +2,9 @@
 2 
 3 dir := protocol
 4 
 5-PROTOCOL_EXTENSIONS =   \
 6-    $(dir)/wayland-drm.xml
 7+PROTOCOL_EXTENSIONS =           \
 8+    $(dir)/wayland-drm.xml      \
 9+    $(dir)/xserver.xml
10 
11 $(dir)_TARGETS := $(PROTOCOL_EXTENSIONS:%.xml=%-protocol.c) \
12                   $(PROTOCOL_EXTENSIONS:%.xml=%-server-protocol.h)
+18, -0
 1@@ -0,0 +1,18 @@
 2+<protocol name="xserver">
 3+
 4+  <interface name="xserver" version="1">
 5+    <request name="set_window_id">
 6+      <arg name="surface" type="object" interface="wl_surface"/>
 7+      <arg name="id" type="uint"/>
 8+    </request>
 9+
10+    <event name="client">
11+      <arg name="fd" type="fd"/>
12+    </event>
13+
14+    <event name="listen_socket">
15+      <arg name="fd" type="fd"/>
16+    </event>
17+  </interface>
18+
19+</protocol>