commit 1fe3b4d

Tony Olagbaiye  ·  2020-07-07 19:21:20 +0000 UTC
parent 5dc48e1
Revert "Remove xwayland support"

This reverts commit 2011cc741cc12b50f6d85529a898b378ae78bd8d.
10 files changed,  +974, -0
+8, -0
 1@@ -34,6 +34,14 @@ PACKAGES :=           \
 2     wld               \
 3     xkbcommon
 4 
 5+ifeq ($(ENABLE_XWAYLAND),1)
 6+PACKAGES +=         \
 7+    xcb             \
 8+    xcb-composite   \
 9+    xcb-ewmh        \
10+    xcb-icccm
11+endif
12+
13 ifneq ($(shell uname),NetBSD)
14     PACKAGES += libinput
15     ifeq ($(ENABLE_LIBUDEV),1)
+4, -0
 1@@ -25,6 +25,10 @@ Dependencies
 2 For input hotplugging on Linux, the following is also required:
 3 * libudev
 4 
 5+For XWayland support, the following are also required:
 6+* libxcb
 7+* xcb-util-wm
 8+
 9 Implementing a window manager using swc
10 ---------------------------------------
11 You must implement two callback functions, `new_window` and `new_screen`, which
+1, -0
1@@ -17,4 +17,5 @@ ENABLE_DEBUG    = 1
2 ENABLE_STATIC   = 1
3 ENABLE_SHARED   = 1
4 ENABLE_LIBUDEV  = 1
5+ENABLE_XWAYLAND = 1
6 
+4, -0
 1@@ -52,6 +52,10 @@ struct swc {
 2 	struct wl_global *subcompositor;
 3 	struct wl_global *xdg_decoration_manager;
 4 	struct wl_global *xdg_shell;
 5+
 6+#ifdef ENABLE_XWAYLAND
 7+	const struct swc_xserver *const xserver;
 8+#endif
 9 };
10 
11 extern struct swc swc;
+9, -0
 1@@ -73,6 +73,15 @@ else
 2     endif
 3 endif
 4 
 5+ifeq ($(ENABLE_XWAYLAND),1)
 6+$(dir)_CFLAGS += -DENABLE_XWAYLAND
 7+$(dir)_PACKAGES += xcb xcb-composite xcb-ewmh xcb-icccm
 8+
 9+SWC_SOURCES +=                      \
10+    libswc/xserver.c                \
11+    libswc/xwm.c
12+endif
13+
14 SWC_STATIC_OBJECTS = $(SWC_SOURCES:%.c=%.o)
15 SWC_SHARED_OBJECTS = $(SWC_SOURCES:%.c=%.lo)
16 
+23, -0
 1@@ -42,11 +42,17 @@
 2 #include "window.h"
 3 #include "xdg_decoration.h"
 4 #include "xdg_shell.h"
 5+#ifdef ENABLE_XWAYLAND
 6+# include "xserver.h"
 7+#endif
 8 
 9 extern struct swc_launch swc_launch;
10 extern const struct swc_bindings swc_bindings;
11 extern struct swc_compositor swc_compositor;
12 extern struct swc_drm swc_drm;
13+#ifdef ENABLE_XWAYLAND
14+extern struct swc_xserver swc_xserver;
15+#endif
16 
17 extern struct pointer_handler screens_pointer_handler;
18 
19@@ -54,6 +60,9 @@ struct swc swc = {
20 	.bindings = &swc_bindings,
21 	.compositor = &swc_compositor,
22 	.drm = &swc_drm,
23+#ifdef ENABLE_XWAYLAND
24+	.xserver = &swc_xserver,
25+#endif
26 };
27 
28 static void
29@@ -187,10 +196,21 @@ swc_initialize(struct wl_display *display, struct wl_event_loop *event_loop, con
30 		goto error13;
31 	}
32 
33+#ifdef ENABLE_XWAYLAND
34+	if (!xserver_initialize()) {
35+		ERROR("Could not initialize xwayland\n");
36+		goto error14;
37+	}
38+#endif
39+
40 	setup_compositor();
41 
42 	return true;
43 
44+#ifdef ENABLE_XWAYLAND
45+error14:
46+	wl_global_destroy(swc.panel_manager);
47+#endif
48 error13:
49 	wl_global_destroy(swc.kde_decoration_manager);
50 error12:
51@@ -224,6 +244,9 @@ error0:
52 EXPORT void
53 swc_finalize(void)
54 {
55+#ifdef ENABLE_XWAYLAND
56+	xserver_finalize();
57+#endif
58 	wl_global_destroy(swc.panel_manager);
59 	wl_global_destroy(swc.xdg_decoration_manager);
60 	wl_global_destroy(swc.xdg_shell);
+319, -0
  1@@ -0,0 +1,319 @@
  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 <signal.h>
 35+#include <stdlib.h>
 36+#include <stdio.h>
 37+#include <unistd.h>
 38+#include <fcntl.h>
 39+#include <errno.h>
 40+#include <sys/stat.h>
 41+#include <sys/socket.h>
 42+#include <sys/un.h>
 43+#include <wayland-server.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+	struct wl_resource *resource;
 51+	struct wl_event_source *usr1_source;
 52+	int display;
 53+	char display_name[16];
 54+	int abstract_fd, unix_fd, wm_fd;
 55+	bool xwm_initialized;
 56+} xserver;
 57+
 58+struct swc_xserver swc_xserver;
 59+
 60+static int
 61+open_socket(struct sockaddr_un *addr)
 62+{
 63+	int fd;
 64+
 65+	if ((fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0)) < 0)
 66+		goto error0;
 67+
 68+	/* Unlink the socket location in case it was being used by a process which
 69+	 * left around a stale lockfile. */
 70+	unlink(addr->sun_path);
 71+
 72+	if (bind(fd, (struct sockaddr *)addr, sizeof(*addr)) < 0)
 73+		goto error1;
 74+
 75+	if (listen(fd, 1) < 0)
 76+		goto error2;
 77+
 78+	return fd;
 79+
 80+error2:
 81+	if (addr->sun_path[0])
 82+		unlink(addr->sun_path);
 83+error1:
 84+	close(fd);
 85+error0:
 86+	return -1;
 87+}
 88+
 89+static bool
 90+open_display(void)
 91+{
 92+	char lock_name[64], pid[12];
 93+	int lock_fd;
 94+	struct sockaddr_un addr = {.sun_family = AF_LOCAL};
 95+
 96+	xserver.display = 0;
 97+
 98+	/* Create X lockfile and server sockets */
 99+	goto begin;
100+
101+retry2:
102+	close(xserver.abstract_fd);
103+retry1:
104+	unlink(lock_name);
105+retry0:
106+	if (++xserver.display > 32) {
107+		ERROR("No open display in first 32\n");
108+		return false;
109+	}
110+
111+begin:
112+	snprintf(lock_name, sizeof(lock_name), LOCK_FMT, xserver.display);
113+	lock_fd = open(lock_name, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0444);
114+
115+	if (lock_fd == -1) {
116+		char *end;
117+		pid_t owner;
118+
119+		/* Check if the owning process is still alive. */
120+		if ((lock_fd = open(lock_name, O_RDONLY)) == -1)
121+			goto retry0;
122+
123+		if (read(lock_fd, pid, sizeof(pid) - 1) != sizeof(pid) - 1)
124+			goto retry0;
125+
126+		owner = strtol(pid, &end, 10);
127+
128+		if (end != pid + 10)
129+			goto retry0;
130+
131+		if (kill(owner, 0) == 0 || errno != ESRCH)
132+			goto retry0;
133+
134+		if (unlink(lock_name) != 0)
135+			goto retry0;
136+
137+		goto begin;
138+	}
139+
140+	snprintf(pid, sizeof(pid), "%10d\n", getpid());
141+	if (write(lock_fd, pid, sizeof(pid) - 1) != sizeof(pid) - 1) {
142+		ERROR("Failed to write PID file\n");
143+		unlink(lock_name);
144+		close(lock_fd);
145+		return false;
146+	}
147+
148+	close(lock_fd);
149+
150+	/* Bind to abstract socket */
151+	addr.sun_path[0] = '\0';
152+	snprintf(addr.sun_path + 1, sizeof(addr.sun_path) - 1, SOCKET_FMT, xserver.display);
153+	if ((xserver.abstract_fd = open_socket(&addr)) < 0)
154+		goto retry1;
155+
156+	/* Bind to unix socket */
157+	mkdir(SOCKET_DIR, 0777);
158+	snprintf(addr.sun_path, sizeof(addr.sun_path), SOCKET_FMT, xserver.display);
159+	if ((xserver.unix_fd = open_socket(&addr)) < 0)
160+		goto retry2;
161+
162+	snprintf(xserver.display_name, sizeof(xserver.display_name), ":%d", xserver.display);
163+	setenv("DISPLAY", xserver.display_name, true);
164+
165+	return true;
166+}
167+
168+static void
169+close_display(void)
170+{
171+	char path[64];
172+
173+	close(xserver.abstract_fd);
174+	close(xserver.unix_fd);
175+
176+	snprintf(path, sizeof(path), SOCKET_FMT, xserver.display);
177+	unlink(path);
178+	snprintf(path, sizeof(path), LOCK_FMT, xserver.display);
179+	unlink(path);
180+
181+	unsetenv("DISPLAY");
182+}
183+
184+static int
185+handle_usr1(int signal_number, void *data)
186+{
187+	if (xwm_initialize(xserver.wm_fd)) {
188+		xserver.xwm_initialized = true;
189+	} else {
190+		ERROR("Failed to initialize X window manager\n");
191+		/* XXX: How do we handle this case? */
192+	}
193+
194+	wl_event_source_remove(xserver.usr1_source);
195+
196+	return 0;
197+}
198+
199+static void
200+handle_client_destroy(struct wl_listener *listener, void *data) {
201+	swc_xserver.client = NULL;
202+}
203+
204+static struct wl_listener client_destroy_listener = {
205+	.notify = handle_client_destroy,
206+};
207+
208+bool
209+xserver_initialize(void)
210+{
211+	int wl[2], wm[2];
212+
213+	/* Open an X display */
214+	if (!open_display()) {
215+		ERROR("Failed to get X lockfile and sockets\n");
216+		goto error0;
217+	}
218+
219+	xserver.usr1_source = wl_event_loop_add_signal(swc.event_loop, SIGUSR1, &handle_usr1, NULL);
220+
221+	if (!xserver.usr1_source) {
222+		ERROR("Failed to create SIGUSR1 event source\n");
223+		goto error1;
224+	}
225+
226+	/* Open a socket for the Wayland connection from Xwayland. */
227+	if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wl) != 0) {
228+		ERROR("Failed to create socketpair: %s\n", strerror(errno));
229+		goto error2;
230+	}
231+
232+	/* Open a socket for the X connection to Xwayland. */
233+	if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wm) != 0) {
234+		ERROR("Failed to create socketpair: %s\n", strerror(errno));
235+		goto error3;
236+	}
237+
238+	if (!(swc_xserver.client = wl_client_create(swc.display, wl[0])))
239+		goto error4;
240+
241+	wl_client_add_destroy_listener(swc_xserver.client, &client_destroy_listener);
242+	xserver.wm_fd = wm[0];
243+
244+	/* Start the X server */
245+	switch (fork()) {
246+	case 0: {
247+		int fds[] = { wl[1], wm[1], xserver.abstract_fd, xserver.unix_fd };
248+		char strings[ARRAY_LENGTH(fds)][16];
249+		unsigned index;
250+		struct sigaction action = {.sa_handler = SIG_IGN };
251+
252+		/* Unset the FD_CLOEXEC flag on the FDs that will get passed to Xwayland. */
253+		for (index = 0; index < ARRAY_LENGTH(fds); ++index) {
254+			if (fcntl(fds[index], F_SETFD, 0) != 0) {
255+				ERROR("fcntl() failed: %s\n", strerror(errno));
256+				goto fail;
257+			}
258+
259+			if (snprintf(strings[index], sizeof(strings[index]), "%d", fds[index]) >= sizeof(strings[index])) {
260+				ERROR("FD is too large\n");
261+				goto fail;
262+			}
263+		}
264+
265+		/* Ignore the USR1 signal so that Xwayland will send a USR1 signal to the
266+		 * parent process (us) after it finishes initializing. See Xserver(1) for
267+		 * more details. */
268+		if (sigaction(SIGUSR1, &action, NULL) != 0) {
269+			ERROR("Failed to set SIGUSR1 handler to SIG_IGN: %s\n", strerror(errno));
270+			goto fail;
271+		}
272+
273+		setenv("WAYLAND_SOCKET", strings[0], true);
274+		execlp("Xwayland", "Xwayland",
275+		       xserver.display_name,
276+		       "-rootless",
277+		       "-terminate",
278+		       "-listen", strings[2],
279+		       "-listen", strings[3],
280+		       "-wm", strings[1],
281+		       NULL);
282+
283+	fail:
284+		exit(EXIT_FAILURE);
285+	}
286+	case -1:
287+		ERROR("fork() failed when trying to start X server: %s\n", strerror(errno));
288+		goto error5;
289+	}
290+
291+	close(wl[1]);
292+	close(wm[1]);
293+
294+	return true;
295+
296+error5:
297+	wl_client_destroy(swc_xserver.client);
298+error4:
299+	close(wm[1]);
300+	close(wm[0]);
301+error3:
302+	close(wl[1]);
303+	close(wl[0]);
304+error2:
305+	wl_event_source_remove(xserver.usr1_source);
306+error1:
307+	close_display();
308+error0:
309+	return false;
310+}
311+
312+void
313+xserver_finalize(void)
314+{
315+	if (xserver.xwm_initialized)
316+		xwm_finalize();
317+	if (swc_xserver.client)
318+		wl_client_destroy(swc_xserver.client);
319+	close_display();
320+}
+36, -0
 1@@ -0,0 +1,36 @@
 2+/* swc: libswc/xserver.h
 3+ *
 4+ * Copyright (c) 2013, 2014 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+struct swc_xserver {
31+	struct wl_client *client;
32+};
33+
34+bool xserver_initialize(void);
35+void xserver_finalize(void);
36+
37+#endif
+538, -0
  1@@ -0,0 +1,538 @@
  2+/* swc: libswc/xwm.c
  3+ *
  4+ * Copyright (c) 2013, 2014 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.h"
 27+#include "internal.h"
 28+#include "surface.h"
 29+#include "swc.h"
 30+#include "util.h"
 31+#include "view.h"
 32+#include "window.h"
 33+#include "xserver.h"
 34+
 35+#include <stdio.h>
 36+#include <xcb/composite.h>
 37+#include <xcb/xcb_ewmh.h>
 38+#include <xcb/xcb_icccm.h>
 39+
 40+struct xwl_window {
 41+	xcb_window_t id;
 42+	uint32_t surface_id;
 43+	bool override_redirect, supports_delete;
 44+	struct wl_list link;
 45+
 46+	/* Only used for paired windows. */
 47+	struct {
 48+		struct window window;
 49+		struct wl_listener surface_destroy_listener;
 50+	};
 51+};
 52+
 53+enum atom {
 54+	ATOM_WL_SURFACE_ID,
 55+	ATOM_WM_DELETE_WINDOW,
 56+	ATOM_WM_PROTOCOLS,
 57+	ATOM_WM_S0,
 58+};
 59+
 60+static struct {
 61+	xcb_connection_t *connection;
 62+	xcb_ewmh_connection_t ewmh;
 63+	xcb_screen_t *screen;
 64+	xcb_window_t window;
 65+	struct xwl_window *focus;
 66+	struct wl_event_source *source;
 67+	struct wl_list windows, unpaired_windows;
 68+	union {
 69+		const char *name;
 70+		xcb_intern_atom_cookie_t cookie;
 71+		xcb_atom_t value;
 72+	} atoms[4];
 73+} xwm = {
 74+	.atoms = {
 75+		[ATOM_WL_SURFACE_ID] = "WL_SURFACE_ID",
 76+		[ATOM_WM_DELETE_WINDOW] = "WM_DELETE_WINDOW",
 77+		[ATOM_WM_PROTOCOLS] = "WM_PROTOCOLS",
 78+		[ATOM_WM_S0] = "WM_S0",
 79+	}
 80+};
 81+
 82+static void
 83+update_name(struct xwl_window *xwl_window)
 84+{
 85+	xcb_get_property_cookie_t wm_name_cookie;
 86+	xcb_ewmh_get_utf8_strings_reply_t wm_name_reply;
 87+
 88+	wm_name_cookie = xcb_ewmh_get_wm_name(&xwm.ewmh, xwl_window->id);
 89+
 90+	if (xcb_ewmh_get_wm_name_reply(&xwm.ewmh, wm_name_cookie, &wm_name_reply, NULL)) {
 91+		window_set_title(&xwl_window->window, wm_name_reply.strings, wm_name_reply.strings_len);
 92+		xcb_ewmh_get_utf8_strings_reply_wipe(&wm_name_reply);
 93+	} else {
 94+		window_set_title(&xwl_window->window, NULL, 0);
 95+	}
 96+}
 97+
 98+static void
 99+update_protocols(struct xwl_window *xwl_window)
100+{
101+	xcb_get_property_cookie_t cookie;
102+	xcb_icccm_get_wm_protocols_reply_t reply;
103+	unsigned index;
104+
105+	cookie = xcb_icccm_get_wm_protocols(xwm.connection, xwl_window->id, xwm.atoms[ATOM_WM_PROTOCOLS].value);
106+	xwl_window->supports_delete = true;
107+
108+	if (!xcb_icccm_get_wm_protocols_reply(xwm.connection, cookie, &reply, NULL))
109+		return;
110+
111+	for (index = 0; index < reply.atoms_len; ++index) {
112+		if (reply.atoms[index] == xwm.atoms[ATOM_WM_DELETE_WINDOW].value)
113+			xwl_window->supports_delete = true;
114+	}
115+
116+	xcb_icccm_get_wm_protocols_reply_wipe(&reply);
117+}
118+
119+static struct xwl_window *
120+find_window(struct wl_list *list, xcb_window_t id)
121+{
122+	struct xwl_window *window;
123+
124+	wl_list_for_each (window, list, link) {
125+		if (window->id == id)
126+			return window;
127+	}
128+
129+	return NULL;
130+}
131+
132+static struct xwl_window *
133+find_window_by_surface_id(struct wl_list *list, uint32_t id)
134+{
135+	struct xwl_window *window;
136+
137+	wl_list_for_each (window, list, link) {
138+		if (window->surface_id == id)
139+			return window;
140+	}
141+
142+	return NULL;
143+}
144+
145+static void
146+move(struct window *window, int32_t x, int32_t y)
147+{
148+	uint32_t mask, values[2];
149+	struct xwl_window *xwl_window = wl_container_of(window, xwl_window, window);
150+
151+	mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y;
152+	values[0] = x;
153+	values[1] = y;
154+
155+	xcb_configure_window(xwm.connection, xwl_window->id, mask, values);
156+	xcb_flush(xwm.connection);
157+}
158+
159+static void
160+configure(struct window *window, uint32_t width, uint32_t height)
161+{
162+	uint32_t mask, values[2];
163+	struct xwl_window *xwl_window = wl_container_of(window, xwl_window, window);
164+
165+	mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
166+	values[0] = width;
167+	values[1] = height;
168+
169+	window->configure.acknowledged = true;
170+	xcb_configure_window(xwm.connection, xwl_window->id, mask, values);
171+	xcb_flush(xwm.connection);
172+}
173+
174+static void
175+focus(struct window *window)
176+{
177+	struct xwl_window *xwl_window = wl_container_of(window, xwl_window, window);
178+
179+	xcb_set_input_focus(xwm.connection, XCB_INPUT_FOCUS_NONE, xwl_window->id, XCB_CURRENT_TIME);
180+	xcb_flush(xwm.connection);
181+	xwm.focus = xwl_window;
182+}
183+
184+static void
185+unfocus(struct window *window)
186+{
187+	struct xwl_window *xwl_window = wl_container_of(window, xwl_window, window);
188+
189+	/* If the window we are unfocusing is the latest xwl_window to be focused, we
190+	 * know we have transitioned to some other window type, so the X11 focus can
191+	 * be set to XCB_NONE. Otherwise, we have transitioned to another X11 window,
192+	 * and the X11 focus has already been updated. */
193+	if (xwl_window == xwm.focus) {
194+		xcb_set_input_focus(xwm.connection, XCB_INPUT_FOCUS_NONE, XCB_NONE, XCB_CURRENT_TIME);
195+		xcb_flush(xwm.connection);
196+	}
197+}
198+
199+static void
200+close(struct window *window)
201+{
202+	struct xwl_window *xwl_window = wl_container_of(window, xwl_window, window);
203+
204+	if (xwl_window->supports_delete) {
205+		xcb_client_message_event_t event = {
206+			.response_type = XCB_CLIENT_MESSAGE,
207+			.format = 32,
208+			.window = xwl_window->id,
209+			.type = xwm.atoms[ATOM_WM_PROTOCOLS].value,
210+			.data.data32 = {
211+				xwm.atoms[ATOM_WM_DELETE_WINDOW].value,
212+				XCB_CURRENT_TIME,
213+			},
214+		};
215+
216+		xcb_send_event(xwm.connection, false, xwl_window->id, XCB_EVENT_MASK_NO_EVENT, (const char *)&event);
217+	} else {
218+		xcb_kill_client(xwm.connection, xwl_window->id);
219+	}
220+
221+	xcb_flush(xwm.connection);
222+}
223+
224+static const struct window_impl xwl_window_handler = {
225+	.move = move,
226+	.configure = configure,
227+	.focus = focus,
228+	.unfocus = unfocus,
229+	.close = close,
230+};
231+
232+static void
233+handle_surface_destroy(struct wl_listener *listener, void *data)
234+{
235+	struct xwl_window *xwl_window = wl_container_of(listener, xwl_window, surface_destroy_listener);
236+
237+	if (xwm.focus == xwl_window)
238+		xwm.focus = NULL;
239+
240+	window_finalize(&xwl_window->window);
241+	wl_list_remove(&xwl_window->link);
242+	wl_list_insert(&xwm.unpaired_windows, &xwl_window->link);
243+	xwl_window->surface_id = 0;
244+}
245+
246+static bool
247+manage_window(struct xwl_window *xwl_window)
248+{
249+	struct wl_resource *resource;
250+	struct surface *surface;
251+	xcb_get_geometry_cookie_t geometry_cookie;
252+	xcb_get_geometry_reply_t *geometry_reply;
253+
254+	resource = wl_client_get_object(swc.xserver->client, xwl_window->surface_id);
255+
256+	if (!resource)
257+		return false;
258+
259+	surface = wl_resource_get_user_data(resource);
260+	geometry_cookie = xcb_get_geometry(xwm.connection, xwl_window->id);
261+
262+	window_initialize(&xwl_window->window, &xwl_window_handler, surface);
263+	xwl_window->surface_destroy_listener.notify = &handle_surface_destroy;
264+	wl_resource_add_destroy_listener(surface->resource, &xwl_window->surface_destroy_listener);
265+
266+	if ((geometry_reply = xcb_get_geometry_reply(xwm.connection, geometry_cookie, NULL))) {
267+		view_move(surface->view, geometry_reply->x, geometry_reply->y);
268+		free(geometry_reply);
269+	}
270+
271+	if (xwl_window->override_redirect) {
272+		compositor_view_show(xwl_window->window.view);
273+	} else {
274+		uint32_t mask, values[1];
275+
276+		mask = XCB_CW_EVENT_MASK;
277+		values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
278+		xcb_change_window_attributes(xwm.connection, xwl_window->id, mask, values);
279+		mask = XCB_CONFIG_WINDOW_BORDER_WIDTH;
280+		values[0] = 0;
281+		xcb_configure_window(xwm.connection, xwl_window->id, mask, values);
282+		update_name(xwl_window);
283+		update_protocols(xwl_window);
284+		window_manage(&xwl_window->window);
285+	}
286+
287+	wl_list_remove(&xwl_window->link);
288+	wl_list_insert(&xwm.windows, &xwl_window->link);
289+
290+	return true;
291+}
292+
293+static void
294+handle_new_surface(struct wl_listener *listener, void *data)
295+{
296+	struct surface *surface = data;
297+	struct xwl_window *window;
298+
299+	window = find_window_by_surface_id(&xwm.unpaired_windows, wl_resource_get_id(surface->resource));
300+
301+	if (!window)
302+		return;
303+
304+	manage_window(window);
305+}
306+
307+static struct wl_listener new_surface_listener = {
308+	.notify = &handle_new_surface
309+};
310+
311+/* X event handlers */
312+static void
313+create_notify(xcb_create_notify_event_t *event)
314+{
315+	struct xwl_window *xwl_window;
316+
317+	if (!(xwl_window = malloc(sizeof *xwl_window)))
318+		return;
319+
320+	xwl_window->id = event->window;
321+	xwl_window->surface_id = 0;
322+	xwl_window->override_redirect = event->override_redirect;
323+	wl_list_insert(&xwm.unpaired_windows, &xwl_window->link);
324+}
325+
326+static void
327+destroy_notify(xcb_destroy_notify_event_t *event)
328+{
329+	struct xwl_window *xwl_window;
330+
331+	if ((xwl_window = find_window(&xwm.windows, event->window))) {
332+		wl_list_remove(&xwl_window->surface_destroy_listener.link);
333+		window_finalize(&xwl_window->window);
334+	} else if (!(xwl_window = find_window(&xwm.unpaired_windows, event->window))) {
335+		return;
336+	}
337+
338+	wl_list_remove(&xwl_window->link);
339+	free(xwl_window);
340+}
341+
342+static void
343+map_request(xcb_map_request_event_t *event)
344+{
345+	xcb_map_window(xwm.connection, event->window);
346+}
347+
348+static void
349+configure_request(xcb_configure_request_event_t *event)
350+{
351+}
352+
353+static void
354+property_notify(xcb_property_notify_event_t *event)
355+{
356+	struct xwl_window *xwl_window;
357+
358+	if (!(xwl_window = find_window(&xwm.windows, event->window)))
359+		return;
360+
361+	if (event->atom == xwm.ewmh._NET_WM_NAME && event->state == XCB_PROPERTY_NEW_VALUE)
362+		update_name(xwl_window);
363+	else if (event->atom == xwm.atoms[ATOM_WM_PROTOCOLS].value)
364+		update_protocols(xwl_window);
365+}
366+
367+static void
368+client_message(xcb_client_message_event_t *event)
369+{
370+	if (event->type == xwm.atoms[ATOM_WL_SURFACE_ID].value) {
371+		struct xwl_window *xwl_window;
372+
373+		if (!(xwl_window = find_window(&xwm.unpaired_windows, event->window)))
374+			return;
375+
376+		xwl_window->surface_id = event->data.data32[0];
377+		manage_window(xwl_window);
378+	}
379+}
380+
381+static int
382+connection_data(int fd, uint32_t mask, void *data)
383+{
384+	xcb_generic_event_t *event;
385+	uint32_t count = 0;
386+
387+	while ((event = xcb_poll_for_event(xwm.connection))) {
388+		switch (event->response_type & ~0x80) {
389+		case XCB_CREATE_NOTIFY:
390+			create_notify((xcb_create_notify_event_t *)event);
391+			break;
392+		case XCB_DESTROY_NOTIFY:
393+			destroy_notify((xcb_destroy_notify_event_t *)event);
394+			break;
395+		case XCB_MAP_REQUEST:
396+			map_request((xcb_map_request_event_t *)event);
397+			break;
398+		case XCB_CONFIGURE_REQUEST:
399+			configure_request((xcb_configure_request_event_t *)event);
400+			break;
401+		case XCB_PROPERTY_NOTIFY:
402+			property_notify((xcb_property_notify_event_t *)event);
403+			break;
404+		case XCB_CLIENT_MESSAGE:
405+			client_message((xcb_client_message_event_t *)event);
406+			break;
407+		}
408+
409+		free(event);
410+		++count;
411+	}
412+
413+	xcb_flush(xwm.connection);
414+
415+	return count;
416+}
417+
418+bool
419+xwm_initialize(int fd)
420+{
421+	const xcb_setup_t *setup;
422+	xcb_screen_iterator_t screen_iterator;
423+	uint32_t mask;
424+	uint32_t values[1];
425+	xcb_void_cookie_t change_attributes_cookie, redirect_subwindows_cookie;
426+	xcb_generic_error_t *error;
427+	xcb_intern_atom_cookie_t *ewmh_cookies;
428+	xcb_intern_atom_reply_t *atom_reply;
429+	unsigned index;
430+	const char *name;
431+	const xcb_query_extension_reply_t *composite_extension;
432+
433+	xwm.connection = xcb_connect_to_fd(fd, NULL);
434+
435+	if (xcb_connection_has_error(xwm.connection)) {
436+		ERROR("xwm: Could not connect to X server\n");
437+		goto error0;
438+	}
439+
440+	xcb_prefetch_extension_data(xwm.connection, &xcb_composite_id);
441+	ewmh_cookies = xcb_ewmh_init_atoms(xwm.connection, &xwm.ewmh);
442+
443+	if (!ewmh_cookies) {
444+		ERROR("xwm: Failed to initialize EWMH atoms\n");
445+		goto error1;
446+	}
447+
448+	for (index = 0; index < ARRAY_LENGTH(xwm.atoms); ++index) {
449+		name = xwm.atoms[index].name;
450+		xwm.atoms[index].cookie = xcb_intern_atom(xwm.connection, 0, strlen(name), name);
451+	}
452+
453+	setup = xcb_get_setup(xwm.connection);
454+	screen_iterator = xcb_setup_roots_iterator(setup);
455+	xwm.screen = screen_iterator.data;
456+
457+	/* Try to select for substructure redirect. */
458+	mask = XCB_CW_EVENT_MASK;
459+	values[0] = XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT;
460+	change_attributes_cookie = xcb_change_window_attributes(xwm.connection, xwm.screen->root, mask, values);
461+
462+	xwm.source = wl_event_loop_add_fd(swc.event_loop, fd, WL_EVENT_READABLE, &connection_data, NULL);
463+	wl_list_init(&xwm.windows);
464+	wl_list_init(&xwm.unpaired_windows);
465+
466+	if (!xwm.source) {
467+		ERROR("xwm: Failed to create X connection event source\n");
468+		goto error2;
469+	}
470+
471+	composite_extension = xcb_get_extension_data(xwm.connection, &xcb_composite_id);
472+
473+	if (!composite_extension->present) {
474+		ERROR("xwm: X server does not have composite extension\n");
475+		goto error3;
476+	}
477+
478+	redirect_subwindows_cookie = xcb_composite_redirect_subwindows_checked(xwm.connection, xwm.screen->root, XCB_COMPOSITE_REDIRECT_MANUAL);
479+
480+	if ((error = xcb_request_check(xwm.connection, change_attributes_cookie))) {
481+		ERROR("xwm: Another window manager is running\n");
482+		free(error);
483+		goto error3;
484+	}
485+
486+	if ((error = xcb_request_check(xwm.connection, redirect_subwindows_cookie))) {
487+		ERROR("xwm: Could not redirect subwindows of root for compositing\n");
488+		free(error);
489+		goto error3;
490+	}
491+
492+	xwm.window = xcb_generate_id(xwm.connection);
493+	xcb_create_window(xwm.connection, 0, xwm.window, xwm.screen->root,
494+	                  0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY,
495+	                  XCB_COPY_FROM_PARENT, 0, NULL);
496+
497+	xcb_ewmh_init_atoms_replies(&xwm.ewmh, ewmh_cookies, &error);
498+
499+	if (error) {
500+		ERROR("xwm: Failed to get EWMH atom replies: %u\n", error->error_code);
501+		goto error3;
502+	}
503+
504+	for (index = 0; index < ARRAY_LENGTH(xwm.atoms); ++index) {
505+		atom_reply = xcb_intern_atom_reply(xwm.connection, xwm.atoms[index].cookie, &error);
506+
507+		if (error) {
508+			ERROR("xwm: Failed to get atom reply: %u\n", error->error_code);
509+			return false;
510+		}
511+
512+		xwm.atoms[index].value = atom_reply->atom;
513+		free(atom_reply);
514+	}
515+
516+	xcb_set_selection_owner(xwm.connection, xwm.window, xwm.atoms[ATOM_WM_S0].value, XCB_CURRENT_TIME);
517+	xcb_flush(xwm.connection);
518+
519+	wl_signal_add(&swc.compositor->signal.new_surface, &new_surface_listener);
520+
521+	return true;
522+
523+error3:
524+	wl_event_source_remove(xwm.source);
525+error2:
526+	xcb_ewmh_connection_wipe(&xwm.ewmh);
527+error1:
528+	xcb_disconnect(xwm.connection);
529+error0:
530+	return false;
531+}
532+
533+void
534+xwm_finalize(void)
535+{
536+	wl_event_source_remove(xwm.source);
537+	xcb_ewmh_connection_wipe(&xwm.ewmh);
538+	xcb_disconnect(xwm.connection);
539+}
+32, -0
 1@@ -0,0 +1,32 @@
 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+
30+bool xwm_initialize(int fd);
31+void xwm_finalize(void);
32+
33+#endif