perf: cache modifiers and layout per mask change; nil-safe Seat
- commit
- 7219ecee67ffe05dcf67936c6fb43fd78edc0982
- author
- brice · · 2026-10-01 18:37 UTC
- parent
- fe9795c
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
3 files · +25 -14
M keyboard.go +15 -7
| @@ -54,6 +54,8 @@ type keyboard struct { | |||
| 54 | 54 | compose *xkb.ComposeState | |
| 55 | 55 | ||
| 56 | 56 | modBits [len(modNames)]uint32 // xkb mask per Modifiers bit; 0 when absent | |
| 57 | + | mods Modifiers // effective modifiers and layout, refreshed when the mask changes | |
| 58 | + | group uint32 | |
| 57 | 59 | lastMask [6]uint32 | |
| 58 | 60 | haveMask bool | |
| 59 | 61 | ||
| @@ -173,6 +175,7 @@ func (k *keyboard) replace(km *xkb.Keymap) error { | |||
| 173 | 175 | if k.haveMask { // the compositor's modifiers survive a keymap change | |
| 174 | 176 | _, _ = s.UpdateMask(k.lastMask[0], k.lastMask[1], k.lastMask[2], k.lastMask[3], k.lastMask[4], k.lastMask[5]) | |
| 175 | 177 | } | |
| 178 | + | k.refresh() | |
| 176 | 179 | return nil | |
| 177 | 180 | } | |
| 178 | 181 | ||
| @@ -183,9 +186,17 @@ func (k *keyboard) setMask(depressed, latched, locked, group uint32) error { | |||
| 183 | 186 | return nil | |
| 184 | 187 | } | |
| 185 | 188 | _, err := k.state.UpdateMask(depressed, latched, locked, 0, 0, group) | |
| 189 | + | k.refresh() | |
| 186 | 190 | return err | |
| 187 | 191 | } | |
| 188 | 192 | ||
| 193 | + | // refresh recomputes the cached modifiers and layout from the xkb state. Both | |
| 194 | + | // only change when the mask does, so keys do not query them. | |
| 195 | + | func (k *keyboard) refresh() { | |
| 196 | + | k.mods, k.group = k.queryModifiers(), 0 | |
| 197 | + | k.group, _ = k.state.Layout() | |
| 198 | + | } | |
| 199 | + | ||
| 189 | 200 | // reset drops held keys, repeat and compose state. It keeps the keymap, the | |
| 190 | 201 | // modifier mask and the focus flag. | |
| 191 | 202 | func (k *keyboard) reset() { | |
| @@ -239,7 +250,7 @@ func (k *keyboard) expired() bool { | |||
| 239 | 250 | return err == nil && n == 8 | |
| 240 | 251 | } | |
| 241 | 252 | ||
| 242 | - | func (k *keyboard) modifiers() Modifiers { | |
| 253 | + | func (k *keyboard) queryModifiers() Modifiers { | |
| 243 | 254 | mods, err := k.state.Mods() | |
| 244 | 255 | if err != nil { | |
| 245 | 256 | return 0 | |
| @@ -312,10 +323,8 @@ func (k *keyboard) release(evdev uint32, ev *KeyEvent) bool { | |||
| 312 | 323 | k.disarm() | |
| 313 | 324 | } | |
| 314 | 325 | ev.Keycode, ev.Keysym, ev.Pressed = evdev, h.sym, false | |
| 315 | - | if k.state != nil { | |
| 316 | - | ev.Modifiers = k.modifiers() | |
| 317 | - | ev.Group, _ = k.state.Layout() | |
| 318 | - | } | |
| 326 | + | k.changed = false | |
| 327 | + | ev.Modifiers, ev.Group = k.mods, k.group | |
| 319 | 328 | return true | |
| 320 | 329 | } | |
| 321 | 330 | ||
| @@ -353,8 +362,7 @@ func (k *keyboard) repeat(ev *KeyEvent) (bool, error) { | |||
| 353 | 362 | // the SecretBuffer, if one is set. In secret mode ev.Text stays nil. | |
| 354 | 363 | func (k *keyboard) translate(evdev, sym uint32, repeat bool, ev *KeyEvent) (textKind, error) { | |
| 355 | 364 | ev.Keycode, ev.Keysym, ev.Pressed, ev.Repeat = evdev, sym, true, repeat | |
| 356 | - | ev.Modifiers = k.modifiers() | |
| 357 | - | ev.Group, _ = k.state.Layout() | |
| 365 | + | ev.Modifiers, ev.Group = k.mods, k.group | |
| 358 | 366 | k.changed = false | |
| 359 | 367 | if k.secret != nil { | |
| 360 | 368 | w := &k.w | |
| @@ -54,6 +54,8 @@ type keyboard struct { | |||||
| 54 | compose *xkb.ComposeState | 54 | compose *xkb.ComposeState | ||
| 55 | 55 | ||||
| 56 | modBits [len(modNames)]uint32 // xkb mask per Modifiers bit; 0 when absent | 56 | modBits [len(modNames)]uint32 // xkb mask per Modifiers bit; 0 when absent | ||
| 57 | + | mods Modifiers // effective modifiers and layout, refreshed when the mask changes | |||
| 58 | + | group uint32 | |||
| 57 | lastMask [6]uint32 | 59 | lastMask [6]uint32 | ||
| 58 | haveMask bool | 60 | haveMask bool | ||
| 59 | 61 | ||||
| @@ -173,6 +175,7 @@ func (k *keyboard) replace(km *xkb.Keymap) error { | |||||
| 173 | if k.haveMask { // the compositor's modifiers survive a keymap change | 175 | if k.haveMask { // the compositor's modifiers survive a keymap change | ||
| 174 | _, _ = s.UpdateMask(k.lastMask[0], k.lastMask[1], k.lastMask[2], k.lastMask[3], k.lastMask[4], k.lastMask[5]) | 176 | _, _ = s.UpdateMask(k.lastMask[0], k.lastMask[1], k.lastMask[2], k.lastMask[3], k.lastMask[4], k.lastMask[5]) | ||
| 175 | } | 177 | } | ||
| 178 | + | k.refresh() | |||
| 176 | return nil | 179 | return nil | ||
| 177 | } | 180 | } | ||
| 178 | 181 | ||||
| @@ -183,9 +186,17 @@ func (k *keyboard) setMask(depressed, latched, locked, group uint32) error { | |||||
| 183 | return nil | 186 | return nil | ||
| 184 | } | 187 | } | ||
| 185 | _, err := k.state.UpdateMask(depressed, latched, locked, 0, 0, group) | 188 | _, err := k.state.UpdateMask(depressed, latched, locked, 0, 0, group) | ||
| 189 | + | k.refresh() | |||
| 186 | return err | 190 | return err | ||
| 187 | } | 191 | } | ||
| 188 | 192 | ||||
| 193 | + | // refresh recomputes the cached modifiers and layout from the xkb state. Both | |||
| 194 | + | // only change when the mask does, so keys do not query them. | |||
| 195 | + | func (k *keyboard) refresh() { | |||
| 196 | + | k.mods, k.group = k.queryModifiers(), 0 | |||
| 197 | + | k.group, _ = k.state.Layout() | |||
| 198 | + | } | |||
| 199 | + | ||||
| 189 | // reset drops held keys, repeat and compose state. It keeps the keymap, the | 200 | // reset drops held keys, repeat and compose state. It keeps the keymap, the | ||
| 190 | // modifier mask and the focus flag. | 201 | // modifier mask and the focus flag. | ||
| 191 | func (k *keyboard) reset() { | 202 | func (k *keyboard) reset() { | ||
| @@ -239,7 +250,7 @@ func (k *keyboard) expired() bool { | |||||
| 239 | return err == nil && n == 8 | 250 | return err == nil && n == 8 | ||
| 240 | } | 251 | } | ||
| 241 | 252 | ||||
| 242 | - | func (k *keyboard) modifiers() Modifiers { | 253 | + | func (k *keyboard) queryModifiers() Modifiers { |
| 243 | mods, err := k.state.Mods() | 254 | mods, err := k.state.Mods() | ||
| 244 | if err != nil { | 255 | if err != nil { | ||
| 245 | return 0 | 256 | return 0 | ||
| @@ -312,10 +323,8 @@ func (k *keyboard) release(evdev uint32, ev *KeyEvent) bool { | |||||
| 312 | k.disarm() | 323 | k.disarm() | ||
| 313 | } | 324 | } | ||
| 314 | ev.Keycode, ev.Keysym, ev.Pressed = evdev, h.sym, false | 325 | ev.Keycode, ev.Keysym, ev.Pressed = evdev, h.sym, false | ||
| 315 | - | if k.state != nil { | 326 | + | k.changed = false |
| 316 | - | ev.Modifiers = k.modifiers() | 327 | + | ev.Modifiers, ev.Group = k.mods, k.group |
| 317 | - | ev.Group, _ = k.state.Layout() | |||
| 318 | - | } | |||
| 319 | return true | 328 | return true | ||
| 320 | } | 329 | } | ||
| 321 | 330 | ||||
| @@ -353,8 +362,7 @@ func (k *keyboard) repeat(ev *KeyEvent) (bool, error) { | |||||
| 353 | // the SecretBuffer, if one is set. In secret mode ev.Text stays nil. | 362 | // the SecretBuffer, if one is set. In secret mode ev.Text stays nil. | ||
| 354 | func (k *keyboard) translate(evdev, sym uint32, repeat bool, ev *KeyEvent) (textKind, error) { | 363 | func (k *keyboard) translate(evdev, sym uint32, repeat bool, ev *KeyEvent) (textKind, error) { | ||
| 355 | ev.Keycode, ev.Keysym, ev.Pressed, ev.Repeat = evdev, sym, true, repeat | 364 | ev.Keycode, ev.Keysym, ev.Pressed, ev.Repeat = evdev, sym, true, repeat | ||
| 356 | - | ev.Modifiers = k.modifiers() | 365 | + | ev.Modifiers, ev.Group = k.mods, k.group |
| 357 | - | ev.Group, _ = k.state.Layout() | |||
| 358 | k.changed = false | 366 | k.changed = false | ||
| 359 | if k.secret != nil { | 367 | if k.secret != nil { | ||
| 360 | w := &k.w | 368 | w := &k.w | ||
M keyboard_test.go +5 -5
| @@ -94,7 +94,7 @@ func TestKeyboardKeymapFD(t *testing.T) { | |||
| 94 | 94 | _, err = f.Write(append(data, 0)) | |
| 95 | 95 | require.NoError(t, err) | |
| 96 | 96 | ||
| 97 | - | k := testKeyboard(t, "", "us") | |
| 97 | + | k := testKeyboard(t, "C", "us") | |
| 98 | 98 | fd, err := unix.Dup(int(f.Fd())) | |
| 99 | 99 | require.NoError(t, err) | |
| 100 | 100 | require.NoError(t, k.replaceFD(fd, len(data)+1)) | |
| @@ -140,7 +140,7 @@ func pressKey2(k *keyboard, code uint32) (KeyEvent, bool) { | |||
| 140 | 140 | } | |
| 141 | 141 | ||
| 142 | 142 | func TestKeyboardRepeatTimer(t *testing.T) { | |
| 143 | - | k := testKeyboard(t, "", "us") | |
| 143 | + | k := testKeyboard(t, "C", "us") | |
| 144 | 144 | k.setRepeat(20, 50) | |
| 145 | 145 | _, ok := pressKey(t, k, keyQ) | |
| 146 | 146 | require.True(t, ok) | |
| @@ -167,7 +167,7 @@ func TestKeyboardRepeatTimer(t *testing.T) { | |||
| 167 | 167 | } | |
| 168 | 168 | ||
| 169 | 169 | func TestKeyboardRepeatTracksCurrentMask(t *testing.T) { | |
| 170 | - | k := testKeyboard(t, "", "us") | |
| 170 | + | k := testKeyboard(t, "C", "us") | |
| 171 | 171 | k.setRepeat(100, 1) | |
| 172 | 172 | pressKey(t, k, keyQ) | |
| 173 | 173 | idx, err := k.keymap.ModIndex("Shift") | |
| @@ -237,7 +237,7 @@ func TestSecretKeysNeverReachText(t *testing.T) { | |||
| 237 | 237 | } | |
| 238 | 238 | ||
| 239 | 239 | func TestSecretChangedFlag(t *testing.T) { | |
| 240 | - | k := testKeyboard(t, "", "us") | |
| 240 | + | k := testKeyboard(t, "C", "us") | |
| 241 | 241 | buf := NewSecretBuffer(2) | |
| 242 | 242 | k.setSecret(buf) | |
| 243 | 243 | pressKey(t, k, keyQ) | |
| @@ -251,7 +251,7 @@ func TestSecretChangedFlag(t *testing.T) { | |||
| 251 | 251 | } | |
| 252 | 252 | ||
| 253 | 253 | func TestSecretRepeat(t *testing.T) { | |
| 254 | - | k := testKeyboard(t, "", "us") | |
| 254 | + | k := testKeyboard(t, "C", "us") | |
| 255 | 255 | buf := NewSecretBuffer(16) | |
| 256 | 256 | k.setSecret(buf) | |
| 257 | 257 | k.setRepeat(100, 1) | |
| @@ -94,7 +94,7 @@ func TestKeyboardKeymapFD(t *testing.T) { | |||||
| 94 | _, err = f.Write(append(data, 0)) | 94 | _, err = f.Write(append(data, 0)) | ||
| 95 | require.NoError(t, err) | 95 | require.NoError(t, err) | ||
| 96 | 96 | ||||
| 97 | - | k := testKeyboard(t, "", "us") | 97 | + | k := testKeyboard(t, "C", "us") |
| 98 | fd, err := unix.Dup(int(f.Fd())) | 98 | fd, err := unix.Dup(int(f.Fd())) | ||
| 99 | require.NoError(t, err) | 99 | require.NoError(t, err) | ||
| 100 | require.NoError(t, k.replaceFD(fd, len(data)+1)) | 100 | require.NoError(t, k.replaceFD(fd, len(data)+1)) | ||
| @@ -140,7 +140,7 @@ func pressKey2(k *keyboard, code uint32) (KeyEvent, bool) { | |||||
| 140 | } | 140 | } | ||
| 141 | 141 | ||||
| 142 | func TestKeyboardRepeatTimer(t *testing.T) { | 142 | func TestKeyboardRepeatTimer(t *testing.T) { | ||
| 143 | - | k := testKeyboard(t, "", "us") | 143 | + | k := testKeyboard(t, "C", "us") |
| 144 | k.setRepeat(20, 50) | 144 | k.setRepeat(20, 50) | ||
| 145 | _, ok := pressKey(t, k, keyQ) | 145 | _, ok := pressKey(t, k, keyQ) | ||
| 146 | require.True(t, ok) | 146 | require.True(t, ok) | ||
| @@ -167,7 +167,7 @@ func TestKeyboardRepeatTimer(t *testing.T) { | |||||
| 167 | } | 167 | } | ||
| 168 | 168 | ||||
| 169 | func TestKeyboardRepeatTracksCurrentMask(t *testing.T) { | 169 | func TestKeyboardRepeatTracksCurrentMask(t *testing.T) { | ||
| 170 | - | k := testKeyboard(t, "", "us") | 170 | + | k := testKeyboard(t, "C", "us") |
| 171 | k.setRepeat(100, 1) | 171 | k.setRepeat(100, 1) | ||
| 172 | pressKey(t, k, keyQ) | 172 | pressKey(t, k, keyQ) | ||
| 173 | idx, err := k.keymap.ModIndex("Shift") | 173 | idx, err := k.keymap.ModIndex("Shift") | ||
| @@ -237,7 +237,7 @@ func TestSecretKeysNeverReachText(t *testing.T) { | |||||
| 237 | } | 237 | } | ||
| 238 | 238 | ||||
| 239 | func TestSecretChangedFlag(t *testing.T) { | 239 | func TestSecretChangedFlag(t *testing.T) { | ||
| 240 | - | k := testKeyboard(t, "", "us") | 240 | + | k := testKeyboard(t, "C", "us") |
| 241 | buf := NewSecretBuffer(2) | 241 | buf := NewSecretBuffer(2) | ||
| 242 | k.setSecret(buf) | 242 | k.setSecret(buf) | ||
| 243 | pressKey(t, k, keyQ) | 243 | pressKey(t, k, keyQ) | ||
| @@ -251,7 +251,7 @@ func TestSecretChangedFlag(t *testing.T) { | |||||
| 251 | } | 251 | } | ||
| 252 | 252 | ||||
| 253 | func TestSecretRepeat(t *testing.T) { | 253 | func TestSecretRepeat(t *testing.T) { | ||
| 254 | - | k := testKeyboard(t, "", "us") | 254 | + | k := testKeyboard(t, "C", "us") |
| 255 | buf := NewSecretBuffer(16) | 255 | buf := NewSecretBuffer(16) | ||
| 256 | k.setSecret(buf) | 256 | k.setSecret(buf) | ||
| 257 | k.setRepeat(100, 1) | 257 | k.setRepeat(100, 1) | ||
M seat.go +5 -2
| @@ -137,7 +137,7 @@ type Seat struct { | |||
| 137 | 137 | shape CursorShape | |
| 138 | 138 | } | |
| 139 | 139 | ||
| 140 | - | // Seat returns the connection's seat, binding wl_seat on first use. The seat | |
| 140 | + | // Seat returns the connection's seat (nil after Close), binding wl_seat on first use. The seat | |
| 141 | 141 | // is also bound when the first surface is created. Input starts flowing to the | |
| 142 | 142 | // Handler once the compositor announces the capabilities. | |
| 143 | 143 | func (c *Conn) Seat() *Seat { | |
| @@ -187,6 +187,9 @@ func (c *Conn) ensureSeat() { | |||
| 187 | 187 | // KeyEvent without text. Installing or removing cancels held keys and repeat. | |
| 188 | 188 | // The previous buffer is not wiped: that is the caller's call. | |
| 189 | 189 | func (s *Seat) SetSecret(b *SecretBuffer) { | |
| 190 | + | if s == nil { | |
| 191 | + | return | |
| 192 | + | } | |
| 190 | 193 | s.secret = b | |
| 191 | 194 | if s.kb != nil { | |
| 192 | 195 | s.kb.setSecret(b) | |
| @@ -197,7 +200,7 @@ func (s *Seat) SetSecret(b *SecretBuffer) { | |||
| 197 | 200 | // when the compositor has no cursor-shape support, the pointer is outside the | |
| 198 | 201 | // connection's surfaces, or shape is unchanged since the last enter. | |
| 199 | 202 | func (s *Seat) SetCursor(shape CursorShape) error { | |
| 200 | - | if s.c.closed { | |
| 203 | + | if s == nil || s.c.closed { | |
| 201 | 204 | return ErrClosed | |
| 202 | 205 | } | |
| 203 | 206 | limit := CursorZoomOut | |
| @@ -137,7 +137,7 @@ type Seat struct { | |||||
| 137 | shape CursorShape | 137 | shape CursorShape | ||
| 138 | } | 138 | } | ||
| 139 | 139 | ||||
| 140 | - | // Seat returns the connection's seat, binding wl_seat on first use. The seat | 140 | + | // Seat returns the connection's seat (nil after Close), binding wl_seat on first use. The seat |
| 141 | // is also bound when the first surface is created. Input starts flowing to the | 141 | // is also bound when the first surface is created. Input starts flowing to the | ||
| 142 | // Handler once the compositor announces the capabilities. | 142 | // Handler once the compositor announces the capabilities. | ||
| 143 | func (c *Conn) Seat() *Seat { | 143 | func (c *Conn) Seat() *Seat { | ||
| @@ -187,6 +187,9 @@ func (c *Conn) ensureSeat() { | |||||
| 187 | // KeyEvent without text. Installing or removing cancels held keys and repeat. | 187 | // KeyEvent without text. Installing or removing cancels held keys and repeat. | ||
| 188 | // The previous buffer is not wiped: that is the caller's call. | 188 | // The previous buffer is not wiped: that is the caller's call. | ||
| 189 | func (s *Seat) SetSecret(b *SecretBuffer) { | 189 | func (s *Seat) SetSecret(b *SecretBuffer) { | ||
| 190 | + | if s == nil { | |||
| 191 | + | return | |||
| 192 | + | } | |||
| 190 | s.secret = b | 193 | s.secret = b | ||
| 191 | if s.kb != nil { | 194 | if s.kb != nil { | ||
| 192 | s.kb.setSecret(b) | 195 | s.kb.setSecret(b) | ||
| @@ -197,7 +200,7 @@ func (s *Seat) SetSecret(b *SecretBuffer) { | |||||
| 197 | // when the compositor has no cursor-shape support, the pointer is outside the | 200 | // when the compositor has no cursor-shape support, the pointer is outside the | ||
| 198 | // connection's surfaces, or shape is unchanged since the last enter. | 201 | // connection's surfaces, or shape is unchanged since the last enter. | ||
| 199 | func (s *Seat) SetCursor(shape CursorShape) error { | 202 | func (s *Seat) SetCursor(shape CursorShape) error { | ||
| 200 | - | if s.c.closed { | 203 | + | if s == nil || s.c.closed { |
| 201 | return ErrClosed | 204 | return ErrClosed | ||
| 202 | } | 205 | } | ||
| 203 | limit := CursorZoomOut | 206 | limit := CursorZoomOut | ||