feat(explorer): S2 —— 新增图形壳应用 explorer 骨架,用户态经 devfs+mmap 直接写屏

Apps/Explorer:复制标准 app 骨架(main.vp / linker.ld / project.vpj,链接地址 0x400000)。启动流程:open /dev/fb0 → ioctl(FB_GET_INFO) 打几何 → mmap 拿到线性地址 → 等约 2 秒(等内核桌面首屏全量重绘过去)→ 画一条渐变横条验证像素真上屏 → open /dev/input/keyboard|mouse 验证输入设备可达 → 常驻不退出。

vpsdk/devfs.vp(新增 SDK 设备层):设备路径常量(与内核注册名逐字一致)、FB_GET_INFO 与 FbInfo 镜像(packed,与内核同布局)、fb_open/fb_get_info/fb_map/kbd_open/mouse_open。

build.py:把 Explorer 加进 required_apps 清单与完整构建分支;内核 main.vp 在四个 app 之后 spawn /APPS/explorer.elf。内核 desktop 与 explorer 并存属迁移中间态,S3 会把窗口合成整体交出去。
This commit is contained in:
TermiNexus committed 2026-10-16 18:43:13 +08:00
1 parent 7d5380148b
commit 6bd2390e08
6 files changed
+216

No files matched your search

+39
View File
@@ -0,0 +1,39 @@
ENTRY(__entry)
SECTIONS {
. = 0x400000;
.text : {
__entry = .;
*(.text.startup)
*(.text)
*(.text.*)
}
.rodata : {
*(.rodata)
*(.rodata.*)
}
.data : {
*(.data)
*(.data.*)
}
.bss : {
__bss_start = .;
*(.bss)
*(.bss.*)
__bss_end = .;
}
/DISCARD/ : {
*(.comment)
*(.note)
*(.eh_frame)
*(.eh_frame_hdr)
*(.reloc)
*(.rela)
*(.debug*)
}
}
+71
View File
@@ -0,0 +1,71 @@
import vpsdk.devfs as devfs
import vpsdk.process as process
import vpsdk.syscall as syscall
from stdint import *
import viperlib
def _dbg(msg: const str):
syscall._syscall1(uint64(syscall.Syscall.SERIAL_PUTS), uint64(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
info: devfs.FbInfo = devfs.fb_get_info(fb_fd)
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)
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) 等桌面把首屏全量重绘做完(否则刚写的像素会被 _mark_all_dirty 的首次重绘盖掉)。
# 这里没有时钟可用,就空转 yield:yield_cpu 约每 10ms 真让出一次,200 次约 2 秒。
for _i in range(200):
process.yield_cpu()
# 3) 画一条横贯屏幕的渐变条 —— 证明"用户态直接写 framebuffer 真的上屏"。
w: int = int(info.width)
h: int = int(info.height)
pitch_px: int = int(info.pitch) / 4
pixels: uint32* = uint32*(fb_va)
y0: int = h / 3
y1: int = y0 + h / 6
for y in range(y0, y1):
row: int = y * pitch_px
for x in range(w):
rr: int = (x * 255) / w
bb: int = 255 - rr
pixels[row + x] = uint32((rr << 16) | (0x78 << 8) | bb)
_dbg("[explorer] painted gradient band\n")
# 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)
# 图形壳暂时只是占位,常驻不退出(app 的 _start 绝不能再 return:栈顶下一格是 0)。
while True:
process.yield_cpu()
return 0
+30
View File
@@ -0,0 +1,30 @@
{
"name": "Explorer",
"version": "1.0.0",
"source_dir": "./",
"build_dir": "./__vpcbuild__",
"compiler": {
"cmd": "llc",
"flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=static", "-O2"]
},
"linker": {
"cmd": "ld.lld.exe",
"flags": [
"-m", "elf_x86_64",
"-T", "linker.ld",
"--oformat", "elf64-x86-64",
"--strip-all"
],
"output": "explorer.elf"
},
"includes": ["../..", "../../../../includes"],
"target": {
"triple": "x86_64-none-elf",
"datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
},
"options": {
"slice_level": 3,
"target": "llvm",
"strict_mode": true
}
}