detached 602baa7

conn.go RawHistoryFolder

1package xturbo
2
3import (
4	"fmt"
5	"os"
6	"time"
7
8	"golang.org/x/sys/unix"
9)
10
11// Conn is a client connection to an X server over a stream socket. It is not
12// safe for concurrent use: one goroutine owns it.
13type Conn struct {
14	fd    int
15	buf   *Buf
16	ext   ExtTable
17	setup []byte
18	root  uint32
19
20	xidBase, xidMask uint32   // resource-id allocation, used by NewXID
21	xids, xidPend    xidRange // current range; prefetched range (left 0: none)
22	xidFree          [xidFreeCap]uint32
23	xidFreeN         int
24	xidReq           Seq  // last GetXIDRange sent
25	xidNone          bool // the last GetXIDRange gave no usable range
26	xidFailed        bool // ... or an X error, xidErr
27	xidErr           XError
28
29	// Blocking calls (Sync, InternAtoms, NewXID refill).
30	h          *Handler      // last non-nil Handler given to Dispatch or Sync
31	rdIdle     bool          // the last Dispatch read nothing: the socket was empty
32	atoms      []uint32      // InternAtoms destination, indexed by slot Args[0]
33	syncErr    XError        // first error on a reserved-kind request
34	syncFailed bool          // syncErr is set
35	resync     Seq           // last GetInputFocus appended by Flush
36	timeout    time.Duration // bound of one Sync, InternAtoms or XID refill
37
38	// Read side, owned by Dispatch: rb[rpos:rend] is received, unconsumed.
39	rb          []byte
40	rpos, rend  int
41	maxPacket   int   // MaxPacket; lowered by tests
42	rerr        error // sticky read failure
43	dispatching bool  // inside Dispatch: rejects nested calls
44	seq         Seq   // last widened sequence number seen
45
46	// Reply ring: Expect registrations indexed by seq&ringMask.
47	ring  [ringSize]slot
48	live  int // registered slots
49	stats Stats
50
51	// Callback arguments, reused per packet so nothing escapes.
52	ev    Event
53	reply Reply
54	xerr  XError
55}
56
57// NewConn takes ownership of fd, a connected stream socket to an X server,
58// and runs the connection setup without authorization (e.g. the -wm fd of an
59// XWayland the caller spawned). It blocks for the handshake, then one round
60// trip negotiates every known extension (QueryExtension each, BIG-REQUESTS
61// Enable when present) into Ext() and the Buf request limit. fd is left
62// O_NONBLOCK. The whole call gives up after 10 s with an error wrapping
63// os.ErrDeadlineExceeded (an alive but silent server). On failure fd is
64// closed.
65func NewConn(fd int) (*Conn, error) {
66	return newConn(fd, "", nil)
67}
68
69func newConn(fd int, authName string, authData []byte) (*Conn, error) {
70	c, err := setupConn(fd, authName, authData)
71	if err != nil {
72		_ = unix.Close(fd)
73		return nil, err
74	}
75	return c, nil
76}
77
78func setupConn(fd int, authName string, authData []byte) (*Conn, error) {
79	deadline := time.Now().Add(connectTimeout)
80	if err := unix.SetNonblock(fd, true); err != nil {
81		return nil, os.NewSyscallError("fcntl", err)
82	}
83	setup, info, err := handshake(fd, authName, authData, deadline)
84	if err != nil {
85		return nil, err
86	}
87	c := newConnState(fd, setup, info)
88	if err := c.bootstrap(deadline); err != nil {
89		return nil, fmt.Errorf("xturbo: extension bootstrap: %w", err)
90	}
91	return c, nil
92}
93
94// newConnState builds a Conn over an fd whose setup already completed.
95func newConnState(fd int, setup []byte, info setupInfo) *Conn {
96	c := &Conn{
97		fd:        fd,
98		buf:       NewBuf(0),
99		setup:     setup,
100		root:      info.root,
101		xidBase:   info.xidBase,
102		xidMask:   info.xidMask,
103		rb:        make([]byte, readBufSize),
104		maxPacket: MaxPacket,
105		timeout:   syncTimeout,
106	}
107	c.buf.Ext = &c.ext
108	c.initXIDs()
109	return c
110}
111
112// Close closes the socket. Later calls, and Flush after Close, return
113// ErrClosed.
114func (c *Conn) Close() error {
115	if c.fd < 0 {
116		return ErrClosed
117	}
118	fd := c.fd
119	c.fd = -1
120	c.buf.Fail(ErrClosed)
121	if err := unix.Close(fd); err != nil {
122		return os.NewSyscallError("close", err)
123	}
124	return nil
125}
126
127// Fd returns the socket, for the caller's poll loop; -1 after Close.
128func (c *Conn) Fd() int { return c.fd }
129
130// Buf returns the request encoder bound to this connection.
131func (c *Conn) Buf() *Buf { return c.buf }
132
133// Ext returns the connection's extension table.
134func (c *Conn) Ext() *ExtTable { return &c.ext }
135
136// SetupBytes returns the full setup reply. It is retained for the life of the
137// Conn; parse it with the xproto Setup view. Do not modify it.
138func (c *Conn) SetupBytes() []byte { return c.setup }
139
140// Root returns the root window of the first screen.
141func (c *Conn) Root() uint32 { return c.root }
142
143// resyncAt is the number of unacknowledged requests after which Flush appends
144// a GetInputFocus, so a long stream of requests without replies or events
145// still brings back a sequence number before the 16-bit wire value wraps.
146const resyncAt = 0x8000
147
148// Flush writes queued requests without blocking. It returns ErrWouldBlock when
149// the socket is full (arm writable interest and call again) and the sticky
150// Buf error once the connection is unusable. With resyncAt or more requests
151// unacknowledged it first appends a GetInputFocus whose reply is discarded
152// (Stats.Resyncs), at most one in flight.
153func (c *Conn) Flush() error {
154	if c.fd < 0 {
155		return ErrClosed
156	}
157	b := c.buf
158	if b.err == nil && b.next-b.acked >= resyncAt && c.resync <= b.acked {
159		if seq := c.fence(); seq != 0 {
160			c.resync = seq
161			c.stats.Resyncs++
162		}
163	}
164	return b.flush(c.fd)
165}
166
167// fence queues a GetInputFocus whose reply is consumed and dropped. Its
168// reply proves every earlier request complete.
169func (c *Conn) fence() Seq {
170	seq := encodeGetInputFocus(c.buf)
171	// On ErrRingFull the reply is merely counted as dropped.
172	_ = c.expect(seq, kindDiscard, [4]uint32{})
173	return seq
174}

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 1

Tags 1

↑↓ moveenter openesc close