nx_nishios_gui.nx source
↩ module page · 141 lines · 9381 B
1// nx_nishios_gui.nx -- NishiOS GUI rung-1: the kernel DRAWS A DESKTOP into a framebuffer + exports it
2// as a REAL viewable image. On real x86 a kernel gets a linear framebuffer (VESA/VBE or UEFI GOP) and
3// draws pixels into it. This models exactly that: a 320x200 RGB framebuffer + kernel drawing primitives
4// (clear / filled rect / bitmap-font text), used to render a NishiOS desktop -- title bar, a window with
5// its own title strip, a logo, a taskbar with a start button -- then writes it out as a genuine 24-bit
6// BMP (BITMAPFILEHEADER+BITMAPINFOHEADER, BGR bottom-up) you can open in any image viewer / browser, plus
7// an HTML wrapper for the "window". This is the GUI you can SEE, drawn by our own code, not a mockup.
8// KAT: the framebuffer holds the expected desktop pixels (bg / title bar / window fill / rendered text)
9// and the BMP is well-formed ('BM', correct filesize, 24bpp).
10// HONEST SCOPE: a STATIC render (no mouse/event-loop/input yet). A live compositor with input + a real
11// framebuffer device wired into the boot kernel are the next rungs. No hw writes (Rule 26).
12// expect_exit: 0 license_tier: ORIGINAL
13import "nx_syscalls.nx"
14import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
15const K_MAGIC_2835: i64 = 2835
16
17func gui_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
18// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
19// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
20// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
21// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
22func gui_num(v: i64) -> i64 { nxi_out(v); return 0 }
23
24// --- framebuffer primitives (what the kernel does to the linear framebuffer) ---
25func setpx(fb: *u8, W: i64, H: i64, x: i64, y: i64, r: i64, g: i64, b: i64) -> i64 {
26 if x>=0 { if x<W { if y>=0 { if y<H { let i: i64=(y*W+x)*3; fb[i]=r as u8; fb[i+1]=g as u8; fb[i+2]=b as u8 } } } }
27 return 0
28}
29func fill_rect(fb: *u8, W: i64, H: i64, x: i64, y: i64, w: i64, h: i64, r: i64, g: i64, b: i64) -> i64 {
30 var yy: i64=0
31 while yy<h { var xx: i64=0; while xx<w { setpx(fb,W,H,x+xx,y+yy,r,g,b); xx=xx+1 } yy=yy+1 }
32 return 0
33}
34// draw one 8x8 glyph at (x,y), each glyph pixel a scale x scale block
35func draw_char(fb: *u8, W: i64, H: i64, x: i64, y: i64, g: *u8, scale: i64, r: i64, gg: i64, b: i64) -> i64 {
36 var row: i64=0
37 while row<8 {
38 let bits: i64 = g[row] as i64
39 var col: i64=0
40 while col<8 {
41 if ((bits >> (7-col)) & 1) == 1 { fill_rect(fb,W,H, x+col*scale, y+row*scale, scale, scale, r,gg,b) }
42 col=col+1
43 }
44 row=row+1
45 }
46 return 0
47}
48func draw_text(fb: *u8, W: i64, H: i64, font: *u8, idx: *u8, n: i64, x: i64, y: i64, scale: i64, r: i64, g: i64, b: i64) -> i64 {
49 var k: i64=0
50 while k<n {
51 let gi: i64 = idx[k] as i64
52 draw_char(fb,W,H, x + k*9*scale, y, ((font as i64) + gi*8) as *u8, scale, r,g,b)
53 k=k+1
54 }
55 return 0
56}
57// install one 8-row glyph into the font table
58func gly(f: *u8, i: i64, r0: i64, r1: i64, r2: i64, r3: i64, r4: i64, r5: i64, r6: i64, r7: i64) -> i64 {
59 let o: i64=i*8
60 f[o]=r0 as u8; f[o+1]=r1 as u8; f[o+2]=r2 as u8; f[o+3]=r3 as u8; f[o+4]=r4 as u8; f[o+5]=r5 as u8; f[o+6]=r6 as u8; f[o+7]=r7 as u8
61 return 0
62}
63func put32(buf: *u8, o: i64, v: i64) -> i64 { buf[o]=(v & 0xFF) as u8; buf[o+1]=((v>>8)&0xFF) as u8; buf[o+2]=((v>>16)&0xFF) as u8; buf[o+3]=((v>>24)&0xFF) as u8; return o+4 }
64func put16(buf: *u8, o: i64, v: i64) -> i64 { buf[o]=(v & 0xFF) as u8; buf[o+1]=((v>>8)&0xFF) as u8; return o+2 }
65
66func main() -> i64 {
67 gui_puts("NishiOS GUI rung-1: the kernel draws a desktop into a framebuffer -> real BMP image\n" as *u8)
68 let W: i64 = 320
69 let H: i64 = 200
70 let fb: *u8 = sys_mmap(W*H*3)
71
72 // font: N I S H O (indices 0..4); "NISHIOS" = N I S H I O S
73 let font: *u8 = sys_mmap(64)
74 gly(font,0, 0x81,0xC1,0xA1,0x91,0x89,0x85,0x83,0x81) // N
75 gly(font,1, 0x7C,0x10,0x10,0x10,0x10,0x10,0x7C,0x00) // I
76 gly(font,2, 0x7E,0x80,0x80,0x7C,0x02,0x02,0x7C,0x00) // S
77 gly(font,3, 0x81,0x81,0x81,0xFF,0x81,0x81,0x81,0x00) // H
78 gly(font,4, 0x7C,0x82,0x82,0x82,0x82,0x82,0x7C,0x00) // O
79 let idx: *u8 = sys_mmap(8)
80 idx[0]=0 as u8; idx[1]=1 as u8; idx[2]=2 as u8; idx[3]=3 as u8; idx[4]=1 as u8; idx[5]=4 as u8; idx[6]=2 as u8 // N I S H I O S
81
82 // --- render the NishiOS desktop ---
83 fill_rect(fb,W,H, 0,0, W,H, 0,40,60) // desktop background (dark teal)
84 fill_rect(fb,W,H, 0,0, W,20, 30,30,120) // top title bar
85 draw_text(fb,W,H, font, idx, 7, 6,3, 2, 255,255,255) // "NISHIOS" in the title bar (white, scale 2)
86 fill_rect(fb,W,H, 50,45, 220,120, 210,210,220) // a window (light gray)
87 fill_rect(fb,W,H, 50,45, 220,16, 60,60,160) // the window's own title strip
88 draw_text(fb,W,H, font, idx, 7, 54,47, 1, 235,235,245) // window title text (small)
89 draw_text(fb,W,H, font, idx, 7, 66,95, 2, 20,20,60) // window body logo (dark, scale 2)
90 fill_rect(fb,W,H, 0,185, W,15, 20,20,40) // taskbar
91 fill_rect(fb,W,H, 4,187, 34,11, 80,200,80) // a green "start" button
92
93 // --- export as a real 24-bit BMP (BGR, bottom-up) ---
94 let rowbytes: i64 = W*3
95 let pixbytes: i64 = rowbytes*H
96 let filesize: i64 = 54 + pixbytes
97 let buf: *u8 = sys_mmap(filesize + 16)
98 buf[0]=66 as u8; buf[1]=77 as u8 // 'B','M'
99 var o: i64=2
100 o=put32(buf,o,filesize); o=put32(buf,o,0); o=put32(buf,o,54) // file header
101 o=put32(buf,o,40); o=put32(buf,o,W); o=put32(buf,o,H); o=put16(buf,o,1); o=put16(buf,o,24) // info header
102 o=put32(buf,o,0); o=put32(buf,o,pixbytes); o=put32(buf,o,K_MAGIC_2835); o=put32(buf,o,K_MAGIC_2835); o=put32(buf,o,0); o=put32(buf,o,0)
103 var fy: i64=0
104 while fy<H {
105 let srcrow: i64 = H-1-fy // BMP is bottom-up
106 var x: i64=0
107 while x<W {
108 let i: i64=(srcrow*W+x)*3
109 buf[o]=fb[i+2]; buf[o+1]=fb[i+1]; buf[o+2]=fb[i]; o=o+3 // BGR
110 x=x+1
111 }
112 fy=fy+1
113 }
114 let fd: i64 = sys_openat_wr("knowledge/status/nishios_gui.bmp\x00" as *u8, 0x1a4)
115 if fd<=0 { gui_puts("GUI RED: cannot write bmp\n" as *u8); sys_exit(1); return 1 }
116 sys_write(fd, buf, filesize); sys_close(fd)
117 // HTML wrapper so it opens as a "window" (2x pixelated)
118 let html: *u8 = "<!doctype html><html><body style=\x27margin:0;background:#0a0a12;display:flex;align-items:center;justify-content:center;height:100vh\x27><div><img src=\x27nishios_gui.bmp\x27 style=\x27image-rendering:pixelated;width:640px;border:1px solid #333\x27><div style=\x27color:#8af;font-family:monospace;text-align:center;margin-top:8px\x27>NishiOS desktop -- drawn by the kernel into a 320x200 framebuffer, exported BMP</div></div></body></html>\x00"
119 let hd: i64 = sys_openat_wr("knowledge/status/nishios_gui.html\x00" as *u8, 0x1a4)
120 if hd>0 { var hn: i64=0; while html[hn]!=(0 as u8){hn=hn+1} sys_write(hd, html, hn); sys_close(hd) }
121
122 gui_puts(" rendered desktop -> knowledge/status/nishios_gui.bmp (" as *u8); gui_num(filesize); gui_puts(" bytes) + nishios_gui.html\n" as *u8)
123
124 // --- KAT ---
125 var pass: i64=0
126 var ttl: i64=0
127 let p_bg: i64 = (40*W+2)*3
128 ttl=ttl+1; gui_puts(" T1 desktop background drawn (0,40,60): " as *u8); if (fb[p_bg] as i64)==0 { if (fb[p_bg+1] as i64)==40 { if (fb[p_bg+2] as i64)==60 { pass=pass+1; gui_puts("PASS\n" as *u8) } else { gui_puts("FAIL\n" as *u8) } } else { gui_puts("FAIL\n" as *u8) } } else { gui_puts("FAIL\n" as *u8) }
129 let p_tb: i64 = (10*W+200)*3
130 ttl=ttl+1; gui_puts(" T2 title bar drawn (30,30,120): " as *u8); if (fb[p_tb] as i64)==30 { if (fb[p_tb+2] as i64)==120 { pass=pass+1; gui_puts("PASS\n" as *u8) } else { gui_puts("FAIL\n" as *u8) } } else { gui_puts("FAIL\n" as *u8) }
131 let p_win: i64 = (150*W+150)*3
132 ttl=ttl+1; gui_puts(" T3 window drawn (210,210,220): " as *u8); if (fb[p_win] as i64)==210 { if (fb[p_win+2] as i64)==220 { pass=pass+1; gui_puts("PASS\n" as *u8) } else { gui_puts("FAIL\n" as *u8) } } else { gui_puts("FAIL\n" as *u8) }
133 // title text: 'N' row0 col0 is set -> pixel (6,3) is white
134 let p_tx: i64 = (3*W+6)*3
135 ttl=ttl+1; gui_puts(" T4 title text 'NISHIOS' rendered (white glyph pixel @6,3): " as *u8); if (fb[p_tx] as i64)==255 { if (fb[p_tx+1] as i64)==255 { pass=pass+1; gui_puts("PASS\n" as *u8) } else { gui_puts("FAIL\n" as *u8) } } else { gui_puts("FAIL\n" as *u8) }
136 ttl=ttl+1; gui_puts(" T5 BMP well-formed ('BM', size, 24bpp): " as *u8); if buf[0]==(66 as u8) { if buf[1]==(77 as u8) { if (buf[28] as i64)==24 { pass=pass+1; gui_puts("PASS\n" as *u8) } else { gui_puts("FAIL\n" as *u8) } } else { gui_puts("FAIL\n" as *u8) } } else { gui_puts("FAIL\n" as *u8) }
137
138 gui_puts("NISHIOS-GUI-GATE passed " as *u8); gui_num(pass); gui_puts("/" as *u8); gui_num(ttl)
139 if pass==ttl { gui_puts(" verdict=GREEN (the kernel drew a real desktop to a framebuffer; open nishios_gui.bmp/html to SEE it)\n" as *u8); sys_exit(0); return 0 }
140 gui_puts(" verdict=RED\n" as *u8); sys_exit(1); return 1
141}