v0.8.0 446d1e3

leases.go RawHistoryFolder

1package app
2
3import (
4	"context"
5	"os"
6	"slices"
7
8	"github.com/bnema/neferwl/internal/ports"
9	"github.com/bnema/zerowrap"
10	"golang.org/x/sys/unix"
11)
12
13// leaseInventoryCard is what the lease publisher reads from a DRM card.
14type leaseInventoryCard interface {
15	Path() string
16	FinishedLeases() []uint32
17	Leasable() []ports.LeaseConnector
18	ClientFD() (*os.File, error)
19}
20
21// leasePublisher tells wayland which connectors of each card can be leased
22// (wp_drm_lease_device_v1), and which leases ended. It keeps one non-master
23// client fd per advertised card and resends an inventory only when it
24// changed. The DRM backend loop owns it.
25type leasePublisher struct {
26	ctx    context.Context
27	events chan<- ports.LeaseMessage
28	log    zerowrap.Logger
29	// seatActive and protected say whether leases may be offered: only on an
30	// active seat in an unprotected session.
31	seatActive, protected func() bool
32	clientFDs             map[leaseInventoryCard]*os.File
33	last                  map[leaseInventoryCard][]ports.LeaseConnector
34}
35
36func newLeasePublisher(ctx context.Context, events chan<- ports.LeaseMessage, seatActive, protected func() bool, log zerowrap.Logger) *leasePublisher {
37	return &leasePublisher{ctx: ctx, events: events, seatActive: seatActive, protected: protected, log: log, clientFDs: map[leaseInventoryCard]*os.File{}, last: map[leaseInventoryCard][]ports.LeaseConnector{}}
38}
39
40// send delivers msg, closing its files when ctx ends first.
41func (p *leasePublisher) send(msg ports.LeaseMessage) {
42	select {
43	case p.events <- msg:
44	case <-p.ctx.Done():
45		ports.CloseLeaseFiles(msg)
46	}
47}
48
49// publish reports the finished leases of c and its leasable connectors. An
50// inactive seat or a protected session withdraws the inventory; protection
51// always tells wayland so.
52func (p *leasePublisher) publish(c leaseInventoryCard) {
53	for _, id := range c.FinishedLeases() {
54		p.send(ports.LeaseFinished{Card: c.Path(), LeaseID: id})
55	}
56	var next []ports.LeaseConnector
57	if p.seatActive() && !p.protected() {
58		next = c.Leasable()
59	}
60	if len(next) == 0 {
61		if p.protected() || p.clientFDs[c] != nil {
62			p.send(ports.LeaseConnectors{Card: c.Path()})
63		}
64		if p.clientFDs[c] != nil {
65			p.clientFDs[c].Close()
66			delete(p.clientFDs, c)
67		}
68		delete(p.last, c)
69		return
70	}
71	if slices.Equal(p.last[c], next) && p.clientFDs[c] != nil {
72		return
73	}
74	if p.clientFDs[c] == nil {
75		f, err := c.ClientFD()
76		if err != nil {
77			p.log.Warn().Err(err).Str("card", c.Path()).Msg("lease client fd")
78			return
79		}
80		p.clientFDs[c] = f
81	}
82	// Each message owns its fd: a queued inventory outlives clientFDs[c].
83	fd, err := unix.FcntlInt(p.clientFDs[c].Fd(), unix.F_DUPFD_CLOEXEC, 0)
84	if err != nil {
85		p.log.Warn().Err(err).Str("card", c.Path()).Msg("lease client fd")
86		return
87	}
88	p.last[c] = next
89	p.send(ports.LeaseConnectors{Card: c.Path(), Device: os.NewFile(uintptr(fd), c.Path()), Connectors: next})
90}
91
92// close releases the client fds.
93func (p *leasePublisher) close() {
94	for _, f := range p.clientFDs {
95		f.Close()
96	}
97	clear(p.clientFDs)
98}

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