refactor(xwm): share one Space with the wayland server and drop dead code
- commit
- dc722bb0b8eb6a07fabe0d2f03a8e7b7372a273e
- author
- brice · · 2026-10-22 15:27 UTC
- committer
- brice · · 2026-10-22 17:54 UTC
- parent
- 8ab1d07
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
16 files · +81 -117
M internal/adapters/wayland/outputs.go +3 -17
| @@ -91,25 +91,10 @@ func (o *output) sendXDG(x *xdgoutput.ZxdgOutputV1, withName bool) { | |||
| 91 | 91 | } | |
| 92 | 92 | } | |
| 93 | 93 | ||
| 94 | - | // xOrigin is the layout point that is X (0,0): the top-left of the layout | |
| 95 | - | // extents, so every X coordinate is non-negative. A plain copy of the | |
| 96 | - | // xwm.Space rule at scale 1 until that type merges. | |
| 97 | - | func (s *Server) xOrigin() (ox, oy int) { | |
| 98 | - | for i, p := range s.outputPlaces { | |
| 99 | - | if i == 0 || p.X < ox { | |
| 100 | - | ox = p.X | |
| 101 | - | } | |
| 102 | - | if i == 0 || p.Y < oy { | |
| 103 | - | oy = p.Y | |
| 104 | - | } | |
| 105 | - | } | |
| 106 | - | return ox, oy | |
| 107 | - | } | |
| 108 | - | ||
| 109 | 94 | // sendXDGX sends the private xdg-output state: the layout position moved to | |
| 110 | 95 | // X space and the logical size. | |
| 111 | 96 | func (o *output) sendXDGX(s *Server, x *xdgoutput.ZxdgOutputV1, withName bool) { | |
| 112 | - | ox, oy := s.xOrigin() | |
| 97 | + | ox, oy := s.xspace.Origin() | |
| 113 | 98 | x.SendLogicalPosition(int32(o.place.X-ox), int32(o.place.Y-oy)) | |
| 114 | 99 | x.SendLogicalSize(int32(o.place.Width), int32(o.place.Height)) | |
| 115 | 100 | if withName && x.Version() >= 2 { | |
| @@ -209,8 +194,9 @@ func (s *Server) setOutputs(c ports.SetOutputs) { | |||
| 209 | 194 | changed := !slices.Equal(s.outputPlaces, c.Outputs) | |
| 210 | 195 | focusChanged := s.focusedOutput != c.Focused | |
| 211 | 196 | s.outputPlaces = append(ports.Layout(nil), c.Outputs...) | |
| 197 | + | xChanged := s.xspace.Set(s.outputPlaces) | |
| 212 | 198 | if s.xwm != nil { | |
| 213 | - | s.xwm.SetLayout(s.outputPlaces) | |
| 199 | + | s.xwm.LayoutChanged(xChanged) | |
| 214 | 200 | } | |
| 215 | 201 | defer func() { | |
| 216 | 202 | if changed { | |
| @@ -91,25 +91,10 @@ func (o *output) sendXDG(x *xdgoutput.ZxdgOutputV1, withName bool) { | |||||
| 91 | } | 91 | } | ||
| 92 | } | 92 | } | ||
| 93 | 93 | ||||
| 94 | - | // xOrigin is the layout point that is X (0,0): the top-left of the layout | |||
| 95 | - | // extents, so every X coordinate is non-negative. A plain copy of the | |||
| 96 | - | // xwm.Space rule at scale 1 until that type merges. | |||
| 97 | - | func (s *Server) xOrigin() (ox, oy int) { | |||
| 98 | - | for i, p := range s.outputPlaces { | |||
| 99 | - | if i == 0 || p.X < ox { | |||
| 100 | - | ox = p.X | |||
| 101 | - | } | |||
| 102 | - | if i == 0 || p.Y < oy { | |||
| 103 | - | oy = p.Y | |||
| 104 | - | } | |||
| 105 | - | } | |||
| 106 | - | return ox, oy | |||
| 107 | - | } | |||
| 108 | - | ||||
| 109 | // sendXDGX sends the private xdg-output state: the layout position moved to | 94 | // sendXDGX sends the private xdg-output state: the layout position moved to | ||
| 110 | // X space and the logical size. | 95 | // X space and the logical size. | ||
| 111 | func (o *output) sendXDGX(s *Server, x *xdgoutput.ZxdgOutputV1, withName bool) { | 96 | func (o *output) sendXDGX(s *Server, x *xdgoutput.ZxdgOutputV1, withName bool) { | ||
| 112 | - | ox, oy := s.xOrigin() | 97 | + | ox, oy := s.xspace.Origin() |
| 113 | x.SendLogicalPosition(int32(o.place.X-ox), int32(o.place.Y-oy)) | 98 | x.SendLogicalPosition(int32(o.place.X-ox), int32(o.place.Y-oy)) | ||
| 114 | x.SendLogicalSize(int32(o.place.Width), int32(o.place.Height)) | 99 | x.SendLogicalSize(int32(o.place.Width), int32(o.place.Height)) | ||
| 115 | if withName && x.Version() >= 2 { | 100 | if withName && x.Version() >= 2 { | ||
| @@ -209,8 +194,9 @@ func (s *Server) setOutputs(c ports.SetOutputs) { | |||||
| 209 | changed := !slices.Equal(s.outputPlaces, c.Outputs) | 194 | changed := !slices.Equal(s.outputPlaces, c.Outputs) | ||
| 210 | focusChanged := s.focusedOutput != c.Focused | 195 | focusChanged := s.focusedOutput != c.Focused | ||
| 211 | s.outputPlaces = append(ports.Layout(nil), c.Outputs...) | 196 | s.outputPlaces = append(ports.Layout(nil), c.Outputs...) | ||
| 197 | + | xChanged := s.xspace.Set(s.outputPlaces) | |||
| 212 | if s.xwm != nil { | 198 | if s.xwm != nil { | ||
| 213 | - | s.xwm.SetLayout(s.outputPlaces) | 199 | + | s.xwm.LayoutChanged(xChanged) |
| 214 | } | 200 | } | ||
| 215 | defer func() { | 201 | defer func() { | ||
| 216 | if changed { | 202 | if changed { | ||
M internal/adapters/wayland/server.go +1 -0
| @@ -293,6 +293,7 @@ type Server struct { | |||
| 293 | 293 | workspaceIDs *workspaceid.IDs | |
| 294 | 294 | outputHeads ports.OutputHeads | |
| 295 | 295 | outputPlaces ports.Layout | |
| 296 | + | xspace xwm.Space // the one layout <-> X mapping, shared with the XWM | |
| 296 | 297 | managementSerial uint32 | |
| 297 | 298 | nextOutputApply uint64 | |
| 298 | 299 | outputReplies map[uint64]*outputConfiguration | |
| @@ -293,6 +293,7 @@ type Server struct { | |||||
| 293 | workspaceIDs *workspaceid.IDs | 293 | workspaceIDs *workspaceid.IDs | ||
| 294 | outputHeads ports.OutputHeads | 294 | outputHeads ports.OutputHeads | ||
| 295 | outputPlaces ports.Layout | 295 | outputPlaces ports.Layout | ||
| 296 | + | xspace xwm.Space // the one layout <-> X mapping, shared with the XWM | |||
| 296 | managementSerial uint32 | 297 | managementSerial uint32 | ||
| 297 | nextOutputApply uint64 | 298 | nextOutputApply uint64 | ||
| 298 | outputReplies map[uint64]*outputConfiguration | 299 | outputReplies map[uint64]*outputConfiguration | ||
M internal/adapters/wayland/x11window.go +4 -3
| @@ -97,6 +97,7 @@ func (xwaylandSurface) Destroy(*xwaylandshell.XwaylandSurfaceV1) {} | |||
| 97 | 97 | func (s *Server) installXWM(x *xwm.XWM) { | |
| 98 | 98 | s.teardownXWM() | |
| 99 | 99 | s.xwm = x | |
| 100 | + | x.UseSpace(&s.xspace) | |
| 100 | 101 | if err := x.Attach(s.display, hostImpl{s}); err != nil { | |
| 101 | 102 | s.log.Warn().Err(err).Msg("X11 window management disabled") | |
| 102 | 103 | s.xwm = nil | |
| @@ -109,7 +110,7 @@ func (s *Server) installXWM(x *xwm.XWM) { | |||
| 109 | 110 | x.SetSerial(surf.identity, surf.x11Serial) | |
| 110 | 111 | } | |
| 111 | 112 | } | |
| 112 | - | x.SetLayout(s.outputPlaces) | |
| 113 | + | x.LayoutChanged(true) | |
| 113 | 114 | } | |
| 114 | 115 | ||
| 115 | 116 | // teardownXWM closes the window manager, dropping its windows. Idempotent. | |
| @@ -329,7 +330,7 @@ func (h hostImpl) OverrideMapped(slot xwm.Slot, xid uint32, surfaceID uint64, x, | |||
| 329 | 330 | return | |
| 330 | 331 | } | |
| 331 | 332 | if old := s.x11Slots[slot]; old != nil { | |
| 332 | - | h.OverrideGone(slot, true) | |
| 333 | + | h.OverrideGone(slot) | |
| 333 | 334 | } | |
| 334 | 335 | win := &window{id: s.nextWindow} | |
| 335 | 336 | win.x11 = &x11Window{server: s, slot: slot, xid: xid, override: true, noInput: noInput, rect: ports.Rect{X: x, Y: y, W: w, H: h2}} | |
| @@ -372,7 +373,7 @@ func (h hostImpl) OverrideMoved(slot xwm.Slot, x, y, w, h2 int) { | |||
| 372 | 373 | } | |
| 373 | 374 | ||
| 374 | 375 | // OverrideGone ends an override window: unmapped, or destroyed. | |
| 375 | - | func (h hostImpl) OverrideGone(slot xwm.Slot, destroyed bool) { | |
| 376 | + | func (h hostImpl) OverrideGone(slot xwm.Slot) { | |
| 376 | 377 | s := h.s | |
| 377 | 378 | win := s.x11Slots[slot] | |
| 378 | 379 | if win == nil || win.x11 == nil || !win.x11.override { | |
| @@ -97,6 +97,7 @@ func (xwaylandSurface) Destroy(*xwaylandshell.XwaylandSurfaceV1) {} | |||||
| 97 | func (s *Server) installXWM(x *xwm.XWM) { | 97 | func (s *Server) installXWM(x *xwm.XWM) { | ||
| 98 | s.teardownXWM() | 98 | s.teardownXWM() | ||
| 99 | s.xwm = x | 99 | s.xwm = x | ||
| 100 | + | x.UseSpace(&s.xspace) | |||
| 100 | if err := x.Attach(s.display, hostImpl{s}); err != nil { | 101 | if err := x.Attach(s.display, hostImpl{s}); err != nil { | ||
| 101 | s.log.Warn().Err(err).Msg("X11 window management disabled") | 102 | s.log.Warn().Err(err).Msg("X11 window management disabled") | ||
| 102 | s.xwm = nil | 103 | s.xwm = nil | ||
| @@ -109,7 +110,7 @@ func (s *Server) installXWM(x *xwm.XWM) { | |||||
| 109 | x.SetSerial(surf.identity, surf.x11Serial) | 110 | x.SetSerial(surf.identity, surf.x11Serial) | ||
| 110 | } | 111 | } | ||
| 111 | } | 112 | } | ||
| 112 | - | x.SetLayout(s.outputPlaces) | 113 | + | x.LayoutChanged(true) |
| 113 | } | 114 | } | ||
| 114 | 115 | ||||
| 115 | // teardownXWM closes the window manager, dropping its windows. Idempotent. | 116 | // teardownXWM closes the window manager, dropping its windows. Idempotent. | ||
| @@ -329,7 +330,7 @@ func (h hostImpl) OverrideMapped(slot xwm.Slot, xid uint32, surfaceID uint64, x, | |||||
| 329 | return | 330 | return | ||
| 330 | } | 331 | } | ||
| 331 | if old := s.x11Slots[slot]; old != nil { | 332 | if old := s.x11Slots[slot]; old != nil { | ||
| 332 | - | h.OverrideGone(slot, true) | 333 | + | h.OverrideGone(slot) |
| 333 | } | 334 | } | ||
| 334 | win := &window{id: s.nextWindow} | 335 | win := &window{id: s.nextWindow} | ||
| 335 | win.x11 = &x11Window{server: s, slot: slot, xid: xid, override: true, noInput: noInput, rect: ports.Rect{X: x, Y: y, W: w, H: h2}} | 336 | win.x11 = &x11Window{server: s, slot: slot, xid: xid, override: true, noInput: noInput, rect: ports.Rect{X: x, Y: y, W: w, H: h2}} | ||
| @@ -372,7 +373,7 @@ func (h hostImpl) OverrideMoved(slot xwm.Slot, x, y, w, h2 int) { | |||||
| 372 | } | 373 | } | ||
| 373 | 374 | ||||
| 374 | // OverrideGone ends an override window: unmapped, or destroyed. | 375 | // OverrideGone ends an override window: unmapped, or destroyed. | ||
| 375 | - | func (h hostImpl) OverrideGone(slot xwm.Slot, destroyed bool) { | 376 | + | func (h hostImpl) OverrideGone(slot xwm.Slot) { |
| 376 | s := h.s | 377 | s := h.s | ||
| 377 | win := s.x11Slots[slot] | 378 | win := s.x11Slots[slot] | ||
| 378 | if win == nil || win.x11 == nil || !win.x11.override { | 379 | if win == nil || win.x11 == nil || !win.x11.override { | ||
M internal/adapters/wayland/xdgoutputx_test.go +9 -0
| @@ -132,3 +132,12 @@ func TestGlobalFilterAllocations(t *testing.T) { | |||
| 132 | 132 | t.Errorf("globalVisible: %.1f allocs, want 0", allocs) | |
| 133 | 133 | } | |
| 134 | 134 | } | |
| 135 | + | ||
| 136 | + | // The X origin is derived by one Space, kept without a window manager. | |
| 137 | + | func TestXSpaceFollowsLayoutWithoutXWM(t *testing.T) { | |
| 138 | + | r := newX11Rig(t) | |
| 139 | + | applyOutputs(t, r.s, r.commands, twoOutputs(-1920)) | |
| 140 | + | if ox, oy := r.s.xspace.Origin(); ox != -1920 || oy != 0 { | |
| 141 | + | t.Fatalf("origin %d,%d, want -1920,0", ox, oy) | |
| 142 | + | } | |
| 143 | + | } | |
| @@ -132,3 +132,12 @@ func TestGlobalFilterAllocations(t *testing.T) { | |||||
| 132 | t.Errorf("globalVisible: %.1f allocs, want 0", allocs) | 132 | t.Errorf("globalVisible: %.1f allocs, want 0", allocs) | ||
| 133 | } | 133 | } | ||
| 134 | } | 134 | } | ||
| 135 | + | ||||
| 136 | + | // The X origin is derived by one Space, kept without a window manager. | |||
| 137 | + | func TestXSpaceFollowsLayoutWithoutXWM(t *testing.T) { | |||
| 138 | + | r := newX11Rig(t) | |||
| 139 | + | applyOutputs(t, r.s, r.commands, twoOutputs(-1920)) | |||
| 140 | + | if ox, oy := r.s.xspace.Origin(); ox != -1920 || oy != 0 { | |||
| 141 | + | t.Fatalf("origin %d,%d, want -1920,0", ox, oy) | |||
| 142 | + | } | |||
| 143 | + | } | |||
M internal/adapters/wayland/xwm/configure.go +13 -1
| @@ -106,7 +106,19 @@ func (x *XWM) SetLayout(l ports.Layout) { | |||
| 106 | 106 | if x.closed { | |
| 107 | 107 | return | |
| 108 | 108 | } | |
| 109 | - | changed := x.space.Set(l) | |
| 109 | + | x.LayoutChanged(x.space.Set(l)) | |
| 110 | + | } | |
| 111 | + | ||
| 112 | + | // UseSpace makes sp the coordinate mapping, owned by the host, which calls | |
| 113 | + | // Set on it and then LayoutChanged. Call before Attach. | |
| 114 | + | func (x *XWM) UseSpace(sp *Space) { x.space = sp } | |
| 115 | + | ||
| 116 | + | // LayoutChanged repositions windows after the shared Space was set; changed | |
| 117 | + | // is Set's result. Display goroutine only. | |
| 118 | + | func (x *XWM) LayoutChanged(changed bool) { | |
| 119 | + | if x.closed { | |
| 120 | + | return | |
| 121 | + | } | |
| 110 | 122 | if n := x.space.Dropped(); n > 0 && !x.warnedSpace { | |
| 111 | 123 | x.warnedSpace = true | |
| 112 | 124 | x.log.Warn().Int("dropped", n).Int("max", maxRegions).Msg("more outputs than the X11 layout holds, extra outputs ignored") | |
| @@ -106,7 +106,19 @@ func (x *XWM) SetLayout(l ports.Layout) { | |||||
| 106 | if x.closed { | 106 | if x.closed { | ||
| 107 | return | 107 | return | ||
| 108 | } | 108 | } | ||
| 109 | - | changed := x.space.Set(l) | 109 | + | x.LayoutChanged(x.space.Set(l)) |
| 110 | + | } | |||
| 111 | + | ||||
| 112 | + | // UseSpace makes sp the coordinate mapping, owned by the host, which calls | |||
| 113 | + | // Set on it and then LayoutChanged. Call before Attach. | |||
| 114 | + | func (x *XWM) UseSpace(sp *Space) { x.space = sp } | |||
| 115 | + | ||||
| 116 | + | // LayoutChanged repositions windows after the shared Space was set; changed | |||
| 117 | + | // is Set's result. Display goroutine only. | |||
| 118 | + | func (x *XWM) LayoutChanged(changed bool) { | |||
| 119 | + | if x.closed { | |||
| 120 | + | return | |||
| 121 | + | } | |||
| 110 | if n := x.space.Dropped(); n > 0 && !x.warnedSpace { | 122 | if n := x.space.Dropped(); n > 0 && !x.warnedSpace { | ||
| 111 | x.warnedSpace = true | 123 | x.warnedSpace = true | ||
| 112 | x.log.Warn().Int("dropped", n).Int("max", maxRegions).Msg("more outputs than the X11 layout holds, extra outputs ignored") | 124 | x.log.Warn().Int("dropped", n).Int("max", maxRegions).Msg("more outputs than the X11 layout holds, extra outputs ignored") | ||
M internal/adapters/wayland/xwm/events.go +2 -3
| @@ -173,9 +173,9 @@ func (x *XWM) onCreateNotify(ev *xturbo.Event) { | |||
| 173 | 173 | return | |
| 174 | 174 | } | |
| 175 | 175 | r := x.tab.rec(s) | |
| 176 | - | r.override = e.OverrideRedirect() | |
| 176 | + | override := e.OverrideRedirect() | |
| 177 | 177 | r.x, r.y, r.w, r.h = int32(e.X()), int32(e.Y()), uint32(e.Width()), uint32(e.Height()) | |
| 178 | - | if r.override { | |
| 178 | + | if override { | |
| 179 | 179 | r.kind = kindUnmanaged | |
| 180 | 180 | x.stats.UnmanagedSeen++ | |
| 181 | 181 | return | |
| @@ -232,7 +232,6 @@ func (x *XWM) onAttrReply(rec *record, r *xturbo.Reply) { | |||
| 232 | 232 | return | |
| 233 | 233 | } | |
| 234 | 234 | if a.OverrideRedirect() { | |
| 235 | - | rec.override = true | |
| 236 | 235 | rec.kind = kindUnmanaged | |
| 237 | 236 | rec.mapped = a.MapState() == xproto.MapStateViewable | |
| 238 | 237 | x.stats.UnmanagedSeen++ | |
| @@ -173,9 +173,9 @@ func (x *XWM) onCreateNotify(ev *xturbo.Event) { | |||||
| 173 | return | 173 | return | ||
| 174 | } | 174 | } | ||
| 175 | r := x.tab.rec(s) | 175 | r := x.tab.rec(s) | ||
| 176 | - | r.override = e.OverrideRedirect() | 176 | + | override := e.OverrideRedirect() |
| 177 | r.x, r.y, r.w, r.h = int32(e.X()), int32(e.Y()), uint32(e.Width()), uint32(e.Height()) | 177 | r.x, r.y, r.w, r.h = int32(e.X()), int32(e.Y()), uint32(e.Width()), uint32(e.Height()) | ||
| 178 | - | if r.override { | 178 | + | if override { |
| 179 | r.kind = kindUnmanaged | 179 | r.kind = kindUnmanaged | ||
| 180 | x.stats.UnmanagedSeen++ | 180 | x.stats.UnmanagedSeen++ | ||
| 181 | return | 181 | return | ||
| @@ -232,7 +232,6 @@ func (x *XWM) onAttrReply(rec *record, r *xturbo.Reply) { | |||||
| 232 | return | 232 | return | ||
| 233 | } | 233 | } | ||
| 234 | if a.OverrideRedirect() { | 234 | if a.OverrideRedirect() { | ||
| 235 | - | rec.override = true | |||
| 236 | rec.kind = kindUnmanaged | 235 | rec.kind = kindUnmanaged | ||
| 237 | rec.mapped = a.MapState() == xproto.MapStateViewable | 236 | rec.mapped = a.MapState() == xproto.MapStateViewable | ||
| 238 | x.stats.UnmanagedSeen++ | 237 | x.stats.UnmanagedSeen++ | ||
M internal/adapters/wayland/xwm/focus.go +4 -1
| @@ -158,6 +158,9 @@ func (x *XWM) removeClient(xid uint32) { | |||
| 158 | 158 | // queue when it stops being mapped (unmap, destroy). It is idempotent. | |
| 159 | 159 | func (x *XWM) dropWindow(s Slot) { | |
| 160 | 160 | r := x.tab.rec(s) | |
| 161 | + | if r != nil { | |
| 162 | + | r.ovQueued = false // a reused slot must not inherit a queued report | |
| 163 | + | } | |
| 161 | 164 | if r == nil || r.kind != kindManaged { | |
| 162 | 165 | return | |
| 163 | 166 | } | |
| @@ -183,7 +186,7 @@ func (x *XWM) forgetWindow(s Slot) { | |||
| 183 | 186 | x.dropWindow(s) | |
| 184 | 187 | if r.kind == kindUnmanaged && r.ovPaired && x.host != nil { | |
| 185 | 188 | r.ovPaired = false | |
| 186 | - | x.host.OverrideGone(s, true) | |
| 189 | + | x.host.OverrideGone(s) | |
| 187 | 190 | } | |
| 188 | 191 | if r.kind == kindManaged && r.id != 0 && x.host != nil { | |
| 189 | 192 | r.id = 0 | |
| @@ -158,6 +158,9 @@ func (x *XWM) removeClient(xid uint32) { | |||||
| 158 | // queue when it stops being mapped (unmap, destroy). It is idempotent. | 158 | // queue when it stops being mapped (unmap, destroy). It is idempotent. | ||
| 159 | func (x *XWM) dropWindow(s Slot) { | 159 | func (x *XWM) dropWindow(s Slot) { | ||
| 160 | r := x.tab.rec(s) | 160 | r := x.tab.rec(s) | ||
| 161 | + | if r != nil { | |||
| 162 | + | r.ovQueued = false // a reused slot must not inherit a queued report | |||
| 163 | + | } | |||
| 161 | if r == nil || r.kind != kindManaged { | 164 | if r == nil || r.kind != kindManaged { | ||
| 162 | return | 165 | return | ||
| 163 | } | 166 | } | ||
| @@ -183,7 +186,7 @@ func (x *XWM) forgetWindow(s Slot) { | |||||
| 183 | x.dropWindow(s) | 186 | x.dropWindow(s) | ||
| 184 | if r.kind == kindUnmanaged && r.ovPaired && x.host != nil { | 187 | if r.kind == kindUnmanaged && r.ovPaired && x.host != nil { | ||
| 185 | r.ovPaired = false | 188 | r.ovPaired = false | ||
| 186 | - | x.host.OverrideGone(s, true) | 189 | + | x.host.OverrideGone(s) |
| 187 | } | 190 | } | ||
| 188 | if r.kind == kindManaged && r.id != 0 && x.host != nil { | 191 | if r.kind == kindManaged && r.id != 0 && x.host != nil { | ||
| 189 | r.id = 0 | 192 | r.id = 0 | ||
M internal/adapters/wayland/xwm/host.go +2 -2
| @@ -46,8 +46,8 @@ type host interface { | |||
| 46 | 46 | OverrideMapped(slot Slot, xid uint32, surfaceID uint64, x, y, w, h int, noInput bool) | |
| 47 | 47 | // OverrideMoved reports a changed layout rect of a mapped override window. | |
| 48 | 48 | OverrideMoved(slot Slot, x, y, w, h int) | |
| 49 | - | // OverrideGone ends a mapped override window: unmapped, or destroyed. | |
| 50 | - | OverrideGone(slot Slot, destroyed bool) | |
| 49 | + | // OverrideGone ends a mapped override window: unmapped or destroyed. | |
| 50 | + | OverrideGone(slot Slot) | |
| 51 | 51 | } | |
| 52 | 52 | ||
| 53 | 53 | // fdSource is the display's event source for the X socket (*server.Source). | |
| @@ -46,8 +46,8 @@ type host interface { | |||||
| 46 | OverrideMapped(slot Slot, xid uint32, surfaceID uint64, x, y, w, h int, noInput bool) | 46 | OverrideMapped(slot Slot, xid uint32, surfaceID uint64, x, y, w, h int, noInput bool) | ||
| 47 | // OverrideMoved reports a changed layout rect of a mapped override window. | 47 | // OverrideMoved reports a changed layout rect of a mapped override window. | ||
| 48 | OverrideMoved(slot Slot, x, y, w, h int) | 48 | OverrideMoved(slot Slot, x, y, w, h int) | ||
| 49 | - | // OverrideGone ends a mapped override window: unmapped, or destroyed. | 49 | + | // OverrideGone ends a mapped override window: unmapped or destroyed. |
| 50 | - | OverrideGone(slot Slot, destroyed bool) | 50 | + | OverrideGone(slot Slot) |
| 51 | } | 51 | } | ||
| 52 | 52 | ||||
| 53 | // fdSource is the display's event source for the X socket (*server.Source). | 53 | // fdSource is the display's event source for the X socket (*server.Source). | ||
M internal/adapters/wayland/xwm/host_mock_test.go +6 -12
| @@ -287,8 +287,8 @@ func (_c *mockhost_Fullscreen_Call) RunAndReturn(run func(slot Slot, on bool)) * | |||
| 287 | 287 | } | |
| 288 | 288 | ||
| 289 | 289 | // OverrideGone provides a mock function for the type mockhost | |
| 290 | - | func (_mock *mockhost) OverrideGone(slot Slot, destroyed bool) { | |
| 291 | - | _mock.Called(slot, destroyed) | |
| 290 | + | func (_mock *mockhost) OverrideGone(slot Slot) { | |
| 291 | + | _mock.Called(slot) | |
| 292 | 292 | return | |
| 293 | 293 | } | |
| 294 | 294 | ||
| @@ -299,24 +299,18 @@ type mockhost_OverrideGone_Call struct { | |||
| 299 | 299 | ||
| 300 | 300 | // OverrideGone is a helper method to define mock.On call | |
| 301 | 301 | // - slot Slot | |
| 302 | - | // - destroyed bool | |
| 303 | - | func (_e *mockhost_Expecter) OverrideGone(slot any, destroyed any) *mockhost_OverrideGone_Call { | |
| 304 | - | return &mockhost_OverrideGone_Call{Call: _e.mock.On("OverrideGone", slot, destroyed)} | |
| 302 | + | func (_e *mockhost_Expecter) OverrideGone(slot any) *mockhost_OverrideGone_Call { | |
| 303 | + | return &mockhost_OverrideGone_Call{Call: _e.mock.On("OverrideGone", slot)} | |
| 305 | 304 | } | |
| 306 | 305 | ||
| 307 | - | func (_c *mockhost_OverrideGone_Call) Run(run func(slot Slot, destroyed bool)) *mockhost_OverrideGone_Call { | |
| 306 | + | func (_c *mockhost_OverrideGone_Call) Run(run func(slot Slot)) *mockhost_OverrideGone_Call { | |
| 308 | 307 | _c.Call.Run(func(args mock.Arguments) { | |
| 309 | 308 | var arg0 Slot | |
| 310 | 309 | if args[0] != nil { | |
| 311 | 310 | arg0 = args[0].(Slot) | |
| 312 | 311 | } | |
| 313 | - | var arg1 bool | |
| 314 | - | if args[1] != nil { | |
| 315 | - | arg1 = args[1].(bool) | |
| 316 | - | } | |
| 317 | 312 | run( | |
| 318 | 313 | arg0, | |
| 319 | - | arg1, | |
| 320 | 314 | ) | |
| 321 | 315 | }) | |
| 322 | 316 | return _c | |
| @@ -327,7 +321,7 @@ func (_c *mockhost_OverrideGone_Call) Return() *mockhost_OverrideGone_Call { | |||
| 327 | 321 | return _c | |
| 328 | 322 | } | |
| 329 | 323 | ||
| 330 | - | func (_c *mockhost_OverrideGone_Call) RunAndReturn(run func(slot Slot, destroyed bool)) *mockhost_OverrideGone_Call { | |
| 324 | + | func (_c *mockhost_OverrideGone_Call) RunAndReturn(run func(slot Slot)) *mockhost_OverrideGone_Call { | |
| 331 | 325 | _c.Run(run) | |
| 332 | 326 | return _c | |
| 333 | 327 | } | |
| @@ -287,8 +287,8 @@ func (_c *mockhost_Fullscreen_Call) RunAndReturn(run func(slot Slot, on bool)) * | |||||
| 287 | } | 287 | } | ||
| 288 | 288 | ||||
| 289 | // OverrideGone provides a mock function for the type mockhost | 289 | // OverrideGone provides a mock function for the type mockhost | ||
| 290 | - | func (_mock *mockhost) OverrideGone(slot Slot, destroyed bool) { | 290 | + | func (_mock *mockhost) OverrideGone(slot Slot) { |
| 291 | - | _mock.Called(slot, destroyed) | 291 | + | _mock.Called(slot) |
| 292 | return | 292 | return | ||
| 293 | } | 293 | } | ||
| 294 | 294 | ||||
| @@ -299,24 +299,18 @@ type mockhost_OverrideGone_Call struct { | |||||
| 299 | 299 | ||||
| 300 | // OverrideGone is a helper method to define mock.On call | 300 | // OverrideGone is a helper method to define mock.On call | ||
| 301 | // - slot Slot | 301 | // - slot Slot | ||
| 302 | - | // - destroyed bool | 302 | + | func (_e *mockhost_Expecter) OverrideGone(slot any) *mockhost_OverrideGone_Call { |
| 303 | - | func (_e *mockhost_Expecter) OverrideGone(slot any, destroyed any) *mockhost_OverrideGone_Call { | 303 | + | return &mockhost_OverrideGone_Call{Call: _e.mock.On("OverrideGone", slot)} |
| 304 | - | return &mockhost_OverrideGone_Call{Call: _e.mock.On("OverrideGone", slot, destroyed)} | |||
| 305 | } | 304 | } | ||
| 306 | 305 | ||||
| 307 | - | func (_c *mockhost_OverrideGone_Call) Run(run func(slot Slot, destroyed bool)) *mockhost_OverrideGone_Call { | 306 | + | func (_c *mockhost_OverrideGone_Call) Run(run func(slot Slot)) *mockhost_OverrideGone_Call { |
| 308 | _c.Call.Run(func(args mock.Arguments) { | 307 | _c.Call.Run(func(args mock.Arguments) { | ||
| 309 | var arg0 Slot | 308 | var arg0 Slot | ||
| 310 | if args[0] != nil { | 309 | if args[0] != nil { | ||
| 311 | arg0 = args[0].(Slot) | 310 | arg0 = args[0].(Slot) | ||
| 312 | } | 311 | } | ||
| 313 | - | var arg1 bool | |||
| 314 | - | if args[1] != nil { | |||
| 315 | - | arg1 = args[1].(bool) | |||
| 316 | - | } | |||
| 317 | run( | 312 | run( | ||
| 318 | arg0, | 313 | arg0, | ||
| 319 | - | arg1, | |||
| 320 | ) | 314 | ) | ||
| 321 | }) | 315 | }) | ||
| 322 | return _c | 316 | return _c | ||
| @@ -327,7 +321,7 @@ func (_c *mockhost_OverrideGone_Call) Return() *mockhost_OverrideGone_Call { | |||||
| 327 | return _c | 321 | return _c | ||
| 328 | } | 322 | } | ||
| 329 | 323 | ||||
| 330 | - | func (_c *mockhost_OverrideGone_Call) RunAndReturn(run func(slot Slot, destroyed bool)) *mockhost_OverrideGone_Call { | 324 | + | func (_c *mockhost_OverrideGone_Call) RunAndReturn(run func(slot Slot)) *mockhost_OverrideGone_Call { |
| 331 | _c.Run(run) | 325 | _c.Run(run) | ||
| 332 | return _c | 326 | return _c | ||
| 333 | } | 327 | } | ||
M internal/adapters/wayland/xwm/mapflow.go +2 -3
| @@ -256,9 +256,8 @@ func (x *XWM) onReparentNotify(ev *xturbo.Event) { | |||
| 256 | 256 | return | |
| 257 | 257 | } | |
| 258 | 258 | rec := x.tab.rec(s) | |
| 259 | - | rec.override = e.OverrideRedirect() | |
| 260 | 259 | rec.x, rec.y = int32(e.X()), int32(e.Y()) | |
| 261 | - | if rec.override { | |
| 260 | + | if e.OverrideRedirect() { | |
| 262 | 261 | rec.kind = kindUnmanaged | |
| 263 | 262 | x.stats.UnmanagedSeen++ | |
| 264 | 263 | return | |
| @@ -348,7 +347,7 @@ func (x *XWM) SurfaceGone(surfaceID uint64) { | |||
| 348 | 347 | r.surface = 0 | |
| 349 | 348 | if r.kind == kindUnmanaged && r.ovPaired { | |
| 350 | 349 | r.ovPaired = false | |
| 351 | - | x.host.OverrideGone(Slot(i+1), false) | |
| 350 | + | x.host.OverrideGone(Slot(i + 1)) | |
| 352 | 351 | } | |
| 353 | 352 | } | |
| 354 | 353 | } | |
| @@ -256,9 +256,8 @@ func (x *XWM) onReparentNotify(ev *xturbo.Event) { | |||||
| 256 | return | 256 | return | ||
| 257 | } | 257 | } | ||
| 258 | rec := x.tab.rec(s) | 258 | rec := x.tab.rec(s) | ||
| 259 | - | rec.override = e.OverrideRedirect() | |||
| 260 | rec.x, rec.y = int32(e.X()), int32(e.Y()) | 259 | rec.x, rec.y = int32(e.X()), int32(e.Y()) | ||
| 261 | - | if rec.override { | 260 | + | if e.OverrideRedirect() { |
| 262 | rec.kind = kindUnmanaged | 261 | rec.kind = kindUnmanaged | ||
| 263 | x.stats.UnmanagedSeen++ | 262 | x.stats.UnmanagedSeen++ | ||
| 264 | return | 263 | return | ||
| @@ -348,7 +347,7 @@ func (x *XWM) SurfaceGone(surfaceID uint64) { | |||||
| 348 | r.surface = 0 | 347 | r.surface = 0 | ||
| 349 | if r.kind == kindUnmanaged && r.ovPaired { | 348 | if r.kind == kindUnmanaged && r.ovPaired { | ||
| 350 | r.ovPaired = false | 349 | r.ovPaired = false | ||
| 351 | - | x.host.OverrideGone(Slot(i+1), false) | 350 | + | x.host.OverrideGone(Slot(i + 1)) |
| 352 | } | 351 | } | ||
| 353 | } | 352 | } | ||
| 354 | } | 353 | } | ||
M internal/adapters/wayland/xwm/space.go +10 -42
| @@ -1,18 +1,13 @@ | |||
| 1 | 1 | package xwm | |
| 2 | 2 | ||
| 3 | - | import ( | |
| 4 | - | "math" | |
| 5 | - | ||
| 6 | - | "github.com/bnema/neferwl/internal/ports" | |
| 7 | - | ) | |
| 3 | + | import "github.com/bnema/neferwl/internal/ports" | |
| 8 | 4 | ||
| 9 | 5 | const maxRegions = 16 | |
| 10 | 6 | ||
| 11 | 7 | // Region is one output's part of the layout and where it is in X space. | |
| 12 | - | // X = X.Origin + (layout - Layout.Origin) * Scale. Scale is 1 in v1. | |
| 8 | + | // X = layout - Origin. | |
| 13 | 9 | type Region struct { | |
| 14 | 10 | Layout, X ports.Rect // logical layout rect; X rect in X pixels | |
| 15 | - | Scale float64 // 1 in v1; reserved for native resolution | |
| 16 | 11 | } | |
| 17 | 12 | ||
| 18 | 13 | // Space maps layout coordinates (which may be negative) to X coordinates | |
| @@ -20,14 +15,14 @@ type Region struct { | |||
| 20 | 15 | type Space struct { | |
| 21 | 16 | regions [maxRegions]Region | |
| 22 | 17 | n int | |
| 23 | - | dropped int // outputs beyond maxRegions in the last Set | |
| 24 | - | ox, oy int // layout point that is X (0,0) | |
| 25 | - | w, h int // X screen size: the extents | |
| 26 | - | gen uint32 // bumped on every change | |
| 18 | + | dropped int // outputs beyond maxRegions in the last Set | |
| 19 | + | ox, oy int // layout point that is X (0,0) | |
| 20 | + | w, h int // X screen size: the extents | |
| 27 | 21 | } | |
| 28 | 22 | ||
| 29 | - | // Gen returns the change counter. | |
| 30 | - | func (s *Space) Gen() uint32 { return s.gen } | |
| 23 | + | // Origin is the layout point that is X (0,0): the top-left of the layout | |
| 24 | + | // extents. | |
| 25 | + | func (s *Space) Origin() (ox, oy int) { return s.ox, s.oy } | |
| 31 | 26 | ||
| 32 | 27 | // Dropped is how many outputs of the last Set did not fit. | |
| 33 | 28 | func (s *Space) Dropped() int { return s.dropped } | |
| @@ -62,7 +57,6 @@ func (s *Space) Set(l ports.Layout) bool { | |||
| 62 | 57 | next.regions[i] = Region{ | |
| 63 | 58 | Layout: ports.Rect{X: o.X, Y: o.Y, W: o.Width, H: o.Height}, | |
| 64 | 59 | X: ports.Rect{X: o.X - next.ox, Y: o.Y - next.oy, W: o.Width, H: o.Height}, | |
| 65 | - | Scale: 1, | |
| 66 | 60 | } | |
| 67 | 61 | } | |
| 68 | 62 | next.w, next.h = maxX-next.ox, maxY-next.oy | |
| @@ -71,7 +65,6 @@ func (s *Space) Set(l ports.Layout) bool { | |||
| 71 | 65 | s.dropped = next.dropped | |
| 72 | 66 | return false | |
| 73 | 67 | } | |
| 74 | - | next.gen = s.gen + 1 | |
| 75 | 68 | *s = next | |
| 76 | 69 | return true | |
| 77 | 70 | } | |
| @@ -128,7 +121,7 @@ func (s *Space) ToX(lx, ly int) (x, y int) { | |||
| 128 | 121 | return lx, ly | |
| 129 | 122 | } | |
| 130 | 123 | r := &s.regions[i] | |
| 131 | - | return r.X.X + scaled(lx-r.Layout.X, r.Scale), r.X.Y + scaled(ly-r.Layout.Y, r.Scale) | |
| 124 | + | return r.X.X + lx - r.Layout.X, r.X.Y + ly - r.Layout.Y | |
| 132 | 125 | } | |
| 133 | 126 | ||
| 134 | 127 | // ToLayout converts an X point to layout coordinates. | |
| @@ -138,30 +131,5 @@ func (s *Space) ToLayout(x, y int) (lx, ly int) { | |||
| 138 | 131 | return x, y | |
| 139 | 132 | } | |
| 140 | 133 | r := &s.regions[i] | |
| 141 | - | return r.Layout.X + unscaled(x-r.X.X, r.Scale), r.Layout.Y + unscaled(y-r.X.Y, r.Scale) | |
| 142 | - | } | |
| 143 | - | ||
| 144 | - | // RegionAtLayout returns the region containing the layout point. | |
| 145 | - | func (s *Space) RegionAtLayout(lx, ly int) (Region, bool) { | |
| 146 | - | for i := 0; i < s.n; i++ { | |
| 147 | - | r := s.regions[i].Layout | |
| 148 | - | if lx >= r.X && lx < r.X+r.W && ly >= r.Y && ly < r.Y+r.H { | |
| 149 | - | return s.regions[i], true | |
| 150 | - | } | |
| 151 | - | } | |
| 152 | - | return Region{}, false | |
| 153 | - | } | |
| 154 | - | ||
| 155 | - | func scaled(v int, sc float64) int { | |
| 156 | - | if sc == 1 || sc == 0 { | |
| 157 | - | return v | |
| 158 | - | } | |
| 159 | - | return int(math.Round(float64(v) * sc)) | |
| 160 | - | } | |
| 161 | - | ||
| 162 | - | func unscaled(v int, sc float64) int { | |
| 163 | - | if sc == 1 || sc == 0 { | |
| 164 | - | return v | |
| 165 | - | } | |
| 166 | - | return int(math.Round(float64(v) / sc)) | |
| 134 | + | return r.Layout.X + x - r.X.X, r.Layout.Y + y - r.X.Y | |
| 167 | 135 | } | |
| @@ -1,18 +1,13 @@ | |||||
| 1 | package xwm | 1 | package xwm | ||
| 2 | 2 | ||||
| 3 | - | import ( | 3 | + | import "github.com/bnema/neferwl/internal/ports" |
| 4 | - | "math" | |||
| 5 | - | ||||
| 6 | - | "github.com/bnema/neferwl/internal/ports" | |||
| 7 | - | ) | |||
| 8 | 4 | ||||
| 9 | const maxRegions = 16 | 5 | const maxRegions = 16 | ||
| 10 | 6 | ||||
| 11 | // Region is one output's part of the layout and where it is in X space. | 7 | // Region is one output's part of the layout and where it is in X space. | ||
| 12 | - | // X = X.Origin + (layout - Layout.Origin) * Scale. Scale is 1 in v1. | 8 | + | // X = layout - Origin. |
| 13 | type Region struct { | 9 | type Region struct { | ||
| 14 | Layout, X ports.Rect // logical layout rect; X rect in X pixels | 10 | Layout, X ports.Rect // logical layout rect; X rect in X pixels | ||
| 15 | - | Scale float64 // 1 in v1; reserved for native resolution | |||
| 16 | } | 11 | } | ||
| 17 | 12 | ||||
| 18 | // Space maps layout coordinates (which may be negative) to X coordinates | 13 | // Space maps layout coordinates (which may be negative) to X coordinates | ||
| @@ -20,14 +15,14 @@ type Region struct { | |||||
| 20 | type Space struct { | 15 | type Space struct { | ||
| 21 | regions [maxRegions]Region | 16 | regions [maxRegions]Region | ||
| 22 | n int | 17 | n int | ||
| 23 | - | dropped int // outputs beyond maxRegions in the last Set | 18 | + | dropped int // outputs beyond maxRegions in the last Set |
| 24 | - | ox, oy int // layout point that is X (0,0) | 19 | + | ox, oy int // layout point that is X (0,0) |
| 25 | - | w, h int // X screen size: the extents | 20 | + | w, h int // X screen size: the extents |
| 26 | - | gen uint32 // bumped on every change | |||
| 27 | } | 21 | } | ||
| 28 | 22 | ||||
| 29 | - | // Gen returns the change counter. | 23 | + | // Origin is the layout point that is X (0,0): the top-left of the layout |
| 30 | - | func (s *Space) Gen() uint32 { return s.gen } | 24 | + | // extents. |
| 25 | + | func (s *Space) Origin() (ox, oy int) { return s.ox, s.oy } | |||
| 31 | 26 | ||||
| 32 | // Dropped is how many outputs of the last Set did not fit. | 27 | // Dropped is how many outputs of the last Set did not fit. | ||
| 33 | func (s *Space) Dropped() int { return s.dropped } | 28 | func (s *Space) Dropped() int { return s.dropped } | ||
| @@ -62,7 +57,6 @@ func (s *Space) Set(l ports.Layout) bool { | |||||
| 62 | next.regions[i] = Region{ | 57 | next.regions[i] = Region{ | ||
| 63 | Layout: ports.Rect{X: o.X, Y: o.Y, W: o.Width, H: o.Height}, | 58 | Layout: ports.Rect{X: o.X, Y: o.Y, W: o.Width, H: o.Height}, | ||
| 64 | X: ports.Rect{X: o.X - next.ox, Y: o.Y - next.oy, W: o.Width, H: o.Height}, | 59 | X: ports.Rect{X: o.X - next.ox, Y: o.Y - next.oy, W: o.Width, H: o.Height}, | ||
| 65 | - | Scale: 1, | |||
| 66 | } | 60 | } | ||
| 67 | } | 61 | } | ||
| 68 | next.w, next.h = maxX-next.ox, maxY-next.oy | 62 | next.w, next.h = maxX-next.ox, maxY-next.oy | ||
| @@ -71,7 +65,6 @@ func (s *Space) Set(l ports.Layout) bool { | |||||
| 71 | s.dropped = next.dropped | 65 | s.dropped = next.dropped | ||
| 72 | return false | 66 | return false | ||
| 73 | } | 67 | } | ||
| 74 | - | next.gen = s.gen + 1 | |||
| 75 | *s = next | 68 | *s = next | ||
| 76 | return true | 69 | return true | ||
| 77 | } | 70 | } | ||
| @@ -128,7 +121,7 @@ func (s *Space) ToX(lx, ly int) (x, y int) { | |||||
| 128 | return lx, ly | 121 | return lx, ly | ||
| 129 | } | 122 | } | ||
| 130 | r := &s.regions[i] | 123 | r := &s.regions[i] | ||
| 131 | - | return r.X.X + scaled(lx-r.Layout.X, r.Scale), r.X.Y + scaled(ly-r.Layout.Y, r.Scale) | 124 | + | return r.X.X + lx - r.Layout.X, r.X.Y + ly - r.Layout.Y |
| 132 | } | 125 | } | ||
| 133 | 126 | ||||
| 134 | // ToLayout converts an X point to layout coordinates. | 127 | // ToLayout converts an X point to layout coordinates. | ||
| @@ -138,30 +131,5 @@ func (s *Space) ToLayout(x, y int) (lx, ly int) { | |||||
| 138 | return x, y | 131 | return x, y | ||
| 139 | } | 132 | } | ||
| 140 | r := &s.regions[i] | 133 | r := &s.regions[i] | ||
| 141 | - | return r.Layout.X + unscaled(x-r.X.X, r.Scale), r.Layout.Y + unscaled(y-r.X.Y, r.Scale) | 134 | + | return r.Layout.X + x - r.X.X, r.Layout.Y + y - r.X.Y |
| 142 | - | } | |||
| 143 | - | ||||
| 144 | - | // RegionAtLayout returns the region containing the layout point. | |||
| 145 | - | func (s *Space) RegionAtLayout(lx, ly int) (Region, bool) { | |||
| 146 | - | for i := 0; i < s.n; i++ { | |||
| 147 | - | r := s.regions[i].Layout | |||
| 148 | - | if lx >= r.X && lx < r.X+r.W && ly >= r.Y && ly < r.Y+r.H { | |||
| 149 | - | return s.regions[i], true | |||
| 150 | - | } | |||
| 151 | - | } | |||
| 152 | - | return Region{}, false | |||
| 153 | - | } | |||
| 154 | - | ||||
| 155 | - | func scaled(v int, sc float64) int { | |||
| 156 | - | if sc == 1 || sc == 0 { | |||
| 157 | - | return v | |||
| 158 | - | } | |||
| 159 | - | return int(math.Round(float64(v) * sc)) | |||
| 160 | - | } | |||
| 161 | - | ||||
| 162 | - | func unscaled(v int, sc float64) int { | |||
| 163 | - | if sc == 1 || sc == 0 { | |||
| 164 | - | return v | |||
| 165 | - | } | |||
| 166 | - | return int(math.Round(float64(v) / sc)) | |||
| 167 | } | 135 | } | ||
M internal/adapters/wayland/xwm/space_test.go +6 -23
| @@ -47,32 +47,14 @@ func TestSpaceLayouts(t *testing.T) { | |||
| 47 | 47 | } | |
| 48 | 48 | } | |
| 49 | 49 | ||
| 50 | - | func TestSpaceHoleAndRegion(t *testing.T) { | |
| 51 | - | var s Space | |
| 52 | - | s.Set(ports.Layout{out(0, 0, 100, 100), out(300, 0, 100, 100)}) | |
| 53 | - | if _, ok := s.RegionAtLayout(200, 50); ok { | |
| 54 | - | t.Fatal("hole must have no region") | |
| 55 | - | } | |
| 56 | - | r, ok := s.RegionAtLayout(350, 50) | |
| 57 | - | if !ok || r.X.X != 300 { | |
| 58 | - | t.Fatalf("region = %+v %v", r, ok) | |
| 59 | - | } | |
| 60 | - | } | |
| 61 | - | ||
| 62 | - | func TestSpaceGenAndEmpty(t *testing.T) { | |
| 50 | + | func TestSpaceEmptyAndOrigin(t *testing.T) { | |
| 63 | 51 | var s Space | |
| 64 | 52 | if x, y := s.ToX(5, 6); x != 5 || y != 6 { | |
| 65 | 53 | t.Fatalf("empty ToX = %d,%d", x, y) | |
| 66 | 54 | } | |
| 67 | - | s.Set(ports.Layout{out(0, 0, 10, 10)}) | |
| 68 | - | g := s.Gen() | |
| 69 | - | s.Set(ports.Layout{out(0, 0, 10, 10)}) | |
| 70 | - | if s.Gen() != g { | |
| 71 | - | t.Fatal("gen bumped without change") | |
| 72 | - | } | |
| 73 | - | s.Set(ports.Layout{out(5, 0, 10, 10)}) | |
| 74 | - | if s.Gen() == g { | |
| 75 | - | t.Fatal("gen not bumped") | |
| 55 | + | s.Set(ports.Layout{out(-640, -20, 640, 480), out(0, 0, 1920, 1080)}) | |
| 56 | + | if ox, oy := s.Origin(); ox != -640 || oy != -20 { | |
| 57 | + | t.Fatalf("origin = %d,%d", ox, oy) | |
| 76 | 58 | } | |
| 77 | 59 | } | |
| 78 | 60 | ||
| @@ -102,7 +84,8 @@ func TestSpaceAllocations(t *testing.T) { | |||
| 102 | 84 | s.Set(l) | |
| 103 | 85 | x, y := s.ToX(-600, 10) | |
| 104 | 86 | x, y = s.ToLayout(x, y) | |
| 105 | - | _, _ = s.RegionAtLayout(x, y) | |
| 87 | + | _, _ = x, y | |
| 88 | + | _, _ = s.Origin() | |
| 106 | 89 | _, _ = s.Size() | |
| 107 | 90 | }) | |
| 108 | 91 | if n != 0 { | |
| @@ -47,32 +47,14 @@ func TestSpaceLayouts(t *testing.T) { | |||||
| 47 | } | 47 | } | ||
| 48 | } | 48 | } | ||
| 49 | 49 | ||||
| 50 | - | func TestSpaceHoleAndRegion(t *testing.T) { | 50 | + | func TestSpaceEmptyAndOrigin(t *testing.T) { |
| 51 | - | var s Space | |||
| 52 | - | s.Set(ports.Layout{out(0, 0, 100, 100), out(300, 0, 100, 100)}) | |||
| 53 | - | if _, ok := s.RegionAtLayout(200, 50); ok { | |||
| 54 | - | t.Fatal("hole must have no region") | |||
| 55 | - | } | |||
| 56 | - | r, ok := s.RegionAtLayout(350, 50) | |||
| 57 | - | if !ok || r.X.X != 300 { | |||
| 58 | - | t.Fatalf("region = %+v %v", r, ok) | |||
| 59 | - | } | |||
| 60 | - | } | |||
| 61 | - | ||||
| 62 | - | func TestSpaceGenAndEmpty(t *testing.T) { | |||
| 63 | var s Space | 51 | var s Space | ||
| 64 | if x, y := s.ToX(5, 6); x != 5 || y != 6 { | 52 | if x, y := s.ToX(5, 6); x != 5 || y != 6 { | ||
| 65 | t.Fatalf("empty ToX = %d,%d", x, y) | 53 | t.Fatalf("empty ToX = %d,%d", x, y) | ||
| 66 | } | 54 | } | ||
| 67 | - | s.Set(ports.Layout{out(0, 0, 10, 10)}) | 55 | + | s.Set(ports.Layout{out(-640, -20, 640, 480), out(0, 0, 1920, 1080)}) |
| 68 | - | g := s.Gen() | 56 | + | if ox, oy := s.Origin(); ox != -640 || oy != -20 { |
| 69 | - | s.Set(ports.Layout{out(0, 0, 10, 10)}) | 57 | + | t.Fatalf("origin = %d,%d", ox, oy) |
| 70 | - | if s.Gen() != g { | |||
| 71 | - | t.Fatal("gen bumped without change") | |||
| 72 | - | } | |||
| 73 | - | s.Set(ports.Layout{out(5, 0, 10, 10)}) | |||
| 74 | - | if s.Gen() == g { | |||
| 75 | - | t.Fatal("gen not bumped") | |||
| 76 | } | 58 | } | ||
| 77 | } | 59 | } | ||
| 78 | 60 | ||||
| @@ -102,7 +84,8 @@ func TestSpaceAllocations(t *testing.T) { | |||||
| 102 | s.Set(l) | 84 | s.Set(l) | ||
| 103 | x, y := s.ToX(-600, 10) | 85 | x, y := s.ToX(-600, 10) | ||
| 104 | x, y = s.ToLayout(x, y) | 86 | x, y = s.ToLayout(x, y) | ||
| 105 | - | _, _ = s.RegionAtLayout(x, y) | 87 | + | _, _ = x, y |
| 88 | + | _, _ = s.Origin() | |||
| 106 | _, _ = s.Size() | 89 | _, _ = s.Size() | ||
| 107 | }) | 90 | }) | ||
| 108 | if n != 0 { | 91 | if n != 0 { | ||
M internal/adapters/wayland/xwm/table.go +3 -3
| @@ -40,9 +40,9 @@ type record struct { | |||
| 40 | 40 | gen uint16 // bumped on insert; stale Expect replies are told apart by it | |
| 41 | 41 | ||
| 42 | 42 | // X state | |
| 43 | - | override, mapped, mapping, ready bool | |
| 44 | - | x, y int32 | |
| 45 | - | w, h uint32 | |
| 43 | + | mapped, mapping, ready bool | |
| 44 | + | x, y int32 | |
| 45 | + | w, h uint32 | |
| 46 | 46 | ||
| 47 | 47 | // properties | |
| 48 | 48 | pending uint16 // propMask bits still awaited | |
| @@ -40,9 +40,9 @@ type record struct { | |||||
| 40 | gen uint16 // bumped on insert; stale Expect replies are told apart by it | 40 | gen uint16 // bumped on insert; stale Expect replies are told apart by it | ||
| 41 | 41 | ||||
| 42 | // X state | 42 | // X state | ||
| 43 | - | override, mapped, mapping, ready bool | 43 | + | mapped, mapping, ready bool |
| 44 | - | x, y int32 | 44 | + | x, y int32 |
| 45 | - | w, h uint32 | 45 | + | w, h uint32 |
| 46 | 46 | ||||
| 47 | // properties | 47 | // properties | ||
| 48 | pending uint16 // propMask bits still awaited | 48 | pending uint16 // propMask bits still awaited | ||
M internal/adapters/wayland/xwm/unmanaged.go +1 -1
| @@ -68,7 +68,7 @@ func (x *XWM) overrideUnmapped(s Slot, rec *record) { | |||
| 68 | 68 | } | |
| 69 | 69 | if rec.ovPaired { | |
| 70 | 70 | rec.ovPaired = false | |
| 71 | - | x.host.OverrideGone(s, false) | |
| 71 | + | x.host.OverrideGone(s) | |
| 72 | 72 | } | |
| 73 | 73 | } | |
| 74 | 74 | ||
| @@ -68,7 +68,7 @@ func (x *XWM) overrideUnmapped(s Slot, rec *record) { | |||||
| 68 | } | 68 | } | ||
| 69 | if rec.ovPaired { | 69 | if rec.ovPaired { | ||
| 70 | rec.ovPaired = false | 70 | rec.ovPaired = false | ||
| 71 | - | x.host.OverrideGone(s, false) | 71 | + | x.host.OverrideGone(s) |
| 72 | } | 72 | } | ||
| 73 | } | 73 | } | ||
| 74 | 74 | ||||
M internal/adapters/wayland/xwm/unmanaged_test.go +12 -3
| @@ -145,7 +145,7 @@ func TestUnmanagedMoveAndLayoutChangeAndUnmap(t *testing.T) { | |||
| 145 | 145 | x.SetLayout(layoutOf(out(-100, 0, 100, 100), out(0, 0, 1920, 1080))) | |
| 146 | 146 | x.flush() | |
| 147 | 147 | ||
| 148 | - | h.EXPECT().OverrideGone(s, false).Once() | |
| 148 | + | h.EXPECT().OverrideGone(s).Once() | |
| 149 | 149 | x.feed(t, p, func() { p.unmapNotify(win, false) }) | |
| 150 | 150 | require.False(t, x.tab.rec(s).ovPaired) | |
| 151 | 151 | } | |
| @@ -158,7 +158,7 @@ func TestUnmanagedDestroyReportsGone(t *testing.T) { | |||
| 158 | 158 | h.EXPECT().OverrideMapped(s, uint32(win), uint64(7), 30, 40, 100, 50, false).Once() | |
| 159 | 159 | x.feed(t, p, func() { p.clientMessage(win, p.atom("WL_SURFACE_SERIAL"), 9, 0) }) | |
| 160 | 160 | readUntil(t, x, func() bool { return x.tab.rec(s).ovPaired }) | |
| 161 | - | h.EXPECT().OverrideGone(s, true).Once() | |
| 161 | + | h.EXPECT().OverrideGone(s).Once() | |
| 162 | 162 | x.feed(t, p, func() { p.destroyNotify(win) }) | |
| 163 | 163 | require.Zero(t, x.tab.lookup(win)) | |
| 164 | 164 | } | |
| @@ -204,7 +204,7 @@ func TestUnmanagedGeometryAllocations(t *testing.T) { | |||
| 204 | 204 | x.SetLayout(layoutOf(out(0, 0, 1920, 1080))) | |
| 205 | 205 | s := x.insert(0x500009) | |
| 206 | 206 | r := x.tab.rec(s) | |
| 207 | - | r.kind, r.override, r.mapped, r.ready, r.ovPaired = kindUnmanaged, true, true, true, true | |
| 207 | + | r.kind, r.mapped, r.ready, r.ovPaired = kindUnmanaged, true, true, true | |
| 208 | 208 | r.x, r.y, r.w, r.h = 30, 40, 100, 50 | |
| 209 | 209 | r.ov = x.layoutRect(r) | |
| 210 | 210 | sinkPeer(t, x, p) | |
| @@ -236,3 +236,12 @@ func TestFocusInUnmanagedAllocations(t *testing.T) { | |||
| 236 | 236 | require.Zero(t, allocs) | |
| 237 | 237 | require.False(t, x.refocus) | |
| 238 | 238 | } | |
| 239 | + | ||
| 240 | + | func TestDropWindowClearsQueuedOverride(t *testing.T) { | |
| 241 | + | x, _, _, _, _ := attached(t) | |
| 242 | + | s := x.insert(0x500009) | |
| 243 | + | r := x.tab.rec(s) | |
| 244 | + | r.kind, r.ovQueued = kindUnmanaged, true | |
| 245 | + | x.dropWindow(s) | |
| 246 | + | require.False(t, r.ovQueued) | |
| 247 | + | } | |
| @@ -145,7 +145,7 @@ func TestUnmanagedMoveAndLayoutChangeAndUnmap(t *testing.T) { | |||||
| 145 | x.SetLayout(layoutOf(out(-100, 0, 100, 100), out(0, 0, 1920, 1080))) | 145 | x.SetLayout(layoutOf(out(-100, 0, 100, 100), out(0, 0, 1920, 1080))) | ||
| 146 | x.flush() | 146 | x.flush() | ||
| 147 | 147 | ||||
| 148 | - | h.EXPECT().OverrideGone(s, false).Once() | 148 | + | h.EXPECT().OverrideGone(s).Once() |
| 149 | x.feed(t, p, func() { p.unmapNotify(win, false) }) | 149 | x.feed(t, p, func() { p.unmapNotify(win, false) }) | ||
| 150 | require.False(t, x.tab.rec(s).ovPaired) | 150 | require.False(t, x.tab.rec(s).ovPaired) | ||
| 151 | } | 151 | } | ||
| @@ -158,7 +158,7 @@ func TestUnmanagedDestroyReportsGone(t *testing.T) { | |||||
| 158 | h.EXPECT().OverrideMapped(s, uint32(win), uint64(7), 30, 40, 100, 50, false).Once() | 158 | h.EXPECT().OverrideMapped(s, uint32(win), uint64(7), 30, 40, 100, 50, false).Once() | ||
| 159 | x.feed(t, p, func() { p.clientMessage(win, p.atom("WL_SURFACE_SERIAL"), 9, 0) }) | 159 | x.feed(t, p, func() { p.clientMessage(win, p.atom("WL_SURFACE_SERIAL"), 9, 0) }) | ||
| 160 | readUntil(t, x, func() bool { return x.tab.rec(s).ovPaired }) | 160 | readUntil(t, x, func() bool { return x.tab.rec(s).ovPaired }) | ||
| 161 | - | h.EXPECT().OverrideGone(s, true).Once() | 161 | + | h.EXPECT().OverrideGone(s).Once() |
| 162 | x.feed(t, p, func() { p.destroyNotify(win) }) | 162 | x.feed(t, p, func() { p.destroyNotify(win) }) | ||
| 163 | require.Zero(t, x.tab.lookup(win)) | 163 | require.Zero(t, x.tab.lookup(win)) | ||
| 164 | } | 164 | } | ||
| @@ -204,7 +204,7 @@ func TestUnmanagedGeometryAllocations(t *testing.T) { | |||||
| 204 | x.SetLayout(layoutOf(out(0, 0, 1920, 1080))) | 204 | x.SetLayout(layoutOf(out(0, 0, 1920, 1080))) | ||
| 205 | s := x.insert(0x500009) | 205 | s := x.insert(0x500009) | ||
| 206 | r := x.tab.rec(s) | 206 | r := x.tab.rec(s) | ||
| 207 | - | r.kind, r.override, r.mapped, r.ready, r.ovPaired = kindUnmanaged, true, true, true, true | 207 | + | r.kind, r.mapped, r.ready, r.ovPaired = kindUnmanaged, true, true, true |
| 208 | r.x, r.y, r.w, r.h = 30, 40, 100, 50 | 208 | r.x, r.y, r.w, r.h = 30, 40, 100, 50 | ||
| 209 | r.ov = x.layoutRect(r) | 209 | r.ov = x.layoutRect(r) | ||
| 210 | sinkPeer(t, x, p) | 210 | sinkPeer(t, x, p) | ||
| @@ -236,3 +236,12 @@ func TestFocusInUnmanagedAllocations(t *testing.T) { | |||||
| 236 | require.Zero(t, allocs) | 236 | require.Zero(t, allocs) | ||
| 237 | require.False(t, x.refocus) | 237 | require.False(t, x.refocus) | ||
| 238 | } | 238 | } | ||
| 239 | + | ||||
| 240 | + | func TestDropWindowClearsQueuedOverride(t *testing.T) { | |||
| 241 | + | x, _, _, _, _ := attached(t) | |||
| 242 | + | s := x.insert(0x500009) | |||
| 243 | + | r := x.tab.rec(s) | |||
| 244 | + | r.kind, r.ovQueued = kindUnmanaged, true | |||
| 245 | + | x.dropWindow(s) | |||
| 246 | + | require.False(t, r.ovQueued) | |||
| 247 | + | } | |||
M internal/adapters/wayland/xwm/xwm.go +3 -3
| @@ -81,13 +81,13 @@ type XWM struct { | |||
| 81 | 81 | unpaired [unpairedCap]struct{ serial, surface uint64 } | |
| 82 | 82 | unpairedN int | |
| 83 | 83 | ||
| 84 | - | space Space // layout <-> X coordinates | |
| 84 | + | space *Space // layout <-> X coordinates, shared with the host | |
| 85 | 85 | warnedSpace bool // more outputs than the Space holds, logged once | |
| 86 | 86 | ovDirty []Slot // mapped unmanaged slots whose geometry changed, capacity == table size | |
| 87 | 87 | } | |
| 88 | 88 | ||
| 89 | 89 | func newXWM(conn *xturbo.Conn, log zerowrap.Logger) *XWM { | |
| 90 | - | x := &XWM{conn: conn, log: log, root: conn.Root(), tab: newTable(initialSlots)} | |
| 90 | + | x := &XWM{conn: conn, log: log, root: conn.Root(), tab: newTable(initialSlots), space: new(Space)} | |
| 91 | 91 | x.h = xturbo.Handler{OnEvent: x.onEvent, OnReply: x.onReply, OnError: x.onError} | |
| 92 | 92 | x.fit() | |
| 93 | 93 | return x | |
| @@ -360,7 +360,7 @@ func (x *XWM) Close() { | |||
| 360 | 360 | x.host.Dissociate(Slot(i+1), true) | |
| 361 | 361 | case r.kind == kindUnmanaged && r.ovPaired: | |
| 362 | 362 | r.ovPaired = false | |
| 363 | - | x.host.OverrideGone(Slot(i+1), true) | |
| 363 | + | x.host.OverrideGone(Slot(i + 1)) | |
| 364 | 364 | } | |
| 365 | 365 | } | |
| 366 | 366 | } | |
| @@ -81,13 +81,13 @@ type XWM struct { | |||||
| 81 | unpaired [unpairedCap]struct{ serial, surface uint64 } | 81 | unpaired [unpairedCap]struct{ serial, surface uint64 } | ||
| 82 | unpairedN int | 82 | unpairedN int | ||
| 83 | 83 | ||||
| 84 | - | space Space // layout <-> X coordinates | 84 | + | space *Space // layout <-> X coordinates, shared with the host |
| 85 | warnedSpace bool // more outputs than the Space holds, logged once | 85 | warnedSpace bool // more outputs than the Space holds, logged once | ||
| 86 | ovDirty []Slot // mapped unmanaged slots whose geometry changed, capacity == table size | 86 | ovDirty []Slot // mapped unmanaged slots whose geometry changed, capacity == table size | ||
| 87 | } | 87 | } | ||
| 88 | 88 | ||||
| 89 | func newXWM(conn *xturbo.Conn, log zerowrap.Logger) *XWM { | 89 | func newXWM(conn *xturbo.Conn, log zerowrap.Logger) *XWM { | ||
| 90 | - | x := &XWM{conn: conn, log: log, root: conn.Root(), tab: newTable(initialSlots)} | 90 | + | x := &XWM{conn: conn, log: log, root: conn.Root(), tab: newTable(initialSlots), space: new(Space)} |
| 91 | x.h = xturbo.Handler{OnEvent: x.onEvent, OnReply: x.onReply, OnError: x.onError} | 91 | x.h = xturbo.Handler{OnEvent: x.onEvent, OnReply: x.onReply, OnError: x.onError} | ||
| 92 | x.fit() | 92 | x.fit() | ||
| 93 | return x | 93 | return x | ||
| @@ -360,7 +360,7 @@ func (x *XWM) Close() { | |||||
| 360 | x.host.Dissociate(Slot(i+1), true) | 360 | x.host.Dissociate(Slot(i+1), true) | ||
| 361 | case r.kind == kindUnmanaged && r.ovPaired: | 361 | case r.kind == kindUnmanaged && r.ovPaired: | ||
| 362 | r.ovPaired = false | 362 | r.ovPaired = false | ||
| 363 | - | x.host.OverrideGone(Slot(i+1), true) | 363 | + | x.host.OverrideGone(Slot(i + 1)) |
| 364 | } | 364 | } | ||
| 365 | } | 365 | } | ||
| 366 | } | 366 | } | ||