windows.go RawHistoryFolder
1package core
2
3import (
4 "time"
5
6 "github.com/bnema/neferwl/internal/ports"
7)
8
9// windowRecord is what core knows about one client surface besides its
10// place in the layout. Fields fill in as wayland reports them, in any order.
11type windowRecord struct {
12 // client holds the app ID and PID of a mapped window, for State.
13 client ports.WindowMapped
14 mappedAt time.Time
15 // sized is set once the window's first committed size was seen.
16 sized bool
17 // region is the input region; nil accepts input everywhere.
18 region *ports.InputRegionChanged
19 // inhibitShortcuts asks to keep compositor binds while focused;
20 // idleInhibit keeps the session awake.
21 inhibitShortcuts bool
22 idleInhibit bool
23}
24
25// windowRegistry owns the per-surface records. Dropping a surface removes
26// everything core keeps about it in one call.
27type windowRegistry struct {
28 records map[WindowID]*windowRecord
29}
30
31func newWindowRegistry() windowRegistry {
32 return windowRegistry{records: map[WindowID]*windowRecord{}}
33}
34
35// at returns the record of id, creating it.
36func (r *windowRegistry) at(id WindowID) *windowRecord {
37 rec := r.records[id]
38 if rec == nil {
39 rec = &windowRecord{}
40 r.records[id] = rec
41 }
42 return rec
43}
44
45// lookup returns the record of id, or the zero record when there is none.
46func (r *windowRegistry) lookup(id WindowID) windowRecord {
47 if rec := r.records[id]; rec != nil {
48 return *rec
49 }
50 return windowRecord{}
51}
52
53func (r *windowRegistry) mapped(v ports.WindowMapped, now time.Time) {
54 rec := r.at(v.ID)
55 rec.client, rec.mappedAt, rec.sized = v, now, false
56}
57
58// firstSize reports whether this is the first committed size of the window
59// since it mapped, and records that it was seen.
60func (r *windowRegistry) firstSize(id WindowID) bool {
61 rec := r.records[id]
62 if rec == nil || rec.sized || rec.client.ID == 0 {
63 return false
64 }
65 rec.sized = true
66 return true
67}
68
69// setAppID updates the app ID of a mapped window only.
70func (r *windowRegistry) setAppID(id WindowID, appID string) {
71 if rec := r.records[id]; rec != nil && rec.client.ID != 0 {
72 rec.client.AppID = appID
73 }
74}
75
76func (r *windowRegistry) setRegion(v ports.InputRegionChanged) { r.at(v.ID).region = &v }
77
78func (r *windowRegistry) setInhibitShortcuts(id WindowID, on bool) {
79 if on {
80 r.at(id).inhibitShortcuts = true
81 } else if rec := r.records[id]; rec != nil {
82 rec.inhibitShortcuts = false
83 }
84}
85
86func (r *windowRegistry) setIdleInhibit(id WindowID, on bool) {
87 if on {
88 r.at(id).idleInhibit = true
89 } else if rec := r.records[id]; rec != nil {
90 rec.idleInhibit = false
91 }
92}
93
94// drop forgets the surface.
95func (r *windowRegistry) drop(id WindowID) { delete(r.records, id) }
96
97// layerGone forgets the input region of a layer surface that is no longer
98// mapped; layers get no WindowUnmapped. The record goes once it is empty.
99func (r *windowRegistry) layerGone(id WindowID) {
100 rec := r.records[id]
101 if rec == nil {
102 return
103 }
104 rec.region = nil
105 if *rec == (windowRecord{}) {
106 delete(r.records, id)
107 }
108}
109
110// acceptsInput reports whether the surface-local point is in id's input
111// region.
112func (r *windowRegistry) acceptsInput(id WindowID, x, y float64) bool {
113 rec := r.records[id]
114 if rec == nil || rec.region == nil || rec.region.All {
115 return true
116 }
117 for _, box := range rec.region.Rects {
118 if x >= float64(box.X) && x < float64(box.X+box.W) && y >= float64(box.Y) && y < float64(box.Y+box.H) {
119 return true
120 }
121 }
122 return false
123}