Files
Deshab-OS/CODE/desktop/shell.c
T
LinehuandCursor 8554a82677 feat(panic): FUCK [panic] mode=strict|fallback + realtest image variant
- UTSM panic_full returns in fallback mode (logs [PANIC:FALLBACK] + code + hit
  count); panic_halt keeps always-halt sites (CPU exception, VMM-E11 LA57,
  VMM-E15 DSK jump, UTSM-E42 test hook). Call sites given safe continuations.
- Mode passed to DSK/FirstInit/login/desktop via DSK_BOOT_FLAG_PANIC_FALLBACK;
  DKM kernel_api.panic / kern.log.panic return to driver; ext4 fatal aborts the
  op via setjmp/longjmp and DSK falls back to FAT32 ESP.
- build.ps1/Makefile: dev=strict, release=fallback, new realtest (release
  options + strict); default -Variant all; serialized iso packing under -j.
- Fix '*/' inside comments (dkm.h, kernel_api.h, bus.c) that broke the build.
- Includes pending work: dkm_bus service bus, mt7920/wlan drivers, desktop
  files/startmenu/prank, pe-tools, diskinfo, docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-12 23:57:19 +08:00

576 lines
20 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* shell.c — 工作区/页面状态机 + 任务视图/右键菜单 + 事件路由(M0 自 main.c 平移)
*/
#include "desktop.h"
/* ---- 全局状态 ---- */
int g_page = 1; /* 1..3 内置, 4..8 自定义 */
int g_taskview = 0;
int g_dash_focus = 0; /* 0=t1 1=t2 2=ed 3=files */
workspace g_ws[MAX_WS];
int g_ws_count = 1;
int g_ws_cur = 0;
cpage g_cpages[MAX_CPAGES];
int g_cpage_count = 0;
/* 右键菜单(移到工作区) */
int g_ctx_open = 0;
int g_ctx_x = 0, g_ctx_y = 0;
int g_ctx_win = -1;
/* 状态栏滑块 */
int g_vol = 65;
int g_bright = 100;
int g_bright_applied = 100; /* 上次应用的亮度值(避免重复处理) */
int g_drag_slider = 0; /* 1=vol 2=bright */
int g_vol_rect[4]; /* x,y,w,h 滑轨 */
int g_bri_rect[4];
int g_quit = 0; /* EXIT 按钮 → 返回 DSK */
/* 双击检测 */
u64 g_last_click_tsc = 0;
int g_last_click_x = -100, g_last_click_y = -100;
/* 面板/控件命中矩形(绘制时更新,点击时使用) */
int g_dash_rect[4][4]; /* 4 面板 x,y,w,h */
int g_save_rect[4];
int g_pg_rect[8][4]; /* 页面切换按钮 */
int g_pg_rm_rect[8][4];
int g_pgadd_rect[4];
int g_tvbtn_rect[4]; /* 任务视图按钮 */
int g_ws_rect[MAX_WS][4];
int g_wsadd_rect[4];
int g_tb_rect[MAX_WINDOWS][4]; /* 任务栏项 */
int g_tb_close_rect[MAX_WINDOWS][4];
int g_tb_quit_rect[4];
int g_tb_adopt_rect[4]; /* 收纳窗口按钮 */
int g_tv_card[MAX_WS + 1][4]; /* 任务视图卡片(+add) */
int g_tv_close[MAX_WS][4];
int g_ctx_rect[4];
int g_ctx_item[MAX_WS + 1][4];
int g_ctx_item_ws[MAX_WS + 1]; /* 菜单行 → 真实工作区索引,-1=新建 */
int g_ide_host[4];
int g_ide_tabx[4]; /* IDE 标签 × */
int g_ide_new_rect[4]; /* IDE "+ NEW IDE" 按钮 */
int g_cp_host[4]; /* 自定义页宿主 */
int g_cp_new_rect[4]; /* 自定义页 "+ NEW INSTANCE" 按钮 */
/* ============================================================
* 工作区 / 页面管理
* ============================================================ */
static void ws_switch(int id) {
if (id < 0 || id >= g_ws_count || id == g_ws_cur) { g_taskview = 0; return; }
g_ws[g_ws_cur].page = g_page; /* 记住离开时的页面 */
g_ws_cur = id;
g_page = g_ws[id].page;
g_taskview = 0;
}
static void ws_create(void) {
if (g_ws_count >= MAX_WS) return;
char *nm = g_ws[g_ws_count].name;
nm[0] = 'W'; nm[1] = 'S'; nm[2] = '-';
nm[3] = '0' + (char)(g_ws_count + 1); nm[4] = 0;
g_ws[g_ws_count].page = 1;
g_ws_count++;
}
/* 关闭工作区:窗口释放到关闭后的当前工作区(Kate release 语义) */
static void ws_close(int id) {
if (g_ws_count <= 1 || id < 0 || id >= g_ws_count) return;
int cur_after = g_ws_cur;
if (g_ws_cur == id) cur_after = (id > 0) ? id - 1 : 0;
else if (g_ws_cur > id) cur_after = g_ws_cur - 1;
for (int i = 0; i < g_win_count; i++) {
if (g_windows[i].ws == id) g_windows[i].ws = cur_after;
else if (g_windows[i].ws > id) g_windows[i].ws--;
}
for (int i = id; i < g_ws_count - 1; i++) g_ws[i] = g_ws[i + 1];
g_ws_count--;
g_ws_cur = cur_after;
g_taskview = 0;
}
void page_switch(int p) {
if (p < 1 || p > 3 + g_cpage_count) return;
g_page = p;
}
/* 自定义页:为尚未挂载的外部 .elf 应用新建页面 */
static void cpage_add(void) {
if (g_cpage_count >= MAX_CPAGES) return;
for (int a = 0; a < g_app_count; a++) {
if (!g_apps[a].elf_name) continue;
int dup = 0;
for (int i = 0; i < g_cpage_count; i++)
if (f32_neq11_w(g_cpages[i].elf11, g_apps[a].elf_name)) { dup = 1; break; }
if (dup) continue;
cpage *cp = &g_cpages[g_cpage_count];
int n = kstrlen(g_apps[a].display_name);
if (n > 11) n = 11;
for (int i = 0; i < n; i++) cp->name[i] = g_apps[a].display_name[i];
cp->name[n] = 0;
for (int i = 0; i < 11; i++) cp->elf11[i] = g_apps[a].elf_name[i];
cp->id = 4 + g_cpage_count;
g_cpage_count++;
g_page = cp->id;
return;
}
}
static void cpage_remove(int idx) {
if (idx < 0 || idx >= g_cpage_count) return;
int removed_page = g_cpages[idx].id;
for (int i = idx; i < g_cpage_count - 1; i++) g_cpages[i] = g_cpages[i + 1];
g_cpage_count--;
for (int i = 0; i < g_cpage_count; i++) g_cpages[i].id = 4 + i;
if (g_page == removed_page) g_page = 1;
else if (g_page > removed_page) g_page--;
}
/* ============================================================
* 事件处理
* ============================================================ */
void slider_set(const int rect[4], int *val, int mx) {
int v = (mx - rect[0]) * 100 / rect[2];
if (v < 0) v = 0;
if (v > 100) v = 100;
*val = v;
}
/* 双击判定(TSC 半秒 + 8px 容差),不更新状态 */
static int dbl_check(int mx, int my) {
if (!g_tsc_per_sec) return 0;
u64 now = rdtsc();
int dx = mx - g_last_click_x, dy = my - g_last_click_y;
return (now - g_last_click_tsc) < g_tsc_per_sec / 2 &&
dx > -8 && dx < 8 && dy > -8 && dy < 8;
}
/* 构建"移到工作区"列表(排除当前工作区,与 Winux-Kate 一致),返回行数 */
int ctx_list_build(int *map) {
int n = 0;
for (int i = 0; i < g_ws_count; i++) {
if (i == g_ws_cur) continue;
map[n++] = i;
}
return n;
}
/* ---- 1·DASHBOARD 点击 ---- */
static int dash_click(int mx, int my, int dbl) {
editor_state *ed = (editor_state *)ADDR_EDDASH;
/* SAVE 按钮 */
if (ed->has_file && rect_hit(g_save_rect, mx, my)) { editor_save(); return 1; }
/* 面板聚焦 */
for (int i = 0; i < 4; i++) {
if (!rect_hit(g_dash_rect[i], mx, my)) continue;
g_dash_focus = i;
if (i == 3) {
/* FILES 行命中 / 双击打开 */
int fx = g_dash_rect[3][0] + 2;
int fy = g_dash_rect[3][1] + KATE_PANEL_HEADER_H + 2;
int fw = g_dash_rect[3][2] - 4;
int fh = g_dash_rect[3][3] - KATE_PANEL_HEADER_H - 4;
if (mx >= fx && mx < fx + fw && my >= fy + 24) {
int max_rows = (fh - 24) / 22;
int row = (my - fy - 24) / 22;
if (row >= 0 && row < max_rows && row < g_file_count) {
if (dbl && row == g_file_sel) files_open(row);
else g_file_sel = row;
}
}
}
return 1;
}
/* 状态栏滑块 */
if (rect_hit(g_vol_rect, mx, my)) {
g_drag_slider = 1;
slider_set(g_vol_rect, &g_vol, mx);
return 1;
}
if (rect_hit(g_bri_rect, mx, my)) {
g_drag_slider = 2;
slider_set(g_bri_rect, &g_bright, mx);
return 1;
}
return 0;
}
/* ---- 2·IDE 点击 ---- */
static int ide_click(int mx, int my, int dbl) {
if (g_ide_attached && rect_hit(g_ide_tabx, mx, my)) {
g_ide_attached = 0;
/* VSCode Phase 2: detach 时停止向 guest 转发输入 */
g_input_forward_enabled = 0;
ide_attach_invalidate(); /* P7.3: detach 后下次 attach 全量重绘 */
slog("ide detached, input forwarding off");
return 1;
}
if (!g_ide_attached && rect_hit(g_ide_new_rect, mx, my)) {
g_ide_attached = 1;
ide_attach_invalidate(); /* P7.3: attach 首帧全量重绘 */
/* attach 时仅当 Linux guest 服务可用才开启转发;否则降级为本地 editor */
g_input_forward_enabled = (g_lxc_svc != 0);
slog(g_input_forward_enabled ? "ide attached, forwarding to guest"
: "ide attached, lxc unavailable (local editor)");
/* VSCode Phase 6: 共享启动路径(X server + VSCode) */
if (g_input_forward_enabled) vscode_launch();
return 1;
}
if (!g_ide_attached && dbl && rect_hit(g_ide_host, mx, my)) {
g_ide_attached = 1;
ide_attach_invalidate(); /* P7.3: attach 首帧全量重绘 */
g_input_forward_enabled = (g_lxc_svc != 0);
slog(g_input_forward_enabled ? "ide attached (dbl), forwarding to guest"
: "ide attached (dbl), lxc unavailable (local editor)");
/* VSCode Phase 6: 共享启动路径(X server + VSCode) */
if (g_input_forward_enabled) vscode_launch();
return 1;
}
return 0;
}
/* ---- 4·DESKTOP 点击 ---- */
static int desktop_click(int mx, int my, int dbl) {
/* M2 Fluent:悬浮任务栏 + 托盘优先路由 */
if (g_fluent && taskbar_click(mx, my)) return 1;
int tb_y = (int)g_fb_h - (g_fluent ? 56 : KATE_TASKBAR_H);
/* 任务栏(仅 KATE 回退路径;Fluent 已由 taskbar_click 消费) */
if (!g_fluent && my >= tb_y) {
if (rect_hit(g_tb_quit_rect, mx, my)) { g_quit = 1; return 1; }
if (rect_hit(g_tb_adopt_rect, mx, my)) {
/* 收纳窗口:全部工作区窗口收归当前工作区并还原最小化 */
for (int i = 0; i < g_win_count; i++) {
g_windows[i].ws = g_ws_cur;
g_windows[i].minimized = 0;
}
return 1;
}
for (int i = 0; i < g_win_count; i++) {
if (!g_tb_rect[i][2] || !rect_hit(g_tb_rect[i], mx, my)) continue;
if (rect_hit(g_tb_close_rect[i], mx, my)) {
win_close_request(&g_windows[i]);
} else {
win_focus(&g_windows[i]);
}
return 1;
}
return 0;
}
/* 窗口 */
desktop_window *w = win_find_at(mx, my);
if (w) {
int wid = w->id;
win_focus(w);
w = win_find(wid); /* focus 重排后重新定位 */
if (!w) return 1;
/* M2 Fluent:右上三钮(关闭/最大化/最小化) */
if (g_fluent) {
if (win_hit_close_btn(w, mx, my)) { win_close_request(w); return 1; }
if (win_hit_max_btn(w, mx, my)) {
/* 最大化/还原:铺满桌面区(顶部 0 到任务栏上方) */
if (w->w >= (int)g_fb_w - 8 && w->h >= (int)g_fb_h - 64) {
w->x = 60; w->y = 40; w->w = 720; w->h = 500; /* 还原 */
} else {
w->x = 0; w->y = 0;
w->w = (int)g_fb_w;
w->h = (int)g_fb_h - 56; /* 任务栏悬浮区预留 */
}
return 1;
}
if (win_hit_min_btn(w, mx, my)) {
w->minimized = 1;
w->focused = 0;
g_focused_win = -1;
return 1;
}
} else if (win_hit_close_btn(w, mx, my)) { win_close_request(w); return 1; }
if (win_hit_titlebar(w, mx, my)) {
g_dragging = 1;
g_drag_win = w->id;
g_drag_off_x = mx - w->x;
g_drag_off_y = my - w->y;
return 1;
}
/* 客户区事件 → 应用 */
if (w->app_id >= 0 && w->app_id < g_app_count &&
g_apps[w->app_id].on_event && w->app_state) {
desktop_event dev;
dev.type = EV_MOUSE_DOWN;
dev.mx = mx; dev.my = my;
dev.button = 0; dev.scancode = 0;
dev.shift = g_shift; dev.ctrl = g_ctrl; dev.alt = g_alt;
app_ctx actx;
fill_app_ctx(&actx, w);
g_apps[w->app_id].on_event(w->app_state, &actx, &dev);
}
return 1;
}
/* 图标(单击选中 / 双击启动) */
for (int i = 0; i < g_icon_count; i++) {
int ix = g_icons[i].x, iy = g_icons[i].y;
if (mx >= ix && mx < ix + ICON_W && my >= iy && my < iy + ICON_H) {
if (dbl && g_icons[i].selected) {
launch_app(g_icons[i].app_id);
} else {
for (int j = 0; j < g_icon_count; j++) g_icons[j].selected = 0;
g_icons[i].selected = 1;
}
return 1;
}
}
/* 空白:取消选择 */
for (int j = 0; j < g_icon_count; j++) g_icons[j].selected = 0;
return 1;
}
/* ---- 左键按下 ---- */
int on_left_press(void) {
int mx = g_mouse_x, my = g_mouse_y;
int dbl = dbl_check(mx, my);
g_last_click_tsc = rdtsc();
g_last_click_x = mx; g_last_click_y = my;
/* 任务视图 overlay(模态) */
if (g_taskview) {
for (int i = 0; i <= g_ws_count; i++) {
if (!rect_hit(g_tv_card[i], mx, my)) continue;
if (i == g_ws_count) ws_create();
else if (rect_hit(g_tv_close[i], mx, my)) ws_close(i);
else ws_switch(i);
return 1;
}
g_taskview = 0; /* 背景点击关闭 */
return 1;
}
/* 右键菜单(模态) */
if (g_ctx_open) {
if (rect_hit(g_ctx_rect, mx, my)) {
for (int i = 0; i <= MAX_WS; i++) {
if (!rect_hit(g_ctx_item[i], mx, my)) continue;
int wi = g_ctx_item_ws[i];
if (wi == -1) {
/* + NEW WORKSPACE:新建并迁移 */
ws_create();
desktop_window *w = win_find(g_ctx_win);
if (w && g_ws_count > 0) w->ws = g_ws_count - 1;
} else if (wi >= 0) {
desktop_window *w = win_find(g_ctx_win);
if (w) w->ws = wi;
}
break;
}
}
g_ctx_open = 0;
return 1;
}
/* 顶栏(仅 KATE 回退路径;Fluent 无 topbar,点击直达页面内容) */
if (!g_fluent && my < KATE_TOPBAR_H) {
if (rect_hit(g_tvbtn_rect, mx, my)) { g_taskview = 1; return 1; }
for (int i = 0; i < g_ws_count; i++)
if (rect_hit(g_ws_rect[i], mx, my)) { ws_switch(i); return 1; }
if (rect_hit(g_wsadd_rect, mx, my)) { ws_create(); return 1; }
for (int i = 0; i < 3 + g_cpage_count; i++) {
if (i >= 3 && rect_hit(g_pg_rm_rect[i], mx, my)) {
cpage_remove(i - 3);
return 1;
}
if (rect_hit(g_pg_rect[i], mx, my)) { page_switch(i + 1); return 1; }
}
if (rect_hit(g_pgadd_rect, mx, my)) { cpage_add(); return 1; }
return 0;
}
/* 页面内容 */
if (g_page == 1) return dash_click(mx, my, dbl);
if (g_page == 2) return ide_click(mx, my, dbl);
if (g_page == 3) return desktop_click(mx, my, dbl);
if (g_page >= 4) {
int launch = 0;
if (rect_hit(g_cp_new_rect, mx, my)) launch = 1;
if (dbl && rect_hit(g_cp_host, mx, my)) launch = 1;
if (launch) {
for (int i = 0; i < g_cpage_count; i++) {
if (g_cpages[i].id != g_page) continue;
launch_external_elf(g_cpages[i].elf11);
break;
}
return 1;
}
return 0;
}
return 0;
}
/* ---- 右键按下:窗口/任务栏项 → 移到工作区菜单 ---- */
int on_right_press(void) {
int mx = g_mouse_x, my = g_mouse_y;
if (g_taskview) { g_taskview = 0; return 1; }
if (g_ctx_open) { g_ctx_open = 0; return 1; }
if (g_page != 3) return 0;
int tb_y = (int)g_fb_h - KATE_TASKBAR_H;
if (my >= tb_y) {
for (int i = 0; i < g_win_count; i++) {
if (!g_tb_rect[i][2] || !rect_hit(g_tb_rect[i], mx, my)) continue;
int lmap[MAX_WS];
int ln = ctx_list_build(lmap);
int mh = 24 + ((ln ? ln : 1) + 1) * 22 + 8;
g_ctx_win = g_windows[i].id;
g_ctx_x = mx;
g_ctx_y = my - mh; /* 菜单向上展开 */
if (g_ctx_y < KATE_TOPBAR_H) g_ctx_y = my;
g_ctx_open = 1;
return 1;
}
return 0;
}
desktop_window *w = win_find_at(mx, my);
if (w) {
g_ctx_win = w->id;
g_ctx_x = mx; g_ctx_y = my;
g_ctx_open = 1;
return 1;
}
return 0;
}
/* ---- 键盘 ---- */
int handle_key(u8 sc) {
/* VSCode Phase 2: IDE attached 且在 IDE 页时,把按键序列转发到 Linux guest。
* 0xE0 前缀字节本身不转发(无对应 keycode),仅靠 g_e0 标志让下一字节选取扩展键变体。
* 转发在修饰键跟踪与页面路由之前完成,保证 VSCode 收到完整 press/release
* 序列(含 Shift/Ctrl/Alt/方向键)。宿主修饰键跟踪继续运行,Ctrl+Tab 切页、
* Ctrl+S 等热键仍可在宿主侧生效,便于用户随时切离 IDE 页。 */
if (g_input_forward_enabled && g_lxc_svc && g_lxc_svc->input_forward_keyboard &&
sc != 0xE0 && g_page == 2) {
u8 base = (u8)(sc & 0x7F);
u16 code = scancode_to_linux_keycode(base, g_e0);
if (code) {
u32 value = (sc & 0x80) ? 0 : 1; /* 0x80 位 = release */
g_lxc_svc->input_forward_keyboard(code, value);
}
}
if (sc == 0xE0) { g_e0 = 1; return 0; }
if (sc == 0x2A || sc == 0x36) { g_shift = 1; return 0; }
if (sc == 0xAA || sc == 0xB6) { g_shift = 0; return 0; }
if (sc == 0x1D) { g_ctrl = 1; g_e0 = 0; return 0; }
if (sc == 0x9D) { g_ctrl = 0; return 0; }
if (sc == 0x38) { g_alt = 1; g_e0 = 0; return 0; }
if (sc == 0xB8) { g_alt = 0; return 0; }
if (sc & 0x80) { g_e0 = 0; return 0; } /* 其他释放码忽略 */
/* M3 开始菜单(模态:Esc 关闭 / 搜索输入 / Enter 启动) */
if (g_sm_open) return startmenu_key(sc, g_shift);
/* 快速设置卡片(Esc 收起) */
if (g_qs_open && sc == 0x01) { g_qs_open = 0; return 1; }
/* WiFi 快捷卡片(模态:Esc 收起 / 密码模式接管输入) */
if (g_wifi_open) return wifi_key(sc, g_shift);
/* Esc:关闭 overlay */
if (sc == 0x01) {
if (g_taskview) { g_taskview = 0; return 1; }
if (g_ctx_open) { g_ctx_open = 0; return 1; }
/* IDE 页:Esc = detach + 退回桌面页(IDE 退出通道) */
if (g_page == 2) {
if (g_ide_attached) {
g_ide_attached = 0;
g_input_forward_enabled = 0;
ide_attach_invalidate();
slog("ide detached (esc), input forwarding off");
}
page_switch(3);
return 1;
}
return 0;
}
if (g_taskview || g_ctx_open) return 0; /* 模态期间吞掉按键 */
/* Ctrl+Tab 切页 / Ctrl+Shift+Tab 上下文菜单(DESKTOP 页) */
if (g_ctrl && sc == 0x0F) {
if (g_shift) {
/* Ctrl+Shift+Tab:对当前聚焦窗口打开"移到工作区"菜单 */
if (g_page == 3 && win_find(g_focused_win)) {
g_ctx_win = g_focused_win;
g_ctx_x = g_mouse_x; g_ctx_y = g_mouse_y;
g_ctx_open = 1;
return 1;
}
return 0;
}
int maxp = 3 + g_cpage_count;
int p = g_page + 1;
if (p > maxp) p = 1;
page_switch(p);
return 1;
}
if (g_ctrl && sc == 0x1F) { editor_save(); return 1; }
/* 页面路由 */
if (g_page == 1) {
if (g_dash_focus <= 1) {
bash_key((bash_state *)(g_dash_focus == 0 ? ADDR_TERM1 : ADDR_TERM2),
sc, g_shift);
return 1;
}
if (g_dash_focus == 2) {
editor_key((editor_state *)ADDR_EDDASH, sc, g_shift);
return 1;
}
/* FILES 面板导航 */
if (sc == 0x48) { if (g_file_sel > 0) g_file_sel--; return 1; }
if (sc == 0x50) { if (g_file_sel < g_file_count - 1) g_file_sel++; return 1; }
if (sc == 0x1C) { files_open(g_file_sel); return 1; }
return 0;
}
if (g_page == 2) {
if (g_ide_attached) {
if (g_input_forward_enabled) {
/* VSCode Phase 2: 键盘已转发到 guest 中的 VSCode,
* 本地 editor 不再重复处理,避免双输入。 */
return 1;
}
editor_key((editor_state *)ADDR_EDDASH, sc, g_shift);
return 1;
}
return 0;
}
if (g_page == 3) {
desktop_window *fw = win_find(g_focused_win);
if (fw && fw->ws == g_ws_cur && fw->app_id >= 0 && fw->app_id < g_app_count &&
g_apps[fw->app_id].on_event && fw->app_state) {
desktop_event dev;
dev.type = EV_KEY_DOWN;
dev.mx = g_mouse_x; dev.my = g_mouse_y;
dev.button = 0;
dev.scancode = sc;
dev.shift = g_shift; dev.ctrl = g_ctrl; dev.alt = g_alt;
app_ctx actx;
fill_app_ctx(&actx, fw);
g_apps[fw->app_id].on_event(fw->app_state, &actx, &dev);
return 1;
}
}
return 0;
}