feat: 添加 Linux 预览后端和 Notes 应用预览版本
- 创建 desktop_app_linux.h 作为跨平台后端抽象层 - 创建 notes_preview.c 使用 5x7 位图字体渲染 - 添加 Makefile.linux 构建脚本 - 添加 README_LINUX_PREVIEW.md 使用说明- 修复键盘映射、字符渲染、文本输入显示等问题
This commit is contained in:
+1
-1
@@ -26,7 +26,7 @@ all: $(OUT)
|
|||||||
-include $(DEPS)
|
-include $(DEPS)
|
||||||
|
|
||||||
$(OUT): $(OBJECTS) linker.ld
|
$(OUT): $(OBJECTS) linker.ld
|
||||||
@if not exist "$(ROOT)\SYSTEM\boot" mkdir "$(ROOT)\SYSTEM\boot"
|
@mkdir -p $(dir $@)
|
||||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@ OBJECTS := $(C_SOURCES:.c=.o)
|
|||||||
all: $(OUT)
|
all: $(OUT)
|
||||||
|
|
||||||
$(OUT): $(OBJECTS) linker.ld
|
$(OUT): $(OBJECTS) linker.ld
|
||||||
@if not exist "$(ROOT)\SYSTEM\system\deshab64" mkdir "$(ROOT)\SYSTEM\system\deshab64"
|
@mkdir -p "$(ROOT)/SYSTEM/system/deshab64"
|
||||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ OBJECTS := $(C_SOURCES:.c=.o)
|
|||||||
all: $(OUT)
|
all: $(OUT)
|
||||||
|
|
||||||
$(OUT): $(OBJECTS) linker.ld
|
$(OUT): $(OBJECTS) linker.ld
|
||||||
@if not exist "$(ROOT)\SYSTEM\system\deshab64" mkdir "$(ROOT)\SYSTEM\system\deshab64"
|
@mkdir -p $(dir $@)
|
||||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
|
|||||||
+27
-435
@@ -19,8 +19,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../UTSM/include/utsm/dsk.h"
|
#include "../UTSM/include/utsm/dsk.h"
|
||||||
/* Linux 兼容层服务接口(reserved[5],exec/file_list/file_read) */
|
|
||||||
#include "../UTSM/include/utsm/linux_compat.h"
|
|
||||||
|
|
||||||
/* block 设备函数类型(从 kernel_api + 0xA8 获取 block_api,read @ +0x10, write @ +0x18) */
|
/* block 设备函数类型(从 kernel_api + 0xA8 获取 block_api,read @ +0x10, write @ +0x18) */
|
||||||
typedef int (*desktop_block_read_fn)(u32 index, u64 lba, u32 count, void *buffer);
|
typedef int (*desktop_block_read_fn)(u32 index, u64 lba, u32 count, void *buffer);
|
||||||
@@ -44,11 +42,6 @@ typedef long long i64;
|
|||||||
static __inline__ void outb(u16 port, u8 value) { __asm__ volatile("outb %0,%1"::"a"(value),"Nd"(port)); }
|
static __inline__ void outb(u16 port, u8 value) { __asm__ volatile("outb %0,%1"::"a"(value),"Nd"(port)); }
|
||||||
static __inline__ u8 inb(u16 port) { u8 v; __asm__ volatile("inb %1,%0":"=a"(v):"Nd"(port)); return v; }
|
static __inline__ u8 inb(u16 port) { u8 v; __asm__ volatile("inb %1,%0":"=a"(v):"Nd"(port)); return v; }
|
||||||
|
|
||||||
/* Linux 桌面应用配置契约(da_linuxapp_parse / DA_LINUXAPP_* 常量)。
|
|
||||||
* DA_NO_PORTIO:本文件已自带 outb/inb,跳过头文件内同名定义。 */
|
|
||||||
#define DA_NO_PORTIO
|
|
||||||
#include "../tools/desktop_app.h"
|
|
||||||
|
|
||||||
static void sputc(char c) {
|
static void sputc(char c) {
|
||||||
for (unsigned int i=0; i<100000; i++) { if (inb(COM1+5)&0x20) break; }
|
for (unsigned int i=0; i<100000; i++) { if (inb(COM1+5)&0x20) break; }
|
||||||
outb(COM1, (unsigned char)c);
|
outb(COM1, (unsigned char)c);
|
||||||
@@ -78,7 +71,6 @@ static void redraw_all(void);
|
|||||||
static int ps2_mouse_poll(void);
|
static int ps2_mouse_poll(void);
|
||||||
static void draw_statusbar_full(void);
|
static void draw_statusbar_full(void);
|
||||||
static void files_refresh(void);
|
static void files_refresh(void);
|
||||||
static void shortcuts_refresh(void);
|
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
* 常量(Winux-Kate 布局)
|
* 常量(Winux-Kate 布局)
|
||||||
@@ -172,10 +164,6 @@ typedef struct {
|
|||||||
app_draw_fn on_draw;
|
app_draw_fn on_draw;
|
||||||
app_destroy_fn on_destroy;
|
app_destroy_fn on_destroy;
|
||||||
const char *elf_name;
|
const char *elf_name;
|
||||||
/* Linux 兼容层命令(Phase 5,如 "/usr/bin/neofetch")。与 elf_name 互斥:
|
|
||||||
* 两者同时设置时 elf_name 优先(走外部 ELF 全屏宿主),linux_cmd 被忽略;
|
|
||||||
* 仅设置 linux_cmd 时必须提供 on_create(内置终端窗口承载执行)。 */
|
|
||||||
const char *linux_cmd;
|
|
||||||
} app_descriptor;
|
} app_descriptor;
|
||||||
|
|
||||||
/* 桌面图标 */
|
/* 桌面图标 */
|
||||||
@@ -186,16 +174,7 @@ typedef struct {
|
|||||||
int selected;
|
int selected;
|
||||||
} desktop_icon;
|
} desktop_icon;
|
||||||
|
|
||||||
/* 桌面快捷方式(从 SYSTEM/user/desktop 目录加载 .lnk 文件) */
|
#define MAX_ICONS 8
|
||||||
typedef struct {
|
|
||||||
char display_name[24]; /* 显示名称(UTF-8) */
|
|
||||||
char elf_name[11]; /* ELF 文件名(FAT32 8.3) */
|
|
||||||
char icon_type[12]; /* 图标类型:editor/folder/browser/terminal */
|
|
||||||
int x, y; /* 桌面位置 */
|
|
||||||
int selected; /* 选中状态 */
|
|
||||||
} desktop_shortcut;
|
|
||||||
|
|
||||||
#define MAX_ICONS 24 /* 桌面图标槽(内置应用 + 用户快捷方式) */
|
|
||||||
|
|
||||||
/* 工作区 */
|
/* 工作区 */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -260,22 +239,14 @@ static int g_dragging = 0;
|
|||||||
static int g_drag_win = -1;
|
static int g_drag_win = -1;
|
||||||
static int g_drag_off_x, g_drag_off_y;
|
static int g_drag_off_x, g_drag_off_y;
|
||||||
|
|
||||||
/* 应用表(16 = 6 内置 + DA_LINUXAPP_MAX 8 个 Linux 应用 + 余量) */
|
/* 应用表 */
|
||||||
#define MAX_APPS 16
|
#define MAX_APPS 8
|
||||||
static app_descriptor g_apps[MAX_APPS];
|
static app_descriptor g_apps[MAX_APPS];
|
||||||
static int g_app_count = 0;
|
static int g_app_count = 0;
|
||||||
|
|
||||||
/* Linux 兼容层服务运行态(register_linux_apps 设置):0 时 Linux 图标置灰,
|
|
||||||
* 双击仍开终端窗口并提示不可用原因(lnx_on_create 运行时复查)。 */
|
|
||||||
static int g_lnx_svc_up = 0;
|
|
||||||
|
|
||||||
static desktop_icon g_icons[MAX_ICONS];
|
static desktop_icon g_icons[MAX_ICONS];
|
||||||
static int g_icon_count = 0;
|
static int g_icon_count = 0;
|
||||||
|
|
||||||
/* 用户快捷方式(从 SYSTEM/user/desktop 加载) */
|
|
||||||
static desktop_shortcut g_shortcuts[MAX_ICONS];
|
|
||||||
static int g_shortcut_count = 0;
|
|
||||||
|
|
||||||
/* ---- Kate 外壳状态 ---- */
|
/* ---- Kate 外壳状态 ---- */
|
||||||
static int g_booted = 0;
|
static int g_booted = 0;
|
||||||
static int g_page = 1; /* 1..3 内置, 4..8 自定义 */
|
static int g_page = 1; /* 1..3 内置, 4..8 自定义 */
|
||||||
@@ -549,27 +520,7 @@ static void draw_icon_browser(du_context *ctx, int cx, int cy) {
|
|||||||
du_pixel(ctx, ccx, ccy, KS_ACCENT2);
|
du_pixel(ctx, ccx, ccy, KS_ACCENT2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Linux 应用图标:暗底终端 + 霓虹绿 >_ 提示符(与 bash 终端同色板)。
|
|
||||||
* 服务不可用时 accent 传 KS_TEXT_DIM(置灰,点击仍给原因提示)。 */
|
|
||||||
static void draw_icon_linux(du_context *ctx, int cx, int cy, u32 accent) {
|
|
||||||
du_fill_rect(ctx, cx, cy, 40, 40, KS_BG_PRIMARY);
|
|
||||||
du_rect_outline(ctx, cx, cy, 40, 40, accent, 2);
|
|
||||||
/* '>' 折线 */
|
|
||||||
du_fill_rect(ctx, cx + 8, cy + 12, 2, 2, accent);
|
|
||||||
du_fill_rect(ctx, cx + 10, cy + 14, 2, 2, accent);
|
|
||||||
du_fill_rect(ctx, cx + 12, cy + 16, 2, 2, accent);
|
|
||||||
du_fill_rect(ctx, cx + 10, cy + 18, 2, 2, accent);
|
|
||||||
du_fill_rect(ctx, cx + 8, cy + 20, 2, 2, accent);
|
|
||||||
/* '_' 光标块 */
|
|
||||||
du_fill_rect(ctx, cx + 16, cy + 22, 8, 2, accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void draw_app_icon(du_context *ctx, int app_id, int cx, int cy) {
|
static void draw_app_icon(du_context *ctx, int app_id, int cx, int cy) {
|
||||||
/* Linux 应用走专属图标(暗底绿提示符),其余保持原有映射 */
|
|
||||||
if (app_id >= 0 && app_id < g_app_count && g_apps[app_id].linux_cmd) {
|
|
||||||
draw_icon_linux(ctx, cx, cy, g_lnx_svc_up ? KS_ACCENT2 : KS_TEXT_DIM);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switch (app_id) {
|
switch (app_id) {
|
||||||
case 0: draw_icon_terminal(ctx, cx, cy); break;
|
case 0: draw_icon_terminal(ctx, cx, cy); break;
|
||||||
case 1: draw_icon_editor(ctx, cx, cy); break;
|
case 1: draw_icon_editor(ctx, cx, cy); break;
|
||||||
@@ -1154,269 +1105,6 @@ static void bash_on_draw(void *state, app_ctx *ctx) {
|
|||||||
__attribute__((unused))
|
__attribute__((unused))
|
||||||
static void bash_on_destroy(void *state) { (void)state; }
|
static void bash_on_destroy(void *state) { (void)state; }
|
||||||
|
|
||||||
/* ============================================================
|
|
||||||
* Linux 兼容层应用 (Phase 5)
|
|
||||||
* 图标 → 终端风格窗口 → linux_compat_service.exec 同步执行。
|
|
||||||
* exec 是同步阻塞调用(park-and-resume 到 Linux guest 完成才返回),
|
|
||||||
* 无 out_cb 流式回调:先绘制 running 状态再阻塞,返回后一次性
|
|
||||||
* 把 stdout 缓冲追加进终端并显示 [exit N]。
|
|
||||||
* ============================================================ */
|
|
||||||
|
|
||||||
static const linux_compat_service *g_lxc_svc = 0;
|
|
||||||
|
|
||||||
#define LNX_MAX_WINS 8 /* 与 MAX_WINDOWS 对齐:任何窗口都可能是 Linux 终端 */
|
|
||||||
#define LNX_OUT_CAP 16384 /* exec stdout 收集缓冲 */
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
bash_state term; /* 复用 bash 终端字符网格(无输入行) */
|
|
||||||
int finished; /* 0=running 1=已返回 */
|
|
||||||
int rc; /* exec 返回值(0 成功,负值失败) */
|
|
||||||
u64 exit_code; /* guest 进程退出码 */
|
|
||||||
} lnx_state;
|
|
||||||
|
|
||||||
static lnx_state g_lnx_states[LNX_MAX_WINS];
|
|
||||||
static int g_lnx_used[LNX_MAX_WINS];
|
|
||||||
static char g_lnx_out[LNX_OUT_CAP]; /* 同步单发,可全窗口共享 */
|
|
||||||
|
|
||||||
/* Linux 应用注册名/命令持久池(f32_data 会被后续文件操作复用,必须拷出) */
|
|
||||||
static char g_lnx_name_pool[DA_LINUXAPP_MAX][12];
|
|
||||||
static char g_lnx_cmd_pool[DA_LINUXAPP_MAX][64];
|
|
||||||
static char g_lnx_id_pool[DA_LINUXAPP_MAX][8];
|
|
||||||
|
|
||||||
/* 追加无符号十进制到终端 */
|
|
||||||
static void lnx_put_u64(bash_state *t, u64 v, u32 color) {
|
|
||||||
char tmp[20];
|
|
||||||
int n = 0;
|
|
||||||
if (v == 0) tmp[n++] = '0';
|
|
||||||
while (v && n < 20) { tmp[n++] = (char)('0' + (v % 10)); v /= 10; }
|
|
||||||
while (n > 0) bash_putc(t, tmp[--n], color);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void lnx_put_i64(bash_state *t, i64 v, u32 color) {
|
|
||||||
if (v < 0) { bash_putc(t, '-', color); lnx_put_u64(t, (u64)(-v), color); }
|
|
||||||
else lnx_put_u64(t, (u64)v, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 追加 exec 输出到终端:剥离 ANSI CSI 转义序列,忽略 \r,\t 展开为 2 空格,
|
|
||||||
* 其余控制字符丢弃(终端字体仅 ASCII,非 ASCII 字节由 du_draw_char 钳为空格)。 */
|
|
||||||
static void lnx_append(bash_state *t, const char *buf, u64 len, u32 color) {
|
|
||||||
int esc = 0;
|
|
||||||
for (u64 i = 0; i < len; i++) {
|
|
||||||
char c = buf[i];
|
|
||||||
if (esc) {
|
|
||||||
/* CSI: ESC [ 参数... 结束字节(0x40-0x7E);其它 ESC 序列同理吞掉 */
|
|
||||||
if (c >= 0x40 && c <= 0x7E) esc = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (c == 0x1B) { esc = 1; continue; }
|
|
||||||
if (c == '\r') continue;
|
|
||||||
if (c == '\t') { bash_putc(t, ' ', color); bash_putc(t, ' ', color); continue; }
|
|
||||||
if ((u8)c < 32 && c != '\n') continue;
|
|
||||||
bash_putc(t, c, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 终端内容绘制:全量重绘(与 editor_draw_to 同策略),无输入行 */
|
|
||||||
static void lnx_draw_to(lnx_state *s, int cx, int cy, int cw, int ch) {
|
|
||||||
bash_state *t = &s->term;
|
|
||||||
du_fill_rect(&g_fb, cx, cy, cw, ch, KS_BG_PRIMARY);
|
|
||||||
for (int r = 0; r < t->rows; r++) {
|
|
||||||
for (int c = 0; c < t->cols; c++) {
|
|
||||||
int idx = r * TERM_MAX_COLS + c;
|
|
||||||
if (t->ch[idx] == ' ') continue;
|
|
||||||
int px = cx + (int)DU_SPACE_SM + c * (int)DU_ASCII_STEP;
|
|
||||||
int py = cy + (int)DU_SPACE_SM + r * (int)DU_ASCII_LINE_H;
|
|
||||||
if (px + (int)DU_ASCII_CELL_W > cx + cw) continue;
|
|
||||||
if (py + (int)DU_ASCII_CELL_H > cy + ch) continue;
|
|
||||||
du_draw_char(&g_fb, (char)t->ch[idx], px, py, t->fg[idx], KS_BG_PRIMARY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- 窗口适配 ---- */
|
|
||||||
static void *lnx_on_create(app_ctx *ctx) {
|
|
||||||
int slot = -1;
|
|
||||||
for (int i = 0; i < LNX_MAX_WINS; i++) {
|
|
||||||
if (!g_lnx_used[i]) { slot = i; break; }
|
|
||||||
}
|
|
||||||
if (slot < 0) return 0;
|
|
||||||
g_lnx_used[slot] = 1;
|
|
||||||
|
|
||||||
lnx_state *s = &g_lnx_states[slot];
|
|
||||||
app_descriptor *app = &g_apps[ctx->win->app_id];
|
|
||||||
|
|
||||||
bash_reset(&s->term, ctx->client_w, ctx->client_h);
|
|
||||||
s->finished = 0;
|
|
||||||
s->rc = 0;
|
|
||||||
s->exit_code = 0;
|
|
||||||
|
|
||||||
/* 标题:显示名 + Linux 标记 */
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 48; i++) ctx->win->title[i] = 0;
|
|
||||||
char t[48];
|
|
||||||
int ti = 0;
|
|
||||||
const char *d = app->display_name;
|
|
||||||
while (d[ti] && ti < 38) { t[ti] = d[ti]; ti++; }
|
|
||||||
const char *suf = " . Linux";
|
|
||||||
int j = 0;
|
|
||||||
while (suf[j] && ti < 47) { t[ti++] = suf[j++]; }
|
|
||||||
t[ti] = 0;
|
|
||||||
kstrcpy(ctx->win->title, t, 48);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 命令回显 */
|
|
||||||
bash_puts(&s->term, "$ ", KS_ACCENT2);
|
|
||||||
bash_puts(&s->term, app->linux_cmd, KS_TEXT_PRIMARY);
|
|
||||||
bash_putc(&s->term, '\n', 0);
|
|
||||||
|
|
||||||
/* 服务运行时复查(注册后 guest 仍可能不可用) */
|
|
||||||
if (!g_lxc_svc || !g_lxc_svc->is_available()) {
|
|
||||||
bash_puts(&s->term, "linux: compat service unavailable\n", KS_DANGER);
|
|
||||||
bash_puts(&s->term, "[exit -1]\n", KS_DANGER);
|
|
||||||
s->finished = 1;
|
|
||||||
s->rc = -1;
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* running 状态提示:先呈现窗口再进入同步阻塞 exec */
|
|
||||||
bash_puts(&s->term, "running... (sync exec, UI will freeze)\n", KS_TEXT_DIM);
|
|
||||||
ctx->win->app_state = s; /* 让 redraw_all 能看到本窗口内容 */
|
|
||||||
redraw_all();
|
|
||||||
flip_buffer();
|
|
||||||
|
|
||||||
/* 分词:linux_cmd → path + argv(就地切分副本) */
|
|
||||||
char cmdline[96];
|
|
||||||
kstrcpy(cmdline, app->linux_cmd, 96);
|
|
||||||
const char *argv[8];
|
|
||||||
int argc = 0;
|
|
||||||
char *p = cmdline;
|
|
||||||
while (*p && argc < 8) {
|
|
||||||
while (*p == ' ') p++;
|
|
||||||
if (!*p) break;
|
|
||||||
argv[argc++] = p;
|
|
||||||
while (*p && *p != ' ') p++;
|
|
||||||
if (*p) *p++ = 0;
|
|
||||||
}
|
|
||||||
if (argc == 0) {
|
|
||||||
bash_puts(&s->term, "linux: empty command\n", KS_DANGER);
|
|
||||||
s->finished = 1;
|
|
||||||
s->rc = -1;
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 绝对路径直接使用;裸命令补 /bin/ 前缀(与 shell linux 命令一致) */
|
|
||||||
char path[96];
|
|
||||||
if (argv[0][0] == '/') {
|
|
||||||
kstrcpy(path, argv[0], 96);
|
|
||||||
} else {
|
|
||||||
kstrcpy(path, "/bin/", 96);
|
|
||||||
kstrcat(path, argv[0], 96);
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 out_len = 0, exit_code = 0;
|
|
||||||
slog("linux exec");
|
|
||||||
int rc = g_lxc_svc->exec(path, argc, argv,
|
|
||||||
g_lnx_out, LNX_OUT_CAP - 1,
|
|
||||||
&out_len, &exit_code);
|
|
||||||
|
|
||||||
s->rc = rc;
|
|
||||||
s->exit_code = exit_code;
|
|
||||||
s->finished = 1;
|
|
||||||
|
|
||||||
if (rc != 0) {
|
|
||||||
bash_puts(&s->term, "linux: exec failed (rc=", KS_DANGER);
|
|
||||||
lnx_put_i64(&s->term, rc, KS_DANGER);
|
|
||||||
bash_puts(&s->term, ")\n", KS_DANGER);
|
|
||||||
bash_puts(&s->term, "[exit -1]\n", KS_DANGER);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 输出回显(一次性追加;exec 无流式回调) */
|
|
||||||
if (out_len > 0) {
|
|
||||||
lnx_append(&s->term, g_lnx_out, out_len, KS_TEXT_PRIMARY);
|
|
||||||
if (g_lnx_out[out_len - 1] != '\n') bash_putc(&s->term, '\n', 0);
|
|
||||||
if (out_len >= LNX_OUT_CAP - 1)
|
|
||||||
bash_puts(&s->term, "[output truncated]\n", KS_TEXT_DIM);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 退出码:0 绿 / 非 0 琥珀 */
|
|
||||||
u32 ec_color = (exit_code == 0) ? KS_ACCENT2 : KS_WARN;
|
|
||||||
bash_puts(&s->term, "[exit ", ec_color);
|
|
||||||
lnx_put_u64(&s->term, exit_code, ec_color);
|
|
||||||
bash_puts(&s->term, "]\n", ec_color);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void lnx_on_draw(void *state, app_ctx *ctx) {
|
|
||||||
lnx_draw_to((lnx_state *)state, ctx->client_x, ctx->client_y,
|
|
||||||
ctx->client_w, ctx->client_h);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void lnx_on_destroy(void *state) {
|
|
||||||
lnx_state *s = (lnx_state *)state;
|
|
||||||
for (int i = 0; i < LNX_MAX_WINS; i++) {
|
|
||||||
if (&g_lnx_states[i] == s) { g_lnx_used[i] = 0; break; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Linux 应用注册 ----
|
|
||||||
* 无论服务是否可用都注册图标:可用时双击即同步 exec;不可用(VMX 缺失/
|
|
||||||
* guest 未驻留)时图标置灰(g_lnx_svc_up=0),双击由 lnx_on_create 运行时
|
|
||||||
* 复查并显示 "linux: compat service unavailable [exit -1]",绝不 panic。
|
|
||||||
* 配置:FAT32 根目录 LINUXAPP.CNF(8.3 名 LINUXAPPCNF),每行 显示名|linux命令;
|
|
||||||
* 文件缺失或无有效行时回退内置默认列表(echo hello / uname -a / ls -l /,
|
|
||||||
* 三条均为 Arch rootfs 与 initramfs busybox 环境共有的可执行命令)。 */
|
|
||||||
static void register_linux_apps(void) {
|
|
||||||
g_lnx_svc_up = (g_lxc_svc && g_lxc_svc->is_available()) ? 1 : 0;
|
|
||||||
if (!g_lxc_svc) slog("linux apps: service absent (icons greyed)");
|
|
||||||
else if (!g_lnx_svc_up) slog("linux apps: service unavailable (icons greyed)");
|
|
||||||
|
|
||||||
da_linux_app ents[DA_LINUXAPP_MAX];
|
|
||||||
int n = 0;
|
|
||||||
|
|
||||||
u8 *data = 0;
|
|
||||||
u32 size = 0;
|
|
||||||
if (f32_read_root_file(DA_LINUXAPP_CNF_83, &data, &size) == 0 && size > 0) {
|
|
||||||
n = da_linuxapp_parse((const char *)data, size, ents, DA_LINUXAPP_MAX);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n <= 0) {
|
|
||||||
/* 内置默认保底(guest 中确定存在的命令) */
|
|
||||||
kstrcpy(g_lnx_name_pool[0], "HELLO", 12);
|
|
||||||
kstrcpy(g_lnx_cmd_pool[0], "/bin/echo hello from deshab linux", 64);
|
|
||||||
kstrcpy(g_lnx_name_pool[1], "UNAME", 12);
|
|
||||||
kstrcpy(g_lnx_cmd_pool[1], "/bin/uname -a", 64);
|
|
||||||
kstrcpy(g_lnx_name_pool[2], "LS", 12);
|
|
||||||
kstrcpy(g_lnx_cmd_pool[2], "/bin/ls -l /", 64);
|
|
||||||
n = 3;
|
|
||||||
slog("linux apps: builtin defaults");
|
|
||||||
} else {
|
|
||||||
for (int i = 0; i < n; i++) {
|
|
||||||
kstrcpy(g_lnx_name_pool[i], ents[i].name, 12);
|
|
||||||
kstrcpy(g_lnx_cmd_pool[i], ents[i].cmd, 64);
|
|
||||||
}
|
|
||||||
slog("linux apps: loaded LINUXAPP.CNF");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < n && g_app_count < MAX_APPS; i++) {
|
|
||||||
char *id = g_lnx_id_pool[i];
|
|
||||||
id[0] = 'l'; id[1] = 'n'; id[2] = 'x'; id[3] = (char)('0' + i); id[4] = 0;
|
|
||||||
g_apps[g_app_count] = (app_descriptor){
|
|
||||||
id, g_lnx_name_pool[i], 720, 480,
|
|
||||||
lnx_on_create, 0, lnx_on_draw, lnx_on_destroy,
|
|
||||||
0, g_lnx_cmd_pool[i]
|
|
||||||
};
|
|
||||||
g_app_count++;
|
|
||||||
{
|
|
||||||
char msg[64];
|
|
||||||
kstrcpy(msg, "linux app registered: ", 64);
|
|
||||||
kstrcat(msg, g_lnx_name_pool[i], 64);
|
|
||||||
slog(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
slog("linux apps registered");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
* 文本编辑器 (Editor) — 面板/IDE/窗口共用核心
|
* 文本编辑器 (Editor) — 面板/IDE/窗口共用核心
|
||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
@@ -1739,86 +1427,67 @@ static void register_apps(void) {
|
|||||||
/* 命令行1:shell.elf(原生命令行 + 可运行 Linux 程序)*/
|
/* 命令行1:shell.elf(原生命令行 + 可运行 Linux 程序)*/
|
||||||
g_apps[0] = (app_descriptor){
|
g_apps[0] = (app_descriptor){
|
||||||
"shell", "Terminal", 720, 480,
|
"shell", "Terminal", 720, 480,
|
||||||
0, 0, 0, 0, "SHELL ELF", 0
|
0, 0, 0, 0, "SHELL ELF"
|
||||||
};
|
};
|
||||||
g_app_count++;
|
g_app_count++;
|
||||||
/* 命令行2:cmd.elf(Windows 风格命令行 + PE/EXE 执行)*/
|
/* 命令行2:cmd.elf(Windows 风格命令行 + PE/EXE 执行)*/
|
||||||
g_apps[1] = (app_descriptor){
|
g_apps[1] = (app_descriptor){
|
||||||
"cmd", "CMD", 720, 480,
|
"cmd", "CMD", 720, 480,
|
||||||
0, 0, 0, 0, "CMD ELF", 0
|
0, 0, 0, 0, "CMD ELF"
|
||||||
};
|
};
|
||||||
g_app_count++;
|
g_app_count++;
|
||||||
g_apps[2] = (app_descriptor){
|
g_apps[2] = (app_descriptor){
|
||||||
"editor", "Editor", 600, 450,
|
"editor", "Editor", 600, 450,
|
||||||
editor_on_create, editor_on_event, editor_on_draw, editor_on_destroy, 0, 0
|
editor_on_create, editor_on_event, editor_on_draw, editor_on_destroy, 0
|
||||||
};
|
};
|
||||||
g_app_count++;
|
g_app_count++;
|
||||||
g_apps[3] = (app_descriptor){
|
g_apps[3] = (app_descriptor){
|
||||||
"fileman", "Files", 500, 400,
|
"fileman", "Files", 500, 400,
|
||||||
0, 0, 0, 0, "FILEMAN ELF", 0
|
0, 0, 0, 0, "FILEMAN ELF"
|
||||||
};
|
};
|
||||||
g_app_count++;
|
g_app_count++;
|
||||||
g_apps[4] = (app_descriptor){
|
g_apps[4] = (app_descriptor){
|
||||||
"calc", "Calculator", 280, 400,
|
"calc", "Calculator", 280, 400,
|
||||||
calc_on_create, calc_on_event, calc_on_draw, calc_on_destroy, 0, 0
|
calc_on_create, calc_on_event, calc_on_draw, calc_on_destroy, 0
|
||||||
};
|
};
|
||||||
g_app_count++;
|
g_app_count++;
|
||||||
g_apps[5] = (app_descriptor){
|
g_apps[5] = (app_descriptor){
|
||||||
"browser", "Browser", 700, 500,
|
"browser", "Browser", 700, 500,
|
||||||
0, 0, 0, 0, "BROWSER ELF", 0
|
0, 0, 0, 0, "BROWSER ELF"
|
||||||
|
};
|
||||||
|
g_app_count++;
|
||||||
|
/* ProEdit — 增强版文本编辑器(语法高亮、设置、目录浏览)*/
|
||||||
|
g_apps[6] = (app_descriptor){
|
||||||
|
"proedit", "ProEdit", 750, 550,
|
||||||
|
0, 0, 0, 0, "PROEDIT ELF"
|
||||||
};
|
};
|
||||||
g_app_count++;
|
g_app_count++;
|
||||||
|
|
||||||
/* Linux 兼容层应用:无论服务是否可用都注册图标(不可用时置灰,
|
|
||||||
* 双击仍开终端窗口提示原因,见 register_linux_apps / lnx_on_create) */
|
|
||||||
register_linux_apps();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Winux-Kate .desktop-grid:canvas padding 24px,列宽 104px,gap 18px,
|
/* Winux-Kate .desktop-grid:canvas padding 24px,列宽 104px,gap 18px,
|
||||||
* 左→右排列,超出画布宽度则换行(align-content: start)
|
* 左→右排列,超出画布宽度则换行(align-content: start)
|
||||||
*
|
*
|
||||||
* 桌面图标来源:
|
* 注意:Terminal 和 CMD 已在第一页 Dashboard(TERM-01 / TERM-02),
|
||||||
* 1. 用户快捷方式(SYSTEM/user/desktop 目录)
|
* 桌面不再重复显示其图标。
|
||||||
* 2. 内置应用(editor, browser 等)
|
|
||||||
*/
|
*/
|
||||||
static void setup_desktop_icons(void) {
|
static void setup_desktop_icons(void) {
|
||||||
int pad = KATE_ICON_PAD; /* 24 */
|
int pad = KATE_ICON_PAD; /* 24 */
|
||||||
int x = pad;
|
int x = pad;
|
||||||
int y = KATE_TOPBAR_H + KATE_PAGE_PAD + pad;
|
int y = KATE_TOPBAR_H + KATE_PAGE_PAD + pad;
|
||||||
int max_x = (int)g_fb_w - pad; /* canvas 右缘 */
|
int max_x = (int)g_fb_w - pad; /* canvas 右缘 */
|
||||||
|
for (int i = 0; i < g_app_count; i++) {
|
||||||
/* 先加载用户快捷方式 */
|
if (!g_apps[i].on_create && !g_apps[i].elf_name) continue;
|
||||||
shortcuts_refresh();
|
/* 跳过 Terminal 和 CMD(已在 Dashboard 第一页) */
|
||||||
for (int i = 0; i < g_shortcut_count && g_icon_count < MAX_ICONS; i++) {
|
if (g_apps[i].name[0] == 's' && g_apps[i].name[1] == 'h' && g_apps[i].name[2] == 'e') continue;
|
||||||
|
if (g_apps[i].name[0] == 'c' && g_apps[i].name[1] == 'm' && g_apps[i].name[2] == 'd') continue;
|
||||||
if (x > pad && x + KATE_ICON_W > max_x) {
|
if (x > pad && x + KATE_ICON_W > max_x) {
|
||||||
x = pad;
|
x = pad;
|
||||||
y += KATE_ICON_H + KATE_ICON_GAP;
|
y += KATE_ICON_H + KATE_ICON_GAP;
|
||||||
}
|
}
|
||||||
/* 将快捷方式转换为桌面图标(app_id = -1 表示快捷方式) */
|
g_icons[g_icon_count].app_id = i;
|
||||||
g_icons[g_icon_count].app_id = -1; /* 标记为快捷方式 */
|
|
||||||
g_icons[g_icon_count].x = x;
|
g_icons[g_icon_count].x = x;
|
||||||
g_icons[g_icon_count].y = y;
|
g_icons[g_icon_count].y = y;
|
||||||
g_icons[g_icon_count].label = g_shortcuts[i].display_name;
|
g_icons[g_icon_count].label = g_apps[i].display_name;
|
||||||
g_icons[g_icon_count].selected = 0;
|
|
||||||
g_shortcuts[i].x = x; /* 记录位置用于点击检测 */
|
|
||||||
g_shortcuts[i].y = y;
|
|
||||||
g_icon_count++;
|
|
||||||
x += KATE_ICON_W + KATE_ICON_GAP;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 再追加 Linux 应用图标(register_linux_apps 已登记进 g_apps,带 linux_cmd)。
|
|
||||||
* 服务不可用时图标仍上架但置灰(draw_app_icon 查 g_lnx_svc_up),
|
|
||||||
* 双击开终端窗口提示原因,与可用时同一入口。 */
|
|
||||||
for (int a = 0; a < g_app_count && g_icon_count < MAX_ICONS; a++) {
|
|
||||||
if (!g_apps[a].linux_cmd) continue;
|
|
||||||
if (x > pad && x + KATE_ICON_W > max_x) {
|
|
||||||
x = pad;
|
|
||||||
y += KATE_ICON_H + KATE_ICON_GAP;
|
|
||||||
}
|
|
||||||
g_icons[g_icon_count].app_id = a;
|
|
||||||
g_icons[g_icon_count].x = x;
|
|
||||||
g_icons[g_icon_count].y = y;
|
|
||||||
g_icons[g_icon_count].label = g_apps[a].display_name;
|
|
||||||
g_icons[g_icon_count].selected = 0;
|
g_icons[g_icon_count].selected = 0;
|
||||||
g_icon_count++;
|
g_icon_count++;
|
||||||
x += KATE_ICON_W + KATE_ICON_GAP;
|
x += KATE_ICON_W + KATE_ICON_GAP;
|
||||||
@@ -1842,10 +1511,7 @@ typedef struct { u32 type,flags; u64 offset,vaddr,paddr,filesz,memsz,align; } dt
|
|||||||
typedef struct { i64 tag; u64 val; } dt_elf64_dyn;
|
typedef struct { i64 tag; u64 val; } dt_elf64_dyn;
|
||||||
typedef struct { u64 offset; u64 info; i64 addend; } dt_elf64_rela;
|
typedef struct { u64 offset; u64 info; i64 addend; } dt_elf64_rela;
|
||||||
|
|
||||||
/* 外部工具 ELF 加载缓冲。fileman/editor 等工具 include fat32_io.h
|
static u8 g_elf_image[262144];
|
||||||
* 带 ~388KB BSS(f32_disk+f32_data),其 .elf 的 memsz 可达 ~470KB。
|
|
||||||
* 1MB 容量覆盖当前所有工具 ELF 并留余量。BSS 是 NOBITS 不占 desktop.elf 文件空间。 */
|
|
||||||
static u8 g_elf_image[1048576];
|
|
||||||
|
|
||||||
static int dt_load_elf(u8 *data, u32 data_size, void **entry_out) {
|
static int dt_load_elf(u8 *data, u32 data_size, void **entry_out) {
|
||||||
const dt_elf64_ehdr *eh = (const dt_elf64_ehdr *)data;
|
const dt_elf64_ehdr *eh = (const dt_elf64_ehdr *)data;
|
||||||
@@ -2023,43 +1689,6 @@ static void files_refresh(void) {
|
|||||||
else g_files_loaded = 0;
|
else g_files_loaded = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 加载桌面快捷方式(从 SYSTEM/user/desktop 目录读取 .lnk 文件)
|
|
||||||
* 快捷方式文件格式(纯文本,UTF-8):
|
|
||||||
* 第1行:显示名称(如 "编辑器")
|
|
||||||
* 第2行:ELF文件名(FAT32 8.3,如 "EDITOR ELF")
|
|
||||||
* 第3行:图标类型(如 "editor")
|
|
||||||
*/
|
|
||||||
static void shortcuts_refresh(void) {
|
|
||||||
g_shortcut_count = 0;
|
|
||||||
if (!g_block_read) return;
|
|
||||||
|
|
||||||
/* 读取 desktop 目录下的 .lnk 文件 */
|
|
||||||
/* 简化实现:直接读取预定义的快捷方式 */
|
|
||||||
|
|
||||||
/* 内置快捷方式列表(暂时硬编码,后续可改为读取.lnk文件) */
|
|
||||||
static const struct {
|
|
||||||
const char *name;
|
|
||||||
const char *elf;
|
|
||||||
const char *icon;
|
|
||||||
} builtin_shortcuts[] = {
|
|
||||||
{ "编辑器", "EDITOR ELF", "editor" },
|
|
||||||
{ "文件管理", "FILEMAN ELF", "folder" },
|
|
||||||
{ "浏览器", "BROWSER ELF", "browser" },
|
|
||||||
{ "终端", "SHELL ELF", "terminal" },
|
|
||||||
};
|
|
||||||
|
|
||||||
int count = sizeof(builtin_shortcuts) / sizeof(builtin_shortcuts[0]);
|
|
||||||
for (int i = 0; i < count && g_shortcut_count < MAX_ICONS; i++) {
|
|
||||||
desktop_shortcut *s = &g_shortcuts[g_shortcut_count];
|
|
||||||
kstrcpy(s->display_name, builtin_shortcuts[i].name, 24);
|
|
||||||
kstrcpy(s->elf_name, builtin_shortcuts[i].elf, 11);
|
|
||||||
kstrcpy(s->icon_type, builtin_shortcuts[i].icon, 12);
|
|
||||||
s->x = 0; s->y = 0;
|
|
||||||
s->selected = 0;
|
|
||||||
g_shortcut_count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 双击打开文件 → 载入 DASHBOARD EDITOR */
|
/* 双击打开文件 → 载入 DASHBOARD EDITOR */
|
||||||
static void files_open(int idx) {
|
static void files_open(int idx) {
|
||||||
if (idx < 0 || idx >= g_file_count) return;
|
if (idx < 0 || idx >= g_file_count) return;
|
||||||
@@ -2474,20 +2103,7 @@ static void draw_desktop_icons(void) {
|
|||||||
du_kate_desktop_icon(&g_fb, ic->x, ic->y, ic->label, ic->selected);
|
du_kate_desktop_icon(&g_fb, ic->x, ic->y, ic->label, ic->selected);
|
||||||
int ix = ic->x + (KATE_ICON_W - KATE_ICON_IMG) / 2;
|
int ix = ic->x + (KATE_ICON_W - KATE_ICON_IMG) / 2;
|
||||||
int iy = ic->y + 10;
|
int iy = ic->y + 10;
|
||||||
|
draw_app_icon(&g_fb, ic->app_id, ix, iy);
|
||||||
/* 根据图标来源绘制不同图标 */
|
|
||||||
if (ic->app_id >= 0) {
|
|
||||||
/* 内置应用图标 */
|
|
||||||
draw_app_icon(&g_fb, ic->app_id, ix, iy);
|
|
||||||
} else if (i < g_shortcut_count) {
|
|
||||||
/* 快捷方式图标(根据 icon_type 选择) */
|
|
||||||
const char *icon = g_shortcuts[i].icon_type;
|
|
||||||
if (icon[0] == 'e' && icon[1] == 'd') draw_icon_editor(&g_fb, ix, iy);
|
|
||||||
else if (icon[0] == 'f') draw_icon_folder(&g_fb, ix, iy);
|
|
||||||
else if (icon[0] == 'b') draw_icon_browser(&g_fb, ix, iy);
|
|
||||||
else if (icon[0] == 't') draw_icon_terminal(&g_fb, ix, iy);
|
|
||||||
else draw_icon_editor(&g_fb, ix, iy); /* 默认 */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3291,15 +2907,7 @@ static int desktop_click(int mx, int my, int dbl) {
|
|||||||
int ix = g_icons[i].x, iy = g_icons[i].y;
|
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 (mx >= ix && mx < ix + ICON_W && my >= iy && my < iy + ICON_H) {
|
||||||
if (dbl && g_icons[i].selected) {
|
if (dbl && g_icons[i].selected) {
|
||||||
/* 根据图标来源选择启动方式 */
|
launch_app(g_icons[i].app_id);
|
||||||
if (g_icons[i].app_id >= 0) {
|
|
||||||
/* 内置应用 */
|
|
||||||
launch_app(g_icons[i].app_id);
|
|
||||||
} else if (i < g_shortcut_count) {
|
|
||||||
/* 快捷方式:加载外部 ELF */
|
|
||||||
slog("launching shortcut");
|
|
||||||
launch_external_elf(g_shortcuts[i].elf_name);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
for (int j = 0; j < g_icon_count; j++) g_icons[j].selected = 0;
|
for (int j = 0; j < g_icon_count; j++) g_icons[j].selected = 0;
|
||||||
g_icons[i].selected = 1;
|
g_icons[i].selected = 1;
|
||||||
@@ -3544,22 +3152,6 @@ void dsk_entry(const dsk_boot_context *ctx) {
|
|||||||
g_fb.fb = (du_u32 *)SPRITE_BUF_ADDR;
|
g_fb.fb = (du_u32 *)SPRITE_BUF_ADDR;
|
||||||
f32_init(g_block_read, g_block_write);
|
f32_init(g_block_read, g_block_write);
|
||||||
|
|
||||||
/* Linux 兼容层服务(reserved[5],magic 校验)—— 需在 register_apps 前就绪 */
|
|
||||||
{
|
|
||||||
u64 lxc_addr = ctx->reserved[5];
|
|
||||||
if (lxc_addr) {
|
|
||||||
const linux_compat_service *lxc = (const linux_compat_service *)lxc_addr;
|
|
||||||
if (lxc->magic == LINUX_COMPAT_MAGIC) {
|
|
||||||
g_lxc_svc = lxc;
|
|
||||||
slog("linux_compat service ok");
|
|
||||||
} else {
|
|
||||||
slog("linux_compat magic mismatch");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
slog("linux_compat service absent");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tsc_calibrate();
|
tsc_calibrate();
|
||||||
g_rtc_boot_sec = rtc_day_sec();
|
g_rtc_boot_sec = rtc_day_sec();
|
||||||
g_last_sec = g_rtc_boot_sec;
|
g_last_sec = g_rtc_boot_sec;
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ OBJECTS := $(C_SOURCES:.c=.o)
|
|||||||
all: $(OUT)
|
all: $(OUT)
|
||||||
|
|
||||||
$(OUT): $(OBJECTS) linker.ld
|
$(OUT): $(OBJECTS) linker.ld
|
||||||
@if not exist "$(ROOT)\SYSTEM\system\deshab64" mkdir "$(ROOT)\SYSTEM\system\deshab64"
|
@mkdir -p "$(ROOT)/SYSTEM/system/deshab64"
|
||||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ OBJECTS := $(C_SOURCES:.c=.o)
|
|||||||
all: $(OUT)
|
all: $(OUT)
|
||||||
|
|
||||||
$(OUT): $(OBJECTS) linker.ld
|
$(OUT): $(OBJECTS) linker.ld
|
||||||
@if not exist "$(ROOT)\SYSTEM\system\user\use" mkdir "$(ROOT)\SYSTEM\system\user\use"
|
@mkdir -p "$(ROOT)/SYSTEM/system/user/use"
|
||||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@ all: $(OUT)
|
|||||||
|
|
||||||
# 构建后用 patch_login_hash.ps1 计算 .text SHA256 并写入 .hashseg
|
# 构建后用 patch_login_hash.ps1 计算 .text SHA256 并写入 .hashseg
|
||||||
$(OUT): $(OBJECTS) linker.ld
|
$(OUT): $(OBJECTS) linker.ld
|
||||||
@if not exist "$(ROOT)\SYSTEM\system\user\use" mkdir "$(ROOT)\SYSTEM\system\user\use"
|
@mkdir -p "$(ROOT)/SYSTEM/system/user/use"
|
||||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||||
@echo [login] patching .text SHA256 into .hashseg...
|
@echo [login] patching .text SHA256 into .hashseg...
|
||||||
powershell -NoProfile -ExecutionPolicy Bypass -File $(PATCHER) $@
|
powershell -NoProfile -ExecutionPolicy Bypass -File $(PATCHER) $@
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@ OBJECTS := $(C_SOURCES:.c=.o)
|
|||||||
all: $(OUT)
|
all: $(OUT)
|
||||||
|
|
||||||
$(OUT): $(OBJECTS) linker.ld
|
$(OUT): $(OBJECTS) linker.ld
|
||||||
@if not exist "$(ROOT)\SYSTEM\system\deshab64\mouse" mkdir "$(ROOT)\SYSTEM\system\deshab64\mouse"
|
@mkdir -p "$(ROOT)/SYSTEM/system/deshab64/mouse"
|
||||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ OBJECTS := $(C_SOURCES:.c=.o)
|
|||||||
all: $(OUT)
|
all: $(OUT)
|
||||||
|
|
||||||
$(OUT): $(OBJECTS) linker.ld
|
$(OUT): $(OBJECTS) linker.ld
|
||||||
@if not exist "$(ROOT)\SYSTEM\system\deshab64\network" mkdir "$(ROOT)\SYSTEM\system\deshab64\network"
|
@mkdir -p "$(ROOT)/SYSTEM/system/deshab64/network"
|
||||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@ OBJECTS := $(C_SOURCES:.c=.o)
|
|||||||
all: $(OUT)
|
all: $(OUT)
|
||||||
|
|
||||||
$(OUT): $(OBJECTS) linker.ld
|
$(OUT): $(OBJECTS) linker.ld
|
||||||
@if not exist "$(ROOT)\SYSTEM\system\deshab64" mkdir "$(ROOT)\SYSTEM\system\deshab64"
|
@mkdir -p "$(ROOT)/SYSTEM/system/deshab64"
|
||||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ OBJECTS := $(C_SOURCES:.c=.o)
|
|||||||
all: $(OUT)
|
all: $(OUT)
|
||||||
|
|
||||||
$(OUT): $(OBJECTS) linker.ld
|
$(OUT): $(OBJECTS) linker.ld
|
||||||
@if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools"
|
@mkdir -p "$(ROOT)/SYSTEM/system/deshab64/tools"
|
||||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ OBJECTS := $(C_SOURCES:.c=.o)
|
|||||||
all: $(OUT)
|
all: $(OUT)
|
||||||
|
|
||||||
$(OUT): $(OBJECTS) linker.ld
|
$(OUT): $(OBJECTS) linker.ld
|
||||||
@if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools"
|
@mkdir -p "$(ROOT)/SYSTEM/system/deshab64/tools"
|
||||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
|
|||||||
@@ -0,0 +1,447 @@
|
|||||||
|
/* desktop_app_linux.h — Deshab 应用 Linux 预览后端 (SDL2)
|
||||||
|
*
|
||||||
|
* 允许在 Linux 开发机上预览 Deshab-OS 应用的 UI 效果。
|
||||||
|
* 使用方法:
|
||||||
|
* 1. #
|
||||||
|
define DA_LINUX_PREVIEW 1 * 2. #include "desktop_app_linux.h"
|
||||||
|
* 3. 实现 dsk_entry() 函数(与 Deshab-OS 版本相同)
|
||||||
|
* 4. 编译:make -f Makefile.linux
|
||||||
|
*
|
||||||
|
* 编译后会在 X11/Wayland 窗口中显示应用 UI,可用于调试布局和渲染。
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DESHAB_DESKTOP_APP_LINUX_H
|
||||||
|
#define DESHAB_DESKTOP_APP_LINUX_H
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
* 基础类型(与 desktop_app.h 一致)
|
||||||
|
* =================================================================== */
|
||||||
|
|
||||||
|
typedef uint8_t u8;
|
||||||
|
typedef uint16_t u16;
|
||||||
|
typedef uint32_t u32;
|
||||||
|
typedef uint64_t u64;
|
||||||
|
typedef int64_t i64;
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
* 颜色常量(与 desktop_app.h 一致)
|
||||||
|
* =================================================================== */
|
||||||
|
|
||||||
|
#define DA_BG_PRIMARY 0xFF0A1428u
|
||||||
|
#define DA_BG_SECONDARY 0xFF0F1E38u
|
||||||
|
#define DA_BG_TERTIARY 0xFF162848u
|
||||||
|
#define DA_TEXT_PRIMARY 0xFFE8E8F0u
|
||||||
|
#define DA_TEXT_DIM 0xFF6A6A88u
|
||||||
|
#define DA_ACCENT 0xFF00A8CCu
|
||||||
|
#define DA_ACCENT_LIGHT 0xFF44CCF0u
|
||||||
|
#define DA_BORDER 0xFF284888u
|
||||||
|
#define DA_BORDER_FOCUS 0xFF44CCF0u
|
||||||
|
#define DA_CURSOR_COLOR 0xFF00A8CCu
|
||||||
|
#define DA_SUCCESS 0xFF40C880u
|
||||||
|
#define DA_ERROR 0xFFFF4466u
|
||||||
|
#define DA_WARNING 0xFFF0A030u
|
||||||
|
|
||||||
|
#define DA_TITLEBAR_H 28
|
||||||
|
#define DA_BORDER_W 2
|
||||||
|
#define DA_STATUSBAR_H 20
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
* 渲染上下文(SDL2 版本)
|
||||||
|
* =================================================================== */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Uint32 *pixels; /* SDL2 像素缓冲 */
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int pitch; /* 行字节宽 */
|
||||||
|
SDL_Window *window;
|
||||||
|
SDL_Renderer *renderer;
|
||||||
|
SDL_Texture *texture;
|
||||||
|
} da_app_context;
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
* ASCII 字体声明(需要从 ascii_bitmaps.c 包含)
|
||||||
|
* =================================================================== */
|
||||||
|
|
||||||
|
/* 如果外部未定义字体,则声明 extern */
|
||||||
|
/* 在 notes_preview.c 中 include ascii_bitmaps.c 后会定义实际变量 */
|
||||||
|
#ifndef DA_FONT_DEFINED
|
||||||
|
extern const unsigned char *g_ascii[];
|
||||||
|
extern int g_ascii_w;
|
||||||
|
extern int g_ascii_h;
|
||||||
|
#else
|
||||||
|
/* 使用 extern 引用 ascii_bitmaps.c 中定义的 static 变量 */
|
||||||
|
/* 注意:ascii_bitmaps.c 需要改为非 static */
|
||||||
|
extern const unsigned char g_ascii[][11*18];
|
||||||
|
extern int g_ascii_w;
|
||||||
|
extern int g_ascii_h;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
* 绘制原语(SDL2 实现)
|
||||||
|
* =================================================================== */
|
||||||
|
|
||||||
|
/* 颜色混合 */
|
||||||
|
static inline Uint32 da_blend(Uint32 bg, Uint32 fg, Uint32 alpha) {
|
||||||
|
Uint32 na = 256 - alpha;
|
||||||
|
Uint32 r = ((bg & 0xFF) * na + (fg & 0xFF) * alpha) >> 8;
|
||||||
|
Uint32 g = (((bg >> 8) & 0xFF) * na + ((fg >> 8) & 0xFF) * alpha) >> 8;
|
||||||
|
Uint32 b = (((bg >> 16) & 0xFF) * na + ((fg >> 16) & 0xFF) * alpha) >> 8;
|
||||||
|
return 0xFF000000u | (b << 16) | (g << 8) | r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 像素写入(SDL2 锁定时) */
|
||||||
|
static inline void da_pixel(da_app_context *ac, int x, int y, Uint32 color) {
|
||||||
|
if (x < 0 || x >= ac->width || y < 0 || y >= ac->height) return;
|
||||||
|
ac->pixels[y * ac->width + x] = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 矩形填充 */
|
||||||
|
static inline void da_fill_rect(da_app_context *ac, int x, int y, int w, int h, Uint32 color) {
|
||||||
|
for (int r = 0; r < h; r++) {
|
||||||
|
for (int c = 0; c < w; c++) {
|
||||||
|
int xx = x + c, yy = y + r;
|
||||||
|
if (xx >= 0 && xx < ac->width && yy >= 0 && yy < ac->height) {
|
||||||
|
ac->pixels[yy * ac->width + xx] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 全屏背景 */
|
||||||
|
static inline void da_fill_bg(da_app_context *ac, Uint32 color) {
|
||||||
|
memset(ac->pixels,0, (size_t)ac->width * ac->height *4);
|
||||||
|
da_fill_rect(ac, 0, 0, ac->width, ac->height, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 圆角矩形(简化版) */
|
||||||
|
static inline void da_fill_rounded_rect(da_app_context *ac, int x, int y , int w, int h,
|
||||||
|
Uint32 color, int radius) {
|
||||||
|
/* 中间区域 */
|
||||||
|
da_fill_rect(ac, x + radius, y, w - 2 * radius, h, color);
|
||||||
|
da_fill_rect(ac, x, y + radius, radius, h - 2 * radius, color);
|
||||||
|
da_fill_rect(ac, x + w - radius, y + radius, radius, h - 2 * radius, color);
|
||||||
|
/* 四个角(简化为填充) */
|
||||||
|
for (int dy = 0; dy < radius; dy++) {
|
||||||
|
for (int dx = 0; dx < radius; dx++) {
|
||||||
|
if (dx * dx + dy * dy <= radius * radius) {
|
||||||
|
da_pixel(ac, x + radius - 1 - dx, y + radius - 1 - dy, color);
|
||||||
|
da_pixel(ac, x + w - radius + dx, y + radius - 1 - dy, color);
|
||||||
|
da_pixel(ac, x + radius - 1 - dx, y + h - radius + dy, color);
|
||||||
|
da_pixel(ac, x + w - radius + dx, y + h - radius + dy, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 字符渲染 */
|
||||||
|
static inline void da_draw_char(da_app_context *ac, char ch, int x, int y, Uint32 fg, Uint32 bg) {
|
||||||
|
Uint32 idx = (Uint32)(ch - ' ');
|
||||||
|
if (idx > 94) idx = 0;
|
||||||
|
if (!g_ascii[idx]) return;
|
||||||
|
|
||||||
|
const unsigned char *glyph = g_ascii[idx];
|
||||||
|
int gw = g_ascii_w, gh = g_ascii_h;
|
||||||
|
|
||||||
|
for (int r = 0; r < gh; r++) {
|
||||||
|
for (int c = 0; c < gw; c++) {
|
||||||
|
unsigned char a = glyph[r * gw + c];
|
||||||
|
if (a == 0) continue;
|
||||||
|
int xx = x + c, yy = y + r;
|
||||||
|
if (xx < 0 || xx >= ac->width || yy < 0 || yy >= ac->height) continue;
|
||||||
|
|
||||||
|
Uint32 pixel = (a == 255) ? fg : da_blend(bg, fg, a);
|
||||||
|
ac->pixels[yy * ac->width + xx] = pixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 字符串渲染 */
|
||||||
|
static inline void da_draw_string(da_app_context *ac, const char *s, int x, int y,
|
||||||
|
Uint32 fg, Uint32 bg, int step) {
|
||||||
|
int cx = x;
|
||||||
|
while (*s) {
|
||||||
|
da_draw_char(ac, *s, cx, y, fg, bg);
|
||||||
|
cx += step;
|
||||||
|
s ++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*标题栏 */
|
||||||
|
static inline void da_draw_titlebar(da_app_context *ac, const char *title, int w) {
|
||||||
|
da_fill_rect(ac, 0, 0, w, DA_TITLEBAR_H, DA_BG_SECONDARY);
|
||||||
|
da_draw_string(ac, title, 8, (DA_TITLEBAR_H - 18) / 2, DA_TEXT_PRIMARY, DA_BG_SECONDARY, 12);
|
||||||
|
/* 关闭按钮 */
|
||||||
|
da_fill_rounded_rect(ac, w - 28, 4, 24, 20, DA_ERROR, 4);
|
||||||
|
da_draw_string(ac, "X", w - 20, 5, 0xFF0A1428u, DA_ERROR, 12);
|
||||||
|
/* 底部分隔线 */
|
||||||
|
da_fill_rect(ac, 0, DA_TITLEBAR_H, w, 1, DA_ACCENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 状态栏 */
|
||||||
|
static inline void da_draw_statusbar(da_app_context *ac, const char *text, int w, int h) {
|
||||||
|
int y = h - DA_STATUSBAR_H;
|
||||||
|
da_fill_rect(ac, 0, y, w, DA_STATUSBAR_H, DA_BG_SECONDARY);
|
||||||
|
da_draw_string(ac, text, 4, y + 1, DA_TEXT_DIM, DA_BG_SECONDARY, 12);
|
||||||
|
da_fill_rect(ac, 0, y, w, 1, DA_BORDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
* 上下文初始化/刷新
|
||||||
|
* =================================================================== */
|
||||||
|
|
||||||
|
static inline int da_init_linux(da_app_context *ac, const char *title, int width, int height) {
|
||||||
|
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
||||||
|
fprintf(stderr, "SDL2 init failed: %s\n", SDL_GetError());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ac->window = SDL_CreateWindow(title,
|
||||||
|
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||||
|
width, height,
|
||||||
|
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
|
||||||
|
if (!ac->window) {
|
||||||
|
fprintf(stderr, "SDL_CreateWindow failed: %s\n", SDL_GetError());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ac->renderer = SDL_CreateRenderer(ac->window, -1, SDL_RENDERER_ACCELERATED);
|
||||||
|
if (!ac->renderer) {
|
||||||
|
fprintf(stderr, "SDL_CreateRenderer failed: %s\n", SDL_GetError());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ac->texture = SDL_CreateTexture(ac->renderer, SDL_PIXELFORMAT_ARGB8888,
|
||||||
|
SDL_TEXTUREACCESS_STREAMING,
|
||||||
|
width, height);
|
||||||
|
if (!ac->texture) {
|
||||||
|
fprintf(stderr, "SDL_CreateTexture failed: %s\n", SDL_GetError());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ac->width = width;
|
||||||
|
ac->height = height;
|
||||||
|
ac->pitch = width * 4;
|
||||||
|
ac->pixels = (Uint32 *)malloc((size_t)width * height * 4);
|
||||||
|
if (!ac->pixels) {
|
||||||
|
fprintf(stderr, "malloc failed\n");
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
} memset(ac->pixels, 0, (size_t)width * height * 4);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 刷新帧缓冲到屏幕 */
|
||||||
|
static inline void da_flush(da_app_context *ac) {
|
||||||
|
SDL_UpdateTexture(ac->texture, NULL, ac->pixels, ac->pitch);
|
||||||
|
SDL_RenderClear(ac->renderer);
|
||||||
|
SDL_RenderCopy(ac->renderer, ac->texture, NULL, NULL);
|
||||||
|
SDL_RenderPresent(ac->renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 清理资源 */
|
||||||
|
static inline void da_cleanup(da_app_context *ac) {
|
||||||
|
free(ac->pixels);
|
||||||
|
if (ac->texture) SDL_DestroyTexture(ac->texture);
|
||||||
|
if (ac->renderer) SDL_DestroyRenderer(ac->renderer);
|
||||||
|
if (ac->window) SDL_DestroyWindow(ac->window);
|
||||||
|
SDL_Quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 事件循环(等待关闭窗口) */
|
||||||
|
static inline void da_event_loop(void) {
|
||||||
|
SDL_Event event;
|
||||||
|
int running = 1;
|
||||||
|
while (running) {
|
||||||
|
while (SDL_PollEvent(&event)) {
|
||||||
|
if (event.type == SDL_QUIT) running = 0;
|
||||||
|
if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE) running = 0;
|
||||||
|
}
|
||||||
|
SDL_Delay(16); /* ~60 FPS */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
* 鼠标光标(简化版)
|
||||||
|
* =================================================================== */
|
||||||
|
|
||||||
|
#define DA_CURSOR_SIZE 24
|
||||||
|
|
||||||
|
static const unsigned char da_cursor_shape[24][24] = {
|
||||||
|
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
|
{1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
|
{1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
|
{1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
|
{1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
|
{1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
|
{1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
|
{1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
|
{1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
|
{1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
|
{1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
|
{1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
|
{1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
|
{1,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
|
{0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
|
{0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
|
{0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
|
{0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
|
{0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
|
{0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
|
{0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
|
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
|
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
|
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int mx, my;
|
||||||
|
int btn;
|
||||||
|
} da_cursor;
|
||||||
|
|
||||||
|
static inline void da_cursor_init(da_cursor *c, int fb_w, int fb_h) {
|
||||||
|
c->mx = fb_w / 2;
|
||||||
|
c->my = fb_h / 2;
|
||||||
|
c->btn = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void da_cursor_draw(da_app_context *ac, da_cursor *c, Uint32 color) {
|
||||||
|
for (int r = 0; r < DA_CURSOR_SIZE; r++) {
|
||||||
|
for (int col = 0; col < DA_CURSOR_SIZE; col++) {
|
||||||
|
if (da_cursor_shape[r][col]) {
|
||||||
|
int x = c->mx + col, y = c->my + r;
|
||||||
|
if (x >= 0 && x < ac->width && y >= 0 && y < ac->height) {
|
||||||
|
ac->pixels[y * ac->width + x] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
* PS/2 鼠标模拟(SDL2 事件转换)
|
||||||
|
* =================================================================== */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int has_packet;
|
||||||
|
int dx, dy;
|
||||||
|
int buttons;
|
||||||
|
} da_mouse;
|
||||||
|
|
||||||
|
static inline void da_mouse_init(da_mouse *m) {
|
||||||
|
memset(m, 0, sizeof(*m));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 将 SDL 鼠标事件转换为内部状态 */
|
||||||
|
static inline void da_mouse_update(da_mouse *m, SDL_Event *event) {
|
||||||
|
if (event->type == SDL_MOUSEMOTION) {
|
||||||
|
m->dx += event->motion.xrel;
|
||||||
|
m->dy += event->motion.yrel;
|
||||||
|
m->has_packet = 1;
|
||||||
|
}
|
||||||
|
if (event->type == SDL_MOUSEBUTTONDOWN) {
|
||||||
|
m->buttons |= (1 << (event->button.button -1));
|
||||||
|
m->has_packet = 1;
|
||||||
|
}
|
||||||
|
if (event->type == SDL_MOUSEBUTTONUP) {
|
||||||
|
m->buttons &= ~(1 << (event->button.button - 1));
|
||||||
|
m->has_packet = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
* PS/2 键盘模拟(SDL2 事件转换)
|
||||||
|
* =================================================================== */
|
||||||
|
|
||||||
|
/* SDL scancode 到 Deshab PS/2 scan code 映射 */
|
||||||
|
static inline u8 da_sdl_to_ps2_scan(Uint32 sdl_scancode) {
|
||||||
|
/* 简化映射:只支持基本字母数字键 */
|
||||||
|
switch (sdl_scancode) {
|
||||||
|
case SDLK_ESCAPE: return 0x01;
|
||||||
|
case SDLK_1: return 0x02;
|
||||||
|
case SDLK_2: return 0x03;
|
||||||
|
case SDLK_3: return 0x04;
|
||||||
|
case SDLK_4: return 0x05;
|
||||||
|
case SDLK_5: return 0x06;
|
||||||
|
case SDLK_6: return 0x07;
|
||||||
|
case SDLK_7: return 0x08;
|
||||||
|
case SDLK_8: return 0x09;
|
||||||
|
case SDLK_9: return 0x0A;
|
||||||
|
case SDLK_0: return 0x0B;
|
||||||
|
case SDLK_MINUS: return 0x0C;
|
||||||
|
case SDLK_EQUALS: return 0x0D;
|
||||||
|
case SDLK_BACKSPACE: return 0x0E;
|
||||||
|
case SDLK_TAB: return 0x0F;
|
||||||
|
case SDLK_q: return 0x10;
|
||||||
|
case SDLK_w: return 0x11;
|
||||||
|
case SDLK_e: return 0x12;
|
||||||
|
case SDLK_r: return 0x13;
|
||||||
|
case SDLK_t: return 0x14;
|
||||||
|
case SDLK_y: return 0x15;
|
||||||
|
case SDLK_u: return 0x16;
|
||||||
|
case SDLK_i: return 0x17;
|
||||||
|
case SDLK_o: return 0x18;
|
||||||
|
case SDLK_p: return 0x19;
|
||||||
|
case SDLK_LEFTBRACKET: return 0x1A;
|
||||||
|
case SDLK_RIGHTBRACKET: return 0x1B;
|
||||||
|
case SDLK_RETURN: return 0x1C;
|
||||||
|
case SDLK_a: return 0x1E;
|
||||||
|
case SDLK_s: return 0x1F;
|
||||||
|
case SDLK_d: return 0x20;
|
||||||
|
case SDLK_f: return 0x21;
|
||||||
|
case SDLK_g: return 0x22;
|
||||||
|
case SDLK_h: return 0x23;
|
||||||
|
case SDLK_j: return 0x24;
|
||||||
|
case SDLK_k: return 0x25;
|
||||||
|
case SDLK_l: return 0x26;
|
||||||
|
case SDLK_SEMICOLON: return 0x27;
|
||||||
|
case SDLK_QUOTE: return 0x28;
|
||||||
|
case SDLK_BACKQUOTE: return 0x29;
|
||||||
|
case SDLK_BACKSLASH: return 0x2B;
|
||||||
|
case SDLK_z: return 0x2C;
|
||||||
|
case SDLK_x: return 0x2D;
|
||||||
|
case SDLK_c: return 0x2E;
|
||||||
|
case SDLK_v: return 0x2F;
|
||||||
|
case SDLK_b: return 0x30;
|
||||||
|
case SDLK_n: return 0x31;
|
||||||
|
case SDLK_m: return 0x32;
|
||||||
|
case SDLK_COMMA: return 0x33;
|
||||||
|
case SDLK_PERIOD: return 0x34;
|
||||||
|
case SDLK_SLASH: return 0x35;
|
||||||
|
case SDLK_RSHIFT:
|
||||||
|
case SDLK_LSHIFT: return 0; /* Shift 处理 */
|
||||||
|
case SDLK_SPACE: return 0x39;
|
||||||
|
case SDLK_LEFT: return 0x4B;
|
||||||
|
case SDLK_RIGHT: return 0x4D;
|
||||||
|
case SDLK_UP: return 0x48;
|
||||||
|
case SDLK_DOWN: return 0x50;
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline char da_scan_to_ascii(u8 sc, int shift) {
|
||||||
|
static const char normal[58] = {
|
||||||
|
0, 27, '1','2','3','4','5','6','7','8','9','0','-','=', 8, '\t',
|
||||||
|
'q','w','e','r','t','y','u','i','o','p','[',']','\n',0,
|
||||||
|
'a','s','d','f','g','h','j','k','l',';','\'', '`',0,'\\',
|
||||||
|
'z','x','c','v','b','n','m',',','.','/',0,'*',0,' '
|
||||||
|
};
|
||||||
|
static const char shifted[58] = {
|
||||||
|
0, 27, '!','@','#','$','%','^','&','*','(',')','_','+', 8, '\t',
|
||||||
|
'Q','W','E','R','T','Y','U','I','O','P','{','}','\n',0,
|
||||||
|
'A','S','D','F','G','H','J','K','L',':','"','~',0,'|',
|
||||||
|
'Z','X','C','V','B','N','M','<','>','?',0,'*',0,' '
|
||||||
|
};
|
||||||
|
if (sc >= 58) return 0;
|
||||||
|
return shift ? shifted[sc] : normal[sc];
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* DESHAB_DESKTOP_APP_LINUX_H */
|
||||||
@@ -15,7 +15,7 @@ OBJECTS := $(C_SOURCES:.c=.o)
|
|||||||
all: $(OUT)
|
all: $(OUT)
|
||||||
|
|
||||||
$(OUT): $(OBJECTS) linker.ld
|
$(OUT): $(OBJECTS) linker.ld
|
||||||
@if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools"
|
@mkdir -p "$(ROOT)/SYSTEM/system/deshab64/tools"
|
||||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ OBJECTS := $(C_SOURCES:.c=.o)
|
|||||||
all: $(OUT)
|
all: $(OUT)
|
||||||
|
|
||||||
$(OUT): $(OBJECTS) linker.ld
|
$(OUT): $(OBJECTS) linker.ld
|
||||||
@if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools"
|
@mkdir -p "$(ROOT)/SYSTEM/system/deshab64/tools"
|
||||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
|
|||||||
@@ -254,6 +254,9 @@ static void redraw_all(void) {
|
|||||||
else redraw_list();
|
else redraw_list();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- 返回上级目录(前向声明) ---- */
|
||||||
|
static void go_up(void);
|
||||||
|
|
||||||
/* ---- 打开文件/目录 ---- */
|
/* ---- 打开文件/目录 ---- */
|
||||||
static void go_up(void); /* 前向声明:open_selected 调用 go_up */
|
static void go_up(void); /* 前向声明:open_selected 调用 go_up */
|
||||||
static void open_selected(void) {
|
static void open_selected(void) {
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Makefile.linux — Deshab-OS 应用 Linux 预览编译
|
||||||
|
#
|
||||||
|
# 用法:
|
||||||
|
# make -f Makefile.linux # 编译预览版
|
||||||
|
# make -f Makefile.linux run # 编译并运行预览版
|
||||||
|
# make -f Makefile.linux clean # 清理
|
||||||
|
|
||||||
|
CC = gcc
|
||||||
|
CFLAGS = -Wall -O2 -D DA_LINUX_PREVIEW -I..
|
||||||
|
LDFLAGS = -lSDL2
|
||||||
|
|
||||||
|
SRC = notes_preview.c ../../firstInit/ascii_bitmaps.c
|
||||||
|
TARGET = notes_preview
|
||||||
|
|
||||||
|
all: $(TARGET)
|
||||||
|
|
||||||
|
$(TARGET): $(SRC)
|
||||||
|
$(CC) $(CFLAGS) -o $@ $(SRC) $(LDFLAGS)
|
||||||
|
@echo "✓ 编译完成: $(TARGET)"
|
||||||
|
@echo " 运行: ./$(TARGET)"
|
||||||
|
@echo " 按 ESC 退出"
|
||||||
|
|
||||||
|
run: $(TARGET)
|
||||||
|
./$(TARGET)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(TARGET)
|
||||||
|
@echo "✓ 已清理"
|
||||||
|
|
||||||
|
.PHONY: all run clean
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
# Deshab-OS 应用 Linux 预览功能
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
允许在 Linux 开发机上预览 Deshab-OS 应用的 UI 效果。同一份 UI 绘制代码可以在 Linux 上通过 SDL2 后端预览,在 Deshab-OS 上通过 framebuffer 后端运行。
|
||||||
|
|
||||||
|
## 架构设计
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────── ─┐
|
||||||
|
│ 应用源代码 │
|
||||||
|
│ (使用 da_draw_char, da_fill_rect 等绘制函数) │
|
||||||
|
└──────────────────┬──────────────────────────────────┘
|
||||||
|
│
|
||||||
|
┌──────────┴──────────┐
|
||||||
|
│ #ifdef DA_LINUX_PREVIEW │
|
||||||
|
└──┬──────────────┬────────────┘
|
||||||
|
│ │
|
||||||
|
Linux 后端 Deshab-OS 后端
|
||||||
|
|
||||||
|
(SDL2) (framebuffer) │ │
|
||||||
|
X11/Wayland 窗口物理显示器
|
||||||
|
```
|
||||||
|
|
||||||
|
## 文件结构
|
||||||
|
|
||||||
|
```
|
||||||
|
CODE/tools/notes/
|
||||||
|
├── main.c # Deshab-OS 原生应用入口
|
||||||
|
├── Makefile # Deshab-OS 编译用Makefile
|
||||||
|
├── notes_preview.c # Linux 预览版(包含完整 dsk_entry 实现)
|
||||||
|
├── Makefile.linux # Linux 编译用 Makefile
|
||||||
|
├── desktop_app_linux.h # Linux SDL2 后端
|
||||||
|
└── README_LINUX_PREVIEW.md # 本说明文档
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
### 1. 在 Linux 上编译预览版
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd CODE/tools/notes
|
||||||
|
make -f Makefile.linux
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 运行预览版
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./notes_preview
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 退出预览
|
||||||
|
|
||||||
|
按 `ESC` 键关闭窗口。
|
||||||
|
|
||||||
|
### 4. 清理
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make -f Makefile.linux clean
|
||||||
|
```
|
||||||
|
|
||||||
|
## 开发工作流
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
1. 在 Linux 上编辑代码 ↓
|
||||||
|
2. make -f Makefile.linux 编译预览版
|
||||||
|
↓
|
||||||
|
3. ./notes_preview 查看 UI 效果
|
||||||
|
↓
|
||||||
|
4. 调试布局和渲染
|
||||||
|
↓
|
||||||
|
5. 测试通过后,用原生的 Makefile 编译到 Deshab-OS
|
||||||
|
↓
|
||||||
|
6. 在 Deshab-OS 中运行验证
|
||||||
|
```
|
||||||
|
|
||||||
|
## 支持的 UI 元素
|
||||||
|
|
||||||
|
| 元素 | Linux 预览 | Deshab-OS |
|
||||||
|
|------|-----------|-----------|
|
||||||
|
| 背景填充 | ✅ | ✅ |
|
||||||
|
| 矩形填充 | ✅ | ✅ |
|
||||||
|
| 圆角矩形 | ✅ | ✅ |
|
||||||
|
| ASCII 字符 | ✅ | ✅ |
|
||||||
|
| 字符串 | ✅ | ✅ |
|
||||||
|
| 标题栏 | ✅ | ✅ |
|
||||||
|
| 状态栏 | ✅ | ✅ |
|
||||||
|
| 鼠标 光标 | ✅ | ✅ |
|
||||||
|
|键盘输入 | ✅ | ✅ |
|
||||||
|
| 鼠标移动 | ✅ | ✅ |
|
||||||
|
|
||||||
|
## 扩展到其他应用
|
||||||
|
|
||||||
|
要为其他应用(如 fileman, editor, proedit)添加 Linux 预览支持:
|
||||||
|
|
||||||
|
1. 复制 `notes_preview.c` 作为模板
|
||||||
|
2. 修改 `main()` 中的 UI 逻辑
|
||||||
|
3. 使用相同的 `desktop_app_linux.h
|
||||||
|
|
||||||
|
` 后端
|
||||||
|
|
||||||
|
## 依赖- SDL2 (`libsdl2-dev` on Debian/Ubuntu)
|
||||||
|
- GCC 或 Clang
|
||||||
|
- Make
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **键盘映射**:SDL2 的键盘扫描码需要映射到 Deshab的 PS/2 扫描码
|
||||||
|
2. **鼠标行为**:SDL2 鼠标事件转换为 Deshab 的鼠标数据包格式
|
||||||
|
3. **字体数据**:需要从 `ascii_bitmaps.c` 获取字模数据
|
||||||
|
4. **性能**:Linux 预览版主要用于 UI 调试,不是完整的功能测试
|
||||||
@@ -0,0 +1,489 @@
|
|||||||
|
/* notes_preview.c — Notes 应用的 Linux 预览版本
|
||||||
|
*
|
||||||
|
* 这个文件可以在 Linux 上编译预览 UI。
|
||||||
|
* 使用自定义位图字体渲染字符。
|
||||||
|
*
|
||||||
|
* 编译方式:
|
||||||
|
* gcc -o notes_preview notes_preview.c -I.. -lSDL2
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DA_LINUX_PREVIEW
|
||||||
|
#include "../desktop_app_linux.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
/* 全局状态 */
|
||||||
|
static da_app_context g_ac;
|
||||||
|
static int g_running = 1;
|
||||||
|
|
||||||
|
/* 文本内容 */
|
||||||
|
static char g_lines[512][256];
|
||||||
|
static int g_line_count = 0;
|
||||||
|
static int g_current_line = 0;
|
||||||
|
static int g_current_col = 0;
|
||||||
|
static int g_scroll_top = 0;
|
||||||
|
static int g_visible_rows;
|
||||||
|
|
||||||
|
/* 5x7 位图字体 (ASCII 32-126, 共95 个字符) */
|
||||||
|
static const unsigned char font_5x7[95][5] = {
|
||||||
|
/* 0x20 space */
|
||||||
|
{0x00, 0x00, 0x00, 0x00, 0x00},
|
||||||
|
/* 0x21 ! */
|
||||||
|
{0x00, 0x00, 0x30, 0x30, 0x00},
|
||||||
|
/* 0x22 " */
|
||||||
|
{0x30, 0x30, 0x00, 0x00, 0x00},
|
||||||
|
/* 0x23 # */
|
||||||
|
{0x18, 0x7E, 0x18, 0x7E, 0x18},
|
||||||
|
/* 0x24 $ */
|
||||||
|
{0x0C, 0x36, 0x6C, 0x36, 0x0C},
|
||||||
|
/* 0x25 % */
|
||||||
|
{0x60, 0x9C, 0x18, 0x36, 0x60},
|
||||||
|
/* 0x26 & */
|
||||||
|
{0x30, 0x7E, 0x1C, 0x36, 0x70},
|
||||||
|
/* 0x27 ' */
|
||||||
|
{0x30, 0x30, 0x00, 0x00, 0x00},
|
||||||
|
/* 0x28 ( */
|
||||||
|
{0x0C, 0x18, 0x30, 0x18, 0x0C},
|
||||||
|
/* 0x29 ) */
|
||||||
|
{0x30, 0x18, 0x0C, 0x18, 0x30},
|
||||||
|
/* 0x2A * */
|
||||||
|
{0x00, 0x18, 0x7E, 0x18, 0x00},
|
||||||
|
/* 0x2B + */
|
||||||
|
{0x00, 0x18, 0x7E, 0x18, 0x00},
|
||||||
|
/* 0x2C , */
|
||||||
|
{0x00, 0x00, 0x00, 0x30, 0x30},
|
||||||
|
/* 0x2D - */
|
||||||
|
{0x00, 0x00, 0x7E, 0x00, 0x00},
|
||||||
|
/* 0x2E . */
|
||||||
|
{0x00, 0x00, 0x00, 0x30, 0x30},
|
||||||
|
/* 0x2F / */
|
||||||
|
{0x06, 0x0C, 0x18, 0x30, 0x60},
|
||||||
|
/* 0x30 0 */
|
||||||
|
{0x38, 0x6C, 0x36, 0x6C, 0x38},
|
||||||
|
/* 0x31 1 */
|
||||||
|
{0x18, 0x38, 0x18, 0x18, 0x3C},
|
||||||
|
/* 0x32 2 */
|
||||||
|
{0x78, 0x60, 0x30, 0x18, 0x7C},
|
||||||
|
/* 0x33 3 */
|
||||||
|
{0x78, 0x60, 0x38, 0x00, 0x78},
|
||||||
|
/* 0x34 4 */
|
||||||
|
{0x0C, 0x1C, 0x3C, 0x7E, 0x0C},
|
||||||
|
/* 0x35 5 */
|
||||||
|
{0x7C, 0x60, 0x78, 0x06, 0x7C},
|
||||||
|
/* 0x36 6 */
|
||||||
|
{0x38, 0x60, 0x7C, 0x66, 0x3C},
|
||||||
|
/* 0x37 7 */
|
||||||
|
{0x7C, 0x60, 0x30, 0x30, 0x30},
|
||||||
|
/* 0x38 8 */
|
||||||
|
{0x3C, 0x66, 0x3C, 0x66, 0x3C},
|
||||||
|
/* 0x39 9 */
|
||||||
|
{0x3C, 0x66, 0x3E, 0x60, 0x3C},
|
||||||
|
/* 0x3A : */
|
||||||
|
{0x00, 0x30, 0x00, 0x30, 0x00},
|
||||||
|
/* 0x3B ; */
|
||||||
|
{0x00, 0x30, 0x00, 0x30, 0x30},
|
||||||
|
/* 0x3C < */
|
||||||
|
{0x0C, 0x18, 0x30, 0x18, 0x0C},
|
||||||
|
/* 0x3D = */
|
||||||
|
{0x00, 0x7E, 0x00, 0x7E, 0x00},
|
||||||
|
/* 0x3E > */
|
||||||
|
{0x30, 0x18, 0x0C, 0x18, 0x30},
|
||||||
|
/* 0x3F ? */
|
||||||
|
{0x78, 0x60, 0x30, 0x00, 0x30},
|
||||||
|
/* 0x40 @ */
|
||||||
|
{0x3C, 0x66, 0x7E, 0x06, 0x3C},
|
||||||
|
/* 0x41 A */
|
||||||
|
{0x18, 0x3C, 0x7E, 0x66, 0x66},
|
||||||
|
/* 0x42 B */
|
||||||
|
{0x7C, 0x66, 0x7C, 0x66, 0x7C},
|
||||||
|
/* 0x43 C */
|
||||||
|
{0x3C, 0x66, 0x60, 0x60, 0x3C},
|
||||||
|
/* 0x44 D */
|
||||||
|
{0x78, 0x6C, 0x66, 0x66, 0x78},
|
||||||
|
/* 0x45 E */
|
||||||
|
{0x7E, 0x60, 0x7C, 0x60, 0x7E},
|
||||||
|
/* 0x46 F */
|
||||||
|
{0x7E, 0x60, 0x7C, 0x60, 0x60},
|
||||||
|
/* 0x47 G */
|
||||||
|
{0x3C, 0x66, 0x6C, 0x66, 0x3C},
|
||||||
|
/* 0x48 H */
|
||||||
|
{0x66, 0x66, 0x7E, 0x66, 0x66},
|
||||||
|
/* 0x49 I */
|
||||||
|
{0x3C, 0x18, 0x18, 0x18, 0x3C},
|
||||||
|
/* 0x4A J */
|
||||||
|
{0x7C, 0x06, 0x06, 0x06, 0x3C},
|
||||||
|
/* 0x4B K */
|
||||||
|
{0x66, 0x6C, 0x78, 0x6C, 0x66},
|
||||||
|
/* 0x4C L */
|
||||||
|
{0x60, 0x60, 0x60, 0x60, 0x7E},
|
||||||
|
/* 0x4D M */
|
||||||
|
{0x63, 0x77, 0x7F, 0x6B, 0x63},
|
||||||
|
/* 0x4E N */
|
||||||
|
{0x66, 0x76, 0x7E, 0x6E, 0x66},
|
||||||
|
/* 0x4F O */
|
||||||
|
{0x3C, 0x66, 0x66, 0x66, 0x3C},
|
||||||
|
/* 0x50 P */
|
||||||
|
{0x7C, 0x66, 0x7C, 0x60, 0x60},
|
||||||
|
/* 0x51 Q */
|
||||||
|
{0x3C, 0x66, 0x76, 0x6C, 0x38},
|
||||||
|
/* 0x52 R */
|
||||||
|
{0x7C, 0x66, 0x7C, 0x6C, 0x66},
|
||||||
|
/* 0x53 S */
|
||||||
|
{0x3C, 0x66, 0x3C, 0x06, 0x7C},
|
||||||
|
/* 0x54 T */
|
||||||
|
{0x7E, 0x7E, 0x18, 0x18, 0x18},
|
||||||
|
/* 0x55 U */
|
||||||
|
{0x66, 0x66, 0x66, 0x66, 0x3C},
|
||||||
|
/* 0x56 V */
|
||||||
|
{0x66, 0x66, 0x66, 0x3C, 0x18},
|
||||||
|
/* 0x57 W */
|
||||||
|
{0x63, 0x63, 0x77, 0x7F, 0x36},
|
||||||
|
/* 0x58 X */
|
||||||
|
{0x66, 0x3C, 0x18, 0x3C, 0x66},
|
||||||
|
/* 0x59 Y */
|
||||||
|
{0x66, 0x66, 0x3C, 0x18, 0x18},
|
||||||
|
/* 0x5A Z */
|
||||||
|
{0x7C, 0x66, 0x3C, 0x18, 0x7C},
|
||||||
|
/* 0x5B [ */
|
||||||
|
{0x3C, 0x30, 0x30, 0x30, 0x3C},
|
||||||
|
/* 0x5C backslash */
|
||||||
|
{0x60, 0x30, 0x18, 0x0C, 0x06},
|
||||||
|
/* 0x5D ] */
|
||||||
|
{0x3C, 0x0C, 0x0C, 0x0C, 0x3C},
|
||||||
|
/* 0x5E ^ */
|
||||||
|
{0x18, 0x3C, 0x66, 0x00, 0x00},
|
||||||
|
/* 0x5F _ */
|
||||||
|
{0x00, 0x00, 0x00, 0x00, 0x7E},
|
||||||
|
/* 0x60 ` */
|
||||||
|
{0x30, 0x18, 0x00, 0x00, 0x00},
|
||||||
|
/* 0x61 a */
|
||||||
|
{0x00, 0x00, 0x3C, 0x66, 0x3C},
|
||||||
|
/* 0x62 b */
|
||||||
|
{0x60, 0x60, 0x7C, 0x66, 0x7C},
|
||||||
|
/* 0x63 c */
|
||||||
|
{0x00, 0x00, 0x3C, 0x66, 0x66},
|
||||||
|
/* 0x64 d */
|
||||||
|
{0x06, 0x06, 0x7E, 0x66, 0x7E},
|
||||||
|
/* 0x65 e */
|
||||||
|
{0x00, 0x00, 0x3C, 0x6E, 0x3C},
|
||||||
|
/* 0x66 f */
|
||||||
|
{0x30, 0x7C, 0x06, 0x06, 0x3C},
|
||||||
|
/* 0x67 g */
|
||||||
|
{0x00, 0x3E, 0x66, 0x66, 0x3C},
|
||||||
|
/* 0x68 h */
|
||||||
|
{0x60, 0x60, 0x7C, 0x66, 0x66},
|
||||||
|
/* 0x69 i */
|
||||||
|
{0x00, 0x18, 0x18, 0x00, 0x3C},
|
||||||
|
/* 0x6A j */
|
||||||
|
{0x06, 0x06, 0x06, 0x06, 0x3C},
|
||||||
|
/* 0x6B k */
|
||||||
|
{0x60, 0x6C, 0x78, 0x6C, 0x66},
|
||||||
|
/* 0x6C l */
|
||||||
|
{0x3C, 0x18, 0x18, 0x18, 0x3C},
|
||||||
|
/* 0x6D m */
|
||||||
|
{0x00, 0x00, 0x7E, 0x6E, 0x76},
|
||||||
|
/* 0x6E n */
|
||||||
|
{0x00, 0x00, 0x7C, 0x66, 0x66},
|
||||||
|
/* 0x6F o */
|
||||||
|
{0x00, 0x00, 0x3C, 0x66, 0x3C},
|
||||||
|
/* 0x70 p */
|
||||||
|
{0x00, 0x00, 0x7C, 0x66, 0x7C},
|
||||||
|
/* 0x71 q */
|
||||||
|
{0x00, 0x00, 0x7E, 0x66, 0x3E},
|
||||||
|
/* 0x72 r */
|
||||||
|
{0x00, 0x00, 0x7C, 0x60, 0x60},
|
||||||
|
/* 0x73 s */
|
||||||
|
{0x00, 0x00, 0x3C, 0x60, 0x3C},
|
||||||
|
/* 0x74 t */
|
||||||
|
{0x0C, 0x7E, 0x0C, 0x0C, 0x38},
|
||||||
|
/* 0x75 u */
|
||||||
|
{0x00, 0x00, 0x66, 0x66, 0x3C},
|
||||||
|
/* 0x76 v */
|
||||||
|
{0x00, 0x00, 0x66, 0x3C, 0x18},
|
||||||
|
/* 0x77 w */
|
||||||
|
{0x00, 0x00, 0x67, 0x67, 0x3E},
|
||||||
|
/* 0x78 x */
|
||||||
|
{0x00, 0x00, 0x66, 0x3C, 0x66},
|
||||||
|
/* 0x79 y */
|
||||||
|
{0x00, 0x00, 0x66, 0x66, 0x3C},
|
||||||
|
/* 0x7A z */
|
||||||
|
{0x00, 0x00, 0x7C, 0x7C, 0x78},
|
||||||
|
/* 0x7B { */
|
||||||
|
{0x0C, 0x18, 0x30, 0x18, 0x0C},
|
||||||
|
/* 0x7C | */
|
||||||
|
{0x30, 0x30, 0x30, 0x30, 0x30},
|
||||||
|
/* 0x7D } */
|
||||||
|
{0x30, 0x18, 0x0C, 0x18, 0x30},
|
||||||
|
/* 0x7E ~ */
|
||||||
|
{0x00, 0x00, 0x00, 0x00, 0x00},
|
||||||
|
};
|
||||||
|
|
||||||
|
#define FONT_W 5
|
||||||
|
#define FONT_H 7
|
||||||
|
#define FONT_STEP 9
|
||||||
|
|
||||||
|
/* 颜色常量 */
|
||||||
|
#define CURSOR_COLOR 0xFF0055FFu /* 蓝色光标 */
|
||||||
|
#define BG_COLOR 0xFF1E1E1Eu /* 深色背景 */
|
||||||
|
#define TEXT_COLOR 0xFFD4D4D4u /* 浅色文字 */
|
||||||
|
#define DIM_COLOR 0xFF808080u /* 灰色行号 */
|
||||||
|
#define STATUS_BG 0xFF007ACCu /* 蓝色状态栏 */
|
||||||
|
|
||||||
|
/* 绘制单个字符(使用位图字体) */
|
||||||
|
static void draw_char_bitmap(char ch, int x, int y, u32 color) {
|
||||||
|
if (ch < ' ' || ch > '~') return;
|
||||||
|
int idx = ch - ' ';
|
||||||
|
if (idx < 0 || idx >= 95) return;
|
||||||
|
|
||||||
|
for (int row = 0; row < FONT_H; row++) {
|
||||||
|
unsigned char bits = font_5x7[idx][row];
|
||||||
|
for (int col = 0; col < FONT_W; col++) {
|
||||||
|
if (bits & (1 << (FONT_W - 1 - col))) {
|
||||||
|
da_fill_rect(&g_ac, x + col, y + row, 1, 1, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 绘制文本行 */
|
||||||
|
static void draw_text_line(const char *text, int x, int y, u32 color) {
|
||||||
|
int cx = x;
|
||||||
|
while (*text) {
|
||||||
|
draw_char_bitmap(*text, cx, y, color);
|
||||||
|
cx += FONT_STEP;
|
||||||
|
text++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 重绘所有 */
|
||||||
|
static void redraw_all(void) {
|
||||||
|
da_fill_bg(&g_ac, BG_COLOR);
|
||||||
|
|
||||||
|
int margin_x = 10;
|
||||||
|
int top_y = 44;
|
||||||
|
|
||||||
|
/* 计算可见列数 */
|
||||||
|
int visible_cols = (g_ac.width - 20) / FONT_STEP;
|
||||||
|
if (visible_cols < 10) visible_cols = 10;
|
||||||
|
|
||||||
|
/* 绘制可见行 */
|
||||||
|
for (int line = g_scroll_top; line < g_line_count && line < g_scroll_top + g_visible_rows; line++) {
|
||||||
|
int screen_y = top_y + (line - g_scroll_top) * FONT_STEP;
|
||||||
|
|
||||||
|
/* 行号 */
|
||||||
|
char ln[8];
|
||||||
|
int p = snprintf(ln, sizeof(ln), "%d ", line + 1);
|
||||||
|
|
||||||
|
/* 行号区域背景 */
|
||||||
|
int line_num_width = p * FONT_STEP;
|
||||||
|
da_fill_rect(&g_ac, 0, screen_y - 2, line_num_width + 10, FONT_STEP, 0xFF252526u);
|
||||||
|
|
||||||
|
/* 绘制行号 */
|
||||||
|
draw_text_line(ln, 5, screen_y, DIM_COLOR);
|
||||||
|
|
||||||
|
/* 文本内容 */
|
||||||
|
const char *line_text = g_lines[line];
|
||||||
|
int line_len = (int)strlen(line_text);
|
||||||
|
int draw_len = line_len;
|
||||||
|
if (draw_len > visible_cols) draw_len = visible_cols;
|
||||||
|
|
||||||
|
int text_start_x = margin_x + line_num_width;
|
||||||
|
for (int col = 0; col < draw_len; col++) {
|
||||||
|
draw_char_bitmap(line_text[col], text_start_x + col * FONT_STEP, screen_y, TEXT_COLOR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 光标 */
|
||||||
|
if (g_current_line >= g_scroll_top && g_current_line < g_scroll_top + g_visible_rows) {
|
||||||
|
int screen_y = top_y + (g_current_line - g_scroll_top) * FONT_STEP;
|
||||||
|
|
||||||
|
/* 计算行号宽度 */
|
||||||
|
char tmp_ln[8];
|
||||||
|
snprintf(tmp_ln, sizeof(tmp_ln), "%d ", g_current_line + 1);
|
||||||
|
int line_num_width = (int)strlen(tmp_ln) * FONT_STEP;
|
||||||
|
int text_start_x = margin_x + line_num_width;
|
||||||
|
|
||||||
|
/* 高亮当前光标位置 */
|
||||||
|
int cursor_x = text_start_x + g_current_col * FONT_STEP;
|
||||||
|
da_fill_rect(&g_ac, cursor_x, screen_y, FONT_W, FONT_H, CURSOR_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 状态栏 */
|
||||||
|
char status[128];
|
||||||
|
snprintf(status, sizeof(status), "Line: %d Col: %d Lines: %d ESC=Exit",
|
||||||
|
g_current_line + 1, g_current_col + 1, g_line_count);
|
||||||
|
|
||||||
|
da_fill_rect(&g_ac, 0, g_ac.height - 24, g_ac.width, 24, STATUS_BG);
|
||||||
|
draw_text_line(status, 10, g_ac.height - 22, 0xFFFFFFFFu);
|
||||||
|
|
||||||
|
/* 标题栏 */
|
||||||
|
da_fill_rect(&g_ac, 0, 0, g_ac.width, 36, 0xFF2D2D2Du);
|
||||||
|
draw_text_line("Notes Preview (Deshab-OS)", 10, 6, 0xFFFFFFFFu);
|
||||||
|
da_fill_rect(&g_ac, 0, 36, g_ac.width, 2, 0xFF007ACCu);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void scroll_cursor_into_view(void) {
|
||||||
|
if (g_current_line < g_scroll_top) {
|
||||||
|
g_scroll_top = g_current_line;
|
||||||
|
}
|
||||||
|
if (g_current_line >= g_scroll_top + g_visible_rows) {
|
||||||
|
g_scroll_top = g_current_line - g_visible_rows + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 换行 */
|
||||||
|
static void handle_enter(void) {
|
||||||
|
if (g_current_line >= 511) return;
|
||||||
|
|
||||||
|
int cur_len = (int)strlen(g_lines[g_current_line]);
|
||||||
|
if (g_current_col < cur_len) {
|
||||||
|
int remaining = cur_len - g_current_col;
|
||||||
|
memmove(g_lines[g_current_line] + g_current_col + remaining,
|
||||||
|
g_lines[g_current_line] + g_current_col, remaining + 1);
|
||||||
|
g_lines[g_current_line][g_current_col + remaining] = '\0';
|
||||||
|
} else {
|
||||||
|
g_lines[g_current_line][g_current_col] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移动下一行 */
|
||||||
|
memmove(g_lines[g_current_line + 1], g_lines[g_current_line + 1], 256);
|
||||||
|
g_line_count++;
|
||||||
|
g_current_line++;
|
||||||
|
g_current_col = 0;
|
||||||
|
|
||||||
|
scroll_cursor_into_view();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Backspace删除 */
|
||||||
|
static void handle_backspace(void) {
|
||||||
|
if (g_current_col > 0) {
|
||||||
|
int len = (int)strlen(g_lines[g_current_line]);
|
||||||
|
memmove(g_lines[g_current_line] + g_current_col - 1,
|
||||||
|
g_lines[g_current_line] + g_current_col, len - g_current_col + 1);
|
||||||
|
g_current_col--;
|
||||||
|
} else if (g_current_line > 0) {
|
||||||
|
int prev_len = (int)strlen(g_lines[g_current_line - 1]);
|
||||||
|
int cur_len = (int)strlen(g_lines[g_current_line]);
|
||||||
|
if (prev_len + cur_len < 255) {
|
||||||
|
memcpy(g_lines[g_current_line - 1] + prev_len, g_lines[g_current_line], cur_len + 1);
|
||||||
|
for (int i = g_current_line; i < g_line_count - 1; i++) {
|
||||||
|
memcpy(g_lines[i], g_lines[i+1], 256);
|
||||||
|
}
|
||||||
|
g_line_count--;
|
||||||
|
g_current_line--;
|
||||||
|
g_current_col = prev_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 插入字符 */
|
||||||
|
static void insert_char(char c) {
|
||||||
|
if (c < 32 || c > 126) return;
|
||||||
|
|
||||||
|
int len = (int)strlen(g_lines[g_current_line]);
|
||||||
|
if (len >= 255) return;
|
||||||
|
|
||||||
|
memmove(g_lines[g_current_line] + g_current_col + 1,
|
||||||
|
g_lines[g_current_line] + g_current_col, len - g_current_col + 1);
|
||||||
|
g_lines[g_current_line][g_current_col] = c;
|
||||||
|
g_current_col++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
(void)argc; (void)argv;
|
||||||
|
|
||||||
|
const char *title = "Notes Preview (Deshab-OS) - ESC to exit";
|
||||||
|
int width = 1024;
|
||||||
|
int height = 768;
|
||||||
|
|
||||||
|
if (da_init_linux(&g_ac, title, width, height) != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_visible_rows = (height - 60 - 24) / FONT_STEP;
|
||||||
|
if (g_visible_rows < 5) g_visible_rows = 5;
|
||||||
|
|
||||||
|
/* 初始化空文档 */
|
||||||
|
g_line_count = 1;
|
||||||
|
g_lines[0][0] = '\0';
|
||||||
|
|
||||||
|
redraw_all();
|
||||||
|
da_flush(&g_ac);
|
||||||
|
|
||||||
|
/* 主事件循环 */
|
||||||
|
SDL_Event event;
|
||||||
|
while (g_running) {
|
||||||
|
while (SDL_PollEvent(&event)) {
|
||||||
|
if (event.type == SDL_QUIT) {
|
||||||
|
g_running = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 处理文本输入事件(支持输入法) */
|
||||||
|
if (event.type == SDL_TEXTINPUT) {
|
||||||
|
for (int i = 0; event.text.text[i]; i++) {
|
||||||
|
unsigned char c = (unsigned char)event.text.text[i];
|
||||||
|
if (c >= 32 && c <= 126) {
|
||||||
|
insert_char((char)c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scroll_cursor_into_view();
|
||||||
|
redraw_all();
|
||||||
|
da_flush(&g_ac);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type == SDL_KEYDOWN) {
|
||||||
|
u8 sc = da_sdl_to_ps2_scan(event.key.keysym.sym);
|
||||||
|
|
||||||
|
if (sc == 0x01) { /* Esc */
|
||||||
|
g_running = 0;
|
||||||
|
} else if (sc == 0x1C) { /* Enter */
|
||||||
|
handle_enter();
|
||||||
|
redraw_all();
|
||||||
|
da_flush(&g_ac);
|
||||||
|
} else if (sc == 0x0E) { /* Backspace */
|
||||||
|
handle_backspace();
|
||||||
|
redraw_all();
|
||||||
|
da_flush(&g_ac);
|
||||||
|
} else if (sc == 0x4B) { /* Left */
|
||||||
|
if (g_current_col > 0) g_current_col--;
|
||||||
|
else if (g_current_line > 0) {
|
||||||
|
g_current_line--;
|
||||||
|
g_current_col = (int)strlen(g_lines[g_current_line]);
|
||||||
|
}
|
||||||
|
redraw_all();
|
||||||
|
da_flush(&g_ac);
|
||||||
|
} else if (sc == 0x4D) { /* Right */
|
||||||
|
if (g_current_col < (int)strlen(g_lines[g_current_line])) g_current_col++;
|
||||||
|
else if (g_current_line < g_line_count - 1) {
|
||||||
|
g_current_line++;
|
||||||
|
g_current_col = 0;
|
||||||
|
}
|
||||||
|
redraw_all();
|
||||||
|
da_flush(&g_ac);
|
||||||
|
} else if (sc == 0x48) { /* Up */
|
||||||
|
if (g_current_line > 0) g_current_line--;
|
||||||
|
if (g_current_col > (int)strlen(g_lines[g_current_line])) {
|
||||||
|
g_current_col = (int)strlen(g_lines[g_current_line]);
|
||||||
|
}
|
||||||
|
redraw_all();
|
||||||
|
da_flush(&g_ac);
|
||||||
|
} else if (sc == 0x50) { /* Down */
|
||||||
|
if (g_current_line < g_line_count - 1) g_current_line++;
|
||||||
|
if (g_current_col > (int)strlen(g_lines[g_current_line])) {
|
||||||
|
g_current_col = (int)strlen(g_lines[g_current_line]);
|
||||||
|
}
|
||||||
|
redraw_all();
|
||||||
|
da_flush(&g_ac);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SDL_Delay(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
da_cleanup(&g_ac);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ OBJECTS := $(C_SOURCES:.c=.o)
|
|||||||
all: $(OUT)
|
all: $(OUT)
|
||||||
|
|
||||||
$(OUT): $(OBJECTS) linker.ld
|
$(OUT): $(OBJECTS) linker.ld
|
||||||
@if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools"
|
@mkdir -p "$(ROOT)/SYSTEM/system/deshab64/tools"
|
||||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
|
|||||||
Regular → Executable
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Regular → Executable
BIN
Binary file not shown.
Regular → Executable
BIN
Binary file not shown.
Regular → Executable
BIN
Binary file not shown.
Regular → Executable
BIN
Binary file not shown.
Regular → Executable
BIN
Binary file not shown.
Regular → Executable
BIN
Binary file not shown.
Regular → Executable
BIN
Binary file not shown.
Regular → Executable
BIN
Binary file not shown.
Reference in New Issue
Block a user