graphics_namespace_pool.go RawHistoryFolder

1package daemon
2
3import (
4	"crypto/sha256"
5	"encoding/binary"
6)
7
8type graphicsNamespaceQuarantine struct {
9	base  uint64
10	fence uint64
11}
12
13// graphicsNamespacePool reserves deterministic, attachment/session-scoped Kitty
14// ID blocks. Once a block may have reached an outer terminal it remains in
15// this bounded table for the daemon lifetime: side-effect Output frames have
16// no terminal ACK. Pool exhaustion disables graphics for new attachments and
17// leaves their ordinary text output intact.
18//
19// The pool is guarded by Daemon.mu and has no lock of its own: every method
20// requires the caller to hold it. The zero value is usable; the maps are
21// created lazily and a zero salt simply skips salting.
22type graphicsNamespacePool struct {
23	reserved    map[uint64]struct{}
24	fences      map[uint64]uint64
25	quarantines map[uint64]*graphicsNamespaceQuarantine
26	// salt separates daemon lifetimes before attachment keys are hashed. Kitty
27	// IDs are terminal-global, so a restarted or neighboring daemon must not
28	// deterministically reopen the previous daemon's block.
29	salt uint64
30}
31
32func newGraphicsNamespacePool() graphicsNamespacePool {
33	p := graphicsNamespacePool{salt: newGraphicsNamespaceSalt()}
34	p.init()
35	return p
36}
37
38func (p *graphicsNamespacePool) init() {
39	if p.reserved == nil {
40		p.reserved = make(map[uint64]struct{})
41	}
42	if p.fences == nil {
43		p.fences = make(map[uint64]uint64)
44	}
45	if p.quarantines == nil {
46		p.quarantines = make(map[uint64]*graphicsNamespaceQuarantine)
47	}
48}
49
50func (p *graphicsNamespacePool) reserveLease(key string) (uint64, uint64) {
51	p.init()
52	hashInput := []byte(key)
53	if p.salt != 0 {
54		salt := make([]byte, 8, 8+len(key))
55		binary.BigEndian.PutUint64(salt, p.salt)
56		hashInput = append(salt, hashInput...)
57	}
58	digest := sha256.Sum256(hashInput)
59	preferred := binary.BigEndian.Uint64(digest[:8]) % graphicsIDNamespaceCount
60	for offset := uint64(0); offset < graphicsIDNamespaceCount; offset++ {
61		block := (preferred + offset) % graphicsIDNamespaceCount
62		if _, exists := p.reserved[block]; exists {
63			continue
64		}
65		fence := nextGraphicsNamespaceFence()
66		p.reserved[block] = struct{}{}
67		p.fences[block] = fence
68		return block*graphicsIDNamespaceSize + 1, fence
69	}
70	return 0, 0
71}
72
73// releaseBase is the legacy base-only release. A quarantined block is never
74// released by it.
75func (p *graphicsNamespacePool) releaseBase(base uint64) {
76	if base == 0 || base%graphicsIDNamespaceSize != 1 {
77		return
78	}
79	block := (base - 1) / graphicsIDNamespaceSize
80	if _, quarantined := p.quarantines[block]; quarantined {
81		return
82	}
83	delete(p.reserved, block)
84	delete(p.fences, block)
85}
86
87func (p *graphicsNamespacePool) releaseLease(state *graphicsOutputState) {
88	if state == nil || state.namespaceBase == 0 || state.namespaceBase%graphicsIDNamespaceSize != 1 {
89		return
90	}
91	block := (state.namespaceBase - 1) / graphicsIDNamespaceSize
92	if _, quarantined := p.quarantines[block]; quarantined {
93		return
94	}
95	if current := p.fences[block]; current != 0 && current != state.namespaceFence {
96		return
97	}
98	delete(p.reserved, block)
99	delete(p.fences, block)
100}
101
102func (p *graphicsNamespacePool) quarantine(state *graphicsOutputState) *graphicsNamespaceQuarantine {
103	if state == nil || state.namespaceBase == 0 || state.namespaceBase%graphicsIDNamespaceSize != 1 {
104		return nil
105	}
106	block := (state.namespaceBase - 1) / graphicsIDNamespaceSize
107	p.init()
108	if _, exists := p.quarantines[block]; exists {
109		return nil
110	}
111	p.reserved[block] = struct{}{}
112	p.fences[block] = state.namespaceFence
113	q := &graphicsNamespaceQuarantine{base: state.namespaceBase, fence: state.namespaceFence}
114	p.quarantines[block] = q
115	return q
116}

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 12

Tags 11

↑↓ moveenter openesc close