code wiki / _hdl_build / nishi_gui.nx
nishi_gui.nx source
↩ module page · 838 lines · 41790 B
1// nishi_gui.nx -- the NISHI browser, graphical + INTERACTIVE (Windows). "Nishi" = west (the family).
2// GUI-R5: a real navigable browser. Self-contained: fetches its own pages in-process by spawning the
3// proven nishi_fetch.exe (CreateProcessA, child stdout -> temp file -> read back). Clickable links
4// (hit-test the mouse against a per-render link table -> fetch that URL -> re-render). Back stack.
5// Still 2x supersampled anti-aliasing + reading-view layout + page title heading + blue links.
6// Built by nx_pe_compile_win_gui.nx (sovereign Win32 FFI; no gcc/curl/zlib/toolkit).
7
8import "nx_syscalls.nx"
9import "nx_font8x8.nx"
10import "nx_browser_render.nx" // R3: the EXTRACTED CSS render core (br_layout cascade+box-layout, br_draw_fb paint)
11import "nx_img_bytes_to_rgb.nx" // R4: decode fetched <img> bytes -> RGB (bytes-in, PE-safe; no sys_read_file)
12
13func win32(id: i64, args: *i64) -> i64 { return id }
14func win32addr(id: i64) -> i64 { return id }
15
16// user32/gdi32/kernel32 imports by FFI id (IAT slot), provided by the keystone.
17// DERIVED FROM THE EMIT ORDER, NOT GUESSED (2026-07-31). The FFI id IS the IAT slot index, and
18// nx_pe_compile_win_gui.nx:274-298 emits the names in that order: ExitProcess(0) GetProcessHeap(1)
19// VirtualAlloc(2) GetStdHandle(3) WriteFile(4) Sleep(5) GetModuleHandleA(6) ... GetClientRect(24).
20// All 20 constants already declared below agree with that table, which is what makes 4 safe to assert.
21// gwrite() at the bottom of this file has called win32(W_WriteFile, a) since it was written, against a
22// constant that did not exist; nx_cc FAILS OPEN on undefined identifiers (seq1012) so nothing said so.
23// A WRONG ordinal here would call a DIFFERENT kernel32 export with WriteFile's arguments, so this is
24// verified against the emitter rather than inferred from the (stale) header comment, which still says
25// slot 2 is HeapAlloc where the emitter writes VirtualAlloc.
26const W_WriteFile: i64 = 4
27const W_GetModuleHandleA: i64 = 6
28const W_RegisterClassExA: i64 = 7
29const W_CreateWindowExA: i64 = 8
30const W_ShowWindow: i64 = 9
31const W_PeekMessageA: i64 = 10
32const W_TranslateMessage: i64 = 11
33const W_DispatchMessageA: i64 = 12
34const W_DefWindowProcA: i64 = 13
35const W_GetDC: i64 = 14
36const W_ReleaseDC: i64 = 15
37const W_IsWindow: i64 = 16
38const W_LoadCursorA: i64 = 17
39const W_SetDIBitsToDevice: i64 = 18
40const W_ReadFile: i64 = 19
41const W_CreateFileA: i64 = 20
42const W_CreateProcessA: i64 = 21
43const W_WaitForSingleObject:i64 = 22
44const W_CloseHandle: i64 = 23
45const W_GetClientRect: i64 = 24 // added to the win_gui import table for live resize re-flow
46
47const LINKMAX: i64 = 1024
48
49func w8(p: *u8, o: i64, v: i64) -> i64 { p[o] = (v & 0xff) as u8; return 0 }
50func w16(p: *u8, o: i64, v: i64) -> i64 { p[o]=(v&0xff) as u8; p[o+1]=((v>>8)&0xff) as u8; return 0 }
51func w32(p: *u8, o: i64, v: i64) -> i64 {
52 p[o]=(v&0xff) as u8; p[o+1]=((v>>8)&0xff) as u8; p[o+2]=((v>>16)&0xff) as u8; p[o+3]=((v>>24)&0xff) as u8; return 0
53}
54func wp(p: *u8, o: i64, v: i64) -> i64 { w32(p,o,v); w32(p,o+4, v / 4294967296); return 0 }
55func rd32(p: *u8, o: i64) -> i64 { return (p[o]&0xff) | ((p[o+1]&0xff)<<8) | ((p[o+2]&0xff)<<16) | ((p[o+3]&0xff)<<24) }
56func rp(p: *u8, o: i64) -> i64 { return rd32(p,o) | (rd32(p,o+4) * 4294967296) }
57func slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
58func print_int(v: i64) -> i64 {
59 let b: *u8 = sys_mmap(32)
60 var n: i64 = v
61 if n < 0 { n = 0 - n }
62 var i: i64 = 30
63 if n == 0 { b[i] = 0x30 as u8; i = i - 1 }
64 else { while n > 0 { b[i] = (0x30 + (n - (n/10)*10)) as u8; n = n / 10; i = i - 1 } }
65 sys_write(1, ((b as i64) + i + 1) as *u8, 30 - i)
66 return 0
67}
68func scopy(dst: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { dst[o+i]=s[i]; i=i+1 } return o+i }
69
70func fill_rect(fb: *u8, W: i64, x0: i64, y0: i64, rw: i64, rh: i64, b: i64, g: i64, r: i64) -> i64 {
71 var y: i64 = y0
72 while y < y0 + rh {
73 var x: i64 = x0
74 while x < x0 + rw {
75 let px: i64 = (y * W + x) * 4
76 fb[px+0]=b as u8; fb[px+1]=g as u8; fb[px+2]=r as u8; fb[px+3]=0 as u8
77 x = x + 1
78 }
79 y = y + 1
80 }
81 return 0
82}
83func draw_char(fb: *u8, W: i64, ft: *u8, c: i64, x0: i64, y0: i64, sc: i64, b: i64, g: i64, r: i64) -> i64 {
84 var cc: i64 = c
85 if cc < 0x20 { return 0 }
86 if cc > 0x7F { cc = 0x3F }
87 let idx: i64 = (cc - 0x20) * 8
88 var row: i64 = 0
89 while row < 8 {
90 let bits: i64 = ft[idx + row] & 0xff
91 var col: i64 = 0
92 while col < 8 {
93 if ((bits >> col) & 1) == 1 { fill_rect(fb, W, x0 + col*sc, y0 + row*sc, sc, sc, b, g, r) }
94 col = col + 1
95 }
96 row = row + 1
97 }
98 return 0
99}
100func draw_span(fb: *u8, W: i64, ft: *u8, x0: i64, y0: i64, text: *u8, start: i64, len: i64, sc: i64, b: i64, g: i64, r: i64) -> i64 {
101 var x: i64 = x0
102 var i: i64 = 0
103 while i < len {
104 let c: i64 = text[start+i] & 0xff
105 if c >= 0x80 { } else { draw_char(fb, W, ft, c, x, y0, sc, b, g, r); x = x + 9 * sc }
106 i = i + 1
107 }
108 return x
109}
110func draw_text(fb: *u8, W: i64, ft: *u8, x0: i64, y0: i64, str: *u8, sc: i64, b: i64, g: i64, r: i64) -> i64 {
111 return draw_span(fb, W, ft, x0, y0, str, 0, slen(str), sc, b, g, r)
112}
113
114// Fetch `url` (ulen bytes) in-process: spawn nishi_fetch.exe with its stdout redirected to a temp
115// file, wait, then read the file back into outbuf. Returns bytes read (0 on failure). a/cmd are
116// caller-owned scratch (a >= 256 i64, cmd >= 2048 B). Reuses the proven sovereign fetch engine.
117func fetch_url(url: *u8, ulen: i64, outbuf: *u8, cap: i64, a: *i64, cmd: *u8) -> i64 {
118 var cl: i64 = scopy(cmd, 0, "_offc/nishi_fetch.exe -H" as *u8) // R3: raw HTML (the CSS renderer wants markup, not text)
119 var i: i64 = 0
120 while i < ulen { cmd[cl+i] = url[i]; i = i + 1 }
121 cmd[cl+ulen] = 0 as u8
122 let app: *u8 = "_offc/nishi_fetch.exe" as *u8
123 let fname: *u8 = "_nav_page.txt" as *u8
124
125 let sa: *u8 = sys_mmap(64)
126 w32(sa, 0, 24); wp(sa, 8, 0); w32(sa, 16, 1) // inheritable handle
127 a[0]=fname as i64; a[1]=0x40000000; a[2]=1; a[3]=sa as i64; a[4]=2; a[5]=0x80; a[6]=0
128 let hout: i64 = win32(W_CreateFileA, a) // temp for child stdout
129 if hout == (0 - 1) { return 0 }
130
131 let si: *u8 = sys_mmap(128)
132 w32(si, 0, 104); w32(si, 60, 0x100); wp(si, 80, 0); wp(si, 88, hout); wp(si, 96, 0) // stdout->temp, stderr discarded (status lines)
133 let pi: *u8 = sys_mmap(64)
134 a[0]=app as i64; a[1]=cmd as i64; a[2]=0; a[3]=0; a[4]=1; a[5]=0x08000000; a[6]=0; a[7]=0; a[8]=si as i64; a[9]=pi as i64
135 win32(W_CreateProcessA, a) // explicit app path + CREATE_NO_WINDOW + inherit handles
136 let hproc: i64 = rp(pi, 0)
137 if hproc == 0 { a[0]=hout; win32(W_CloseHandle, a); return 0 }
138
139 // Wait for the child WHILE pumping the UI message queue, so the window never goes "not responding" and
140 // gets torn down (the render-then-close bug). DispatchMessage -> DefWindowProc (the nav logic lives in
141 // the MAIN loop, not the wndproc), so this is re-entrancy-safe. Poll the child with a 50ms wait per turn.
142 let fmsg: *u8 = sys_mmap(64)
143 var fw: i64 = 1
144 while fw == 1 {
145 var pd: i64 = 1
146 while pd == 1 {
147 a[0]=fmsg as i64; a[1]=0; a[2]=0; a[3]=0; a[4]=1
148 if win32(W_PeekMessageA, a) != 0 { win32(W_TranslateMessage, a) } else { pd = 0 }
149 }
150 a[0]=hproc; a[1]=50
151 if win32(W_WaitForSingleObject, a) == 0 { fw = 0 }
152 }
153 a[0]=hproc; win32(W_CloseHandle, a)
154 a[0]=rp(pi,8); win32(W_CloseHandle, a)
155 a[0]=hout; win32(W_CloseHandle, a)
156
157 a[0]=fname as i64; a[1]=0x80000000; a[2]=1; a[3]=0; a[4]=3; a[5]=0x80; a[6]=0
158 let hin: i64 = win32(W_CreateFileA, a) // reopen temp for read
159 if hin == (0 - 1) { return 0 }
160 let got: *u8 = sys_mmap(64)
161 var total: i64 = 0
162 var go: i64 = 1
163 while go == 1 {
164 w32(got, 0, 0)
165 a[0]=hin; a[1]=(outbuf as i64)+total; a[2]=cap-total; a[3]=got as i64; a[4]=0
166 win32(W_ReadFile, a)
167 let n: i64 = rd32(got, 0)
168 if n <= 0 { go = 0 } else { total = total + n }
169 if total >= cap { go = 0 }
170 }
171 a[0]=hin; win32(W_CloseHandle, a)
172 return total
173}
174
175// read a LOCAL file wholesale via Win32 CreateFileA + ReadFile (the emitter provides these FFI slots;
176// it does NOT thunk sys_read_file). Used to render a local page in-window, network-independent.
177func read_local(fname: *u8, outbuf: *u8, cap: i64, a: *i64) -> i64 {
178 a[0]=fname as i64; a[1]=0x80000000; a[2]=1; a[3]=0; a[4]=3; a[5]=0x80; a[6]=0
179 let hin: i64 = win32(W_CreateFileA, a)
180 if hin == (0 - 1) { return 0 }
181 let got: *u8 = sys_mmap(64)
182 var total: i64 = 0
183 var go: i64 = 1
184 while go == 1 {
185 w32(got, 0, 0)
186 a[0]=hin; a[1]=(outbuf as i64)+total; a[2]=cap-total; a[3]=got as i64; a[4]=0
187 win32(W_ReadFile, a)
188 let n: i64 = rd32(got, 0)
189 if n <= 0 { go = 0 } else { total = total + n }
190 if total >= cap { go = 0 }
191 }
192 a[0]=hin; win32(W_CloseHandle, a)
193 return total
194}
195
196// title = first line; body = the rest. out[0]=title len, out[1]=body offset.
197func split_page(pbuf: *u8, plen: i64, out: *i64) -> i64 {
198 var tl: i64 = 0
199 var d: i64 = 0
200 while d == 0 {
201 if tl >= plen { d = 1 }
202 else { let c: i64 = pbuf[tl]&0xff; if c == 0x0a { d=1 } else { if c == 0x0d { d=1 } else { tl=tl+1 } } }
203 }
204 var bs: i64 = tl
205 if bs < plen { if (pbuf[bs]&0xff) == 0x0d { bs = bs + 1 } }
206 if bs < plen { if (pbuf[bs]&0xff) == 0x0a { bs = bs + 1 } }
207 out[0] = tl
208 out[1] = bs
209 return 0
210}
211
212// Resolve a (possibly relative) href against the current page URL -> absolute, into out. Returns len.
213func resolve_url(raw: *u8, rawlen: i64, cur: *u8, curlen: i64, out: *u8) -> i64 {
214 if rawlen >= 4 { if (raw[0]&0xff)==0x68 { if (raw[1]&0xff)==0x74 { if (raw[2]&0xff)==0x74 { if (raw[3]&0xff)==0x70 {
215 var i: i64 = 0
216 while i < rawlen { out[i]=raw[i]; i=i+1 }
217 return rawlen // already absolute http(s)
218 } } } } }
219 if rawlen >= 2 { if (raw[0]&0xff)==0x2f { if (raw[1]&0xff)==0x2f {
220 var o: i64 = scopy(out, 0, "https:" as *u8)
221 var i: i64 = 0
222 while i < rawlen { out[o+i]=raw[i]; i=i+1 }
223 return o + rawlen // //host/... -> https://host/...
224 } } }
225 if rawlen >= 1 { if (raw[0]&0xff)==0x2f { // /abs/path -> scheme://host + raw
226 var slashes: i64 = 0
227 var k: i64 = 0
228 var hostend: i64 = curlen
229 var dn: i64 = 0
230 while dn == 0 {
231 if k >= curlen { dn = 1 } else { if (cur[k]&0xff)==0x2f { slashes=slashes+1; if slashes==3 { hostend=k; dn=1 } } k=k+1 }
232 }
233 var o: i64 = 0
234 while o < hostend { out[o]=cur[o]; o=o+1 }
235 var i: i64 = 0
236 while i < rawlen { out[hostend+i]=raw[i]; i=i+1 }
237 return hostend + rawlen
238 } }
239 // other relative -> base directory of cur (through last '/') + raw
240 var lastslash: i64 = 0
241 var k2: i64 = 0
242 while k2 < curlen { if (cur[k2]&0xff)==0x2f { lastslash=k2 } k2=k2+1 }
243 var o2: i64 = 0
244 while o2 <= lastslash { out[o2]=cur[o2]; o2=o2+1 }
245 var i2: i64 = 0
246 while i2 < rawlen { out[lastslash+1+i2]=raw[i2]; i2=i2+1 }
247 return lastslash + 1 + rawlen
248}
249
250// R4: fetch a binary resource (image) via nishi_fetch -B into a temp file. Trimmed fetch_url: -B = raw
251// bytes (no HTML/charset mangle), own temp _offc/_img.bin so it doesn't clobber the page. 1 on spawn OK.
252func fetch_image(url: *u8, ulen: i64, a: *i64, cmd: *u8) -> i64 {
253 var cl: i64 = scopy(cmd, 0, "_offc/nishi_fetch.exe -B" as *u8)
254 var i: i64 = 0
255 while i < ulen { cmd[cl+i] = url[i]; i = i + 1 }
256 cmd[cl+ulen] = 0 as u8
257 let app: *u8 = "_offc/nishi_fetch.exe" as *u8
258 let fname: *u8 = "_offc/_img.bin" as *u8
259 let sa: *u8 = sys_mmap(64)
260 w32(sa, 0, 24); wp(sa, 8, 0); w32(sa, 16, 1)
261 a[0]=fname as i64; a[1]=0x40000000; a[2]=1; a[3]=sa as i64; a[4]=2; a[5]=0x80; a[6]=0
262 let hout: i64 = win32(W_CreateFileA, a)
263 if hout == (0 - 1) { return 0 }
264 let si: *u8 = sys_mmap(128)
265 w32(si, 0, 104); w32(si, 60, 0x100); wp(si, 80, 0); wp(si, 88, hout); wp(si, 96, 0)
266 let pi: *u8 = sys_mmap(64)
267 a[0]=app as i64; a[1]=cmd as i64; a[2]=0; a[3]=0; a[4]=1; a[5]=0x08000000; a[6]=0; a[7]=0; a[8]=si as i64; a[9]=pi as i64
268 win32(W_CreateProcessA, a)
269 let hproc: i64 = rp(pi, 0)
270 if hproc == 0 { a[0]=hout; win32(W_CloseHandle, a); return 0 }
271 let fmsg: *u8 = sys_mmap(64)
272 var fw: i64 = 1
273 while fw == 1 {
274 var pd: i64 = 1
275 while pd == 1 {
276 a[0]=fmsg as i64; a[1]=0; a[2]=0; a[3]=0; a[4]=1
277 if win32(W_PeekMessageA, a) != 0 { win32(W_TranslateMessage, a) } else { pd = 0 }
278 }
279 a[0]=hproc; a[1]=50
280 if win32(W_WaitForSingleObject, a) == 0 { fw = 0 }
281 }
282 a[0]=hproc; win32(W_CloseHandle, a)
283 a[0]=rp(pi,8); win32(W_CloseHandle, a)
284 a[0]=hout; win32(W_CloseHandle, a)
285 return 1
286}
287
288// R4: after br_layout, for each box carrying an <img src>, resolve + fetch (-B) + decode + stash the RGB
289// into page.bimg so br_draw_fb's image pass blits it. Bounded to a few fetches (each spawn is ~seconds).
290func load_images(page: *Page, baseurl: *u8, baselen: i64, a: *i64, cmd: *u8, tmpurl: *u8, imgbuf: *u8, imgcap: i64) -> i64 {
291 let tree: *LayoutTree = page.tree
292 var done: i64 = 0
293 var tries: i64 = 0
294 var i: i64 = 0
295 while i < tree.count {
296 var go: i64 = 0
297 if done < 4 { if tries < 6 { if page.bsrc_off[i] != (0 - 1) { go = 1 } } }
298 if go == 1 {
299 let so: i64 = page.bsrc_off[i]
300 let sl: i64 = page.bsrc_len[i]
301 // Skip srcs we CAN'T decode -- SVG (src ends ".svg") + data: URIs -- so the small fetch budget
302 // reaches the real JPEG/PNG content images (Wikipedia's first <img>s are all icon SVGs). Note
303 // ".svg.png" thumbnails end in ".png", so they are NOT skipped -- they ARE decodable PNGs.
304 var skip: i64 = 0
305 if sl >= 4 { if (page.buf[so+sl-4]&0xff)==46 { if (page.buf[so+sl-3]&0xff)==115 { if (page.buf[so+sl-2]&0xff)==118 { if (page.buf[so+sl-1]&0xff)==103 { skip = 1 } } } } }
306 if sl >= 5 { if (page.buf[so]&0xff)==100 { if (page.buf[so+1]&0xff)==97 { if (page.buf[so+2]&0xff)==116 { if (page.buf[so+3]&0xff)==97 { if (page.buf[so+4]&0xff)==58 { skip = 1 } } } } } }
307 if skip == 0 {
308 tries = tries + 1
309 let srcbuf: *u8 = sys_mmap(sl + 2)
310 var j: i64 = 0
311 while j < sl { srcbuf[j] = page.buf[so + j]; j = j + 1 }
312 srcbuf[sl] = 0 as u8
313 let al: i64 = resolve_url(srcbuf, sl, baseurl, baselen, tmpurl)
314 if al > 0 {
315 tmpurl[al] = 0 as u8
316 if fetch_image(tmpurl, al, a, cmd) == 1 {
317 let blen: i64 = read_local("_offc/_img.bin" as *u8, imgbuf, imgcap, a)
318 if blen > 8 {
319 let wh: *i64 = sys_mmap(16) as *i64
320 let rgb: *u8 = nx_img_bytes_to_rgb(imgbuf, blen, wh)
321 if (rgb as i64) != 0 {
322 page.bimg[i] = rgb as i64
323 page.bimg_w[i] = wh[0]
324 page.bimg_h[i] = wh[1]
325 done = done + 1
326 }
327 }
328 }
329 }
330 }
331 }
332 i = i + 1
333 }
334 return done
335}
336
337// strip inline link markers (STX href SOH ... ETX) -> clean text into out. Returns out_len.
338func strip_markers(src: *u8, len: i64, out: *u8) -> i64 {
339 var i: i64 = 0
340 var o: i64 = 0
341 while i < len {
342 let c: i64 = src[i]&0xff
343 if c == 0x02 {
344 var u: i64 = i + 1
345 var f: i64 = 0
346 while f == 0 { if u >= len { f=1 } else { if (src[u]&0xff)==0x01 { f=1 } else { u=u+1 } } }
347 i = u + 1
348 } else { if c == 0x03 { i=i+1 } else { if c == 0x01 { i=i+1 } else { out[o]=src[i]; o=o+1; i=i+1 } } }
349 }
350 return o
351}
352
353// record one clickable rect (32B: x0@0,y0@4,x1@8,y1@12, urlPtr@16, urlLen@24) in display coords.
354func rec_link(linktab: *u8, lcount: *i64, x0: i64, y0: i64, x1: i64, y1: i64, uptr: i64, ulen: i64) -> i64 {
355 if lcount[0] >= LINKMAX { return 0 }
356 if x1 <= x0 { return 0 }
357 let e: i64 = lcount[0] * 32
358 w32(linktab, e+0, x0); w32(linktab, e+4, y0); w32(linktab, e+8, x1); w32(linktab, e+12, y1)
359 wp (linktab, e+16, uptr); w32(linktab, e+24, ulen)
360 lcount[0] = lcount[0] + 1
361 return 0
362}
363
364// Lay body text into [ytop,maxy], char-wrapped + scrolled. Inline link markers (STX href SOH anchor
365// ETX) render the anchor BLUE and record per-row clickable rects (32B/entry) for x/y hit-testing.
366// Fragment (#...) links render as plain text (not clickable). lcount[0] = recorded visible rects.
367func draw_body(fb: *u8, W: i64, ft: *u8, x0: i64, ytop: i64, colw: i64, lineh: i64, maxy: i64,
368 text: *u8, len: i64, sc: i64, scroll_px: i64, linktab: *u8, lcount: *i64) -> i64 {
369 let cw: i64 = 9 * sc
370 var x: i64 = x0
371 var y: i64 = ytop - scroll_px
372 var i: i64 = 0
373 lcount[0] = 0
374 var link_on: i64 = 0 // 0=none, 1=clickable link, 2=fragment (rendered plain, not clickable)
375 var luptr: i64 = 0
376 var lulen: i64 = 0
377 var rowx0: i64 = 0
378 while i < len {
379 let c: i64 = text[i]&0xff
380 if c == 0x02 { // STX: begin link, capture raw href
381 var u: i64 = i + 1
382 var fu: i64 = 0
383 while fu == 0 { if u >= len { fu=1 } else { if (text[u]&0xff)==0x01 { fu=1 } else { u=u+1 } } }
384 luptr = (text as i64) + (i + 1)
385 lulen = u - (i + 1)
386 i = u + 1
387 if lulen > 0 {
388 let lpc: *u8 = luptr as *u8
389 if (lpc[0]&0xff)==0x23 { link_on = 2 } else { link_on = 1 }
390 } else { link_on = 0 }
391 rowx0 = x
392 } else {
393 if c == 0x03 { // ETX: end link -> record this row's rect
394 if link_on == 1 { if y >= ytop { if y <= maxy { rec_link(linktab, lcount, rowx0/2, y/2, x/2, (y+lineh)/2, luptr, lulen) } } }
395 link_on = 0
396 i = i + 1
397 } else {
398 if c == 0x01 { i = i + 1 } // stray SOH -> skip
399 else {
400 if c >= 0x80 { i = i + 1 }
401 else {
402 if c == 0x0a {
403 if link_on == 1 { if y >= ytop { if y <= maxy { rec_link(linktab, lcount, rowx0/2, y/2, x/2, (y+lineh)/2, luptr, lulen) } } }
404 x = x0; y = y + lineh
405 if link_on != 0 { rowx0 = x }
406 i = i + 1
407 } else {
408 if c != 0x0d {
409 if (x + cw) > (x0 + colw) { // wrap -> close this row's link rect, start a new row
410 if link_on == 1 { if y >= ytop { if y <= maxy { rec_link(linktab, lcount, rowx0/2, y/2, x/2, (y+lineh)/2, luptr, lulen) } } }
411 x = x0; y = y + lineh
412 if link_on != 0 { rowx0 = x }
413 }
414 if c != 0x20 { if y >= ytop { if y <= maxy {
415 if link_on == 1 { draw_char(fb, W, ft, c, x, y, sc, 225, 115, 30) }
416 else { draw_char(fb, W, ft, c, x, y, sc, 55, 55, 58) }
417 } } }
418 x = x + cw
419 }
420 i = i + 1
421 }
422 }
423 }
424 }
425 }
426 if y > maxy { i = len }
427 }
428 return 0
429}
430
431// box-downsample the 2x render buffer fb2 (RWxRH) into the display fb (WxH) -> anti-aliased.
432func downsample(fb: *u8, W: i64, H: i64, fb2: *u8, RW: i64) -> i64 {
433 var y: i64 = 0
434 while y < H {
435 var x: i64 = 0
436 while x < W {
437 let s: i64 = ((2*y) * RW + (2*x)) * 4
438 let s2: i64 = ((2*y+1) * RW + (2*x)) * 4
439 let b: i64 = ((fb2[s]&0xff) + (fb2[s+4]&0xff) + (fb2[s2]&0xff) + (fb2[s2+4]&0xff)) >> 2
440 let g: i64 = ((fb2[s+1]&0xff) + (fb2[s+5]&0xff) + (fb2[s2+1]&0xff) + (fb2[s2+5]&0xff)) >> 2
441 let r: i64 = ((fb2[s+2]&0xff) + (fb2[s+6]&0xff) + (fb2[s2+2]&0xff) + (fb2[s2+6]&0xff)) >> 2
442 let d: i64 = (y * W + x) * 4
443 fb[d]=r as u8; fb[d+1]=g as u8; fb[d+2]=b as u8; fb[d+3]=0 as u8 // R3.1: src fb2 is RGBA -> emit BGRA (DIB order)
444 x = x + 1
445 }
446 y = y + 1
447 }
448 return 0
449}
450
451// static chrome at 2x render res: toolbar, address bar (current URL), logo, page title, footer.
452func draw_chrome(fb2: *u8, RW: i64, RH: i64, ft: *u8, tptr: *u8, tlen: i64, urlptr: *u8, urllen: i64) -> i64 {
453 fill_rect(fb2, RW, 0, 0, RW, RH, 252, 252, 252)
454 fill_rect(fb2, RW, 0, 0, RW, 100, 246, 245, 242) // toolbar
455 fill_rect(fb2, RW, 0, 100, RW, 3, 210, 210, 214) // toolbar border
456 fill_rect(fb2, RW, 24, 26, 52, 48, 230, 230, 232) // back/fwd buttons
457 fill_rect(fb2, RW, 92, 26, 52, 48, 230, 230, 232)
458 draw_text(fb2, RW, ft, 40, 36, "<" as *u8, 3, 90, 90, 95)
459 draw_text(fb2, RW, ft, 108, 36, ">" as *u8, 3, 90, 90, 95)
460 fill_rect(fb2, RW, 244, 22, RW - 560, 56, 218, 218, 222) // address bar border
461 fill_rect(fb2, RW, 248, 26, RW - 568, 48, 255, 255, 255) // address field
462 fill_rect(fb2, RW, RW - 80, 26, 52, 48, 210, 110, 40) // logo
463 draw_text(fb2, RW, ft, RW - 72, 34, "n" as *u8, 4, 245, 235, 220)
464 var ashow: i64 = urllen
465 if ashow > 62 { ashow = 62 }
466 draw_span(fb2, RW, ft, 268, 38, urlptr, 0, ashow, 3, 90, 90, 95)
467 // page title heading (centered-left in the reading column)
468 let mL: i64 = (RW - 1480) / 2
469 var tshow: i64 = tlen
470 if tshow > 33 { tshow = 33 }
471 draw_span(fb2, RW, ft, mL, 130, tptr, 0, tshow, 6, 25, 25, 30)
472 fill_rect(fb2, RW, mL, 206, 1480, 3, 225, 225, 228) // hairline under title
473 // footer
474 fill_rect(fb2, RW, 0, RH - 56, RW, 56, 248, 247, 245)
475 draw_text(fb2, RW, ft, 40, RH - 44, "Nishi - click a blue link to navigate - Backspace = back - PgDn/PgUp/wheel scroll" as *u8, 2, 150, 150, 150)
476 return 0
477}
478
479func render_content(fb2: *u8, RW: i64, RH: i64, ft: *u8, body: *u8, blen: i64, scroll_px: i64, linktab: *u8, lcount: *i64) -> i64 {
480 let mL: i64 = (RW - 1480) / 2
481 fill_rect(fb2, RW, 0, 222, RW, RH - 222 - 60, 252, 252, 252) // clear body viewport
482 draw_body(fb2, RW, ft, mL, 238, 1480, 44, RH - 70, body, blen, 4, scroll_px, linktab, lcount)
483 return 0
484}
485
486// Paint a "Loading" banner straight to the display + blit it, so a click visibly
487// registers before the (blocking) in-process fetch runs.
488func show_loading(a: *i64, hwnd: i64, bmi: *u8, fb: *u8, W: i64, H: i64, ft: *u8, url: *u8, ulen: i64) -> i64 {
489 fill_rect(fb, W, 0, 104, W, 36, 210, 110, 40) // orange progress bar under the toolbar
490 fill_rect(fb, W, 0, 140, W, 28, 246, 245, 242)
491 draw_text(fb, W, ft, 16, 114, "Loading" as *u8, 2, 255, 255, 255)
492 var n: i64 = ulen
493 if n > 110 { n = 110 }
494 draw_span(fb, W, ft, 110, 146, url, 0, n, 1, 120, 120, 125)
495 a[0]=hwnd
496 let hdc: i64 = win32(W_GetDC, a)
497 a[0]=hdc; a[1]=0; a[2]=0; a[3]=W; a[4]=H; a[5]=0; a[6]=0; a[7]=0; a[8]=H; a[9]=fb as i64; a[10]=bmi as i64; a[11]=0
498 win32(W_SetDIBitsToDevice, a)
499 a[0]=hwnd; a[1]=hdc; win32(W_ReleaseDC, a)
500 return 0
501}
502
503// Draw the address field directly to the display buffer while it is being typed (with a caret).
504// Display coords: the chrome's address field is render (248,26,1480,48) -> display (124,13,740,24).
505func draw_addrbar_edit(fb: *u8, W: i64, ft: *u8, ed: *u8, edlen: i64) -> i64 {
506 fill_rect(fb, W, 124, 13, 740, 24, 255, 255, 255) // clear the field
507 var shown: i64 = edlen
508 if shown > 40 { shown = 40 } // keep the tail (caret) visible
509 let endx: i64 = draw_span(fb, W, ft, 132, 16, ed, edlen - shown, shown, 2, 40, 40, 45)
510 fill_rect(fb, W, endx, 15, 2, 20, 50, 50, 60) // caret
511 return 0
512}
513
514func hist_push(histbuf: *u8, hoff: *i64, hlen: *i64, histn: i64, hcur: i64, url: *u8, ulen: i64) -> i64 {
515 hoff[histn] = hcur
516 var k: i64 = 0
517 while k < ulen { histbuf[hcur+k] = url[k]; k = k + 1 }
518 hlen[histn] = ulen
519 return hcur + ulen
520}
521
522// R3: convert an RGBA framebuffer (nx_paint order px[0]=R, what br_draw_fb writes) to the display's
523// BGRA (Windows 32bpp DIB byte order), n pixels. The one bridge between the sovereign paint + the DIB.
524func rgba_to_bgra(dst: *u8, src: *u8, n: i64) -> i64 {
525 var i: i64 = 0
526 while i < n {
527 let s: i64 = i * 4
528 dst[s+0] = src[s+2] // B
529 dst[s+1] = src[s+1] // G
530 dst[s+2] = src[s+0] // R
531 dst[s+3] = 0 as u8
532 i = i + 1
533 }
534 return 0
535}
536// R3: render the laid-out page into the display fb via the shared CSS paint organ (br_draw_fb -> RGBA fb2),
537// then bridge RGBA->BGRA into the DIB fb that the message loop blits.
538func paint_css(page: *Page, fbs: *Framebuffer, fb: *u8, fb2: *u8, W: i64, H: i64, url: *u8, ulen: i64, scroll_px: i64) -> i64 {
539 br_set_fast_vec(1) // glyph-atlas fast path: 17ms warm -> full body vector is affordable
540 page.vec_headings = 2 // professional PROPORTIONAL vector typography THROUGHOUT (headings + body), generative pioneer face
541 br_draw_fb_at(fbs, page, W, url, ulen, 1, scroll_px) // honor the scroll offset (was br_draw_fb -> scroll_y=0, i.e. no scrolling)
542 // vertical SCROLLBAR on the right edge when the page overflows the viewport (chrome-fixed, page-space thumb)
543 let vp: i64 = H - NX_CHROME_H
544 if page.page_h > vp { if vp > 0 {
545 nx_paint_solid_rect(fbs, W - 10, NX_CHROME_H, 10, vp, br_color_from_int(0x00EDEFF2)) // track
546 var th: i64 = vp * vp / page.page_h
547 if th < 28 { th = 28 }
548 if th > vp { th = vp }
549 var ty: i64 = NX_CHROME_H
550 let denom: i64 = page.page_h - vp
551 if denom > 0 { ty = NX_CHROME_H + scroll_px * (vp - th) / denom }
552 nx_paint_solid_rect(fbs, W - 9, ty, 8, th, br_color_from_int(0x00A8AEB8)) // thumb
553 } }
554 rgba_to_bgra(fb, fb2, W * H)
555 return 0
556}
557
558func gappend(buf: *u8, n: i64, val: i64) -> i64 {
559 var m: i64 = val
560 if m < 0 { buf[n] = 45 as u8; n = n + 1; m = 0 - m }
561 if m == 0 { buf[n] = 48 as u8; buf[n + 1] = 32 as u8; return n + 2 }
562 let t: *u8 = sys_mmap(24)
563 var k: i64 = 0
564 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
565 var j: i64 = 0
566 while j < k { buf[n + j] = t[k - 1 - j]; j = j + 1 }
567 buf[n + k] = 32 as u8
568 return n + k + 1
569}
570func gwrite(a: *i64, buf: *u8, n: i64) -> i64 {
571 let lp: *u8 = "_offc/gui.log\x00" as *u8
572 a[0] = lp as i64; a[1] = 0x40000000; a[2] = 1; a[3] = 0; a[4] = 2; a[5] = 0x80; a[6] = 0
573 let h: i64 = win32(W_CreateFileA, a)
574 if h == (0 - 1) { return 0 }
575 let got: *u8 = sys_mmap(64)
576 a[0] = h; a[1] = buf as i64; a[2] = n; a[3] = got as i64; a[4] = 0
577 win32(W_WriteFile, a)
578 a[0] = h; win32(W_CloseHandle, a)
579 return 0
580}
581func gmark(a: *i64, buf: *u8, val: i64) -> i64 {
582 let n: i64 = gappend(buf, 0, val)
583 gwrite(a, buf, n)
584 return 0
585}
586func main() -> i64 {
587 var W: i64 = 1024 // mutable: live resize updates these to the new client size
588 var H: i64 = 720
589 let RW: i64 = 2048 // max render/display buffer size (resize clamps to this)
590 let RH: i64 = 1440
591 let a: *i64 = sys_mmap(2048) as *i64
592 let glogbuf: *u8 = sys_mmap(262144)
593 var glogn: i64 = 0
594
595 a[0] = 0
596 let hInst: i64 = win32(W_GetModuleHandleA, a)
597 a[0] = 0; a[1] = 32512
598 let hCur: i64 = win32(W_LoadCursorA, a)
599
600 let cls: *u8 = "nishigui" as *u8
601 let title: *u8 = "Nishi - sovereign browser (native Windows, hardware-up)" as *u8
602
603 let wc: *u8 = sys_mmap(80)
604 w32(wc, 0, 80); w32(wc, 4, 0)
605 wp (wc, 8, win32addr(W_DefWindowProcA))
606 w32(wc, 16, 0); w32(wc, 20, 0)
607 wp (wc, 24, hInst); wp (wc, 32, 0); wp (wc, 40, hCur); wp (wc, 48, 0); wp (wc, 56, 0)
608 wp (wc, 64, cls as i64); wp (wc, 72, 0)
609 a[0] = wc as i64
610 win32(W_RegisterClassExA, a)
611
612 a[0]=0; a[1]=cls as i64; a[2]=title as i64; a[3]=0x10CF0000
613 a[4]=70; a[5]=50; a[6]=W; a[7]=H; a[8]=0; a[9]=0; a[10]=hInst; a[11]=0
614 let hwnd: i64 = win32(W_CreateWindowExA, a)
615 if hwnd == 0 { sys_exit(1); return 1 }
616 a[0]=hwnd; a[1]=5
617 win32(W_ShowWindow, a)
618
619 // Establish the window: pump its show/paint/activate messages BEFORE the ~4s blocking initial fetch,
620 // so Windows does not tear down a never-pumped window (the render-then-close-after-~1.7s bug). WM_CREATE
621 // already ran synchronously inside CreateWindowExA; this drains WM_SHOWWINDOW/WM_PAINT/WM_ACTIVATE.
622 let msg0: *u8 = sys_mmap(64)
623 var pw0: i64 = 0
624 while pw0 < 50 {
625 a[0]=msg0 as i64; a[1]=0; a[2]=0; a[3]=0; a[4]=1
626 if win32(W_PeekMessageA, a) != 0 { win32(W_TranslateMessage, a); win32(W_DispatchMessageA, a) }
627 pw0 = pw0 + 1
628 }
629
630 let fb2: *u8 = sys_mmap(RW * RH * 4)
631 let fb: *u8 = sys_mmap(RW * RH * 4) // max-size display buffer so live resize can grow up to RW x RH
632 let rectbuf: *u8 = sys_mmap(16) // GetClientRect output (left,top,right,bottom)
633 let ft: *u8 = font8x8_table()
634 // R3: the CSS page + an RGBA framebuffer view over fb2's buffer (at display res W x H) that br_draw_fb paints into.
635 let page: *Page = sys_mmap(NX_PAGE_BYTES) as *Page
636 let fbs: *Framebuffer = sys_mmap(NX_FRAMEBUFFER_BYTES) as *Framebuffer
637 nx_framebuffer_init(fbs, fb2, W, H) // 1:1 framebuffer view over fb2 (br_draw_fb paints here at scale=1)
638
639 let pcap: i64 = 2097152
640 let pbuf: *u8 = sys_mmap(pcap)
641 let cmd: *u8 = sys_mmap(2048)
642 let urlbuf: *u8 = sys_mmap(2048)
643 let tmpurl: *u8 = sys_mmap(2048)
644 let titlebuf: *u8 = sys_mmap(4096)
645 let linktab: *u8 = sys_mmap(LINKMAX * 32)
646 let lcount: *i64 = sys_mmap(16) as *i64
647 let sp: *i64 = sys_mmap(16) as *i64
648 let imgbuf: *u8 = sys_mmap(4194304) // R4: scratch for a fetched image's bytes (read_local -> decode)
649
650 let histbuf: *u8 = sys_mmap(131072)
651 let hoff: *i64 = sys_mmap(1024) as *i64
652 let hlen: *i64 = sys_mmap(1024) as *i64
653 var histn: i64 = 0
654 var hcur: i64 = 0
655
656 // initial page: fetch the start URL LIVE via the (now DNS-fixed) sovereign fetcher. read_local() stays
657 // available for a future offline home page; live browsing is the proof now.
658 var curlen: i64 = scopy(urlbuf, 0, "https://en.wikipedia.org/wiki/Web_browser" as *u8)
659 urlbuf[curlen] = 0 as u8
660 var plen: i64 = fetch_url(urlbuf, curlen, pbuf, pcap, a, cmd)
661 hcur = hist_push(histbuf, hoff, hlen, histn, hcur, urlbuf, curlen); histn = histn + 1
662 split_page(pbuf, plen, sp)
663 var body: *u8 = ((pbuf as i64) + sp[1]) as *u8
664 var blen: i64 = plen - sp[1]
665 var scroll_px: i64 = 0
666 var editing: i64 = 0 // address bar being typed?
667 let editbuf: *u8 = sys_mmap(2048)
668 var editlen: i64 = 0
669
670 let bmi: *u8 = sys_mmap(40)
671 w32(bmi,0,40); w32(bmi,4,W); w32(bmi,8, 0 - H)
672 w16(bmi,12,1); w16(bmi,14,32); w32(bmi,16,0)
673
674 page.raw = pbuf; page.raw_len = plen // R3: lay out + paint the REAL page via the CSS organ
675 br_layout(page, W)
676 load_images(page, urlbuf, curlen, a, cmd, tmpurl, imgbuf, 4194304) // R4: fetch+decode <img> -> page.bimg (VirtualAlloc-backed)
677 paint_css(page, fbs, fb, fb2, W, H, urlbuf, curlen, scroll_px)
678
679 let msg: *u8 = sys_mmap(64)
680 var running: i64 = 1
681 while running == 1 {
682 var need: i64 = 0 // re-render body (scroll)
683 var nav: i64 = 0 // navigate: re-fetch + full re-render
684 var full: i64 = 0 // re-render chrome+body without fetching (exit address-bar edit)
685 var edit_dirty: i64 = 0 // address-bar text changed -> redraw the field
686 var do_back: i64 = 0 // go back one history entry
687 var pumping: i64 = 1
688 while pumping == 1 { // drain the whole message queue each frame
689 a[0]=msg as i64; a[1]=0; a[2]=0; a[3]=0; a[4]=1
690 let got: i64 = win32(W_PeekMessageA, a)
691 if got == 0 { pumping = 0 } else {
692 let m: i64 = rd32(msg, 8)
693 if m == 0x100 {
694 let vk: i64 = rd32(msg, 16) & 0xffff
695 if editing == 1 {
696 if vk == 0x0D { // Enter -> go to typed URL
697 var pre: i64 = 0
698 if editlen >= 4 { if (editbuf[0]&0xff)==0x68 { if (editbuf[1]&0xff)==0x74 { if (editbuf[2]&0xff)==0x74 { if (editbuf[3]&0xff)==0x70 { pre = 1 } } } } }
699 var ul2: i64 = 0
700 if pre == 0 { ul2 = scopy(urlbuf, 0, "https://" as *u8) }
701 var z3: i64 = 0
702 while z3 < editlen { urlbuf[ul2+z3] = editbuf[z3]; z3 = z3 + 1 }
703 ul2 = ul2 + editlen
704 urlbuf[ul2] = 0 as u8; curlen = ul2
705 editing = 0
706 show_loading(a, hwnd, bmi, fb, W, H, ft, urlbuf, ul2)
707 let np3: i64 = fetch_url(urlbuf, curlen, pbuf, pcap, a, cmd)
708 if np3 > 0 { plen = np3; hcur = hist_push(histbuf, hoff, hlen, histn, hcur, urlbuf, curlen); histn = histn + 1; nav = 1 } else { full = 1 }
709 }
710 if vk == 0x08 { if editlen > 0 { editlen = editlen - 1 } edit_dirty = 1 } // Backspace deletes
711 if vk == 0x1B { editing = 0; full = 1 } // Escape cancels
712 } else {
713 if vk == 0x22 { scroll_px = scroll_px + 560; need = 1 } // PgDn
714 if vk == 0x21 { scroll_px = scroll_px - 560; need = 1 } // PgUp
715 if vk == 0x28 { scroll_px = scroll_px + 132; need = 1 } // Down
716 if vk == 0x26 { scroll_px = scroll_px - 132; need = 1 } // Up
717 if vk == 0x1B { running = 0 } // Escape -> clean quit (X-button/drag = DefWindowProc follow-up)
718 if vk == 0x20 { scroll_px = scroll_px + 560; need = 1 } // Space
719 if vk == 0x24 { scroll_px = 0; need = 1 } // Home
720 if vk == 0x08 { do_back = 1 } // Backspace = back
721 }
722 }
723 if m == 0x102 { // WM_CHAR: address-bar typing
724 if editing == 1 {
725 let ch: i64 = rd32(msg, 16) & 0xff
726 if ch >= 0x20 { if ch < 0x7f { if editlen < 2040 { editbuf[editlen] = ch as u8; editlen = editlen + 1; edit_dirty = 1 } } }
727 }
728 }
729 if m == 0x20A { // mouse wheel
730 let dd: i64 = (rd32(msg, 16) >> 16) & 0xffff
731 if dd > 0x7fff { scroll_px = scroll_px + 132 } else { scroll_px = scroll_px - 132 }
732 need = 1
733 }
734 if m == 0x201 { // left button down
735 let mx: i64 = rd32(msg, 24) & 0xffff
736 let my: i64 = (rd32(msg, 24) >> 16) & 0xffff
737 var consumed: i64 = 0
738 if my >= 13 { if my <= 37 { if mx >= 124 { if mx <= 864 { // click address bar -> edit
739 editing = 1
740 var z4: i64 = 0
741 while z4 < curlen { editbuf[z4] = urlbuf[z4]; z4 = z4 + 1 }
742 editlen = curlen
743 edit_dirty = 1; consumed = 1
744 } } } }
745 if consumed == 0 { if my >= 13 { if my <= 37 { if mx >= 12 { if mx <= 38 { do_back = 1; consumed = 1 } } } } } // < back button
746 if consumed == 0 {
747 if editing == 1 { editing = 0; full = 1 } // click in page -> leave edit mode
748 // R3: hit-test the click against the LAYOUT's href boxes. br_draw_fb paints a box at
749 // b.y + NX_CHROME_H at 1:1, so doc coords = (mx, my - NX_CHROME_H).
750 let choff: *i64 = sys_mmap(8) as *i64
751 let chlen: *i64 = sys_mmap(8) as *i64
752 if br_hit_link(page, mx, my - NX_CHROME_H, choff, chlen) == 1 {
753 let rawp: i64 = (page.buf as i64) + choff[0]
754 let rawl: i64 = chlen[0]
755 let nl: i64 = resolve_url(rawp as *u8, rawl, urlbuf, curlen, tmpurl) // relative href -> absolute
756 var z2: i64 = 0
757 while z2 < nl { urlbuf[z2] = tmpurl[z2]; z2 = z2 + 1 }
758 urlbuf[nl] = 0 as u8; curlen = nl
759 show_loading(a, hwnd, bmi, fb, W, H, ft, urlbuf, nl)
760 let np2: i64 = fetch_url(urlbuf, curlen, pbuf, pcap, a, cmd)
761 if np2 > 0 {
762 plen = np2
763 hcur = hist_push(histbuf, hoff, hlen, histn, hcur, urlbuf, curlen); histn = histn + 1
764 nav = 1
765 }
766 }
767 }
768 }
769 a[0]=msg as i64; win32(W_TranslateMessage, a)
770 // DispatchMessage lets DefWindowProc run the standard NON-CLIENT behavior -- title-bar DRAG,
771 // the MIN/MAX/RESTORE buttons, and RESIZE borders. Skipping it (the old stability hack) is
772 // exactly why the window couldn't be moved/minimized/closed. We handle WM_CLOSE (0x10, the X
773 // button / Alt-F4) OURSELVES for a clean quit instead of letting DefWindowProc DestroyWindow
774 // out from under the render loop; every OTHER message is dispatched so the controls work.
775 if m == 0x10 { running = 0 } else { a[0]=msg as i64; win32(W_DispatchMessageA, a) }
776 } }
777 // LIVE RESIZE: WM_SIZE is SENT to the wndproc (which this PeekMessage loop bypasses), so poll the
778 // client size each frame instead -- catches maximize / drag-resize / half-screen snap -- and re-flow
779 // the page to the new width. Clamped to the RWxRH buffer; minimized (0x0) is ignored.
780 a[0]=hwnd; a[1]=rectbuf as i64; win32(W_GetClientRect, a)
781 let cw: i64 = rd32(rectbuf, 8)
782 let ch: i64 = rd32(rectbuf, 12)
783 if cw >= 300 { if ch >= 200 {
784 var rsz: i64 = 0
785 if cw != W { rsz = 1 }
786 if ch != H { rsz = 1 }
787 if rsz == 1 {
788 var nw: i64 = cw
789 var nh: i64 = ch
790 if nw > RW { nw = RW }
791 if nh > RH { nh = RH }
792 W = nw; H = nh
793 nx_framebuffer_init(fbs, fb2, W, H) // re-view fb2 at the new W x H
794 w32(bmi, 4, W); w32(bmi, 8, 0 - H) // BITMAPINFO width / top-down height for the blit
795 br_layout(page, W) // re-flow the page to the new width
796 full = 1 // full re-paint this frame
797 }
798 } }
799 if do_back == 1 {
800 if histn >= 2 {
801 histn = histn - 1
802 let bo: i64 = hoff[histn-1]
803 let bl: i64 = hlen[histn-1]
804 var z5: i64 = 0
805 while z5 < bl { urlbuf[z5] = histbuf[bo+z5]; z5 = z5 + 1 }
806 urlbuf[bl] = 0 as u8; curlen = bl
807 show_loading(a, hwnd, bmi, fb, W, H, ft, urlbuf, bl)
808 let npb: i64 = fetch_url(urlbuf, curlen, pbuf, pcap, a, cmd)
809 if npb > 0 { plen = npb; nav = 1 }
810 }
811 }
812 if nav == 1 {
813 page.raw = pbuf; page.raw_len = plen
814 br_layout(page, W)
815 scroll_px = 0
816 paint_css(page, fbs, fb, fb2, W, H, urlbuf, curlen, scroll_px)
817 } else {
818 if full == 1 {
819 paint_css(page, fbs, fb, fb2, W, H, urlbuf, curlen, scroll_px)
820 }
821 }
822 if scroll_px < 0 { scroll_px = 0 }
823 var maxsc: i64 = page.page_h - (H - NX_CHROME_H)
824 if maxsc < 0 { maxsc = 0 }
825 if scroll_px > maxsc { scroll_px = maxsc }
826 if need == 1 { paint_css(page, fbs, fb, fb2, W, H, urlbuf, curlen, scroll_px) }
827 if edit_dirty == 1 { draw_addrbar_edit(fb, W, ft, editbuf, editlen) }
828 a[0]=hwnd
829 let hdc: i64 = win32(W_GetDC, a)
830 a[0]=hdc; a[1]=0; a[2]=0; a[3]=W; a[4]=H; a[5]=0; a[6]=0; a[7]=0; a[8]=H; a[9]=fb as i64; a[10]=bmi as i64; a[11]=0
831 win32(W_SetDIBitsToDevice, a)
832 a[0]=hwnd; a[1]=hdc; win32(W_ReleaseDC, a)
833 a[0]=hwnd
834 if win32(W_IsWindow, a) == 0 { running = 0 }
835 sys_sleep_ms(16)
836 }
837 sys_exit(0); return 0
838}