keyboard.go RawHistoryFolder
1package core
2
3import "maps"
4
5// keyboardCandidates is what the keyboard focus picks from, strongest first.
6// The layer checks are lazy: they only run while a clicked layer holds the
7// keyboard.
8type keyboardCandidates struct {
9 // exclusive is the mapped top/overlay layer with exclusive keyboard
10 // interactivity and the highest ID; grab the topmost grabbing popup.
11 exclusive, grab WindowID
12 // window is the focused window of the focused output.
13 window WindowID
14 // layerShown reports whether a layer is still mapped on-demand and
15 // visible; windows is the focused window of each output.
16 layerShown func(WindowID) bool
17 windows func() map[*screen]WindowID
18}
19
20// keyboard owns who holds the keyboard: the rules picking the focus, the
21// on-demand layer the user clicked, the focus last told to wayland and the
22// shortcuts inhibitor active now.
23type keyboard struct {
24 // layer is the on-demand layer surface the user clicked; it keeps the
25 // keyboard while it stays mapped on-demand and no window focus changes
26 // on any output (over, taken at the click): any click elsewhere, bind,
27 // activation or new window takes the keyboard back. The pointer moving
28 // to another output does not.
29 layer WindowID
30 over map[*screen]WindowID
31 // sent is the focus last sent to wayland; inhibiting the shortcuts
32 // inhibitor active now (0: none).
33 sent WindowID
34 inhibiting WindowID
35}
36
37// focus is the exclusive layer, else a grabbing popup, else the clicked
38// on-demand layer, else the focused window. A clicked layer that is hidden
39// (by a fullscreen window) or outlived its window focus loses the keyboard
40// for good.
41func (k *keyboard) focus(c keyboardCandidates) WindowID {
42 if c.exclusive != 0 {
43 return c.exclusive
44 }
45 // A menu with a grab takes the keyboard until it closes.
46 if c.grab != 0 {
47 return c.grab
48 }
49 if k.layer != 0 && (!c.layerShown(k.layer) || !maps.Equal(k.over, c.windows())) {
50 k.layer, k.over = 0, nil
51 }
52 if k.layer != 0 {
53 return k.layer
54 }
55 return c.window
56}
57
58// clickLayer gives the keyboard to the clicked on-demand layer while the
59// window focus stays windows. It reports whether the holder changed.
60func (k *keyboard) clickLayer(id WindowID, windows map[*screen]WindowID) bool {
61 if k.layer == id {
62 return false
63 }
64 k.layer, k.over = id, windows
65 return true
66}
67
68// takeBack returns the keyboard from a clicked layer to the windows.
69func (k *keyboard) takeBack() { k.layer, k.over = 0, nil }
70
71// inhibit makes the focus the active shortcuts inhibitor when it asks to
72// be (wants). It returns the inhibitor to release and the one to activate
73// (0: none) when the active one changes.
74func (k *keyboard) inhibit(focus WindowID, wants bool) (release, activate WindowID, changed bool) {
75 want := WindowID(0)
76 if focus != 0 && wants {
77 want = focus
78 }
79 if want == k.inhibiting {
80 return 0, 0, false
81 }
82 release, k.inhibiting = k.inhibiting, want
83 return release, want, true
84}
85
86// inhibited reports whether the focus gets every key: it is the active
87// shortcuts inhibitor.
88func (k *keyboard) inhibited(focus WindowID) bool {
89 return k.inhibiting != 0 && k.inhibiting == focus
90}