Commit Graph
263 Commits
Author SHA1 Message Date
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 859197e5e9 feat(kbd): USB 键盘按住自动重复
USB HID 键盘只在状态变化时上报;a3c1d22 为避免 Enter 洪流加入整份报告去重后,
按住不放不再产生任何字符。

- 记录当前按住的非 CapsLock 键与下一次补发时刻
- 同一份报告重复到达(按键仍按住)时,按 500ms 首次延迟、33ms 间隔
  重新走 _decode_press,注入与按下沿相同的 ASCII/ANSI 字节
- 松开或换键时更新/清除重复目标;CapsLock 本身不重复
2026-10-27 12:43:19 +08:00
TermiNexus b8ca4826a9 chore(kernel): 移除帧缓冲参数的影子副本校验
fb/width/height/pitch 偶发被写坏的根因(页表/1GB 大页拆分时写坏内存)已修复,
不再需要 fail-fast 影子防御:

- 删除 _ShadowFb/_ShadowW/_ShadowH/_ShadowPitch 与 _SetFrameShadow、_CheckFrameLocals
- 删除 after_usb / after_vga / pre_devfs_fb0 / pre_explorer 四处校验调用
- devfs_register_fb0 恢复直接使用 __init__ 里的局部几何
  (原先为规避局部量被写坏,改为从 BootInfo 重取)
2026-10-27 11:54:41 +08:00
TermiNexus f43951c1f8 fix(fat32): 以 . / .. 结尾的路径会误开第一个子目录
dir_open_path 在 follow_path 返回后仍按“父目录中的最后一项”回读目录项。
当路径以 “.” 或 “..” 结尾时,follow_path 已经把 dp 打开成目标目录,
这段回读会把目标目录的第一个子项当成最终项再打开:

  根目录 cd .. → full=/.. → follow_path 打开根 → 回读根的第一项 APP
  → 实际进入 /APP,于是 ls 显示的是 app 里的 helloworld.asm。

现在检测末尾的 “.” / “..”,与根路径一样直接返回已打开的 dp。
2026-10-26 23:10:38 +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 338472e304 fix(vpui): Label 自然尺寸与 pack 交叉轴居中
Input Demo 的两个显示问题:

- Label 没有显式 w/h,构造后 w/h 恒为 0:pack 时按 0 尺寸计算,
  PACK_BOTTOM 的标签被贴到内容区底边之外(字模被裁),且不占纵向空间,
  后面的按钮与 Name: 标签重叠。新增 _fit_natural() 按文本最长行/行数
  计算自然尺寸,__init__ 与 config() 时更新。
- pack 在未 fill 时原先一律贴左上,导致 Submit 按钮紧贴左边缘。
  改为与 tkinter 默认 anchor=center 一致:TOP/BOTTOM 横向居中、
  LEFT/RIGHT 纵向居中,沿 pack 方向仍保留 padx/pady。
- redraw 先刷新窗口尺寸再 _do_layout(),避免 resize 后布局滞后一帧。
2026-10-26 23:04:25 +08:00
TermiNexus ad6f0f68a2 chore(boot): GOP 显示模式改用 22
同时精简 GOP 初始化处的冗余注释。bootx64.efi 为编译产物,已停止跟踪,
需要时用 build.py --bootloader 重编。
2026-10-26 22:41:52 +08:00
TermiNexus ca448eee98 chore(build): 移除 VPUI64 动态库并调整构建入口
- 删除 Libs/VPUI64(linker.ld / project.vpj / vpui64.vp),同步 README
- viperc 路径改到 ViperC/__vpcbuild__/output
- 新增 --nokernel(显式 --kernel 优先,且与 --kernel-only 互斥)
- 未编内核时的提示改为「跳过内核编译」
2026-10-26 22:41:47 +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 0c47d81767 feat(diag): 异常现场完整转储(裸串口通路 + 无错误码向量)
- isr.s:ISR_NOERR 也保存 RIP/CS/RFLAGS/RSP,_fault_err 用 -1 表示无错误码;
  两类宏都补存 rbx/rbp/r12(用户态 #PF 时 r12 常为出错 store 的目标对象基址)
- idt.vp:新增不经驱动/缓冲的裸串口输出(asm.outb),打印 rip/rsp/cs/flags、
  u 位、callee-saved、错误码;#UD 附带出错指令字节,#PF 附带 cr2
- 前 32 条异常打印完整现场,之后每次只补 '*',避免异常风暴刷爆串口
2026-10-26 22:41:14 +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 624554df01 fix(sched): 通过 rdi 传递用户线程入口参数
用户线程入口的第一个形参在 rdi,但 drop_to_user_mode 这条路径从未设置过 rdi,
thread_create(fn, arg) 的 arg 在用户侧一直是垃圾值。iretq 只恢复
RIP/CS/RFLAGS/RSP/SS,因此在 iretq 前显式 mov rdi 即可。
2026-10-26 22:40:10 +08:00
TermiNexus 1ccacf8204 refactor(userspace): 清理 t/c 依赖与过时注释
Terminal、SerialLogger 不再 import t, c;dynlib 删除已失效的指针级数说明。
2026-10-26 22:40:07 +08:00
TermiNexus 3583f78efc refactor(kernel): 适配 ViperC 新语法
- 去掉不再需要的 import t, c
- class → struct(纯数据聚合)
- t.CArray[T, N] → [N]T
- static 修饰符移到类型前
- @__attribute(packed) → @__attribute__(packed)
- UHCI 中断回调参数改用 callable[[], int]
- 同步 CPUID 注释中的旧类型名
2026-10-26 22:39:56 +08:00
TermiNexus 89dd08895a chore(disk): 停止跟踪编译产物 bootx64.efi
disk/ 已在 .gitignore 中豁免;引导器由 build.py --bootloader 从 boot/boot.c 重编,不再纳入版本库。本地磁盘上的新产物保留。
2026-10-26 22:39:48 +08:00
TermiNexus 7b6c508b21 增加 disk 产物豁免 2026-10-26 22:31:46 +08:00
TermiNexus e9d8024138 fix(sched): 压缩 _do_switch 注释,规避内联汇编 2047 字节静默截断
llc 报 <inline asm>:55:13 invalid operand + 孤立 mov 的真因:ViperC 的 _asm_append_cstr 只写入 ASM_TEXT_BUF_SIZE-1=2047 字节且静默丢弃后续内容。_do_switch 的 asm 模板(含 CRLF)为 2245 字节,第 2047 字节正好落在 mov [rsi + 40], rcx 的 mov 之后 ⇒ 生成的汇编串以孤立 mov 结尾,popfq/pop/ret 全部丢失。压缩块内 4 条中文注释(模板降到 1894 字节),指令与 clobber 未动。附:sha1_map 第 57 行 3e23dc8764e4a4dd:sched/sched.vp 与报错哈希一致;全仓 16 个含 asm 的 .vp、189 个模板中仅此一处超过 2047 字节。
2026-10-17 17:36:45 +08:00
TermiNexus 89385d1b42 fix(sched): 恢复立即数 store 的 qword ptr(去掉后操作数大小有歧义)
上一笔把 sched.vp 里所有 qword ptr 都去掉了,误伤 _thread_entry 构造初始栈帧的 8 条立即数 store:mov [rax - 64], 0x202 这类'内存←立即数'无法由另一侧推断大小,报 ambiguous operand size。寄存器配对的 mov(rcx/[rsi]、[rdx]/rcx 等)保持无 ptr 写法,立即数 store 恢复 qword ptr。
2026-10-17 17:21:20 +08:00
TermiNexus 937b51a0c5 fix(sched): 内联汇编去掉 qword ptr,操作数大小改由寄存器推断
llc 集成汇编器在 Intel 语法下对 mov rcx, qword ptr [rsi] 报 Expected 'PTR' or 'ptr' token(列号正落在 ptr 处),而生成的 .ll 文本本身是对的。改写为 mov rcx, [rsi] / mov [rdx], rcx / mov [rax], rsp 等形式:内存操作数大小可由另一侧寄存器(rcx/rsp)推断,语义完全等价。
2026-10-17 17:19:23 +08:00
TermiNexus 7d8a4ee6ff fix(sched): 内联汇编改用 lea + 间接寻址,绕开 [rip + sym + 偏移] 不被集成汇编器接受
llc 集成汇编器对 mov qword ptr [rip + _isr_scratch + 8], rcx 这种'RIP 相对符号 + 常量偏移'的直接寻址报 invalid operand(把整条指令识别成缺源操作数)。改为先 lea rsi, [rip + _isr_scratch](lea 形式此前已验证可用),再按 [rsi + N] / [rdx + N] 访问,共 12 处(保存 6 + 恢复 6)。rsi/rdx 都在 _do_switch 的 push 列表内,切栈后写入会被随后的 pop 恢复,不影响新线程寄存器。
2026-10-17 17:17:28 +08:00
TermiNexus 645e4015ca feat(sched): 线程切换保存/恢复 FPU/SSE(fxsave/fxrstor)
混合抢占后中断帧只保存通用寄存器,异步抢占可能落在 SSE 表达式中间,FP 密集 app(Gargantua/Scene3D)的 XMM 会被其它线程覆盖。① Thread 末尾内嵌 fpu_area[528] + fpu_ptr,用 (base+15)&~15 手工对齐出 512B FXSAVE 区(编译器类装饰器只认 packed,不认 aligned);Thread 由 64B 变为 600B,MAX_THREADS=16 ⇒ 线程表 9600B,Scheduler 约 9624B。② _do_switch:切 rsp 之前 fxsave [old],换栈后 fxrstor [new];仅用 RAX 取地址(其值已压栈),不改动 GPR 与中断状态。③ 新线程 FPU 默认镜像直接写内存(512B 清零、FCW=0x037F、MXCSR=0x1F80、MXCSR_MASK=0xFFFF),不执行 fninit 以免破坏创建者的实时 XMM。④ 内嵌于 Thread,随静态存储存活,无需额外回收。⑤ 协作式 yield 与抢占共用 _do_switch,因此两条路径都正确隔离 SSE。⑥ _thread_entry 的硬编码偏移同步为 +9600/×600,并加 fail-fast 校验(不符立即 FATAL,不静默跑飞)。
2026-10-17 17:15:14 +08:00
TermiNexus f2d8e771c2 feat(sched): 协作 + 时间片抢占(IRQ0 返回用户态时切换)
原先 timer_tick 只置 needs_reschedule,唯一消费点是 SPAWN 阻塞循环,纯计算线程永不被切走。① 抢占点:isr32_handler 在 timer_handler(含 pic.eoi(0)/apic.eoi)返回之后、且 from_user != 0(ISR 入口读 CS 低两位)时调用 try_reschedule;栈切换仍在 _do_switch。只对 Ring3 返回路径生效,内核临界区/syscall 中途不切。② 中断帧保留在被切线程内核栈上,靠 rsp 切换自然保存/恢复,不拷贝、不在切换中途 iretq。③ 修正全局 _isr_scratch 跨线程覆盖:ISR_SAVE_CLOBBERED 把 rax/rcx/r8-r11 存在全局 scratch,抢占后跨线程会互相覆盖;_do_switch 现按 tid 保存/恢复(_isr_scratch_tbl[16*6])。④ ISR 走中断门 IF=0,_if_saved=0,切回后不 sti,由 iretq 用用户 RFLAGS 恢复;_yield_lock 防嵌套。⑤ 单核前提(_sched_ptr/tick_count 非 per-CPU)。⑥ yield_now 语义、SCHED_QUANTUM=10、Gargantua 分块均未改。已知缺口:中断帧未保存 XMM/MXCSR,FP 密集 app 在异步抢占下有 XMM 被覆盖风险,待补 fxsave/fxrstor。
2026-10-17 17:08:46 +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 eb953bb1d3 feat(keyboard): 接入 USB 键盘输入到 /dev/input/keyboard
现场证据:keyboard.init 的 0xF4 被 ACK,但按键时 [kbd] first IRQ1 从不出现;同时 [usb] device addr=1 class=03 proto=01 ep=1 说明真实按键来自 USB 键盘。usb_kbd 已通过 UHCI 中断回调收到报告并生成 kbd_buf 事件,但全仓无人消费(usb_kbd_thread 从未启动),所以 /dev/input/keyboard 永远读不到 USB 按键。① keyboard.vp 新增 inject_byte/inject_csi/inject_csi_tilde 三个公开注入函数,复用 _out_push/_emit_csi,不改 read_scancode/has_data/irq_handler 语义。② usb_kbd.vp 在 _on_int_complete 的按下沿(与上一帧 6 键码比较后新出现的键)按 boot protocol(枚举时已 SET_PROTOCOL(0):modifier+reserved+keycode[6])解码成 ASCII/ANSI,注入同一 out_buf:字母数字含 Shift/CapsLock、Enter\\n、Backspace\\b、方向键 ESC[A/B/C/D、Home/End ESC[H/F、Delete/PgUp/PgDn/Insert ESC[3~/5~/6~/2~,与 PS/2 路径一致;按住不重复、松开不产字节。③ 不新建线程、不启动 kbd_thread,USB 与 PS/2 共用同一队列,中断上下文内写入(IF=0)。④ 加一次性诊断 [usb_kbd] first key。
2026-10-17 15:43:48 +08:00
TermiNexus d19d96dc70 build: 默认重编内核,--app 时才跳过
上一轮把默认改成不编内核,导致内核侧修复(键盘解码/诊断)没进产物,日志与代码对不上。恢复为:不带 --app 时默认编内核 + 库 + 全部应用;--app=a,b 视为只迭代应用(跳过内核),需要内核时显式加 --kernel。
2026-10-17 15:36:56 +08:00
TermiNexus 4195b05f75 diag(keyboard): IRQ1 首达与 0xF4 ACK 打点
键盘仍无字符。加两条一次性诊断:① irq_handler 首次触发打 [kbd] first IRQ1(判断中断是否送达);② init() 的 0xF4 扫描使能命令按 ACK 结果打 ok/not ACKed(判断命令是否送到键盘)。两者组合可区分'命令没送达'、'中断没到'、'解码丢字节'三种情况。
2026-10-17 15:33:55 +08:00
TermiNexus 0f235d0d0b fix(keyboard): 移除 devfs 注册时的重复 keyboard.init()
键盘只收到一个残留字节(ch=50)后就没有数据,符合'数据上报被关掉'。keyboard.init() 被调了两次:devfs_register_keyboard() 与 services/keyboard.vp 的 start()。init() 会读-改-写 8042 CCB 并安装 IRQ1,二次读改写可能把键盘时钟/数据上报关掉。改为只在 service 层初始化一次。
2026-10-17 13:54:52 +08:00
TermiNexus 03e1137189 fix(keyboard): 去掉重复初始化,键盘只由 service 层 init 一次
现场:explorer 能 open /dev/input/keyboard,但按下键后 [explorer] kbd 一次都不出现 —— 内核侧没有数据。devfs_register_keyboard() 与 services/keyboard.start() 各调了一次 keyboard.init(),而 init 会读-改-写 8042 的 CCB(含数据上报/中断掩码位);第二次执行时设备状态已变,容易把上报关掉。现在初始化只保留 service 层那一次(它更晚、是最终状态);kbd_thread 并未被启动(kbd_svc.th 从未创建),不存在抢数据的问题。
2026-10-17 13:36:38 +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 e839b37ee2 fix(desk): 事件环越界 —— 64 槽超出每 pid 0x1000 区间
每 pid 事件环按 0x1000=4096B 间隔排布,而环总大小 = RingCtrl(16) + 64 × DeskMsg(64) = 4112B:第 63 号槽的最后 16 字节正好覆盖下一个 pid 的 RingCtrl(head/tail/size/_pad 被写成消息尾部)。后果:下一个 pid 的环 size=0、head==tail ⇒ 该进程的键盘/鼠标事件永远收不到;再对它 ring_push 会算 (tail+1)%0,除零异常直接打死 explorer —— 这就是'多窗口关闭后容易卡死/键盘无效'的根因之一。改为 63 槽(4048B),落在区间内。
2026-10-17 13:20:22 +08:00
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