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
M
Makefile
+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