main f2d166b

inputtimestamps.go RawHistoryFolder

1package wayland
2
3import (
4	"slices"
5	"time"
6
7	"github.com/bnema/go-wayland-bindings/server/inputtimestamps"
8	"github.com/bnema/go-wayland-bindings/server/wayland"
9	"github.com/bnema/purego-libwayland/server"
10)
11
12// zwp_input_timestamps_v1: a client subscribes a wl_keyboard or wl_pointer
13// and gets the device's high-resolution timestamp (libinput microseconds, in
14// the protocol's nanosecond fields) right before each key, motion,
15// button, axis and axis_stop event it sends. Subscriptions are per device
16// resource and die with the device (the object becomes inert) or when the
17// client destroys the timestamps object. There are no touch events, so touch
18// subscriptions only create an inert object.
19//
20// The stamp lists are replaced, never mutated, like the seat device lists.
21
22func registerInputTimestamps(d *server.Display, s *Server) error {
23	return inputtimestamps.NewZwpInputTimestampsManagerV1Global(d, 1, func(c server.Client, v, id uint32) {
24		_, _ = inputtimestamps.NewZwpInputTimestampsManagerV1(c, int32(v), id, timestampsManager{s})
25	})
26}
27
28type timestampsManager struct{ server *Server }
29
30func (timestampsManager) Destroy(*inputtimestamps.ZwpInputTimestampsManagerV1) {}
31
32func (m timestampsManager) GetKeyboardTimestamps(r *inputtimestamps.ZwpInputTimestampsManagerV1, id uint32, kb *wayland.Keyboard) {
33	var dev *server.Resource
34	if kb != nil {
35		dev = kb.Resource
36	}
37	m.subscribe(r, id, dev, m.server.seat.keyStamps)
38}
39
40func (m timestampsManager) GetPointerTimestamps(r *inputtimestamps.ZwpInputTimestampsManagerV1, id uint32, p *wayland.Pointer) {
41	var dev *server.Resource
42	if p != nil {
43		dev = p.Resource
44	}
45	m.subscribe(r, id, dev, m.server.seat.pointerStamps)
46}
47
48// GetTouchTimestamps creates the object only: the seat has no touch events.
49func (timestampsManager) GetTouchTimestamps(r *inputtimestamps.ZwpInputTimestampsManagerV1, id uint32, _ *wayland.Touch) {
50	_, _ = inputtimestamps.NewZwpInputTimestampsV1(r.Client(), r.Version(), id, timestampsHandler{})
51}
52
53// subscribe creates the timestamps object for dev and records it in stamps.
54// A missing or dead device leaves the object inert.
55func (m timestampsManager) subscribe(r *inputtimestamps.ZwpInputTimestampsManagerV1, id uint32, dev *server.Resource, stamps map[*server.Resource][]*inputtimestamps.ZwpInputTimestampsV1) {
56	ts, err := inputtimestamps.NewZwpInputTimestampsV1(r.Client(), r.Version(), id, timestampsHandler{})
57	if err != nil || dev == nil || !dev.Alive() {
58		return
59	}
60	stamps[dev] = append(slices.Clone(stamps[dev]), ts)
61	ts.OnDestroy = func() {
62		// The device may have died first and dropped its entry.
63		list, ok := stamps[dev]
64		if !ok {
65			return
66		}
67		list = slices.DeleteFunc(slices.Clone(list), func(x *inputtimestamps.ZwpInputTimestampsV1) bool { return x == ts })
68		if len(list) == 0 {
69			delete(stamps, dev)
70		} else {
71			stamps[dev] = list
72		}
73	}
74}
75
76type timestampsHandler struct{}
77
78func (timestampsHandler) Destroy(*inputtimestamps.ZwpInputTimestampsV1) {}
79
80// stampKeyboard sends t to the subscribers of k; call it right before a
81// wl_keyboard.key. It allocates nothing.
82func (s *Server) stampKeyboard(k *wayland.Keyboard, t time.Duration) {
83	sendStamps(s.seat.keyStamps[k.Resource], t)
84}
85
86// stampPointer sends t to the subscribers of p; call it right before a
87// wl_pointer motion, button, axis or axis_stop.
88func (s *Server) stampPointer(p *wayland.Pointer, t time.Duration) {
89	sendStamps(s.seat.pointerStamps[p.Resource], t)
90}
91
92func sendStamps(list []*inputtimestamps.ZwpInputTimestampsV1, t time.Duration) {
93	if len(list) == 0 {
94		return
95	}
96	sec := uint64(t / time.Second)
97	ns := uint32(t % time.Second)
98	for _, ts := range list {
99		ts.SendTimestamp(uint32(sec>>32), uint32(sec), ns)
100	}
101}

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