diff --git a/Makefile b/Makefile index f542c88..05b665a 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ WAYLAND_SCANNER=$(shell pkg-config --variable=wayland_scanner wayland-scanner) CFLAGS ?= -g -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare CFLAGS += -I. -DWLR_USE_UNSTABLE -std=c99 -Werror=declaration-after-statement -PKGS = wlroots wayland-server xkbcommon +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/dwl.c b/dwl.c index e4ab735..74f368e 100644 --- a/dwl.c +++ b/dwl.c @@ -32,6 +32,7 @@ #include #include #include +#include #include /* macros */ @@ -46,6 +47,8 @@ /* enums */ enum { CurNormal, CurMove, CurResize }; /* cursor */ +enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar, + NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */ typedef union { int i; @@ -70,6 +73,7 @@ typedef struct { struct wlr_xdg_surface *xdg_surface; struct wlr_xwayland_surface *xwayland_surface; }; + struct wl_listener activate; struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; @@ -148,6 +152,7 @@ struct render_data { }; /* function declarations */ +static void activate(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); @@ -164,11 +169,13 @@ 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); static void destroynotify(struct wl_listener *listener, void *data); +static void destroynotifyindependent(struct wl_listener *listener, void *data); static void destroyxdeco(struct wl_listener *listener, void *data); static Monitor *dirtomon(int dir); static void focusclient(Client *c, struct wlr_surface *surface, int lift); static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); +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); @@ -178,6 +185,7 @@ static void keypressmod(struct wl_listener *listener, void *data); static void killclient(const Arg *arg); static Client *lastfocused(void); static void maprequest(struct wl_listener *listener, void *data); +static void maprequestindependent(struct wl_listener *listener, void *data); static void motionabsolute(struct wl_listener *listener, void *data); static void motionnotify(uint32_t time); static void motionrelative(struct wl_listener *listener, void *data); @@ -207,7 +215,10 @@ 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 unmapnotifyindependent(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); @@ -224,6 +235,7 @@ static struct wlr_xdg_shell *xdg_shell; static struct wl_list clients; /* tiling order */ static struct wl_list fstack; /* focus order */ static struct wl_list stack; /* stacking z-order */ +static struct wl_list independents; static struct wlr_xdg_decoration_manager_v1 *xdeco_mgr; static struct wlr_cursor *cursor; @@ -240,6 +252,8 @@ 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}; @@ -254,11 +268,21 @@ 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}; +static struct wl_listener xwayland_ready = {.notify = xwaylandready}; /* configuration, allows nested code to access above variables */ #include "config.h" /* function implementations */ +void +activate(struct wl_listener *listener, void *data) +{ + Client *c = wl_container_of(listener, c, activate); + + if (c && c->isx11) + wlr_xwayland_surface_activate(c->xwayland_surface, 1); +} + void applybounds(Client *c, struct wlr_box *bbox) { @@ -307,6 +331,7 @@ applyrules(Client *c) mon = m; } } + updatewindowtype(c); setmon(c, mon, newtags); } @@ -541,19 +566,37 @@ 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->xwayland_surface = xwayland_surface; - c->isx11 = 1; - c->bw = borderpx; + if (xwayland_surface->override_redirect) { - /* 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->destroy.notify = destroynotify; - wl_signal_add(&xwayland_surface->events.destroy, &c->destroy); + /* Allocate an independent for this surface */ + c = xwayland_surface->data = calloc(1, sizeof(*c)); + c->xwayland_surface = xwayland_surface; + + /* Listen to the various events it can emit */ + c->map.notify = maprequestindependent; + wl_signal_add(&xwayland_surface->events.map, &c->map); + c->unmap.notify = unmapnotifyindependent; + wl_signal_add(&xwayland_surface->events.unmap, &c->unmap); + c->destroy.notify = destroynotifyindependent; + wl_signal_add(&xwayland_surface->events.destroy, &c->destroy); + } else { + + /* Allocate a Client for this surface */ + c = xwayland_surface->data = calloc(1, sizeof(*c)); + c->xwayland_surface = xwayland_surface; + c->isx11 = 1; + c->bw = borderpx; + + /* Listen to the various events it can emit */ + c->activate.notify = activate; + wl_signal_add(&xwayland_surface->events.request_activate, &c->activate); + 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->destroy.notify = destroynotify; + wl_signal_add(&xwayland_surface->events.destroy, &c->destroy); + } } void @@ -603,6 +646,17 @@ destroynotify(struct wl_listener *listener, void *data) free(c); } +void +destroynotifyindependent(struct wl_listener *listener, void *data) +{ + /* Called when the surface is destroyed and should never be shown again. */ + Client *c = wl_container_of(listener, c, destroy); + wl_list_remove(&c->map.link); + wl_list_remove(&c->unmap.link); + wl_list_remove(&c->destroy.link); + free(c); +} + void destroyxdeco(struct wl_listener *listener, void *data) { @@ -676,8 +730,8 @@ focusclient(Client *c, struct wlr_surface *surface, int lift) } /* - * If the focused toplevel has changed, deactivate the old one and - * activate the new one. This lets the clients know to repaint + * If the focused toplevel has changed, deactivate the old one. Always + * activate the current one. This lets the clients know to repaint * accordingly, e.g. show/hide a caret. */ if (tl != ptl && ptl) { @@ -686,7 +740,7 @@ focusclient(Client *c, struct wlr_surface *surface, int lift) else wlr_xdg_toplevel_set_activated(ptl->xdg_surface, 0); } - if (tl != ptl && tl) { + if (tl) { if (tl->isx11) wlr_xwayland_surface_activate(tl->xwayland_surface, 1); else @@ -731,6 +785,23 @@ focusstack(const Arg *arg) focusclient(c, NULL, 1); } +Atom +getatom(xcb_connection_t *xc, const char *name) +{ + Atom atom; + xcb_generic_error_t *error; + xcb_intern_atom_cookie_t cookie; + xcb_intern_atom_reply_t *reply; + + cookie = xcb_intern_atom(xc, 0, strlen(name), name); + reply = xcb_intern_atom_reply(xc, cookie, &error); + if (reply != NULL && error == NULL) + atom = reply->atom; + free(reply); + + return atom; +} + void getxdecomode(struct wl_listener *listener, void *data) { @@ -890,6 +961,15 @@ maprequest(struct wl_listener *listener, void *data) applyrules(c); } +void +maprequestindependent(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); + /* Insert this independent into independents lists. */ + wl_list_insert(&independents, &c->link); +} + void motionabsolute(struct wl_listener *listener, void *data) { @@ -1131,9 +1211,38 @@ renderclients(Monitor *m, struct timespec *now) } } +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->xwayland_surface->x; + geom.y = c->xwayland_surface->y; + geom.width = c->xwayland_surface->width; + geom.height = c->xwayland_surface->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->xwayland_surface->x; + rdata.y = c->xwayland_surface->y; + + wlr_surface_for_each_surface(c->xwayland_surface->surface, render, &rdata); + } +} + void rendermon(struct wl_listener *listener, void *data) { + struct wlr_output *output = data; + /* This function is called every time an output is ready to display a frame, * generally at the output's refresh rate (e.g. 60Hz). */ Monitor *m = wl_container_of(listener, m, frame); @@ -1150,6 +1259,7 @@ rendermon(struct wl_listener *listener, void *data) wlr_renderer_clear(drw, rootcolor); renderclients(m, &now); + renderindependents(output, &now); /* 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 @@ -1423,6 +1533,7 @@ setup(void) wl_list_init(&clients); wl_list_init(&fstack); wl_list_init(&stack); + wl_list_init(&independents); xdg_shell = wlr_xdg_shell_create(dpy); wl_signal_add(&xdg_shell->events.new_surface, &new_xdg_surface); @@ -1484,6 +1595,7 @@ setup(void) */ xwayland = wlr_xwayland_create(dpy, compositor, true); if (xwayland) { + wl_signal_add(&xwayland->events.ready, &xwayland_ready); wl_signal_add(&xwayland->events.new_surface, &new_xwayland_surface); setenv("DISPLAY", xwayland->display_name, true); @@ -1605,6 +1717,49 @@ unmapnotify(struct wl_listener *listener, void *data) wl_list_remove(&c->slink); } +void +unmapnotifyindependent(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); +} + +void +updatewindowtype(Client *c) +{ + size_t i; + + if (c->isx11) + for (i = 0; i < c->xwayland_surface->window_type_len; i++) + if (c->xwayland_surface->window_type[i] == netatom[NetWMWindowTypeDialog] || + c->xwayland_surface->window_type[i] == netatom[NetWMWindowTypeSplash] || + c->xwayland_surface->window_type[i] == netatom[NetWMWindowTypeToolbar] || + c->xwayland_surface->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 */ + 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) {