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}