579 lines
22 KiB
C
579 lines
22 KiB
C
/* apps_builtin.c — 内置应用 bash/editor/calc + FILES 面板(M0 自 main.c 平移)
|
||
*/
|
||
#include "desktop.h"
|
||
|
||
/* ---- FILES 面板全局状态 ---- */
|
||
fentry g_files[MAX_FILES];
|
||
int g_file_count = 0;
|
||
int g_file_sel = -1;
|
||
int g_files_loaded = 0;
|
||
|
||
/* ============================================================
|
||
* 终端 (Bash) — 面板/窗口共用核心
|
||
* ============================================================ */
|
||
|
||
const char *BASH_PROMPT = "deshab# ";
|
||
|
||
void bash_reset(bash_state *s, int w, int h) {
|
||
for (int i = 0; i < TERM_MAX_CHARS; i++) {
|
||
s->ch[i] = ' '; s->fg[i] = KS_TEXT_PRIMARY;
|
||
}
|
||
s->cols = (w - (int)DU_SPACE_SM * 2) / (int)DU_ASCII_STEP;
|
||
s->rows = (h - (int)DU_SPACE_SM * 2) / (int)DU_ASCII_LINE_H;
|
||
if (s->cols > TERM_MAX_COLS) s->cols = TERM_MAX_COLS;
|
||
if (s->rows > TERM_MAX_ROWS) s->rows = TERM_MAX_ROWS;
|
||
if (s->cols < 10) s->cols = 10;
|
||
if (s->rows < 3) s->rows = 3;
|
||
s->cur_col = 0; s->cur_row = 0;
|
||
s->input_len = 0; s->input_cursor = 0;
|
||
s->prompt_len = kstrlen(BASH_PROMPT);
|
||
/* 初始化脏行标记:所有行标记为脏,确保首次渲染 */
|
||
for (int i = 0; i < TERM_MAX_ROWS; i++) s->dirty_rows[i] = 1;
|
||
s->any_dirty = 1;
|
||
}
|
||
|
||
static void bash_putc(bash_state *s, char c, u32 color) {
|
||
if (c == '\n') {
|
||
s->dirty_rows[s->cur_row] = 1; /* 标记当前行为脏 */
|
||
s->any_dirty = 1;
|
||
s->cur_col = 0; s->cur_row++;
|
||
return;
|
||
}
|
||
if (s->cur_row >= s->rows) {
|
||
/* 滚动:所有行都需要重绘 */
|
||
for (int r = 1; r < s->rows; r++) {
|
||
for (int c2 = 0; c2 < s->cols; c2++) {
|
||
s->ch[(r-1)*TERM_MAX_COLS+c2] = s->ch[r*TERM_MAX_COLS+c2];
|
||
s->fg[(r-1)*TERM_MAX_COLS+c2] = s->fg[r*TERM_MAX_COLS+c2];
|
||
}
|
||
}
|
||
for (int c2 = 0; c2 < s->cols; c2++) {
|
||
s->ch[(s->rows-1)*TERM_MAX_COLS+c2] = ' ';
|
||
}
|
||
s->cur_row = s->rows - 1;
|
||
/* 标记所有行为脏 */
|
||
for (int r = 0; r < s->rows; r++) s->dirty_rows[r] = 1;
|
||
s->any_dirty = 1;
|
||
}
|
||
if (s->cur_col >= s->cols) { s->cur_col = 0; s->cur_row++; }
|
||
int idx = s->cur_row * TERM_MAX_COLS + s->cur_col;
|
||
s->ch[idx] = (u8)c; s->fg[idx] = color;
|
||
s->cur_col++;
|
||
/* 标记当前行为脏 */
|
||
s->dirty_rows[s->cur_row] = 1;
|
||
s->any_dirty = 1;
|
||
}
|
||
|
||
void bash_puts(bash_state *s, const char *str, u32 color) {
|
||
while (*str) bash_putc(s, *str++, color);
|
||
}
|
||
|
||
static void bash_execute(bash_state *s, const char *cmd) {
|
||
while (*cmd == ' ') cmd++;
|
||
bash_puts(s, BASH_PROMPT, KS_ACCENT2);
|
||
bash_puts(s, cmd, KS_TEXT_PRIMARY);
|
||
bash_putc(s, '\n', 0);
|
||
|
||
if (*cmd == 0) return;
|
||
if (cmd[0]=='h'&&cmd[1]=='e'&&cmd[2]=='l'&&cmd[3]=='p') {
|
||
bash_puts(s, " help clear echo version\n", KS_ACCENT2);
|
||
bash_puts(s, " uname about reboot halt\n", KS_ACCENT2);
|
||
bash_puts(s, " pwd whoami id\n", KS_ACCENT2);
|
||
} else if (cmd[0]=='c'&&cmd[1]=='l'&&cmd[2]=='e'&&cmd[3]=='a'&&cmd[4]=='r') {
|
||
for (int i = 0; i < TERM_MAX_CHARS; i++) { s->ch[i] = ' '; }
|
||
s->cur_col = 0; s->cur_row = 0;
|
||
} else if (cmd[0]=='v'&&cmd[1]=='e'&&cmd[2]=='r') {
|
||
bash_puts(s, "Deshab OS v0.1.0 (Winux-Kate shell)\n", KS_ACCENT2);
|
||
} else if (cmd[0]=='u'&&cmd[1]=='n'&&cmd[2]=='a') {
|
||
bash_puts(s, "Deshab\n", KS_TEXT_PRIMARY);
|
||
} else if (cmd[0]=='a'&&cmd[1]=='b'&&cmd[2]=='o'&&cmd[3]=='u'&&cmd[4]=='t') {
|
||
bash_puts(s, "Deshab OS - SAS-R0 Kernel\n", KS_ACCENT);
|
||
bash_puts(s, "Winux-Kate UI Structure\n", KS_ACCENT2);
|
||
} else if (cmd[0]=='e'&&cmd[1]=='c'&&cmd[2]=='h'&&cmd[3]=='o') {
|
||
const char *arg = cmd + 4;
|
||
while (*arg == ' ') arg++;
|
||
bash_puts(s, arg, KS_TEXT_PRIMARY);
|
||
bash_putc(s, '\n', 0);
|
||
} else if (cmd[0]=='p'&&cmd[1]=='w'&&cmd[2]=='d') {
|
||
bash_puts(s, "/\n", KS_TEXT_PRIMARY);
|
||
} else if (cmd[0]=='w'&&cmd[1]=='h'&&cmd[2]=='o') {
|
||
bash_puts(s, "root\n", KS_TEXT_PRIMARY);
|
||
} else if (cmd[0]=='i'&&cmd[1]=='d') {
|
||
bash_puts(s, "uid=0(root) gid=0(root)\n", KS_TEXT_PRIMARY);
|
||
} else if (cmd[0]=='r'&&cmd[1]=='e'&&cmd[2]=='b') {
|
||
bash_puts(s, "Rebooting...\n", KS_ACCENT2);
|
||
outb(0x64, 0xFE);
|
||
for(;;) __asm__("hlt");
|
||
} else if (cmd[0]=='h'&&cmd[1]=='a'&&cmd[2]=='l'&&cmd[3]=='t') {
|
||
bash_puts(s, "Halted.\n", KS_ACCENT2);
|
||
for(;;) __asm__("hlt");
|
||
} else {
|
||
bash_puts(s, "unknown: ", KS_DANGER);
|
||
bash_puts(s, cmd, KS_DANGER);
|
||
bash_putc(s, '\n', 0);
|
||
}
|
||
}
|
||
|
||
void bash_key(bash_state *s, u8 sc, int shift) {
|
||
if (sc == 0x1C) {
|
||
s->input_buf[s->input_len] = 0;
|
||
bash_execute(s, s->input_buf);
|
||
s->input_len = 0; s->input_cursor = 0;
|
||
} else if (sc == 0x0E) {
|
||
if (s->input_cursor > 0) {
|
||
for (int i = s->input_cursor-1; i < s->input_len-1; i++)
|
||
s->input_buf[i] = s->input_buf[i+1];
|
||
s->input_len--; s->input_cursor--;
|
||
}
|
||
} else {
|
||
char c = scan_to_ascii(sc, shift);
|
||
if (c && c >= 32 && c <= 126 && s->input_len < 255) {
|
||
for (int i = s->input_len; i > s->input_cursor; i--)
|
||
s->input_buf[i] = s->input_buf[i-1];
|
||
s->input_buf[s->input_cursor++] = c;
|
||
s->input_len++;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 绘制终端到指定矩形区域(面板或窗口客户区)
|
||
* 性能优化:增量渲染,只绘制脏行 */
|
||
void bash_draw_to(bash_state *s, int cx, int cy, int cw, int ch) {
|
||
/* 快速退出:终端内容未修改,跳过绘制 */
|
||
if (!s->any_dirty) return;
|
||
|
||
du_fill_rect(&g_fb, cx, cy, cw, ch, KS_BG_PRIMARY);
|
||
|
||
for (int r = 0; r < s->rows; r++) {
|
||
/* 增量渲染:只绘制脏行 */
|
||
if (!s->dirty_rows[r]) continue;
|
||
|
||
for (int c = 0; c < s->cols; c++) {
|
||
int idx = r * TERM_MAX_COLS + c;
|
||
if (s->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, s->ch[idx], px, py, s->fg[idx], KS_BG_PRIMARY);
|
||
}
|
||
|
||
/* 清除脏行标记 */
|
||
s->dirty_rows[r] = 0;
|
||
}
|
||
|
||
int input_y = cy + (int)DU_SPACE_SM + s->cur_row * (int)DU_ASCII_LINE_H;
|
||
if (input_y + (int)DU_ASCII_CELL_H <= cy + ch) {
|
||
du_draw_string(&g_fb, BASH_PROMPT,
|
||
cx + (int)DU_SPACE_SM, input_y,
|
||
KS_ACCENT2, KS_BG_PRIMARY, DU_ASCII_STEP);
|
||
du_draw_string(&g_fb, s->input_buf,
|
||
cx + (int)DU_SPACE_SM + s->prompt_len * (int)DU_ASCII_STEP,
|
||
input_y,
|
||
KS_TEXT_PRIMARY, KS_BG_PRIMARY, DU_ASCII_STEP);
|
||
int cursor_x = cx + (int)DU_SPACE_SM + (s->prompt_len + s->input_cursor) * (int)DU_ASCII_STEP;
|
||
du_fill_rect(&g_fb, cursor_x, input_y + (int)DU_ASCII_CELL_H - 3,
|
||
(int)DU_ASCII_CELL_W, 2, KS_ACCENT);
|
||
}
|
||
|
||
/* 清除全局脏标记 */
|
||
s->any_dirty = 0;
|
||
}
|
||
|
||
/* ---- 窗口适配 ---- */
|
||
__attribute__((unused))
|
||
static void *bash_on_create(app_ctx *ctx) {
|
||
bash_state *s = (bash_state *)ADDR_TERMWIN;
|
||
bash_reset(s, ctx->client_w, ctx->client_h);
|
||
bash_puts(s, "Deshab Bash v0.1\nType help for commands\n\n", KS_TEXT_PRIMARY);
|
||
return s;
|
||
}
|
||
__attribute__((unused))
|
||
static void bash_on_event(void *state, app_ctx *ctx, desktop_event *ev) {
|
||
if (ev->type != EV_KEY_DOWN) return;
|
||
bash_key((bash_state *)state, ev->scancode, ev->shift);
|
||
ctx->win->dirty = 1;
|
||
}
|
||
__attribute__((unused))
|
||
static void bash_on_draw(void *state, app_ctx *ctx) {
|
||
bash_draw_to((bash_state *)state, ctx->client_x, ctx->client_y,
|
||
ctx->client_w, ctx->client_h);
|
||
}
|
||
__attribute__((unused))
|
||
static void bash_on_destroy(void *state) { (void)state; }
|
||
|
||
/* ============================================================
|
||
* 文本编辑器 (Editor) — 面板/IDE/窗口共用核心
|
||
* ============================================================ */
|
||
|
||
static void editor_recalc_cursor(editor_state *s) {
|
||
s->cursor_line = 0; s->cursor_col = 0;
|
||
for (int i = 0; i < s->cursor_pos && i < s->text_len; i++) {
|
||
if (s->text[i] == '\n') { s->cursor_line++; s->cursor_col = 0; }
|
||
else s->cursor_col++;
|
||
}
|
||
}
|
||
|
||
static int editor_line_start(editor_state *s, int line) {
|
||
int l = 0;
|
||
for (int i = 0; i < s->text_len; i++) {
|
||
if (l == line) return i;
|
||
if (s->text[i] == '\n') l++;
|
||
}
|
||
return s->text_len;
|
||
}
|
||
|
||
void editor_reset(editor_state *s, const char *sample) {
|
||
s->text_len = 0; s->cursor_pos = 0;
|
||
s->scroll_y = 0; s->modified = 0;
|
||
s->has_file = 0;
|
||
s->disp[0] = 0;
|
||
while (*sample && s->text_len < EDITOR_BUF_SIZE - 1) {
|
||
s->text[s->text_len++] = *sample++;
|
||
}
|
||
s->cursor_pos = s->text_len;
|
||
editor_recalc_cursor(s);
|
||
}
|
||
|
||
void editor_key(editor_state *s, u8 sc, int shift) {
|
||
if (sc == 0x1C) {
|
||
if (s->text_len < EDITOR_BUF_SIZE - 1) {
|
||
for (int i = s->text_len; i > s->cursor_pos; i--)
|
||
s->text[i] = s->text[i-1];
|
||
s->text[s->cursor_pos] = '\n';
|
||
s->text_len++; s->cursor_pos++;
|
||
s->modified = 1;
|
||
}
|
||
} else if (sc == 0x0E) {
|
||
if (s->cursor_pos > 0) {
|
||
for (int i = s->cursor_pos - 1; i < s->text_len - 1; i++)
|
||
s->text[i] = s->text[i+1];
|
||
s->text_len--; s->cursor_pos--;
|
||
s->modified = 1;
|
||
}
|
||
} else if (sc == 0x48) {
|
||
if (s->cursor_line > 0) {
|
||
int prev_start = editor_line_start(s, s->cursor_line - 1);
|
||
int col = s->cursor_col;
|
||
s->cursor_pos = prev_start;
|
||
for (int i = 0; i < col && s->cursor_pos < s->text_len && s->text[s->cursor_pos] != '\n'; i++)
|
||
s->cursor_pos++;
|
||
}
|
||
} else if (sc == 0x50) {
|
||
int next_start = editor_line_start(s, s->cursor_line + 1);
|
||
if (next_start < s->text_len || s->cursor_line < 999) {
|
||
int col = s->cursor_col;
|
||
s->cursor_pos = next_start;
|
||
for (int i = 0; i < col && s->cursor_pos < s->text_len && s->text[s->cursor_pos] != '\n'; i++)
|
||
s->cursor_pos++;
|
||
}
|
||
} else if (sc == 0x4B) {
|
||
if (s->cursor_pos > 0) s->cursor_pos--;
|
||
} else if (sc == 0x4D) {
|
||
if (s->cursor_pos < s->text_len) s->cursor_pos++;
|
||
} else if (sc == 0x47) {
|
||
s->cursor_pos = editor_line_start(s, s->cursor_line);
|
||
} else if (sc == 0x4F) {
|
||
s->cursor_pos = editor_line_start(s, s->cursor_line);
|
||
while (s->cursor_pos < s->text_len && s->text[s->cursor_pos] != '\n')
|
||
s->cursor_pos++;
|
||
} else {
|
||
char c = scan_to_ascii(sc, shift);
|
||
if (c && c >= 32 && c <= 126 && s->text_len < EDITOR_BUF_SIZE - 1) {
|
||
for (int i = s->text_len; i > s->cursor_pos; i--)
|
||
s->text[i] = s->text[i-1];
|
||
s->text[s->cursor_pos] = c;
|
||
s->text_len++; s->cursor_pos++;
|
||
s->modified = 1;
|
||
}
|
||
}
|
||
editor_recalc_cursor(s);
|
||
}
|
||
|
||
/* 绘制编辑器到指定矩形区域 */
|
||
void editor_draw_to(editor_state *s, int cx, int cy, int cw, int ch) {
|
||
int line_num_w = 5 * (int)DU_ASCII_STEP + (int)DU_SPACE_SM;
|
||
int text_area_x = cx + line_num_w;
|
||
int text_area_w = cw - line_num_w;
|
||
int visible_rows = (ch - 20) / (int)DU_ASCII_LINE_H;
|
||
int visible_cols = text_area_w / (int)DU_ASCII_STEP;
|
||
if (visible_rows < 1) visible_rows = 1;
|
||
if (visible_cols < 4) visible_cols = 4;
|
||
|
||
du_fill_rect(&g_fb, cx, cy, cw, ch, KS_BG_SECONDARY);
|
||
|
||
if (s->cursor_line < s->scroll_y) s->scroll_y = s->cursor_line;
|
||
if (s->cursor_line >= s->scroll_y + visible_rows) s->scroll_y = s->cursor_line - visible_rows + 1;
|
||
|
||
int line = 0;
|
||
int pos = 0;
|
||
for (int i = 0; i < s->text_len || line <= s->cursor_line; ) {
|
||
if (line >= s->scroll_y + visible_rows) break;
|
||
|
||
if (line >= s->scroll_y) {
|
||
int screen_y = cy + (line - s->scroll_y) * (int)DU_ASCII_LINE_H;
|
||
|
||
char ln[8];
|
||
ln[0] = '0' + ((line+1)/100)%10;
|
||
ln[1] = '0' + ((line+1)/10)%10;
|
||
ln[2] = '0' + (line+1)%10;
|
||
ln[3] = 0;
|
||
du_draw_string(&g_fb, ln, cx + 2, screen_y,
|
||
KS_TEXT_DIM, KS_BG_SECONDARY, DU_ASCII_STEP);
|
||
|
||
du_fill_rect(&g_fb, cx + line_num_w - 2, screen_y, 1,
|
||
(int)DU_ASCII_CELL_H, KS_BORDER_DIM);
|
||
|
||
int col = 0;
|
||
while (pos < s->text_len && s->text[pos] != '\n' && col < visible_cols) {
|
||
du_draw_char(&g_fb, s->text[pos],
|
||
text_area_x + col * (int)DU_ASCII_STEP,
|
||
screen_y,
|
||
KS_TEXT_PRIMARY, KS_BG_SECONDARY);
|
||
col++; pos++;
|
||
}
|
||
if (pos < s->text_len && s->text[pos] == '\n') pos++;
|
||
} else {
|
||
while (pos < s->text_len && s->text[pos] != '\n') pos++;
|
||
if (pos < s->text_len) pos++;
|
||
}
|
||
line++;
|
||
if (pos >= s->text_len && line > s->cursor_line) break;
|
||
}
|
||
|
||
/* 光标(青色竖条) */
|
||
if (s->cursor_line >= s->scroll_y && s->cursor_line < s->scroll_y + visible_rows) {
|
||
int cursor_y = cy + (s->cursor_line - s->scroll_y) * (int)DU_ASCII_LINE_H;
|
||
int cursor_x = text_area_x + s->cursor_col * (int)DU_ASCII_STEP;
|
||
du_fill_rect(&g_fb, cursor_x, cursor_y, 2, (int)DU_ASCII_CELL_H, KS_ACCENT);
|
||
}
|
||
|
||
/* 底部迷你状态栏 */
|
||
int status_y = cy + ch - 20;
|
||
du_fill_rect(&g_fb, cx, status_y, cw, 20, KS_BG_TERTIARY);
|
||
du_divider_h(&g_fb, cx, status_y, cw, KS_BORDER);
|
||
char status[64];
|
||
int sp = 0;
|
||
const char *p1 = "L:"; while(*p1) status[sp++] = *p1++;
|
||
status[sp++] = '0' + (s->cursor_line+1)/10%10;
|
||
status[sp++] = '0' + (s->cursor_line+1)%10;
|
||
const char *p2 = " C:"; while(*p2) status[sp++] = *p2++;
|
||
status[sp++] = '0' + (s->cursor_col+1)/10%10;
|
||
status[sp++] = '0' + (s->cursor_col+1)%10;
|
||
if (s->modified) { const char *m = " *"; while(*m) status[sp++] = *m++; }
|
||
if (s->has_file) {
|
||
const char *m = " "; while(*m) status[sp++] = *m++;
|
||
for (int i = 0; s->disp[i] && sp < 60; i++) status[sp++] = s->disp[i];
|
||
}
|
||
status[sp] = 0;
|
||
du_draw_string(&g_fb, status, cx + 4, status_y + 1,
|
||
KS_TEXT_DIM, KS_BG_TERTIARY, DU_ASCII_STEP);
|
||
}
|
||
|
||
/* ---- 窗口适配 ---- */
|
||
void *editor_on_create(app_ctx *ctx) {
|
||
editor_state *s = (editor_state *)ADDR_EDWIN;
|
||
editor_reset(s, "# Welcome to Deshab Editor\n#\n\nStart typing here...\n");
|
||
for (int i = 0; i < 47; i++) ctx->win->title[i] = 0;
|
||
const char *t = "Editor - untitled";
|
||
for (int i = 0; t[i] && i < 47; i++) ctx->win->title[i] = t[i];
|
||
return s;
|
||
}
|
||
void editor_on_event(void *state, app_ctx *ctx, desktop_event *ev) {
|
||
if (ev->type != EV_KEY_DOWN) return;
|
||
editor_key((editor_state *)state, ev->scancode, ev->shift);
|
||
ctx->win->dirty = 1;
|
||
}
|
||
void editor_on_draw(void *state, app_ctx *ctx) {
|
||
editor_draw_to((editor_state *)state, ctx->client_x, ctx->client_y,
|
||
ctx->client_w, ctx->client_h);
|
||
}
|
||
void editor_on_destroy(void *state) { (void)state; }
|
||
|
||
/* ============================================================
|
||
* 计算器 (Calculator)
|
||
* ============================================================ */
|
||
|
||
static void calc_update_display(calc_state *s) {
|
||
s->display_len = 0;
|
||
i64 val = s->current;
|
||
if (val < 0) { s->display[s->display_len++] = '-'; val = -val; }
|
||
if (val == 0) { s->display[s->display_len++] = '0'; }
|
||
else {
|
||
char tmp[20]; int tl = 0;
|
||
while (val > 0) { tmp[tl++] = '0' + (int)(val % 10); val /= 10; }
|
||
for (int i = tl-1; i >= 0; i--) s->display[s->display_len++] = tmp[i];
|
||
}
|
||
s->display[s->display_len] = 0;
|
||
}
|
||
|
||
void *calc_on_create(app_ctx *ctx) {
|
||
calc_state *s = (calc_state *)ADDR_CALC;
|
||
s->accumulator = 0; s->current = 0;
|
||
s->op = 0; s->new_number = 1;
|
||
calc_update_display(s);
|
||
const char *t = "Calculator";
|
||
for (int i = 0; t[i] && i < 47; i++) ctx->win->title[i] = t[i];
|
||
return s;
|
||
}
|
||
|
||
void calc_on_event(void *state, app_ctx *ctx, desktop_event *ev) {
|
||
calc_state *s = (calc_state *)state;
|
||
if (ev->type != EV_KEY_DOWN) return;
|
||
char c = scan_to_ascii(ev->scancode, ev->shift);
|
||
if (c >= '0' && c <= '9') {
|
||
if (s->new_number) { s->current = 0; s->new_number = 0; }
|
||
s->current = s->current * 10 + (c - '0');
|
||
} else if (c == '+') { s->accumulator = s->current; s->op = 1; s->new_number = 1; }
|
||
else if (c == '-') { s->accumulator = s->current; s->op = 2; s->new_number = 1; }
|
||
else if (c == '*') { s->accumulator = s->current; s->op = 3; s->new_number = 1; }
|
||
else if (c == '/') { s->accumulator = s->current; s->op = 4; s->new_number = 1; }
|
||
else if (c == '=' || c == '\n') {
|
||
switch (s->op) {
|
||
case 1: s->current = s->accumulator + s->current; break;
|
||
case 2: s->current = s->accumulator - s->current; break;
|
||
case 3: s->current = s->accumulator * s->current; break;
|
||
case 4: s->current = (s->current != 0) ? s->accumulator / s->current : 0; break;
|
||
}
|
||
s->op = 0; s->new_number = 1;
|
||
} else if (c == 'c' || c == 'C') {
|
||
s->current = 0; s->accumulator = 0; s->op = 0; s->new_number = 1;
|
||
}
|
||
calc_update_display(s);
|
||
ctx->win->dirty = 1;
|
||
}
|
||
|
||
void calc_on_draw(void *state, app_ctx *ctx) {
|
||
calc_state *s = (calc_state *)state;
|
||
int cx = ctx->client_x;
|
||
int cy = ctx->client_y;
|
||
int cw = ctx->client_w;
|
||
int ch = ctx->client_h;
|
||
|
||
du_fill_rect(&g_fb, cx, cy, cw, ch, KS_BG_SECONDARY);
|
||
|
||
int disp_h = 40;
|
||
du_fill_rounded_rect(&g_fb, cx + 8, cy + 8, cw - 16, disp_h,
|
||
KS_BG_PRIMARY, DU_RADIUS_MD);
|
||
du_rect_outline(&g_fb, cx + 8, cy + 8, cw - 16, disp_h,
|
||
KS_ACCENT, DU_RADIUS_MD);
|
||
int text_x = cx + cw - 16 - s->display_len * (int)DU_ASCII_STEP - 8;
|
||
du_draw_string(&g_fb, s->display, text_x, cy + 16,
|
||
KS_ACCENT, KS_BG_PRIMARY, DU_ASCII_STEP);
|
||
|
||
const char *btn_labels[] = {
|
||
"C", "+/-", "%", "/",
|
||
"7", "8", "9", "*",
|
||
"4", "5", "6", "-",
|
||
"1", "2", "3", "+",
|
||
"0", ".", "=", 0
|
||
};
|
||
int btn_w = (cw - 16 - 12) / 4;
|
||
int btn_h = 32;
|
||
int btn_y = cy + 8 + disp_h + 8;
|
||
for (int i = 0; i < 19; i++) {
|
||
int row = i / 4, col = i % 4;
|
||
int bx = cx + 8 + col * (btn_w + 3);
|
||
int by = btn_y + row * (btn_h + 3);
|
||
if (by + btn_h > cy + ch - 4) break;
|
||
u32 bg = (i < 4) ? KS_ACCENT_DIM :
|
||
(i % 4 == 3) ? KS_ACCENT :
|
||
(i == 18) ? KS_ACCENT2 :
|
||
KS_BG_TERTIARY;
|
||
u32 fg = ((i % 4 == 3) || (i == 18)) ? KS_TEXT_INVERT : KS_TEXT_PRIMARY;
|
||
du_fill_rounded_rect(&g_fb, bx, by, btn_w, btn_h, bg, DU_RADIUS_SM);
|
||
du_rect_outline(&g_fb, bx, by, btn_w, btn_h, KS_BORDER, DU_RADIUS_SM);
|
||
du_draw_string(&g_fb, btn_labels[i],
|
||
bx + (btn_w - 1*(int)DU_ASCII_STEP)/2,
|
||
by + (btn_h - (int)DU_ASCII_CELL_H)/2,
|
||
fg, bg, DU_ASCII_STEP);
|
||
}
|
||
}
|
||
|
||
void calc_on_destroy(void *state) { (void)state; }
|
||
|
||
/* ============================================================
|
||
* FILES 面板(FAT32 真实读写)
|
||
* ============================================================ */
|
||
|
||
static int files_list_cb(const char *name, u32 size, u8 attr, void *ud) {
|
||
(void)ud;
|
||
if (attr & 0x10) return 0; /* 跳过子目录 */
|
||
if (g_file_count >= MAX_FILES) return 1; /* 停止 */
|
||
|
||
/* 只显示 user 文件:跳过 .ELF 可执行文件和系统模块 */
|
||
int nl = kstrlen(name);
|
||
if (nl >= 4 && name[nl-4] == '.' && name[nl-3] == 'E' &&
|
||
name[nl-2] == 'L' && name[nl-1] == 'F') return 0;
|
||
/* FIRSTINI* (FirstInit.elf,Name83=FIRSTINIT → 显示名 FIRSTINI.T) */
|
||
if (nl >= 8 && name[0] == 'F' && name[1] == 'I' && name[2] == 'R' &&
|
||
name[3] == 'S' && name[4] == 'T' && name[5] == 'I' && name[6] == 'N' &&
|
||
name[7] == 'I') return 0;
|
||
|
||
fentry *f = &g_files[g_file_count];
|
||
f->size = size;
|
||
kstrcpy(f->disp, name, 13);
|
||
/* disp (NAME.EXT) → name11 (NAME EXT) */
|
||
for (int i = 0; i < 11; i++) f->name11[i] = ' ';
|
||
int i = 0, o = 0;
|
||
while (name[i] && name[i] != '.' && o < 8) f->name11[o++] = name[i++];
|
||
while (name[i] && name[i] != '.') i++;
|
||
if (name[i] == '.') {
|
||
i++;
|
||
o = 8;
|
||
while (name[i] && o < 11) f->name11[o++] = name[i++];
|
||
}
|
||
g_file_count++;
|
||
return 0;
|
||
}
|
||
|
||
void files_refresh(void) {
|
||
g_file_count = 0;
|
||
g_file_sel = -1;
|
||
if (!g_block_read) { g_files_loaded = 0; return; }
|
||
if (f32_list_root_w(files_list_cb, 0) == 0) g_files_loaded = 1;
|
||
else g_files_loaded = 0;
|
||
}
|
||
|
||
/* 双击打开文件 → 载入 DASHBOARD EDITOR */
|
||
void files_open(int idx) {
|
||
if (idx < 0 || idx >= g_file_count) return;
|
||
editor_state *ed = (editor_state *)ADDR_EDDASH;
|
||
fentry *f = &g_files[idx];
|
||
|
||
u8 *data = 0; u32 size = 0;
|
||
if (f32_read_root_file_w(f->name11, &data, &size) != 0) {
|
||
slog("file open failed");
|
||
return;
|
||
}
|
||
ed->text_len = 0;
|
||
u32 cap = EDITOR_BUF_SIZE - 1;
|
||
if (size > cap) size = cap;
|
||
for (u32 i = 0; i < size; i++) {
|
||
char c = (char)data[i];
|
||
if ((u8)c >= 32 || c == '\n' || c == '\t')
|
||
ed->text[ed->text_len++] = c;
|
||
}
|
||
for (int i = 0; i < 11; i++) ed->name11[i] = f->name11[i];
|
||
kstrcpy(ed->disp, f->disp, 13);
|
||
ed->has_file = 1;
|
||
ed->modified = 0;
|
||
ed->scroll_y = 0;
|
||
ed->cursor_pos = 0;
|
||
editor_recalc_cursor(ed);
|
||
g_dash_focus = 2;
|
||
slog("file loaded into editor");
|
||
}
|
||
|
||
/* EDITOR SAVE 回写磁盘 */
|
||
void editor_save(void) {
|
||
editor_state *ed = (editor_state *)ADDR_EDDASH;
|
||
if (!ed->has_file) return;
|
||
if (f32_write_root_file_w(ed->name11, (const u8 *)ed->text, (u32)ed->text_len) == 0) {
|
||
ed->modified = 0;
|
||
slog("file saved");
|
||
} else {
|
||
slog("file save failed");
|
||
}
|
||
}
|