nx_x11_connect.nx source
↩ module page · 258 lines · 8779 B
1// nx_x11_connect.nx -- bits-up X11 wire-protocol connection setup.
2//
3// Opens an AF_UNIX socket to the local X server (typically WSLg's
4// /tmp/.X11-unix/X0 on Windows/WSL2, or an X11/Xorg server on
5// Linux), sends the connection-setup request, and reads the
6// server's reply. Returns the connected fd + parsed server info.
7//
8// X11 protocol reference: X Window System Protocol §8 (Connection
9// Setup), public wire spec (X11 Release 11.0+). Same flavor of
10// bits-up work as TLS/HTTP: we read the spec, write the bytes,
11// no third-party client library.
12//
13// Setup request bytes (12 + auth-name + auth-data, padded to 4):
14// byte 0: byte-order ('l' = 0x6c = little-endian, 'B' = big)
15// byte 1: pad (0)
16// bytes 2-3: major version (11) [u16 LE]
17// bytes 4-5: minor version (0) [u16 LE]
18// bytes 6-7: auth_proto_name_len [u16 LE]
19// bytes 8-9: auth_proto_data_len [u16 LE]
20// bytes 10-11: pad
21// auth_proto_name (padded to 4)
22// auth_proto_data (padded to 4)
23//
24// Server reply byte 0:
25// 1 = success (info follows)
26// 0 = failed (reason string follows)
27// 2 = authenticate (more auth needed)
28//
29// For local WSLg, anonymous auth (zero-length name/data) is accepted
30// when the connecting uid matches the server's owner. If WSLg
31// rejects this, we'll add MIT-MAGIC-COOKIE-1 via ~/.Xauthority parse.
32//
33// Public API:
34// nx_x11_connect_local(display_num)
35// Connects to /tmp/.X11-unix/X<display_num>, performs setup
36// handshake. Returns negative verdict or positive fd.
37//
38// Sealed verdict (negative on failure):
39// NX_X11_OK positive fd returned
40// NX_X11_SOCKET_FAIL socket()/connect() failed
41// NX_X11_WRITE_FAIL could not send setup request
42// NX_X11_READ_FAIL could not read 8-byte setup header
43// NX_X11_REJECTED server returned status != 1 (failed/auth)
44//
45// license_tier: ORIGINAL
46// genealogy_id: international-research-sources/x_org/xproto_v11_r0
47// lineage_id: nishi_x11_connect_q10
48
49import "nx_syscalls.nx"
50import "nx_unix_socket.nx"
51
52const NX_X11_OK: i64 = 1
53const NX_X11_SOCKET_FAIL: i64 = 2
54const NX_X11_WRITE_FAIL: i64 = 3
55const NX_X11_READ_FAIL: i64 = 4
56const NX_X11_REJECTED: i64 = 5
57const NX_X11_VERDICT_N: i64 = 6
58
59// X11 connection state: fd + parsed setup-response info. Filled by
60// nx_x11_connect_local on success. resource_id_base is the first ID
61// the client can allocate; each new resource (window, gc) uses the
62// next id from this base. root_window is the screen's root id,
63// used as parent for top-level CreateWindow.
64struct X11Conn {
65 fd: i64,
66 resource_id_base: i64,
67 resource_id_mask: i64,
68 root_window: i64,
69 root_visual: i64,
70 root_depth: i64,
71 next_resource_id: i64,
72 max_req_words: i64,
73 min_keycode: i64,
74 max_keycode: i64
75}
76
77const NX_X11_CONN_BYTES: i64 = 80
78
79// LE u16 read.
80func _x11_rd_u16(buf: *u8, off: i64) -> i64 {
81 return ((buf[off] & 0xff) as i64) | (((buf[off + 1] & 0xff) as i64) << 8)
82}
83
84// LE u32 read.
85func _x11_rd_u32(buf: *u8, off: i64) -> i64 {
86 return ((buf[off] & 0xff) as i64)
87 | (((buf[off + 1] & 0xff) as i64) << 8)
88 | (((buf[off + 2] & 0xff) as i64) << 16)
89 | (((buf[off + 3] & 0xff) as i64) << 24)
90}
91
92// Allocate the next available resource ID from the connection's pool.
93func nx_x11_alloc_id(c: *X11Conn) -> i64 {
94 let id: i64 = (c.next_resource_id & c.resource_id_mask) | c.resource_id_base
95 c.next_resource_id = c.next_resource_id + 1
96 return id
97}
98
99// Build the X11 socket path "/tmp/.X11-unix/X<N>" into out (NUL-term'd).
100func _x11_build_path(out: *u8, display_num: i64) -> i64 {
101 let s: *u8 = "/tmp/.X11-unix/X\x00"
102 var i: i64 = 0
103 while s[i] != 0 { out[i] = s[i]; i = i + 1 }
104 // Decimal display number (single digit covers :0..:9; covers WSLg)
105 if display_num < 10 {
106 out[i] = (0x30 + display_num) as u8
107 out[i + 1] = 0
108 return i + 1
109 }
110 // Two-digit fallback for :10..:99
111 out[i] = (0x30 + (display_num / 10)) as u8
112 out[i + 1] = (0x30 + (display_num % 10)) as u8
113 out[i + 2] = 0
114 return i + 2
115}
116
117// Write a full N bytes to fd, retrying on short writes.
118func _x11_write_n(fd: i64, buf: *u8, n: i64) -> i64 {
119 var off: i64 = 0
120 while off < n {
121 let w: i64 = sys_write(fd, ((buf as i64) + off) as *u8, n - off)
122 if w <= 0 { return 0 - 1 }
123 off = off + w
124 }
125 return 0
126}
127
128// Read EXACTLY n bytes (or fail).
129func _x11_read_n(fd: i64, buf: *u8, n: i64) -> i64 {
130 var off: i64 = 0
131 while off < n {
132 let r: i64 = sys_read(fd, ((buf as i64) + off) as *u8, n - off)
133 if r <= 0 { return 0 - 1 }
134 off = off + r
135 }
136 return 0
137}
138
139// Public: connect + handshake + parse setup response. Returns
140// *X11Conn cast as i64 (always > 0) on success, or 0 - NX_X11_* on
141// failure. Caller casts to *X11Conn.
142func nx_x11_connect_local(display_num: i64) -> i64 {
143 let path: *u8 = sys_mmap(64)
144 _x11_build_path(path, display_num)
145
146 let fd: i64 = nx_unix_connect(path)
147 if fd < 0 { return 0 - NX_X11_SOCKET_FAIL }
148
149 // Build 12-byte setup request: anonymous auth.
150 let req: *u8 = sys_mmap(16)
151 req[0] = 0x6C; req[1] = 0x00
152 req[2] = 0x0B; req[3] = 0x00 // major 11
153 req[4] = 0x00; req[5] = 0x00 // minor 0
154 req[6] = 0x00; req[7] = 0x00 // auth_name_len = 0
155 req[8] = 0x00; req[9] = 0x00 // auth_data_len = 0
156 req[10] = 0x00; req[11] = 0x00
157
158 if _x11_write_n(fd, req, 12) < 0 {
159 sys_close(fd)
160 return 0 - NX_X11_WRITE_FAIL
161 }
162
163 // Read 8-byte setup-reply header.
164 let hdr: *u8 = sys_mmap(16)
165 if _x11_read_n(fd, hdr, 8) < 0 {
166 sys_close(fd)
167 return 0 - NX_X11_READ_FAIL
168 }
169
170 let status: i64 = hdr[0] & 0xff
171 if status != 1 {
172 sys_close(fd)
173 return 0 - NX_X11_REJECTED
174 }
175
176 let extra_words: i64 = _x11_rd_u16(hdr, 6)
177 let extra_bytes: i64 = extra_words * 4
178 let extra: *u8 = sys_mmap(extra_bytes + 64)
179 if extra_bytes > 0 {
180 if _x11_read_n(fd, extra, extra_bytes) < 0 {
181 sys_close(fd)
182 return 0 - NX_X11_READ_FAIL
183 }
184 }
185
186 // Parse the setup-success additional data (X Protocol §8):
187 // 0: release_number (4)
188 // 4: resource_id_base (4)
189 // 8: resource_id_mask (4)
190 // 12: motion_buffer_size (4)
191 // 16: vendor_length (2)
192 // 18: max_request_length (2)
193 // 20: num_screens (1)
194 // 21: num_formats (1)
195 // 22: image_byte_order (1)
196 // 23: bitmap_format_bit_order (1)
197 // 24: bitmap_format_scanline_unit (1)
198 // 25: bitmap_format_scanline_pad (1)
199 // 26: min_keycode (1)
200 // 27: max_keycode (1)
201 // 28-31: pad
202 // 32+: vendor (vendor_length, padded to 4)
203 // pixmap_formats (8 bytes * num_formats)
204 // screens (variable, first 4 bytes of each screen = root window)
205 let rid_base: i64 = _x11_rd_u32(extra, 4)
206 let rid_mask: i64 = _x11_rd_u32(extra, 8)
207 let vendor_len: i64 = _x11_rd_u16(extra, 16)
208 let max_req: i64 = _x11_rd_u16(extra, 18)
209 let n_formats: i64 = (extra[21] & 0xff) as i64
210 let min_kc: i64 = (extra[26] & 0xff) as i64
211 let max_kc: i64 = (extra[27] & 0xff) as i64
212
213 // Vendor occupies `vendor_len` bytes padded to 4-byte multiple.
214 var pad_vendor: i64 = vendor_len
215 if (pad_vendor & 3) != 0 {
216 pad_vendor = pad_vendor + 4 - (pad_vendor & 3)
217 }
218 let screens_off: i64 = 32 + pad_vendor + n_formats * 8
219
220 // First screen layout (X Protocol §8):
221 // 0-3: root window
222 // 4-7: default colormap
223 // 8-11: white_pixel
224 // 12-15: black_pixel
225 // 16-19: current_input_mask
226 // 20-21: width_in_pixels
227 // 22-23: height_in_pixels
228 // 24-25: width_in_mm
229 // 26-27: height_in_mm
230 // 28-29: min_installed_maps
231 // 30-31: max_installed_maps
232 // 32-35: root_visual
233 // 36: backing_stores
234 // 37: save_unders
235 // 38: root_depth
236 // 39: n_depths
237 let root_win: i64 = _x11_rd_u32(extra, screens_off + 0)
238 let root_visual: i64 = _x11_rd_u32(extra, screens_off + 32)
239 let root_depth: i64 = (extra[screens_off + 38] & 0xff) as i64
240
241 let c_raw: *u8 = sys_mmap(NX_X11_CONN_BYTES + 16)
242 let c: *X11Conn = c_raw as *X11Conn
243 c.fd = fd
244 c.resource_id_base = rid_base
245 c.resource_id_mask = rid_mask
246 c.root_window = root_win
247 c.root_visual = root_visual
248 c.root_depth = root_depth
249 c.next_resource_id = 0
250 c.max_req_words = max_req
251 c.min_keycode = min_kc
252 c.max_keycode = max_kc
253 return c as i64
254}
255
256func main() -> i64 {
257 return 0
258}