From e3e9504fcd0b1b38037aa9dffd04ac0de1ce5e82 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Mon, 10 Aug 2020 12:50:56 -0500 Subject: [PATCH] make XWayland support optional --- Makefile | 7 +- config.mk | 5 + dwl.c | 312 +++++++++++++++++++++++++++++++----------------------- 3 files changed, 191 insertions(+), 133 deletions(-) create mode 100644 config.mk diff --git a/Makefile b/Makefile index 7c90456..56ab751 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,10 @@ +include config.mk + +CFLAGS += -I. -DWLR_USE_UNSTABLE -std=c99 -Werror=declaration-after-statement + WAYLAND_PROTOCOLS=$(shell pkg-config --variable=pkgdatadir wayland-protocols) WAYLAND_SCANNER=$(shell pkg-config --variable=wayland_scanner wayland-scanner) -CFLAGS ?= -g -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare -Wno-error=unused-function -CFLAGS += -I. -DWLR_USE_UNSTABLE -std=c99 -Werror=declaration-after-statement - PKGS = wlroots wayland-server xcb xkbcommon CFLAGS += $(foreach p,$(PKGS),$(shell pkg-config --cflags $(p))) LDLIBS += $(foreach p,$(PKGS),$(shell pkg-config --libs $(p))) diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..a101f23 --- /dev/null +++ b/config.mk @@ -0,0 +1,5 @@ +# Default compile flags (overridable by environment) +CFLAGS ?= -g -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare -Wno-error=unused-function + +# Uncomment to build XWayland support +#CFLAGS += -DXWAYLAND diff --git a/dwl.c b/dwl.c index fbdfbe0..d5559e6 100644 --- a/dwl.c +++ b/dwl.c @@ -33,9 +33,11 @@ #include #include #include -#include -#include #include +#ifdef XWAYLAND +#include +#include +#endif /* macros */ #define MAX(A, B) ((A) > (B) ? (A) : (B)) @@ -45,13 +47,19 @@ #define LENGTH(X) (sizeof X / sizeof X[0]) #define END(A) ((A) + LENGTH(A)) #define TAGMASK ((1 << LENGTH(tags)) - 1) +#ifdef XWAYLAND #define WLR_SURFACE(C) ((C)->type != XDGShell ? (C)->surface.xwayland->surface : (C)->surface.xdg->surface) +#else +#define WLR_SURFACE(C) ((C)->surface.xdg->surface) +#endif /* enums */ enum { CurNormal, CurMove, CurResize }; /* cursor */ enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar, NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */ +#ifdef XWAYLAND enum { XDGShell, X11Managed, X11Unmanaged }; /* client types */ +#endif typedef union { int i; @@ -74,16 +82,22 @@ typedef struct { struct wl_list slink; union { struct wlr_xdg_surface *xdg; +#ifdef XWAYLAND struct wlr_xwayland_surface *xwayland; +#endif } surface; +#ifdef XWAYLAND struct wl_listener activate; +#endif struct wl_listener commit; struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; struct wlr_box geom; /* layout-relative, includes border */ Monitor *mon; +#ifdef XWAYLAND unsigned int type; +#endif int bw; unsigned int tags; int isfloating; @@ -157,7 +171,6 @@ struct render_data { }; /* function declarations */ -static void activatex11(struct wl_listener *listener, void *data); static void applybounds(Client *c, struct wlr_box *bbox); static void applyrules(Client *c); static void arrange(Monitor *m); @@ -169,7 +182,6 @@ static void cleanupmon(struct wl_listener *listener, void *data); static void createkeyboard(struct wlr_input_device *device); static void createmon(struct wl_listener *listener, void *data); static void createnotify(struct wl_listener *listener, void *data); -static void createnotifyx11(struct wl_listener *listener, void *data); static void createpointer(struct wlr_input_device *device); static void createxdeco(struct wl_listener *listener, void *data); static void cursorframe(struct wl_listener *listener, void *data); @@ -180,7 +192,6 @@ static void focusclient(Client *old, Client *c, int lift); static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); static Client *focustop(Monitor *m); -static Atom getatom(xcb_connection_t *xc, const char *name); static void getxdecomode(struct wl_listener *listener, void *data); static void incnmaster(const Arg *arg); static void inputdevice(struct wl_listener *listener, void *data); @@ -219,9 +230,7 @@ static void tile(Monitor *m); static void togglefloating(const Arg *arg); static void toggletag(const Arg *arg); static void toggleview(const Arg *arg); -static void updatewindowtype(Client *c); static void unmapnotify(struct wl_listener *listener, void *data); -static void xwaylandready(struct wl_listener *listener, void *data); static void view(const Arg *arg); static Client *xytoclient(double x, double y); static Monitor *xytomon(double x, double y); @@ -233,7 +242,6 @@ static struct wl_display *dpy; static struct wlr_backend *backend; static struct wlr_renderer *drw; static struct wlr_compositor *compositor; -static struct wlr_xwayland *xwayland; static struct wlr_xdg_shell *xdg_shell; static struct wl_list clients; /* tiling order */ @@ -256,8 +264,6 @@ static struct wlr_box sgeom; static struct wl_list mons; static Monitor *selmon; -static Atom netatom[NetLast]; - /* global event handlers */ static struct wl_listener cursor_axis = {.notify = axisnotify}; static struct wl_listener cursor_button = {.notify = buttonpress}; @@ -268,11 +274,22 @@ static struct wl_listener new_input = {.notify = inputdevice}; static struct wl_listener new_output = {.notify = createmon}; static struct wl_listener new_xdeco = {.notify = createxdeco}; static struct wl_listener new_xdg_surface = {.notify = createnotify}; -static struct wl_listener new_xwayland_surface = {.notify = createnotifyx11}; static struct wl_listener request_cursor = {.notify = setcursor}; static struct wl_listener request_set_psel = {.notify = setpsel}; static struct wl_listener request_set_sel = {.notify = setsel}; + +#ifdef XWAYLAND +static void activatex11(struct wl_listener *listener, void *data); +static void createnotifyx11(struct wl_listener *listener, void *data); +static Atom getatom(xcb_connection_t *xc, const char *name); +static void renderindependents(struct wlr_output *output, struct timespec *now); +static void updatewindowtype(Client *c); +static void xwaylandready(struct wl_listener *listener, void *data); +static struct wl_listener new_xwayland_surface = {.notify = createnotifyx11}; static struct wl_listener xwayland_ready = {.notify = xwaylandready}; +static struct wlr_xwayland *xwayland; +static Atom netatom[NetLast]; +#endif /* configuration, allows nested code to access above variables */ #include "config.h" @@ -281,16 +298,6 @@ static struct wl_listener xwayland_ready = {.notify = xwaylandready}; struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; /* function implementations */ -void -activatex11(struct wl_listener *listener, void *data) -{ - Client *c = wl_container_of(listener, c, activate); - - /* Only "managed" windows can be activated */ - if (c->type == X11Managed) - wlr_xwayland_surface_activate(c->surface.xwayland, 1); -} - void applybounds(Client *c, struct wlr_box *bbox) { @@ -318,10 +325,17 @@ applyrules(Client *c) /* rule matching */ c->isfloating = 0; - appid = c->type != XDGShell ? c->surface.xwayland->class : - c->surface.xdg->toplevel->app_id; - title = c->type != XDGShell ? c->surface.xwayland->title : - c->surface.xdg->toplevel->title; +#ifdef XWAYLAND + updatewindowtype(c); + if (c->type != XDGShell) { + appid = c->surface.xwayland->class; + title = c->surface.xwayland->title; + } else +#endif + { + appid = c->surface.xdg->toplevel->app_id; + title = c->surface.xdg->toplevel->title; + } if (!appid) appid = broken; if (!title) @@ -338,7 +352,6 @@ applyrules(Client *c) mon = m; } } - updatewindowtype(c); setmon(c, mon, newtags); } @@ -552,7 +565,6 @@ createnotify(struct wl_listener *listener, void *data) /* Allocate a Client for this surface */ c = xdg_surface->data = calloc(1, sizeof(*c)); c->surface.xdg = xdg_surface; - c->type = XDGShell; c->bw = borderpx; /* Tell the client not to try anything fancy */ @@ -570,29 +582,6 @@ createnotify(struct wl_listener *listener, void *data) wl_signal_add(&xdg_surface->events.destroy, &c->destroy); } -void -createnotifyx11(struct wl_listener *listener, void *data) -{ - struct wlr_xwayland_surface *xwayland_surface = data; - Client *c; - - /* Allocate a Client for this surface */ - c = xwayland_surface->data = calloc(1, sizeof(*c)); - c->surface.xwayland = xwayland_surface; - c->type = xwayland_surface->override_redirect ? X11Unmanaged : X11Managed; - c->bw = borderpx; - - /* Listen to the various events it can emit */ - c->map.notify = maprequest; - wl_signal_add(&xwayland_surface->events.map, &c->map); - c->unmap.notify = unmapnotify; - wl_signal_add(&xwayland_surface->events.unmap, &c->unmap); - c->activate.notify = activatex11; - wl_signal_add(&xwayland_surface->events.request_activate, &c->activate); - c->destroy.notify = destroynotify; - wl_signal_add(&xwayland_surface->events.destroy, &c->destroy); -} - void createpointer(struct wlr_input_device *device) { @@ -637,10 +626,12 @@ destroynotify(struct wl_listener *listener, void *data) wl_list_remove(&c->map.link); wl_list_remove(&c->unmap.link); wl_list_remove(&c->destroy.link); - if (c->type == XDGShell) - wl_list_remove(&c->commit.link); +#ifdef XWAYLAND if (c->type == X11Managed) wl_list_remove(&c->activate.link); + else if (c->type == XDGShell) +#endif + wl_list_remove(&c->commit.link); free(c); } @@ -688,9 +679,11 @@ focusclient(Client *old, Client *c, int lift) /* Deactivate old client if focus is changing */ if (c != old && old) { +#ifdef XWAYLAND if (old->type != XDGShell) wlr_xwayland_surface_activate(old->surface.xwayland, 0); else +#endif wlr_xdg_toplevel_set_activated(old->surface.xdg, 0); } @@ -711,9 +704,11 @@ focusclient(Client *old, Client *c, int lift) selmon = c->mon; /* Activate the new client */ +#ifdef XWAYLAND if (c->type != XDGShell) wlr_xwayland_surface_activate(c->surface.xwayland, 1); else +#endif wlr_xdg_toplevel_set_activated(c->surface.xdg, 1); } @@ -762,21 +757,6 @@ focustop(Monitor *m) return NULL; } -Atom -getatom(xcb_connection_t *xc, const char *name) -{ - Atom atom = 0; - xcb_intern_atom_cookie_t cookie; - xcb_intern_atom_reply_t *reply; - - cookie = xcb_intern_atom(xc, 0, strlen(name), name); - if ((reply = xcb_intern_atom_reply(xc, cookie, NULL))) - atom = reply->atom; - free(reply); - - return atom; -} - void getxdecomode(struct wl_listener *listener, void *data) { @@ -895,9 +875,11 @@ killclient(const Arg *arg) if (!sel) return; +#ifdef XWAYLAND if (sel->type != XDGShell) wlr_xwayland_surface_close(sel->surface.xwayland); else +#endif wlr_xdg_toplevel_send_close(sel->surface.xdg); } @@ -907,23 +889,28 @@ maprequest(struct wl_listener *listener, void *data) /* Called when the surface is mapped, or ready to display on-screen. */ Client *c = wl_container_of(listener, c, map); +#ifdef XWAYLAND if (c->type == X11Unmanaged) { /* Insert this independent into independents lists. */ wl_list_insert(&independents, &c->link); return; } +#endif /* Insert this client into client lists. */ wl_list_insert(&clients, &c->link); wl_list_insert(&fstack, &c->flink); wl_list_insert(&stack, &c->slink); +#ifdef XWAYLAND if (c->type != XDGShell) { c->geom.x = c->surface.xwayland->x; c->geom.y = c->surface.xwayland->y; c->geom.width = c->surface.xwayland->width + 2 * c->bw; c->geom.height = c->surface.xwayland->height + 2 * c->bw; - } else { + } else +#endif + { wlr_xdg_surface_get_geometry(c->surface.xdg, &c->geom); c->geom.width += 2 * c->bw; c->geom.height += 2 * c->bw; @@ -985,11 +972,13 @@ motionnotify(uint32_t time) /* Otherwise, find the client under the pointer and send the event along. */ if ((c = xytoclient(cursor->x, cursor->y))) { +#ifdef XWAYLAND if (c->type != XDGShell) surface = wlr_surface_surface_at(c->surface.xwayland->surface, cursor->x - c->geom.x - c->bw, cursor->y - c->geom.y - c->bw, &sx, &sy); else +#endif surface = wlr_xdg_surface_surface_at(c->surface.xdg, cursor->x - c->geom.x - c->bw, cursor->y - c->geom.y - c->bw, &sx, &sy); @@ -1183,39 +1172,15 @@ renderclients(Monitor *m, struct timespec *now) rdata.when = now; rdata.x = c->geom.x + c->bw; rdata.y = c->geom.y + c->bw; +#ifdef XWAYLAND if (c->type != XDGShell) wlr_surface_for_each_surface(c->surface.xwayland->surface, render, &rdata); else +#endif wlr_xdg_surface_for_each_surface(c->surface.xdg, render, &rdata); } } -void -renderindependents(struct wlr_output *output, struct timespec *now) -{ - Client *c; - struct render_data rdata; - struct wlr_box geom; - - wl_list_for_each_reverse(c, &independents, link) { - geom.x = c->surface.xwayland->x; - geom.y = c->surface.xwayland->y; - geom.width = c->surface.xwayland->width; - geom.height = c->surface.xwayland->height; - - /* Only render visible clients which show on this output */ - if (!wlr_output_layout_intersects(output_layout, output, &geom)) - continue; - - rdata.output = output; - rdata.when = now; - rdata.x = c->surface.xwayland->x; - rdata.y = c->surface.xwayland->y; - - wlr_surface_for_each_surface(c->surface.xwayland->surface, render, &rdata); - } -} - void rendermon(struct wl_listener *listener, void *data) { @@ -1247,7 +1212,9 @@ rendermon(struct wl_listener *listener, void *data) wlr_renderer_clear(drw, rootcolor); renderclients(m, &now); +#ifdef XWAYLAND renderindependents(m->wlr_output, &now); +#endif /* Hardware cursors are rendered by the GPU on a separate plane, and can be * moved around without re-rendering what's beneath them - which is more @@ -1280,11 +1247,13 @@ resize(Client *c, int x, int y, int w, int h, int interact) c->geom.height = h; applybounds(c, bbox); /* wlroots makes this a no-op if size hasn't changed */ +#ifdef XWAYLAND if (c->type != XDGShell) wlr_xwayland_surface_configure(c->surface.xwayland, c->geom.x, c->geom.y, c->geom.width - 2 * c->bw, c->geom.height - 2 * c->bw); else +#endif c->resize = wlr_xdg_toplevel_set_size(c->surface.xdg, c->geom.width - 2 * c->bw, c->geom.height - 2 * c->bw); } @@ -1579,6 +1548,7 @@ setup(void) wl_signal_add(&seat->events.request_set_primary_selection, &request_set_psel); +#ifdef XWAYLAND /* * Initialise the XWayland X server. * It will be started when the first X client is started. @@ -1592,6 +1562,7 @@ setup(void) } else { fprintf(stderr, "failed to setup XWayland X server, continuing without it\n"); } +#endif } void @@ -1703,49 +1674,15 @@ unmapnotify(struct wl_listener *listener, void *data) /* Called when the surface is unmapped, and should no longer be shown. */ Client *c = wl_container_of(listener, c, unmap); wl_list_remove(&c->link); +#ifdef XWAYLAND if (c->type == X11Unmanaged) return; +#endif setmon(c, NULL, 0); wl_list_remove(&c->flink); wl_list_remove(&c->slink); } -void -updatewindowtype(Client *c) -{ - size_t i; - - if (c->type != XDGShell) - for (i = 0; i < c->surface.xwayland->window_type_len; i++) - if (c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeDialog] || - c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeSplash] || - c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeToolbar] || - c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeUtility]) - c->isfloating = 1; -} - -void -xwaylandready(struct wl_listener *listener, void *data) { - xcb_connection_t *xc = xcb_connect(xwayland->display_name, NULL); - int err = xcb_connection_has_error(xc); - if (err) { - fprintf(stderr, "xcb_connect to X server failed with code %d\n. Continuing with degraded functionality.\n", err); - return; - } - - /* Collect atoms we are interested in. If getatom returns 0, we will - * not detect that window type. */ - netatom[NetWMWindowTypeDialog] = getatom(xc, "_NET_WM_WINDOW_TYPE_DIALOG"); - netatom[NetWMWindowTypeSplash] = getatom(xc, "_NET_WM_WINDOW_TYPE_SPLASH"); - netatom[NetWMWindowTypeUtility] = getatom(xc, "_NET_WM_WINDOW_TYPE_TOOLBAR"); - netatom[NetWMWindowTypeToolbar] = getatom(xc, "_NET_WM_WINDOW_TYPE_UTILITY"); - - /* assign the one and only seat */ - wlr_xwayland_set_seat(xwayland, seat); - - xcb_disconnect(xc); -} - void view(const Arg *arg) { @@ -1810,6 +1747,119 @@ zoom(const Arg *arg) arrange(selmon); } +#ifdef XWAYLAND +void +activatex11(struct wl_listener *listener, void *data) +{ + Client *c = wl_container_of(listener, c, activate); + + /* Only "managed" windows can be activated */ + if (c->type == X11Managed) + wlr_xwayland_surface_activate(c->surface.xwayland, 1); +} + +void +createnotifyx11(struct wl_listener *listener, void *data) +{ + struct wlr_xwayland_surface *xwayland_surface = data; + Client *c; + + /* Allocate a Client for this surface */ + c = xwayland_surface->data = calloc(1, sizeof(*c)); + c->surface.xwayland = xwayland_surface; + c->type = xwayland_surface->override_redirect ? X11Unmanaged : X11Managed; + c->bw = borderpx; + + /* Listen to the various events it can emit */ + c->map.notify = maprequest; + wl_signal_add(&xwayland_surface->events.map, &c->map); + c->unmap.notify = unmapnotify; + wl_signal_add(&xwayland_surface->events.unmap, &c->unmap); + c->activate.notify = activatex11; + wl_signal_add(&xwayland_surface->events.request_activate, &c->activate); + c->destroy.notify = destroynotify; + wl_signal_add(&xwayland_surface->events.destroy, &c->destroy); +} + +Atom +getatom(xcb_connection_t *xc, const char *name) +{ + Atom atom = 0; + xcb_intern_atom_cookie_t cookie; + xcb_intern_atom_reply_t *reply; + + cookie = xcb_intern_atom(xc, 0, strlen(name), name); + if ((reply = xcb_intern_atom_reply(xc, cookie, NULL))) + atom = reply->atom; + free(reply); + + return atom; +} + +void +renderindependents(struct wlr_output *output, struct timespec *now) +{ + Client *c; + struct render_data rdata; + struct wlr_box geom; + + wl_list_for_each_reverse(c, &independents, link) { + geom.x = c->surface.xwayland->x; + geom.y = c->surface.xwayland->y; + geom.width = c->surface.xwayland->width; + geom.height = c->surface.xwayland->height; + + /* Only render visible clients which show on this output */ + if (!wlr_output_layout_intersects(output_layout, output, &geom)) + continue; + + rdata.output = output; + rdata.when = now; + rdata.x = c->surface.xwayland->x; + rdata.y = c->surface.xwayland->y; + + wlr_surface_for_each_surface(c->surface.xwayland->surface, render, &rdata); + } +} + +void +updatewindowtype(Client *c) +{ + size_t i; + + if (c->type != XDGShell) + for (i = 0; i < c->surface.xwayland->window_type_len; i++) + if (c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeDialog] || + c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeSplash] || + c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeToolbar] || + c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeUtility]) + c->isfloating = 1; +} + +void +xwaylandready(struct wl_listener *listener, void *data) +{ + xcb_connection_t *xc = xcb_connect(xwayland->display_name, NULL); + int err = xcb_connection_has_error(xc); + if (err) { + fprintf(stderr, "xcb_connect to X server failed with code %d\n. Continuing with degraded functionality.\n", err); + return; + } + + /* Collect atoms we are interested in. If getatom returns 0, we will + * not detect that window type. */ + netatom[NetWMWindowTypeDialog] = getatom(xc, "_NET_WM_WINDOW_TYPE_DIALOG"); + netatom[NetWMWindowTypeSplash] = getatom(xc, "_NET_WM_WINDOW_TYPE_SPLASH"); + netatom[NetWMWindowTypeUtility] = getatom(xc, "_NET_WM_WINDOW_TYPE_TOOLBAR"); + netatom[NetWMWindowTypeToolbar] = getatom(xc, "_NET_WM_WINDOW_TYPE_UTILITY"); + + /* assign the one and only seat */ + wlr_xwayland_set_seat(xwayland, seat); + + xcb_disconnect(xc); +} +#endif + int main(int argc, char *argv[]) { @@ -1854,7 +1904,9 @@ main(int argc, char *argv[]) run(startup_cmd); /* Once wl_display_run returns, we shut down the server. */ +#ifdef XWAYLAND wlr_xwayland_destroy(xwayland); +#endif wl_display_destroy_clients(dpy); wl_display_destroy(dpy);