Commit Graph
228 Commits
Author SHA1 Message Date
TermiNexus 266e6fbcfb feat(keyboard): 恢复 Shift/CapsLock 与方向键等特殊键
旧内核桌面 desktop.vp 里有完整的键盘解码(修饰键状态、特殊键映射、重复),S3e 下沉后丢了,只剩一个简化 scancode_to_ascii(shift/caps 字段从未赋值、方向键返回 0 被丢)。① 内核 keyboard.vp:新增解码层 _decode_scancode + 输出队列,跟踪 Shift(0x2A/0x36)/CapsLock(0x3A)/Ctrl/Alt,Shift 输出大写与上档符号、Caps 只翻转字母区;E0 前缀的独立方向键/Home/End/Delete/PageUp/PageDown/Insert 输出 ANSI 序列(ESC[A/B/C/D、ESC[H/F、ESC[3~/5~/6~/2~);释放码只清状态不产字符。devfs 的 ASCII 字节流契约不变。② vpui:Tk 新增 _ansi_state/_ansi_param 与 _fold_ansi_key(),在 KEY 分发前折叠 ANSI 序列,映射到既有 VK_* 语义(Text/Entry 里那些分支不再是死代码)。Trim:Insert 无 VK_INSERT 定义、被吞掉;typematic 重复未恢复;USB 键盘未涉及。
2026-10-17 13:01:56 +08:00
TermiNexus 66dc11364a fix(keyboard): 断码不再产出字符,修正每个键输出两个字符
scancode_to_ascii 先剥掉 bit7(KEY_RELEASED)再查表,导致按键释放也被翻译成同一个 ASCII —— 打 ls 得到 llss。断码直接返回 '\\0'。
2026-10-17 12:00:26 +08:00
TermiNexus e0308ba074 feat(input): 键盘接入 app —— 焦点回退 + 无聚焦控件时路由到文本控件
① explorer:新增 _key_target(),focused 无效(-1/已关/已最小化)时回退到 z 序最高的可见窗口,不再把键盘输入整体丢弃;载荷经核对无需改(explorer 传 evt.x,vpui.Text 读的正是 evt.x)。② vpui:Tk 新增 _first_text_widget(),mainloop 的 KEY 分支在 focused_widget 为空时回退到第一个可见的 Text/Entry,并同步置焦点;顺带修 Text.handle_event 只认 '\\r' 导致回车无效(内核给的是 '\\n')。Terminal 无需改动。
2026-10-17 12:00:19 +08:00
TermiNexus 51ce473092 feat(explorer): 任务栏窗口图标可点击激活/恢复
原来任务栏图标只画不响应:鼠标按下分支只做窗口 hit_test,点在任务栏上返回 -1 就什么都不做。新增 _taskbar_icon_x/_taskbar_icon_y/_taskbar_icon_hit/_activate_from_taskbar:几何与绘制共用同一套函数(避免热区与绘制分叉),筛选与 ordinal 顺序和 _draw_taskbar_rows 完全一致(used && active,含 minimized)。按下时先判 taskbar 图标命中 —— 命中则激活:minimized 先恢复并置脏,再走既有 focus()(更新 z 序、聚焦、整体置脏);未命中才继续原有窗口命中/拖动逻辑,本次点击不会被重复处理。
2026-10-17 11:55:22 +08:00
TermiNexus 0593f1798b fix(explorer): 窗口创建/销毁时整体置脏,避免内容残留
抽出 mark_all_windows()(所有可见窗口整体置脏,含阴影带),CREATE 与 DESTROY 分支各调用一次并保留 mark_titles()。原来这两处只标记标题栏 + 任务栏:新建窗口的内容区不脏(可能空白),关窗后下方露出的旧内容也不会重画,都要等鼠标经过才恢复。focus() 的内联循环同步收敛到该函数。
2026-10-17 11:53:14 +08:00
TermiNexus fbbb10099d fix(explorer): 焦点/层级切换时把可见窗口整体置脏
现象:窗口层级切换后内容不刷新,要等鼠标经过才覆盖。原因:focus() 只调 mark_titles()(标题栏 + 任务栏),没有标记内容区;被抬起的窗口内容区因此不会重画,原来压在上面的窗口仍留在屏幕。改为层级变化时对所有可见窗口调 mark_win(含阴影带),随后按新 z 序整体重画。
2026-10-17 11:52:17 +08:00
TermiNexus a19c5d1a53 perf(explorer): 修半透明/圆角混合底,并优化阴影与混合热点
① 混合底:上一版背景裁剪会跳过窗口覆盖的列,导致 back 在那些列保留上一帧内容;而 WIN7 标题栏 alpha 混合、WIN11/XP 圆角逐像素裁剪都需要读到正确背景。现在按样式在必要行补铺背景:WIN7 铺标题栏行 + 底部圆角段,WIN11/XP 铺上下圆角段,CLASSIC/WIN10 不铺(直角不透明)。补铺发生在所有窗口绘制之前,下层窗口随后正常覆盖。② 性能:阴影 alpha 改按行查表 + 无除法 SWAR 变暗;_blend/_blend_row 改 SWAR 并行混合(无除法、无格式分支);内容行不再先整行 fill 再 blit,直接 blit + 左右 1px 边框;圆角行逐像素只保留左右各 r 列,中段整段拷贝(宽 800 窗每圆角行从约 800 次降到 16 次);关闭键圆角先扫连续区间再整段 fill;render_range 缓存每个可见窗口的几何/样式,_top_z 只算一次并传入。
2026-10-17 11:36:34 +08:00
TermiNexus 9f372ce814 refactor(desk): ring_push/ring_pop 改整块 memcpy,避免协议加字段漏拷
原来两个环函数逐字段拷贝(type/win/pid/a..d/title),DeskMsg 新增 style 时漏同步,导致 style 在入队时被丢弃。改为按 DeskMsg.__sizeof__() 整块 memcpy:结构体以后再加字段自动生效,不会再出现忘记同步某一处的问题。
2026-10-17 11:26:59 +08:00
TermiNexus 384ab6e75f fix(desk): ring_push/ring_pop 漏拷 style 字段
根因:两个环函数是**逐字段拷贝**(type/win/pid/a/b/c/d + title),新增 DeskMsg.style 时没有同步补上。于是 app 侧正确写入 _msg.style(IR 已核对:store 到字段 7),但入队时被丢弃;explorer 读到的永远是共享槽里的残留 0 —— 日志表现为五个 CREATE 全 style=0,而 app 侧 [vpui] set_style NZ / geometry style!=0 完全正常。补上 push/pop 两侧的 style 拷贝。
2026-10-17 11:25:43 +08:00
TermiNexus a60c51076a diag(vpui): set_style 与 geometry 打印样式值,定位 style 恒 0
日志证据:五个 CREATE 全是 style=0,而 Scene3D 明明调了 set_style(window.STYLE_WIN11) —— 断点在 app 侧而不是 explorer。加两条一次性诊断:set_style 打印 0/NZ(确认方法被调用且参数非零),geometry 建窗前打印 self.style 是否为零。判读:两条都非零 ⇒ 断点在 _send/消息;set_style 就为 0 ⇒ window.STYLE_* 跨模块取值有问题。
2026-10-17 11:06:23 +08:00
TermiNexus 844fbff31c fix(kernel,explorer): 内核不再画屏消除残留;CREATE 打印 style 诊断
蓝色残留根因:main.vp 里 VGA_drv.Print 在 __init__ 尾部执行,而它晚于 app spawn —— explorer 已经把桌面画上去了,内核随后又往 framebuffer 打一行测试文本/清屏,于是永远盖在桌面上(整屏刷一次也救不了,因为之后又被画)。S3e 之后屏幕由 explorer 独占合成,直接删除内核侧 VGA 驱动创建与 Print。另:handle_request 的 CREATE 分支加一条诊断打印(style/w/h),用于确认 style 是否真的透传到 explorer —— 5 条日志,定位后撤。
2026-10-17 11:03:05 +08:00
TermiNexus ae39fab5d8 perf(explorer): 背景只铺窗口外区间;进主循环前整屏刷一次
① 修卡:render_range 原来对每一行都先铺整行背景(全宽)再画窗口,对窗口覆盖的那几百列纯属浪费 —— Scene3D 每帧刷全窗时等于每帧多拷一整屏背景。改为逐行只铺没被任何可见窗口覆盖的 x 区间(窗口收集与 z 排序提到背景之前)。② 修 VGA 残留:内核启动期的 VGA/gfx 测试内容(蓝色测试块)不属于任何脏行,只靠增量渲染会永远留在屏幕上;serve() 进主循环前先 mark_all + 整屏渲染一次。
2026-10-16 23:07:09 +08:00
TermiNexus 78b0ca8db7 feat(explorer): 窗口样式差异可见化(标题栏/边框圆角/按钮按样式区分)
参考 desktop.vp 的 _draw_app_window 与三枚按钮函数,把 5 种样式的差异做到一眼可辨:标题栏 CLASSIC 蓝紫纯色 / WIN10 近黑 / WIN11 深灰 / WIN7 浅蓝 Aero 渐变(alpha 玻璃) / WINXP 蓝渐变;边框 CLASSIC-win10 直角、WIN7/11/XP 圆角 r=8;按钮 CLASSIC 16x16 深蓝底灰 X、WIN10/11 46x30 无底色灰字形、WIN7 28x20、WINXP 21x21 且红底白 X。新增 _is_in_corner/_is_on_border/_corner_alpha 与按行绘制的 _draw_flat_row/_draw_round_row/_blend_row/_title_row_color;关闭键热区改为与绘制共用同一套几何函数,避免点击错位。标题栏高度仍统一用 TITLE_H=32:app 侧内容区高度依赖 vpui 的 TITLE_H,改高度会让内容错位,故差异只由配色/圆角/按钮体现。
2026-10-16 23:04:21 +08:00
TermiNexus aab4643dfa feat(desk): style 透传到 CREATE 请求;任务栏随焦点/窗口集合刷新
① style 透传:DeskMsg 增加 style 字段(放在 d 与 title 之间),window._send 增加 style 可选参数(默认 0,其它调用点不受影响),create_window 把 style 一并压进 CREATE 请求,explorer 的 handle_request 用它建窗(原来硬编码 0)—— 这样 vpui 的 set_style 才能真正生效,样式区分与圆角才有意义。DeskMsg 是共享协议结构,app 与 explorer 必须一起重编。② 任务栏刷新:mark_titles 追加标记任务栏条带(焦点切换会调它,任务栏图标配色随之更新),CREATE 与 DESTROY 分支也各调一次(图标数量变化)。
2026-10-16 22:42:58 +08:00
TermiNexus 465ef4a910 feat(build): 新增 --app=a,b 只编指定应用;默认不重编内核
默认行为改为**不重编内核**(多数迭代只改应用/库),需要时显式加 --kernel;--app=hello,explorer 只编译列出的应用(逗号分隔),不带 --app 仍编译全部应用;新增 --libs 显式编译 Libs 下的 .so(带 --app 时默认跳过库,要一起编就加 --libs);应用/库编译改为表驱动(APP_LIST/LIB_LIST),--noapp 的拷贝逻辑抽到 CopyExisting()。
2026-10-16 22:41:28 +08:00
TermiNexus c4ff870941 perf(explorer): 恢复逐脏段渲染与提交,避免退化成全屏重绘
上一版把脏行合并成一段连续范围渲染 + 一次提交,导致窗口(上)与任务栏(下)同时脏时每帧全屏渲染 + 全屏 4MB 提交,帧率崩。改回逐个脏段处理:每段先渲染进 back,随后逐行提交该段,段内一致性仍然成立,而写屏总量回到与脏区面积成正比。
2026-10-16 22:37:46 +08:00
TermiNexus 5f863bed71 perf(explorer): 合成改画后备缓冲,渲染末尾只提交脏行到 framebuffer
消除撕裂:原来所有绘制直接写真实 framebuffer,与显示器扫描并发 ⇒ 撕裂/闪烁;而用户态无法做垂直同步(QEMU/OVMF 的 GOP 线性帧缓冲没有 VGA 回扫信号,FB_WAIT_VBLANK 实测返回 0 空转)。改法对齐历史内核桌面的做法:DeskServer 增加进程本地后备缓冲 back(与 bg_row 同机制 mmap 匿名页),row_fill/row_blit/draw_char/阴影混合/任务栏 logo 等所有写入点从 self.fb 改到 self.back(back 行距 = fb_w,framebuffer 行距 = pitch_px),render() 先把脏行范围渲染到 back,末尾再把仍脏的行逐行 memcpy 提交到 framebuffer。分配失败时打印并退回直写。同时删掉已完成使命的 vblank iters 诊断打印。
2026-10-16 22:31:35 +08:00
TermiNexus 3945decc2d fix(explorer): 窗口覆盖到底部条带时把任务栏一并置脏
现象:任务栏要鼠标经过(光标行恰好落在它上面)才刷新一行。因为任务栏只在启动的 mark_all 画过一次,之后没有时钟每秒标脏,唯一能让任务栏行变脏的是窗口变化或光标自身所在行。窗口在 render_range 里是画在任务栏**之上**的,所以窗口覆盖到底部条带时必须连带标记任务栏条带,否则残留旧窗口内容、也无法恢复。
2026-10-16 22:17:25 +08:00
TermiNexus e1d31c3dc9 diag(explorer,fb0): vblank 返回等待迭代数并打点;脏行合并为一段渲染
排查'仍闪烁':① FB_WAIT_VBLANK 改为返回等待消耗的迭代数(到上限 = 超时 = 当前没有 VGA 回扫信号),explorer 首次调用打印一次 —— 用来判定垂直回扫同步是否真的生效;② render() 原来按连续脏行分段、每段各渲染一次,同一帧的多次写屏会跨越多个扫描周期,现在把脏行合并成一段连续范围一次性渲染,缩小撕裂窗口。
2026-10-16 21:55:36 +08:00
TermiNexus ecabd59518 feat(explorer): 还原窗口样式、阴影、任务栏与三枚窗口按钮,精简注释
对齐被删除的内核桌面 desktop.vp 的视觉:窗口改为四边框 + 标题栏(活动/非活动配色)+ 标题内容分隔线 + 内容按 1px 内缩合成,并按 WinSlot.style 选择样式配色;新增最小化/最大化/关闭三枚按钮;新增矩形窗口阴影(SHADOW_* + self._blend 按 framebuffer 格式做 alpha 混合);任务栏改为深色底 + 分 opper线 + 蓝色 logo 圆盘 + ViperOS 文本 + 每个活动窗口的带边框图标(焦点/普通/最小化三色)与标题首字符,高度 28→50 对齐旧桌面。置脏范围改用 mark_win(含阴影带)/ mark_titles(活动窗口切换时刷新所有标题栏)。同时精简全文注释(不保留历史沿革叙述)。任务栏时钟未实现:用户态无时间源;圆角未实现:style 尚未透传到 CREATE 请求;最小化/最大化仅外观未接线。
2026-10-16 21:47:44 +08:00
TermiNexus e41701e201 feat(fb0): 新增 FB_WAIT_VBLANK ioctl,合成前等垂直回扫
explorer 直接写 framebuffer 会与显示器扫描并发,造成撕裂/闪烁;而它在 Ring3 没有 IO 端口权限,不能像历史内核桌面那样自己 inb 0x3DA。所以在 /dev/fb0 上加 FB_WAIT_VBLANK(0x4601)由内核代等垂直回扫(带超时,无 VGA 时序时不死等),vpsdk 暴露 fb_wait_vblank(),desk_server.render() 写屏前调用一次。
2026-10-16 21:37:58 +08:00
TermiNexus 89c37cea22 fix(mouse): Y 增量取反成屏幕方向,修正上下移动反向
PS/2 协议的 Y 轴正方向是向上,而屏幕坐标 Y 向下为正,内核出口少了一次取反 —— 现场表现为鼠标上下移动反向。在 devfs 的 mouse_device.read 里统一转换成屏幕方向(消费方直接累加即可),与历史 desktop.vp 中 pkt_dy 的用法保持一致。只改内核,explorer 的 ELF 无需重编。
2026-10-16 21:33:31 +08:00
TermiNexus 1c0720a746 perf(explorer,vpui): 矩形填充改 rep stosd,鼠标输入一次取完队列
性能三处:① vpui._fill_rect 原为逐像素双重循环(带边界判断),app 每帧都要把整窗填一遍背景色(Scene3D 是 640×360 = 23 万次写),改为逐行 string.memset32(rep stosd),先统一裁剪再整行填充;② explorer.row_fill 同样逐像素,任务栏(1360 像素/行)与标题栏每帧要填上万像素,同样改 rep stosd;③ 鼠标延迟:poll_input 原来每轮只从设备取一个包,而 serve 一轮要跑 poll_requests + poll_input + render(毫秒级),PS/2 是 100Hz ⇒ 输入在队列里排队累积、光标明显滞后。改为一次把已到的包全部取完(位移累加、按键以最后一个包为准),并把位移累加+钳制抽成 _move_cursor()。
2026-10-16 21:31:04 +08:00
TermiNexus d840c7546f fix(vpui): 窗口改双缓冲,消除合成器读到半成品帧造成的闪烁
根因:Tk.redraw 每帧先 _fill_rect 把整窗填成背景色、再逐控件重画,而这块缓冲就是 explorer 合成时逐行 blit 的共享 arena(render_window_rows 直接从 arena 读)。于是整窗纯背景色、控件还没画的那段窗口期一旦被合成器采到就是一次闪烁;鼠标移动让 explorer 重画窗口所在行时同样会撞上。改法:Tk 增加进程本地后备缓冲(懒分配、窗口变大时重新分配并 free 旧的),整帧画完后一次性 memcpy 进 arena 再 flush,合成器任何时刻读到的都是完整帧。代价是每帧一次全窗拷贝(零拷贝通路只在合成那一段保留)。分配失败不静默:打日志后退回直写 arena。
2026-10-16 21:27:14 +08:00
TermiNexus 6eb000d01d fix(mouse): 鼠标设备改返回相对位移,光标位置归 explorer 累积
现场:鼠标一有输入,光标立刻跳到左上角。根因:devfs 的 mouse_device.read 在 size>=9 时填的是 mouse.get_x()/get_y(),而 mouse_x/mouse_y 这两个静态量从未被更新(irq_handler 只把解码后的包入队 packets[]),恒为 0;explorer 拿到 (0,0) 就照抄成光标位置。改法:内核只给出 PS/2 包里的相对位移 dx/dy(uint8 原始字节手工做有符号扩展,不依赖 int8 转换语义),光标位置作为 explorer 自己的状态累积并做边界钳制 —— 内核不知道屏幕几何,钳制本来就该在图形壳这一层。
2026-10-16 21:23:05 +08:00
TermiNexus 14568e49e5 fix(explorer): 光标改用 desktop.vp 历史里的 16x16 位图模板
上一版的三角是临时凑的形状,这里改回被删实现里的正规模板:16×16 位图掩码,0 = 透明(不写像素,背景由 render_range 先重画窗口内容恢复,移动不留残影)、1 = 黑边、2 = 白心。模板逐字取自 VKernel/Kernel/services/desktop.vp(S3e 删除前,git 13eee8e^)的 _CURSOR_ARROW;绘制时把同一行连续同色像素合并成一次 row_fill,并处理光标越出左右边界时的裁剪。同一历史文件里另有 _CURSOR_IBEAM / _CURSOR_RESIZE_EW / _CURSOR_RESIZE_NS / _CURSOR_RESIZE_NWSE 四套模板,将来接上 WinSlot.cursor 即可启用(注释里已记录出处)。
2026-10-16 21:18:06 +08:00
TermiNexus 7536cc6fdb fix(explorer): 光标换成指针形状,替换原来的 10x10 白框黑心方块
原实现是 row_fill 画一个 10x10 白方块再把中间 6x6 挖成黑,视觉上是个方框而不是鼠标指针。改为顶点在左上、逐行向右下加宽 1 像素的实心三角(12x12),白边 + 黑心;形状外的像素一个都不写,背景由 render_range 先重画窗口内容恢复,所以移动不留残影。脏行标记的高度同步从 CURSOR_SZ 换成 CURSOR_H。
2026-10-16 21:15:39 +08:00
TermiNexus a35896e36e test(explorer): 首次鼠标包打点,区分内核无中断与收到包未处理 2026-10-16 20:57:56 +08:00
TermiNexus 453c353e01 fix(sched): sched_yield 改为无条件切换,原实现只在时间片到期时才切
现场:app 用 4000 次 yield_cpu() 等 /dev/desk ready(explorer 负责 format 并置 ready=1),却全部超时——三个 app 都打出 [win] desk not ready (attach timeout) / create_window: connect FAILED / geometry win_id=-1,最后静默退出;而 explorer 的 [elf] wrapper pid=5 出现在所有 app 退完之后。根因:YIELD syscall 走 sched.Scheduler.try_reschedule(),而它只在 needs_reschedule(时间片到期,由 tick 置位)时才真正 _yield(),因此 N 次 sched_yield 全落在同一个时间片内一次都不切换 ⇒ 主动等待外设的进程被饿死、初始化者永远轮不上。修法:新增 Scheduler.yield_now() 无条件切换,YIELD 分支改用它;try_reschedule() 仍保留给中断/抢占路径使用。
2026-10-16 20:57:55 +08:00
TermiNexus 2f20cf42aa fix(vpui): 建窗失败改为可见错误上报;Scene3D 加分步诊断打点
背景:Tk.mainloop 的循环条件是 window.is_active(win_id),而 is_active 对 win_id < 0 直接返回 0 ⇒ 一旦 create_window 失败,app 一次循环都不进、一句日志都不打就 process.exit(0)(现场:Scene3D spawn 后静默退出,「没有窗口」就是这么来的)。改动:window.vp 的 _attach / _connect / create_window 四个失败返回点补 [win] 错误上报(open 失败 / mmap 失败 / desk 未就绪超时 / 请求环压入失败 / CREATED 超时),这些是错误可见性而非临时噪音,长期保留;Scene3D/main.vp 加 _dbg 分步打点(start / tk ok / canvas ok / scene ok / geometry 后的 win_id 与 buf / 渲染线程 / mainloop 返回),用于定位它到底死在哪一步,定位完成后撤除。
2026-10-16 20:51:07 +08:00
TermiNexus 2dacfa85fb fix(input): 启用 PS/2 鼠标初始化,桌面鼠标通路恢复
main.vp 里 mouse.init() 一直被注释,而 services/mouse.vp 的 start() 只做 USB 鼠标初始化 ⇒ PS/2 鼠标的 IRQ12 从未注册、采样率与数据上报从未使能。表现是 explorer 能 open /dev/input/mouse(fd=3)却永远收不到事件,桌面有键盘无鼠标。恢复调用。
2026-10-16 20:51:01 +08:00
TermiNexus 48017a3d66 chore(devfs): 清理定位期临时探针,directory_count/file_count 恢复 static
main.vp:删除 devfs 注册结果回读([main] devfs reg / files[k] / find_in_dir / serial0(before fb/desk) fd)、fb0 注册后回读([main] fb0 reg)、desk 共享区地址比对([main] desk shm)、serial0 查找结果([main] devfs serial0 fd)共 6 组诊断打印,去掉不再使用的局部量。保留 _CheckFrameLocals 系列(fail-fast 断言,不是探针)。devfs.vp:删除 framebuffer_device.ioctl 内的两条 [fb0] ioctl 诊断打印与随之无用的 import viperlib;directory_count/file_count 恢复 static(跨模块读取点已随探针删除)。fat32_types.vp 的 winread FAIL / dir read failed 保留为正式错误上报(错误必须立即可见),未改动。
2026-10-16 20:45:40 +08:00
TermiNexus cfb76b1b94 fix(ide): PIO 数据就绪只认 DRQ,并在发命令前/失败后清掉残留扇区
现场(serial.log 113-136):winread FAIL abs=8192 res=1,紧接着读根目录扇区 8192 读回 ELF 文件头 ⇒ 目录项全是垃圾 ⇒ Hello/Terminal/Gargantua 三个 spawn 连锁失败,之后又自行恢复(间歇)。两个成因:① waitDataReady 把 DRDY 当作数据就绪(DRDY 空闲时恒为 1),在 BSY 刚清、DRQ 未置的窗口里误判成功,随后从 0x1F0 读到上一次传输的残留;② 一旦某次读提前失败返回、数据没被取走(DRQ 仍置位),ATA 规定设备会忽略命令块寄存器写入 ⇒ LBA 保留旧值 ⇒ 后续每次读都拿到同一块残留缓冲,形成连锁污染。改法:waitDataReady 改为必须 !BSY && DRQ;新增 flushStaleData(),在 readSector/writeSector 发命令前以及失败返回前把残留扇区取走丢弃。未改 includes,无需三重编译。
2026-10-16 20:39:56 +08:00
TermiNexus 4d0f29d533 fix(devfs): create_file 缺对象时直接失败,不再自建栈实例节点
把 files[] 改成指针数组后,else 分支的 new_file: devfs_file* = devfs_file() 暴露为栈实例逃逸,编译器以 LifetimeError 拦截(stack address escapes)。改为缺对象一律 return -1:设备节点必须由调用方以长寿命实例(模块级/堆)提供,不静默造占位对象。现有调用点(devfs_register_serial0/serial1/keyboard/mouse/fb0/desk)全部传对象,行为不变。
2026-10-16 20:34:02 +08:00
TermiNexus 441ea0c883 fix(devfs): files[] 改存指针,避免设备对象按值切片丢掉派生字段
现场:main.vp 探针读模块级 fb0_file 得 w=1360 h=768 pitch=5440(注册正确),而 explorer 侧 [fb0] ioctl 的 self 读得 w=0 h=0 pitch=0 fmt=0。IR 证据(devfs 编译单元 .text.ll 的 create_file):files[file_count] = *file_ptr 被生成成 llvm.load/llvm.store 整个 devfs_file 结构体(88 字节基类布局),派生字段 fb_phys/fb_size/fb_w/fb_h/fb_pitch/fb_format 从未被写入,保持 __init__ 的 memset 0;vptr 一并拷过去所以虚调用仍能进 framebuffer_device.ioctl,但读到的派生字段全是 0。这是对象切片而非编译器缺陷。

改法:files 由 [MAX_DEVICE_FILES]devfs_file 改为 devfs_file* 指针数组,create_file 只登记指针(设备对象由 devfs_register_* 以模块级实例持有,生命周期贯穿内核运行期);find_in_dir/list_in_dir 相应去掉取址;__init__ 的 memset 长度改为指针数组真实大小 8*MAX_DEVICE_FILES(原来按 devfs_file.__sizeof__() 清会越界清掉后面全局量)。顺带修好同样依赖派生字段的 /dev/desk(shm_device.shm_phys/shm_size)。
2026-10-16 20:32:12 +08:00
TermiNexus 3c595527bc test(fb0): 注册后立刻回读设备对象字段的临时探针
区分两种真因:devfs_register_fb0 传参即为 0,还是注册正确后设备对象字段被踩成 0。_CheckFrameLocals 只校验 __init__ 的长寿命局部量,管不到设备对象里的字段,因此补一条紧跟注册之后的回读探针,与 explorer 侧 [fb0] ioctl 的 w/h 对读即可定位
2026-10-16 20:23:58 +08:00
TermiNexus 0fd1bf7567 fix(fb0): 几何登记为 0 + FbInfo 按值返回只回传一半 —— explorer 拿不到 framebuffer 几何
现场(serial.log 330-339):[fb0] ioctl fd=6 req=0x4600 arg=0x7fffffdffb78 w=0 h=0 ⇒ request 与 arg 传递都正常、写入回读也正常,真正的问题是设备对象里的 fb_w/fb_h/fb_pitch/fb_format/fb_phys/fb_size 全为 0;随后 explorer 以 pitch=0 调 mmap 失败退出。两个独立缺陷:① 内核侧——devfs_register_fb0 收到的是被清零的 width/height/pitch/fb_format/fb_size(main.vp 顶部注释记录的"__init__ 的 fb/width/height/pitch 局部量被偶发写坏"同类问题:这些长寿命局部量在 L121 之后、L304 之前被写坏,而 L366+ 的 _CheckFrameLocals 又不报,属时点相关),改为在调用点直接从 BootInfo 重取几何并补一条 _CheckFrameLocals(pre_devfs_fb0);② SDK 侧——fb_get_info 按值返回 32 字节 FbInfo,现场表现为只有前 16 字节有效、后 16 字节是调用方栈残留(size=1802724708 phys=0x130c30),改为出参 out + 返回 ioctl 返回码;③ explorer 侧改为判返回码与 pitch/size 非 0,失败即 _dbg 说明原因后 fail-fast 退出,不再带着 0 几何继续。
2026-10-16 20:20:36 +08:00
TermiNexus 35fc8b8ea8 chore(devfs): 临时探针 - fb0 ioctl 打印收到的 request/arg 与写入回读,定位 explorer fb 信息全 0
现场:explorer 打印 [explorer] fb 0x0 pitch=0 fmt=0 size=<垃圾> phys=0x130b90 —— 6 个字段里 3 个为 0、其余是残留,判定「这次 ioctl 没有真正写入用户缓冲」而非写错地址。两种可能:(a) syscall 派发把 request 传成别的值 ⇒ ioctl 直接 return -1;(b) 写入落在用户 VA 之外的地址空间。本探针在内核侧打印 fd/req/arg 与写入后回读的 w/h/pitch/fmt:若 req 不是 0x4600 ⇒ (a);若 req 正确且回读正常而 app 仍是残留 ⇒ (b)。app 侧应同时检查 ioctl 返回值(非 0 时绝不能用未写入的 info),下一步一并改。
2026-10-16 20:17:50 +08:00
TermiNexus 14011b1f2d fix(devfs): find_child/list_subdirs 的 dirp 漏了指针星号,导致 devfs_open 返回 -1
现场:devfs_register_* 之后 files[] 计数与名字全对、find_in_dir 正常,但 files[k].parent_dir 被写成 0x7d218(内核启动栈区 0x60000-0x80000),devfs_open(/dev/serial0) 一律 -1,用户态 app 因此建窗失败、起来就退。根因:devfs_directory.find_child 里 dirp 标注为值类型 devfs_directory 却赋 &directories[d](指针),list_subdirs 同样是这个 typo;编译器按值类型处理该变量,dirp.parent/dirp.name 读错位数据,return dirp 返回的是栈上残留值(0x7d218),偶然残留槽恰为 &directories[n] 时又看似正常(keyboard 的 0x135018 即此巧合),故表现为时对时错、多数错。改为 devfs_directory*(同文件其余 6 处 &directories[]/&files[] 本就带星号)。
2026-10-16 20:14:39 +08:00
TermiNexus 745c0e0b83 fix(devfs): directory_count/file_count 去掉 static,允许跨模块读取用于诊断
编译器报 NameError: drivers.devfs.devfs.directory_count is a static symbol, cross-module use is forbidden(static 仅定义模块内可见)。这两个只是注册计数,改为普通模块级变量即可让 main.vp 的临时探针读取;文件内原有的 global directory_count, file_count 声明语义不变。
2026-10-16 20:01:10 +08:00
TermiNexus 9469ee8b4d chore(main): 临时探针 - 打印 devfs 注册计数/files[] 名字/直接 find_in_dir,三分 devfs_open=-1 的成因
devfs_open(/dev/serial0) 在仅注册 serial0/serial1/keyboard/mouse 之后就是 -1,且对象布局已由 MLIR 证明正确(vptr 在偏移 0、files[32] 按值数组、create_file 的路径切分与整份拷贝写法正确)。故加三条探针:注册后打印 directory_count/file_count、逐个打印 files[k].name/private_data/parent_dir、并用 files[0].parent_dir 直接调 find_in_dir(serial0)。判读:counts=0 或名字空 ⇒ 注册没落表;名字正常而 find_in_dir 返回 None ⇒ 名字比较路径坏;两者都正常而 devfs_open 仍 -1 ⇒ 封装层路径切分/返回值问题。
2026-10-16 19:59:47 +08:00
TermiNexus 73c29a5bd4 fix(fat32): 目录项读取失败时打印 dir read failed 现场并 fail-fast
dir_read_entry 里 win_read 失败原本静默返回 FR_DISK_ERR:错误虽有上抛,但没有任何现场,上层只能看到 find end res=1(当成找不到文件)或把 win[] 里的残留字节当垃圾目录项(name=ELF、attr=00)解析,无法判断根因是读盘失败。现改为显式打印 [f32] dir read failed sec/off/dirsec/res 后再返回 FR_DISK_ERR。
2026-10-16 19:49:01 +08:00
TermiNexus 887736d397 chore(main): 临时 bisect 探针 —— 在注册 fb0/desk 之前也查一次 devfs serial0
上一条探针已否掉重叠假设:desk shm=0xa781000(8MB)与 devfs_files=0x135140 / devfs_dirs=0x134cb0 相距极远,不重叠。本次再补一条 before 探针:若 “before fb/desk” 那行 fd>=0 而之后那行 fd=-1,即证明是本轮新增的 fb0/desk 注册把 devfs 表搞坏;若两行都 -1,则问题在更早(注册顺序/serial0 本身)。
2026-10-16 19:44:13 +08:00
TermiNexus 7f071b495a chore(main): 临时量出 desk 共享区与 devfs 注册表地址,验证 mm 是否把堆分到内核 .bss 上
MLIR 已排除编译器布局问题:devfs_file = (ptr, array<64xi8>, ptr, ptr)(vptr 在 0、基类字段在前)、framebuffer_device 在其后追加 6 个字段、files[32] 按值数组 —— 全对、无重叠。于是头号嫌疑转为:devfs_register_desk 里那块 8MB mm.malloc 与内核 .bss(devfs 的 directories/files/directory_count/file_count)重叠,把注册表踩掉 ⇒ devfs_open 查不到设备 ⇒ fd=-1(且 serial0_open 不会被调、无 [serial0] open: 打印)。本探针打印 desk shm 地址/size 与 devfs_files/devfs_dirs 地址,一眼可判重叠。定位后连同上一探针一并删除。
2026-10-16 19:42:30 +08:00
TermiNexus 2481336dec chore(main): 临时加 devfs serial0 查找结果探针,定位“用户态写串口无输出/设备查找失败”
症状:三个 app 启动即 [syscall] exit called(create_window 失败 ⇒ is_active(-1) 为 0 ⇒ mainloop 立即返回)、explorer 的 [explorer] start 一行都没有、内核自己的 devfs 冒烟输出([serial0] open/write)也全缺 ⇒ 三条都指向 devfs_open 查不到设备。探针:[main] devfs serial0 fd=N(<0 即 lookup 失败,serial0_open 不会被调、也就不会有 [serial0] open 打印)。定位后删除。
2026-10-16 19:38:25 +08:00
TermiNexus d6d07b25ee feat(s5): 删除 TELL/SIZE 私有 syscall —— 用 lseek 的 SEEK_CUR/SEEK_END 表达
内核:SEEK 补真正的 whence(0=SET 1=CUR 2=END,END 时 offset 为距末尾的距离,offset>size 返回 -1);删除 TELL/SIZE 的枚举项、派发分支与 TELL/fsize 函数(fat32.tell/size 仍留给 SEEK 内部用)。

SDK:vpsdk/fs.vp 的 seek() 增加 whence 参数(默认 0,调用方不变);tell() 改为 lseek(fd,0,SEEK_CUR);fsize() 改为三次 lseek 往返(记住位置 → 跳末尾取大小 → 复位),不改变最终位置。

私有段只剩 5 项:OPENDIR/READDIR/CLOSEDIR(openat+getdents64)与 LOAD_SO/SO_CALL1(动态库加载下沉)。
2026-10-16 19:33:46 +08:00
TermiNexus 1fc1fab1e1 feat(explorer): S3f —— 补用户态字体渲染:窗口标题文字 + 任务栏底板
字体表直接复用 vpsdk/vpui.vp 的 font(8x16 点阵、MSB 优先、row = font[ci][fy]),不另抄第二份;新增 draw_char/draw_str。

合成器:① 窗口标题栏画上该窗口的 title(标题在建窗时随 CREATE 消息内联进槽位,set_title 走 REQ_TITLE 更新);② 屏幕底部 28px 任务栏底板 + "ViperOS" 标签,绘制次序为 背景 → 任务栏 → 窗口(窗口可盖住任务栏,与旧内核桌面同序)。

仍未做:任务栏时钟(用户态暂无时间源,需要 /dev/rtc 之类)、窗口侧边框与样式区分、最小化、拖边缩放、崩溃进程的窗口回收。
2026-10-16 19:28:08 +08:00
TermiNexus d96362186f fix(explorer): push_event/push_event_pid 补齐第 6 个参数 d,修 llvm.call 操作数个数错
现象:f4160f3611036a68.ll 报 llvm.call 传了 7 个操作数、被调方期望 6。根因是我这边实参个数不一致:签名是 (self,win,etype,a,b,c)(self+5=6),而鼠标三处调用按"再带一个 buttons"写了 6 个实参(self+6=7)。DeskMsg 本来就有 a..d 四个载荷位,window.vp 也早已用 _evt.d 当 button,所以正确修法是把签名补上 d:push_event/push_event_pid 各加一个 d 参数,并把原先少传的 KEY/CLOSE/CREATED 三处补齐。

说明:这不是编译器实现问题 —— 编译器(LLVM verifier)报得准确;只是实参个数这类错误没在前端拦住、漏到了 LLVM 层,属于前端检查的缺口,可另案记录。
2026-10-16 19:24:09 +08:00
TermiNexus c4305301fc fix(vpsdk): 恢复 window.vp 对 vpsdk.stdlib 的 import(app 裸名 malloc 的链接来源)
现象:ld.lld 报 undefined symbol: malloc,引用来自 Apps/HelloWorld 的 _start(4 处)。根因:malloc 是 vpsdk/stdlib.vp 的 CExport 裸名,app 侧那些裸名调用要靠 window.vp 的 import vpsdk.stdlib 把 stdlib 拉进链接图;S3d 重写 window.vp 时把它当未使用删掉了 ⇒ stdlib 不再进图 ⇒ malloc 无人定义。

同时删除 main.vp 里上一轮加的两处说明注释(按用户要求不保留)。
2026-10-16 19:20:26 +08:00
TermiNexus d96b632226 chore(main): 删掉内核身份下调 slog.so log_info 的启动冒烟调用
S4 之后 log_info 走 open(/dev/serial0)+write 两个 syscall,而该调用点在 devfs_init/devfs_register_serial0 之前 ⇒ 那条日志必然丢失。直接删除这个早期验证用的冒烟调用(动态库路径仍被 HelloWorld 的 dynlib.load_so/so_call1 与内核 LOAD_SO/SO_CALL1 覆盖)。同步更新上方的自启动说明注释。
2026-10-16 19:19:00 +08:00