code wiki / (root) / nx_x11_connect.nx

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}