refactor(core): group Core's lock state and sent caches into sub-structs
- commit
- 640f8005b6d59f19b0a3a9edf79f91e645274a52
- author
- brice · · 2026-10-08 05:54 UTC
- parent
- 794cca9
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
6 files · +45 -33
M internal/core/core.go +16 -12
| @@ -76,9 +76,7 @@ type Core struct { | |||
| 76 | 76 | // workspace while overviewReal is filled) and by state(); never retained. | |
| 77 | 77 | realBuf []Placement | |
| 78 | 78 | security ports.SecurityState | |
| 79 | - | lockSurfaces []ports.LockSurfacePlacement | |
| 80 | - | lockFocus WindowID | |
| 81 | - | lockPinned bool // lockFocus was clicked or typed into: it keeps focus | |
| 79 | + | lock lockState | |
| 82 | 80 | inputKeys map[string]bool | |
| 83 | 81 | inputActive bool | |
| 84 | 82 | startup [][]string | |
| @@ -115,18 +113,15 @@ type Core struct { | |||
| 115 | 113 | seq uint64 | |
| 116 | 114 | // layerChanged is set once layer state arrives from wayland. | |
| 117 | 115 | layerChanged bool | |
| 118 | - | sentOutputs ports.SetOutputs | |
| 116 | + | sent sentCache | |
| 119 | 117 | specs []NamedWorkspace | |
| 120 | 118 | presets []Width | |
| 121 | 119 | slots map[slotKey]*slotState | |
| 122 | 120 | placement spawnPlacement | |
| 123 | 121 | // toSpawn holds slots to start; Run sends them (apply has no context). | |
| 124 | 122 | toSpawn []slotKey | |
| 125 | - | sentPending bool | |
| 126 | 123 | firstTerminalResolved bool | |
| 127 | 124 | windows windowRegistry | |
| 128 | - | sentState ports.State | |
| 129 | - | sentWorkspaces ports.Workspaces | |
| 130 | 125 | // popups are the placed xdg_popups; popupOrder stacks them, oldest first. | |
| 131 | 126 | popups map[WindowID]*popupState | |
| 132 | 127 | popupOrder []WindowID | |
| @@ -154,6 +149,15 @@ type Core struct { | |||
| 154 | 149 | pulse focusPulse | |
| 155 | 150 | } | |
| 156 | 151 | ||
| 152 | + | // sentCache is what core last sent on its latest-only channels; an | |
| 153 | + | // unchanged value is not sent again. | |
| 154 | + | type sentCache struct { | |
| 155 | + | outputs ports.SetOutputs | |
| 156 | + | state ports.State | |
| 157 | + | workspaces ports.Workspaces | |
| 158 | + | pending bool // spawn placement pending | |
| 159 | + | } | |
| 160 | + | ||
| 157 | 161 | func keyName(s string) string { | |
| 158 | 162 | if len(s) == 1 && unicode.IsLetter(rune(s[0])) { | |
| 159 | 163 | return strings.ToLower(s) | |
| @@ -314,11 +318,11 @@ func (c *Core) apply(cfg ports.Config) error { | |||
| 314 | 318 | // publishPending tells wayland at once when slots start waiting, before | |
| 315 | 319 | // their windows can map. | |
| 316 | 320 | func (c *Core) publishPending(ctx context.Context) error { | |
| 317 | - | if p := c.placement.anyPending(); p != c.sentPending { | |
| 321 | + | if p := c.placement.anyPending(); p != c.sent.pending { | |
| 318 | 322 | if err := c.command(ctx, ports.SlotsPending{Pending: p}); err != nil { | |
| 319 | 323 | return err | |
| 320 | 324 | } | |
| 321 | - | c.sentPending = p | |
| 325 | + | c.sent.pending = p | |
| 322 | 326 | } | |
| 323 | 327 | return nil | |
| 324 | 328 | } | |
| @@ -835,16 +839,16 @@ func (c *Core) warpPointer(ctx context.Context, v ports.PointerWarp) error { | |||
| 835 | 839 | // geometry only when the outputs themselves moved. | |
| 836 | 840 | func (c *Core) syncOutputs(ctx context.Context) error { | |
| 837 | 841 | v := ports.SetOutputs{Outputs: c.layout(), Focused: c.cur().name(), Off: c.offOutputs()} | |
| 838 | - | if sameOutputs(v, c.sentOutputs) { | |
| 842 | + | if sameOutputs(v, c.sent.outputs) { | |
| 839 | 843 | return nil | |
| 840 | 844 | } | |
| 841 | 845 | if err := c.command(ctx, v); err != nil { | |
| 842 | 846 | return err | |
| 843 | 847 | } | |
| 844 | - | if c.ch.Layouts != nil && !slices.Equal(v.Outputs, c.sentOutputs.Outputs) { | |
| 848 | + | if c.ch.Layouts != nil && !slices.Equal(v.Outputs, c.sent.outputs.Outputs) { | |
| 845 | 849 | latest(c.ch.Layouts, v.Outputs) | |
| 846 | 850 | } | |
| 847 | - | c.sentOutputs = v | |
| 851 | + | c.sent.outputs = v | |
| 848 | 852 | return nil | |
| 849 | 853 | } | |
| 850 | 854 | ||
| @@ -76,9 +76,7 @@ type Core struct { | |||||
| 76 | // workspace while overviewReal is filled) and by state(); never retained. | 76 | // workspace while overviewReal is filled) and by state(); never retained. | ||
| 77 | realBuf []Placement | 77 | realBuf []Placement | ||
| 78 | security ports.SecurityState | 78 | security ports.SecurityState | ||
| 79 | - | lockSurfaces []ports.LockSurfacePlacement | 79 | + | lock lockState |
| 80 | - | lockFocus WindowID | |||
| 81 | - | lockPinned bool // lockFocus was clicked or typed into: it keeps focus | |||
| 82 | inputKeys map[string]bool | 80 | inputKeys map[string]bool | ||
| 83 | inputActive bool | 81 | inputActive bool | ||
| 84 | startup [][]string | 82 | startup [][]string | ||
| @@ -115,18 +113,15 @@ type Core struct { | |||||
| 115 | seq uint64 | 113 | seq uint64 | ||
| 116 | // layerChanged is set once layer state arrives from wayland. | 114 | // layerChanged is set once layer state arrives from wayland. | ||
| 117 | layerChanged bool | 115 | layerChanged bool | ||
| 118 | - | sentOutputs ports.SetOutputs | 116 | + | sent sentCache |
| 119 | specs []NamedWorkspace | 117 | specs []NamedWorkspace | ||
| 120 | presets []Width | 118 | presets []Width | ||
| 121 | slots map[slotKey]*slotState | 119 | slots map[slotKey]*slotState | ||
| 122 | placement spawnPlacement | 120 | placement spawnPlacement | ||
| 123 | // toSpawn holds slots to start; Run sends them (apply has no context). | 121 | // toSpawn holds slots to start; Run sends them (apply has no context). | ||
| 124 | toSpawn []slotKey | 122 | toSpawn []slotKey | ||
| 125 | - | sentPending bool | |||
| 126 | firstTerminalResolved bool | 123 | firstTerminalResolved bool | ||
| 127 | windows windowRegistry | 124 | windows windowRegistry | ||
| 128 | - | sentState ports.State | |||
| 129 | - | sentWorkspaces ports.Workspaces | |||
| 130 | // popups are the placed xdg_popups; popupOrder stacks them, oldest first. | 125 | // popups are the placed xdg_popups; popupOrder stacks them, oldest first. | ||
| 131 | popups map[WindowID]*popupState | 126 | popups map[WindowID]*popupState | ||
| 132 | popupOrder []WindowID | 127 | popupOrder []WindowID | ||
| @@ -154,6 +149,15 @@ type Core struct { | |||||
| 154 | pulse focusPulse | 149 | pulse focusPulse | ||
| 155 | } | 150 | } | ||
| 156 | 151 | ||||
| 152 | + | // sentCache is what core last sent on its latest-only channels; an | |||
| 153 | + | // unchanged value is not sent again. | |||
| 154 | + | type sentCache struct { | |||
| 155 | + | outputs ports.SetOutputs | |||
| 156 | + | state ports.State | |||
| 157 | + | workspaces ports.Workspaces | |||
| 158 | + | pending bool // spawn placement pending | |||
| 159 | + | } | |||
| 160 | + | ||||
| 157 | func keyName(s string) string { | 161 | func keyName(s string) string { | ||
| 158 | if len(s) == 1 && unicode.IsLetter(rune(s[0])) { | 162 | if len(s) == 1 && unicode.IsLetter(rune(s[0])) { | ||
| 159 | return strings.ToLower(s) | 163 | return strings.ToLower(s) | ||
| @@ -314,11 +318,11 @@ func (c *Core) apply(cfg ports.Config) error { | |||||
| 314 | // publishPending tells wayland at once when slots start waiting, before | 318 | // publishPending tells wayland at once when slots start waiting, before | ||
| 315 | // their windows can map. | 319 | // their windows can map. | ||
| 316 | func (c *Core) publishPending(ctx context.Context) error { | 320 | func (c *Core) publishPending(ctx context.Context) error { | ||
| 317 | - | if p := c.placement.anyPending(); p != c.sentPending { | 321 | + | if p := c.placement.anyPending(); p != c.sent.pending { |
| 318 | if err := c.command(ctx, ports.SlotsPending{Pending: p}); err != nil { | 322 | if err := c.command(ctx, ports.SlotsPending{Pending: p}); err != nil { | ||
| 319 | return err | 323 | return err | ||
| 320 | } | 324 | } | ||
| 321 | - | c.sentPending = p | 325 | + | c.sent.pending = p |
| 322 | } | 326 | } | ||
| 323 | return nil | 327 | return nil | ||
| 324 | } | 328 | } | ||
| @@ -835,16 +839,16 @@ func (c *Core) warpPointer(ctx context.Context, v ports.PointerWarp) error { | |||||
| 835 | // geometry only when the outputs themselves moved. | 839 | // geometry only when the outputs themselves moved. | ||
| 836 | func (c *Core) syncOutputs(ctx context.Context) error { | 840 | func (c *Core) syncOutputs(ctx context.Context) error { | ||
| 837 | v := ports.SetOutputs{Outputs: c.layout(), Focused: c.cur().name(), Off: c.offOutputs()} | 841 | v := ports.SetOutputs{Outputs: c.layout(), Focused: c.cur().name(), Off: c.offOutputs()} | ||
| 838 | - | if sameOutputs(v, c.sentOutputs) { | 842 | + | if sameOutputs(v, c.sent.outputs) { |
| 839 | return nil | 843 | return nil | ||
| 840 | } | 844 | } | ||
| 841 | if err := c.command(ctx, v); err != nil { | 845 | if err := c.command(ctx, v); err != nil { | ||
| 842 | return err | 846 | return err | ||
| 843 | } | 847 | } | ||
| 844 | - | if c.ch.Layouts != nil && !slices.Equal(v.Outputs, c.sentOutputs.Outputs) { | 848 | + | if c.ch.Layouts != nil && !slices.Equal(v.Outputs, c.sent.outputs.Outputs) { |
| 845 | latest(c.ch.Layouts, v.Outputs) | 849 | latest(c.ch.Layouts, v.Outputs) | ||
| 846 | } | 850 | } | ||
| 847 | - | c.sentOutputs = v | 851 | + | c.sent.outputs = v |
| 848 | return nil | 852 | return nil | ||
| 849 | } | 853 | } | ||
| 850 | 854 | ||||
M internal/core/security.go +23 -15
| @@ -7,6 +7,14 @@ import ( | |||
| 7 | 7 | "github.com/bnema/neferwl/internal/ports" | |
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | + | // lockState is the session lock's surfaces and keyboard focus. A new | |
| 11 | + | // security epoch resets it whole. | |
| 12 | + | type lockState struct { | |
| 13 | + | surfaces []ports.LockSurfacePlacement | |
| 14 | + | focus WindowID | |
| 15 | + | pinned bool // focus was clicked or typed into: it keeps focus | |
| 16 | + | } | |
| 17 | + | ||
| 10 | 18 | // syncSecurity runs only on the owner. The gate, not event selection order, | |
| 11 | 19 | // establishes the epoch; surface events can only fill in that exact epoch. | |
| 12 | 20 | func (c *Core) syncSecurity() bool { | |
| @@ -21,7 +29,7 @@ func (c *Core) syncSecurity() bool { | |||
| 21 | 29 | return false | |
| 22 | 30 | } | |
| 23 | 31 | c.security = state | |
| 24 | - | c.lockSurfaces, c.lockFocus, c.lockPinned = nil, 0, false | |
| 32 | + | c.lock = lockState{} | |
| 25 | 33 | c.pressed, c.buttons, c.inputKeys = map[string]bool{}, map[uint32]bool{}, map[string]bool{} | |
| 26 | 34 | c.pointer, c.grab, c.pointerOutput = 0, 0, "" | |
| 27 | 35 | c.drag, c.swallow, c.mods, c.lastButton = nil, map[uint32]bool{}, 0, 0 | |
| @@ -95,7 +103,7 @@ func (c *Core) applyLockChanged(v ports.SessionLockChanged) bool { | |||
| 95 | 103 | if !c.security.Protected { | |
| 96 | 104 | return true | |
| 97 | 105 | } | |
| 98 | - | c.lockSurfaces = slices.Clone(v.Surfaces) | |
| 106 | + | c.lock.surfaces = slices.Clone(v.Surfaces) | |
| 99 | 107 | c.lockKeyboardFocus() | |
| 100 | 108 | // A destroyed or resized role must not retain a pointer grab. | |
| 101 | 109 | if !c.validLockID(c.pointer) { | |
| @@ -110,7 +118,7 @@ func (c *Core) applyLockChanged(v ports.SessionLockChanged) bool { | |||
| 110 | 118 | ||
| 111 | 119 | func (c *Core) lockSurface(sc *screen) (ports.LockSurfacePlacement, bool) { | |
| 112 | 120 | o := sc.mon.Output() | |
| 113 | - | for _, s := range c.lockSurfaces { | |
| 121 | + | for _, s := range c.lock.surfaces { | |
| 114 | 122 | if s.ID != 0 && sc.name() != "" && s.Output == sc.name() && s.Width == o.W && s.Height == o.H && o.W > 0 && o.H > 0 { | |
| 115 | 123 | return s, true | |
| 116 | 124 | } | |
| @@ -136,27 +144,27 @@ func (c *Core) validLockID(id WindowID) bool { | |||
| 136 | 144 | // surface once it maps: lock clients map their surfaces one by one, in any | |
| 137 | 145 | // order. | |
| 138 | 146 | func (c *Core) lockKeyboardFocus() WindowID { | |
| 139 | - | valid := c.validLockID(c.lockFocus) | |
| 140 | - | if valid && c.lockPinned { | |
| 141 | - | return c.lockFocus | |
| 147 | + | valid := c.validLockID(c.lock.focus) | |
| 148 | + | if valid && c.lock.pinned { | |
| 149 | + | return c.lock.focus | |
| 142 | 150 | } | |
| 143 | 151 | if c.focusScreen >= 0 && c.focusScreen < len(c.screens) { | |
| 144 | 152 | if s, ok := c.lockSurface(c.screens[c.focusScreen]); ok { | |
| 145 | - | c.lockFocus, c.lockPinned = s.ID, false | |
| 146 | - | return c.lockFocus | |
| 153 | + | c.lock.focus, c.lock.pinned = s.ID, false | |
| 154 | + | return c.lock.focus | |
| 147 | 155 | } | |
| 148 | 156 | } | |
| 149 | 157 | if valid { | |
| 150 | - | return c.lockFocus | |
| 158 | + | return c.lock.focus | |
| 151 | 159 | } | |
| 152 | - | c.lockFocus, c.lockPinned = 0, false | |
| 160 | + | c.lock.focus, c.lock.pinned = 0, false | |
| 153 | 161 | for _, sc := range c.screens { | |
| 154 | 162 | if s, ok := c.lockSurface(sc); ok { | |
| 155 | - | c.lockFocus = s.ID | |
| 163 | + | c.lock.focus = s.ID | |
| 156 | 164 | break | |
| 157 | 165 | } | |
| 158 | 166 | } | |
| 159 | - | return c.lockFocus | |
| 167 | + | return c.lock.focus | |
| 160 | 168 | } | |
| 161 | 169 | ||
| 162 | 170 | func (c *Core) lockHit(x, y float64) (WindowID, float64, float64) { | |
| @@ -242,7 +250,7 @@ func (c *Core) protectedInput(ctx context.Context, ev ports.InputEvent) error { | |||
| 242 | 250 | switch v := ev.(type) { | |
| 243 | 251 | case ports.KeyEvent: | |
| 244 | 252 | if id := c.lockKeyboardFocus(); id != 0 { | |
| 245 | - | c.lockPinned = true // typing must not move focus mid-secret | |
| 253 | + | c.lock.pinned = true // typing must not move focus mid-secret | |
| 246 | 254 | return c.command(ctx, ports.ForwardKey{ID: id, Key: v}) | |
| 247 | 255 | } | |
| 248 | 256 | case ports.PointerMotion: | |
| @@ -277,8 +285,8 @@ func (c *Core) protectedInput(ctx context.Context, ev ports.InputEvent) error { | |||
| 277 | 285 | } | |
| 278 | 286 | if c.validLockID(id) { | |
| 279 | 287 | if v.Pressed { | |
| 280 | - | changed := c.lockFocus != id | |
| 281 | - | c.lockFocus, c.lockPinned = id, true | |
| 288 | + | changed := c.lock.focus != id | |
| 289 | + | c.lock.focus, c.lock.pinned = id, true | |
| 282 | 290 | if changed { | |
| 283 | 291 | if err := c.publishProtected(ctx); err != nil { | |
| 284 | 292 | return err | |
| @@ -7,6 +7,14 @@ import ( | |||||
| 7 | "github.com/bnema/neferwl/internal/ports" | 7 | "github.com/bnema/neferwl/internal/ports" | ||
| 8 | ) | 8 | ) | ||
| 9 | 9 | ||||
| 10 | + | // lockState is the session lock's surfaces and keyboard focus. A new | |||
| 11 | + | // security epoch resets it whole. | |||
| 12 | + | type lockState struct { | |||
| 13 | + | surfaces []ports.LockSurfacePlacement | |||
| 14 | + | focus WindowID | |||
| 15 | + | pinned bool // focus was clicked or typed into: it keeps focus | |||
| 16 | + | } | |||
| 17 | + | ||||
| 10 | // syncSecurity runs only on the owner. The gate, not event selection order, | 18 | // syncSecurity runs only on the owner. The gate, not event selection order, | ||
| 11 | // establishes the epoch; surface events can only fill in that exact epoch. | 19 | // establishes the epoch; surface events can only fill in that exact epoch. | ||
| 12 | func (c *Core) syncSecurity() bool { | 20 | func (c *Core) syncSecurity() bool { | ||
| @@ -21,7 +29,7 @@ func (c *Core) syncSecurity() bool { | |||||
| 21 | return false | 29 | return false | ||
| 22 | } | 30 | } | ||
| 23 | c.security = state | 31 | c.security = state | ||
| 24 | - | c.lockSurfaces, c.lockFocus, c.lockPinned = nil, 0, false | 32 | + | c.lock = lockState{} |
| 25 | c.pressed, c.buttons, c.inputKeys = map[string]bool{}, map[uint32]bool{}, map[string]bool{} | 33 | c.pressed, c.buttons, c.inputKeys = map[string]bool{}, map[uint32]bool{}, map[string]bool{} | ||
| 26 | c.pointer, c.grab, c.pointerOutput = 0, 0, "" | 34 | c.pointer, c.grab, c.pointerOutput = 0, 0, "" | ||
| 27 | c.drag, c.swallow, c.mods, c.lastButton = nil, map[uint32]bool{}, 0, 0 | 35 | c.drag, c.swallow, c.mods, c.lastButton = nil, map[uint32]bool{}, 0, 0 | ||
| @@ -95,7 +103,7 @@ func (c *Core) applyLockChanged(v ports.SessionLockChanged) bool { | |||||
| 95 | if !c.security.Protected { | 103 | if !c.security.Protected { | ||
| 96 | return true | 104 | return true | ||
| 97 | } | 105 | } | ||
| 98 | - | c.lockSurfaces = slices.Clone(v.Surfaces) | 106 | + | c.lock.surfaces = slices.Clone(v.Surfaces) |
| 99 | c.lockKeyboardFocus() | 107 | c.lockKeyboardFocus() | ||
| 100 | // A destroyed or resized role must not retain a pointer grab. | 108 | // A destroyed or resized role must not retain a pointer grab. | ||
| 101 | if !c.validLockID(c.pointer) { | 109 | if !c.validLockID(c.pointer) { | ||
| @@ -110,7 +118,7 @@ func (c *Core) applyLockChanged(v ports.SessionLockChanged) bool { | |||||
| 110 | 118 | ||||
| 111 | func (c *Core) lockSurface(sc *screen) (ports.LockSurfacePlacement, bool) { | 119 | func (c *Core) lockSurface(sc *screen) (ports.LockSurfacePlacement, bool) { | ||
| 112 | o := sc.mon.Output() | 120 | o := sc.mon.Output() | ||
| 113 | - | for _, s := range c.lockSurfaces { | 121 | + | for _, s := range c.lock.surfaces { |
| 114 | if s.ID != 0 && sc.name() != "" && s.Output == sc.name() && s.Width == o.W && s.Height == o.H && o.W > 0 && o.H > 0 { | 122 | if s.ID != 0 && sc.name() != "" && s.Output == sc.name() && s.Width == o.W && s.Height == o.H && o.W > 0 && o.H > 0 { | ||
| 115 | return s, true | 123 | return s, true | ||
| 116 | } | 124 | } | ||
| @@ -136,27 +144,27 @@ func (c *Core) validLockID(id WindowID) bool { | |||||
| 136 | // surface once it maps: lock clients map their surfaces one by one, in any | 144 | // surface once it maps: lock clients map their surfaces one by one, in any | ||
| 137 | // order. | 145 | // order. | ||
| 138 | func (c *Core) lockKeyboardFocus() WindowID { | 146 | func (c *Core) lockKeyboardFocus() WindowID { | ||
| 139 | - | valid := c.validLockID(c.lockFocus) | 147 | + | valid := c.validLockID(c.lock.focus) |
| 140 | - | if valid && c.lockPinned { | 148 | + | if valid && c.lock.pinned { |
| 141 | - | return c.lockFocus | 149 | + | return c.lock.focus |
| 142 | } | 150 | } | ||
| 143 | if c.focusScreen >= 0 && c.focusScreen < len(c.screens) { | 151 | if c.focusScreen >= 0 && c.focusScreen < len(c.screens) { | ||
| 144 | if s, ok := c.lockSurface(c.screens[c.focusScreen]); ok { | 152 | if s, ok := c.lockSurface(c.screens[c.focusScreen]); ok { | ||
| 145 | - | c.lockFocus, c.lockPinned = s.ID, false | 153 | + | c.lock.focus, c.lock.pinned = s.ID, false |
| 146 | - | return c.lockFocus | 154 | + | return c.lock.focus |
| 147 | } | 155 | } | ||
| 148 | } | 156 | } | ||
| 149 | if valid { | 157 | if valid { | ||
| 150 | - | return c.lockFocus | 158 | + | return c.lock.focus |
| 151 | } | 159 | } | ||
| 152 | - | c.lockFocus, c.lockPinned = 0, false | 160 | + | c.lock.focus, c.lock.pinned = 0, false |
| 153 | for _, sc := range c.screens { | 161 | for _, sc := range c.screens { | ||
| 154 | if s, ok := c.lockSurface(sc); ok { | 162 | if s, ok := c.lockSurface(sc); ok { | ||
| 155 | - | c.lockFocus = s.ID | 163 | + | c.lock.focus = s.ID |
| 156 | break | 164 | break | ||
| 157 | } | 165 | } | ||
| 158 | } | 166 | } | ||
| 159 | - | return c.lockFocus | 167 | + | return c.lock.focus |
| 160 | } | 168 | } | ||
| 161 | 169 | ||||
| 162 | func (c *Core) lockHit(x, y float64) (WindowID, float64, float64) { | 170 | func (c *Core) lockHit(x, y float64) (WindowID, float64, float64) { | ||
| @@ -242,7 +250,7 @@ func (c *Core) protectedInput(ctx context.Context, ev ports.InputEvent) error { | |||||
| 242 | switch v := ev.(type) { | 250 | switch v := ev.(type) { | ||
| 243 | case ports.KeyEvent: | 251 | case ports.KeyEvent: | ||
| 244 | if id := c.lockKeyboardFocus(); id != 0 { | 252 | if id := c.lockKeyboardFocus(); id != 0 { | ||
| 245 | - | c.lockPinned = true // typing must not move focus mid-secret | 253 | + | c.lock.pinned = true // typing must not move focus mid-secret |
| 246 | return c.command(ctx, ports.ForwardKey{ID: id, Key: v}) | 254 | return c.command(ctx, ports.ForwardKey{ID: id, Key: v}) | ||
| 247 | } | 255 | } | ||
| 248 | case ports.PointerMotion: | 256 | case ports.PointerMotion: | ||
| @@ -277,8 +285,8 @@ func (c *Core) protectedInput(ctx context.Context, ev ports.InputEvent) error { | |||||
| 277 | } | 285 | } | ||
| 278 | if c.validLockID(id) { | 286 | if c.validLockID(id) { | ||
| 279 | if v.Pressed { | 287 | if v.Pressed { | ||
| 280 | - | changed := c.lockFocus != id | 288 | + | changed := c.lock.focus != id |
| 281 | - | c.lockFocus, c.lockPinned = id, true | 289 | + | c.lock.focus, c.lock.pinned = id, true |
| 282 | if changed { | 290 | if changed { | ||
| 283 | if err := c.publishProtected(ctx); err != nil { | 291 | if err := c.publishProtected(ctx); err != nil { | ||
| 284 | return err | 292 | return err | ||
M internal/core/security_internal_test.go +1 -1
| @@ -62,7 +62,7 @@ func TestProtectedDropsPrivateOffscreenCaptureAndDesktopEffects(t *testing.T) { | |||
| 62 | 62 | c.pointer, c.grab = 10, 10 | |
| 63 | 63 | c.keyboard = keyboard{sent: 10} | |
| 64 | 64 | state = ports.SecurityState{Generation: 2} | |
| 65 | - | if !c.syncSecurity() || len(c.inputKeys) != 0 || c.grab != 0 || c.keyboard.sent != 0 || len(c.lockSurfaces) != 0 { | |
| 65 | + | if !c.syncSecurity() || len(c.inputKeys) != 0 || c.grab != 0 || c.keyboard.sent != 0 || len(c.lock.surfaces) != 0 { | |
| 66 | 66 | t.Fatal("release did not reset input ownership") | |
| 67 | 67 | } | |
| 68 | 68 | for _, ev := range []ports.InputEvent{ports.KeyEvent{Keycode: 30}, ports.PointerButton{Button: 0x110}} { | |
| @@ -62,7 +62,7 @@ func TestProtectedDropsPrivateOffscreenCaptureAndDesktopEffects(t *testing.T) { | |||||
| 62 | c.pointer, c.grab = 10, 10 | 62 | c.pointer, c.grab = 10, 10 | ||
| 63 | c.keyboard = keyboard{sent: 10} | 63 | c.keyboard = keyboard{sent: 10} | ||
| 64 | state = ports.SecurityState{Generation: 2} | 64 | state = ports.SecurityState{Generation: 2} | ||
| 65 | - | if !c.syncSecurity() || len(c.inputKeys) != 0 || c.grab != 0 || c.keyboard.sent != 0 || len(c.lockSurfaces) != 0 { | 65 | + | if !c.syncSecurity() || len(c.inputKeys) != 0 || c.grab != 0 || c.keyboard.sent != 0 || len(c.lock.surfaces) != 0 { |
| 66 | t.Fatal("release did not reset input ownership") | 66 | t.Fatal("release did not reset input ownership") | ||
| 67 | } | 67 | } | ||
| 68 | for _, ev := range []ports.InputEvent{ports.KeyEvent{Keycode: 30}, ports.PointerButton{Button: 0x110}} { | 68 | for _, ev := range []ports.InputEvent{ports.KeyEvent{Keycode: 30}, ports.PointerButton{Button: 0x110}} { | ||
M internal/core/security_review_test.go +1 -1
| @@ -39,7 +39,7 @@ func TestInputOutputSwitchBackpressureKeepsAdmittedEpoch(t *testing.T) { | |||
| 39 | 39 | return v | |
| 40 | 40 | }) | |
| 41 | 41 | c.opts.Security = gate | |
| 42 | - | c.sentOutputs = ports.SetOutputs{Outputs: c.layout(), Focused: c.cur().name(), Off: c.offOutputs()} | |
| 42 | + | c.sent.outputs = ports.SetOutputs{Outputs: c.layout(), Focused: c.cur().name(), Off: c.offOutputs()} | |
| 43 | 43 | commands := make(chan ports.ClientCommand) | |
| 44 | 44 | c.ch.Commands = commands | |
| 45 | 45 | done := make(chan error, 1) | |
| @@ -39,7 +39,7 @@ func TestInputOutputSwitchBackpressureKeepsAdmittedEpoch(t *testing.T) { | |||||
| 39 | return v | 39 | return v | ||
| 40 | }) | 40 | }) | ||
| 41 | c.opts.Security = gate | 41 | c.opts.Security = gate | ||
| 42 | - | c.sentOutputs = ports.SetOutputs{Outputs: c.layout(), Focused: c.cur().name(), Off: c.offOutputs()} | 42 | + | c.sent.outputs = ports.SetOutputs{Outputs: c.layout(), Focused: c.cur().name(), Off: c.offOutputs()} |
| 43 | commands := make(chan ports.ClientCommand) | 43 | commands := make(chan ports.ClientCommand) | ||
| 44 | c.ch.Commands = commands | 44 | c.ch.Commands = commands | ||
| 45 | done := make(chan error, 1) | 45 | done := make(chan error, 1) | ||
M internal/core/state.go +2 -2
| @@ -14,10 +14,10 @@ func (c *Core) publishState() { | |||
| 14 | 14 | return | |
| 15 | 15 | } | |
| 16 | 16 | st := c.state() | |
| 17 | - | if reflect.DeepEqual(st, c.sentState) { | |
| 17 | + | if reflect.DeepEqual(st, c.sent.state) { | |
| 18 | 18 | return | |
| 19 | 19 | } | |
| 20 | - | c.sentState = st | |
| 20 | + | c.sent.state = st | |
| 21 | 21 | latest(c.ch.State, st) | |
| 22 | 22 | } | |
| 23 | 23 | ||
| @@ -14,10 +14,10 @@ func (c *Core) publishState() { | |||||
| 14 | return | 14 | return | ||
| 15 | } | 15 | } | ||
| 16 | st := c.state() | 16 | st := c.state() | ||
| 17 | - | if reflect.DeepEqual(st, c.sentState) { | 17 | + | if reflect.DeepEqual(st, c.sent.state) { |
| 18 | return | 18 | return | ||
| 19 | } | 19 | } | ||
| 20 | - | c.sentState = st | 20 | + | c.sent.state = st |
| 21 | latest(c.ch.State, st) | 21 | latest(c.ch.State, st) | ||
| 22 | } | 22 | } | ||
| 23 | 23 | ||||
M internal/core/workspaces.go +2 -2
| @@ -32,9 +32,9 @@ func (c *Core) publishWorkspaces() { | |||
| 32 | 32 | } | |
| 33 | 33 | snapshot.Outputs = append(snapshot.Outputs, out) | |
| 34 | 34 | } | |
| 35 | - | if reflect.DeepEqual(snapshot, c.sentWorkspaces) { | |
| 35 | + | if reflect.DeepEqual(snapshot, c.sent.workspaces) { | |
| 36 | 36 | return | |
| 37 | 37 | } | |
| 38 | - | c.sentWorkspaces = snapshot | |
| 38 | + | c.sent.workspaces = snapshot | |
| 39 | 39 | latest(c.ch.Workspaces, snapshot) | |
| 40 | 40 | } | |
| @@ -32,9 +32,9 @@ func (c *Core) publishWorkspaces() { | |||||
| 32 | } | 32 | } | ||
| 33 | snapshot.Outputs = append(snapshot.Outputs, out) | 33 | snapshot.Outputs = append(snapshot.Outputs, out) | ||
| 34 | } | 34 | } | ||
| 35 | - | if reflect.DeepEqual(snapshot, c.sentWorkspaces) { | 35 | + | if reflect.DeepEqual(snapshot, c.sent.workspaces) { |
| 36 | return | 36 | return | ||
| 37 | } | 37 | } | ||
| 38 | - | c.sentWorkspaces = snapshot | 38 | + | c.sent.workspaces = snapshot |
| 39 | latest(c.ch.Workspaces, snapshot) | 39 | latest(c.ch.Workspaces, snapshot) | ||
| 40 | } | 40 | } | ||