Skip to content
This repository has been archived by the owner on Jan 26, 2024. It is now read-only.

xwayland: add server and basic window functionality #10

Merged
merged 4 commits into from
Jul 21, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 125 additions & 35 deletions dwl.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <wlr/types/wlr_xdg_output_v1.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h>
#include <wlr/xwayland.h>
#include <xkbcommon/xkbcommon.h>

/* macros */
Expand All @@ -41,6 +42,7 @@
#define LENGTH(X) (sizeof X / sizeof X[0])
#define END(A) ((A) + LENGTH(A))
#define TAGMASK ((1 << LENGTH(tags)) - 1)
#define WLR_SURFACE(C) (c->isxdg ? c->xdg_surface->surface : c->xwayland_surface->surface)

/* enums */
enum { CurNormal, CurMove, CurResize }; /* cursor */
Expand All @@ -64,11 +66,15 @@ typedef struct {
struct wl_list link;
struct wl_list flink;
struct wl_list slink;
struct wlr_xdg_surface *xdg_surface;
union {
struct wlr_xdg_surface *xdg_surface;
struct wlr_xwayland_surface *xwayland_surface;
};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The anonymous-union-and-a-boolean seemed the simplest approach, and sway is using it.

Happy to change if anything better is suggested.

struct wl_listener map;
struct wl_listener unmap;
struct wl_listener destroy;
struct wlr_box geom; /* layout-relative, includes border */
int isxdg;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could become an enum, when we add layer shell.

Monitor *mon;
int bw;
unsigned int tags;
Expand Down Expand Up @@ -148,7 +154,8 @@ static void buttonpress(struct wl_listener *listener, void *data);
static void chvt(const Arg *arg);
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 createnotifyxdg(struct wl_listener *listener, void *data);
static void createnotifyxwayland(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);
Expand Down Expand Up @@ -205,6 +212,8 @@ static const char broken[] = "broken";
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 */
Expand Down Expand Up @@ -235,7 +244,8 @@ static struct wl_listener cursor_motion_absolute = {.notify = motionabsolute};
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_xdg_surface = {.notify = createnotifyxdg};
static struct wl_listener new_xwayland_surface = {.notify = createnotifyxwayland};
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};
Expand Down Expand Up @@ -271,10 +281,15 @@ applyrules(Client *c)

/* rule matching */
c->isfloating = 0;
if (!(appid = c->xdg_surface->toplevel->app_id))
appid = broken;
if (!(title = c->xdg_surface->toplevel->title))
title = broken;
if (c->isxdg) {
if (!(appid = c->xdg_surface->toplevel->app_id))
appid = broken;
if (!(title = c->xdg_surface->toplevel->title))
title = broken;
} else {
if (!(title = c->xwayland_surface->title))
title = broken;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

xwayland does not have an app_id

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does appear to still have instance/class though

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does appear to still have instance/class though

We can add those when we use them e.g. rules based on class.

}

for (r = rules; r < END(rules); r++) {
if ((!r->title || strstr(title, r->title))
Expand Down Expand Up @@ -328,9 +343,14 @@ buttonpress(struct wl_listener *listener, void *data)
case WLR_BUTTON_PRESSED:;
/* Change focus if the button was _pressed_ over a client */
if ((c = xytoclient(cursor->x, cursor->y))) {
surface = wlr_xdg_surface_surface_at(c->xdg_surface,
cursor->x - c->geom.x - c->bw,
cursor->y - c->geom.y - c->bw, NULL, NULL);
if (c->isxdg)
surface = wlr_xdg_surface_surface_at(c->xdg_surface,
cursor->x - c->geom.x - c->bw,
cursor->y - c->geom.y - c->bw, NULL, NULL);
else
surface = wlr_surface_surface_at(c->xwayland_surface->surface,
cursor->x - c->geom.x - c->bw,
cursor->y - c->geom.y - c->bw, NULL, NULL);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cases like these intentionally invoke different methods, as the xdg version has more functionality.

focusclient(c, surface, 1);
}

Expand Down Expand Up @@ -458,7 +478,7 @@ createmon(struct wl_listener *listener, void *data)
}

void
createnotify(struct wl_listener *listener, void *data)
createnotifyxdg(struct wl_listener *listener, void *data)
{
/* This event is raised when wlr_xdg_shell receives a new xdg surface from a
* client, either a toplevel (application window) or popup. */
Expand All @@ -471,6 +491,7 @@ createnotify(struct wl_listener *listener, void *data)
/* Allocate a Client for this surface */
c = xdg_surface->data = calloc(1, sizeof(*c));
c->xdg_surface = xdg_surface;
c->isxdg = 1;
c->bw = borderpx;

/* Tell the client not to try anything fancy */
Expand All @@ -486,6 +507,27 @@ createnotify(struct wl_listener *listener, void *data)
wl_signal_add(&xdg_surface->events.destroy, &c->destroy);
}

void
createnotifyxwayland(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->isxdg = 0;
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->destroy.notify = destroynotify;
wl_signal_add(&xwayland_surface->events.destroy, &c->destroy);
}

void
createpointer(struct wlr_input_device *device)
{
Expand Down Expand Up @@ -566,16 +608,16 @@ focusclient(Client *c, struct wlr_surface *surface, int lift)
Client *sel = selclient();
struct wlr_keyboard *kb;
/* Previous and new xdg toplevel surfaces */
struct wlr_xdg_surface *ptl = sel ? sel->xdg_surface : NULL;
struct wlr_xdg_surface *tl = c ? c->xdg_surface : NULL;
Client *ptl = sel;
Client *tl = c;
/* Previously focused surface */
struct wlr_surface *psurface = seat->keyboard_state.focused_surface;

if (c) {
/* assert(VISIBLEON(c, c->mon)); ? */
/* Use top-level wlr_surface if nothing more specific given */
if (!surface)
surface = c->xdg_surface->surface;
surface = WLR_SURFACE(c);

/* Focus the correct monitor (must come after selclient!) */
selmon = c->mon;
Expand Down Expand Up @@ -610,10 +652,18 @@ focusclient(Client *c, struct wlr_surface *surface, int lift)
* activate the new one. This lets the clients know to repaint
* accordingly, e.g. show/hide a caret.
*/
if (tl != ptl && ptl)
wlr_xdg_toplevel_set_activated(ptl, 0);
if (tl != ptl && tl)
wlr_xdg_toplevel_set_activated(tl, 1);
if (tl != ptl && ptl) {
if (ptl->isxdg)
wlr_xdg_toplevel_set_activated(ptl->xdg_surface, 0);
else
wlr_xwayland_surface_activate(ptl->xwayland_surface, 0);
}
if (tl != ptl && tl) {
if (tl->isxdg)
wlr_xdg_toplevel_set_activated(tl->xdg_surface, 1);
else
wlr_xwayland_surface_activate(tl->xwayland_surface, 1);
}
}

void
Expand Down Expand Up @@ -783,9 +833,17 @@ maprequest(struct wl_listener *listener, void *data)
wl_list_insert(&clients, &c->link);
wl_list_insert(&fstack, &c->flink);
wl_list_insert(&stack, &c->slink);
wlr_xdg_surface_get_geometry(c->xdg_surface, &c->geom);
c->geom.width += 2 * c->bw;
c->geom.height += 2 * c->bw;

if (c->isxdg) {
wlr_xdg_surface_get_geometry(c->xdg_surface, &c->geom);
c->geom.width += 2 * c->bw;
c->geom.height += 2 * c->bw;
} else {
c->geom.x = c->xwayland_surface->x;
c->geom.y = c->xwayland_surface->y;
c->geom.width = c->xwayland_surface->width + 2 * c->bw;
c->geom.height = c->xwayland_surface->height + 2 * c->bw;
}

/* Set initial monitor, tags, floating status, and focus */
applyrules(c);
Expand Down Expand Up @@ -830,10 +888,16 @@ motionnotify(uint32_t time)
}

/* Otherwise, find the client under the pointer and send the event along. */
if ((c = xytoclient(cursor->x, cursor->y)))
surface = wlr_xdg_surface_surface_at(c->xdg_surface,
cursor->x - c->geom.x - c->bw,
cursor->y - c->geom.y - c->bw, &sx, &sy);
if ((c = xytoclient(cursor->x, cursor->y))) {
if (c->isxdg)
surface = wlr_xdg_surface_surface_at(c->xdg_surface,
cursor->x - c->geom.x - c->bw,
cursor->y - c->geom.y - c->bw, &sx, &sy);
else
surface = wlr_surface_surface_at(c->xwayland_surface->surface,
cursor->x - c->geom.x - c->bw,
cursor->y - c->geom.y - c->bw, &sx, &sy);
}
/* If there's no client surface under the cursor, set the cursor image to a
* default. This is what makes the cursor image appear when you move it
* off of a client or over its border. */
Expand Down Expand Up @@ -893,7 +957,8 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy,
{
/* Use top level surface if nothing more specific given */
if (c && !surface)
surface = c->xdg_surface->surface;
surface = WLR_SURFACE(c);

/* If surface is already focused, only notify of motion */
if (surface && surface == seat->pointer_state.focused_surface) {
wlr_seat_pointer_notify_motion(seat, time, sx, sy);
Expand Down Expand Up @@ -985,6 +1050,7 @@ renderclients(Monitor *m, struct timespec *now)
int i, w, h;
struct render_data rdata;
struct wlr_box *borders;
struct wlr_surface *surface;
/* Each subsequent window we render is rendered on top of the last. Because
* our stacking list is ordered front-to-back, we iterate over it backwards. */
wl_list_for_each_reverse(c, &stack, slink) {
Expand All @@ -993,11 +1059,12 @@ renderclients(Monitor *m, struct timespec *now)
output_layout, m->wlr_output, &c->geom))
continue;

surface = WLR_SURFACE(c);
ox = c->geom.x, oy = c->geom.y;
wlr_output_layout_output_coords(output_layout, m->wlr_output,
&ox, &oy);
w = c->xdg_surface->surface->current.width;
h = c->xdg_surface->surface->current.height;
w = surface->current.width;
h = surface->current.height;
borders = (struct wlr_box[4]) {
{ox, oy, w + 2 * c->bw, c->bw}, /* top */
{ox, oy + c->bw, c->bw, h}, /* left */
Expand All @@ -1015,8 +1082,11 @@ renderclients(Monitor *m, struct timespec *now)
rdata.output = m->wlr_output,
rdata.when = now,
rdata.x = c->geom.x + c->bw,
rdata.y = c->geom.y + c->bw,
wlr_xdg_surface_for_each_surface(c->xdg_surface, render, &rdata);
rdata.y = c->geom.y + c->bw;
if (c->isxdg)
wlr_xdg_surface_for_each_surface(c->xdg_surface, render, &rdata);
else
wlr_surface_for_each_surface(c->xwayland_surface->surface, render, &rdata);
}
}

Expand Down Expand Up @@ -1069,8 +1139,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 */
wlr_xdg_toplevel_set_size(c->xdg_surface,
c->geom.width - 2 * c->bw, c->geom.height - 2 * c->bw);
if (c->isxdg)
wlr_xdg_toplevel_set_size(c->xdg_surface,
c->geom.width - 2 * c->bw, c->geom.height - 2 * c->bw);
else
wlr_xwayland_surface_configure(c->xwayland_surface,
c->geom.x, c->geom.y,
c->geom.width - 2 * c->bw, c->geom.height - 2 * c->bw);
}

void
Expand Down Expand Up @@ -1214,19 +1289,20 @@ setmon(Client *c, Monitor *m, unsigned int newtags)
{
int hadfocus;
Monitor *oldmon = c->mon;
struct wlr_surface *surface = WLR_SURFACE(c);
if (oldmon == m)
return;
hadfocus = (c == selclient());
c->mon = m;
/* XXX leave/enter is not optimal but works */
if (oldmon) {
wlr_surface_send_leave(c->xdg_surface->surface, oldmon->wlr_output);
wlr_surface_send_leave(surface, oldmon->wlr_output);
arrange(oldmon);
}
if (m) {
/* Make sure window actually overlaps with the monitor */
applybounds(c, &m->m);
wlr_surface_send_enter(c->xdg_surface->surface, m->wlr_output);
wlr_surface_send_enter(surface, m->wlr_output);
c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */
arrange(m);
}
Expand Down Expand Up @@ -1282,7 +1358,7 @@ setup(void)
* to dig your fingers in and play with their behavior if you want. Note that
* the clients cannot set the selection directly without compositor approval,
* see the setsel() function. */
wlr_compositor_create(dpy, drw);
compositor = wlr_compositor_create(dpy, drw);
wlr_screencopy_manager_v1_create(dpy);
wlr_data_device_manager_create(dpy);
wlr_primary_selection_v1_device_manager_create(dpy);
Expand Down Expand Up @@ -1360,6 +1436,19 @@ setup(void)
&request_set_sel);
wl_signal_add(&seat->events.request_set_primary_selection,
&request_set_psel);

/*
* Initialise the XWayland X server.
* It will be started when the first X client is started.
*/
xwayland = wlr_xwayland_create(dpy, compositor, true);
if (xwayland) {
wl_signal_add(&xwayland->events.new_surface, &new_xwayland_surface);

setenv("DISPLAY", xwayland->display_name, true);
} else {
fprintf(stderr, "failed to setup XWayland X server, continuing without it\n");
}
}

void
Expand Down Expand Up @@ -1550,6 +1639,7 @@ main(int argc, char *argv[])
run(startup_cmd);

/* Once wl_display_run returns, we shut down the server. */
wlr_xwayland_destroy(xwayland);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is null safe.

wl_display_destroy_clients(dpy);
wl_display_destroy(dpy);
return EXIT_SUCCESS;
Expand Down