feat(wayland): apply temporary modifier overrides for hint clicks
- commit
- 208261521ee7d4dbbad6cddb5c16bd6613f0e7cd
- author
- brice · · 2026-10-19 16:03 UTC
- committer
- brice · · 2026-10-19 17:36 UTC
- parent
- 6319bfa
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
3 files · +146 -3
M internal/adapters/wayland/keyboard_test.go +88 -1
| @@ -130,13 +130,14 @@ type eventsProxy struct { | |||
| 130 | 130 | opcodes []uint16 | |
| 131 | 131 | keymaps []string | |
| 132 | 132 | locked []uint32 // locked modifiers of each modifiers event | |
| 133 | + | depress []uint32 // depressed modifiers of each modifiers event | |
| 133 | 134 | } | |
| 134 | 135 | ||
| 135 | 136 | func (p *eventsProxy) Dispatch(e *wlturbo.Event) { | |
| 136 | 137 | p.opcodes = append(p.opcodes, e.Opcode) | |
| 137 | 138 | if e.Opcode == uint16(wayland.KeyboardEventModifiers) { | |
| 138 | 139 | e.Uint32() // serial | |
| 139 | - | e.Uint32() // depressed | |
| 140 | + | p.depress = append(p.depress, e.Uint32()) | |
| 140 | 141 | e.Uint32() // latched | |
| 141 | 142 | p.locked = append(p.locked, e.Uint32()) | |
| 142 | 143 | return | |
| @@ -254,3 +255,89 @@ func TestFootKeyboard(t *testing.T) { | |||
| 254 | 255 | } | |
| 255 | 256 | } | |
| 256 | 257 | } | |
| 258 | + | ||
| 259 | + | // SetModifiers sends the requested depressed mask with the real latched and | |
| 260 | + | // locked state, and Restore sends the real state back; the seat's own state | |
| 261 | + | // is untouched. A window that is not focused is ignored. | |
| 262 | + | func TestSetModifiersOverrideAndRestore(t *testing.T) { | |
| 263 | + | s, events, commands, dir := keyboardServer(t) | |
| 264 | + | target, proxy := focusedTarget(t, s, events, commands, dir) | |
| 265 | + | const capsLocked = 2 | |
| 266 | + | var id ports.WindowID | |
| 267 | + | set := make(chan struct{}) | |
| 268 | + | s.display.Do(func() { s.seat.modState = ports.ModState{Locked: capsLocked}; id = s.seat.focused; close(set) }) | |
| 269 | + | <-set | |
| 270 | + | // A marker command applied on the display means every command sent before | |
| 271 | + | // it was applied (they apply in order); the roundtrip then delivers the | |
| 272 | + | // events they sent. | |
| 273 | + | var pending bool | |
| 274 | + | wait := func() { | |
| 275 | + | t.Helper() | |
| 276 | + | pending = !pending | |
| 277 | + | commands <- ports.SlotsPending{Pending: pending} | |
| 278 | + | deadline := time.Now().Add(2 * time.Second) | |
| 279 | + | for { | |
| 280 | + | var now bool | |
| 281 | + | if !s.display.Do(func() { now = s.slotsPending }) { | |
| 282 | + | t.Fatal("display stopped") | |
| 283 | + | } | |
| 284 | + | if now == pending { | |
| 285 | + | break | |
| 286 | + | } | |
| 287 | + | if time.Now().After(deadline) { | |
| 288 | + | t.Fatal("commands not applied") | |
| 289 | + | } | |
| 290 | + | time.Sleep(time.Millisecond) | |
| 291 | + | } | |
| 292 | + | roundtrip(t, target) | |
| 293 | + | } | |
| 294 | + | proxy.depress, proxy.locked = nil, nil | |
| 295 | + | commands <- ports.SetModifiers{ID: id + 100, Mods: ports.ModCtrl} | |
| 296 | + | wait() | |
| 297 | + | if len(proxy.depress) != 0 { | |
| 298 | + | t.Fatalf("unfocused window got modifiers %v", proxy.depress) | |
| 299 | + | } | |
| 300 | + | commands <- ports.SetModifiers{ID: id, Mods: ports.ModCtrl | ports.ModShift} | |
| 301 | + | wait() | |
| 302 | + | if !slices.Equal(proxy.depress, []uint32{xkbControl | xkbShift}) || !slices.Equal(proxy.locked, []uint32{capsLocked}) { | |
| 303 | + | t.Fatalf("override depressed %v locked %v", proxy.depress, proxy.locked) | |
| 304 | + | } | |
| 305 | + | done := make(chan ports.ModState, 1) | |
| 306 | + | commands <- ports.SetModifiers{ID: id, Restore: true, State: ports.ModState{Locked: capsLocked}} | |
| 307 | + | wait() | |
| 308 | + | if !slices.Equal(proxy.depress, []uint32{xkbControl | xkbShift, 0}) || !slices.Equal(proxy.locked, []uint32{capsLocked, capsLocked}) { | |
| 309 | + | t.Fatalf("restore depressed %v locked %v", proxy.depress, proxy.locked) | |
| 310 | + | } | |
| 311 | + | s.display.Do(func() { done <- s.seat.modState }) | |
| 312 | + | if m := <-done; m != (ports.ModState{Locked: capsLocked}) { | |
| 313 | + | t.Fatalf("seat state changed: %+v", m) | |
| 314 | + | } | |
| 315 | + | } | |
| 316 | + | ||
| 317 | + | // Restore carries the real state core saw, which swallowed keys (a latch a | |
| 318 | + | // hint label key consumed) never gave the seat: it is sent and adopted. | |
| 319 | + | func TestSetModifiersRestoreAdoptsCoreState(t *testing.T) { | |
| 320 | + | s, events, commands, dir := keyboardServer(t) | |
| 321 | + | target, proxy := focusedTarget(t, s, events, commands, dir) | |
| 322 | + | const shiftLatched, capsLocked = 1, 2 | |
| 323 | + | var id ports.WindowID | |
| 324 | + | set := make(chan struct{}) | |
| 325 | + | s.display.Do(func() { s.seat.modState = ports.ModState{Latched: shiftLatched}; id = s.seat.focused; close(set) }) | |
| 326 | + | <-set | |
| 327 | + | proxy.depress, proxy.locked = nil, nil | |
| 328 | + | want := ports.ModState{Locked: capsLocked} // the latch was consumed | |
| 329 | + | commands <- ports.SetModifiers{ID: id, Restore: true, State: want} | |
| 330 | + | var got ports.ModState | |
| 331 | + | deadline := time.Now().Add(2 * time.Second) | |
| 332 | + | for got != want { | |
| 333 | + | if time.Now().After(deadline) { | |
| 334 | + | t.Fatalf("seat state %+v, want %+v", got, want) | |
| 335 | + | } | |
| 336 | + | s.display.Do(func() { got = s.seat.modState }) | |
| 337 | + | time.Sleep(time.Millisecond) | |
| 338 | + | } | |
| 339 | + | roundtrip(t, target) | |
| 340 | + | if !slices.Equal(proxy.locked, []uint32{capsLocked}) || !slices.Equal(proxy.depress, []uint32{0}) { | |
| 341 | + | t.Fatalf("restore depressed %v locked %v", proxy.depress, proxy.locked) | |
| 342 | + | } | |
| 343 | + | } | |
| @@ -130,13 +130,14 @@ type eventsProxy struct { | |||||
| 130 | opcodes []uint16 | 130 | opcodes []uint16 | ||
| 131 | keymaps []string | 131 | keymaps []string | ||
| 132 | locked []uint32 // locked modifiers of each modifiers event | 132 | locked []uint32 // locked modifiers of each modifiers event | ||
| 133 | + | depress []uint32 // depressed modifiers of each modifiers event | |||
| 133 | } | 134 | } | ||
| 134 | 135 | ||||
| 135 | func (p *eventsProxy) Dispatch(e *wlturbo.Event) { | 136 | func (p *eventsProxy) Dispatch(e *wlturbo.Event) { | ||
| 136 | p.opcodes = append(p.opcodes, e.Opcode) | 137 | p.opcodes = append(p.opcodes, e.Opcode) | ||
| 137 | if e.Opcode == uint16(wayland.KeyboardEventModifiers) { | 138 | if e.Opcode == uint16(wayland.KeyboardEventModifiers) { | ||
| 138 | e.Uint32() // serial | 139 | e.Uint32() // serial | ||
| 139 | - | e.Uint32() // depressed | 140 | + | p.depress = append(p.depress, e.Uint32()) |
| 140 | e.Uint32() // latched | 141 | e.Uint32() // latched | ||
| 141 | p.locked = append(p.locked, e.Uint32()) | 142 | p.locked = append(p.locked, e.Uint32()) | ||
| 142 | return | 143 | return | ||
| @@ -254,3 +255,89 @@ func TestFootKeyboard(t *testing.T) { | |||||
| 254 | } | 255 | } | ||
| 255 | } | 256 | } | ||
| 256 | } | 257 | } | ||
| 258 | + | ||||
| 259 | + | // SetModifiers sends the requested depressed mask with the real latched and | |||
| 260 | + | // locked state, and Restore sends the real state back; the seat's own state | |||
| 261 | + | // is untouched. A window that is not focused is ignored. | |||
| 262 | + | func TestSetModifiersOverrideAndRestore(t *testing.T) { | |||
| 263 | + | s, events, commands, dir := keyboardServer(t) | |||
| 264 | + | target, proxy := focusedTarget(t, s, events, commands, dir) | |||
| 265 | + | const capsLocked = 2 | |||
| 266 | + | var id ports.WindowID | |||
| 267 | + | set := make(chan struct{}) | |||
| 268 | + | s.display.Do(func() { s.seat.modState = ports.ModState{Locked: capsLocked}; id = s.seat.focused; close(set) }) | |||
| 269 | + | <-set | |||
| 270 | + | // A marker command applied on the display means every command sent before | |||
| 271 | + | // it was applied (they apply in order); the roundtrip then delivers the | |||
| 272 | + | // events they sent. | |||
| 273 | + | var pending bool | |||
| 274 | + | wait := func() { | |||
| 275 | + | t.Helper() | |||
| 276 | + | pending = !pending | |||
| 277 | + | commands <- ports.SlotsPending{Pending: pending} | |||
| 278 | + | deadline := time.Now().Add(2 * time.Second) | |||
| 279 | + | for { | |||
| 280 | + | var now bool | |||
| 281 | + | if !s.display.Do(func() { now = s.slotsPending }) { | |||
| 282 | + | t.Fatal("display stopped") | |||
| 283 | + | } | |||
| 284 | + | if now == pending { | |||
| 285 | + | break | |||
| 286 | + | } | |||
| 287 | + | if time.Now().After(deadline) { | |||
| 288 | + | t.Fatal("commands not applied") | |||
| 289 | + | } | |||
| 290 | + | time.Sleep(time.Millisecond) | |||
| 291 | + | } | |||
| 292 | + | roundtrip(t, target) | |||
| 293 | + | } | |||
| 294 | + | proxy.depress, proxy.locked = nil, nil | |||
| 295 | + | commands <- ports.SetModifiers{ID: id + 100, Mods: ports.ModCtrl} | |||
| 296 | + | wait() | |||
| 297 | + | if len(proxy.depress) != 0 { | |||
| 298 | + | t.Fatalf("unfocused window got modifiers %v", proxy.depress) | |||
| 299 | + | } | |||
| 300 | + | commands <- ports.SetModifiers{ID: id, Mods: ports.ModCtrl | ports.ModShift} | |||
| 301 | + | wait() | |||
| 302 | + | if !slices.Equal(proxy.depress, []uint32{xkbControl | xkbShift}) || !slices.Equal(proxy.locked, []uint32{capsLocked}) { | |||
| 303 | + | t.Fatalf("override depressed %v locked %v", proxy.depress, proxy.locked) | |||
| 304 | + | } | |||
| 305 | + | done := make(chan ports.ModState, 1) | |||
| 306 | + | commands <- ports.SetModifiers{ID: id, Restore: true, State: ports.ModState{Locked: capsLocked}} | |||
| 307 | + | wait() | |||
| 308 | + | if !slices.Equal(proxy.depress, []uint32{xkbControl | xkbShift, 0}) || !slices.Equal(proxy.locked, []uint32{capsLocked, capsLocked}) { | |||
| 309 | + | t.Fatalf("restore depressed %v locked %v", proxy.depress, proxy.locked) | |||
| 310 | + | } | |||
| 311 | + | s.display.Do(func() { done <- s.seat.modState }) | |||
| 312 | + | if m := <-done; m != (ports.ModState{Locked: capsLocked}) { | |||
| 313 | + | t.Fatalf("seat state changed: %+v", m) | |||
| 314 | + | } | |||
| 315 | + | } | |||
| 316 | + | ||||
| 317 | + | // Restore carries the real state core saw, which swallowed keys (a latch a | |||
| 318 | + | // hint label key consumed) never gave the seat: it is sent and adopted. | |||
| 319 | + | func TestSetModifiersRestoreAdoptsCoreState(t *testing.T) { | |||
| 320 | + | s, events, commands, dir := keyboardServer(t) | |||
| 321 | + | target, proxy := focusedTarget(t, s, events, commands, dir) | |||
| 322 | + | const shiftLatched, capsLocked = 1, 2 | |||
| 323 | + | var id ports.WindowID | |||
| 324 | + | set := make(chan struct{}) | |||
| 325 | + | s.display.Do(func() { s.seat.modState = ports.ModState{Latched: shiftLatched}; id = s.seat.focused; close(set) }) | |||
| 326 | + | <-set | |||
| 327 | + | proxy.depress, proxy.locked = nil, nil | |||
| 328 | + | want := ports.ModState{Locked: capsLocked} // the latch was consumed | |||
| 329 | + | commands <- ports.SetModifiers{ID: id, Restore: true, State: want} | |||
| 330 | + | var got ports.ModState | |||
| 331 | + | deadline := time.Now().Add(2 * time.Second) | |||
| 332 | + | for got != want { | |||
| 333 | + | if time.Now().After(deadline) { | |||
| 334 | + | t.Fatalf("seat state %+v, want %+v", got, want) | |||
| 335 | + | } | |||
| 336 | + | s.display.Do(func() { got = s.seat.modState }) | |||
| 337 | + | time.Sleep(time.Millisecond) | |||
| 338 | + | } | |||
| 339 | + | roundtrip(t, target) | |||
| 340 | + | if !slices.Equal(proxy.locked, []uint32{capsLocked}) || !slices.Equal(proxy.depress, []uint32{0}) { | |||
| 341 | + | t.Fatalf("restore depressed %v locked %v", proxy.depress, proxy.locked) | |||
| 342 | + | } | |||
| 343 | + | } | |||
M internal/adapters/wayland/seat.go +56 -0
| @@ -83,6 +83,8 @@ func (s *Server) applyInput(cmd ports.ClientCommand) { | |||
| 83 | 83 | target = c.ID | |
| 84 | 84 | case ports.ForwardKey: | |
| 85 | 85 | target = c.ID | |
| 86 | + | case ports.SetModifiers: | |
| 87 | + | target = c.ID | |
| 86 | 88 | } | |
| 87 | 89 | if target != 0 && !s.lockInputTarget(target) { | |
| 88 | 90 | return | |
| @@ -177,6 +179,8 @@ func (s *Server) applyInput(cmd ports.ClientCommand) { | |||
| 177 | 179 | if s.seat.focused != c.ID { | |
| 178 | 180 | s.changeFocus(c.ID) | |
| 179 | 181 | } | |
| 182 | + | case ports.SetModifiers: | |
| 183 | + | s.setModifiers(c) | |
| 180 | 184 | case ports.ForwardKey: | |
| 181 | 185 | if !(s.security != nil && s.security.Snapshot().Protected) && s.grabKey(c) { | |
| 182 | 186 | return | |
| @@ -219,6 +223,58 @@ func (s *Server) applyInput(cmd ports.ClientCommand) { | |||
| 219 | 223 | } | |
| 220 | 224 | } | |
| 221 | 225 | ||
| 226 | + | // xkb modifier masks of the default keymaps: Shift, Control, Mod1 (Alt) and | |
| 227 | + | // Mod4 (Super). The server does not parse the keymap for its modifier indices. | |
| 228 | + | const ( | |
| 229 | + | xkbShift = 1 << 0 | |
| 230 | + | xkbControl = 1 << 2 | |
| 231 | + | xkbMod1 = 1 << 3 | |
| 232 | + | xkbMod4 = 1 << 6 | |
| 233 | + | ) | |
| 234 | + | ||
| 235 | + | // depressedMask maps core modifier flags to xkb depressed masks. | |
| 236 | + | func depressedMask(m ports.Mods) uint32 { | |
| 237 | + | var d uint32 | |
| 238 | + | for _, f := range []struct { | |
| 239 | + | flag ports.Mods | |
| 240 | + | mask uint32 | |
| 241 | + | }{{ports.ModShift, xkbShift}, {ports.ModCtrl, xkbControl}, {ports.ModAlt, xkbMod1}, {ports.ModSuper, xkbMod4}} { | |
| 242 | + | if m&f.flag != 0 { | |
| 243 | + | d |= f.mask | |
| 244 | + | } | |
| 245 | + | } | |
| 246 | + | return d | |
| 247 | + | } | |
| 248 | + | ||
| 249 | + | // setModifiers overrides, then restores, the modifiers the focused window's | |
| 250 | + | // keyboards (or the input method grab) see. The override replaces the | |
| 251 | + | // depressed modifiers and keeps latched, locked and group; the seat's real | |
| 252 | + | // modState is only replaced by a Restore, with the state core carries. | |
| 253 | + | func (s *Server) setModifiers(c ports.SetModifiers) { | |
| 254 | + | _, keyboards := s.focusTarget(c.ID) | |
| 255 | + | if c.ID != s.seat.focused || len(keyboards) == 0 { | |
| 256 | + | s.log.Debug().Uint64("id", uint64(c.ID)).Msg("ignored set modifiers") | |
| 257 | + | return | |
| 258 | + | } | |
| 259 | + | s.useKeymap(nil) | |
| 260 | + | s.serial++ | |
| 261 | + | if c.Restore { | |
| 262 | + | // Swallowed keys never reached the seat: core's snapshot is the | |
| 263 | + | // real state. | |
| 264 | + | s.seat.modState = c.State | |
| 265 | + | } | |
| 266 | + | m := s.seat.modState | |
| 267 | + | if !c.Restore { | |
| 268 | + | m.Depressed = depressedMask(c.Mods) | |
| 269 | + | } | |
| 270 | + | for _, k := range keyboards { | |
| 271 | + | k.SendModifiers(s.serial, m.Depressed, m.Latched, m.Locked, m.Group) | |
| 272 | + | } | |
| 273 | + | if g := s.grab(); g != nil { | |
| 274 | + | g.res.SendModifiers(s.serial, m.Depressed, m.Latched, m.Locked, m.Group) | |
| 275 | + | } | |
| 276 | + | } | |
| 277 | + | ||
| 222 | 278 | func (s *Server) windowKeyboards(w *window) []*wayland.Keyboard { | |
| 223 | 279 | if w == nil || !w.mapped || !w.xdg.resource.Resource.Alive() { | |
| 224 | 280 | return nil | |
| @@ -83,6 +83,8 @@ func (s *Server) applyInput(cmd ports.ClientCommand) { | |||||
| 83 | target = c.ID | 83 | target = c.ID | ||
| 84 | case ports.ForwardKey: | 84 | case ports.ForwardKey: | ||
| 85 | target = c.ID | 85 | target = c.ID | ||
| 86 | + | case ports.SetModifiers: | |||
| 87 | + | target = c.ID | |||
| 86 | } | 88 | } | ||
| 87 | if target != 0 && !s.lockInputTarget(target) { | 89 | if target != 0 && !s.lockInputTarget(target) { | ||
| 88 | return | 90 | return | ||
| @@ -177,6 +179,8 @@ func (s *Server) applyInput(cmd ports.ClientCommand) { | |||||
| 177 | if s.seat.focused != c.ID { | 179 | if s.seat.focused != c.ID { | ||
| 178 | s.changeFocus(c.ID) | 180 | s.changeFocus(c.ID) | ||
| 179 | } | 181 | } | ||
| 182 | + | case ports.SetModifiers: | |||
| 183 | + | s.setModifiers(c) | |||
| 180 | case ports.ForwardKey: | 184 | case ports.ForwardKey: | ||
| 181 | if !(s.security != nil && s.security.Snapshot().Protected) && s.grabKey(c) { | 185 | if !(s.security != nil && s.security.Snapshot().Protected) && s.grabKey(c) { | ||
| 182 | return | 186 | return | ||
| @@ -219,6 +223,58 @@ func (s *Server) applyInput(cmd ports.ClientCommand) { | |||||
| 219 | } | 223 | } | ||
| 220 | } | 224 | } | ||
| 221 | 225 | ||||
| 226 | + | // xkb modifier masks of the default keymaps: Shift, Control, Mod1 (Alt) and | |||
| 227 | + | // Mod4 (Super). The server does not parse the keymap for its modifier indices. | |||
| 228 | + | const ( | |||
| 229 | + | xkbShift = 1 << 0 | |||
| 230 | + | xkbControl = 1 << 2 | |||
| 231 | + | xkbMod1 = 1 << 3 | |||
| 232 | + | xkbMod4 = 1 << 6 | |||
| 233 | + | ) | |||
| 234 | + | ||||
| 235 | + | // depressedMask maps core modifier flags to xkb depressed masks. | |||
| 236 | + | func depressedMask(m ports.Mods) uint32 { | |||
| 237 | + | var d uint32 | |||
| 238 | + | for _, f := range []struct { | |||
| 239 | + | flag ports.Mods | |||
| 240 | + | mask uint32 | |||
| 241 | + | }{{ports.ModShift, xkbShift}, {ports.ModCtrl, xkbControl}, {ports.ModAlt, xkbMod1}, {ports.ModSuper, xkbMod4}} { | |||
| 242 | + | if m&f.flag != 0 { | |||
| 243 | + | d |= f.mask | |||
| 244 | + | } | |||
| 245 | + | } | |||
| 246 | + | return d | |||
| 247 | + | } | |||
| 248 | + | ||||
| 249 | + | // setModifiers overrides, then restores, the modifiers the focused window's | |||
| 250 | + | // keyboards (or the input method grab) see. The override replaces the | |||
| 251 | + | // depressed modifiers and keeps latched, locked and group; the seat's real | |||
| 252 | + | // modState is only replaced by a Restore, with the state core carries. | |||
| 253 | + | func (s *Server) setModifiers(c ports.SetModifiers) { | |||
| 254 | + | _, keyboards := s.focusTarget(c.ID) | |||
| 255 | + | if c.ID != s.seat.focused || len(keyboards) == 0 { | |||
| 256 | + | s.log.Debug().Uint64("id", uint64(c.ID)).Msg("ignored set modifiers") | |||
| 257 | + | return | |||
| 258 | + | } | |||
| 259 | + | s.useKeymap(nil) | |||
| 260 | + | s.serial++ | |||
| 261 | + | if c.Restore { | |||
| 262 | + | // Swallowed keys never reached the seat: core's snapshot is the | |||
| 263 | + | // real state. | |||
| 264 | + | s.seat.modState = c.State | |||
| 265 | + | } | |||
| 266 | + | m := s.seat.modState | |||
| 267 | + | if !c.Restore { | |||
| 268 | + | m.Depressed = depressedMask(c.Mods) | |||
| 269 | + | } | |||
| 270 | + | for _, k := range keyboards { | |||
| 271 | + | k.SendModifiers(s.serial, m.Depressed, m.Latched, m.Locked, m.Group) | |||
| 272 | + | } | |||
| 273 | + | if g := s.grab(); g != nil { | |||
| 274 | + | g.res.SendModifiers(s.serial, m.Depressed, m.Latched, m.Locked, m.Group) | |||
| 275 | + | } | |||
| 276 | + | } | |||
| 277 | + | ||||
| 222 | func (s *Server) windowKeyboards(w *window) []*wayland.Keyboard { | 278 | func (s *Server) windowKeyboards(w *window) []*wayland.Keyboard { | ||
| 223 | if w == nil || !w.mapped || !w.xdg.resource.Resource.Alive() { | 279 | if w == nil || !w.mapped || !w.xdg.resource.Resource.Alive() { | ||
| 224 | return nil | 280 | return nil | ||
M internal/adapters/wayland/server.go +2 -2
| @@ -752,13 +752,13 @@ func (s *Server) apply(cmd ports.ClientCommand) { | |||
| 752 | 752 | // Only input/focus commands require an owner envelope. Config relay | |
| 753 | 753 | // also produces SetKeymap and other epoch-independent commands. | |
| 754 | 754 | switch cmd.(type) { | |
| 755 | - | case ports.PointerFocus, ports.PointerMotionTo, ports.PointerButtonTo, ports.PointerAxisTo, ports.GestureBeginTo, ports.GestureUpdateTo, ports.GestureEndTo, ports.FocusWindow, ports.ForwardKey, | |
| 755 | + | case ports.PointerFocus, ports.PointerMotionTo, ports.PointerButtonTo, ports.PointerAxisTo, ports.GestureBeginTo, ports.GestureUpdateTo, ports.GestureEndTo, ports.FocusWindow, ports.ForwardKey, ports.SetModifiers, | |
| 756 | 756 | ports.DataDragFocus, ports.DataDragMotion, ports.DataDragDrop, ports.DataDragCancel: | |
| 757 | 757 | return | |
| 758 | 758 | } | |
| 759 | 759 | } | |
| 760 | 760 | switch c := cmd.(type) { | |
| 761 | - | case ports.PointerFocus, ports.PointerMotionTo, ports.PointerButtonTo, ports.PointerAxisTo, ports.GestureBeginTo, ports.GestureUpdateTo, ports.GestureEndTo, ports.SetKeymap, ports.FocusWindow, ports.ForwardKey: | |
| 761 | + | case ports.PointerFocus, ports.PointerMotionTo, ports.PointerButtonTo, ports.PointerAxisTo, ports.GestureBeginTo, ports.GestureUpdateTo, ports.GestureEndTo, ports.SetKeymap, ports.FocusWindow, ports.ForwardKey, ports.SetModifiers: | |
| 762 | 762 | s.applyInput(c) | |
| 763 | 763 | case ports.DataDragFocus, ports.DataDragMotion, ports.DataDragDrop, ports.DataDragCancel: | |
| 764 | 764 | s.applyDrag(c) | |
| @@ -752,13 +752,13 @@ func (s *Server) apply(cmd ports.ClientCommand) { | |||||
| 752 | // Only input/focus commands require an owner envelope. Config relay | 752 | // Only input/focus commands require an owner envelope. Config relay | ||
| 753 | // also produces SetKeymap and other epoch-independent commands. | 753 | // also produces SetKeymap and other epoch-independent commands. | ||
| 754 | switch cmd.(type) { | 754 | switch cmd.(type) { | ||
| 755 | - | case ports.PointerFocus, ports.PointerMotionTo, ports.PointerButtonTo, ports.PointerAxisTo, ports.GestureBeginTo, ports.GestureUpdateTo, ports.GestureEndTo, ports.FocusWindow, ports.ForwardKey, | 755 | + | case ports.PointerFocus, ports.PointerMotionTo, ports.PointerButtonTo, ports.PointerAxisTo, ports.GestureBeginTo, ports.GestureUpdateTo, ports.GestureEndTo, ports.FocusWindow, ports.ForwardKey, ports.SetModifiers, |
| 756 | ports.DataDragFocus, ports.DataDragMotion, ports.DataDragDrop, ports.DataDragCancel: | 756 | ports.DataDragFocus, ports.DataDragMotion, ports.DataDragDrop, ports.DataDragCancel: | ||
| 757 | return | 757 | return | ||
| 758 | } | 758 | } | ||
| 759 | } | 759 | } | ||
| 760 | switch c := cmd.(type) { | 760 | switch c := cmd.(type) { | ||
| 761 | - | case ports.PointerFocus, ports.PointerMotionTo, ports.PointerButtonTo, ports.PointerAxisTo, ports.GestureBeginTo, ports.GestureUpdateTo, ports.GestureEndTo, ports.SetKeymap, ports.FocusWindow, ports.ForwardKey: | 761 | + | case ports.PointerFocus, ports.PointerMotionTo, ports.PointerButtonTo, ports.PointerAxisTo, ports.GestureBeginTo, ports.GestureUpdateTo, ports.GestureEndTo, ports.SetKeymap, ports.FocusWindow, ports.ForwardKey, ports.SetModifiers: |
| 762 | s.applyInput(c) | 762 | s.applyInput(c) | ||
| 763 | case ports.DataDragFocus, ports.DataDragMotion, ports.DataDragDrop, ports.DataDragCancel: | 763 | case ports.DataDragFocus, ports.DataDragMotion, ports.DataDragDrop, ports.DataDragCancel: | ||
| 764 | s.applyDrag(c) | 764 | s.applyDrag(c) | ||