v0.2.1 7e79979

workspaces.go RawHistoryFolder

1package testserver
2
3import (
4	"encoding/binary"
5
6	"github.com/bnema/purego-libwayland/protocol/extworkspace"
7	"github.com/bnema/purego-libwayland/protocol/wayland"
8	"github.com/bnema/purego-libwayland/server"
9
10	"github.com/bnema/nefercap/internal/adapters/wayland/testserver/neferwl"
11)
12
13type workspace struct {
14	spec    WorkspaceSpec
15	removed bool
16}
17
18type wsHandle struct {
19	w   *workspace
20	res *extworkspace.ExtWorkspaceHandleV1
21}
22
23type wsManager struct {
24	s       *Server
25	res     *extworkspace.ExtWorkspaceManagerV1
26	group   *extworkspace.ExtWorkspaceGroupHandleV1
27	handles []*wsHandle
28}
29
30func (m *wsManager) Commit(*extworkspace.ExtWorkspaceManagerV1) {}
31func (m *wsManager) Stop(*extworkspace.ExtWorkspaceManagerV1)   {}
32
33type groupHandler struct{}
34
35func (groupHandler) CreateWorkspace(*extworkspace.ExtWorkspaceGroupHandleV1, string) {}
36func (groupHandler) Destroy(*extworkspace.ExtWorkspaceGroupHandleV1)                 {}
37
38type handleHandler struct{}
39
40func (handleHandler) Destroy(*extworkspace.ExtWorkspaceHandleV1)    {}
41func (handleHandler) Activate(*extworkspace.ExtWorkspaceHandleV1)   {}
42func (handleHandler) Deactivate(*extworkspace.ExtWorkspaceHandleV1) {}
43func (handleHandler) Assign(*extworkspace.ExtWorkspaceHandleV1, *extworkspace.ExtWorkspaceGroupHandleV1) {
44}
45func (handleHandler) Remove(*extworkspace.ExtWorkspaceHandleV1) {}
46
47func stateBits(w WorkspaceSpec) uint32 {
48	var st uint32
49	if w.Active {
50		st |= uint32(extworkspace.ExtWorkspaceHandleV1StateActive)
51	}
52	if w.Hidden {
53		st |= uint32(extworkspace.ExtWorkspaceHandleV1StateHidden)
54	}
55	return st
56}
57
58// announce sends the group, its outputs and every workspace, then done.
59func (m *wsManager) announce(c server.Client) {
60	g, err := extworkspace.NewExtWorkspaceGroupHandleV1(c, 1, 0, groupHandler{})
61	if err != nil {
62		return
63	}
64	m.group = g
65	m.res.SendWorkspaceGroup(g)
66	g.SendCapabilities(0)
67	for _, o := range m.s.outputs {
68		for _, r := range o.res {
69			if r.Client() == c {
70				g.SendOutputEnter(r)
71			}
72		}
73	}
74	for i, w := range m.s.workspaces {
75		if !w.removed {
76			m.add(c, g, w, i)
77		}
78	}
79	m.res.SendDone()
80}
81
82func (m *wsManager) add(c server.Client, g *extworkspace.ExtWorkspaceGroupHandleV1, w *workspace, index int) {
83	r, err := extworkspace.NewExtWorkspaceHandleV1(c, 1, 0, handleHandler{})
84	if err != nil {
85		return
86	}
87	m.handles = append(m.handles, &wsHandle{w: w, res: r})
88	m.res.SendWorkspace(r)
89	if w.spec.ID != "" {
90		r.SendId(w.spec.ID)
91	}
92	r.SendName(w.spec.Name)
93	var b [4]byte
94	binary.NativeEndian.PutUint32(b[:], uint32(index))
95	r.SendCoordinates(b[:])
96	r.SendState(stateBits(w.spec))
97	r.SendCapabilities(0)
98	g.SendWorkspaceEnter(r)
99}
100
101func (s *Server) workspaceNamed(name string) *workspace {
102	for _, w := range s.workspaces {
103		if w.spec.Name == name {
104			return w
105		}
106	}
107	return nil
108}
109
110// SetActive changes the active state of the named workspace.
111func (s *Server) SetActive(name string, active bool) {
112	s.Do(func() {
113		w := s.workspaceNamed(name)
114		if w == nil {
115			return
116		}
117		w.spec.Active = active
118		for _, m := range s.wsMgrs {
119			for _, h := range m.handles {
120				if h.w == w && !w.removed {
121					h.res.SendState(stateBits(w.spec))
122				}
123			}
124			m.res.SendDone()
125		}
126	})
127}
128
129// RemoveWorkspace removes the named workspace from every manager.
130func (s *Server) RemoveWorkspace(name string) {
131	s.Do(func() {
132		w := s.workspaceNamed(name)
133		if w == nil || w.removed {
134			return
135		}
136		w.removed = true
137		for _, m := range s.wsMgrs {
138			for _, h := range m.handles {
139				if h.w == w {
140					m.group.SendWorkspaceLeave(h.res)
141					h.res.SendRemoved()
142				}
143			}
144			m.res.SendDone()
145		}
146		for _, c := range s.sessions {
147			if c.src.ws == w {
148				c.stop()
149			}
150		}
151	})
152}
153
154// workspaceOf finds the workspace behind a handle resource.
155func (s *Server) workspaceOf(r *server.Resource) *workspace {
156	for _, m := range s.wsMgrs {
157		for _, h := range m.handles {
158			if h.res.Resource == r {
159				return h.w
160			}
161		}
162	}
163	return nil
164}
165
166type neferwlSources struct{ s *Server }
167
168// frameObject is one neferwl_workspace_frame_v1.
169type frameObject struct {
170	w   *workspace
171	res *neferwl.NeferwlWorkspaceFrameV1
172}
173
174func (frameObject) Destroy(*neferwl.NeferwlWorkspaceFrameV1) {}
175
176func (f *frameObject) send() {
177	if r := f.w.spec.Frame; r != ([4]int32{}) {
178		f.res.SendFrame(r[0], r[1], r[2], r[3])
179	}
180}
181
182func (m neferwlSources) GetWorkspaceFrame(self *neferwl.NeferwlImageCaptureSourceManagerV1, id uint32, h *extworkspace.ExtWorkspaceHandleV1) {
183	s := m.s
184	f := &frameObject{w: s.workspaceOf(h.Resource)}
185	res, err := neferwl.NewNeferwlWorkspaceFrameV1(self.Client(), 1, id, f)
186	if err != nil {
187		return
188	}
189	f.res = res
190	s.stats.WorkspaceFrames++
191	res.OnDestroy = func() {
192		s.stats.DestroyedWorkspaceFrames++
193		s.frames = removeFrame(s.frames, f)
194	}
195	if f.w == nil || f.w.removed {
196		return
197	}
198	s.frames = append(s.frames, f)
199	f.send()
200}
201
202func removeFrame(list []*frameObject, f *frameObject) []*frameObject {
203	for i, g := range list {
204		if g == f {
205			return append(list[:i], list[i+1:]...)
206		}
207	}
208	return list
209}
210
211// SetFrame changes the frame of the named workspace and reports it to every
212// frame object that watches it.
213func (s *Server) SetFrame(name string, frame [4]int32) {
214	s.Do(func() {
215		w := s.workspaceNamed(name)
216		if w == nil {
217			return
218		}
219		w.spec.Frame = frame
220		for _, f := range s.frames {
221			if f.w == w && !w.removed {
222				f.send()
223			}
224		}
225	})
226}
227
228func (neferwlSources) Destroy(*neferwl.NeferwlImageCaptureSourceManagerV1) {}
229func (m neferwlSources) CreateWorkspaceSource(self *neferwl.NeferwlImageCaptureSourceManagerV1, id uint32, h *extworkspace.ExtWorkspaceHandleV1) {
230	w := m.s.workspaceOf(h.Resource)
231	m.s.newSource(self.Client(), id, &source{kind: "workspace", ws: w, gone: w == nil || w.removed})
232}
233func (m neferwlSources) CreateOutputRegionSource(self *neferwl.NeferwlImageCaptureSourceManagerV1, id uint32, out *wayland.Output, x, y, w, h int32) {
234	if w < 1 || h < 1 {
235		self.PostError(uint32(neferwl.NeferwlImageCaptureSourceManagerV1ErrorInvalidRegion), "empty region")
236		return
237	}
238	m.s.newSource(self.Client(), id, &source{kind: "region", out: m.s.outputOf(out.Resource), region: [4]int32{x, y, w, h}})
239}

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 2

Tags 9

↑↓ moveenter openesc close