xwayland: add server and basic window functionality (#10)

* xwayland: add server and basic window functionality

* xwayland: add server and basic window functionality

* xwayland: add server and basic window functionality

* xwayland: add server and basic window functionality
This commit is contained in:
Alexander Courtis 2020-07-22 08:35:46 +10:00 committed by GitHub
parent 91b18d4d4d
commit 57fef50310
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 125 additions and 35 deletions

160
dwl.c
View File

@ -31,6 +31,7 @@
#include <wlr/types/wlr_xdg_output_v1.h> #include <wlr/types/wlr_xdg_output_v1.h>
#include <wlr/types/wlr_xdg_shell.h> #include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include <wlr/xwayland.h>
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>
/* macros */ /* macros */
@ -41,6 +42,7 @@
#define LENGTH(X) (sizeof X / sizeof X[0]) #define LENGTH(X) (sizeof X / sizeof X[0])
#define END(A) ((A) + LENGTH(A)) #define END(A) ((A) + LENGTH(A))
#define TAGMASK ((1 << LENGTH(tags)) - 1) #define TAGMASK ((1 << LENGTH(tags)) - 1)
#define WLR_SURFACE(C) (c->isxdg ? c->xdg_surface->surface : c->xwayland_surface->surface)
/* enums */ /* enums */
enum { CurNormal, CurMove, CurResize }; /* cursor */ enum { CurNormal, CurMove, CurResize }; /* cursor */
@ -64,11 +66,15 @@ typedef struct {
struct wl_list link; struct wl_list link;
struct wl_list flink; struct wl_list flink;
struct wl_list slink; struct wl_list slink;
struct wlr_xdg_surface *xdg_surface; union {
struct wlr_xdg_surface *xdg_surface;
struct wlr_xwayland_surface *xwayland_surface;
};
struct wl_listener map; struct wl_listener map;
struct wl_listener unmap; struct wl_listener unmap;
struct wl_listener destroy; struct wl_listener destroy;
struct wlr_box geom; /* layout-relative, includes border */ struct wlr_box geom; /* layout-relative, includes border */
int isxdg;
Monitor *mon; Monitor *mon;
int bw; int bw;
unsigned int tags; unsigned int tags;
@ -148,7 +154,8 @@ static void buttonpress(struct wl_listener *listener, void *data);
static void chvt(const Arg *arg); static void chvt(const Arg *arg);
static void createkeyboard(struct wlr_input_device *device); static void createkeyboard(struct wlr_input_device *device);
static void createmon(struct wl_listener *listener, void *data); 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 createpointer(struct wlr_input_device *device);
static void createxdeco(struct wl_listener *listener, void *data); static void createxdeco(struct wl_listener *listener, void *data);
static void cursorframe(struct wl_listener *listener, void *data); static void cursorframe(struct wl_listener *listener, void *data);
@ -205,6 +212,8 @@ static const char broken[] = "broken";
static struct wl_display *dpy; static struct wl_display *dpy;
static struct wlr_backend *backend; static struct wlr_backend *backend;
static struct wlr_renderer *drw; static struct wlr_renderer *drw;
static struct wlr_compositor *compositor;
static struct wlr_xwayland *xwayland;
static struct wlr_xdg_shell *xdg_shell; static struct wlr_xdg_shell *xdg_shell;
static struct wl_list clients; /* tiling order */ static struct wl_list clients; /* tiling order */
@ -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_input = {.notify = inputdevice};
static struct wl_listener new_output = {.notify = createmon}; static struct wl_listener new_output = {.notify = createmon};
static struct wl_listener new_xdeco = {.notify = createxdeco}; 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_cursor = {.notify = setcursor};
static struct wl_listener request_set_psel = {.notify = setpsel}; static struct wl_listener request_set_psel = {.notify = setpsel};
static struct wl_listener request_set_sel = {.notify = setsel}; static struct wl_listener request_set_sel = {.notify = setsel};
@ -271,10 +281,15 @@ applyrules(Client *c)
/* rule matching */ /* rule matching */
c->isfloating = 0; c->isfloating = 0;
if (!(appid = c->xdg_surface->toplevel->app_id)) if (c->isxdg) {
appid = broken; if (!(appid = c->xdg_surface->toplevel->app_id))
if (!(title = c->xdg_surface->toplevel->title)) appid = broken;
title = broken; if (!(title = c->xdg_surface->toplevel->title))
title = broken;
} else {
if (!(title = c->xwayland_surface->title))
title = broken;
}
for (r = rules; r < END(rules); r++) { for (r = rules; r < END(rules); r++) {
if ((!r->title || strstr(title, r->title)) if ((!r->title || strstr(title, r->title))
@ -328,9 +343,14 @@ buttonpress(struct wl_listener *listener, void *data)
case WLR_BUTTON_PRESSED:; case WLR_BUTTON_PRESSED:;
/* Change focus if the button was _pressed_ over a client */ /* Change focus if the button was _pressed_ over a client */
if ((c = xytoclient(cursor->x, cursor->y))) { if ((c = xytoclient(cursor->x, cursor->y))) {
surface = wlr_xdg_surface_surface_at(c->xdg_surface, if (c->isxdg)
cursor->x - c->geom.x - c->bw, surface = wlr_xdg_surface_surface_at(c->xdg_surface,
cursor->y - c->geom.y - c->bw, NULL, NULL); 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);
focusclient(c, surface, 1); focusclient(c, surface, 1);
} }
@ -458,7 +478,7 @@ createmon(struct wl_listener *listener, void *data)
} }
void 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 /* This event is raised when wlr_xdg_shell receives a new xdg surface from a
* client, either a toplevel (application window) or popup. */ * client, either a toplevel (application window) or popup. */
@ -471,6 +491,7 @@ createnotify(struct wl_listener *listener, void *data)
/* Allocate a Client for this surface */ /* Allocate a Client for this surface */
c = xdg_surface->data = calloc(1, sizeof(*c)); c = xdg_surface->data = calloc(1, sizeof(*c));
c->xdg_surface = xdg_surface; c->xdg_surface = xdg_surface;
c->isxdg = 1;
c->bw = borderpx; c->bw = borderpx;
/* Tell the client not to try anything fancy */ /* Tell the client not to try anything fancy */
@ -486,6 +507,27 @@ createnotify(struct wl_listener *listener, void *data)
wl_signal_add(&xdg_surface->events.destroy, &c->destroy); 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 void
createpointer(struct wlr_input_device *device) createpointer(struct wlr_input_device *device)
{ {
@ -566,8 +608,8 @@ focusclient(Client *c, struct wlr_surface *surface, int lift)
Client *sel = selclient(); Client *sel = selclient();
struct wlr_keyboard *kb; struct wlr_keyboard *kb;
/* Previous and new xdg toplevel surfaces */ /* Previous and new xdg toplevel surfaces */
struct wlr_xdg_surface *ptl = sel ? sel->xdg_surface : NULL; Client *ptl = sel;
struct wlr_xdg_surface *tl = c ? c->xdg_surface : NULL; Client *tl = c;
/* Previously focused surface */ /* Previously focused surface */
struct wlr_surface *psurface = seat->keyboard_state.focused_surface; struct wlr_surface *psurface = seat->keyboard_state.focused_surface;
@ -575,7 +617,7 @@ focusclient(Client *c, struct wlr_surface *surface, int lift)
/* assert(VISIBLEON(c, c->mon)); ? */ /* assert(VISIBLEON(c, c->mon)); ? */
/* Use top-level wlr_surface if nothing more specific given */ /* Use top-level wlr_surface if nothing more specific given */
if (!surface) if (!surface)
surface = c->xdg_surface->surface; surface = WLR_SURFACE(c);
/* Focus the correct monitor (must come after selclient!) */ /* Focus the correct monitor (must come after selclient!) */
selmon = c->mon; selmon = c->mon;
@ -610,10 +652,18 @@ focusclient(Client *c, struct wlr_surface *surface, int lift)
* activate the new one. This lets the clients know to repaint * activate the new one. This lets the clients know to repaint
* accordingly, e.g. show/hide a caret. * accordingly, e.g. show/hide a caret.
*/ */
if (tl != ptl && ptl) if (tl != ptl && ptl) {
wlr_xdg_toplevel_set_activated(ptl, 0); if (ptl->isxdg)
if (tl != ptl && tl) wlr_xdg_toplevel_set_activated(ptl->xdg_surface, 0);
wlr_xdg_toplevel_set_activated(tl, 1); 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 void
@ -783,9 +833,17 @@ maprequest(struct wl_listener *listener, void *data)
wl_list_insert(&clients, &c->link); wl_list_insert(&clients, &c->link);
wl_list_insert(&fstack, &c->flink); wl_list_insert(&fstack, &c->flink);
wl_list_insert(&stack, &c->slink); wl_list_insert(&stack, &c->slink);
wlr_xdg_surface_get_geometry(c->xdg_surface, &c->geom);
c->geom.width += 2 * c->bw; if (c->isxdg) {
c->geom.height += 2 * c->bw; 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 */ /* Set initial monitor, tags, floating status, and focus */
applyrules(c); applyrules(c);
@ -830,10 +888,16 @@ motionnotify(uint32_t time)
} }
/* Otherwise, find the client under the pointer and send the event along. */ /* Otherwise, find the client under the pointer and send the event along. */
if ((c = xytoclient(cursor->x, cursor->y))) if ((c = xytoclient(cursor->x, cursor->y))) {
surface = wlr_xdg_surface_surface_at(c->xdg_surface, if (c->isxdg)
cursor->x - c->geom.x - c->bw, surface = wlr_xdg_surface_surface_at(c->xdg_surface,
cursor->y - c->geom.y - c->bw, &sx, &sy); 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 /* 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 * default. This is what makes the cursor image appear when you move it
* off of a client or over its border. */ * off of a client or over its border. */
@ -893,7 +957,8 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy,
{ {
/* Use top level surface if nothing more specific given */ /* Use top level surface if nothing more specific given */
if (c && !surface) if (c && !surface)
surface = c->xdg_surface->surface; surface = WLR_SURFACE(c);
/* If surface is already focused, only notify of motion */ /* If surface is already focused, only notify of motion */
if (surface && surface == seat->pointer_state.focused_surface) { if (surface && surface == seat->pointer_state.focused_surface) {
wlr_seat_pointer_notify_motion(seat, time, sx, sy); wlr_seat_pointer_notify_motion(seat, time, sx, sy);
@ -985,6 +1050,7 @@ renderclients(Monitor *m, struct timespec *now)
int i, w, h; int i, w, h;
struct render_data rdata; struct render_data rdata;
struct wlr_box *borders; struct wlr_box *borders;
struct wlr_surface *surface;
/* Each subsequent window we render is rendered on top of the last. Because /* 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. */ * our stacking list is ordered front-to-back, we iterate over it backwards. */
wl_list_for_each_reverse(c, &stack, slink) { wl_list_for_each_reverse(c, &stack, slink) {
@ -993,11 +1059,12 @@ renderclients(Monitor *m, struct timespec *now)
output_layout, m->wlr_output, &c->geom)) output_layout, m->wlr_output, &c->geom))
continue; continue;
surface = WLR_SURFACE(c);
ox = c->geom.x, oy = c->geom.y; ox = c->geom.x, oy = c->geom.y;
wlr_output_layout_output_coords(output_layout, m->wlr_output, wlr_output_layout_output_coords(output_layout, m->wlr_output,
&ox, &oy); &ox, &oy);
w = c->xdg_surface->surface->current.width; w = surface->current.width;
h = c->xdg_surface->surface->current.height; h = surface->current.height;
borders = (struct wlr_box[4]) { borders = (struct wlr_box[4]) {
{ox, oy, w + 2 * c->bw, c->bw}, /* top */ {ox, oy, w + 2 * c->bw, c->bw}, /* top */
{ox, oy + c->bw, c->bw, h}, /* left */ {ox, oy + c->bw, c->bw, h}, /* left */
@ -1015,8 +1082,11 @@ renderclients(Monitor *m, struct timespec *now)
rdata.output = m->wlr_output, rdata.output = m->wlr_output,
rdata.when = now, rdata.when = now,
rdata.x = c->geom.x + c->bw, rdata.x = c->geom.x + c->bw,
rdata.y = c->geom.y + c->bw, rdata.y = c->geom.y + c->bw;
wlr_xdg_surface_for_each_surface(c->xdg_surface, render, &rdata); 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);
} }
} }
@ -1069,8 +1139,13 @@ resize(Client *c, int x, int y, int w, int h, int interact)
c->geom.height = h; c->geom.height = h;
applybounds(c, bbox); applybounds(c, bbox);
/* wlroots makes this a no-op if size hasn't changed */ /* wlroots makes this a no-op if size hasn't changed */
wlr_xdg_toplevel_set_size(c->xdg_surface, if (c->isxdg)
c->geom.width - 2 * c->bw, c->geom.height - 2 * c->bw); 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 void
@ -1214,19 +1289,20 @@ setmon(Client *c, Monitor *m, unsigned int newtags)
{ {
int hadfocus; int hadfocus;
Monitor *oldmon = c->mon; Monitor *oldmon = c->mon;
struct wlr_surface *surface = WLR_SURFACE(c);
if (oldmon == m) if (oldmon == m)
return; return;
hadfocus = (c == selclient()); hadfocus = (c == selclient());
c->mon = m; c->mon = m;
/* XXX leave/enter is not optimal but works */ /* XXX leave/enter is not optimal but works */
if (oldmon) { if (oldmon) {
wlr_surface_send_leave(c->xdg_surface->surface, oldmon->wlr_output); wlr_surface_send_leave(surface, oldmon->wlr_output);
arrange(oldmon); arrange(oldmon);
} }
if (m) { if (m) {
/* Make sure window actually overlaps with the monitor */ /* Make sure window actually overlaps with the monitor */
applybounds(c, &m->m); 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 */ c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */
arrange(m); arrange(m);
} }
@ -1282,7 +1358,7 @@ setup(void)
* to dig your fingers in and play with their behavior if you want. Note that * 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, * the clients cannot set the selection directly without compositor approval,
* see the setsel() function. */ * see the setsel() function. */
wlr_compositor_create(dpy, drw); compositor = wlr_compositor_create(dpy, drw);
wlr_screencopy_manager_v1_create(dpy); wlr_screencopy_manager_v1_create(dpy);
wlr_data_device_manager_create(dpy); wlr_data_device_manager_create(dpy);
wlr_primary_selection_v1_device_manager_create(dpy); wlr_primary_selection_v1_device_manager_create(dpy);
@ -1360,6 +1436,19 @@ setup(void)
&request_set_sel); &request_set_sel);
wl_signal_add(&seat->events.request_set_primary_selection, wl_signal_add(&seat->events.request_set_primary_selection,
&request_set_psel); &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 void
@ -1550,6 +1639,7 @@ main(int argc, char *argv[])
run(startup_cmd); run(startup_cmd);
/* Once wl_display_run returns, we shut down the server. */ /* Once wl_display_run returns, we shut down the server. */
wlr_xwayland_destroy(xwayland);
wl_display_destroy_clients(dpy); wl_display_destroy_clients(dpy);
wl_display_destroy(dpy); wl_display_destroy(dpy);
return EXIT_SUCCESS; return EXIT_SUCCESS;