swc

Unnamed repository; edit this file 'description' to name the repository.
git clone git://git.nihaljere.xyz/swc
Log | Files | Refs | README | LICENSE

commit 1fe3b4d45f9e4f03f92401f6c771a2cd60047029
parent 5dc48e1022d2d6e3df2361e8ec311ea4d89a1db5
Author: Tony Olagbaiye <me@fron.io>
Date:   Tue,  7 Jul 2020 20:21:20 +0100

Revert "Remove xwayland support"

This reverts commit 2011cc741cc12b50f6d85529a898b378ae78bd8d.

Diffstat:
MMakefile | 8++++++++
MREADME.md | 4++++
Mconfig.mk | 1+
Mlibswc/internal.h | 4++++
Mlibswc/local.mk | 9+++++++++
Mlibswc/swc.c | 23+++++++++++++++++++++++
Alibswc/xserver.c | 319+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alibswc/xserver.h | 36++++++++++++++++++++++++++++++++++++
Alibswc/xwm.c | 538+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alibswc/xwm.h | 32++++++++++++++++++++++++++++++++
10 files changed, 974 insertions(+), 0 deletions(-)

diff --git a/Makefile b/Makefile @@ -34,6 +34,14 @@ PACKAGES := \ wld \ xkbcommon +ifeq ($(ENABLE_XWAYLAND),1) +PACKAGES += \ + xcb \ + xcb-composite \ + xcb-ewmh \ + xcb-icccm +endif + ifneq ($(shell uname),NetBSD) PACKAGES += libinput ifeq ($(ENABLE_LIBUDEV),1) diff --git a/README.md b/README.md @@ -25,6 +25,10 @@ Dependencies For input hotplugging on Linux, the following is also required: * libudev +For XWayland support, the following are also required: +* libxcb +* xcb-util-wm + Implementing a window manager using swc --------------------------------------- You must implement two callback functions, `new_window` and `new_screen`, which diff --git a/config.mk b/config.mk @@ -17,4 +17,5 @@ ENABLE_DEBUG = 1 ENABLE_STATIC = 1 ENABLE_SHARED = 1 ENABLE_LIBUDEV = 1 +ENABLE_XWAYLAND = 1 diff --git a/libswc/internal.h b/libswc/internal.h @@ -52,6 +52,10 @@ struct swc { struct wl_global *subcompositor; struct wl_global *xdg_decoration_manager; struct wl_global *xdg_shell; + +#ifdef ENABLE_XWAYLAND + const struct swc_xserver *const xserver; +#endif }; extern struct swc swc; diff --git a/libswc/local.mk b/libswc/local.mk @@ -73,6 +73,15 @@ else endif endif +ifeq ($(ENABLE_XWAYLAND),1) +$(dir)_CFLAGS += -DENABLE_XWAYLAND +$(dir)_PACKAGES += xcb xcb-composite xcb-ewmh xcb-icccm + +SWC_SOURCES += \ + libswc/xserver.c \ + libswc/xwm.c +endif + SWC_STATIC_OBJECTS = $(SWC_SOURCES:%.c=%.o) SWC_SHARED_OBJECTS = $(SWC_SOURCES:%.c=%.lo) diff --git a/libswc/swc.c b/libswc/swc.c @@ -42,11 +42,17 @@ #include "window.h" #include "xdg_decoration.h" #include "xdg_shell.h" +#ifdef ENABLE_XWAYLAND +# include "xserver.h" +#endif extern struct swc_launch swc_launch; extern const struct swc_bindings swc_bindings; extern struct swc_compositor swc_compositor; extern struct swc_drm swc_drm; +#ifdef ENABLE_XWAYLAND +extern struct swc_xserver swc_xserver; +#endif extern struct pointer_handler screens_pointer_handler; @@ -54,6 +60,9 @@ struct swc swc = { .bindings = &swc_bindings, .compositor = &swc_compositor, .drm = &swc_drm, +#ifdef ENABLE_XWAYLAND + .xserver = &swc_xserver, +#endif }; static void @@ -187,10 +196,21 @@ swc_initialize(struct wl_display *display, struct wl_event_loop *event_loop, con goto error13; } +#ifdef ENABLE_XWAYLAND + if (!xserver_initialize()) { + ERROR("Could not initialize xwayland\n"); + goto error14; + } +#endif + setup_compositor(); return true; +#ifdef ENABLE_XWAYLAND +error14: + wl_global_destroy(swc.panel_manager); +#endif error13: wl_global_destroy(swc.kde_decoration_manager); error12: @@ -224,6 +244,9 @@ error0: EXPORT void swc_finalize(void) { +#ifdef ENABLE_XWAYLAND + xserver_finalize(); +#endif wl_global_destroy(swc.panel_manager); wl_global_destroy(swc.xdg_decoration_manager); wl_global_destroy(swc.xdg_shell); diff --git a/libswc/xserver.c b/libswc/xserver.c @@ -0,0 +1,319 @@ +/* swc: libswc/xserver.c + * + * Copyright (c) 2013 Michael Forney + * + * Based in part upon xwayland/launcher.c from weston, which is + * + * Copyright © 2011 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "xserver.h" +#include "internal.h" +#include "util.h" +#include "xwm.h" + +#include <signal.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <wayland-server.h> + +#define LOCK_FMT "/tmp/.X%d-lock" +#define SOCKET_DIR "/tmp/.X11-unix" +#define SOCKET_FMT SOCKET_DIR "/X%d" + +static struct { + struct wl_resource *resource; + struct wl_event_source *usr1_source; + int display; + char display_name[16]; + int abstract_fd, unix_fd, wm_fd; + bool xwm_initialized; +} xserver; + +struct swc_xserver swc_xserver; + +static int +open_socket(struct sockaddr_un *addr) +{ + int fd; + + if ((fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0)) < 0) + goto error0; + + /* Unlink the socket location in case it was being used by a process which + * left around a stale lockfile. */ + unlink(addr->sun_path); + + if (bind(fd, (struct sockaddr *)addr, sizeof(*addr)) < 0) + goto error1; + + if (listen(fd, 1) < 0) + goto error2; + + return fd; + +error2: + if (addr->sun_path[0]) + unlink(addr->sun_path); +error1: + close(fd); +error0: + return -1; +} + +static bool +open_display(void) +{ + char lock_name[64], pid[12]; + int lock_fd; + struct sockaddr_un addr = {.sun_family = AF_LOCAL}; + + xserver.display = 0; + + /* Create X lockfile and server sockets */ + goto begin; + +retry2: + close(xserver.abstract_fd); +retry1: + unlink(lock_name); +retry0: + if (++xserver.display > 32) { + ERROR("No open display in first 32\n"); + return false; + } + +begin: + snprintf(lock_name, sizeof(lock_name), LOCK_FMT, xserver.display); + lock_fd = open(lock_name, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0444); + + if (lock_fd == -1) { + char *end; + pid_t owner; + + /* Check if the owning process is still alive. */ + if ((lock_fd = open(lock_name, O_RDONLY)) == -1) + goto retry0; + + if (read(lock_fd, pid, sizeof(pid) - 1) != sizeof(pid) - 1) + goto retry0; + + owner = strtol(pid, &end, 10); + + if (end != pid + 10) + goto retry0; + + if (kill(owner, 0) == 0 || errno != ESRCH) + goto retry0; + + if (unlink(lock_name) != 0) + goto retry0; + + goto begin; + } + + snprintf(pid, sizeof(pid), "%10d\n", getpid()); + if (write(lock_fd, pid, sizeof(pid) - 1) != sizeof(pid) - 1) { + ERROR("Failed to write PID file\n"); + unlink(lock_name); + close(lock_fd); + return false; + } + + close(lock_fd); + + /* Bind to abstract socket */ + addr.sun_path[0] = '\0'; + snprintf(addr.sun_path + 1, sizeof(addr.sun_path) - 1, SOCKET_FMT, xserver.display); + if ((xserver.abstract_fd = open_socket(&addr)) < 0) + goto retry1; + + /* Bind to unix socket */ + mkdir(SOCKET_DIR, 0777); + snprintf(addr.sun_path, sizeof(addr.sun_path), SOCKET_FMT, xserver.display); + if ((xserver.unix_fd = open_socket(&addr)) < 0) + goto retry2; + + snprintf(xserver.display_name, sizeof(xserver.display_name), ":%d", xserver.display); + setenv("DISPLAY", xserver.display_name, true); + + return true; +} + +static void +close_display(void) +{ + char path[64]; + + close(xserver.abstract_fd); + close(xserver.unix_fd); + + snprintf(path, sizeof(path), SOCKET_FMT, xserver.display); + unlink(path); + snprintf(path, sizeof(path), LOCK_FMT, xserver.display); + unlink(path); + + unsetenv("DISPLAY"); +} + +static int +handle_usr1(int signal_number, void *data) +{ + if (xwm_initialize(xserver.wm_fd)) { + xserver.xwm_initialized = true; + } else { + ERROR("Failed to initialize X window manager\n"); + /* XXX: How do we handle this case? */ + } + + wl_event_source_remove(xserver.usr1_source); + + return 0; +} + +static void +handle_client_destroy(struct wl_listener *listener, void *data) { + swc_xserver.client = NULL; +} + +static struct wl_listener client_destroy_listener = { + .notify = handle_client_destroy, +}; + +bool +xserver_initialize(void) +{ + int wl[2], wm[2]; + + /* Open an X display */ + if (!open_display()) { + ERROR("Failed to get X lockfile and sockets\n"); + goto error0; + } + + xserver.usr1_source = wl_event_loop_add_signal(swc.event_loop, SIGUSR1, &handle_usr1, NULL); + + if (!xserver.usr1_source) { + ERROR("Failed to create SIGUSR1 event source\n"); + goto error1; + } + + /* Open a socket for the Wayland connection from Xwayland. */ + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wl) != 0) { + ERROR("Failed to create socketpair: %s\n", strerror(errno)); + goto error2; + } + + /* Open a socket for the X connection to Xwayland. */ + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wm) != 0) { + ERROR("Failed to create socketpair: %s\n", strerror(errno)); + goto error3; + } + + if (!(swc_xserver.client = wl_client_create(swc.display, wl[0]))) + goto error4; + + wl_client_add_destroy_listener(swc_xserver.client, &client_destroy_listener); + xserver.wm_fd = wm[0]; + + /* Start the X server */ + switch (fork()) { + case 0: { + int fds[] = { wl[1], wm[1], xserver.abstract_fd, xserver.unix_fd }; + char strings[ARRAY_LENGTH(fds)][16]; + unsigned index; + struct sigaction action = {.sa_handler = SIG_IGN }; + + /* Unset the FD_CLOEXEC flag on the FDs that will get passed to Xwayland. */ + for (index = 0; index < ARRAY_LENGTH(fds); ++index) { + if (fcntl(fds[index], F_SETFD, 0) != 0) { + ERROR("fcntl() failed: %s\n", strerror(errno)); + goto fail; + } + + if (snprintf(strings[index], sizeof(strings[index]), "%d", fds[index]) >= sizeof(strings[index])) { + ERROR("FD is too large\n"); + goto fail; + } + } + + /* Ignore the USR1 signal so that Xwayland will send a USR1 signal to the + * parent process (us) after it finishes initializing. See Xserver(1) for + * more details. */ + if (sigaction(SIGUSR1, &action, NULL) != 0) { + ERROR("Failed to set SIGUSR1 handler to SIG_IGN: %s\n", strerror(errno)); + goto fail; + } + + setenv("WAYLAND_SOCKET", strings[0], true); + execlp("Xwayland", "Xwayland", + xserver.display_name, + "-rootless", + "-terminate", + "-listen", strings[2], + "-listen", strings[3], + "-wm", strings[1], + NULL); + + fail: + exit(EXIT_FAILURE); + } + case -1: + ERROR("fork() failed when trying to start X server: %s\n", strerror(errno)); + goto error5; + } + + close(wl[1]); + close(wm[1]); + + return true; + +error5: + wl_client_destroy(swc_xserver.client); +error4: + close(wm[1]); + close(wm[0]); +error3: + close(wl[1]); + close(wl[0]); +error2: + wl_event_source_remove(xserver.usr1_source); +error1: + close_display(); +error0: + return false; +} + +void +xserver_finalize(void) +{ + if (xserver.xwm_initialized) + xwm_finalize(); + if (swc_xserver.client) + wl_client_destroy(swc_xserver.client); + close_display(); +} diff --git a/libswc/xserver.h b/libswc/xserver.h @@ -0,0 +1,36 @@ +/* swc: libswc/xserver.h + * + * Copyright (c) 2013, 2014 Michael Forney + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef SWC_XSERVER_H +#define SWC_XSERVER_H + +#include <stdbool.h> + +struct swc_xserver { + struct wl_client *client; +}; + +bool xserver_initialize(void); +void xserver_finalize(void); + +#endif diff --git a/libswc/xwm.c b/libswc/xwm.c @@ -0,0 +1,538 @@ +/* swc: libswc/xwm.c + * + * Copyright (c) 2013, 2014 Michael Forney + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "xwm.h" +#include "compositor.h" +#include "internal.h" +#include "surface.h" +#include "swc.h" +#include "util.h" +#include "view.h" +#include "window.h" +#include "xserver.h" + +#include <stdio.h> +#include <xcb/composite.h> +#include <xcb/xcb_ewmh.h> +#include <xcb/xcb_icccm.h> + +struct xwl_window { + xcb_window_t id; + uint32_t surface_id; + bool override_redirect, supports_delete; + struct wl_list link; + + /* Only used for paired windows. */ + struct { + struct window window; + struct wl_listener surface_destroy_listener; + }; +}; + +enum atom { + ATOM_WL_SURFACE_ID, + ATOM_WM_DELETE_WINDOW, + ATOM_WM_PROTOCOLS, + ATOM_WM_S0, +}; + +static struct { + xcb_connection_t *connection; + xcb_ewmh_connection_t ewmh; + xcb_screen_t *screen; + xcb_window_t window; + struct xwl_window *focus; + struct wl_event_source *source; + struct wl_list windows, unpaired_windows; + union { + const char *name; + xcb_intern_atom_cookie_t cookie; + xcb_atom_t value; + } atoms[4]; +} xwm = { + .atoms = { + [ATOM_WL_SURFACE_ID] = "WL_SURFACE_ID", + [ATOM_WM_DELETE_WINDOW] = "WM_DELETE_WINDOW", + [ATOM_WM_PROTOCOLS] = "WM_PROTOCOLS", + [ATOM_WM_S0] = "WM_S0", + } +}; + +static void +update_name(struct xwl_window *xwl_window) +{ + xcb_get_property_cookie_t wm_name_cookie; + xcb_ewmh_get_utf8_strings_reply_t wm_name_reply; + + wm_name_cookie = xcb_ewmh_get_wm_name(&xwm.ewmh, xwl_window->id); + + if (xcb_ewmh_get_wm_name_reply(&xwm.ewmh, wm_name_cookie, &wm_name_reply, NULL)) { + window_set_title(&xwl_window->window, wm_name_reply.strings, wm_name_reply.strings_len); + xcb_ewmh_get_utf8_strings_reply_wipe(&wm_name_reply); + } else { + window_set_title(&xwl_window->window, NULL, 0); + } +} + +static void +update_protocols(struct xwl_window *xwl_window) +{ + xcb_get_property_cookie_t cookie; + xcb_icccm_get_wm_protocols_reply_t reply; + unsigned index; + + cookie = xcb_icccm_get_wm_protocols(xwm.connection, xwl_window->id, xwm.atoms[ATOM_WM_PROTOCOLS].value); + xwl_window->supports_delete = true; + + if (!xcb_icccm_get_wm_protocols_reply(xwm.connection, cookie, &reply, NULL)) + return; + + for (index = 0; index < reply.atoms_len; ++index) { + if (reply.atoms[index] == xwm.atoms[ATOM_WM_DELETE_WINDOW].value) + xwl_window->supports_delete = true; + } + + xcb_icccm_get_wm_protocols_reply_wipe(&reply); +} + +static struct xwl_window * +find_window(struct wl_list *list, xcb_window_t id) +{ + struct xwl_window *window; + + wl_list_for_each (window, list, link) { + if (window->id == id) + return window; + } + + return NULL; +} + +static struct xwl_window * +find_window_by_surface_id(struct wl_list *list, uint32_t id) +{ + struct xwl_window *window; + + wl_list_for_each (window, list, link) { + if (window->surface_id == id) + return window; + } + + return NULL; +} + +static void +move(struct window *window, int32_t x, int32_t y) +{ + uint32_t mask, values[2]; + struct xwl_window *xwl_window = wl_container_of(window, xwl_window, window); + + mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y; + values[0] = x; + values[1] = y; + + xcb_configure_window(xwm.connection, xwl_window->id, mask, values); + xcb_flush(xwm.connection); +} + +static void +configure(struct window *window, uint32_t width, uint32_t height) +{ + uint32_t mask, values[2]; + struct xwl_window *xwl_window = wl_container_of(window, xwl_window, window); + + mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; + values[0] = width; + values[1] = height; + + window->configure.acknowledged = true; + xcb_configure_window(xwm.connection, xwl_window->id, mask, values); + xcb_flush(xwm.connection); +} + +static void +focus(struct window *window) +{ + struct xwl_window *xwl_window = wl_container_of(window, xwl_window, window); + + xcb_set_input_focus(xwm.connection, XCB_INPUT_FOCUS_NONE, xwl_window->id, XCB_CURRENT_TIME); + xcb_flush(xwm.connection); + xwm.focus = xwl_window; +} + +static void +unfocus(struct window *window) +{ + struct xwl_window *xwl_window = wl_container_of(window, xwl_window, window); + + /* If the window we are unfocusing is the latest xwl_window to be focused, we + * know we have transitioned to some other window type, so the X11 focus can + * be set to XCB_NONE. Otherwise, we have transitioned to another X11 window, + * and the X11 focus has already been updated. */ + if (xwl_window == xwm.focus) { + xcb_set_input_focus(xwm.connection, XCB_INPUT_FOCUS_NONE, XCB_NONE, XCB_CURRENT_TIME); + xcb_flush(xwm.connection); + } +} + +static void +close(struct window *window) +{ + struct xwl_window *xwl_window = wl_container_of(window, xwl_window, window); + + if (xwl_window->supports_delete) { + xcb_client_message_event_t event = { + .response_type = XCB_CLIENT_MESSAGE, + .format = 32, + .window = xwl_window->id, + .type = xwm.atoms[ATOM_WM_PROTOCOLS].value, + .data.data32 = { + xwm.atoms[ATOM_WM_DELETE_WINDOW].value, + XCB_CURRENT_TIME, + }, + }; + + xcb_send_event(xwm.connection, false, xwl_window->id, XCB_EVENT_MASK_NO_EVENT, (const char *)&event); + } else { + xcb_kill_client(xwm.connection, xwl_window->id); + } + + xcb_flush(xwm.connection); +} + +static const struct window_impl xwl_window_handler = { + .move = move, + .configure = configure, + .focus = focus, + .unfocus = unfocus, + .close = close, +}; + +static void +handle_surface_destroy(struct wl_listener *listener, void *data) +{ + struct xwl_window *xwl_window = wl_container_of(listener, xwl_window, surface_destroy_listener); + + if (xwm.focus == xwl_window) + xwm.focus = NULL; + + window_finalize(&xwl_window->window); + wl_list_remove(&xwl_window->link); + wl_list_insert(&xwm.unpaired_windows, &xwl_window->link); + xwl_window->surface_id = 0; +} + +static bool +manage_window(struct xwl_window *xwl_window) +{ + struct wl_resource *resource; + struct surface *surface; + xcb_get_geometry_cookie_t geometry_cookie; + xcb_get_geometry_reply_t *geometry_reply; + + resource = wl_client_get_object(swc.xserver->client, xwl_window->surface_id); + + if (!resource) + return false; + + surface = wl_resource_get_user_data(resource); + geometry_cookie = xcb_get_geometry(xwm.connection, xwl_window->id); + + window_initialize(&xwl_window->window, &xwl_window_handler, surface); + xwl_window->surface_destroy_listener.notify = &handle_surface_destroy; + wl_resource_add_destroy_listener(surface->resource, &xwl_window->surface_destroy_listener); + + if ((geometry_reply = xcb_get_geometry_reply(xwm.connection, geometry_cookie, NULL))) { + view_move(surface->view, geometry_reply->x, geometry_reply->y); + free(geometry_reply); + } + + if (xwl_window->override_redirect) { + compositor_view_show(xwl_window->window.view); + } else { + uint32_t mask, values[1]; + + mask = XCB_CW_EVENT_MASK; + values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE; + xcb_change_window_attributes(xwm.connection, xwl_window->id, mask, values); + mask = XCB_CONFIG_WINDOW_BORDER_WIDTH; + values[0] = 0; + xcb_configure_window(xwm.connection, xwl_window->id, mask, values); + update_name(xwl_window); + update_protocols(xwl_window); + window_manage(&xwl_window->window); + } + + wl_list_remove(&xwl_window->link); + wl_list_insert(&xwm.windows, &xwl_window->link); + + return true; +} + +static void +handle_new_surface(struct wl_listener *listener, void *data) +{ + struct surface *surface = data; + struct xwl_window *window; + + window = find_window_by_surface_id(&xwm.unpaired_windows, wl_resource_get_id(surface->resource)); + + if (!window) + return; + + manage_window(window); +} + +static struct wl_listener new_surface_listener = { + .notify = &handle_new_surface +}; + +/* X event handlers */ +static void +create_notify(xcb_create_notify_event_t *event) +{ + struct xwl_window *xwl_window; + + if (!(xwl_window = malloc(sizeof *xwl_window))) + return; + + xwl_window->id = event->window; + xwl_window->surface_id = 0; + xwl_window->override_redirect = event->override_redirect; + wl_list_insert(&xwm.unpaired_windows, &xwl_window->link); +} + +static void +destroy_notify(xcb_destroy_notify_event_t *event) +{ + struct xwl_window *xwl_window; + + if ((xwl_window = find_window(&xwm.windows, event->window))) { + wl_list_remove(&xwl_window->surface_destroy_listener.link); + window_finalize(&xwl_window->window); + } else if (!(xwl_window = find_window(&xwm.unpaired_windows, event->window))) { + return; + } + + wl_list_remove(&xwl_window->link); + free(xwl_window); +} + +static void +map_request(xcb_map_request_event_t *event) +{ + xcb_map_window(xwm.connection, event->window); +} + +static void +configure_request(xcb_configure_request_event_t *event) +{ +} + +static void +property_notify(xcb_property_notify_event_t *event) +{ + struct xwl_window *xwl_window; + + if (!(xwl_window = find_window(&xwm.windows, event->window))) + return; + + if (event->atom == xwm.ewmh._NET_WM_NAME && event->state == XCB_PROPERTY_NEW_VALUE) + update_name(xwl_window); + else if (event->atom == xwm.atoms[ATOM_WM_PROTOCOLS].value) + update_protocols(xwl_window); +} + +static void +client_message(xcb_client_message_event_t *event) +{ + if (event->type == xwm.atoms[ATOM_WL_SURFACE_ID].value) { + struct xwl_window *xwl_window; + + if (!(xwl_window = find_window(&xwm.unpaired_windows, event->window))) + return; + + xwl_window->surface_id = event->data.data32[0]; + manage_window(xwl_window); + } +} + +static int +connection_data(int fd, uint32_t mask, void *data) +{ + xcb_generic_event_t *event; + uint32_t count = 0; + + while ((event = xcb_poll_for_event(xwm.connection))) { + switch (event->response_type & ~0x80) { + case XCB_CREATE_NOTIFY: + create_notify((xcb_create_notify_event_t *)event); + break; + case XCB_DESTROY_NOTIFY: + destroy_notify((xcb_destroy_notify_event_t *)event); + break; + case XCB_MAP_REQUEST: + map_request((xcb_map_request_event_t *)event); + break; + case XCB_CONFIGURE_REQUEST: + configure_request((xcb_configure_request_event_t *)event); + break; + case XCB_PROPERTY_NOTIFY: + property_notify((xcb_property_notify_event_t *)event); + break; + case XCB_CLIENT_MESSAGE: + client_message((xcb_client_message_event_t *)event); + break; + } + + free(event); + ++count; + } + + xcb_flush(xwm.connection); + + return count; +} + +bool +xwm_initialize(int fd) +{ + const xcb_setup_t *setup; + xcb_screen_iterator_t screen_iterator; + uint32_t mask; + uint32_t values[1]; + xcb_void_cookie_t change_attributes_cookie, redirect_subwindows_cookie; + xcb_generic_error_t *error; + xcb_intern_atom_cookie_t *ewmh_cookies; + xcb_intern_atom_reply_t *atom_reply; + unsigned index; + const char *name; + const xcb_query_extension_reply_t *composite_extension; + + xwm.connection = xcb_connect_to_fd(fd, NULL); + + if (xcb_connection_has_error(xwm.connection)) { + ERROR("xwm: Could not connect to X server\n"); + goto error0; + } + + xcb_prefetch_extension_data(xwm.connection, &xcb_composite_id); + ewmh_cookies = xcb_ewmh_init_atoms(xwm.connection, &xwm.ewmh); + + if (!ewmh_cookies) { + ERROR("xwm: Failed to initialize EWMH atoms\n"); + goto error1; + } + + for (index = 0; index < ARRAY_LENGTH(xwm.atoms); ++index) { + name = xwm.atoms[index].name; + xwm.atoms[index].cookie = xcb_intern_atom(xwm.connection, 0, strlen(name), name); + } + + setup = xcb_get_setup(xwm.connection); + screen_iterator = xcb_setup_roots_iterator(setup); + xwm.screen = screen_iterator.data; + + /* Try to select for substructure redirect. */ + mask = XCB_CW_EVENT_MASK; + values[0] = XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT; + change_attributes_cookie = xcb_change_window_attributes(xwm.connection, xwm.screen->root, mask, values); + + xwm.source = wl_event_loop_add_fd(swc.event_loop, fd, WL_EVENT_READABLE, &connection_data, NULL); + wl_list_init(&xwm.windows); + wl_list_init(&xwm.unpaired_windows); + + if (!xwm.source) { + ERROR("xwm: Failed to create X connection event source\n"); + goto error2; + } + + composite_extension = xcb_get_extension_data(xwm.connection, &xcb_composite_id); + + if (!composite_extension->present) { + ERROR("xwm: X server does not have composite extension\n"); + goto error3; + } + + redirect_subwindows_cookie = xcb_composite_redirect_subwindows_checked(xwm.connection, xwm.screen->root, XCB_COMPOSITE_REDIRECT_MANUAL); + + if ((error = xcb_request_check(xwm.connection, change_attributes_cookie))) { + ERROR("xwm: Another window manager is running\n"); + free(error); + goto error3; + } + + if ((error = xcb_request_check(xwm.connection, redirect_subwindows_cookie))) { + ERROR("xwm: Could not redirect subwindows of root for compositing\n"); + free(error); + goto error3; + } + + xwm.window = xcb_generate_id(xwm.connection); + xcb_create_window(xwm.connection, 0, xwm.window, xwm.screen->root, + 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY, + XCB_COPY_FROM_PARENT, 0, NULL); + + xcb_ewmh_init_atoms_replies(&xwm.ewmh, ewmh_cookies, &error); + + if (error) { + ERROR("xwm: Failed to get EWMH atom replies: %u\n", error->error_code); + goto error3; + } + + for (index = 0; index < ARRAY_LENGTH(xwm.atoms); ++index) { + atom_reply = xcb_intern_atom_reply(xwm.connection, xwm.atoms[index].cookie, &error); + + if (error) { + ERROR("xwm: Failed to get atom reply: %u\n", error->error_code); + return false; + } + + xwm.atoms[index].value = atom_reply->atom; + free(atom_reply); + } + + xcb_set_selection_owner(xwm.connection, xwm.window, xwm.atoms[ATOM_WM_S0].value, XCB_CURRENT_TIME); + xcb_flush(xwm.connection); + + wl_signal_add(&swc.compositor->signal.new_surface, &new_surface_listener); + + return true; + +error3: + wl_event_source_remove(xwm.source); +error2: + xcb_ewmh_connection_wipe(&xwm.ewmh); +error1: + xcb_disconnect(xwm.connection); +error0: + return false; +} + +void +xwm_finalize(void) +{ + wl_event_source_remove(xwm.source); + xcb_ewmh_connection_wipe(&xwm.ewmh); + xcb_disconnect(xwm.connection); +} diff --git a/libswc/xwm.h b/libswc/xwm.h @@ -0,0 +1,32 @@ +/* swc: libswc/xwm.h + * + * Copyright (c) 2013 Michael Forney + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef SWC_XWM_H +#define SWC_XWM_H + +#include <stdbool.h> + +bool xwm_initialize(int fd); +void xwm_finalize(void); + +#endif