v0.1.0 d9218d6

scale.go RawHistoryFolder

1package wayland
2
3import (
4	"math"
5
6	"github.com/bnema/purego-libwayland/protocol/fractionalscale"
7	"github.com/bnema/purego-libwayland/protocol/viewporter"
8	"github.com/bnema/purego-libwayland/protocol/wayland"
9	"github.com/bnema/purego-libwayland/server"
10)
11
12// Surfaces follow the output of their window or layer (the focused output
13// until they are shown): wl_surface.enter/leave, preferred buffer scale and
14// fractional scale all come from it.
15
16// outputOfSurface is the output a surface is on: its window's configured
17// output, its layer output, or the focused one.
18func (s *Server) outputOfSurface(surf *surface) *output {
19	// Subsurfaces are on their root's output.
20	surf = surf.root()
21	// Popups are on their parent's output.
22	for surf.xdg != nil && surf.xdg.window != nil && surf.xdg.window.popup != nil && surf.xdg.window.popup.parent != nil {
23		surf = surf.xdg.window.popup.parent.xdg.surface.root()
24	}
25	name := ""
26	switch {
27	case surf.xdg != nil && surf.xdg.window != nil:
28		name = surf.xdg.window.last.Output
29	case surf.layer != nil && surf.layer.output != nil:
30		return surf.layer.output
31	}
32	return s.outputByName(name)
33}
34
35// sendTreeScale sends the scale to a surface and its subsurfaces.
36func (surf *surface) sendTreeScale() {
37	surf.sendScale()
38	for _, ch := range surf.sub.children {
39		ch.sendTreeScale()
40	}
41}
42
43// sendScale tells the surface about its output: enter/leave, the integer
44// buffer scale (wl_surface v6+) and the fractional scale.
45func (surf *surface) sendScale() {
46	s := surf.server
47	if surf.destroyed || surf.wl == nil || !surf.wl.Resource.Alive() {
48		return
49	}
50	o := s.outputOfSurface(surf)
51	if o != surf.on {
52		if surf.on != nil {
53			surf.leave(surf.on)
54		}
55		surf.on = o
56		if o != nil {
57			for _, r := range o.resources {
58				if r.Client() == surf.wl.Client() {
59					surf.wl.SendEnter(r)
60				}
61			}
62		}
63	}
64	scale := 1.0
65	if o != nil {
66		scale = o.place.Scale
67	}
68	if scale == surf.scale {
69		return
70	}
71	surf.scale = scale
72	if surf.wl.Version() >= 6 {
73		surf.wl.SendPreferredBufferScale(integerScale(scale))
74	}
75	if f := s.fractions[surf]; f != nil {
76		f.SendPreferredScale(uint32(math.Round(scale * 120)))
77	}
78}
79
80// leave sends wl_surface.leave for o when the surface is on it.
81func (surf *surface) leave(o *output) {
82	if surf.on != o || o == nil {
83		return
84	}
85	surf.on = nil
86	if surf.destroyed || !surf.wl.Resource.Alive() {
87		return
88	}
89	for _, r := range o.resources {
90		if r.Client() == surf.wl.Client() && r.Resource.Alive() {
91			surf.wl.SendLeave(r)
92		}
93	}
94}
95
96func registerScale(d *server.Display, s *Server) error {
97	if err := fractionalscale.NewWpFractionalScaleManagerV1Global(d, 1, func(c server.Client, v, id uint32) {
98		_, _ = fractionalscale.NewWpFractionalScaleManagerV1(c, int32(v), id, fractionManager{s})
99	}); err != nil {
100		return err
101	}
102	return viewporter.NewWpViewporterGlobal(d, 1, func(c server.Client, v, id uint32) {
103		_, _ = viewporter.NewWpViewporter(c, int32(v), id, viewporterHandler{s})
104	})
105}
106
107type fractionManager struct{ server *Server }
108
109func (fractionManager) Destroy(*fractionalscale.WpFractionalScaleManagerV1) {}
110func (m fractionManager) GetFractionalScale(r *fractionalscale.WpFractionalScaleManagerV1, id uint32, w *wayland.Surface) {
111	s := m.server
112	surf := s.surfaces[w.GetResource()]
113	if surf == nil {
114		return
115	}
116	if _, dup := s.fractions[surf]; dup {
117		r.PostError(uint32(fractionalscale.WpFractionalScaleManagerV1ErrorFractionalScaleExists), "fractional scale already exists")
118		return
119	}
120	f, err := fractionalscale.NewWpFractionalScaleV1(r.Client(), r.Version(), id, fraction{})
121	if err != nil {
122		return
123	}
124	s.fractions[surf] = f
125	f.OnDestroy = func() { delete(s.fractions, surf) }
126	scale := 1.0
127	if o := s.outputOfSurface(surf); o != nil {
128		scale = o.place.Scale
129	}
130	f.SendPreferredScale(uint32(math.Round(scale * 120)))
131}
132
133type fraction struct{}
134
135func (fraction) Destroy(*fractionalscale.WpFractionalScaleV1) {}
136
137type viewporterHandler struct{ server *Server }
138
139func (viewporterHandler) Destroy(*viewporter.WpViewporter) {}
140func (h viewporterHandler) GetViewport(r *viewporter.WpViewporter, id uint32, w *wayland.Surface) {
141	surf := h.server.surfaces[w.GetResource()]
142	if surf == nil {
143		return
144	}
145	if surf.viewport != nil {
146		r.PostError(uint32(viewporter.WpViewporterErrorViewportExists), "viewport already exists")
147		return
148	}
149	vp := &viewport{surface: surf}
150	res, err := viewporter.NewWpViewport(r.Client(), r.Version(), id, vp)
151	if err != nil {
152		return
153	}
154	surf.viewport = vp
155	res.OnDestroy = func() {
156		// Destroying the viewport resets the size on the next commit.
157		if surf.viewport == vp {
158			surf.viewport = nil
159		}
160	}
161}
162
163// viewport holds the pending and committed wp_viewport destination; the
164// source crop is accepted and ignored (clients use it for video crops).
165type viewport struct {
166	surface          *surface
167	pendingW, destW  int32
168	pendingH, destH  int32
169	pendingSet, dest bool
170}
171
172func (*viewport) Destroy(*viewporter.WpViewport) {}
173func (*viewport) SetSource(*viewporter.WpViewport, server.Fixed, server.Fixed, server.Fixed, server.Fixed) {
174}
175func (v *viewport) SetDestination(r *viewporter.WpViewport, w, h int32) {
176	if v.surface.destroyed {
177		r.PostError(uint32(viewporter.WpViewportErrorNoSurface), "surface destroyed")
178		return
179	}
180	if (w == -1) != (h == -1) || (w != -1 && (w <= 0 || h <= 0)) {
181		r.PostError(uint32(viewporter.WpViewportErrorBadValue), "invalid destination size")
182		return
183	}
184	v.pendingW, v.pendingH, v.pendingSet = w, h, w != -1
185}
186func (v *viewport) commit() { v.destW, v.destH, v.dest = v.pendingW, v.pendingH, v.pendingSet }
187
188// logicalSize is the surface size in logical pixels: the viewport
189// destination, else the buffer divided by its integer buffer scale.
190func (s *surface) logicalSize(bw, bh int) (int, int) {
191	if s.viewport != nil && s.viewport.dest {
192		return int(s.viewport.destW), int(s.viewport.destH)
193	}
194	scale := max(s.bufferScale, 1)
195	return bw / scale, bh / scale
196}

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 23

Tags 10

↑↓ moveenter openesc close