explorer 直接写 framebuffer 会与显示器扫描并发,造成撕裂/闪烁;而它在 Ring3 没有 IO 端口权限,不能像历史内核桌面那样自己 inb 0x3DA。所以在 /dev/fb0 上加 FB_WAIT_VBLANK(0x4601)由内核代等垂直回扫(带超时,无 VGA 时序时不死等),vpsdk 暴露 fb_wait_vblank(),desk_server.render() 写屏前调用一次。
94 lines
3.5 KiB
Plaintext
94 lines
3.5 KiB
Plaintext
import vpsdk.devfs as devfs
|
||
import vpsdk.desk as desk
|
||
import vpsdk.process as process
|
||
from stdint import *
|
||
import viperlib
|
||
import desk_server
|
||
|
||
|
||
def _dbg(msg: const str):
|
||
# S4:改走 /dev/serial0(SERIAL_PUTS 私有 syscall 已删除)
|
||
devfs.serial_puts(msg)
|
||
|
||
|
||
@__attribute__(section(".text.startup"), aligned(16))
|
||
export def _start() -> int:
|
||
_dbg("[explorer] start\n")
|
||
|
||
# 1) 打开 framebuffer 设备,问几何,再映射成线性地址
|
||
fb_fd: int = devfs.fb_open()
|
||
if fb_fd < 0:
|
||
_dbg("[explorer] open /dev/fb0 FAILED\n")
|
||
process.exit(1)
|
||
return 0
|
||
|
||
# fb_get_info 改为出参 + 返回码:必须判返回码,否则拿到的是"未写入的残留"
|
||
# (旧写法按值返回 FbInfo,只见前 16 字节有效 ⇒ size/phys 打出垃圾)。
|
||
info: devfs.FbInfo
|
||
_fb_ret: int = devfs.fb_get_info(fb_fd, &info)
|
||
if _fb_ret != 0:
|
||
_dbg("[explorer] FB_GET_INFO FAILED\n")
|
||
process.exit(1)
|
||
return 0
|
||
|
||
buf: [160]char
|
||
viperlib.snprintf(&buf, 160,
|
||
"[explorer] fb %ux%u pitch=%u fmt=%u size=%lu phys=0x%lx\n",
|
||
info.width, info.height, info.pitch, info.format, info.size, info.phys)
|
||
_dbg(&buf)
|
||
|
||
if info.pitch == 0 or info.size == 0:
|
||
# 几何为 0 ⇒ 内核侧登记/回填没成功(例如注册时传进去的 width/height 是 0)。
|
||
# 继续走只会以 pitch=0 画崩,这里 fail-fast 并把原因说清楚。
|
||
_dbg("[explorer] fb geometry invalid (pitch/size=0)\n")
|
||
process.exit(1)
|
||
return 0
|
||
|
||
fb_va: uint64 = devfs.fb_map(fb_fd, info.size)
|
||
if fb_va == 0:
|
||
_dbg("[explorer] mmap /dev/fb0 FAILED\n")
|
||
process.exit(1)
|
||
return 0
|
||
|
||
buf2: [96]char
|
||
viperlib.snprintf(&buf2, 96, "[explorer] fb mapped at 0x%lx\n", fb_va)
|
||
_dbg(&buf2)
|
||
|
||
# 2) 屏幕几何/映射地址已打印(上面两行),像素通路由此验证;真正的绘制在服务端。
|
||
fb: uint32* = uint32*(fb_va)
|
||
|
||
# 4) 输入设备可达性:能 open 到就说明 /dev/* 通路已打通
|
||
# (真正读事件、接管窗口合成放到 S3 —— 内核桌面此刻还在抢同一个 PS/2 口)。
|
||
kfd: int = devfs.kbd_open()
|
||
mfd: int = devfs.mouse_open()
|
||
buf3: [96]char
|
||
viperlib.snprintf(&buf3, 96, "[explorer] input: kbd=%d mouse=%d\n", kfd, mfd)
|
||
_dbg(&buf3)
|
||
|
||
# 5) 挂上 /dev/desk 共享区并格式化 —— explorer 是图形壳,共享区由它初始化,
|
||
# app 侧只 desk_ready() 校验。这里先把头打出来证明 IPC 底座可用。
|
||
desk_fd: int = desk.desk_open()
|
||
if desk_fd < 0:
|
||
_dbg("[explorer] open /dev/desk FAILED\n")
|
||
process.exit(1)
|
||
return 0
|
||
shm: uint64 = desk.desk_map(desk_fd)
|
||
if shm == 0:
|
||
_dbg("[explorer] mmap /dev/desk FAILED\n")
|
||
process.exit(1)
|
||
return 0
|
||
desk.desk_format(shm, info.width, info.height, info.pitch)
|
||
hdr: desk.DeskHeader* = desk.DeskHeader*(shm + desk.desk_header_off())
|
||
buf4: [144]char
|
||
viperlib.snprintf(&buf4, 144,
|
||
"[explorer] desk shm=0x%lx ready=%d slot=%lu arena=%u\n",
|
||
shm, hdr.ready, uint64(desk.desk_slot_size()), hdr.arena_off)
|
||
_dbg(&buf4)
|
||
|
||
# 6) 进入窗口服务端主循环(永不返回):请求环 + 输入 + 合成。
|
||
srv: desk_server.DeskServer = desk_server.DeskServer()
|
||
srv.init(fb, int(info.width), int(info.height), int(info.pitch), info.format, shm, kfd, mfd, fb_fd)
|
||
_dbg("[explorer] desk server running\n")
|
||
srv.serve()
|
||
return 0
|