nx_x11_window.nx source
↩ module page · 225 lines · 8006 B
1// nx_x11_window.nx -- bits-up X11 CreateWindow + MapWindow.
2//
3// Session 2 of the bits-up browser arc (Task #83). Composes the
4// X11 connection from nx_x11_connect with the wire-protocol opcodes
5// that put a window on screen. WSLg translates the X11 stream into
6// a real native Windows surface.
7//
8// CreateWindow (opcode 1) layout (X Protocol §10 / xproto.xml):
9// byte 0: opcode = 1
10// byte 1: depth (0 = CopyFromParent)
11// bytes 2-3: request_length (4-byte units)
12// bytes 4-7: window_id (allocated via nx_x11_alloc_id)
13// bytes 8-11: parent (e.g. root_window)
14// bytes 12-13: x (signed)
15// bytes 14-15: y
16// bytes 16-17: width
17// bytes 18-19: height
18// bytes 20-21: border_width
19// bytes 22-23: class (1 = InputOutput, 2 = InputOnly)
20// bytes 24-27: visual (0 = CopyFromParent)
21// bytes 28-31: value_mask
22// (optional) value_list (4 bytes per set bit in value_mask)
23//
24// value-mask bits we use (X protocol §6.7):
25// CWBackPixel = 0x00000002
26// CWEventMask = 0x00000800
27//
28// MapWindow (opcode 8):
29// byte 0: opcode = 8
30// byte 1: unused
31// bytes 2-3: request_length = 2
32// bytes 4-7: window_id
33//
34// API:
35// nx_x11_create_window(conn, x, y, w, h, bg_pixel, event_mask)
36// -> window_id (or 0 - NX_X11_*)
37// nx_x11_map_window(conn, window_id) -> 0 | negative
38// nx_x11_flush_via_get_input_focus(conn)
39// -- forces server to actually process pending requests by
40// sending a synchronous round-trip (GetInputFocus). Without
41// this WSLg sometimes buffers MapWindow indefinitely.
42//
43// license_tier: ORIGINAL
44// genealogy_id: international-research-sources/x_org/xproto_v11_r0
45// lineage_id: nishi_x11_window_q10
46
47import "nx_syscalls.nx"
48import "nx_x11_connect.nx"
49
50const NX_X11_CW_BACK_PIXEL: i64 = 0x00000002
51const NX_X11_CW_EVENT_MASK: i64 = 0x00000800
52
53// LE u16/u32 writers.
54func _x11_wr_u16(buf: *u8, off: i64, v: i64) -> i64 {
55 buf[off] = (v & 0xff) as u8
56 buf[off + 1] = ((v >> 8) & 0xff) as u8
57 return 0
58}
59
60func _x11_wr_u32(buf: *u8, off: i64, v: i64) -> i64 {
61 buf[off] = (v & 0xff) as u8
62 buf[off + 1] = ((v >> 8) & 0xff) as u8
63 buf[off + 2] = ((v >> 16) & 0xff) as u8
64 buf[off + 3] = ((v >> 24) & 0xff) as u8
65 return 0
66}
67
68// Write n bytes to conn.fd, retrying on short writes.
69func _x11_send(conn: *X11Conn, buf: *u8, n: i64) -> i64 {
70 var off: i64 = 0
71 while off < n {
72 let w: i64 = sys_write(conn.fd, ((buf as i64) + off) as *u8, n - off)
73 if w <= 0 { return 0 - 1 }
74 off = off + w
75 }
76 return 0
77}
78
79// CreateWindow + value list (CWBackPixel + CWEventMask).
80// Returns new window_id (positive) or 0 - NX_X11_* on failure.
81func nx_x11_create_window(conn: *X11Conn,
82 x: i64, y: i64, w: i64, h: i64,
83 bg_pixel: i64, event_mask: i64) -> i64 {
84 let wid: i64 = nx_x11_alloc_id(conn)
85
86 // Request: 32 bytes core + 8 bytes values (2 values * 4 bytes)
87 let req: *u8 = sys_mmap(64)
88 var i: i64 = 0
89 while i < 64 { req[i] = 0; i = i + 1 }
90
91 let value_mask: i64 = NX_X11_CW_BACK_PIXEL | NX_X11_CW_EVENT_MASK
92 let total_len_bytes: i64 = 40
93 let total_len_words: i64 = total_len_bytes / 4 // 10
94
95 req[0] = 1 // opcode
96 req[1] = 0 // depth = CopyFromParent
97 _x11_wr_u16(req, 2, total_len_words)
98 _x11_wr_u32(req, 4, wid)
99 _x11_wr_u32(req, 8, conn.root_window)
100 _x11_wr_u16(req, 12, x)
101 _x11_wr_u16(req, 14, y)
102 _x11_wr_u16(req, 16, w)
103 _x11_wr_u16(req, 18, h)
104 _x11_wr_u16(req, 20, 0) // border-width
105 _x11_wr_u16(req, 22, 1) // class = InputOutput
106 _x11_wr_u32(req, 24, 0) // visual = CopyFromParent
107 _x11_wr_u32(req, 28, value_mask)
108 // value list: pixel values follow in value-mask bit order
109 _x11_wr_u32(req, 32, bg_pixel)
110 _x11_wr_u32(req, 36, event_mask)
111
112 if _x11_send(conn, req, total_len_bytes) < 0 { return 0 - 3 }
113 return wid
114}
115
116// ChangeProperty (opcode 18) -- set a string property on a window.
117// Used to set WM_NAME so window managers (WSLg's compositor) can
118// label the window and surface it on the desktop.
119//
120// Atom IDs are well-known: WM_NAME=39, STRING=31.
121//
122// Request layout:
123// byte 0: opcode = 18
124// byte 1: mode (0 = Replace)
125// bytes 2-3: request_length (4-byte units)
126// bytes 4-7: window
127// bytes 8-11: property atom
128// bytes 12-15: type atom
129// byte 16: format (8, 16, or 32)
130// bytes 17-19: pad
131// bytes 20-23: data_len (number of FORMAT-sized elements)
132// bytes 24+: data (padded to 4-byte multiple)
133func nx_x11_set_wm_name(conn: *X11Conn, window: i64,
134 name: *u8, name_len: i64) -> i64 {
135 // Round data length up to 4-byte multiple.
136 var padded_len: i64 = name_len
137 if (padded_len & 3) != 0 {
138 padded_len = padded_len + 4 - (padded_len & 3)
139 }
140 let total_len_bytes: i64 = 24 + padded_len
141 let total_len_words: i64 = total_len_bytes / 4
142
143 let req: *u8 = sys_mmap(total_len_bytes + 16)
144 var i: i64 = 0
145 while i < total_len_bytes + 16 { req[i] = 0; i = i + 1 }
146
147 req[0] = 18 // opcode
148 req[1] = 0 // mode = Replace
149 _x11_wr_u16(req, 2, total_len_words)
150 _x11_wr_u32(req, 4, window)
151 _x11_wr_u32(req, 8, 39) // property = WM_NAME
152 _x11_wr_u32(req, 12, 31) // type = STRING
153 req[16] = 8 // format = 8 bits
154 _x11_wr_u32(req, 20, name_len) // data_len (8-bit chars)
155 var k: i64 = 0
156 while k < name_len {
157 req[24 + k] = name[k]
158 k = k + 1
159 }
160 return _x11_send(conn, req, total_len_bytes)
161}
162
163// MapWindow opcode 8.
164func nx_x11_map_window(conn: *X11Conn, window_id: i64) -> i64 {
165 let req: *u8 = sys_mmap(16)
166 req[0] = 8
167 req[1] = 0
168 _x11_wr_u16(req, 2, 2)
169 _x11_wr_u32(req, 4, window_id)
170 return _x11_send(conn, req, 8)
171}
172
173// GetInputFocus (opcode 43) -- 4-byte request that elicits a 32-byte
174// reply. Used as a synchronous round-trip to force the server to
175// process all queued requests. ALSO checks for X11 error replies:
176// byte 0 == 0 means Error reply (byte 1 is the error code).
177//
178// Returns:
179// 0 = reply received cleanly (server happy with prior requests)
180// -1 = send failed
181// -2 = read failed
182// 100+code = X11 error code (3=Window, 4=Pixmap, 5=Atom, 8=Match,
183// 9=Drawable, 10=Access, 11=Alloc, 12=Colormap, ...)
184func nx_x11_flush_via_get_input_focus(conn: *X11Conn) -> i64 {
185 let req: *u8 = sys_mmap(8)
186 req[0] = 43
187 req[1] = 0
188 _x11_wr_u16(req, 2, 1)
189 if _x11_send(conn, req, 4) < 0 { return 0 - 1 }
190 // Drain packets until we see OUR Reply (kind=1). Events (kind>=2)
191 // and errors (kind=0) get dropped on the diagnostic path; a real
192 // event-loop forwards them to a dispatcher. Critical: consume
193 // OUR reply so subsequent calls don't pick it up by mistake.
194 let reply: *u8 = sys_mmap(64)
195 var saw_reply: i64 = 0
196 var safety: i64 = 0
197 while saw_reply == 0 {
198 if safety >= 64 { return 0 - 3 }
199 var got: i64 = 0
200 while got < 32 {
201 let r: i64 = sys_read(conn.fd, ((reply as i64) + got) as *u8, 32 - got)
202 if r <= 0 { return 0 - 2 }
203 got = got + r
204 }
205 let kind: i64 = (reply[0] & 0xff) as i64
206 if kind == 1 { saw_reply = 1 }
207 safety = safety + 1
208 }
209 return 0
210}
211
212// Stub kept to satisfy the legacy err-code path that no longer fires.
213func _x11_dead_err_path() -> i64 {
214 let reply: *u8 = sys_mmap(8)
215 let kind: i64 = 0
216 if kind == 0 {
217 let err_code: i64 = (reply[1] & 0xff) as i64
218 return 100 + err_code
219 }
220 return 0
221}
222
223func main() -> i64 {
224 return 0
225}