Add xwayland, fix naming

Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
2025-08-10 12:58:55 +03:00
parent 025ee9da91
commit ec61c13aa6
2 changed files with 376 additions and 47 deletions

View File

@@ -43,9 +43,14 @@ void toplevel_commit_notify(struct wl_listener *l, void *)
{
auto *tl = wl_container_of(l, (LunarWM_Toplevel *)(NULL), commit);
if (tl->xdg_toplevel->base->initial_commit) {
wlr_xdg_toplevel_set_size(tl->xdg_toplevel, 0, 0);
if (!tl || !tl->surface)
return;
if (!tl->is_xwayland) {
if (tl->u.xdg && tl->u.xdg->base->initial_commit) {
wlr_xdg_toplevel_set_size(tl->u.xdg, 0, 0);
return;
}
}
LunarWM_Toplevel_update(tl);
@@ -65,12 +70,13 @@ void toplevel_destroy_notify(struct wl_listener *l, void *)
free(tl);
}
bool LunarWM_Toplevel_init(
LunarWM_Toplevel *tl, struct LunarWM *wm, struct wlr_xdg_toplevel *xdg)
bool LunarWM_Toplevel_init_xdg(
LunarWM_Toplevel *tl, LunarWM *wm, struct wlr_xdg_toplevel *xdg)
{
tl->id = vector_size(wm->wayland.v_toplevels) + 1;
tl->server = wm;
tl->xdg_toplevel = xdg;
tl->is_xwayland = false;
tl->u.xdg = xdg;
tl->surface = xdg->base->surface;
assert(tl->surface);
@@ -84,6 +90,26 @@ bool LunarWM_Toplevel_init(
return true;
}
bool LunarWM_Toplevel_init_xwayland(
LunarWM_Toplevel *tl, LunarWM *wm, struct wlr_xwayland_surface *xwl)
{
tl->id = vector_size(wm->wayland.v_toplevels) + 1;
tl->server = wm;
tl->is_xwayland = true;
tl->u.xwl = xwl;
tl->surface = xwl->surface;
assert(tl->surface);
tl->commit.notify = toplevel_commit_notify;
wl_signal_add(&tl->surface->events.commit, &tl->commit);
tl->destroy.notify = toplevel_destroy_notify;
wl_signal_add(&tl->surface->events.destroy, &tl->destroy);
return true;
}
bool LunarWM_Toplevel_destroy(LunarWM_Toplevel *this)
{
wl_list_remove(&this->commit.link);
@@ -131,6 +157,278 @@ bool LunarWM_Toplevel_update(LunarWM_Toplevel *this)
return true;
}
static void clamp_to_hints(
const struct wlr_xwayland_surface *x, uint16_t *w, uint16_t *h)
{
xcb_size_hints_t *hints = x->size_hints;
if (!hints)
return;
if ((hints->flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE)) {
if (*w < hints->min_width)
*w = hints->min_width;
if (*h < hints->min_height)
*h = hints->min_height;
}
if ((hints->flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)) {
if (hints->max_width > 0 && *w > hints->max_width)
*w = hints->max_width;
if (hints->max_height > 0 && *h > hints->max_height)
*h = hints->max_height;
}
if ((hints->flags & XCB_ICCCM_SIZE_HINT_P_RESIZE_INC)) {
if (hints->width_inc > 0)
*w = (*w / hints->width_inc) * hints->width_inc;
if (hints->height_inc > 0)
*h = (*h / hints->height_inc) * hints->height_inc;
}
if ((hints->flags & XCB_ICCCM_SIZE_HINT_BASE_SIZE)) {
if (*w < hints->base_width)
*w = hints->base_width;
if (*h < hints->base_height)
*h = hints->base_height;
}
}
struct XwlHooks {
LunarWM *wm;
struct wlr_xwayland_surface *xwl;
struct wl_listener associate;
struct wl_listener dissociate;
struct wl_listener destroy;
struct wl_listener req_configure;
struct wl_listener req_maximize;
struct wl_listener req_fullscreen;
struct wl_listener set_geometry;
};
static void xwayland_ready_notify(struct wl_listener *l, void *data)
{
LunarWM *wm = wl_container_of(l, wm, wayland.xwayland_ready);
wlr_xwayland_set_seat(wm->wayland.xwayland, wm->wayland.seat);
setenv("DISPLAY", wm->wayland.xwayland->display_name, 1);
}
static void handle_associate(struct wl_listener *ll, void *d)
{
struct wlr_xwayland_surface *x = d;
LunarWM *wm2 = wl_container_of(ll, wm2, wayland.xwayland_associate_tmp);
if (!x->surface)
return;
LunarWM_Toplevel *tl = calloc(1, sizeof *tl);
if (!tl)
return;
if (LunarWM_Toplevel_init_xwayland(tl, wm2, x)) {
vector_add(&wm2->wayland.v_toplevels, tl);
} else {
free(tl);
}
wl_list_remove(&wm2->wayland.xwayland_associate_tmp.link);
}
static void handle_dissociate(struct wl_listener *ll, void *d)
{
struct wlr_xwayland_surface *x = d;
LunarWM *wm2 = wl_container_of(ll, wm2, wayland.xwayland_dissociate_tmp);
for (size_t i = 0; i < vector_size(wm2->wayland.v_toplevels); ++i) {
LunarWM_Toplevel *tl = wm2->wayland.v_toplevels[i];
if (tl->is_xwayland && tl->u.xwl == x) {
vector_remove(wm2->wayland.v_toplevels, i);
LunarWM_Toplevel_destroy(tl);
free(tl);
break;
}
}
wl_list_remove(&wm2->wayland.xwayland_dissociate_tmp.link);
}
static void xwl_hooks_destroy(struct XwlHooks *h)
{
if (!h)
return;
if (h->associate.link.prev || h->associate.link.next)
wl_list_remove(&h->associate.link);
if (h->dissociate.link.prev || h->dissociate.link.next)
wl_list_remove(&h->dissociate.link);
if (h->destroy.link.prev || h->destroy.link.next)
wl_list_remove(&h->destroy.link);
free(h);
}
static void xwl_on_associate(struct wl_listener *ll, void *data)
{
struct XwlHooks *h = wl_container_of(ll, h, associate);
struct wlr_xwayland_surface *x = h->xwl;
if (!x || !x->surface)
return;
LunarWM_Toplevel *tl = calloc(1, sizeof *tl);
if (!tl)
return;
if (LunarWM_Toplevel_init_xwayland(tl, h->wm, x)) {
vector_add(&h->wm->wayland.v_toplevels, tl);
} else {
free(tl);
}
}
static void xwl_unmap_toplevel(LunarWM_Toplevel *tl)
{
if (!tl)
return;
if (tl->commit.link.prev || tl->commit.link.next)
wl_list_remove(&tl->commit.link);
if (tl->destroy.link.prev || tl->destroy.link.next)
wl_list_remove(&tl->destroy.link);
if (tl->locked_buffer) {
wlr_buffer_unlock(tl->locked_buffer);
tl->locked_buffer = NULL;
}
tl->texture = NULL;
tl->gles_texture = NULL;
tl->rl_texture = (Texture) { 0 };
tl->surface = NULL;
}
static void xwl_on_dissociate(struct wl_listener *ll, void *data)
{
struct XwlHooks *h = wl_container_of(ll, h, dissociate);
LunarWM *wm = h->wm;
struct wlr_xwayland_surface *x = h->xwl;
for (size_t i = 0; i < vector_size(wm->wayland.v_toplevels); ++i) {
LunarWM_Toplevel *tl = wm->wayland.v_toplevels[i];
if (tl->is_xwayland && tl->u.xwl == x) {
xwl_unmap_toplevel(tl);
break;
}
}
}
static void xwl_on_request_configure(struct wl_listener *ll, void *data)
{
struct XwlHooks *xh = wl_container_of(ll, xh, req_configure);
struct wlr_xwayland_surface_configure_event *ev = data;
if (!xh->xwl)
return;
int16_t x = xh->xwl->x, y = xh->xwl->y;
uint16_t w = xh->xwl->width, h = xh->xwl->height;
if (ev->mask & XCB_CONFIG_WINDOW_X)
x = ev->x;
if (ev->mask & XCB_CONFIG_WINDOW_Y)
y = ev->y;
if (ev->mask & XCB_CONFIG_WINDOW_WIDTH)
w = ev->width;
if (ev->mask & XCB_CONFIG_WINDOW_HEIGHT)
h = ev->height;
clamp_to_hints(xh->xwl, &w, &h);
wlr_xwayland_surface_configure(xh->xwl, x, y, w, h);
}
static void xwl_on_request_maximize(struct wl_listener *ll, void *data)
{
struct XwlHooks *xh = wl_container_of(ll, xh, req_maximize);
wlr_xwayland_surface_set_maximized(xh->xwl, true, true);
}
static void xwl_on_request_fullscreen(struct wl_listener *ll, void *data)
{
struct XwlHooks *xh = wl_container_of(ll, xh, req_fullscreen);
wlr_xwayland_surface_set_fullscreen(xh->xwl, true);
}
static void xwl_on_set_geometry(struct wl_listener *ll, void *data)
{
(void)ll;
(void)data;
}
static void xwl_on_destroy(struct wl_listener *ll, void *data)
{
struct XwlHooks *xh = wl_container_of(ll, xh, destroy);
LunarWM *wm = xh->wm;
struct wlr_xwayland_surface *x = xh->xwl;
if (xh->req_configure.link.prev)
wl_list_remove(&xh->req_configure.link);
if (xh->req_maximize.link.prev)
wl_list_remove(&xh->req_maximize.link);
if (xh->req_fullscreen.link.prev)
wl_list_remove(&xh->req_fullscreen.link);
if (xh->set_geometry.link.prev)
wl_list_remove(&xh->set_geometry.link);
if (xh->associate.link.prev)
wl_list_remove(&xh->associate.link);
if (xh->dissociate.link.prev)
wl_list_remove(&xh->dissociate.link);
if (xh->destroy.link.prev)
wl_list_remove(&xh->destroy.link);
for (size_t i = 0; i < vector_size(wm->wayland.v_toplevels); ++i) {
LunarWM_Toplevel *tl = wm->wayland.v_toplevels[i];
if (tl->is_xwayland && tl->u.xwl == x) {
vector_remove(wm->wayland.v_toplevels, i);
free(tl);
break;
}
}
free(xh);
}
static void xwayland_new_surface_notify(struct wl_listener *l, void *data)
{
LunarWM *wm = wl_container_of(l, wm, wayland.xwayland_new_surface);
struct wlr_xwayland_surface *xwl = data;
if (xwl->surface) {
LunarWM_Toplevel *tl = calloc(1, sizeof *tl);
if (tl && LunarWM_Toplevel_init_xwayland(tl, wm, xwl))
vector_add(&wm->wayland.v_toplevels, tl);
else
free(tl);
}
struct XwlHooks *h = calloc(1, sizeof *h);
if (!h)
return;
h->wm = wm;
h->xwl = xwl;
h->associate.notify = xwl_on_associate;
wl_signal_add(&xwl->events.associate, &h->associate);
h->dissociate.notify = xwl_on_dissociate;
wl_signal_add(&xwl->events.dissociate, &h->dissociate);
h->req_configure.notify = xwl_on_request_configure;
wl_signal_add(&xwl->events.request_configure, &h->req_configure);
h->req_maximize.notify = xwl_on_request_maximize;
wl_signal_add(&xwl->events.request_maximize, &h->req_maximize);
h->req_fullscreen.notify = xwl_on_request_fullscreen;
wl_signal_add(&xwl->events.request_fullscreen, &h->req_fullscreen);
h->set_geometry.notify = xwl_on_set_geometry;
wl_signal_add(&xwl->events.set_geometry, &h->set_geometry);
h->destroy.notify = xwl_on_destroy;
wl_signal_add(&xwl->events.destroy, &h->destroy);
xwl->data = h;
}
void LunarWM_Toplevel_focus(LunarWM_Toplevel *this)
{
if (!this)
@@ -139,19 +437,29 @@ void LunarWM_Toplevel_focus(LunarWM_Toplevel *this)
LunarWM *wm = this->server;
struct wlr_seat *seat = wm->wayland.seat;
struct wlr_surface *prev_surface = seat->keyboard_state.focused_surface;
struct wlr_surface *surface = this->xdg_toplevel->base->surface;
struct wlr_surface *surface = this->surface;
if (prev_surface == surface) {
return;
}
if (prev_surface) {
struct wlr_xdg_toplevel *prev_toplevel
struct wlr_xdg_toplevel *prev_tl
= wlr_xdg_toplevel_try_from_wlr_surface(prev_surface);
if (prev_toplevel != NULL) {
wlr_xdg_toplevel_set_activated(prev_toplevel, false);
}
if (prev_tl)
wlr_xdg_toplevel_set_activated(prev_tl, false);
struct wlr_xwayland_surface *prev_x
= wlr_xwayland_surface_try_from_wlr_surface(prev_surface);
if (prev_x)
wlr_xwayland_surface_activate(prev_x, false);
}
struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat);
wlr_xdg_toplevel_set_activated(this->xdg_toplevel, true);
if (this->is_xwayland) {
wlr_xwayland_surface_offer_focus(this->u.xwl);
wlr_xwayland_surface_activate(this->u.xwl, true);
} else {
wlr_xdg_toplevel_set_activated(this->u.xdg, true);
}
if (keyboard != NULL) {
wlr_seat_keyboard_notify_enter(seat, surface, keyboard->keycodes,
keyboard->num_keycodes, &keyboard->modifiers);
@@ -338,7 +646,7 @@ static void new_xdg_toplevel_listener_notify(
return;
}
if (LunarWM_Toplevel_init(tl, wm, xdg_tl)) {
if (LunarWM_Toplevel_init_xdg(tl, wm, xdg_tl)) {
vector_add(&wm->wayland.v_toplevels, tl);
} else {
wlr_log(WLR_ERROR, "Failed to initialize Toplevel.");
@@ -454,7 +762,13 @@ static bool init_wayland(LunarWM *this)
this->wayland.xwayland = wlr_xwayland_create(this->wayland.display,
this->wayland.compositor, this->cman->cfg.xwayland.lazy);
setenv("DISPLAY", this->wayland.xwayland->display_name, 1);
this->wayland.xwayland_ready.notify = xwayland_ready_notify;
wl_signal_add(&this->wayland.xwayland->events.ready,
&this->wayland.xwayland_ready);
this->wayland.xwayland_new_surface.notify = xwayland_new_surface_notify;
wl_signal_add(&this->wayland.xwayland->events.new_surface,
&this->wayland.xwayland_new_surface);
}
return true;
@@ -1576,34 +1890,34 @@ static void DrawBillboardNoShear(
// pass yFlip from tl->attribs.invert_y
static void DrawTexture3D(
Texture2D tex, Vector3 position, Vector3 target, float scale, bool yFlip)
Texture2D tex, Vector3 position, Vector3 target, float scale, bool y_flip)
{
if (!tex.id)
return;
Vector3 fwd = Vector3Normalize(Vector3Subtract(target, position));
Vector3 upRef = (fabsf(fwd.y) > 0.99f) ? (Vector3) { 0, 0, 1 }
: (Vector3) { 0, 1, 0 };
Vector3 right = Vector3Normalize(Vector3CrossProduct(fwd, upRef));
Vector3 up_ref = (fabsf(fwd.y) > 0.99f) ? (Vector3) { 0, 0, 1 }
: (Vector3) { 0, 1, 0 };
Vector3 right = Vector3Normalize(Vector3CrossProduct(fwd, up_ref));
Vector3 up = Vector3CrossProduct(right, fwd);
float halfW = 0.5f * (float)tex.width * scale;
float halfH = 0.5f * (float)tex.height * scale;
float half_w = 0.5f * (float)tex.width * scale;
float half_h = 0.5f * (float)tex.height * scale;
Vector3 tl
= Vector3Add(Vector3Subtract(position, Vector3Scale(right, halfW)),
Vector3Scale(up, halfH));
Vector3 tr = Vector3Add(Vector3Add(position, Vector3Scale(right, halfW)),
Vector3Scale(up, halfH));
= Vector3Add(Vector3Subtract(position, Vector3Scale(right, half_w)),
Vector3Scale(up, half_h));
Vector3 tr = Vector3Add(Vector3Add(position, Vector3Scale(right, half_w)),
Vector3Scale(up, half_h));
Vector3 br
= Vector3Subtract(Vector3Add(position, Vector3Scale(right, halfW)),
Vector3Scale(up, halfH));
Vector3 bl
= Vector3Subtract(Vector3Subtract(position, Vector3Scale(right, halfW)),
Vector3Scale(up, halfH));
= Vector3Subtract(Vector3Add(position, Vector3Scale(right, half_w)),
Vector3Scale(up, half_h));
Vector3 bl = Vector3Subtract(
Vector3Subtract(position, Vector3Scale(right, half_w)),
Vector3Scale(up, half_h));
float vt = yFlip ? 1.0f : 0.0f;
float vb = yFlip ? 0.0f : 1.0f;
float vt = y_flip ? 1.0f : 0.0f;
float vb = y_flip ? 0.0f : 1.0f;
rlDisableBackfaceCulling(); // ensure visible regardless of winding
rlSetTexture(tex.id);
@@ -1755,30 +2069,32 @@ static bool render_layer(LunarWM *this, LunarWM_RenderLayerInfo *info, float dt)
RL_ATTACHMENT_TEXTURE2D, 0);
assert(rlFramebufferComplete(this->renderer.fbo));
uint32_t const eyeW
uint32_t const eye_w
= this->xr.a_view_configuration_views[0].recommendedImageRectWidth;
uint32_t const eyeH
uint32_t const eye_h
= this->xr.a_view_configuration_views[0].recommendedImageRectHeight;
this->renderer.tmp_rt = (RenderTexture2D) {
.id = this->renderer.fbo,
.texture = { color_tex, (int)eyeW * view_count, (int)eyeH, 1, -1 },
.depth = { depth_tex, (int)eyeW * view_count, (int)eyeH, 1, -1 },
.texture = { color_tex, (int)eye_w * view_count, (int)eye_h, 1, -1 },
.depth = { depth_tex, (int)eye_w * view_count, (int)eye_h, 1, -1 },
};
// head-space view matrix (matches rlOpenXR)
XrSpaceLocation headLoc = { .type = XR_TYPE_SPACE_LOCATION };
xrLocateSpace(this->xr.view_space, this->xr.local_space,
info->predicted_display_time, &headLoc);
auto const headView = MatrixInvert(xr_matrix(headLoc.pose));
auto const head_view = MatrixInvert(xr_matrix(headLoc.pose));
// per-eye projection + view-offset
Matrix const projR = xr_projection_matrix(views[0].fov);
Matrix const projL = xr_projection_matrix(views[1].fov);
Matrix const viewOffL = MatrixMultiply(xr_matrix(views[0].pose), headView);
Matrix const viewOffR = MatrixMultiply(xr_matrix(views[1].pose), headView);
Matrix const proj_r = xr_projection_matrix(views[0].fov);
Matrix const proj_l = xr_projection_matrix(views[1].fov);
Matrix const view_off_l
= MatrixMultiply(xr_matrix(views[0].pose), head_view);
Matrix const view_off_r
= MatrixMultiply(xr_matrix(views[1].pose), head_view);
int const hud_size = eyeH * 0.3;
int const hud_size = eye_h * 0.3;
if (!IsTextureValid(this->renderer.hud_rt.texture)) {
this->renderer.hud_rt = LoadRenderTexture(hud_size, hud_size);
}
@@ -1795,10 +2111,10 @@ static bool render_layer(LunarWM *this, LunarWM_RenderLayerInfo *info, float dt)
BeginTextureMode(this->renderer.tmp_rt);
rlEnableStereoRender();
rlSetMatrixProjectionStereo(projR, projL); // right, left (yes)
rlSetMatrixViewOffsetStereo(viewOffR, viewOffL);
rlSetMatrixProjectionStereo(proj_r, proj_l); // right, left (yes)
rlSetMatrixViewOffsetStereo(view_off_r, view_off_l);
glViewport(0, 0, (GLsizei)eyeW * view_count, (GLsizei)eyeH);
glViewport(0, 0, (GLsizei)eye_w * view_count, (GLsizei)eye_h);
ClearBackground((Color) { 0, 0, 10, 255 });
for (int i = 0; i < 1; i++) {
@@ -1850,14 +2166,14 @@ static bool render_layer(LunarWM *this, LunarWM_RenderLayerInfo *info, float dt)
info->layer_projection_views_count = view_count;
for (uint32_t i = 0; i < view_count; ++i) {
int32_t const xOff = i * eyeW;
int32_t const xOff = i * eye_w;
auto *pv = &info->layer_projection_views[i];
pv->pose = views[i].pose;
pv->fov = views[i].fov;
pv->subImage.swapchain = color_sc->swapchain;
pv->subImage.imageRect.offset = (XrOffset2Di) { .x = xOff, .y = 0 };
pv->subImage.imageRect.extent
= (XrExtent2Di) { .width = eyeW, .height = eyeH };
= (XrExtent2Di) { .width = eye_w, .height = eye_h };
pv->subImage.imageArrayIndex = 0;
}

View File

@@ -50,12 +50,18 @@ typedef struct {
typedef struct {
uint32_t id;
bool is_xwayland;
struct LunarWM *server;
struct wl_listener commit;
struct wl_listener destroy;
struct wlr_xdg_toplevel *xdg_toplevel;
union {
struct wlr_xdg_toplevel *xdg;
struct wlr_xwayland_surface *xwl;
} u;
struct wlr_surface *surface;
struct wlr_texture *texture;
@@ -65,8 +71,10 @@ typedef struct {
Texture2D rl_texture;
} LunarWM_Toplevel;
bool LunarWM_Toplevel_init(
bool LunarWM_Toplevel_init_xdg(
LunarWM_Toplevel *tl, struct LunarWM *wm, struct wlr_xdg_toplevel *xdg);
bool LunarWM_Toplevel_init_xwayland(
LunarWM_Toplevel *tl, struct LunarWM *wm, struct wlr_xwayland_surface *xwl);
bool LunarWM_Toplevel_destroy(LunarWM_Toplevel *this);
bool LunarWM_Toplevel_update(LunarWM_Toplevel *this);
@@ -130,6 +138,11 @@ typedef struct LunarWM {
struct wlr_xwayland *xwayland;
struct wl_listener xwayland_ready;
struct wl_listener xwayland_new_surface;
struct wl_listener xwayland_associate_tmp; // per-surface temp
struct wl_listener xwayland_dissociate_tmp; // per-surface temp
LunarWM_Toplevel **v_toplevels;
int current_focus;
} wayland;