xwayland: floating windows support, unmanaged (dropdown menu) support, xwayland shares the seat

This commit is contained in:
Alexander Courtis 2020-07-24 12:36:09 +10:00
parent 53798dfbff
commit 8248814983
2 changed files with 171 additions and 16 deletions

View File

@ -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)))

161
dwl.c
View File

@ -32,6 +32,7 @@
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h>
#include <wlr/xwayland.h>
#include <X11/Xlib.h>
#include <xkbcommon/xkbcommon.h>
/* 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,6 +566,21 @@ createnotifyx11(struct wl_listener *listener, void *data)
struct wlr_xwayland_surface *xwayland_surface = data;
Client *c;
if (xwayland_surface->override_redirect) {
/* 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;
@ -548,12 +588,15 @@ createnotifyx11(struct wl_listener *listener, void *data)
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)
{