detached a0ea919

Merge branch 'feat/x6-window-role' into feat/xwayland-builtin

commit
a0ea919d490e9ca747a3700440f06f60af0517a9
author
brice · · 2026-10-22 10:58 UTC
merge of
aedc87d + 033015d · diff against the first parent
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
checks
0/1
17 files · +130 -46
M internal/adapters/wayland/activation.go +3 -3
@@ -66,7 +66,7 @@ func (a activation) Activate(_ *xdgactivation.ActivationV1, token string, surf *
6666 }
6767 var w *window
6868 if reason == "" {
69- if w = s.windows[state.root().windowID()]; w == nil || w.toplevel == nil || !w.mapped {
69+ if w = s.windows[state.root().windowID()]; w == nil || !w.isToplevel() || !w.mapped {
7070 reason = "not_toplevel"
7171 }
7272 }
@@ -116,8 +116,8 @@ func (t *tokenRequest) Commit(r *xdgactivation.ActivationTokenV1) {
116116
117117 // focusClient is the client with the keyboard focus.
118118 func (s *Server) focusClient() server.Client {
119- if w := s.windows[s.seat.focused]; w != nil && w.xdg.resource.Resource.Alive() {
120- return w.xdg.resource.Client()
119+ if w := s.windows[s.seat.focused]; w != nil && w.alive() {
120+ return w.client()
121121 }
122122 if l := s.layers[s.seat.focused]; l != nil && l.resource.Resource.Alive() {
123123 return l.resource.Client()
@@ -66,7 +66,7 @@ func (a activation) Activate(_ *xdgactivation.ActivationV1, token string, surf *
66 }66 }
67 var w *window67 var w *window
68 if reason == "" {68 if reason == "" {
69- if w = s.windows[state.root().windowID()]; w == nil || w.toplevel == nil || !w.mapped {69+ if w = s.windows[state.root().windowID()]; w == nil || !w.isToplevel() || !w.mapped {
70 reason = "not_toplevel"70 reason = "not_toplevel"
71 }71 }
72 }72 }
@@ -116,8 +116,8 @@ func (t *tokenRequest) Commit(r *xdgactivation.ActivationTokenV1) {
116 116
117 // focusClient is the client with the keyboard focus.117 // focusClient is the client with the keyboard focus.
118 func (s *Server) focusClient() server.Client {118 func (s *Server) focusClient() server.Client {
119- if w := s.windows[s.seat.focused]; w != nil && w.xdg.resource.Resource.Alive() {119+ if w := s.windows[s.seat.focused]; w != nil && w.alive() {
120- return w.xdg.resource.Client()120+ return w.client()
121 }121 }
122 if l := s.layers[s.seat.focused]; l != nil && l.resource.Resource.Alive() {122 if l := s.layers[s.seat.focused]; l != nil && l.resource.Resource.Alive() {
123 return l.resource.Client()123 return l.resource.Client()
M internal/adapters/wayland/constraints.go +8 -8
@@ -61,11 +61,11 @@ func (h pointerWarp) WarpPointer(r *pointerwarp.WpPointerWarpV1, surf *wayland.S
6161 return
6262 }
6363 w := s.windows[s.seat.pointerFocus]
64- if w == nil || !w.mapped || w.xdg.resource.Client() != r.Client() || s.surfaces[surf.Resource] != w.xdg.surface {
64+ if w == nil || !w.mapped || w.client() != r.Client() || s.surfaces[surf.Resource] != w.surf() {
6565 return
6666 }
6767 // Surface-local to window-local (core's, from the geometry origin).
68- g := w.xdg.geometry
68+ g := w.geometry()
6969 s.emit(ports.PointerWarp{ID: w.id, X: x.Float() - float64(g.X), Y: y.Float() - float64(g.Y)})
7070 }
7171
@@ -80,12 +80,12 @@ func (s *Server) relativeMotion(w *window, c ports.PointerMotionTo) bool {
8080 if s.protected() || c.DX == 0 && c.DY == 0 && c.UnaccelDX == 0 && c.UnaccelDY == 0 {
8181 return false
8282 }
83- if !w.mapped || !w.xdg.resource.Resource.Alive() {
83+ if !w.mapped || !w.alive() {
8484 return false
8585 }
8686 sent := false
8787 usec := uint64(c.Time / time.Microsecond)
88- for _, rel := range s.relatives[w.xdg.resource.Client()] {
88+ for _, rel := range s.relatives[w.client()] {
8989 if rel.Resource.Alive() {
9090 rel.SendRelativeMotion(uint32(usec>>32), uint32(usec), server.FixedFromFloat(c.DX), server.FixedFromFloat(c.DY), server.FixedFromFloat(c.UnaccelDX), server.FixedFromFloat(c.UnaccelDY))
9191 sent = true
@@ -272,7 +272,7 @@ func (s *Server) updateConstraint() {
272272 }
273273 var want *constraint
274274 if w := s.windows[s.seat.pointerFocus]; w != nil && w.mapped && s.seat.pointerFocus == s.seat.focused {
275- if c := s.constraints[w.xdg.surface]; c != nil && !c.defunct && (c.active || c.contains(w)) {
275+ if c := s.constraints[w.surf()]; c != nil && !c.defunct && (c.active || c.contains(w)) {
276276 want = c
277277 }
278278 }
@@ -340,10 +340,10 @@ func (s *Server) emitConstraint(c *constraint) {
340340 // activates only there, so it never warps the pointer.
341341 func (c *constraint) contains(w *window) bool {
342342 r := c.region
343- x, y := w.surfacePoint(w.xdg.server.seat.pointerX, w.xdg.server.seat.pointerY)
343+ x, y := w.surfacePoint(c.surface.server.seat.pointerX, c.surface.server.seat.pointerY)
344344 all, rects := c.surface.effectiveInput()
345345 if !all {
346- localX, localY := x-float64(w.xdg.geometry.X), y-float64(w.xdg.geometry.Y)
346+ localX, localY := x-float64(w.geometry().X), y-float64(w.geometry().Y)
347347 inside := false
348348 for _, input := range rects {
349349 if localX >= float64(input.X) && localX < float64(input.X+input.W) && localY >= float64(input.Y) && localY < float64(input.Y+input.H) {
@@ -363,5 +363,5 @@ func (c *constraint) contains(w *window) bool {
363363
364364 // locked reports whether the window's pointer is locked.
365365 func (s *Server) locked(w *window) bool {
366- return !s.protected() && s.constraint != nil && s.constraint.lock != nil && s.constraint.surface == w.xdg.surface
366+ return !s.protected() && s.constraint != nil && s.constraint.lock != nil && s.constraint.surface == w.surf()
367367 }
@@ -61,11 +61,11 @@ func (h pointerWarp) WarpPointer(r *pointerwarp.WpPointerWarpV1, surf *wayland.S
61 return61 return
62 }62 }
63 w := s.windows[s.seat.pointerFocus]63 w := s.windows[s.seat.pointerFocus]
64- if w == nil || !w.mapped || w.xdg.resource.Client() != r.Client() || s.surfaces[surf.Resource] != w.xdg.surface {64+ if w == nil || !w.mapped || w.client() != r.Client() || s.surfaces[surf.Resource] != w.surf() {
65 return65 return
66 }66 }
67 // Surface-local to window-local (core's, from the geometry origin).67 // Surface-local to window-local (core's, from the geometry origin).
68- g := w.xdg.geometry68+ g := w.geometry()
69 s.emit(ports.PointerWarp{ID: w.id, X: x.Float() - float64(g.X), Y: y.Float() - float64(g.Y)})69 s.emit(ports.PointerWarp{ID: w.id, X: x.Float() - float64(g.X), Y: y.Float() - float64(g.Y)})
70 }70 }
71 71
@@ -80,12 +80,12 @@ func (s *Server) relativeMotion(w *window, c ports.PointerMotionTo) bool {
80 if s.protected() || c.DX == 0 && c.DY == 0 && c.UnaccelDX == 0 && c.UnaccelDY == 0 {80 if s.protected() || c.DX == 0 && c.DY == 0 && c.UnaccelDX == 0 && c.UnaccelDY == 0 {
81 return false81 return false
82 }82 }
83- if !w.mapped || !w.xdg.resource.Resource.Alive() {83+ if !w.mapped || !w.alive() {
84 return false84 return false
85 }85 }
86 sent := false86 sent := false
87 usec := uint64(c.Time / time.Microsecond)87 usec := uint64(c.Time / time.Microsecond)
88- for _, rel := range s.relatives[w.xdg.resource.Client()] {88+ for _, rel := range s.relatives[w.client()] {
89 if rel.Resource.Alive() {89 if rel.Resource.Alive() {
90 rel.SendRelativeMotion(uint32(usec>>32), uint32(usec), server.FixedFromFloat(c.DX), server.FixedFromFloat(c.DY), server.FixedFromFloat(c.UnaccelDX), server.FixedFromFloat(c.UnaccelDY))90 rel.SendRelativeMotion(uint32(usec>>32), uint32(usec), server.FixedFromFloat(c.DX), server.FixedFromFloat(c.DY), server.FixedFromFloat(c.UnaccelDX), server.FixedFromFloat(c.UnaccelDY))
91 sent = true91 sent = true
@@ -272,7 +272,7 @@ func (s *Server) updateConstraint() {
272 }272 }
273 var want *constraint273 var want *constraint
274 if w := s.windows[s.seat.pointerFocus]; w != nil && w.mapped && s.seat.pointerFocus == s.seat.focused {274 if w := s.windows[s.seat.pointerFocus]; w != nil && w.mapped && s.seat.pointerFocus == s.seat.focused {
275- if c := s.constraints[w.xdg.surface]; c != nil && !c.defunct && (c.active || c.contains(w)) {275+ if c := s.constraints[w.surf()]; c != nil && !c.defunct && (c.active || c.contains(w)) {
276 want = c276 want = c
277 }277 }
278 }278 }
@@ -340,10 +340,10 @@ func (s *Server) emitConstraint(c *constraint) {
340 // activates only there, so it never warps the pointer.340 // activates only there, so it never warps the pointer.
341 func (c *constraint) contains(w *window) bool {341 func (c *constraint) contains(w *window) bool {
342 r := c.region342 r := c.region
343- x, y := w.surfacePoint(w.xdg.server.seat.pointerX, w.xdg.server.seat.pointerY)343+ x, y := w.surfacePoint(c.surface.server.seat.pointerX, c.surface.server.seat.pointerY)
344 all, rects := c.surface.effectiveInput()344 all, rects := c.surface.effectiveInput()
345 if !all {345 if !all {
346- localX, localY := x-float64(w.xdg.geometry.X), y-float64(w.xdg.geometry.Y)346+ localX, localY := x-float64(w.geometry().X), y-float64(w.geometry().Y)
347 inside := false347 inside := false
348 for _, input := range rects {348 for _, input := range rects {
349 if localX >= float64(input.X) && localX < float64(input.X+input.W) && localY >= float64(input.Y) && localY < float64(input.Y+input.H) {349 if localX >= float64(input.X) && localX < float64(input.X+input.W) && localY >= float64(input.Y) && localY < float64(input.Y+input.H) {
@@ -363,5 +363,5 @@ func (c *constraint) contains(w *window) bool {
363 363
364 // locked reports whether the window's pointer is locked.364 // locked reports whether the window's pointer is locked.
365 func (s *Server) locked(w *window) bool {365 func (s *Server) locked(w *window) bool {
366- return !s.protected() && s.constraint != nil && s.constraint.lock != nil && s.constraint.surface == w.xdg.surface366+ return !s.protected() && s.constraint != nil && s.constraint.lock != nil && s.constraint.surface == w.surf()
367 }367 }
M internal/adapters/wayland/decoration.go +1 -1
@@ -80,7 +80,7 @@ func (d *decoration) UnsetMode(r *xdgdecoration.ZxdgToplevelDecorationV1)
8080 func (d *decoration) send(r *xdgdecoration.ZxdgToplevelDecorationV1) {
8181 r.SendConfigure(uint32(xdgdecoration.ZxdgToplevelDecorationV1ModeServerSide))
8282 for _, w := range d.s.windows {
83- if w.toplevel == d.toplevel && w.hasLast && w.toplevel.Resource.Alive() && w.xdg.resource.Resource.Alive() {
83+ if w.toplevel == d.toplevel && w.hasLast && w.toplevelAlive() && w.alive() {
8484 w.sendConfigure()
8585 return
8686 }
@@ -80,7 +80,7 @@ func (d *decoration) UnsetMode(r *xdgdecoration.ZxdgToplevelDecorationV1)
80 func (d *decoration) send(r *xdgdecoration.ZxdgToplevelDecorationV1) {80 func (d *decoration) send(r *xdgdecoration.ZxdgToplevelDecorationV1) {
81 r.SendConfigure(uint32(xdgdecoration.ZxdgToplevelDecorationV1ModeServerSide))81 r.SendConfigure(uint32(xdgdecoration.ZxdgToplevelDecorationV1ModeServerSide))
82 for _, w := range d.s.windows {82 for _, w := range d.s.windows {
83- if w.toplevel == d.toplevel && w.hasLast && w.toplevel.Resource.Alive() && w.xdg.resource.Resource.Alive() {83+ if w.toplevel == d.toplevel && w.hasLast && w.toplevelAlive() && w.alive() {
84 w.sendConfigure()84 w.sendConfigure()
85 return85 return
86 }86 }
M internal/adapters/wayland/dialog.go +1 -1
@@ -76,7 +76,7 @@ func (w *window) setModal(modal bool) {
7676 return
7777 }
7878 w.modal = modal
79- if w.mapped {
79+ if w.mapped && w.xdg != nil {
8080 w.xdg.server.emit(ports.WindowModal{ID: w.id, Modal: modal})
8181 }
8282 }
@@ -76,7 +76,7 @@ func (w *window) setModal(modal bool) {
76 return76 return
77 }77 }
78 w.modal = modal78 w.modal = modal
79- if w.mapped {79+ if w.mapped && w.xdg != nil {
80 w.xdg.server.emit(ports.WindowModal{ID: w.id, Modal: modal})80 w.xdg.server.emit(ports.WindowModal{ID: w.id, Modal: modal})
81 }81 }
82 }82 }
M internal/adapters/wayland/extforeigntoplevel.go +1 -1
@@ -72,7 +72,7 @@ func (*extToplevelList) Destroy(*ext.ExtForeignToplevelListV1) {}
7272 func (s *Server) mappedToplevels() []ports.WindowID {
7373 ids := make([]ports.WindowID, 0, len(s.windows))
7474 for id, w := range s.windows {
75- if w.toplevel != nil && w.mapped {
75+ if w.isToplevel() && w.mapped {
7676 ids = append(ids, id)
7777 }
7878 }
@@ -72,7 +72,7 @@ func (*extToplevelList) Destroy(*ext.ExtForeignToplevelListV1) {}
72 func (s *Server) mappedToplevels() []ports.WindowID {72 func (s *Server) mappedToplevels() []ports.WindowID {
73 ids := make([]ports.WindowID, 0, len(s.windows))73 ids := make([]ports.WindowID, 0, len(s.windows))
74 for id, w := range s.windows {74 for id, w := range s.windows {
75- if w.toplevel != nil && w.mapped {75+ if w.isToplevel() && w.mapped {
76 ids = append(ids, id)76 ids = append(ids, id)
77 }77 }
78 }78 }
M internal/adapters/wayland/foreigntoplevel.go +6 -6
@@ -44,7 +44,7 @@ func registerForeignToplevel(d *server.Display, s *Server) error {
4444 r.OnDestroy = func() { s.toplevelManagers = removeItem(s.toplevelManagers, m) }
4545 ids := make([]ports.WindowID, 0, len(s.windows))
4646 for id, w := range s.windows {
47- if w.toplevel != nil && w.mapped {
47+ if w.isToplevel() && w.mapped {
4848 ids = append(ids, id)
4949 }
5050 }
@@ -186,7 +186,7 @@ func toplevelState(w *window, version int32) []byte {
186186
187187 // toplevelChanged updates every manager after w mapped or changed.
188188 func (s *Server) toplevelChanged(w *window) {
189- if w.toplevel == nil || !w.mapped {
189+ if !w.isToplevel() || !w.mapped {
190190 return
191191 }
192192 for _, m := range s.toplevelManagers {
@@ -220,7 +220,7 @@ func (s *Server) refreshToplevels() {
220220 for _, l := range s.extToplevelLists {
221221 for id, h := range l.handles {
222222 w := s.windows[id]
223- if h.closed || w == nil || w.toplevel == nil || !w.mapped {
223+ if h.closed || w == nil || !w.isToplevel() || !w.mapped {
224224 l.closed(id)
225225 }
226226 }
@@ -231,7 +231,7 @@ func (s *Server) refreshToplevels() {
231231 for _, m := range s.toplevelManagers {
232232 for id, h := range m.handles {
233233 w := s.windows[id]
234- if h.req.closed || w == nil || w.toplevel == nil || !w.mapped {
234+ if h.req.closed || w == nil || !w.isToplevel() || !w.mapped {
235235 m.closed(id)
236236 }
237237 }
@@ -253,7 +253,7 @@ func (t *toplevelRequests) window() *window {
253253 if t.s.protected() || t.closed {
254254 return nil
255255 }
256- if w := t.s.windows[t.id]; w != nil && w.toplevel != nil && w.mapped && w.toplevel.Resource.Alive() {
256+ if w := t.s.windows[t.id]; w != nil && w.toplevelAlive() && w.mapped {
257257 return w
258258 }
259259 return nil
@@ -274,7 +274,7 @@ func (t *toplevelRequests) Activate(*wlr.ZwlrForeignToplevelHandleV1, *wayland.S
274274 }
275275 func (t *toplevelRequests) Close(*wlr.ZwlrForeignToplevelHandleV1) {
276276 if w := t.window(); w != nil {
277- w.toplevel.SendClose()
277+ w.sendClose()
278278 }
279279 }
280280 func (t *toplevelRequests) SetRectangle(r *wlr.ZwlrForeignToplevelHandleV1, _ *wayland.Surface, _, _, w, h int32) {
@@ -44,7 +44,7 @@ func registerForeignToplevel(d *server.Display, s *Server) error {
44 r.OnDestroy = func() { s.toplevelManagers = removeItem(s.toplevelManagers, m) }44 r.OnDestroy = func() { s.toplevelManagers = removeItem(s.toplevelManagers, m) }
45 ids := make([]ports.WindowID, 0, len(s.windows))45 ids := make([]ports.WindowID, 0, len(s.windows))
46 for id, w := range s.windows {46 for id, w := range s.windows {
47- if w.toplevel != nil && w.mapped {47+ if w.isToplevel() && w.mapped {
48 ids = append(ids, id)48 ids = append(ids, id)
49 }49 }
50 }50 }
@@ -186,7 +186,7 @@ func toplevelState(w *window, version int32) []byte {
186 186
187 // toplevelChanged updates every manager after w mapped or changed.187 // toplevelChanged updates every manager after w mapped or changed.
188 func (s *Server) toplevelChanged(w *window) {188 func (s *Server) toplevelChanged(w *window) {
189- if w.toplevel == nil || !w.mapped {189+ if !w.isToplevel() || !w.mapped {
190 return190 return
191 }191 }
192 for _, m := range s.toplevelManagers {192 for _, m := range s.toplevelManagers {
@@ -220,7 +220,7 @@ func (s *Server) refreshToplevels() {
220 for _, l := range s.extToplevelLists {220 for _, l := range s.extToplevelLists {
221 for id, h := range l.handles {221 for id, h := range l.handles {
222 w := s.windows[id]222 w := s.windows[id]
223- if h.closed || w == nil || w.toplevel == nil || !w.mapped {223+ if h.closed || w == nil || !w.isToplevel() || !w.mapped {
224 l.closed(id)224 l.closed(id)
225 }225 }
226 }226 }
@@ -231,7 +231,7 @@ func (s *Server) refreshToplevels() {
231 for _, m := range s.toplevelManagers {231 for _, m := range s.toplevelManagers {
232 for id, h := range m.handles {232 for id, h := range m.handles {
233 w := s.windows[id]233 w := s.windows[id]
234- if h.req.closed || w == nil || w.toplevel == nil || !w.mapped {234+ if h.req.closed || w == nil || !w.isToplevel() || !w.mapped {
235 m.closed(id)235 m.closed(id)
236 }236 }
237 }237 }
@@ -253,7 +253,7 @@ func (t *toplevelRequests) window() *window {
253 if t.s.protected() || t.closed {253 if t.s.protected() || t.closed {
254 return nil254 return nil
255 }255 }
256- if w := t.s.windows[t.id]; w != nil && w.toplevel != nil && w.mapped && w.toplevel.Resource.Alive() {256+ if w := t.s.windows[t.id]; w != nil && w.toplevelAlive() && w.mapped {
257 return w257 return w
258 }258 }
259 return nil259 return nil
@@ -274,7 +274,7 @@ func (t *toplevelRequests) Activate(*wlr.ZwlrForeignToplevelHandleV1, *wayland.S
274 }274 }
275 func (t *toplevelRequests) Close(*wlr.ZwlrForeignToplevelHandleV1) {275 func (t *toplevelRequests) Close(*wlr.ZwlrForeignToplevelHandleV1) {
276 if w := t.window(); w != nil {276 if w := t.window(); w != nil {
277- w.toplevel.SendClose()277+ w.sendClose()
278 }278 }
279 }279 }
280 func (t *toplevelRequests) SetRectangle(r *wlr.ZwlrForeignToplevelHandleV1, _ *wayland.Surface, _, _, w, h int32) {280 func (t *toplevelRequests) SetRectangle(r *wlr.ZwlrForeignToplevelHandleV1, _ *wayland.Surface, _, _, w, h int32) {
M internal/adapters/wayland/frames.go +1 -1
@@ -183,7 +183,7 @@ func (s *Server) pacePass(flipped map[string]bool, reports []ports.OutputPresent
183183 func toplevelRoot(surf *surface) *surface {
184184 surf = surf.root()
185185 for surf.xdg != nil && surf.xdg.window != nil && surf.xdg.window.popup != nil && surf.xdg.window.popup.parent != nil {
186- surf = surf.xdg.window.popup.parent.xdg.surface.root()
186+ surf = surf.xdg.window.popup.parent.surf().root()
187187 }
188188 return surf
189189 }
@@ -183,7 +183,7 @@ func (s *Server) pacePass(flipped map[string]bool, reports []ports.OutputPresent
183 func toplevelRoot(surf *surface) *surface {183 func toplevelRoot(surf *surface) *surface {
184 surf = surf.root()184 surf = surf.root()
185 for surf.xdg != nil && surf.xdg.window != nil && surf.xdg.window.popup != nil && surf.xdg.window.popup.parent != nil {185 for surf.xdg != nil && surf.xdg.window != nil && surf.xdg.window.popup != nil && surf.xdg.window.popup.parent != nil {
186- surf = surf.xdg.window.popup.parent.xdg.surface.root()186+ surf = surf.xdg.window.popup.parent.surf().root()
187 }187 }
188 return surf188 return surf
189 }189 }
M internal/adapters/wayland/globals.go +6 -1
@@ -131,8 +131,13 @@ func (c compositor) CreateSurface(r *wayland.Compositor, id uint32) {
131131 state.identity = c.server.nextSurface
132132 state.wl = w
133133 c.server.surfaces[w.Resource] = state
134+ c.server.surfaceByIdentity[state.identity] = state
134135 state.sendScale()
135- w.OnDestroy = func() { delete(c.server.surfaces, w.Resource); state.Destroy(w) }
136+ w.OnDestroy = func() {
137+ delete(c.server.surfaces, w.Resource)
138+ delete(c.server.surfaceByIdentity, state.identity)
139+ state.Destroy(w)
140+ }
136141 }
137142 }
138143 func (c compositor) CreateRegion(r *wayland.Compositor, id uint32) {
@@ -131,8 +131,13 @@ func (c compositor) CreateSurface(r *wayland.Compositor, id uint32) {
131 state.identity = c.server.nextSurface131 state.identity = c.server.nextSurface
132 state.wl = w132 state.wl = w
133 c.server.surfaces[w.Resource] = state133 c.server.surfaces[w.Resource] = state
134+ c.server.surfaceByIdentity[state.identity] = state
134 state.sendScale()135 state.sendScale()
135- w.OnDestroy = func() { delete(c.server.surfaces, w.Resource); state.Destroy(w) }136+ w.OnDestroy = func() {
137+ delete(c.server.surfaces, w.Resource)
138+ delete(c.server.surfaceByIdentity, state.identity)
139+ state.Destroy(w)
140+ }
136 }141 }
137 }142 }
138 func (c compositor) CreateRegion(r *wayland.Compositor, id uint32) {143 func (c compositor) CreateRegion(r *wayland.Compositor, id uint32) {
M internal/adapters/wayland/imagecopy.go +1 -1
@@ -351,7 +351,7 @@ func (c *captureTarget) geometry() (o *output, rect image.Rectangle, hidden, ok
351351 case srcWindow:
352352 // Core's state gives the size; before it, the last configure.
353353 w := c.s.windows[src.window]
354- if w == nil || !w.mapped || w.toplevel == nil || w.gen != src.gen {
354+ if w == nil || !w.mapped || !w.isToplevel() || w.gen != src.gen {
355355 return nil, image.Rectangle{}, false, false
356356 }
357357 name, size := w.last.Output, ports.Rect{W: w.last.Width, H: w.last.Height}
@@ -351,7 +351,7 @@ func (c *captureTarget) geometry() (o *output, rect image.Rectangle, hidden, ok
351 case srcWindow:351 case srcWindow:
352 // Core's state gives the size; before it, the last configure.352 // Core's state gives the size; before it, the last configure.
353 w := c.s.windows[src.window]353 w := c.s.windows[src.window]
354- if w == nil || !w.mapped || w.toplevel == nil || w.gen != src.gen {354+ if w == nil || !w.mapped || !w.isToplevel() || w.gen != src.gen {
355 return nil, image.Rectangle{}, false, false355 return nil, image.Rectangle{}, false, false
356 }356 }
357 name, size := w.last.Output, ports.Rect{W: w.last.Width, H: w.last.Height}357 name, size := w.last.Output, ports.Rect{W: w.last.Width, H: w.last.Height}
M internal/adapters/wayland/popup.go +2 -2
@@ -31,7 +31,7 @@ func (s *Server) newPopup(x *xdgSurface, r *xdgshell.Surface, id uint32, parentX
3131 var parent *window
3232 if parentXDG != nil {
3333 for _, w := range s.windows {
34- if w.xdg.resource != nil && w.xdg.resource.Resource == parentXDG.Resource {
34+ if w.xdg != nil && w.xdg.resource != nil && w.xdg.resource.Resource == parentXDG.Resource {
3535 parent = w
3636 }
3737 }
@@ -166,7 +166,7 @@ func (p *popup) configure(c ports.ConfigurePopup) {
166166 func (s *Server) topGrab(client server.Client, parent *window) bool {
167167 var top *window
168168 for _, w := range s.windows {
169- if w.popup != nil && w.popup.grab && !w.popup.done && w.xdg.resource.Client() == client && (top == nil || w.id > top.id) {
169+ if w.popup != nil && w.popup.grab && !w.popup.done && w.client() == client && (top == nil || w.id > top.id) {
170170 top = w
171171 }
172172 }
@@ -31,7 +31,7 @@ func (s *Server) newPopup(x *xdgSurface, r *xdgshell.Surface, id uint32, parentX
31 var parent *window31 var parent *window
32 if parentXDG != nil {32 if parentXDG != nil {
33 for _, w := range s.windows {33 for _, w := range s.windows {
34- if w.xdg.resource != nil && w.xdg.resource.Resource == parentXDG.Resource {34+ if w.xdg != nil && w.xdg.resource != nil && w.xdg.resource.Resource == parentXDG.Resource {
35 parent = w35 parent = w
36 }36 }
37 }37 }
@@ -166,7 +166,7 @@ func (p *popup) configure(c ports.ConfigurePopup) {
166 func (s *Server) topGrab(client server.Client, parent *window) bool {166 func (s *Server) topGrab(client server.Client, parent *window) bool {
167 var top *window167 var top *window
168 for _, w := range s.windows {168 for _, w := range s.windows {
169- if w.popup != nil && w.popup.grab && !w.popup.done && w.xdg.resource.Client() == client && (top == nil || w.id > top.id) {169+ if w.popup != nil && w.popup.grab && !w.popup.done && w.client() == client && (top == nil || w.id > top.id) {
170 top = w170 top = w
171 }171 }
172 }172 }
M internal/adapters/wayland/scale.go +1 -1
@@ -21,7 +21,7 @@ func (s *Server) outputOfSurface(surf *surface) *output {
2121 surf = surf.root()
2222 // Popups are on their parent's output.
2323 for surf.xdg != nil && surf.xdg.window != nil && surf.xdg.window.popup != nil && surf.xdg.window.popup.parent != nil {
24- surf = surf.xdg.window.popup.parent.xdg.surface.root()
24+ surf = surf.xdg.window.popup.parent.surf().root()
2525 }
2626 name := ""
2727 switch {
@@ -21,7 +21,7 @@ func (s *Server) outputOfSurface(surf *surface) *output {
21 surf = surf.root()21 surf = surf.root()
22 // Popups are on their parent's output.22 // Popups are on their parent's output.
23 for surf.xdg != nil && surf.xdg.window != nil && surf.xdg.window.popup != nil && surf.xdg.window.popup.parent != nil {23 for surf.xdg != nil && surf.xdg.window != nil && surf.xdg.window.popup != nil && surf.xdg.window.popup.parent != nil {
24- surf = surf.xdg.window.popup.parent.xdg.surface.root()24+ surf = surf.xdg.window.popup.parent.surf().root()
25 }25 }
26 name := ""26 name := ""
27 switch {27 switch {
M internal/adapters/wayland/screencast.go +1 -1
@@ -146,7 +146,7 @@ func (s *Server) openCast(cmd ports.CastCommand) {
146146 gone := ports.CaptureReasonOutputGone
147147 if cmd.Window != 0 {
148148 gone = ports.CaptureReasonWindowGone
149- if w := s.windows[cmd.Window]; w != nil && w.mapped && w.toplevel != nil {
149+ if w := s.windows[cmd.Window]; w != nil && w.mapped && w.isToplevel() {
150150 src = &captureSource{kind: srcWindow, window: cmd.Window, gen: w.gen}
151151 open.Window = cmd.Window
152152 }
@@ -146,7 +146,7 @@ func (s *Server) openCast(cmd ports.CastCommand) {
146 gone := ports.CaptureReasonOutputGone146 gone := ports.CaptureReasonOutputGone
147 if cmd.Window != 0 {147 if cmd.Window != 0 {
148 gone = ports.CaptureReasonWindowGone148 gone = ports.CaptureReasonWindowGone
149- if w := s.windows[cmd.Window]; w != nil && w.mapped && w.toplevel != nil {149+ if w := s.windows[cmd.Window]; w != nil && w.mapped && w.isToplevel() {
150 src = &captureSource{kind: srcWindow, window: cmd.Window, gen: w.gen}150 src = &captureSource{kind: srcWindow, window: cmd.Window, gen: w.gen}
151 open.Window = cmd.Window151 open.Window = cmd.Window
152 }152 }
M internal/adapters/wayland/seat.go +8 -8
@@ -276,10 +276,10 @@ func (s *Server) setModifiers(c ports.SetModifiers) {
276276 }
277277
278278 func (s *Server) windowKeyboards(w *window) []*wayland.Keyboard {
279- if w == nil || !w.mapped || !w.xdg.resource.Resource.Alive() {
279+ if w == nil || !w.mapped || !w.alive() {
280280 return nil
281281 }
282- return s.clientKeyboards(w.xdg.resource.Client())
282+ return s.clientKeyboards(w.client())
283283 }
284284
285285 // clientKeyboards returns c's wl_keyboards without allocating. The list holds
@@ -301,7 +301,7 @@ func (s *Server) focusTarget(id ports.WindowID) (*wayland.Surface, []*wayland.Ke
301301 return l.surface.wl, s.clientKeyboards(l.surface.wl.Client())
302302 }
303303 if w := s.windows[id]; w != nil && w.mapped {
304- if surf := w.xdg.surfaceResource(); surf != nil {
304+ if surf := w.surfaceResource(); surf != nil {
305305 return surf, s.windowKeyboards(w)
306306 }
307307 }
@@ -319,8 +319,8 @@ func (s *Server) pointerTarget(id ports.WindowID) (server.Client, []*wayland.Poi
319319 if l := s.lockSurfaces[id]; l != nil && s.lockInputTarget(id) {
320320 return l.resource.Client(), s.clientPointers(l.resource.Client()), true
321321 }
322- if w := s.windows[id]; w != nil && w.mapped && w.xdg.resource.Resource.Alive() {
323- return w.xdg.resource.Client(), s.windowPointers(w), true
322+ if w := s.windows[id]; w != nil && w.mapped && w.alive() {
323+ return w.client(), s.windowPointers(w), true
324324 }
325325 if l := s.layers[id]; l != nil && l.mapped && l.resource.Resource.Alive() {
326326 return l.resource.Client(), s.layerPointers(l), true
@@ -335,7 +335,7 @@ func (s *Server) pointerSurface(id ports.WindowID, x, y float64) (*wayland.Surfa
335335 return l.surface.wl, s.clientPointers(l.surface.wl.Client()), x, y
336336 }
337337 if w := s.windows[id]; w != nil {
338- if surf := w.xdg.surfaceResource(); surf != nil && surf.Resource.Alive() {
338+ if surf := w.surfaceResource(); surf != nil && surf.Resource.Alive() {
339339 x, y := w.surfacePoint(x, y)
340340 return surf, s.windowPointers(w), x, y
341341 }
@@ -543,10 +543,10 @@ func pointerFrame(p *wayland.Pointer) {
543543 }
544544 }
545545 func (s *Server) windowPointers(w *window) []*wayland.Pointer {
546- if w == nil || !w.mapped || !w.xdg.resource.Resource.Alive() {
546+ if w == nil || !w.mapped || !w.alive() {
547547 return nil
548548 }
549- return s.clientPointers(w.xdg.resource.Client())
549+ return s.clientPointers(w.client())
550550 }
551551
552552 func (s *Server) layerPointers(l *layerSurface) []*wayland.Pointer {
@@ -276,10 +276,10 @@ func (s *Server) setModifiers(c ports.SetModifiers) {
276 }276 }
277 277
278 func (s *Server) windowKeyboards(w *window) []*wayland.Keyboard {278 func (s *Server) windowKeyboards(w *window) []*wayland.Keyboard {
279- if w == nil || !w.mapped || !w.xdg.resource.Resource.Alive() {279+ if w == nil || !w.mapped || !w.alive() {
280 return nil280 return nil
281 }281 }
282- return s.clientKeyboards(w.xdg.resource.Client())282+ return s.clientKeyboards(w.client())
283 }283 }
284 284
285 // clientKeyboards returns c's wl_keyboards without allocating. The list holds285 // clientKeyboards returns c's wl_keyboards without allocating. The list holds
@@ -301,7 +301,7 @@ func (s *Server) focusTarget(id ports.WindowID) (*wayland.Surface, []*wayland.Ke
301 return l.surface.wl, s.clientKeyboards(l.surface.wl.Client())301 return l.surface.wl, s.clientKeyboards(l.surface.wl.Client())
302 }302 }
303 if w := s.windows[id]; w != nil && w.mapped {303 if w := s.windows[id]; w != nil && w.mapped {
304- if surf := w.xdg.surfaceResource(); surf != nil {304+ if surf := w.surfaceResource(); surf != nil {
305 return surf, s.windowKeyboards(w)305 return surf, s.windowKeyboards(w)
306 }306 }
307 }307 }
@@ -319,8 +319,8 @@ func (s *Server) pointerTarget(id ports.WindowID) (server.Client, []*wayland.Poi
319 if l := s.lockSurfaces[id]; l != nil && s.lockInputTarget(id) {319 if l := s.lockSurfaces[id]; l != nil && s.lockInputTarget(id) {
320 return l.resource.Client(), s.clientPointers(l.resource.Client()), true320 return l.resource.Client(), s.clientPointers(l.resource.Client()), true
321 }321 }
322- if w := s.windows[id]; w != nil && w.mapped && w.xdg.resource.Resource.Alive() {322+ if w := s.windows[id]; w != nil && w.mapped && w.alive() {
323- return w.xdg.resource.Client(), s.windowPointers(w), true323+ return w.client(), s.windowPointers(w), true
324 }324 }
325 if l := s.layers[id]; l != nil && l.mapped && l.resource.Resource.Alive() {325 if l := s.layers[id]; l != nil && l.mapped && l.resource.Resource.Alive() {
326 return l.resource.Client(), s.layerPointers(l), true326 return l.resource.Client(), s.layerPointers(l), true
@@ -335,7 +335,7 @@ func (s *Server) pointerSurface(id ports.WindowID, x, y float64) (*wayland.Surfa
335 return l.surface.wl, s.clientPointers(l.surface.wl.Client()), x, y335 return l.surface.wl, s.clientPointers(l.surface.wl.Client()), x, y
336 }336 }
337 if w := s.windows[id]; w != nil {337 if w := s.windows[id]; w != nil {
338- if surf := w.xdg.surfaceResource(); surf != nil && surf.Resource.Alive() {338+ if surf := w.surfaceResource(); surf != nil && surf.Resource.Alive() {
339 x, y := w.surfacePoint(x, y)339 x, y := w.surfacePoint(x, y)
340 return surf, s.windowPointers(w), x, y340 return surf, s.windowPointers(w), x, y
341 }341 }
@@ -543,10 +543,10 @@ func pointerFrame(p *wayland.Pointer) {
543 }543 }
544 }544 }
545 func (s *Server) windowPointers(w *window) []*wayland.Pointer {545 func (s *Server) windowPointers(w *window) []*wayland.Pointer {
546- if w == nil || !w.mapped || !w.xdg.resource.Resource.Alive() {546+ if w == nil || !w.mapped || !w.alive() {
547 return nil547 return nil
548 }548 }
549- return s.clientPointers(w.xdg.resource.Client())549+ return s.clientPointers(w.client())
550 }550 }
551 551
552 func (s *Server) layerPointers(l *layerSurface) []*wayland.Pointer {552 func (s *Server) layerPointers(l *layerSurface) []*wayland.Pointer {
M internal/adapters/wayland/server.go +13 -9
@@ -215,8 +215,11 @@ type Server struct {
215215 // foreignExports are the live xdg-foreign handles (foreign.go).
216216 foreignExports map[string]*foreignExport
217217 surfaces map[*server.Resource]*surface
218- buffers map[*server.Resource]clientBuffer
219- dmabuf *dmabufGlobal
218+ // surfaceByIdentity finds a surface by its identity (xwayland_shell_v1
219+ // pairing); kept with surfaces.
220+ surfaceByIdentity map[uint64]*surface
221+ buffers map[*server.Resource]clientBuffer
222+ dmabuf *dmabufGlobal
220223 // hdrOutputs records confirmed DRM modesets; absent outputs are SDR.
221224 hdrOutputs map[string]ports.OutputHDR
222225 colorID uint64
@@ -367,6 +370,7 @@ func New(opts Options, ch Channels, log zerowrap.Logger) (*Server, error) {
367370 logDamage: opts.LogDamage,
368371 channels: ch,
369372 surfaces: make(map[*server.Resource]*surface),
373+ surfaceByIdentity: make(map[uint64]*surface),
370374 buffers: make(map[*server.Resource]clientBuffer),
371375 windows: make(map[ports.WindowID]*window),
372376 layers: make(map[ports.WindowID]*layerSurface),
@@ -778,7 +782,7 @@ func (s *Server) apply(cmd ports.ClientCommand) {
778782 }
779783 case ports.ConfigureWindow:
780784 w := s.windows[c.ID]
781- if w == nil || w.toplevel == nil || !w.toplevel.Resource.Alive() || !w.xdg.resource.Resource.Alive() {
785+ if w == nil || !w.toplevelAlive() || !w.alive() {
782786 s.log.Debug().Uint64("id", uint64(c.ID)).Msg("configure missing window")
783787 return
784788 }
@@ -789,16 +793,16 @@ func (s *Server) apply(cmd ports.ClientCommand) {
789793 scanoutChanged := !w.hasLast || w.last.Fullscreen != c.Fullscreen || w.last.Output != c.Output || w.last.Visible != c.Visible
790794 w.last, w.hasLast = c, true
791795 if visibilityChanged {
792- s.relocateCallbacks(w.xdg.surface)
796+ s.relocateCallbacks(w.surf())
793797 s.dropFeedbacks(time.Now())
794798 s.wakePacer() // reconsider FIFO barriers at the new period
795799 }
796800 w.sendConfigure()
797801 s.toplevelChanged(w)
798- if scanoutChanged && w.xdg.surface != nil {
799- s.dmabuf.resendSurface(w.xdg.surface)
802+ if scanoutChanged && w.surf() != nil {
803+ s.dmabuf.resendSurface(w.surf())
800804 }
801- if surf := w.xdg.surface; surf != nil && c.Output != "" {
805+ if surf := w.surf(); surf != nil && c.Output != "" {
802806 surf.sendTreeScale()
803807 }
804808 if scanoutChanged {
@@ -819,10 +823,10 @@ func (s *Server) apply(cmd ports.ClientCommand) {
819823 s.setShortcutsInhibit(c)
820824 case ports.CloseWindow:
821825 w := s.windows[c.ID]
822- if w == nil || w.toplevel == nil || !w.toplevel.Resource.Alive() {
826+ if w == nil || !w.toplevelAlive() {
823827 s.log.Debug().Uint64("id", uint64(c.ID)).Msg("close missing window")
824828 return
825829 }
826- w.toplevel.SendClose()
830+ w.sendClose()
827831 }
828832 }
@@ -215,8 +215,11 @@ type Server struct {
215 // foreignExports are the live xdg-foreign handles (foreign.go).215 // foreignExports are the live xdg-foreign handles (foreign.go).
216 foreignExports map[string]*foreignExport216 foreignExports map[string]*foreignExport
217 surfaces map[*server.Resource]*surface217 surfaces map[*server.Resource]*surface
218- buffers map[*server.Resource]clientBuffer218+ // surfaceByIdentity finds a surface by its identity (xwayland_shell_v1
219- dmabuf *dmabufGlobal219+ // pairing); kept with surfaces.
220+ surfaceByIdentity map[uint64]*surface
221+ buffers map[*server.Resource]clientBuffer
222+ dmabuf *dmabufGlobal
220 // hdrOutputs records confirmed DRM modesets; absent outputs are SDR.223 // hdrOutputs records confirmed DRM modesets; absent outputs are SDR.
221 hdrOutputs map[string]ports.OutputHDR224 hdrOutputs map[string]ports.OutputHDR
222 colorID uint64225 colorID uint64
@@ -367,6 +370,7 @@ func New(opts Options, ch Channels, log zerowrap.Logger) (*Server, error) {
367 logDamage: opts.LogDamage,370 logDamage: opts.LogDamage,
368 channels: ch,371 channels: ch,
369 surfaces: make(map[*server.Resource]*surface),372 surfaces: make(map[*server.Resource]*surface),
373+ surfaceByIdentity: make(map[uint64]*surface),
370 buffers: make(map[*server.Resource]clientBuffer),374 buffers: make(map[*server.Resource]clientBuffer),
371 windows: make(map[ports.WindowID]*window),375 windows: make(map[ports.WindowID]*window),
372 layers: make(map[ports.WindowID]*layerSurface),376 layers: make(map[ports.WindowID]*layerSurface),
@@ -778,7 +782,7 @@ func (s *Server) apply(cmd ports.ClientCommand) {
778 }782 }
779 case ports.ConfigureWindow:783 case ports.ConfigureWindow:
780 w := s.windows[c.ID]784 w := s.windows[c.ID]
781- if w == nil || w.toplevel == nil || !w.toplevel.Resource.Alive() || !w.xdg.resource.Resource.Alive() {785+ if w == nil || !w.toplevelAlive() || !w.alive() {
782 s.log.Debug().Uint64("id", uint64(c.ID)).Msg("configure missing window")786 s.log.Debug().Uint64("id", uint64(c.ID)).Msg("configure missing window")
783 return787 return
784 }788 }
@@ -789,16 +793,16 @@ func (s *Server) apply(cmd ports.ClientCommand) {
789 scanoutChanged := !w.hasLast || w.last.Fullscreen != c.Fullscreen || w.last.Output != c.Output || w.last.Visible != c.Visible793 scanoutChanged := !w.hasLast || w.last.Fullscreen != c.Fullscreen || w.last.Output != c.Output || w.last.Visible != c.Visible
790 w.last, w.hasLast = c, true794 w.last, w.hasLast = c, true
791 if visibilityChanged {795 if visibilityChanged {
792- s.relocateCallbacks(w.xdg.surface)796+ s.relocateCallbacks(w.surf())
793 s.dropFeedbacks(time.Now())797 s.dropFeedbacks(time.Now())
794 s.wakePacer() // reconsider FIFO barriers at the new period798 s.wakePacer() // reconsider FIFO barriers at the new period
795 }799 }
796 w.sendConfigure()800 w.sendConfigure()
797 s.toplevelChanged(w)801 s.toplevelChanged(w)
798- if scanoutChanged && w.xdg.surface != nil {802+ if scanoutChanged && w.surf() != nil {
799- s.dmabuf.resendSurface(w.xdg.surface)803+ s.dmabuf.resendSurface(w.surf())
800 }804 }
801- if surf := w.xdg.surface; surf != nil && c.Output != "" {805+ if surf := w.surf(); surf != nil && c.Output != "" {
802 surf.sendTreeScale()806 surf.sendTreeScale()
803 }807 }
804 if scanoutChanged {808 if scanoutChanged {
@@ -819,10 +823,10 @@ func (s *Server) apply(cmd ports.ClientCommand) {
819 s.setShortcutsInhibit(c)823 s.setShortcutsInhibit(c)
820 case ports.CloseWindow:824 case ports.CloseWindow:
821 w := s.windows[c.ID]825 w := s.windows[c.ID]
822- if w == nil || w.toplevel == nil || !w.toplevel.Resource.Alive() {826+ if w == nil || !w.toplevelAlive() {
823 s.log.Debug().Uint64("id", uint64(c.ID)).Msg("close missing window")827 s.log.Debug().Uint64("id", uint64(c.ID)).Msg("close missing window")
824 return828 return
825 }829 }
826- w.toplevel.SendClose()830+ w.sendClose()
827 }831 }
828 }832 }
M internal/adapters/wayland/surface.go +4 -0
@@ -28,6 +28,10 @@ const (
2828 roleSessionLock
2929 // roleDragIcon is the icon of a data drag (dnd.go).
3030 roleDragIcon
31+ // roleX11 is the surface of an X11 window paired through
32+ // xwayland_shell_v1.
33+ //lint:ignore U1000 used by the X11 window role (xwm)
34+ roleX11
3135 )
3236
3337 type surface struct {
@@ -28,6 +28,10 @@ const (
28 roleSessionLock28 roleSessionLock
29 // roleDragIcon is the icon of a data drag (dnd.go).29 // roleDragIcon is the icon of a data drag (dnd.go).
30 roleDragIcon30 roleDragIcon
31+ // roleX11 is the surface of an X11 window paired through
32+ // xwayland_shell_v1.
33+ //lint:ignore U1000 used by the X11 window role (xwm)
34+ roleX11
31 )35 )
32 36
33 type surface struct {37 type surface struct {
A internal/adapters/wayland/window.go +71 -0
@@ -0,0 +1,71 @@
1+package wayland
2+
3+import (
4+ "github.com/bnema/go-wayland-bindings/server/wayland"
5+ "github.com/bnema/neferwl/internal/ports"
6+ "github.com/bnema/purego-libwayland/server"
7+)
8+
9+// Role-independent accessors of a window. Code outside xdg.go goes through
10+// them instead of dereferencing w.xdg, which is nil for windows of another
11+// role.
12+
13+// isToplevel reports whether w is a toplevel (not a popup).
14+func (w *window) isToplevel() bool { return w.toplevel != nil }
15+
16+// surf is the window's surface, nil when it has none.
17+func (w *window) surf() *surface {
18+ if w.xdg != nil {
19+ return w.xdg.surface
20+ }
21+ return nil
22+}
23+
24+// alive reports whether the window's role object is alive.
25+func (w *window) alive() bool {
26+ return w.xdg != nil && w.xdg.resource.Resource.Alive()
27+}
28+
29+// client is the window's client; the zero Client when it has no role object.
30+func (w *window) client() server.Client {
31+ if w.xdg != nil {
32+ return w.xdg.resource.Client()
33+ }
34+ return server.Client{}
35+}
36+
37+// geometry is the committed window geometry; empty means the whole surface.
38+func (w *window) geometry() ports.Rect {
39+ if w.xdg != nil {
40+ return w.xdg.geometry
41+ }
42+ return ports.Rect{}
43+}
44+
45+// sendConfigure sends w.last to the client.
46+func (w *window) sendConfigure() {
47+ if w.xdg != nil {
48+ w.sendXDGConfigure()
49+ }
50+}
51+
52+// sendClose asks the client to close the window.
53+func (w *window) sendClose() {
54+ if w.toplevel != nil {
55+ w.toplevel.SendClose()
56+ }
57+}
58+
59+// toplevelAlive reports whether the window is a toplevel whose protocol
60+// object is alive.
61+func (w *window) toplevelAlive() bool {
62+ return w.toplevel != nil && w.toplevel.Resource.Alive()
63+}
64+
65+// surfaceResource is the window's wl_surface, nil when it has none or it died.
66+func (w *window) surfaceResource() *wayland.Surface {
67+ if w.xdg != nil {
68+ return w.xdg.surfaceResource()
69+ }
70+ return nil
71+}
@@ -0,0 +1,71 @@
1+package wayland
2+
3+import (
4+ "github.com/bnema/go-wayland-bindings/server/wayland"
5+ "github.com/bnema/neferwl/internal/ports"
6+ "github.com/bnema/purego-libwayland/server"
7+)
8+
9+// Role-independent accessors of a window. Code outside xdg.go goes through
10+// them instead of dereferencing w.xdg, which is nil for windows of another
11+// role.
12+
13+// isToplevel reports whether w is a toplevel (not a popup).
14+func (w *window) isToplevel() bool { return w.toplevel != nil }
15+
16+// surf is the window's surface, nil when it has none.
17+func (w *window) surf() *surface {
18+ if w.xdg != nil {
19+ return w.xdg.surface
20+ }
21+ return nil
22+}
23+
24+// alive reports whether the window's role object is alive.
25+func (w *window) alive() bool {
26+ return w.xdg != nil && w.xdg.resource.Resource.Alive()
27+}
28+
29+// client is the window's client; the zero Client when it has no role object.
30+func (w *window) client() server.Client {
31+ if w.xdg != nil {
32+ return w.xdg.resource.Client()
33+ }
34+ return server.Client{}
35+}
36+
37+// geometry is the committed window geometry; empty means the whole surface.
38+func (w *window) geometry() ports.Rect {
39+ if w.xdg != nil {
40+ return w.xdg.geometry
41+ }
42+ return ports.Rect{}
43+}
44+
45+// sendConfigure sends w.last to the client.
46+func (w *window) sendConfigure() {
47+ if w.xdg != nil {
48+ w.sendXDGConfigure()
49+ }
50+}
51+
52+// sendClose asks the client to close the window.
53+func (w *window) sendClose() {
54+ if w.toplevel != nil {
55+ w.toplevel.SendClose()
56+ }
57+}
58+
59+// toplevelAlive reports whether the window is a toplevel whose protocol
60+// object is alive.
61+func (w *window) toplevelAlive() bool {
62+ return w.toplevel != nil && w.toplevel.Resource.Alive()
63+}
64+
65+// surfaceResource is the window's wl_surface, nil when it has none or it died.
66+func (w *window) surfaceResource() *wayland.Surface {
67+ if w.xdg != nil {
68+ return w.xdg.surfaceResource()
69+ }
70+ return nil
71+}
M internal/adapters/wayland/xdg.go +2 -2
@@ -151,8 +151,8 @@ type window struct {
151151 hasLast bool
152152 }
153153
154-// sendConfigure sends w.last as xdg_toplevel + xdg_surface configure.
155-func (w *window) sendConfigure() {
154+// sendXDGConfigure sends w.last as xdg_toplevel + xdg_surface configure.
155+func (w *window) sendXDGConfigure() {
156156 c := w.last
157157 w.toplevel.SendConfigure(int32(c.Width), int32(c.Height), toplevelStates(c, w.toplevel.Version()))
158158 s := w.xdg.server
@@ -151,8 +151,8 @@ type window struct {
151 hasLast bool151 hasLast bool
152 }152 }
153 153
154-// sendConfigure sends w.last as xdg_toplevel + xdg_surface configure.154+// sendXDGConfigure sends w.last as xdg_toplevel + xdg_surface configure.
155-func (w *window) sendConfigure() {155+func (w *window) sendXDGConfigure() {
156 c := w.last156 c := w.last
157 w.toplevel.SendConfigure(int32(c.Width), int32(c.Height), toplevelStates(c, w.toplevel.Version()))157 w.toplevel.SendConfigure(int32(c.Width), int32(c.Height), toplevelStates(c, w.toplevel.Version()))
158 s := w.xdg.server158 s := w.xdg.server

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 25

Tags 10

↑↓ moveenter openesc close