code wiki / _hdl_build / nishi_gui_gif_t139.nx

nishi_gui_gif_t139.nx source

↩ module page · 851 lines · 42874 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_gif_t139.nx" // R3: the EXTRACTED CSS render core (br_layout cascade+box-layout, br_draw_fb paint) 11import "nx_img_bytes_to_rgb_gif_t139.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 image_result: *i64=gif_own(NX_IMAGE_RESULT_WORDS*8) as *i64 292 if image_result==(0 as *i64){return 0-1} 293 let tree: *LayoutTree = page.tree 294 var done: i64 = 0 295 var tries: i64 = 0 296 // BR14: EAGER FIRST. The budget below is BOUNDED (a few spawns, each ~seconds), so the ORDER it is 297 // spent in decides which figures a reader actually sees. loading="lazy" marks the figures a browser 298 // defers because they sit below the fold, and page.bsrc_lazy carries that per box. Two passes -- 299 // eager, then lazy -- so a bounded budget lands on above-the-fold figures first. 300 // THE FLAG NEVER SUPPRESSES A FETCH, ONLY ORDERS IT: this window paints the whole document, and a 301 // renderer that skips lazy images produces exactly the blank figures BR14 exists to remove. 302 var pass: i64 = 0 303 while pass < 2 { 304 var i: i64 = 0 305 while i < tree.count { 306 var go: i64 = 0 307 if page.bsrc_lazy[i] == pass { if done < 4 { if tries < 6 { if page.bsrc_off[i] != (0 - 1) { go = 1 } } } } 308 if go == 1 { 309 let so: i64 = page.bsrc_off[i] 310 let sl: i64 = page.bsrc_len[i] 311 // Skip srcs we CAN'T decode -- SVG (src ends ".svg") + data: URIs -- so the small fetch budget 312 // reaches the real JPEG/PNG content images (Wikipedia's first <img>s are all icon SVGs). Note 313 // ".svg.png" thumbnails end in ".png", so they are NOT skipped -- they ARE decodable PNGs. 314 var skip: i64 = 0 315 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 } } } } } 316 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 } } } } } } 317 if skip == 0 { 318 tries = tries + 1 319 let srcbuf: *u8 = sys_mmap(sl + 2) 320 var j: i64 = 0 321 while j < sl { srcbuf[j] = page.buf[so + j]; j = j + 1 } 322 srcbuf[sl] = 0 as u8 323 let al: i64 = resolve_url(srcbuf, sl, baseurl, baselen, tmpurl) 324 if al > 0 { 325 tmpurl[al] = 0 as u8 326 if fetch_image(tmpurl, al, a, cmd) == 1 { 327 let blen: i64 = read_local("_offc/_img.bin" as *u8, imgbuf, imgcap, a) 328 if blen > 8 { 329 let image_status: i64=nx_img_bytes_decode_result(imgbuf,blen,image_result) 330 if image_status==GIF_OK_FIRST_IMAGE { 331 if br_image_take_result(page,i,image_result)==0 {done=done+1} 332 } 333 if nx_img_result_release(image_result)!=0 {gif_release(image_result as *u8,NX_IMAGE_RESULT_WORDS*8);return 0-1} 334 } 335 } 336 } 337 } 338 } 339 i = i + 1 340 } 341 pass = pass + 1 342 } 343 if gif_release(image_result as *u8,NX_IMAGE_RESULT_WORDS*8)!=0{return 0-1} 344 return done 345} 346 347// strip inline link markers (STX href SOH ... ETX) -> clean text into out. Returns out_len. 348func strip_markers(src: *u8, len: i64, out: *u8) -> i64 { 349 var i: i64 = 0 350 var o: i64 = 0 351 while i < len { 352 let c: i64 = src[i]&0xff 353 if c == 0x02 { 354 var u: i64 = i + 1 355 var f: i64 = 0 356 while f == 0 { if u >= len { f=1 } else { if (src[u]&0xff)==0x01 { f=1 } else { u=u+1 } } } 357 i = u + 1 358 } 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 } } } 359 } 360 return o 361} 362 363// record one clickable rect (32B: x0@0,y0@4,x1@8,y1@12, urlPtr@16, urlLen@24) in display coords. 364func rec_link(linktab: *u8, lcount: *i64, x0: i64, y0: i64, x1: i64, y1: i64, uptr: i64, ulen: i64) -> i64 { 365 if lcount[0] >= LINKMAX { return 0 } 366 if x1 <= x0 { return 0 } 367 let e: i64 = lcount[0] * 32 368 w32(linktab, e+0, x0); w32(linktab, e+4, y0); w32(linktab, e+8, x1); w32(linktab, e+12, y1) 369 wp (linktab, e+16, uptr); w32(linktab, e+24, ulen) 370 lcount[0] = lcount[0] + 1 371 return 0 372} 373 374// Lay body text into [ytop,maxy], char-wrapped + scrolled. Inline link markers (STX href SOH anchor 375// ETX) render the anchor BLUE and record per-row clickable rects (32B/entry) for x/y hit-testing. 376// Fragment (#...) links render as plain text (not clickable). lcount[0] = recorded visible rects. 377func draw_body(fb: *u8, W: i64, ft: *u8, x0: i64, ytop: i64, colw: i64, lineh: i64, maxy: i64, 378 text: *u8, len: i64, sc: i64, scroll_px: i64, linktab: *u8, lcount: *i64) -> i64 { 379 let cw: i64 = 9 * sc 380 var x: i64 = x0 381 var y: i64 = ytop - scroll_px 382 var i: i64 = 0 383 lcount[0] = 0 384 var link_on: i64 = 0 // 0=none, 1=clickable link, 2=fragment (rendered plain, not clickable) 385 var luptr: i64 = 0 386 var lulen: i64 = 0 387 var rowx0: i64 = 0 388 while i < len { 389 let c: i64 = text[i]&0xff 390 if c == 0x02 { // STX: begin link, capture raw href 391 var u: i64 = i + 1 392 var fu: i64 = 0 393 while fu == 0 { if u >= len { fu=1 } else { if (text[u]&0xff)==0x01 { fu=1 } else { u=u+1 } } } 394 luptr = (text as i64) + (i + 1) 395 lulen = u - (i + 1) 396 i = u + 1 397 if lulen > 0 { 398 let lpc: *u8 = luptr as *u8 399 if (lpc[0]&0xff)==0x23 { link_on = 2 } else { link_on = 1 } 400 } else { link_on = 0 } 401 rowx0 = x 402 } else { 403 if c == 0x03 { // ETX: end link -> record this row's rect 404 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) } } } 405 link_on = 0 406 i = i + 1 407 } else { 408 if c == 0x01 { i = i + 1 } // stray SOH -> skip 409 else { 410 if c >= 0x80 { i = i + 1 } 411 else { 412 if c == 0x0a { 413 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) } } } 414 x = x0; y = y + lineh 415 if link_on != 0 { rowx0 = x } 416 i = i + 1 417 } else { 418 if c != 0x0d { 419 if (x + cw) > (x0 + colw) { // wrap -> close this row's link rect, start a new row 420 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) } } } 421 x = x0; y = y + lineh 422 if link_on != 0 { rowx0 = x } 423 } 424 if c != 0x20 { if y >= ytop { if y <= maxy { 425 if link_on == 1 { draw_char(fb, W, ft, c, x, y, sc, 225, 115, 30) } 426 else { draw_char(fb, W, ft, c, x, y, sc, 55, 55, 58) } 427 } } } 428 x = x + cw 429 } 430 i = i + 1 431 } 432 } 433 } 434 } 435 } 436 if y > maxy { i = len } 437 } 438 return 0 439} 440 441// box-downsample the 2x render buffer fb2 (RWxRH) into the display fb (WxH) -> anti-aliased. 442func downsample(fb: *u8, W: i64, H: i64, fb2: *u8, RW: i64) -> i64 { 443 var y: i64 = 0 444 while y < H { 445 var x: i64 = 0 446 while x < W { 447 let s: i64 = ((2*y) * RW + (2*x)) * 4 448 let s2: i64 = ((2*y+1) * RW + (2*x)) * 4 449 let b: i64 = ((fb2[s]&0xff) + (fb2[s+4]&0xff) + (fb2[s2]&0xff) + (fb2[s2+4]&0xff)) >> 2 450 let g: i64 = ((fb2[s+1]&0xff) + (fb2[s+5]&0xff) + (fb2[s2+1]&0xff) + (fb2[s2+5]&0xff)) >> 2 451 let r: i64 = ((fb2[s+2]&0xff) + (fb2[s+6]&0xff) + (fb2[s2+2]&0xff) + (fb2[s2+6]&0xff)) >> 2 452 let d: i64 = (y * W + x) * 4 453 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) 454 x = x + 1 455 } 456 y = y + 1 457 } 458 return 0 459} 460 461// static chrome at 2x render res: toolbar, address bar (current URL), logo, page title, footer. 462func draw_chrome(fb2: *u8, RW: i64, RH: i64, ft: *u8, tptr: *u8, tlen: i64, urlptr: *u8, urllen: i64) -> i64 { 463 fill_rect(fb2, RW, 0, 0, RW, RH, 252, 252, 252) 464 fill_rect(fb2, RW, 0, 0, RW, 100, 246, 245, 242) // toolbar 465 fill_rect(fb2, RW, 0, 100, RW, 3, 210, 210, 214) // toolbar border 466 fill_rect(fb2, RW, 24, 26, 52, 48, 230, 230, 232) // back/fwd buttons 467 fill_rect(fb2, RW, 92, 26, 52, 48, 230, 230, 232) 468 draw_text(fb2, RW, ft, 40, 36, "<" as *u8, 3, 90, 90, 95) 469 draw_text(fb2, RW, ft, 108, 36, ">" as *u8, 3, 90, 90, 95) 470 fill_rect(fb2, RW, 244, 22, RW - 560, 56, 218, 218, 222) // address bar border 471 fill_rect(fb2, RW, 248, 26, RW - 568, 48, 255, 255, 255) // address field 472 fill_rect(fb2, RW, RW - 80, 26, 52, 48, 210, 110, 40) // logo 473 draw_text(fb2, RW, ft, RW - 72, 34, "n" as *u8, 4, 245, 235, 220) 474 var ashow: i64 = urllen 475 if ashow > 62 { ashow = 62 } 476 draw_span(fb2, RW, ft, 268, 38, urlptr, 0, ashow, 3, 90, 90, 95) 477 // page title heading (centered-left in the reading column) 478 let mL: i64 = (RW - 1480) / 2 479 var tshow: i64 = tlen 480 if tshow > 33 { tshow = 33 } 481 draw_span(fb2, RW, ft, mL, 130, tptr, 0, tshow, 6, 25, 25, 30) 482 fill_rect(fb2, RW, mL, 206, 1480, 3, 225, 225, 228) // hairline under title 483 // footer 484 fill_rect(fb2, RW, 0, RH - 56, RW, 56, 248, 247, 245) 485 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) 486 return 0 487} 488 489func render_content(fb2: *u8, RW: i64, RH: i64, ft: *u8, body: *u8, blen: i64, scroll_px: i64, linktab: *u8, lcount: *i64) -> i64 { 490 let mL: i64 = (RW - 1480) / 2 491 fill_rect(fb2, RW, 0, 222, RW, RH - 222 - 60, 252, 252, 252) // clear body viewport 492 draw_body(fb2, RW, ft, mL, 238, 1480, 44, RH - 70, body, blen, 4, scroll_px, linktab, lcount) 493 return 0 494} 495 496// Paint a "Loading" banner straight to the display + blit it, so a click visibly 497// registers before the (blocking) in-process fetch runs. 498func show_loading(a: *i64, hwnd: i64, bmi: *u8, fb: *u8, W: i64, H: i64, ft: *u8, url: *u8, ulen: i64) -> i64 { 499 fill_rect(fb, W, 0, 104, W, 36, 210, 110, 40) // orange progress bar under the toolbar 500 fill_rect(fb, W, 0, 140, W, 28, 246, 245, 242) 501 draw_text(fb, W, ft, 16, 114, "Loading" as *u8, 2, 255, 255, 255) 502 var n: i64 = ulen 503 if n > 110 { n = 110 } 504 draw_span(fb, W, ft, 110, 146, url, 0, n, 1, 120, 120, 125) 505 a[0]=hwnd 506 let hdc: i64 = win32(W_GetDC, a) 507 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 508 win32(W_SetDIBitsToDevice, a) 509 a[0]=hwnd; a[1]=hdc; win32(W_ReleaseDC, a) 510 return 0 511} 512 513// Draw the address field directly to the display buffer while it is being typed (with a caret). 514// Display coords: the chrome's address field is render (248,26,1480,48) -> display (124,13,740,24). 515func draw_addrbar_edit(fb: *u8, W: i64, ft: *u8, ed: *u8, edlen: i64) -> i64 { 516 fill_rect(fb, W, 124, 13, 740, 24, 255, 255, 255) // clear the field 517 var shown: i64 = edlen 518 if shown > 40 { shown = 40 } // keep the tail (caret) visible 519 let endx: i64 = draw_span(fb, W, ft, 132, 16, ed, edlen - shown, shown, 2, 40, 40, 45) 520 fill_rect(fb, W, endx, 15, 2, 20, 50, 50, 60) // caret 521 return 0 522} 523 524func hist_push(histbuf: *u8, hoff: *i64, hlen: *i64, histn: i64, hcur: i64, url: *u8, ulen: i64) -> i64 { 525 hoff[histn] = hcur 526 var k: i64 = 0 527 while k < ulen { histbuf[hcur+k] = url[k]; k = k + 1 } 528 hlen[histn] = ulen 529 return hcur + ulen 530} 531 532// R3: convert an RGBA framebuffer (nx_paint order px[0]=R, what br_draw_fb writes) to the display's 533// BGRA (Windows 32bpp DIB byte order), n pixels. The one bridge between the sovereign paint + the DIB. 534func rgba_to_bgra(dst: *u8, src: *u8, n: i64) -> i64 { 535 var i: i64 = 0 536 while i < n { 537 let s: i64 = i * 4 538 dst[s+0] = src[s+2] // B 539 dst[s+1] = src[s+1] // G 540 dst[s+2] = src[s+0] // R 541 dst[s+3] = 0 as u8 542 i = i + 1 543 } 544 return 0 545} 546// R3: render the laid-out page into the display fb via the shared CSS paint organ (br_draw_fb -> RGBA fb2), 547// then bridge RGBA->BGRA into the DIB fb that the message loop blits. 548func paint_css(page: *Page, fbs: *Framebuffer, fb: *u8, fb2: *u8, W: i64, H: i64, url: *u8, ulen: i64, scroll_px: i64) -> i64 { 549 br_set_fast_vec(1) // glyph-atlas fast path: 17ms warm -> full body vector is affordable 550 page.vec_headings = 2 // professional PROPORTIONAL vector typography THROUGHOUT (headings + body), generative pioneer face 551 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) 552 // vertical SCROLLBAR on the right edge when the page overflows the viewport (chrome-fixed, page-space thumb) 553 let vp: i64 = H - NX_CHROME_H 554 if page.page_h > vp { if vp > 0 { 555 nx_paint_solid_rect(fbs, W - 10, NX_CHROME_H, 10, vp, br_color_from_int(0x00EDEFF2)) // track 556 var th: i64 = vp * vp / page.page_h 557 if th < 28 { th = 28 } 558 if th > vp { th = vp } 559 var ty: i64 = NX_CHROME_H 560 let denom: i64 = page.page_h - vp 561 if denom > 0 { ty = NX_CHROME_H + scroll_px * (vp - th) / denom } 562 nx_paint_solid_rect(fbs, W - 9, ty, 8, th, br_color_from_int(0x00A8AEB8)) // thumb 563 } } 564 rgba_to_bgra(fb, fb2, W * H) 565 return 0 566} 567 568func gappend(buf: *u8, n: i64, val: i64) -> i64 { 569 var m: i64 = val 570 if m < 0 { buf[n] = 45 as u8; n = n + 1; m = 0 - m } 571 if m == 0 { buf[n] = 48 as u8; buf[n + 1] = 32 as u8; return n + 2 } 572 let t: *u8 = sys_mmap(24) 573 var k: i64 = 0 574 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 575 var j: i64 = 0 576 while j < k { buf[n + j] = t[k - 1 - j]; j = j + 1 } 577 buf[n + k] = 32 as u8 578 return n + k + 1 579} 580func gwrite(a: *i64, buf: *u8, n: i64) -> i64 { 581 let lp: *u8 = "_offc/gui.log\x00" as *u8 582 a[0] = lp as i64; a[1] = 0x40000000; a[2] = 1; a[3] = 0; a[4] = 2; a[5] = 0x80; a[6] = 0 583 let h: i64 = win32(W_CreateFileA, a) 584 if h == (0 - 1) { return 0 } 585 let got: *u8 = sys_mmap(64) 586 a[0] = h; a[1] = buf as i64; a[2] = n; a[3] = got as i64; a[4] = 0 587 win32(W_WriteFile, a) 588 a[0] = h; win32(W_CloseHandle, a) 589 return 0 590} 591func gmark(a: *i64, buf: *u8, val: i64) -> i64 { 592 let n: i64 = gappend(buf, 0, val) 593 gwrite(a, buf, n) 594 return 0 595} 596func main() -> i64 { 597 var W: i64 = 1024 // mutable: live resize updates these to the new client size 598 var H: i64 = 720 599 let RW: i64 = 2048 // max render/display buffer size (resize clamps to this) 600 let RH: i64 = 1440 601 let a: *i64 = sys_mmap(2048) as *i64 602 let glogbuf: *u8 = sys_mmap(262144) 603 var glogn: i64 = 0 604 605 a[0] = 0 606 let hInst: i64 = win32(W_GetModuleHandleA, a) 607 a[0] = 0; a[1] = 32512 608 let hCur: i64 = win32(W_LoadCursorA, a) 609 610 let cls: *u8 = "nishigui" as *u8 611 let title: *u8 = "Nishi - sovereign browser (native Windows, hardware-up)" as *u8 612 613 let wc: *u8 = sys_mmap(80) 614 w32(wc, 0, 80); w32(wc, 4, 0) 615 wp (wc, 8, win32addr(W_DefWindowProcA)) 616 w32(wc, 16, 0); w32(wc, 20, 0) 617 wp (wc, 24, hInst); wp (wc, 32, 0); wp (wc, 40, hCur); wp (wc, 48, 0); wp (wc, 56, 0) 618 wp (wc, 64, cls as i64); wp (wc, 72, 0) 619 a[0] = wc as i64 620 win32(W_RegisterClassExA, a) 621 622 a[0]=0; a[1]=cls as i64; a[2]=title as i64; a[3]=0x10CF0000 623 a[4]=70; a[5]=50; a[6]=W; a[7]=H; a[8]=0; a[9]=0; a[10]=hInst; a[11]=0 624 let hwnd: i64 = win32(W_CreateWindowExA, a) 625 if hwnd == 0 { sys_exit(1); return 1 } 626 a[0]=hwnd; a[1]=5 627 win32(W_ShowWindow, a) 628 629 // Establish the window: pump its show/paint/activate messages BEFORE the ~4s blocking initial fetch, 630 // so Windows does not tear down a never-pumped window (the render-then-close-after-~1.7s bug). WM_CREATE 631 // already ran synchronously inside CreateWindowExA; this drains WM_SHOWWINDOW/WM_PAINT/WM_ACTIVATE. 632 let msg0: *u8 = sys_mmap(64) 633 var pw0: i64 = 0 634 while pw0 < 50 { 635 a[0]=msg0 as i64; a[1]=0; a[2]=0; a[3]=0; a[4]=1 636 if win32(W_PeekMessageA, a) != 0 { win32(W_TranslateMessage, a); win32(W_DispatchMessageA, a) } 637 pw0 = pw0 + 1 638 } 639 640 let fb2: *u8 = sys_mmap(RW * RH * 4) 641 let fb: *u8 = sys_mmap(RW * RH * 4) // max-size display buffer so live resize can grow up to RW x RH 642 let rectbuf: *u8 = sys_mmap(16) // GetClientRect output (left,top,right,bottom) 643 let ft: *u8 = font8x8_table() 644 // R3: the CSS page + an RGBA framebuffer view over fb2's buffer (at display res W x H) that br_draw_fb paints into. 645 let page: *Page = sys_mmap(NX_PAGE_BYTES) as *Page 646 let fbs: *Framebuffer = sys_mmap(NX_FRAMEBUFFER_BYTES) as *Framebuffer 647 nx_framebuffer_init(fbs, fb2, W, H) // 1:1 framebuffer view over fb2 (br_draw_fb paints here at scale=1) 648 649 let pcap: i64 = 2097152 650 let pbuf: *u8 = sys_mmap(pcap) 651 let cmd: *u8 = sys_mmap(2048) 652 let urlbuf: *u8 = sys_mmap(2048) 653 let tmpurl: *u8 = sys_mmap(2048) 654 let titlebuf: *u8 = sys_mmap(4096) 655 let linktab: *u8 = sys_mmap(LINKMAX * 32) 656 let lcount: *i64 = sys_mmap(16) as *i64 657 let sp: *i64 = sys_mmap(16) as *i64 658 let imgbuf: *u8 = sys_mmap(4194304) // R4: scratch for a fetched image's bytes (read_local -> decode) 659 660 let histbuf: *u8 = sys_mmap(131072) 661 let hoff: *i64 = sys_mmap(1024) as *i64 662 let hlen: *i64 = sys_mmap(1024) as *i64 663 var histn: i64 = 0 664 var hcur: i64 = 0 665 666 // initial page: fetch the start URL LIVE via the (now DNS-fixed) sovereign fetcher. read_local() stays 667 // available for a future offline home page; live browsing is the proof now. 668 var curlen: i64 = scopy(urlbuf, 0, "https://en.wikipedia.org/wiki/Web_browser" as *u8) 669 urlbuf[curlen] = 0 as u8 670 var plen: i64 = fetch_url(urlbuf, curlen, pbuf, pcap, a, cmd) 671 hcur = hist_push(histbuf, hoff, hlen, histn, hcur, urlbuf, curlen); histn = histn + 1 672 split_page(pbuf, plen, sp) 673 var body: *u8 = ((pbuf as i64) + sp[1]) as *u8 674 var blen: i64 = plen - sp[1] 675 var scroll_px: i64 = 0 676 var editing: i64 = 0 // address bar being typed? 677 let editbuf: *u8 = sys_mmap(2048) 678 var editlen: i64 = 0 679 680 let bmi: *u8 = sys_mmap(40) 681 w32(bmi,0,40); w32(bmi,4,W); w32(bmi,8, 0 - H) 682 w16(bmi,12,1); w16(bmi,14,32); w32(bmi,16,0) 683 684 page.raw = pbuf; page.raw_len = plen // R3: lay out + paint the REAL page via the CSS organ 685 br_layout(page, W) 686 load_images(page, urlbuf, curlen, a, cmd, tmpurl, imgbuf, 4194304) // R4: fetch+decode <img> -> page.bimg (VirtualAlloc-backed) 687 paint_css(page, fbs, fb, fb2, W, H, urlbuf, curlen, scroll_px) 688 689 let msg: *u8 = sys_mmap(64) 690 var running: i64 = 1 691 while running == 1 { 692 var need: i64 = 0 // re-render body (scroll) 693 var nav: i64 = 0 // navigate: re-fetch + full re-render 694 var full: i64 = 0 // re-render chrome+body without fetching (exit address-bar edit) 695 var edit_dirty: i64 = 0 // address-bar text changed -> redraw the field 696 var do_back: i64 = 0 // go back one history entry 697 var pumping: i64 = 1 698 while pumping == 1 { // drain the whole message queue each frame 699 a[0]=msg as i64; a[1]=0; a[2]=0; a[3]=0; a[4]=1 700 let got: i64 = win32(W_PeekMessageA, a) 701 if got == 0 { pumping = 0 } else { 702 let m: i64 = rd32(msg, 8) 703 if m == 0x100 { 704 let vk: i64 = rd32(msg, 16) & 0xffff 705 if editing == 1 { 706 if vk == 0x0D { // Enter -> go to typed URL 707 var pre: i64 = 0 708 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 } } } } } 709 var ul2: i64 = 0 710 if pre == 0 { ul2 = scopy(urlbuf, 0, "https://" as *u8) } 711 var z3: i64 = 0 712 while z3 < editlen { urlbuf[ul2+z3] = editbuf[z3]; z3 = z3 + 1 } 713 ul2 = ul2 + editlen 714 urlbuf[ul2] = 0 as u8; curlen = ul2 715 editing = 0 716 show_loading(a, hwnd, bmi, fb, W, H, ft, urlbuf, ul2) 717 let np3: i64 = fetch_url(urlbuf, curlen, pbuf, pcap, a, cmd) 718 if np3 > 0 { plen = np3; hcur = hist_push(histbuf, hoff, hlen, histn, hcur, urlbuf, curlen); histn = histn + 1; nav = 1 } else { full = 1 } 719 } 720 if vk == 0x08 { if editlen > 0 { editlen = editlen - 1 } edit_dirty = 1 } // Backspace deletes 721 if vk == 0x1B { editing = 0; full = 1 } // Escape cancels 722 } else { 723 if vk == 0x22 { scroll_px = scroll_px + 560; need = 1 } // PgDn 724 if vk == 0x21 { scroll_px = scroll_px - 560; need = 1 } // PgUp 725 if vk == 0x28 { scroll_px = scroll_px + 132; need = 1 } // Down 726 if vk == 0x26 { scroll_px = scroll_px - 132; need = 1 } // Up 727 if vk == 0x1B { running = 0 } // Escape -> clean quit (X-button/drag = DefWindowProc follow-up) 728 if vk == 0x20 { scroll_px = scroll_px + 560; need = 1 } // Space 729 if vk == 0x24 { scroll_px = 0; need = 1 } // Home 730 if vk == 0x08 { do_back = 1 } // Backspace = back 731 } 732 } 733 if m == 0x102 { // WM_CHAR: address-bar typing 734 if editing == 1 { 735 let ch: i64 = rd32(msg, 16) & 0xff 736 if ch >= 0x20 { if ch < 0x7f { if editlen < 2040 { editbuf[editlen] = ch as u8; editlen = editlen + 1; edit_dirty = 1 } } } 737 } 738 } 739 if m == 0x20A { // mouse wheel 740 let dd: i64 = (rd32(msg, 16) >> 16) & 0xffff 741 if dd > 0x7fff { scroll_px = scroll_px + 132 } else { scroll_px = scroll_px - 132 } 742 need = 1 743 } 744 if m == 0x201 { // left button down 745 let mx: i64 = rd32(msg, 24) & 0xffff 746 let my: i64 = (rd32(msg, 24) >> 16) & 0xffff 747 var consumed: i64 = 0 748 if my >= 13 { if my <= 37 { if mx >= 124 { if mx <= 864 { // click address bar -> edit 749 editing = 1 750 var z4: i64 = 0 751 while z4 < curlen { editbuf[z4] = urlbuf[z4]; z4 = z4 + 1 } 752 editlen = curlen 753 edit_dirty = 1; consumed = 1 754 } } } } 755 if consumed == 0 { if my >= 13 { if my <= 37 { if mx >= 12 { if mx <= 38 { do_back = 1; consumed = 1 } } } } } // < back button 756 if consumed == 0 { 757 if editing == 1 { editing = 0; full = 1 } // click in page -> leave edit mode 758 // R3: hit-test the click against the LAYOUT's href boxes. br_draw_fb paints a box at 759 // b.y + NX_CHROME_H at 1:1, so doc coords = (mx, my - NX_CHROME_H). 760 let choff: *i64 = sys_mmap(8) as *i64 761 let chlen: *i64 = sys_mmap(8) as *i64 762 if br_hit_link(page, mx, my - NX_CHROME_H, choff, chlen) == 1 { 763 let rawp: i64 = (page.buf as i64) + choff[0] 764 let rawl: i64 = chlen[0] 765 let nl: i64 = resolve_url(rawp as *u8, rawl, urlbuf, curlen, tmpurl) // relative href -> absolute 766 var z2: i64 = 0 767 while z2 < nl { urlbuf[z2] = tmpurl[z2]; z2 = z2 + 1 } 768 urlbuf[nl] = 0 as u8; curlen = nl 769 show_loading(a, hwnd, bmi, fb, W, H, ft, urlbuf, nl) 770 let np2: i64 = fetch_url(urlbuf, curlen, pbuf, pcap, a, cmd) 771 if np2 > 0 { 772 plen = np2 773 hcur = hist_push(histbuf, hoff, hlen, histn, hcur, urlbuf, curlen); histn = histn + 1 774 nav = 1 775 } 776 } 777 } 778 } 779 a[0]=msg as i64; win32(W_TranslateMessage, a) 780 // DispatchMessage lets DefWindowProc run the standard NON-CLIENT behavior -- title-bar DRAG, 781 // the MIN/MAX/RESTORE buttons, and RESIZE borders. Skipping it (the old stability hack) is 782 // exactly why the window couldn't be moved/minimized/closed. We handle WM_CLOSE (0x10, the X 783 // button / Alt-F4) OURSELVES for a clean quit instead of letting DefWindowProc DestroyWindow 784 // out from under the render loop; every OTHER message is dispatched so the controls work. 785 if m == 0x10 { running = 0 } else { a[0]=msg as i64; win32(W_DispatchMessageA, a) } 786 } } 787 // LIVE RESIZE: WM_SIZE is SENT to the wndproc (which this PeekMessage loop bypasses), so poll the 788 // client size each frame instead -- catches maximize / drag-resize / half-screen snap -- and re-flow 789 // the page to the new width. Clamped to the RWxRH buffer; minimized (0x0) is ignored. 790 a[0]=hwnd; a[1]=rectbuf as i64; win32(W_GetClientRect, a) 791 let cw: i64 = rd32(rectbuf, 8) 792 let ch: i64 = rd32(rectbuf, 12) 793 if cw >= 300 { if ch >= 200 { 794 var rsz: i64 = 0 795 if cw != W { rsz = 1 } 796 if ch != H { rsz = 1 } 797 if rsz == 1 { 798 var nw: i64 = cw 799 var nh: i64 = ch 800 if nw > RW { nw = RW } 801 if nh > RH { nh = RH } 802 W = nw; H = nh 803 nx_framebuffer_init(fbs, fb2, W, H) // re-view fb2 at the new W x H 804 w32(bmi, 4, W); w32(bmi, 8, 0 - H) // BITMAPINFO width / top-down height for the blit 805 br_layout(page, W) // re-flow the page to the new width 806 load_images(page,urlbuf,curlen,a,cmd,tmpurl,imgbuf,4194304) 807 full = 1 // full re-paint this frame 808 } 809 } } 810 if do_back == 1 { 811 if histn >= 2 { 812 histn = histn - 1 813 let bo: i64 = hoff[histn-1] 814 let bl: i64 = hlen[histn-1] 815 var z5: i64 = 0 816 while z5 < bl { urlbuf[z5] = histbuf[bo+z5]; z5 = z5 + 1 } 817 urlbuf[bl] = 0 as u8; curlen = bl 818 show_loading(a, hwnd, bmi, fb, W, H, ft, urlbuf, bl) 819 let npb: i64 = fetch_url(urlbuf, curlen, pbuf, pcap, a, cmd) 820 if npb > 0 { plen = npb; nav = 1 } 821 } 822 } 823 if nav == 1 { 824 page.raw = pbuf; page.raw_len = plen 825 br_layout(page, W) 826 load_images(page,urlbuf,curlen,a,cmd,tmpurl,imgbuf,4194304) 827 scroll_px = 0 828 paint_css(page, fbs, fb, fb2, W, H, urlbuf, curlen, scroll_px) 829 } else { 830 if full == 1 { 831 paint_css(page, fbs, fb, fb2, W, H, urlbuf, curlen, scroll_px) 832 } 833 } 834 if scroll_px < 0 { scroll_px = 0 } 835 var maxsc: i64 = page.page_h - (H - NX_CHROME_H) 836 if maxsc < 0 { maxsc = 0 } 837 if scroll_px > maxsc { scroll_px = maxsc } 838 if need == 1 { paint_css(page, fbs, fb, fb2, W, H, urlbuf, curlen, scroll_px) } 839 if edit_dirty == 1 { draw_addrbar_edit(fb, W, ft, editbuf, editlen) } 840 a[0]=hwnd 841 let hdc: i64 = win32(W_GetDC, a) 842 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 843 win32(W_SetDIBitsToDevice, a) 844 a[0]=hwnd; a[1]=hdc; win32(W_ReleaseDC, a) 845 a[0]=hwnd 846 if win32(W_IsWindow, a) == 0 { running = 0 } 847 sys_sleep_ms(16) 848 } 849 br_release_image_metadata(page) 850 sys_exit(0); return 0 851}