1package wayland
2
3import (
4 "reflect"
5 "strings"
6
7 wlr "github.com/bnema/go-wayland-bindings/server/wlroutputmanagement"
8 "github.com/bnema/neferwl/internal/ports"
9 "github.com/bnema/purego-libwayland/server"
10)
11
12type managementHead struct {
13 res *wlr.ZwlrOutputHeadV1
14 modes map[*server.Resource]ports.OutputMode
15 modeResources []*wlr.ZwlrOutputModeV1
16 info ports.OutputHead
17}
18type outputManager struct {
19 s *Server
20 res *wlr.ZwlrOutputManagerV1
21 heads map[string]*managementHead
22 stopped bool
23 places map[string]ports.OutputPlacement
24}
25
26func registerOutputManagement(d *server.Display, s *Server) error {
27 return wlr.NewZwlrOutputManagerV1Global(d, 4, func(c server.Client, v, id uint32) {
28 m := &outputManager{s: s, heads: map[string]*managementHead{}, places: map[string]ports.OutputPlacement{}}
29 res, err := wlr.NewZwlrOutputManagerV1(c, int32(v), id, m)
30 if err != nil {
31 return
32 }
33 m.res = res
34 s.outputManagers = append(s.outputManagers, m)
35 res.OnDestroy = func() { s.outputManagers = removeItem(s.outputManagers, m) }
36 for _, h := range s.outputHeads.Heads {
37 m.addHead(h)
38 }
39 m.update()
40 res.SendDone(s.managementSerial)
41 })
42}
43func (m *outputManager) Stop(_ *wlr.ZwlrOutputManagerV1) {
44 m.stopped = true
45 m.res.SendFinished()
46 m.res.Destroy()
47}
48func (m *outputManager) CreateConfiguration(r *wlr.ZwlrOutputManagerV1, id, serial uint32) {
49 c := &outputConfiguration{manager: m, heads: map[string]*headConfiguration{}, serial: serial}
50 res, err := wlr.NewZwlrOutputConfigurationV1(r.Client(), r.Version(), id, c)
51 if err != nil {
52 return
53 }
54 c.res = res
55 res.OnDestroy = func() {
56 for _, h := range c.heads {
57 if h.res != nil {
58 h.res.Destroy()
59 }
60 }
61 for id, pending := range m.s.outputReplies {
62 if pending == c {
63 delete(m.s.outputReplies, id)
64 }
65 }
66 }
67 if serial != m.s.managementSerial {
68 c.cancelled = true
69 c.used = true
70 res.SendCancelled()
71 }
72}
73func (m *outputManager) addHead(info ports.OutputHead) {
74 h := &managementHead{info: info, modes: map[*server.Resource]ports.OutputMode{}}
75 res, err := wlr.NewZwlrOutputHeadV1(m.res.Client(), m.res.Version(), 0, managementHeadHandler{})
76 if err != nil {
77 return
78 }
79 h.res = res
80 m.heads[info.Info.Name] = h
81 m.res.SendHead(res)
82 res.SendName(info.Info.Name)
83 desc := strings.Join(strings.Fields(info.Info.Make+" "+info.Info.Model+" "+info.Info.Serial+" ("+info.Info.Name+")"), " ")
84 res.SendDescription(desc)
85 if info.Info.PhysicalW > 0 && info.Info.PhysicalH > 0 {
86 res.SendPhysicalSize(int32(info.Info.PhysicalW), int32(info.Info.PhysicalH))
87 }
88 if res.Version() >= 2 {
89 res.SendMake(info.Info.Make)
90 res.SendModel(info.Info.Model)
91 res.SendSerialNumber(info.Info.Serial)
92 }
93 for _, mode := range info.Modes {
94 mr, e := wlr.NewZwlrOutputModeV1(res.Client(), min(res.Version(), 3), 0, managementModeHandler{})
95 if e != nil {
96 continue
97 }
98 h.modes[mr.Resource] = mode
99 h.modeResources = append(h.modeResources, mr)
100 mr.OnDestroy = func() {
101 delete(h.modes, mr.Resource)
102 h.modeResources = removeItem(h.modeResources, mr)
103 }
104 res.SendMode(mr)
105 mr.SendSize(int32(mode.Width), int32(mode.Height))
106 mr.SendRefresh(int32(mode.RefreshMilli))
107 if mode.Preferred {
108 mr.SendPreferred()
109 }
110 }
111 m.sendState(h, info)
112}
113func (m *outputManager) update() {
114 if m.stopped {
115 return
116 }
117 wanted := map[string]ports.OutputHead{}
118 for _, h := range m.s.outputHeads.Heads {
119 wanted[h.Info.Name] = h
120 }
121 for name, h := range m.heads {
122 if _, ok := wanted[name]; !ok {
123 h.res.SendFinished()
124 for _, mode := range h.modeResources {
125 mode.SendFinished()
126 }
127 delete(m.heads, name)
128 delete(m.places, name)
129 }
130 }
131 for _, info := range m.s.outputHeads.Heads {
132 h := m.heads[info.Info.Name]
133 if h != nil && (!reflect.DeepEqual(h.info.Modes, info.Modes) || h.info.Info.Make != info.Info.Make || h.info.Info.Model != info.Info.Model || h.info.Info.Serial != info.Info.Serial || h.info.Info.PhysicalW != info.Info.PhysicalW || h.info.Info.PhysicalH != info.Info.PhysicalH) {
134 h.res.SendFinished()
135 for _, mode := range h.modeResources {
136 mode.SendFinished()
137 }
138 delete(m.heads, info.Info.Name)
139 delete(m.places, info.Info.Name)
140 h = nil
141 }
142 if h == nil {
143 m.addHead(info)
144 continue // addHead has already sent the initial state.
145 }
146 changed := !reflect.DeepEqual(h.info, info)
147 placementChanged := m.places[info.Info.Name] != m.s.placementFor(info.Info.Name)
148 if !changed && !placementChanged {
149 continue
150 }
151 m.sendState(h, info)
152 }
153}
154func (m *outputManager) sendState(h *managementHead, info ports.OutputHead) {
155 h.info = info
156 enabled := int32(0)
157 if info.Enabled {
158 enabled = 1
159 }
160 h.res.SendEnabled(enabled)
161 if info.Enabled {
162 for res, mode := range h.modes {
163 if info.Current != nil && mode.Width == info.Current.Width && mode.Height == info.Current.Height && mode.RefreshMilli == info.Current.RefreshMilli {
164 h.res.SendCurrentMode(wlr.WrapZwlrOutputModeV1(res))
165 break
166 }
167 }
168 place := m.s.placementFor(info.Info.Name)
169 if place.Info.Name != "" {
170 h.res.SendPosition(int32(place.X), int32(place.Y))
171 h.res.SendScale(server.FixedFromFloat(place.Scale))
172 h.res.SendTransform(int32(place.Transform))
173 } else {
174 h.res.SendTransform(0)
175 }
176 }
177 m.places[info.Info.Name] = m.s.placementFor(info.Info.Name)
178}
179func (s *Server) placementFor(name string) ports.OutputPlacement {
180 for _, p := range s.outputPlaces {
181 if p.Info.Name == name {
182 return p
183 }
184 }
185 return ports.OutputPlacement{}
186}
187func (s *Server) setOutputHeads(h ports.OutputHeads) {
188 if reflect.DeepEqual(s.outputHeads, h) {
189 return
190 }
191 s.outputHeads = h
192 s.refreshOutputManagers()
193}
194func (s *Server) refreshOutputManagers() {
195 s.managementSerial++
196 if s.managementSerial == 0 {
197 s.managementSerial++
198 }
199 for _, m := range s.outputManagers {
200 if m.stopped {
201 continue
202 }
203 m.update()
204 m.res.SendDone(s.managementSerial)
205 }
206}
207
208type managementHeadHandler struct{}
209
210func (managementHeadHandler) Release(*wlr.ZwlrOutputHeadV1) {}
211
212type managementModeHandler struct{}
213
214func (managementModeHandler) Release(*wlr.ZwlrOutputModeV1) {}