code wiki / _hdl_build / nx_render_wiki_styled_window.nx
nx_render_wiki_styled_window.nx source
↩ module page · 261 lines · 12860 B
1// nx_render_wiki_styled_window.nx -- the STYLED live browser window: real Wikipedia, scrollable, with
2// the CSS cascade driving per-element TEXT SIZE + COLOR. Merges the proven styling pipeline (combined
3// [HTML][CSS] buffer -> layout_from_dom -> box-indexed cascade -> computed) into the scrollable X11
4// window; each text box looks up its parent element box's computed font-size (-> glyph px_size) and
5// color (-> gc foreground). Layout sizes boxes by font-size so bigger headings don't overlap. Sovereign.
6import "nx_syscalls.nx"
7import "nx_css_tokenize.nx"
8import "nx_css_parse.nx"
9import "nx_css_apply.nx"
10import "nx_css_selector_match.nx"
11import "nx_html_tokenizer.nx"
12import "nx_dom_query.nx"
13import "nx_layout_box.nx"
14import "nx_layout_default_display.nx"
15import "nx_layout_from_dom.nx"
16import "nx_layout_block.nx"
17import "nx_x11_connect.nx"
18import "nx_x11_window.nx"
19import "nx_x11_paint.nx"
20import "nx_x11_paint_text_5x7.nx"
21const K_MAGIC_16384: i64 = 16384
22const K_MAGIC_8192: i64 = 8192
23
24func sw_slen(s: *u8) -> i64 { var k: i64=0; while s[k]!=(0 as u8){k=k+1} return k }
25func sw_region_is(src: *u8, off: i64, len: i64, lit: *u8) -> i64 {
26 let ll: i64 = sw_slen(lit)
27 if len != ll { return 0 }
28 var i: i64 = 0
29 while i < ll { if (src[off+i] & 0xff) != (lit[i] & 0xff) { return 0 } i = i + 1 }
30 return 1
31}
32func sw_parse_int(src: *u8, off: i64, len: i64) -> i64 {
33 var v: i64 = 0
34 var i: i64 = 0
35 while i < len { let c: i64 = src[off+i] & 0xff; if c >= 48 { if c <= 57 { v = v*10 + (c-48) } } i = i + 1 }
36 return v
37}
38func sw_hex1(c: i64) -> i64 {
39 if c >= 48 { if c <= 57 { return c - 48 } }
40 if c >= 97 { if c <= 102 { return c - 87 } }
41 if c >= 65 { if c <= 70 { return c - 55 } }
42 return 0
43}
44// look up a numeric computed property for box_idx; returns value or `dflt`.
45func sw_comp_int(src: *u8, computed: *CssComputedDecl, ncomp: i64, box_idx: i64, prop: *u8, dflt: i64) -> i64 {
46 var j: i64 = 0
47 while j < ncomp {
48 let cd: *CssComputedDecl = ((computed as i64) + j * NX_CSS_COMPUTED_DECL_BYTES) as *CssComputedDecl
49 if cd.element_idx == box_idx { if sw_region_is(src, cd.prop_off, cd.prop_len, prop) == 1 {
50 return sw_parse_int(src, cd.val_off, cd.val_len)
51 } }
52 j = j + 1
53 }
54 return dflt
55}
56// look up computed `color` (hexcolor) for box_idx -> X11 pixel 0x00RRGGBB; returns -1 if none.
57func sw_comp_color(src: *u8, computed: *CssComputedDecl, ncomp: i64, box_idx: i64) -> i64 {
58 var j: i64 = 0
59 while j < ncomp {
60 let cd: *CssComputedDecl = ((computed as i64) + j * NX_CSS_COMPUTED_DECL_BYTES) as *CssComputedDecl
61 if cd.element_idx == box_idx { if sw_region_is(src, cd.prop_off, cd.prop_len, "color\x00" as *u8) == 1 {
62 if cd.val_len >= 6 {
63 let o: i64 = cd.val_off
64 let r: i64 = sw_hex1(src[o] & 0xff)*16 + sw_hex1(src[o+1] & 0xff)
65 let g: i64 = sw_hex1(src[o+2] & 0xff)*16 + sw_hex1(src[o+3] & 0xff)
66 let b: i64 = sw_hex1(src[o+4] & 0xff)*16 + sw_hex1(src[o+5] & 0xff)
67 return (r << 16) | (g << 8) | b
68 }
69 } }
70 j = j + 1
71 }
72 return 0 - 1
73}
74func sw_extract(html: *u8, hlen: i64, elements: *CssElement, maxn: i64) -> i64 {
75 let c: *HtmlCursor=sys_mmap(24) as *HtmlCursor; nx_html_cursor_init(c,html,hlen)
76 let tok: *HtmlToken=sys_mmap(56) as *HtmlToken
77 let ao: *i64=sys_mmap(8) as *i64; let al: *i64=sys_mmap(8) as *i64; let bo: *i64=sys_mmap(8) as *i64; let bl: *i64=sys_mmap(8) as *i64
78 var n: i64=0
79 while 1==1 {
80 nx_html_next_token(c,tok)
81 if tok.kind==NX_HTML_TOK_EOF { return n }
82 if n>=maxn { return n }
83 var ie: i64=0
84 if tok.kind==NX_HTML_TOK_START_TAG { ie=1 }
85 if tok.kind==NX_HTML_TOK_SELF_CLOSING { ie=1 }
86 if ie==1 {
87 let e: *CssElement=((elements as i64)+n*NX_CSS_ELEMENT_BYTES) as *CssElement
88 e.src=html; e.tag_off=tok.name_off; e.tag_len=tok.name_len
89 if nx_dom_find_attr(html,tok.src_off,tok.src_len,"id\x00" as *u8,ao,al)==1 { e.id_off=ao[0]; e.id_len=al[0] } else { e.id_off=0; e.id_len=0 }
90 if nx_dom_find_attr(html,tok.src_off,tok.src_len,"class\x00" as *u8,bo,bl)==1 { e.class_off=bo[0]; e.class_len=bl[0] } else { e.class_off=0; e.class_len=0 }
91 n=n+1
92 }
93 }
94 return n
95}
96func sw_is_elem(k: i64) -> i64 { if k==NX_LAYOUT_BOX_BLOCK {return 1} if k==NX_LAYOUT_BOX_INLINE {return 1} if k==NX_LAYOUT_BOX_INLINE_BLOCK {return 1} return 0 }
97
98// draw all visible text boxes with REAL X fonts, styled by the cascade (font picked by computed
99// font-size, color from computed). fb/fh/fbig = font ids for body / heading / big-heading.
100func draw_styled(c: *X11Conn, wid: i64, gc: i64, paper: i64, tree: *LayoutTree, html: *u8,
101 computed: *CssComputedDecl, ncomp: i64, win_h: i64, scroll_y: i64,
102 fb: i64, fh: i64, fbig: i64) -> i64 {
103 nx_x11_fill_rect(c, wid, paper, 0, 0, 1000, win_h)
104 var i: i64 = 0
105 while i < tree.count {
106 let b: *LayoutBox = ((tree.boxes as i64) + i * NX_LAYOUT_BOX_BYTES) as *LayoutBox
107 if b.kind == NX_LAYOUT_BOX_TEXT { if b.text_len > 0 {
108 let sy: i64 = b.y - scroll_y
109 if sy >= 0 - 40 { if sy < win_h {
110 let par: i64 = b.parent_idx
111 let fs: i64 = sw_comp_int(html, computed, ncomp, par, "font-size\x00" as *u8, 16)
112 var fid: i64 = fb
113 var asc: i64 = 12
114 if fs > 18 { fid = fh; asc = 16 }
115 if fs > 28 { fid = fbig; asc = 19 }
116 var col: i64 = sw_comp_color(html, computed, ncomp, par)
117 if col < 0 { col = 0 } // default black
118 nx_x11_set_gc_font(c, gc, fid)
119 nx_x11_set_gc_foreground(c, gc, col)
120 var n: i64 = b.text_len
121 if n > 200 { n = 200 }
122 nx_x11_image_text8(c, wid, gc, b.x + 4, sy + asc, ((html as i64) + b.text_off) as *u8, n)
123 } }
124 } }
125 i = i + 1
126 }
127 nx_x11_flush_via_get_input_focus(c)
128 return 0
129}
130
131func main() -> i64 {
132 let path: *u8 = "knowledge/fetched/wiki_web_dev.html\x00"
133 let lp: *i64 = sys_mmap(16) as *i64
134 let raw: *u8 = sys_read_file(path, lp)
135 let hlen: i64 = *lp
136 if hlen <= 0 { sys_write(2,"no html\n" as *u8,8); return 1 }
137 // UA stylesheet: heading sizes + colors, link color (font-size px -> glyph scale via /8)
138 let css: *u8 = "h1{font-size:40px;color:#000080}h2{font-size:30px;color:#101060}h3{font-size:24px;color:#202020}a{color:#0000c0}body{font-size:16px}\x00"
139 let clen: i64 = sw_slen(css)
140 let total: i64 = hlen + clen
141 let buf: *u8 = sys_mmap(total + 16)
142 var i: i64 = 0
143 while i < hlen { buf[i] = raw[i]; i = i + 1 }
144 i = 0
145 while i < clen { buf[hlen + i] = css[i]; i = i + 1 }
146
147 let VW: i64 = 1000
148 let boxes: *LayoutBox = (sys_mmap(NX_LAYOUT_BOX_BYTES*K_MAGIC_16384+16)) as *LayoutBox
149 let tree: *LayoutTree = (sys_mmap(64)) as *LayoutTree
150 nx_layout_tree_init(tree, boxes, K_MAGIC_16384)
151 let stk: *LayoutFromDomStack = (sys_mmap(NX_LAYOUT_FROM_DOM_STACK_BYTES+8)) as *LayoutFromDomStack
152 let sidx: *i64 = (sys_mmap(8*128)) as *i64
153 nx_layout_from_dom_stack_init(stk, sidx, 128)
154 nx_layout_from_dom(buf, hlen, tree, stk)
155
156 // box-indexed CssElement[] (rung 3)
157 let ext: *CssElement = (sys_mmap(NX_CSS_ELEMENT_BYTES*K_MAGIC_16384 as nx_size)) as *CssElement
158 let n_ext: i64 = sw_extract(buf, hlen, ext, K_MAGIC_16384)
159 let boxel: *CssElement = (sys_mmap((NX_CSS_ELEMENT_BYTES*(tree.count+1)) as nx_size)) as *CssElement
160 var k: i64 = 0
161 i = 0
162 while i < tree.count {
163 let b: *LayoutBox = ((tree.boxes as i64)+i*NX_LAYOUT_BOX_BYTES) as *LayoutBox
164 let e: *CssElement = ((boxel as i64)+i*NX_CSS_ELEMENT_BYTES) as *CssElement
165 e.src = buf
166 var mapped: i64 = 0
167 if sw_is_elem(b.kind)==1 { if b.parent_idx != (0-1) { if k < n_ext {
168 let s: *CssElement = ((ext as i64)+k*NX_CSS_ELEMENT_BYTES) as *CssElement
169 e.tag_off=s.tag_off; e.tag_len=s.tag_len; e.id_off=s.id_off; e.id_len=s.id_len; e.class_off=s.class_off; e.class_len=s.class_len
170 k=k+1; mapped=1
171 } } }
172 if mapped==0 { e.tag_off=0; e.tag_len=0; e.id_off=0; e.id_len=0; e.class_off=0; e.class_len=0 }
173 i = i + 1
174 }
175
176 // parse + cascade
177 let cur: *CssCursor = (sys_mmap(NX_CSS_CURSOR_BYTES) as nx_size) as *CssCursor
178 nx_css_cursor_init(cur, buf, total); cur.pos = hlen
179 let tok: *CssToken = (sys_mmap(NX_CSS_TOKEN_BYTES) as nx_size) as *CssToken
180 let rules: *CssRule = (sys_mmap(NX_CSS_RULE_BYTES*32) as nx_size) as *CssRule
181 let decls: *CssDeclaration = (sys_mmap(NX_CSS_DECLARATION_BYTES*64) as nx_size) as *CssDeclaration
182 let st: *CssParseState = (sys_mmap(128) as nx_size) as *CssParseState
183 nx_css_parse_state_init(st, cur, tok, rules, 32, decls, 64)
184 nx_css_parse(st)
185 let computed: *CssComputedDecl = (sys_mmap(NX_CSS_COMPUTED_DECL_BYTES*K_MAGIC_8192 as nx_size)) as *CssComputedDecl
186 let cb: *i64 = (sys_mmap(8)) as *i64
187 nx_css_apply(buf, rules, st.rule_count, decls, st.decl_count, boxel, tree.count, computed, K_MAGIC_8192, cb)
188 let ncomp: i64 = cb[0]
189
190 // layout WITH the cascade (sizes boxes by font-size)
191 let pbuf: *u8 = sys_mmap(256)
192 let ltable: *LayoutPropTable = (sys_mmap(NX_LAYOUT_PROP_TABLE_BYTES)) as *LayoutPropTable
193 nx_layout_prop_table_init(ltable, pbuf)
194 let resolve: *CssResolveCtx = (sys_mmap(NX_CSS_RESOLVE_CTX_BYTES)) as *CssResolveCtx
195 resolve.root_font_size_px = 16; resolve.parent_font_size_px = 16; resolve.parent_dimension_px = VW
196 let lctx: *LayoutCtx = (sys_mmap(NX_LAYOUT_CTX_BYTES)) as *LayoutCtx
197 nx_layout_ctx_init(lctx, tree, computed, ncomp, buf, VW, resolve)
198 let page_h: i64 = nx_layout_block_layout(lctx, ltable, 0)
199
200 // window + event loop
201 let rc: i64 = nx_x11_connect_local(0)
202 if rc < 0 { sys_write(2,"no X\n" as *u8,5); return 3 }
203 let cc: *X11Conn = rc as *X11Conn
204 let WW: i64 = VW
205 let WH: i64 = 760
206 let mask: i64 = NX_X11_EVENT_EXPOSURE | NX_X11_EVENT_KEY_PRESS | NX_X11_EVENT_BUTTON_PRESS | NX_X11_EVENT_STRUCTURE
207 let wid: i64 = nx_x11_create_window(cc, 50, 50, WW, WH, 0x00FFFFFF, mask)
208 if wid < 0 { return 4 }
209 let title: *u8 = "Nishi Browser (styled) - Wikipedia: Web development\x00"
210 var tl: i64 = 0
211 while title[tl] != 0 { tl = tl + 1 }
212 nx_x11_set_wm_name(cc, wid, title, tl)
213 let gc: i64 = nx_x11_create_gc(cc, wid, 0x00000000)
214 let paper: i64 = nx_x11_create_gc(cc, wid, 0x00FFFFFF)
215 // real X server fonts (anti-jank: crisp text instead of the 5x7 bitmap), picked by font-size
216 let fb: i64 = nx_x11_open_font(cc, "9x15\x00" as *u8, 4) // body
217 let fh: i64 = nx_x11_open_font(cc, "10x20\x00" as *u8, 5) // h2/h3
218 let fbig: i64 = nx_x11_open_font(cc, "12x24\x00" as *u8, 5) // h1
219 if nx_x11_map_window(cc, wid) < 0 { return 5 }
220 nx_x11_flush_via_get_input_focus(cc)
221 sys_write(1, "Nishi STYLED browser window open -- scroll wheel/arrows, q quits\n" as *u8, 64)
222
223 var scroll_y: i64 = 0
224 var max_scroll: i64 = page_h - WH
225 if max_scroll < 0 { max_scroll = 0 }
226 draw_styled(cc, wid, gc, paper, tree, buf, computed, ncomp, WH, scroll_y, fb, fh, fbig)
227 let ev: *u8 = sys_mmap(64)
228 var running: i64 = 1
229 while running == 1 {
230 if nx_x11_read_event(cc, ev) != 0 { running = 0 }
231 else {
232 let kind: i64 = (ev[0] & 0x7f) as i64
233 var dirty: i64 = 0
234 if kind == 12 { dirty = 1 }
235 if kind == 4 {
236 let btn: i64 = (ev[1] & 0xff) as i64
237 if btn == 5 { scroll_y = scroll_y + 120; dirty = 1 }
238 if btn == 4 { scroll_y = scroll_y - 120; dirty = 1 }
239 if btn == 1 { scroll_y = scroll_y + 500; dirty = 1 }
240 }
241 if kind == 2 {
242 let kc: i64 = (ev[1] & 0xff) as i64
243 if kc == 116 { scroll_y = scroll_y + 120; dirty = 1 }
244 if kc == 111 { scroll_y = scroll_y - 120; dirty = 1 }
245 if kc == 117 { scroll_y = scroll_y + 600; dirty = 1 }
246 if kc == 112 { scroll_y = scroll_y - 600; dirty = 1 }
247 if kc == 65 { scroll_y = scroll_y + 600; dirty = 1 }
248 if kc == 9 { running = 0 }
249 if kc == 24 { running = 0 }
250 }
251 if kind == 17 { running = 0 }
252 if dirty == 1 {
253 if scroll_y < 0 { scroll_y = 0 }
254 if scroll_y > max_scroll { scroll_y = max_scroll }
255 draw_styled(cc, wid, gc, paper, tree, buf, computed, ncomp, WH, scroll_y, fb, fh, fbig)
256 }
257 }
258 }
259 sys_close(cc.fd)
260 return 0
261}