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 Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· 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) { | |||
| 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 | 192 | // onCreateNotify records a new top-level window. Override-redirect windows | |
| 188 | 193 | // are kept (their geometry and serial matter later) but never reach the host. | |
| 189 | 194 | func (x *XWM) onCreateNotify(ev *xturbo.Event) { | |
| 190 | 195 | e := xproto.CreateNotifyOf(ev) | |
| 191 | 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 | 198 | return | |
| 194 | 199 | } | |
| 195 | 200 | s := x.insert(xid) | |
| @@ -237,7 +242,7 @@ func (x *XWM) onTreeReply(r *xturbo.Reply) { | |||
| 237 | 242 | children := q.Children() | |
| 238 | 243 | for i := 0; i < children.Len(); i++ { | |
| 239 | 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 | 246 | continue | |
| 242 | 247 | } | |
| 243 | 248 | 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 windows | 192 | // 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 | return | 198 | 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 | continue | 246 | 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) { | |||
| 203 | 203 | }) | |
| 204 | 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 | + | } | |
| @@ -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 | + | } | |||