detached 07dab5d

fix(xwm): never adopt the XSETTINGS window as a client window

commit
07dab5daf5460b32e0b96a38210add70aec10087
author
brice · · 2026-10-22 16:51 UTC
committer
brice · · 2026-10-22 17:55 UTC
parent
a50e31e
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +21 -2
M internal/adapters/wayland/xwm/events.go +7 -2
@@ -184,12 +184,17 @@ func (x *XWM) onError(e *xturbo.XError) {
184184 }
185185 }
186186
187+// ownWindow reports whether xid is one of the XWM's own helper windows.
188+func (x *XWM) ownWindow(xid uint32) bool {
189+ return xid != 0 && (xid == x.wmWin || xid == x.selWin || xid == x.xs.win)
190+}
191+
187192 // onCreateNotify records a new top-level window. Override-redirect windows
188193 // are kept (their geometry and serial matter later) but never reach the host.
189194 func (x *XWM) onCreateNotify(ev *xturbo.Event) {
190195 e := xproto.CreateNotifyOf(ev)
191196 xid := uint32(e.Window())
192- if xid == x.wmWin || xid == x.selWin || x.tab.lookup(xid) != 0 {
197+ if x.ownWindow(xid) || x.tab.lookup(xid) != 0 {
193198 return
194199 }
195200 s := x.insert(xid)
@@ -237,7 +242,7 @@ func (x *XWM) onTreeReply(r *xturbo.Reply) {
237242 children := q.Children()
238243 for i := 0; i < children.Len(); i++ {
239244 xid := uint32(children.At(i))
240- if xid == x.wmWin || xid == x.selWin || x.tab.lookup(xid) != 0 {
245+ if x.ownWindow(xid) || x.tab.lookup(xid) != 0 {
241246 continue
242247 }
243248 s := x.insert(xid)
@@ -184,12 +184,17 @@ func (x *XWM) onError(e *xturbo.XError) {
184 }184 }
185 }185 }
186 186
187+// ownWindow reports whether xid is one of the XWM's own helper windows.
188+func (x *XWM) ownWindow(xid uint32) bool {
189+ return xid != 0 && (xid == x.wmWin || xid == x.selWin || xid == x.xs.win)
190+}
191+
187 // onCreateNotify records a new top-level window. Override-redirect windows192 // onCreateNotify records a new top-level window. Override-redirect windows
188 // are kept (their geometry and serial matter later) but never reach the host.193 // are kept (their geometry and serial matter later) but never reach the host.
189 func (x *XWM) onCreateNotify(ev *xturbo.Event) {194 func (x *XWM) onCreateNotify(ev *xturbo.Event) {
190 e := xproto.CreateNotifyOf(ev)195 e := xproto.CreateNotifyOf(ev)
191 xid := uint32(e.Window())196 xid := uint32(e.Window())
192- if xid == x.wmWin || xid == x.selWin || x.tab.lookup(xid) != 0 {197+ if x.ownWindow(xid) || x.tab.lookup(xid) != 0 {
193 return198 return
194 }199 }
195 s := x.insert(xid)200 s := x.insert(xid)
@@ -237,7 +242,7 @@ func (x *XWM) onTreeReply(r *xturbo.Reply) {
237 children := q.Children()242 children := q.Children()
238 for i := 0; i < children.Len(); i++ {243 for i := 0; i < children.Len(); i++ {
239 xid := uint32(children.At(i))244 xid := uint32(children.At(i))
240- if xid == x.wmWin || xid == x.selWin || x.tab.lookup(xid) != 0 {245+ if x.ownWindow(xid) || x.tab.lookup(xid) != 0 {
241 continue246 continue
242 }247 }
243 s := x.insert(xid)248 s := x.insert(xid)
M internal/adapters/wayland/xwm/xsettings_test.go +14 -0
@@ -203,3 +203,17 @@ func TestXSettingsNoChangeAllocations(t *testing.T) {
203203 })
204204 require.Zero(t, n)
205205 }
206+
207+// The XSETTINGS manager window is never adopted as a client window.
208+func TestXSettingsWindowNotAdopted(t *testing.T) {
209+ x, p := bootSettings(t, ports.XwaylandSettings{XSettings: true}, nil)
210+ h := newMockhost(t)
211+ post, _ := newPost()
212+ x.attach(newMockfdSource(t), h, post)
213+ require.NotZero(t, x.xs.win)
214+ p.createNotify(x.xs.win, false)
215+ ev := x.Stats().Events
216+ readUntil(t, x, func() bool { return x.Stats().Events > ev })
217+ x.flush()
218+ require.Zero(t, x.tab.lookup(x.xs.win))
219+}
@@ -203,3 +203,17 @@ func TestXSettingsNoChangeAllocations(t *testing.T) {
203 })203 })
204 require.Zero(t, n)204 require.Zero(t, n)
205 }205 }
206+
207+// The XSETTINGS manager window is never adopted as a client window.
208+func TestXSettingsWindowNotAdopted(t *testing.T) {
209+ x, p := bootSettings(t, ports.XwaylandSettings{XSettings: true}, nil)
210+ h := newMockhost(t)
211+ post, _ := newPost()
212+ x.attach(newMockfdSource(t), h, post)
213+ require.NotZero(t, x.xs.win)
214+ p.createNotify(x.xs.win, false)
215+ ev := x.Stats().Events
216+ readUntil(t, x, func() bool { return x.Stats().Events > ev })
217+ x.flush()
218+ require.Zero(t, x.tab.lookup(x.xs.win))
219+}

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 26

Tags 10

↑↓ moveenter openesc close