343 lines
12 KiB
C
343 lines
12 KiB
C
/* Deshab Editor — 全屏文本编辑器
|
|
* PS/2 键盘输入,行号 + 光标 + 垂直滚动。
|
|
* Ctrl+O 打开文件(输入 8.3 名,从 FAT32 根目录读取)
|
|
* Ctrl+S 保存文件(输入 8.3 名,写入 FAT32 根目录)
|
|
* Esc 退出。
|
|
*/
|
|
|
|
#include "../../firstInit/ascii_bitmaps.c"
|
|
#include "../desktop_app.h"
|
|
#include "../fat32_io.h"
|
|
|
|
#define CHAR_STEP 12
|
|
#define CHAR_H 18
|
|
#define EDITOR_BUF_SIZE 65536
|
|
#define INPUT_BUF_SIZE 16
|
|
|
|
static da_app_context g_ac;
|
|
|
|
static char text_buf[EDITOR_BUF_SIZE];
|
|
static int text_len = 0;
|
|
static int cursor_pos = 0;
|
|
static int scroll_y = 0;
|
|
static int cursor_line = 0;
|
|
static int cursor_col = 0;
|
|
static int modified = 0;
|
|
static char filename[64] = "untitled";
|
|
static int ctrl_down = 0;
|
|
|
|
/* 输入模式:Ctrl+O/Ctrl+S 时进入,在状态栏输入文件名 */
|
|
static int input_mode = 0; /* 0=编辑, 1=open, 2=save */
|
|
static char input_buf[INPUT_BUF_SIZE];
|
|
static int input_len = 0;
|
|
|
|
static int line_start(int line) {
|
|
int l = 0;
|
|
for (int i = 0; i < text_len; i++) {
|
|
if (l == line) return i;
|
|
if (text_buf[i] == '\n') l++;
|
|
}
|
|
return text_len;
|
|
}
|
|
|
|
static void recalc_cursor(void) {
|
|
cursor_line = 0; cursor_col = 0;
|
|
for (int i = 0; i < cursor_pos && i < text_len; i++) {
|
|
if (text_buf[i] == '\n') { cursor_line++; cursor_col = 0; }
|
|
else cursor_col++;
|
|
}
|
|
}
|
|
|
|
static void init_default_text(void) {
|
|
const char *sample = "# Deshab Editor\n#\n\nStart typing here...\n\nCtrl+O: Open Ctrl+S: Save Esc: Exit\n";
|
|
while (*sample && text_len < EDITOR_BUF_SIZE - 1)
|
|
text_buf[text_len++] = *sample++;
|
|
cursor_pos = text_len;
|
|
recalc_cursor();
|
|
}
|
|
|
|
/* ---- 文件打开/保存 ---- */
|
|
static void do_open(void) {
|
|
if (input_len == 0) return;
|
|
char name11[11];
|
|
if (f32_name_to_83(input_buf, name11) != 0) {
|
|
da_slog("editor", "open: bad name");
|
|
return;
|
|
}
|
|
u8 *data = 0; u32 size = 0;
|
|
if (f32_read_root_file(name11, &data, &size) != 0) {
|
|
da_slog("editor", "open: read failed");
|
|
return;
|
|
}
|
|
if (size > EDITOR_BUF_SIZE) size = EDITOR_BUF_SIZE;
|
|
text_len = (int)size;
|
|
for (int i = 0; i < text_len; i++) text_buf[i] = (char)data[i];
|
|
cursor_pos = 0;
|
|
modified = 0;
|
|
/* 更新 filename 显示 */
|
|
int p = 0;
|
|
for (int i = 0; i < input_len && p < 60; i++) filename[p++] = input_buf[i];
|
|
filename[p] = 0;
|
|
recalc_cursor();
|
|
da_slog("editor", "open: ok");
|
|
}
|
|
|
|
static void do_save(void) {
|
|
if (input_len == 0) return;
|
|
char name11[11];
|
|
if (f32_name_to_83(input_buf, name11) != 0) {
|
|
da_slog("editor", "save: bad name");
|
|
return;
|
|
}
|
|
if (f32_write_root_file(name11, (const u8 *)text_buf, (u32)text_len) != 0) {
|
|
da_slog("editor", "save: write failed");
|
|
return;
|
|
}
|
|
modified = 0;
|
|
int p = 0;
|
|
for (int i = 0; i < input_len && p < 60; i++) filename[p++] = input_buf[i];
|
|
filename[p] = 0;
|
|
da_slog("editor", "save: ok");
|
|
}
|
|
|
|
static void enter_input_mode(int mode) {
|
|
input_mode = mode;
|
|
input_len = 0;
|
|
input_buf[0] = 0;
|
|
}
|
|
|
|
static void commit_input(void) {
|
|
int mode = input_mode;
|
|
input_mode = 0;
|
|
if (mode == 1) do_open();
|
|
else if (mode == 2) do_save();
|
|
}
|
|
|
|
static void redraw_all(void) {
|
|
da_fill_bg(&g_ac, DA_BG_SECONDARY);
|
|
char title[80];
|
|
int p = 0;
|
|
const char *t1 = "Editor - ";
|
|
while (*t1) title[p++] = *t1++;
|
|
for (int i = 0; filename[i] && p < 70; i++) title[p++] = filename[i];
|
|
if (modified) { title[p++] = ' '; title[p++] = '*'; }
|
|
title[p] = 0;
|
|
da_draw_titlebar(&g_ac, title, (i64)g_ac.fb_w);
|
|
|
|
int margin_x = 8;
|
|
int top_y = DA_TITLEBAR_H + 4;
|
|
int line_num_w = 5 * CHAR_STEP + 8;
|
|
int text_x = margin_x + line_num_w;
|
|
int visible_rows = ((int)g_ac.fb_h - top_y - DA_STATUSBAR_H - 8) / CHAR_H;
|
|
int visible_cols = ((int)g_ac.fb_w - text_x - 8) / CHAR_STEP;
|
|
|
|
/* 自动滚动 */
|
|
if (cursor_line < scroll_y) scroll_y = cursor_line;
|
|
if (cursor_line >= scroll_y + visible_rows) scroll_y = cursor_line - visible_rows + 1;
|
|
|
|
/* 渲染可见行 */
|
|
int line = 0, pos = 0;
|
|
for (; pos <= text_len && line < scroll_y + visible_rows; ) {
|
|
if (line >= scroll_y) {
|
|
int screen_y = top_y + (line - scroll_y) * CHAR_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;
|
|
da_draw_string(&g_ac, ln, margin_x, screen_y, DA_TEXT_DIM, DA_BG_SECONDARY, CHAR_STEP);
|
|
da_fill_rect(&g_ac, margin_x + line_num_w - 2, screen_y, 1, CHAR_H, DA_BORDER);
|
|
int col = 0;
|
|
while (pos < text_len && text_buf[pos] != '\n' && col < visible_cols) {
|
|
da_draw_char(&g_ac, text_buf[pos],
|
|
text_x + col * CHAR_STEP, screen_y,
|
|
DA_TEXT_PRIMARY, DA_BG_SECONDARY);
|
|
col++; pos++;
|
|
}
|
|
} else {
|
|
while (pos < text_len && text_buf[pos] != '\n') pos++;
|
|
}
|
|
if (pos < text_len && text_buf[pos] == '\n') pos++;
|
|
line++;
|
|
if (pos >= text_len) {
|
|
if (line > cursor_line) break;
|
|
}
|
|
}
|
|
|
|
/* 光标(编辑模式) */
|
|
if (!input_mode && cursor_line >= scroll_y && cursor_line < scroll_y + visible_rows) {
|
|
int cy = top_y + (cursor_line - scroll_y) * CHAR_H;
|
|
int cx = text_x + cursor_col * CHAR_STEP;
|
|
da_fill_rect(&g_ac, cx, cy, 2, CHAR_H, DA_ACCENT_LIGHT);
|
|
}
|
|
|
|
/* 状态栏 */
|
|
char status[96]; p = 0;
|
|
if (input_mode) {
|
|
const char *prompt = (input_mode == 1) ? "Open: " : "Save: ";
|
|
while (*prompt) status[p++] = *prompt++;
|
|
for (int i = 0; i < input_len; i++) status[p++] = input_buf[i];
|
|
status[p++] = '_';
|
|
const char *hint = " Enter:confirm Esc:cancel";
|
|
while (*hint && p < 95) status[p++] = *hint++;
|
|
} else {
|
|
const char *s1 = "L:"; while (*s1) status[p++] = *s1++;
|
|
status[p++] = '0' + (cursor_line+1)/10%10; status[p++] = '0' + (cursor_line+1)%10;
|
|
const char *s2 = " C:"; while (*s2) status[p++] = *s2++;
|
|
status[p++] = '0' + (cursor_col+1)/10%10; status[p++] = '0' + (cursor_col+1)%10;
|
|
if (modified) { const char *m = " *"; while (*m) status[p++] = *m++; }
|
|
const char *s3 = " Ctrl+O:Open Ctrl+S:Save Esc:Exit";
|
|
while (*s3 && p < 95) status[p++] = *s3++;
|
|
}
|
|
status[p] = 0;
|
|
da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
|
|
}
|
|
|
|
__attribute__((visibility("default")))
|
|
void dsk_entry(const da_boot_context *ctx) {
|
|
__asm__ volatile("cli");
|
|
da_slog("editor", "boot");
|
|
|
|
if (!ctx || ctx->magic != DA_BOOT_MAGIC) {
|
|
da_slog("editor", "bad context");
|
|
for(;;) __asm__("hlt");
|
|
}
|
|
|
|
da_init(&g_ac, ctx);
|
|
f32_init((f32_block_read_fn)g_ac.block_read, (f32_block_write_fn)g_ac.block_write);
|
|
init_default_text();
|
|
redraw_all();
|
|
|
|
int shift = 0, e0 = 0;
|
|
for (;;) {
|
|
u8 st = inb(0x64);
|
|
if (!(st & 1)) { __asm__("pause"); continue; }
|
|
u8 data = inb(0x60);
|
|
if (st & 0x20) continue;
|
|
u8 sc = data;
|
|
if (sc == 0xE0) { e0 = 1; continue; }
|
|
if (sc == 0x2A || sc == 0x36) { shift = 1; continue; }
|
|
if (sc == 0xAA || sc == 0xB6) { shift = 0; continue; }
|
|
if (sc == 0x1D) { ctrl_down = 1; continue; }
|
|
if (sc == 0x9D) { ctrl_down = 0; continue; }
|
|
if (sc & 0x80) { e0 = 0; continue; }
|
|
|
|
/* Esc:输入模式取消 / 编辑模式退出 */
|
|
if (sc == 0x01 && !e0) {
|
|
if (input_mode) { input_mode = 0; redraw_all(); continue; }
|
|
da_slog("editor", "exit");
|
|
return;
|
|
}
|
|
|
|
/* ---- 输入模式:输入文件名 ---- */
|
|
if (input_mode) {
|
|
if (sc == 0x1C) { /* Enter - 确认 */
|
|
commit_input();
|
|
redraw_all();
|
|
continue;
|
|
}
|
|
if (sc == 0x0E) { /* Backspace */
|
|
if (input_len > 0) input_len--;
|
|
input_buf[input_len] = 0;
|
|
redraw_all();
|
|
continue;
|
|
}
|
|
char c = da_scan_to_ascii(sc, shift);
|
|
if (c && c >= 32 && c <= 126 && input_len < INPUT_BUF_SIZE - 1) {
|
|
input_buf[input_len++] = c;
|
|
input_buf[input_len] = 0;
|
|
redraw_all();
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* ---- 编辑模式 ---- */
|
|
/* Esc 退出 */
|
|
if (sc == 0x01 && !e0) { da_slog("editor", "exit"); return; }
|
|
|
|
if (e0) {
|
|
if (sc == 0x48) { /* Up */
|
|
if (cursor_line > 0) {
|
|
int prev = line_start(cursor_line - 1);
|
|
cursor_pos = prev;
|
|
for (int i = 0; i < cursor_col && cursor_pos < text_len && text_buf[cursor_pos] != '\n'; i++)
|
|
cursor_pos++;
|
|
}
|
|
} else if (sc == 0x50) { /* Down */
|
|
int next = line_start(cursor_line + 1);
|
|
cursor_pos = next;
|
|
for (int i = 0; i < cursor_col && cursor_pos < text_len && text_buf[cursor_pos] != '\n'; i++)
|
|
cursor_pos++;
|
|
} else if (sc == 0x4B) { /* Left */
|
|
if (cursor_pos > 0) cursor_pos--;
|
|
} else if (sc == 0x4D) { /* Right */
|
|
if (cursor_pos < text_len) cursor_pos++;
|
|
} else if (sc == 0x47) { /* Home */
|
|
cursor_pos = line_start(cursor_line);
|
|
} else if (sc == 0x4F) { /* End */
|
|
cursor_pos = line_start(cursor_line);
|
|
while (cursor_pos < text_len && text_buf[cursor_pos] != '\n') cursor_pos++;
|
|
} else if (sc == 0x49) { /* PageUp */
|
|
for (int i = 0; i < 10 && cursor_line > 0; i++) {
|
|
cursor_pos = line_start(cursor_line - 1);
|
|
recalc_cursor();
|
|
}
|
|
} else if (sc == 0x51) { /* PageDown */
|
|
for (int i = 0; i < 10; i++) {
|
|
int next = line_start(cursor_line + 1);
|
|
if (next >= text_len) break;
|
|
cursor_pos = next;
|
|
recalc_cursor();
|
|
}
|
|
}
|
|
e0 = 0;
|
|
recalc_cursor();
|
|
redraw_all();
|
|
continue;
|
|
}
|
|
|
|
if (sc == 0x1C) { /* Enter */
|
|
if (text_len < EDITOR_BUF_SIZE - 1) {
|
|
for (int i = text_len; i > cursor_pos; i--) text_buf[i] = text_buf[i-1];
|
|
text_buf[cursor_pos] = '\n';
|
|
text_len++; cursor_pos++;
|
|
modified = 1;
|
|
}
|
|
} else if (sc == 0x0E) { /* Backspace */
|
|
if (cursor_pos > 0) {
|
|
for (int i = cursor_pos - 1; i < text_len - 1; i++) text_buf[i] = text_buf[i+1];
|
|
text_len--; cursor_pos--;
|
|
modified = 1;
|
|
}
|
|
} else if (sc == 0x53 && e0) { /* Delete */
|
|
if (cursor_pos < text_len) {
|
|
for (int i = cursor_pos; i < text_len - 1; i++) text_buf[i] = text_buf[i+1];
|
|
text_len--;
|
|
modified = 1;
|
|
}
|
|
} else {
|
|
/* Ctrl+O: 打开文件 */
|
|
if (ctrl_down && (sc == 0x18)) {
|
|
enter_input_mode(1);
|
|
redraw_all();
|
|
continue;
|
|
}
|
|
/* Ctrl+S: 保存文件 */
|
|
if (ctrl_down && (sc == 0x1F)) {
|
|
enter_input_mode(2);
|
|
redraw_all();
|
|
continue;
|
|
}
|
|
char c = da_scan_to_ascii(sc, shift);
|
|
if (c && c >= 32 && c <= 126 && text_len < EDITOR_BUF_SIZE - 1) {
|
|
for (int i = text_len; i > cursor_pos; i--) text_buf[i] = text_buf[i-1];
|
|
text_buf[cursor_pos] = c;
|
|
text_len++; cursor_pos++;
|
|
modified = 1;
|
|
}
|
|
}
|
|
recalc_cursor();
|
|
redraw_all();
|
|
}
|
|
}
|