code wiki / _hdl_build / nx_render_wiki_window.nx

nx_render_wiki_window.nx source

↩ module page · 194 lines · 8893 B

1// nx_render_wiki_window.nx -- render the REAL fetched Wikipedia page into a REAL, SCROLLABLE on-screen 2// window on the Windows desktop (WSLg X server). Composes the sovereign pipeline (layout_from_dom + 3// layout_block) + the X11 client organs. Text drawn with our 5x7 font (nx_x11_paint_text_5x7 -- no 4// server font needed). A real X11 event loop: stays open until you close it; scroll with the mouse 5// wheel / arrow keys / space / PageUp-Down; Esc or q quits. No gcc/V8/Chrome. 6import "nx_syscalls.nx" 7import "nx_css_tokenize.nx" 8import "nx_css_apply.nx" 9import "nx_layout_box.nx" 10import "nx_layout_from_dom.nx" 11import "nx_layout_block.nx" 12import "nx_x11_connect.nx" 13import "nx_x11_window.nx" 14import "nx_x11_paint.nx" 15import "nx_x11_paint_text_5x7.nx" 16const K_MAGIC_16384: i64 = 16384 17const K_MAGIC_8000: i64 = 8000 18 19// index of byte `b` at/after `start`, or -1. 20func find_byte(html: *u8, hlen: i64, start: i64, b: i64) -> i64 { 21 var i: i64 = start 22 while i < hlen { if (html[i] & 0xff) == b { return i } i = i + 1 } 23 return 0 - 1 24} 25// index of "</a" at/after `start`, or -1. 26func find_close_a(html: *u8, hlen: i64, start: i64) -> i64 { 27 var i: i64 = start 28 while i + 2 < hlen { 29 if (html[i] & 0xff) == 60 { if (html[i+1] & 0xff) == 47 { 30 let c: i64 = html[i+2] & 0xff 31 if c == 97 { return i } 32 if c == 65 { return i } 33 } } 34 i = i + 1 35 } 36 return 0 - 1 37} 38// scan <a ...>TEXT</a> link-text ranges -> ranges[2k]=start, ranges[2k+1]=end. Returns link count. 39func scan_links(html: *u8, hlen: i64, ranges: *i64, maxn: i64) -> i64 { 40 var i: i64 = 0 41 var n: i64 = 0 42 while i < hlen { 43 if n >= maxn { return n } 44 var is_a: i64 = 0 45 if (html[i] & 0xff) == 60 { if i + 2 < hlen { 46 let c1: i64 = html[i+1] & 0xff 47 if c1 == 97 { is_a = 1 } 48 if c1 == 65 { is_a = 1 } 49 if is_a == 1 { 50 let c2: i64 = html[i+2] & 0xff 51 var ok: i64 = 0 52 if c2 == 32 { ok = 1 } 53 if c2 == 62 { ok = 1 } 54 if c2 == 9 { ok = 1 } 55 if c2 == 10 { ok = 1 } 56 if ok == 0 { is_a = 0 } 57 } 58 } } 59 if is_a == 1 { 60 let gt: i64 = find_byte(html, hlen, i + 2, 62) 61 if gt < 0 { return n } 62 let close: i64 = find_close_a(html, hlen, gt + 1) 63 if close < 0 { return n } 64 ranges[n*2] = gt + 1 65 ranges[n*2+1] = close 66 n = n + 1 67 i = close + 3 68 } else { i = i + 1 } 69 } 70 return n 71} 72// is byte-offset `off` inside any link text range? 73func is_link(off: i64, ranges: *i64, n: i64) -> i64 { 74 var i: i64 = 0 75 while i < n { 76 if off >= ranges[i*2] { if off < ranges[i*2+1] { return 1 } } 77 i = i + 1 78 } 79 return 0 80} 81 82// clear to white, then draw every text box visible at the current scroll offset (links in blue). 83func draw_page(c: *X11Conn, wid: i64, ink: i64, blue: i64, paper: i64, tree: *LayoutTree, html: *u8, 84 win_w: i64, win_h: i64, scroll_y: i64, ranges: *i64, nlinks: i64) -> i64 { 85 nx_x11_fill_rect(c, wid, paper, 0, 0, win_w, win_h) // clear (no backing store) 86 var i: i64 = 0 87 while i < tree.count { 88 let b: *LayoutBox = ((tree.boxes as i64) + i * NX_LAYOUT_BOX_BYTES) as *LayoutBox 89 if b.kind == NX_LAYOUT_BOX_TEXT { 90 if b.text_len > 0 { 91 let sy: i64 = b.y * 2 - scroll_y // 2x zoom: scale position + font together (no overlap) 92 if sy >= 0 - 28 { if sy < win_h { 93 var n: i64 = b.text_len 94 if n > 200 { n = 200 } 95 var gc: i64 = ink 96 if is_link(b.text_off, ranges, nlinks) == 1 { gc = blue } 97 nx_x11_paint_text_5x7(c, wid, gc, b.x * 2 + 4, sy + 2, ((html as i64) + b.text_off) as *u8, n, 2) 98 } } 99 } 100 } 101 i = i + 1 102 } 103 nx_x11_flush_via_get_input_focus(c) 104 return 0 105} 106 107func main() -> i64 { 108 let path: *u8 = "knowledge/fetched/wiki_web_dev.html\x00" 109 let len_p: *i64 = sys_mmap(16) as *i64 110 let html: *u8 = sys_read_file(path, len_p) 111 let html_len: i64 = *len_p 112 if html_len <= 0 { sys_write(2, "no wiki html\n" as *u8, 12); return 1 } 113 114 let VW: i64 = 1000 115 let boxes: *LayoutBox = (sys_mmap(NX_LAYOUT_BOX_BYTES * K_MAGIC_16384 + 16)) as *LayoutBox 116 let tree: *LayoutTree = (sys_mmap(64)) as *LayoutTree 117 nx_layout_tree_init(tree, boxes, K_MAGIC_16384) 118 let stk: *LayoutFromDomStack = (sys_mmap(NX_LAYOUT_FROM_DOM_STACK_BYTES + 8)) as *LayoutFromDomStack 119 let stk_idx: *i64 = (sys_mmap(8 * 128)) as *i64 120 nx_layout_from_dom_stack_init(stk, stk_idx, 128) 121 if nx_layout_from_dom(html, html_len, tree, stk) != 0 { sys_write(2, "layout fail\n" as *u8, 11); return 2 } 122 let prop_buf: *u8 = sys_mmap(256) 123 let ltable: *LayoutPropTable = (sys_mmap(NX_LAYOUT_PROP_TABLE_BYTES)) as *LayoutPropTable 124 nx_layout_prop_table_init(ltable, prop_buf) 125 let resolve: *CssResolveCtx = (sys_mmap(NX_CSS_RESOLVE_CTX_BYTES)) as *CssResolveCtx 126 resolve.root_font_size_px = 16 127 resolve.parent_font_size_px = 16 128 resolve.parent_dimension_px = VW 129 let lctx: *LayoutCtx = (sys_mmap(NX_LAYOUT_CTX_BYTES)) as *LayoutCtx 130 nx_layout_ctx_init(lctx, tree, 0 as *CssComputedDecl, 0, html, VW, resolve) 131 let page_h: i64 = nx_layout_block_layout(lctx, ltable, 0) 132 133 let rc: i64 = nx_x11_connect_local(0) 134 if rc < 0 { sys_write(2, "no X server (WSLg)\n" as *u8, 18); return 3 } 135 let c: *X11Conn = rc as *X11Conn 136 let WW: i64 = VW 137 let WH: i64 = 760 138 let mask: i64 = NX_X11_EVENT_EXPOSURE | NX_X11_EVENT_KEY_PRESS | NX_X11_EVENT_BUTTON_PRESS | NX_X11_EVENT_STRUCTURE 139 let wid: i64 = nx_x11_create_window(c, 60, 60, WW, WH, 0x00FFFFFF, mask) 140 if wid < 0 { return 4 } 141 let title: *u8 = "Nishi Browser - en.wikipedia.org/wiki/Web_development (wheel/arrows scroll, q quits)\x00" 142 var tl: i64 = 0 143 while title[tl] != 0 { tl = tl + 1 } 144 nx_x11_set_wm_name(c, wid, title, tl) 145 let ink: i64 = nx_x11_create_gc(c, wid, 0x00000000) // black text 146 let blue: i64 = nx_x11_create_gc(c, wid, 0x000000FF) // blue links 147 let paper: i64 = nx_x11_create_gc(c, wid, 0x00FFFFFF) // white clear 148 let ranges: *i64 = (sys_mmap(8 * 2 * K_MAGIC_8000)) as *i64 // link text ranges (up to K_MAGIC_8000 links) 149 let nlinks: i64 = scan_links(html, html_len, ranges, K_MAGIC_8000) 150 if nx_x11_map_window(c, wid) < 0 { return 5 } 151 nx_x11_flush_via_get_input_focus(c) 152 sys_write(1, "Nishi browser window open -- scroll with wheel/arrows, q to quit\n" as *u8, 65) 153 154 var scroll_y: i64 = 0 155 var max_scroll: i64 = page_h * 2 - WH // page is rendered at 2x zoom 156 if max_scroll < 0 { max_scroll = 0 } 157 draw_page(c, wid, ink, blue, paper, tree, html, WW, WH, scroll_y, ranges, nlinks) 158 159 // ---- X11 event loop: scroll + redraw until the window is closed ---- 160 let ev: *u8 = sys_mmap(64) 161 var running: i64 = 1 162 while running == 1 { 163 if nx_x11_read_event(c, ev) != 0 { running = 0 } 164 else { 165 let kind: i64 = (ev[0] & 0x7f) as i64 166 var dirty: i64 = 0 167 if kind == 12 { dirty = 1 } // Expose -> redraw 168 if kind == 4 { // ButtonPress (mouse) 169 let btn: i64 = (ev[1] & 0xff) as i64 170 if btn == 5 { scroll_y = scroll_y + 120; dirty = 1 } // wheel down 171 if btn == 4 { scroll_y = scroll_y - 120; dirty = 1 } // wheel up 172 if btn == 1 { scroll_y = scroll_y + 600; dirty = 1 } // left-click = page down 173 } 174 if kind == 2 { // KeyPress 175 let kc: i64 = (ev[1] & 0xff) as i64 176 if kc == 116 { scroll_y = scroll_y + 120; dirty = 1 } // Down 177 if kc == 111 { scroll_y = scroll_y - 120; dirty = 1 } // Up 178 if kc == 117 { scroll_y = scroll_y + 600; dirty = 1 } // PageDown 179 if kc == 112 { scroll_y = scroll_y - 600; dirty = 1 } // PageUp 180 if kc == 65 { scroll_y = scroll_y + 600; dirty = 1 } // Space 181 if kc == 9 { running = 0 } // Esc 182 if kc == 24 { running = 0 } // q 183 } 184 if kind == 17 { running = 0 } // DestroyNotify 185 if dirty == 1 { 186 if scroll_y < 0 { scroll_y = 0 } 187 if scroll_y > max_scroll { scroll_y = max_scroll } 188 draw_page(c, wid, ink, blue, paper, tree, html, WW, WH, scroll_y, ranges, nlinks) 189 } 190 } 191 } 192 sys_close(c.fd) 193 return 0 194}