-
Notifications
You must be signed in to change notification settings - Fork 278
xwayland: add server and basic window functionality #10
Changes from all commits
e9bfd8c
ae24555
b3eb7b3
978c27d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 */ | ||
|
@@ -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 */ | ||
|
@@ -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; | ||
}; | ||
struct wl_listener map; | ||
struct wl_listener unmap; | ||
struct wl_listener destroy; | ||
struct wlr_box geom; /* layout-relative, includes border */ | ||
int isxdg; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
|
@@ -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); | ||
|
@@ -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 */ | ||
|
@@ -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}; | ||
|
@@ -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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. xwayland does not have an app_id There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It does appear to still have instance/class though There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
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)) | ||
|
@@ -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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
} | ||
|
||
|
@@ -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. */ | ||
|
@@ -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 */ | ||
|
@@ -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) | ||
{ | ||
|
@@ -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; | ||
|
@@ -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 | ||
|
@@ -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); | ||
|
@@ -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. */ | ||
|
@@ -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); | ||
|
@@ -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) { | ||
|
@@ -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 */ | ||
|
@@ -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); | ||
} | ||
} | ||
|
||
|
@@ -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 | ||
|
@@ -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); | ||
} | ||
|
@@ -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); | ||
|
@@ -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 | ||
|
@@ -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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
|
There was a problem hiding this comment.
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.