v0.3.0 cf370a0

outputmanagement.go RawHistoryFolder

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) {}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 25

Tags 10

↑↓ moveenter openesc close