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}