Commit Graph
83 Commits
Author SHA1 Message Date
TermiNexus 93a4f5bdcb chore(explorer): remove [explorer] debug output
Drop startup/input/frame tracing from the desktop shell and window
server, including the one-shot diagnostic counters.
2026-10-27 17:54:06 +08:00
TermiNexus 976b8307f9 chore(scene3d): remove [s3d] debug output
Drop the serial tracing from startup, the render thread and the
pre-render loop.
2026-10-27 17:52:32 +08:00
TermiNexus 925e0ffaea feat(power): add reboot and shutdown syscalls and terminal commands
- REBOOT (0x1008): 8042 reset with PCI 0xCF9 fallback
- SHUTDOWN (0x1009): ACPI S5 writes to the common QEMU/Bochs/VBox
  PM1a control ports, then halt
- vpsdk.process.reboot()/shutdown()
- Terminal commands: reboot, shutdown (help updated)
2026-10-27 16:56:26 +08:00
TermiNexus c379cadc38 feat(terminal): 日志与输入行之间留空行
- Text._exec_input 在本命令确实产生输出时追加一个空行,
  提示符与最后一条日志之间保持一个空行;无输出的命令不补
- 用实际 line_count 判断,clear 后不会被误补空行
- Terminal 启动时的介绍日志后也插入一个空行
2026-10-27 14:52:09 +08:00
TermiNexus 99c8cea8ee feat(window): 滚轮事件下传与 Terminal 滚动
- desk 协议新增 DESK_MOUSE_WHEEL;window 新增 EVENT_TYPE_WHEEL
- Explorer 汇总一批鼠标包的 wheel 增量,投给光标下的窗口
  (窗口内坐标;任务栏区域不投递)
- InputEvent.button 承载滚轮有符号增量(uint8 编码,消费方按 int8 解码)
- vpui.Text 处理 WHEEL:每次滚动 3 行,按“输出行 + 输入行”总行数钳制,
  并同步滚动条;输入行仍保持在可视区内
2026-10-27 14:50:04 +08:00
TermiNexus 5e2598a992 feat(terminal): 输入行跟随输出并显示当前路径
- Text 不再把输入行固定在控件底部:输入行紧跟在最后一条输出之后,
  输出增加时随之下移;超出可视区时 scroll_offset 保持输入行可见
- Text 增加 prompt 字段 / set_prompt;点击定位、光标、_exec_input、
  滚动条计算都按实际 prompt 长度和总行数(输出行 + 输入行)计算
- Terminal 的提示符改为“归一化路径 + /> ”:
  根目录是 "/> ",/apps 是 "/apps/> "
- cd 成功后更新提示符;裸 cd 仍打印当前路径
2026-10-27 14:41:35 +08:00
TermiNexus ab18afd26e feat(window): WIN7 关闭按钮红底改为圆形
WIN7 样式的关闭按钮仍保留 28×20 的命中区,但红色背景改为
区域内居中的 20×20 圆,圆心与 X 图标中心重合;
hover / 按下时的颜色变化规则不变。
2026-10-27 14:40:48 +08:00
TermiNexus b94de46ce5 feat(window): 任务栏层级提升到窗口之上
- render_range 顺序改为:背景 → 窗口 → 任务栏 → 光标,
  窗口拖到任务栏区域时会被任务栏盖住
- hit_test 在任务栏高度内直接返回 -1,避免窗口视觉上被任务栏
  盖住后还能点到下面的窗口;任务栏图标仍由 _taskbar_icon_hit 处理
2026-10-27 14:35:38 +08:00
TermiNexus f9e91a9d6e fix(gargantua): 调整窗口大小时不再改画布源尺寸
现象:计算过程中拖动/缩放 Gargantua 窗口,tid=2 以
[0E] rip=0x413f2f cr2=0x46c000 退出。

原因:RESIZE 处理器把 FBCanvas 的源尺寸用
set_fb(fb, new_w, new_h) 改成窗口内容尺寸,但 _fb_mem 仍是固定的
160×90。窗口变大后 FBCanvas.draw 会按新的 _cw 逐行读取
self._fb[py*_cw+px],越过 _fb_mem 末尾(0x46c000)触发用户态缺页。

修复:RESIZE 时不再改画布源尺寸,只调用 root.redraw() 按新窗口
尺寸重绘;超过源尺寸的部分由 FBCanvas 现有的 min(_cw,self.w)
裁剪逻辑处理。
2026-10-27 14:23:35 +08:00
TermiNexus 845633773b fix(window): 标题栏按钮保持方角,仅按窗口圆角裁剪
上一版把 WIN11 关闭按钮红底做了内收 + 按钮圆角,不符合预期:
窗口是全圆角,但按钮本身应当是方角,唯一需要丢弃的是与窗口
右上圆角重合的那部分像素。

- WIN11 关闭按钮红底恢复整块矩形,仅用 _window_round_keep 丢弃窗口圆角外像素
- WIN7/WINXP 按钮底色同样改为方角(r=0),仍做窗口轮廓裁剪
- 按钮命中区域保持不变(仍是完整方角矩形)
2026-10-27 13:25:41 +08:00
TermiNexus 8dc806e7e2 fix(window): WIN11 关闭按钮 hover 红底按圆角裁剪
WIN11 窗口是圆角样式,关闭按钮又紧贴右上角;此前 hover/按下直接
row_fill 整块矩形,红色会盖住窗口圆角外的区域。

- 新增 _window_round_keep:按窗口自身的圆角轮廓判断像素是否可画
- 新增 _close_fill_row:同时按“按钮圆角 + 窗口圆角”双重裁剪
- WIN11 关闭按钮红底向内收 2px、圆角 3px;WIN7/WINXP 也走窗口轮廓裁剪
- 移除不再使用的 _round_fill_row
2026-10-27 13:21:34 +08:00
TermiNexus b9efc30d71 feat(window): 标题栏 hover、最大化图标、边缘缩放与控件光标
1) 标题栏按钮 hover:min/max 背景按 alpha 压暗,close 变红、图标变白;
   按下态更深。新增 hover_btn_win/kind,仅在变化时重画标题栏。
2) 最大化图标改为单线方框(上边框不再两行加厚);最大化后显示
   两个错位重叠方框作为“还原”。
3) 窗口边缘 5px 热区支持拖拽缩放:左/右 ↔、上/下 ↕、四角斜向;
   缩放中实时更新几何与内容缓冲(按 1.5 倍扩容,避免 arena 抖动),
   并向 app 发 RESIZE。
4) 新增 DESK_REQ_CURSOR / window.set_cursor_type 真正下发光标类型;
   explorer 按窗口 cursor + 边缘热区选择箭头 / I 形 / 手指 / 缩放光标。
5) vpui.Tk 在 HOVER 时按最上层控件设置光标:Entry/Text → I 形,
   Button → 手指;Entry 自己的光标请求移除,避免与集中逻辑冲突。
2026-10-27 13:14:42 +08:00
TermiNexus 6ecd891f64 fix(desk): 清空共享区槽表并按版本拒绝旧 app
现场:所有窗口消失、任务栏多出若干图标,点击后 explorer 以
[0E] rip=0x4061e8 cr2=0x1207e8fff 缺页退出。

原因:desk_format 只清 DeskHeader,没有清 0x100 起的 WinSlot 表。
DESK_SHM_SIZE 从 8MB 提到 32MB 后,mm.malloc 拿到的内存不再恰好全零
(混入 bootloader/固件遗留数据),随机 used/active 被当成窗口,
合成时按垃圾 fb_off 越界读像素。

- desk_format 先 memset 整个控制区(0x000000 ~ 0x200000)
- 分配槽位时整槽清零,避免复用残留
- DESK_VERSION 1 → 2,desk_ready 校验版本:
  旧 app 按旧 WinSlot 偏移读写会写坏共享区,现在直接拒绝挂载
- render_window_rows 校验 fb_off 范围,坏槽位只跳过,不再拖崩合成器
2026-10-27 12:52:23 +08:00
TermiNexus 8e94458fc2 fix(window): 最大化窗口不绘制阴影
最大化后窗口底边正好贴住任务栏,阴影(下沿 14px)会盖在任务栏上。
最大化状态跳过阴影绘制。
2026-10-27 12:44:54 +08:00
TermiNexus 03a5738702 perf(window): 最小化窗口的 FLUSH 不再置脏
应用最小化后仍会继续绘制并发送 FLUSH;此前每条 FLUSH 都标脏窗口区域,
合成器于是每帧重画背景。最小化时直接忽略置脏即可。
2026-10-27 12:43:53 +08:00
TermiNexus 632bc877df feat(window): 鼠标捕获与窗口最小化/最大化
鼠标:
- 内容区按下后记录 capture_win,按住期间的移动仍投 HOVER、松开投 RELEASE
  (即使光标移出窗口)。此前 btn_down != 0 时完全不发 HOVER,滚动条
  拖动拿不到位置更新。
- 标题栏按钮按下态改为通用 btn_win/btn_kind,支持关闭/最小化/最大化。

窗口:
- WinSlot 新增 maximized、restore_*、fb_cap 字段
- 最小化:隐藏窗口并把焦点交给最高可见窗口;任务栏点击恢复,
  再点一次已聚焦窗口则最小化
- 最大化:保存还原几何,铺满工作区(不含任务栏)并发送 RESIZE 事件;
  再次点击还原;拖动最大化窗口会先还原
- 像素缓冲按 fb_cap 复用,仅在容量不足时从 arena 扩容,避免反复
  最大化/还原耗尽 bump arena
- 1920x1080 最大化需要约 7.3MB 内容缓冲:DESK_SHM_SIZE 8MB → 32MB
2026-10-27 12:43:19 +08:00
TermiNexus 329a60b123 feat(terminal): 裸 cd 显示当前路径并归一化 cwd
- 只输入 cd 时不再重置到根目录,改为打印当前路径(pwd 语义)
- 新增 _normalize_path:折叠重复分隔符、去掉 .、处理 ..,
  根目录下 cd .. 仍为 /
- _cwd 保存归一化后的路径,pwd/ls 不再出现 /app/../.. 这类串,
  也避免路径越 cd 越长
2026-10-26 23:09:19 +08:00
TermiNexus fa1a92b568 chore(terminal): 链接不再 --strip-all,保留符号
便于崩溃现场按地址反查函数,与内核保留符号的做法一致。
2026-10-26 22:41:39 +08:00
TermiNexus bc089a0c69 feat(diag): 用户态窗口渲染链路临时打点
沿 Scene3D → FBCanvas → vpui.redraw → window.flush → explorer 合成器逐段打点,
用于定位窗口内容不上屏停在哪一环:

- Explorer:fb 哨兵像素回读、建窗槽位回读、首个 FLUSH、首帧 back/fb/任务栏像素、
  每 240 帧统计槽位/最上层窗口矩形/中心像素/共享缓冲首像素
- vpui:首次 redraw(缓冲地址尺寸)、st1~st4 分段(malloc/铺底/控件/拷回)、
  首次 flush、FBCanvas 首绘的源缓冲与像素
- Scene3D:主线程预渲染 3 帧(非清屏像素计数、三角形命中/调用数)、渲染线程
  入口与每 60 轮采样;渲染线程参数类型改为 rawptr

以上均为临时诊断,定位完成后应移除。
2026-10-26 22:41:32 +08:00
TermiNexus 8c31645c0f fix(gfx): floorf/ceilf 参数显式收窄为 float
min/max 是 double 内建,结果直接传给 floorf/ceilf 时编译器禁止隐式
double->float,实际会把 f64 的低 32 位当 f32 传参,使三角形包围盒取值全错。
内核 vesafb 与 Scene3D 两处光栅化同步修正。
2026-10-26 22:41:09 +08:00
TermiNexus 1ccacf8204 refactor(userspace): 清理 t/c 依赖与过时注释
Terminal、SerialLogger 不再 import t, c;dynlib 删除已失效的指针级数说明。
2026-10-26 22:40:07 +08:00
TermiNexus a3c1d221e6 fix(kbd,sched,scene3d): 键盘报告去重;用户线程 return 跳板修正;关窗时序
① Enter 洪流:usb_kbd._on_int_complete 的按下沿依赖 kbd_prev_keycodes,而状态提交在函数末尾第三趟循环;kbd_report 是常驻 DMA 缓冲,按键保持/NAK/重复完成都会让回调反复看到同一份报告 ⇒ 每次中断都注入一次 Enter,Terminal 刷出 [cmd] enter,explorer 被拖死。新增 kbd_prev_sig(modifier+6 keycode 的标量签名)整份报告去重;同一趟循环里先提交 prev 再判定;全 0 报告只更新状态;抽出 _rearm() 保证所有出口都重排中断 IN 且 DMA 读取先于重排。② explorer 限流:kb_pend[64] + _deliver_kb,单轮最多投递 64 字节,ring_push 返回 -1 时停在当前字节(不再当成功丢弃),pending 清空才补读;鼠标取包同样 64 包/轮上限。③ 关窗卡死根因:process.vp 的 _tramp_code 是 mov eax,34; int 0x80; jmp \$,而 Syscall.THREAD_EXIT=60 ⇒ 用户线程 return 后走 syscall 34(无匹配分支)返回 Ring3 在 jmp \$ 空转,空转不再产生 syscall,而内核只在 syscall 返回路径消费 needs_reschedule ⇒ 协作式调度彻底停摆,整机冻结。sched.create_user_thread 在跳板映射后把立即数低字节改为 60(幂等)。④ 次生:主线程先退会立即回收自己的用户栈,而 Scene3D 渲染线程还引用其栈上对象。Scene3D 渲染线程退出改为显式 process.thread_exit() 并置 _render_done;主线程 mainloop 返回后先 yield 等渲染线程退出(上限 10000 次)再 exit。⑤ window.flush 在槽位 used==0 时直接返回;vpui.Tk.redraw 入口判 win_id 与 is_active,窗口已回收时不碰 arena 不 flush。
2026-10-17 16:49:37 +08:00
TermiNexus 7e7204a09a fix(mm,explorer): slab 页严格归属与分配位图;关闭按钮按下态与松手关闭
① [mm] BAD blk 的解码:site=2=free_block、reason=1=magic 失配;blk 实际是 slab_page+16(页在 0x1b56020),sz 是该页 free_head,而非块大小。被踩的是页头前 8~16 字节,最可能是无锁 buddy 在 usb.init(sched.enable 之后)与用户进程 MMAP 并发 malloc 时重复授予同一 8KB 块。② mm 加固:_slab_find_page 不再只看 magic,新增 _slab_page_owns(block_ptr 回指、指针在对象区内、按 obj_size 对齐、序号 < total_objs);slab_page 新增 alloc_bits,只有已分配对象可 free,重复释放/野指针被拒,避免 free_count 提前到 total_objs 把仍在用的页拆回 buddy;free_obj 拒绝已全空继续收对象;_create_page 失败路径清零 magic。③ hid_ascii 仅只读且已有边界检查,非本次堆损坏来源;usb_kbd 把 code >= 64 改为显式早返回。④ 关闭按钮:按下只置 close_down 并置脏(按钮画按下态:CLASSIC 40,40,70→20,20,45;WIN7/XP 红底 200,50,50→140,25,25;WIN10/11 补深灰底),松手且仍命中才发 CLOSE + used=0,移出则撤销按下态恢复;几何复用 _close_btn_* 同一套函数。
2026-10-17 16:23:44 +08:00
TermiNexus bd1cd2f228 fix(explorer): 逐包跟踪按下/释放沿,修复单击需长按
批量取包后只保留最后一包的按键状态:快速点击的按下+释放落在同一批里,最终 btn=0,按下沿永远检测不到(表现为必须长按)。改为批次内维护 cur 并记录 saw_press(任一分包出现 0→1)。同时把释放与拖动从 elif 改为独立 if:同批内按下并松开时,按下分支之后必须立刻走释放,否则 btn_down 卡在 1,后续点击继续失效。
2026-10-17 15:52:32 +08:00
TermiNexus 4003b0d286 diag(explorer): 首次键盘字节打点,区分内核无数据与事件未送达
键盘仍无输入:在 poll_input 的键盘分支加一次性打印(kn 字节数 + 首字节)。判读:没有该行 ⇒ 内核 keyboard 设备这一侧没数据;有该行但 Terminal 仍不显示 ⇒ explorer 投递或 vpui 路由问题。
2026-10-17 13:26:50 +08:00
TermiNexus b405973e91 perf(gargantua): 渲染按 64 像素分块并每块让出 CPU
原实现一次连续算 4 行(640 像素,单像素最坏 100 步),长时间不让出,单核下输入/合成全卡。改为 64 像素一块,每块后 process.yield_cpu();每 4 行仍 root.redraw() 保持渐进刷新节奏;事件轮询从每 4 行一次提升到每 64 像素一次。单次占用降到约 1/10,逐像素结果不变(仅调度顺序变化)。
2026-10-17 13:20:22 +08:00
TermiNexus 850c4b8adc fix(keyboard,explorer): 键盘解码单队列化消除读循环自旋;关窗清理悬垂状态
① 卡死/键盘无效:has_data() 把'原始待解码'与'已解码可读'混成一个计数,而 read_scancode() 只看已解码队列;一旦出现 has_data()>0 但本次取不到字节(断码/修饰键/E0 前缀),devfs 的读循环里 count 不前进而 has_data() 恒真 ⇒ asm.sti() 后无 yield 自旋,协作式调度下整机停死。改为解码下沉到 IRQ、只保留一个输出队列(has_data()==out_count,read_scancode() 返回 0 ⟺ 队列空),ANSI 序列整条原子入队;devfs 读循环以 read_scancode() 返回 0 为唯一终止条件。② 关窗:DESTROY 与关闭键路径清 drag_win,focus/_activate_from_taskbar 校验槽位存活;poll_requests 单轮最多处理一圈(head 已推进,剩余留到下轮,不丢消息也不自旋)。③ vpui:孤立 ESC 后紧跟的普通键不再被吞。
2026-10-17 13:20:22 +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 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 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 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 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 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 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 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