From d5b15f1884dba59fd2cca39ce510c3d525c1df3d Mon Sep 17 00:00:00 2001 From: DeedleFake Date: Tue, 29 Sep 2026 11:54:55 -0400 Subject: [PATCH 1/6] wlr: add xwayland surface associate and dissociate events --- xwayland.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/xwayland.go b/xwayland.go index 5aa39d3..4b2ca02 100644 --- a/xwayland.go +++ b/xwayland.go @@ -106,6 +106,24 @@ func (s XwaylandSurface) OnDestroy(cb func(XwaylandSurface)) Listener { }) } +// OnAssociate is called when the X11 window is paired with a +// wlr_surface. Surface returns an invalid Surface until then, so +// listeners on the Surface, such as OnMap, should be added here. +func (s XwaylandSurface) OnAssociate(cb func(XwaylandSurface)) Listener { + return newListener(&s.p.events.associate, func(lis Listener, data unsafe.Pointer) { + cb(s) + }) +} + +// OnDissociate is called when the X11 window is about to lose its +// wlr_surface. Listeners added to the Surface in OnAssociate should be +// removed here. +func (s XwaylandSurface) OnDissociate(cb func(XwaylandSurface)) Listener { + return newListener(&s.p.events.dissociate, func(lis Listener, data unsafe.Pointer) { + cb(s) + }) +} + func (s XwaylandSurface) OnRequestMove(cb func(surface XwaylandSurface)) Listener { return newListener(&s.p.events.request_move, func(lis Listener, data unsafe.Pointer) { cb(s) From 8b845b0e427df8ad119d4c6d639222153b17856a Mon Sep 17 00:00:00 2001 From: DeedleFake Date: Tue, 29 Sep 2026 11:55:03 -0400 Subject: [PATCH 2/6] wlr: add xdg toplevel destroy event --- xdg.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/xdg.go b/xdg.go index f4af2f2..27f0013 100644 --- a/xdg.go +++ b/xdg.go @@ -215,6 +215,15 @@ type XDGToplevel struct { p *C.struct_wlr_xdg_toplevel } +// OnDestroy is called when the toplevel role object is destroyed. This +// happens before the base XDGSurface's destroy event, and listeners on +// the toplevel must be removed by then. +func (t XDGToplevel) OnDestroy(cb func(XDGToplevel)) Listener { + return newListener(&t.p.events.destroy, func(lis Listener, data unsafe.Pointer) { + cb(t) + }) +} + func (t XDGToplevel) OnRequestMove(cb func(t XDGToplevel, client SeatClient, serial uint32)) Listener { return newListener(&t.p.events.request_move, func(lis Listener, data unsafe.Pointer) { event := (*C.struct_wlr_xdg_toplevel_move_event)(data) From 43771c3ab8d8c1ec7a50cb60d7a1c03084f55116 Mon Sep 17 00:00:00 2001 From: DeedleFake Date: Tue, 29 Sep 2026 11:55:03 -0400 Subject: [PATCH 3/6] wlr: add keyboard valid check --- input.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/input.go b/input.go index 4130c5d..9c50595 100644 --- a/input.go +++ b/input.go @@ -38,6 +38,10 @@ type Keyboard struct { p *C.struct_wlr_keyboard } +func (k Keyboard) Valid() bool { + return k.p != nil +} + func (k Keyboard) Base() InputDevice { return InputDevice{p: &k.p.base} } From c11b2737b651899a821365ce8807a9d9625c2b30 Mon Sep 17 00:00:00 2001 From: DeedleFake Date: Tue, 29 Sep 2026 12:04:11 -0400 Subject: [PATCH 4/6] wlr: keep go pointers out of render texture options --- renderpass.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/renderpass.go b/renderpass.go index 864ed8a..777834a 100644 --- a/renderpass.go +++ b/renderpass.go @@ -2,6 +2,13 @@ package wlr /* #include + +// options can't hold a Go pointer when it's passed to C, so alpha is +// pointed to from here instead. +static inline void _wlr_render_pass_add_texture(struct wlr_render_pass *pass, struct wlr_render_texture_options *options, float alpha) { + options->alpha = α + wlr_render_pass_add_texture(pass, options); +} */ import "C" @@ -37,7 +44,6 @@ func (r RenderPass) Submit() bool { } func (r RenderPass) AddTexture(texture Texture, srcBox image.Rectangle, dstBox image.Rectangle, alpha float32, transform OutputTransform, filterMode FilterMode, blendMode BlendMode) { - var alphaC C.float = C.float(alpha) var options C.struct_wlr_render_texture_options options.texture = texture.p if srcBox != (image.Rectangle{}) { @@ -46,11 +52,10 @@ func (r RenderPass) AddTexture(texture Texture, srcBox image.Rectangle, dstBox i if dst := boxToC(dstBox); dst != nil { options.dst_box = *dst } - options.alpha = &alphaC options.transform = C.enum_wl_output_transform(transform) options.filter_mode = C.enum_wlr_scale_filter_mode(filterMode) options.blend_mode = C.enum_wlr_render_blend_mode(blendMode) - C.wlr_render_pass_add_texture(r.p, &options) + C._wlr_render_pass_add_texture(r.p, &options, C.float(alpha)) } func (r RenderPass) AddRect(box image.Rectangle, c color.Color, blendMode BlendMode) { From bc2c03ef1fbc7dbbeff4a5a3cfbe7da4a8eb16bb Mon Sep 17 00:00:00 2001 From: DeedleFake Date: Tue, 29 Sep 2026 12:04:20 -0400 Subject: [PATCH 5/6] wlr: add surface commit event and xdg surface initialization state --- surface.go | 6 ++++++ xdg.go | 13 +++++++++++++ 2 files changed, 19 insertions(+) diff --git a/surface.go b/surface.go index 9aef2de..bce4b04 100644 --- a/surface.go +++ b/surface.go @@ -72,6 +72,12 @@ func (s Surface) OnUnmap(cb func(Surface)) Listener { }) } +func (s Surface) OnCommit(cb func(Surface)) Listener { + return newListener(&s.p.events.commit, func(lis Listener, data unsafe.Pointer) { + cb(s) + }) +} + func (s Surface) OnDestroy(cb func(Surface)) Listener { return newListener(&s.p.events.destroy, func(lis Listener, data unsafe.Pointer) { cb(s) diff --git a/xdg.go b/xdg.go index 27f0013..721e269 100644 --- a/xdg.go +++ b/xdg.go @@ -122,6 +122,19 @@ func (s XDGSurface) HasSurface(sub Surface) bool { return C._wlr_xdg_surface_has_surface(s.p, sub.p) != 0 } +// Initialized returns true once the client has made its initial +// commit. Configure events, including those sent by setting toplevel +// state, can't be sent before that. +func (s XDGSurface) Initialized() bool { + return bool(s.p.initialized) +} + +// InitialCommit returns true if the commit currently being handled is +// the surface's initial commit. +func (s XDGSurface) InitialCommit() bool { + return bool(s.p.initial_commit) +} + func (s XDGSurface) Role() XDGSurfaceRole { return XDGSurfaceRole(s.p.role) } From 55b4d2cd7e9f60e62e74a1ff44beb9afac6a39d7 Mon Sep 17 00:00:00 2001 From: DeedleFake Date: Tue, 29 Sep 2026 12:42:12 -0400 Subject: [PATCH 6/6] wlr: explain why render texture alpha is passed by value --- renderpass.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/renderpass.go b/renderpass.go index 777834a..4ddb1f3 100644 --- a/renderpass.go +++ b/renderpass.go @@ -3,8 +3,12 @@ package wlr /* #include -// options can't hold a Go pointer when it's passed to C, so alpha is -// pointed to from here instead. +// cgo lets Go pass a pointer to Go memory into C only if that memory +// holds no unpinned Go pointers. options is Go memory, so pointing +// options->alpha at a Go variable makes the cgo pointer check panic. +// Taking alpha by value and pointing at the C parameter avoids that. +// The pointer only has to outlive the call: every renderer reads alpha +// during wlr_render_pass_add_texture and doesn't keep options. static inline void _wlr_render_pass_add_texture(struct wlr_render_pass *pass, struct wlr_render_texture_options *options, float alpha) { options->alpha = α wlr_render_pass_add_texture(pass, options);