feat: 图形桌面 + 独立工具应用 + Sealed Arc UI 设计系统 | desktop.elf窗口管理器/6个工具应用(bash/editor/fileman/browser/curl/ping)/desktop_app.h公共头文件/Sealed Arc设计系统/DRR完整实现/Process增强/DSK启动改为加载desktop.elf/Shell重构/开发文档
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
CC := clang
|
||||
LD := ld.lld
|
||||
|
||||
ROOT := ../../..
|
||||
OUT := $(ROOT)/SYSTEM/system/deshab64/tools/bash.elf
|
||||
|
||||
CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I.. -I../../UTSM/include -O2
|
||||
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
|
||||
|
||||
C_SOURCES := main.c
|
||||
OBJECTS := $(C_SOURCES:.c=.o)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(OUT)
|
||||
|
||||
$(OUT): $(OBJECTS) linker.ld
|
||||
@if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools"
|
||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
|
||||
@@ -0,0 +1,16 @@
|
||||
ENTRY(dsk_entry)
|
||||
|
||||
PHDRS
|
||||
{
|
||||
text PT_LOAD FLAGS(7);
|
||||
rodata PT_LOAD FLAGS(4);
|
||||
data PT_LOAD FLAGS(7);
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x1000;
|
||||
.text : ALIGN(4K) { *(.text*) } :text
|
||||
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
|
||||
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
|
||||
}
|
||||
@@ -0,0 +1,456 @@
|
||||
/* Deshab Bash — 全屏终端应用
|
||||
* PS/2 键盘输入,命令行编辑,内置命令。
|
||||
* Esc 键退出返回 DSK。
|
||||
*/
|
||||
|
||||
#include "../../firstInit/ascii_bitmaps.c"
|
||||
#include "../desktop_app.h"
|
||||
|
||||
/* ---- 终端参数 ---- */
|
||||
#define TERM_COLS 120
|
||||
#define TERM_ROWS 40
|
||||
#define TERM_MAX_CHARS (TERM_COLS * TERM_ROWS)
|
||||
#define CHAR_STEP 12
|
||||
#define CHAR_H 18
|
||||
#define PROMPT_STR "deshab# "
|
||||
|
||||
static da_app_context g_ac;
|
||||
|
||||
/* 终端缓冲区 */
|
||||
static u8 term_ch[TERM_MAX_CHARS];
|
||||
static u32 term_fg[TERM_MAX_CHARS];
|
||||
static int term_w = TERM_COLS;
|
||||
static int term_h = TERM_ROWS;
|
||||
static int cur_col = 0;
|
||||
static int cur_row = 0;
|
||||
|
||||
/* 输入缓冲 */
|
||||
static char input_buf[256];
|
||||
static int input_len = 0;
|
||||
static int input_cursor = 0;
|
||||
|
||||
/* 命令历史 */
|
||||
#define HIST_SIZE 16
|
||||
static char history[HIST_SIZE][256];
|
||||
static int hist_count = 0;
|
||||
static int hist_idx = 0;
|
||||
|
||||
/* 坐标 */
|
||||
static int term_x, term_y;
|
||||
static int content_h;
|
||||
|
||||
static void term_init_layout(void) {
|
||||
term_x = 8;
|
||||
term_y = DA_TITLEBAR_H + 4;
|
||||
int avail_w = (int)g_ac.fb_w - 16;
|
||||
int avail_h = (int)g_ac.fb_h - term_y - DA_STATUSBAR_H - 8;
|
||||
term_w = avail_w / CHAR_STEP;
|
||||
term_h = avail_h / CHAR_H;
|
||||
if (term_w < 10) term_w = 10;
|
||||
if (term_h < 5) term_h = 5;
|
||||
if (term_w > TERM_COLS) term_w = TERM_COLS;
|
||||
if (term_h > TERM_ROWS) term_h = TERM_ROWS;
|
||||
content_h = term_h * CHAR_H;
|
||||
}
|
||||
|
||||
static void term_clear(void) {
|
||||
for (int i = 0; i < TERM_MAX_CHARS; i++) {
|
||||
term_ch[i] = ' ';
|
||||
term_fg[i] = DA_TEXT_PRIMARY;
|
||||
}
|
||||
cur_col = 0; cur_row = 0;
|
||||
}
|
||||
|
||||
static void term_scroll(void) {
|
||||
for (int r = 1; r < term_h; r++) {
|
||||
for (int c = 0; c < term_w; c++) {
|
||||
term_ch[(r-1)*TERM_COLS+c] = term_ch[r*TERM_COLS+c];
|
||||
term_fg[(r-1)*TERM_COLS+c] = term_fg[r*TERM_COLS+c];
|
||||
}
|
||||
}
|
||||
for (int c = 0; c < term_w; c++) {
|
||||
term_ch[(term_h-1)*TERM_COLS+c] = ' ';
|
||||
term_fg[(term_h-1)*TERM_COLS+c] = DA_TEXT_PRIMARY;
|
||||
}
|
||||
cur_row = term_h - 1; cur_col = 0;
|
||||
}
|
||||
|
||||
static void term_newline(void) {
|
||||
cur_col = 0; cur_row++;
|
||||
if (cur_row >= term_h) term_scroll();
|
||||
}
|
||||
|
||||
static void term_putc_color(char c, u32 color) {
|
||||
if (c == '\n') { term_newline(); return; }
|
||||
if (c == '\r') { cur_col = 0; return; }
|
||||
if (c == '\t') { int sp = 4 - (cur_col % 4); for (int i = 0; i < sp; i++) term_putc_color(' ', color); return; }
|
||||
if (c < ' ' || c > '~') c = '?';
|
||||
if (cur_col >= term_w) term_newline();
|
||||
int idx = cur_row * TERM_COLS + cur_col;
|
||||
term_ch[idx] = (u8)c; term_fg[idx] = color;
|
||||
cur_col++;
|
||||
}
|
||||
|
||||
static void term_putc(char c) { term_putc_color(c, DA_TEXT_PRIMARY); }
|
||||
static void term_puts_color(const char *s, u32 color) { while (*s) term_putc_color(*s++, color); }
|
||||
static void term_puts(const char *s) { term_puts_color(s, DA_TEXT_PRIMARY); }
|
||||
|
||||
/* ---- 绘制 ---- */
|
||||
static void redraw_all(void) {
|
||||
da_fill_bg(&g_ac, DA_BG_PRIMARY);
|
||||
da_draw_titlebar(&g_ac, "Deshab Bash", (i64)g_ac.fb_w);
|
||||
/* 终端字符 */
|
||||
for (int r = 0; r < term_h; r++) {
|
||||
for (int c = 0; c < term_w; c++) {
|
||||
int idx = r * TERM_COLS + c;
|
||||
if (term_ch[idx] == ' ') continue;
|
||||
da_draw_char(&g_ac, term_ch[idx],
|
||||
term_x + c * CHAR_STEP, term_y + r * CHAR_H,
|
||||
term_fg[idx], DA_BG_PRIMARY);
|
||||
}
|
||||
}
|
||||
/* 光标 */
|
||||
if (cur_col < term_w && cur_row < term_h) {
|
||||
da_fill_rect(&g_ac, term_x + cur_col * CHAR_STEP,
|
||||
term_y + cur_row * CHAR_H + CHAR_H - 3, 11, 2, DA_ACCENT);
|
||||
}
|
||||
/* 状态栏 */
|
||||
char status[64];
|
||||
int p = 0;
|
||||
const char *s1 = "/ root@deshab ";
|
||||
while (*s1) status[p++] = *s1++;
|
||||
u8 hh, mm; da_rtc_time(&hh, &mm);
|
||||
status[p++] = '0' + hh/10; status[p++] = '0' + hh%10;
|
||||
status[p++] = ':'; status[p++] = '0' + mm/10; status[p++] = '0' + mm%10;
|
||||
status[p] = 0;
|
||||
da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
|
||||
}
|
||||
|
||||
/* ---- 内置命令 ---- */
|
||||
static int str_eq(const char *a, const char *b) {
|
||||
while (*a && *b) { if (*a != *b) return 0; a++; b++; }
|
||||
return *a == *b;
|
||||
}
|
||||
|
||||
static void cmd_help(void) {
|
||||
term_puts_color("Deshab Bash - 内置命令:\n", DA_PROMPT_COLOR);
|
||||
term_puts(" help 显示此帮助\n");
|
||||
term_puts(" clear 清屏\n");
|
||||
term_puts(" echo <text> 显示文本\n");
|
||||
term_puts(" version 显示系统版本\n");
|
||||
term_puts(" ver 同 version\n");
|
||||
term_puts(" uname 显示系统名称\n");
|
||||
term_puts(" date 显示当前日期时间\n");
|
||||
term_puts(" about 关于 Deshab\n");
|
||||
term_puts(" reboot 重启系统\n");
|
||||
term_puts(" halt 关机\n");
|
||||
term_puts(" pwd 显示当前目录\n");
|
||||
term_puts(" whoami 显示当前用户\n");
|
||||
term_puts(" id 显示用户ID\n");
|
||||
term_puts(" ls 列出根目录文件\n");
|
||||
term_puts(" cat <file> 显示文件内容\n");
|
||||
}
|
||||
|
||||
static void cmd_version(void) {
|
||||
term_puts_color("Deshab OS v0.1.0\n", DA_SUCCESS);
|
||||
term_puts(" Kernel: DSK (Deshab System Kernel)\n");
|
||||
term_puts(" Architecture: x86_64\n");
|
||||
term_puts(" ABI: DKM v1\n");
|
||||
term_puts(" Bootloader: Limine\n");
|
||||
}
|
||||
|
||||
static void cmd_uname(void) { term_puts("Deshab\n"); }
|
||||
|
||||
static void cmd_about(void) {
|
||||
term_puts_color("=== Deshab OS ===\n", DA_PROMPT_COLOR);
|
||||
term_puts("单地址空间 Ring0 内核实验系统\n");
|
||||
term_puts("架构: UTSM + DSK + DKM\n");
|
||||
term_puts_color("Built with Clang + lld\n", DA_TEXT_DIM);
|
||||
}
|
||||
|
||||
static void cmd_clear(void) { term_clear(); }
|
||||
|
||||
static void cmd_reboot(void) {
|
||||
term_puts_color("Rebooting...\n", DA_PROMPT_COLOR);
|
||||
redraw_all();
|
||||
outb(0x64, 0xFE);
|
||||
for(;;) __asm__("hlt");
|
||||
}
|
||||
|
||||
static void cmd_halt(void) {
|
||||
term_puts_color("System halted.\n", DA_PROMPT_COLOR);
|
||||
redraw_all();
|
||||
for(;;) __asm__("hlt");
|
||||
}
|
||||
|
||||
static void cmd_echo(const char *args) {
|
||||
if (args && *args) term_puts_color(args, DA_TEXT_PRIMARY);
|
||||
term_putc('\n');
|
||||
}
|
||||
|
||||
static void cmd_date(void) {
|
||||
u8 year, month, day, hour, minute, second;
|
||||
outb(0x70, 9); year = inb(0x71);
|
||||
outb(0x70, 8); month = inb(0x71);
|
||||
outb(0x70, 7); day = inb(0x71);
|
||||
outb(0x70, 4); hour = inb(0x71);
|
||||
outb(0x70, 2); minute = inb(0x71);
|
||||
outb(0x70, 0); second = inb(0x71);
|
||||
year = (u8)((year>>4)*10+(year&0xF));
|
||||
month = (u8)((month>>4)*10+(month&0xF));
|
||||
day = (u8)((day>>4)*10+(day&0xF));
|
||||
hour = (u8)((hour>>4)*10+(hour&0xF));
|
||||
minute = (u8)((minute>>4)*10+(minute&0xF));
|
||||
second = (u8)((second>>4)*10+(second&0xF));
|
||||
char buf[32]; int p = 0;
|
||||
buf[p++]='2'; buf[p++]='0'; buf[p++]='0'+year/10; buf[p++]='0'+year%10; buf[p++]='-';
|
||||
buf[p++]='0'+month/10; buf[p++]='0'+month%10; buf[p++]='-';
|
||||
buf[p++]='0'+day/10; buf[p++]='0'+day%10; buf[p++]=' ';
|
||||
buf[p++]='0'+hour/10; buf[p++]='0'+hour%10; buf[p++]=':';
|
||||
buf[p++]='0'+minute/10; buf[p++]='0'+minute%10; buf[p++]=':';
|
||||
buf[p++]='0'+second/10; buf[p++]='0'+second%10; buf[p]=0;
|
||||
term_puts(buf); term_putc('\n');
|
||||
}
|
||||
|
||||
static void cmd_ls(void) {
|
||||
if (!g_ac.block_read) {
|
||||
term_puts_color("ls: 无块设备可用\n", DA_ERROR);
|
||||
return;
|
||||
}
|
||||
/* 读取 FAT32 根目录 — 简化版:读取 BPB 获取根目录区 */
|
||||
u8 bpb[512];
|
||||
if (g_ac.block_read(0, 0, 1, bpb) != 0) {
|
||||
term_puts_color("ls: 读取 BPB 失败\n", DA_ERROR);
|
||||
return;
|
||||
}
|
||||
/* 检查 FAT32 签名 */
|
||||
if (bpb[510] != 0x55 || bpb[511] != 0xAA) {
|
||||
term_puts_color("ls: 无有效 FAT32 分区\n", DA_ERROR);
|
||||
return;
|
||||
}
|
||||
u32 root_cluster = *(u32 *)(bpb + 44);
|
||||
u16 bytes_per_sector = *(u16 *)(bpb + 11);
|
||||
u8 sectors_per_cluster = bpb[13];
|
||||
u32 fat_start = *(u16 *)(bpb + 14);
|
||||
u32 data_start = fat_start + (*(u16 *)(bpb + 22)) * 2;
|
||||
(void)bytes_per_sector;
|
||||
(void)sectors_per_cluster;
|
||||
(void)data_start;
|
||||
|
||||
/* 读取根目录簇 — 简化:只读取第一个簇 */
|
||||
u8 dir_buf[8192];
|
||||
u32 cluster = root_cluster;
|
||||
u32 lba = data_start + (cluster - 2) * sectors_per_cluster;
|
||||
if (g_ac.block_read(0, lba, sectors_per_cluster, dir_buf) != 0) {
|
||||
term_puts_color("ls: 读取根目录失败\n", DA_ERROR);
|
||||
return;
|
||||
}
|
||||
/* 解析目录项 */
|
||||
int found = 0;
|
||||
for (int i = 0; i < (int)(sectors_per_cluster * 512); i += 32) {
|
||||
u8 first = dir_buf[i];
|
||||
if (first == 0x00) break;
|
||||
if (first == 0xE5) continue;
|
||||
if (dir_buf[i+11] & 0x08) continue; /* 卷标 */
|
||||
if (dir_buf[i+11] & 0x10) {
|
||||
/* 目录 */
|
||||
term_puts_color("[DIR] ", DA_ACCENT);
|
||||
} else {
|
||||
term_puts_color(" ", DA_TEXT_PRIMARY);
|
||||
}
|
||||
/* 文件名(8.3) */
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if (dir_buf[i+j] == ' ') break;
|
||||
term_putc((char)dir_buf[i+j]);
|
||||
}
|
||||
if (dir_buf[i+8] != ' ') {
|
||||
term_putc('.');
|
||||
for (int j = 8; j < 11; j++) {
|
||||
if (dir_buf[i+j] == ' ') break;
|
||||
term_putc((char)dir_buf[i+j]);
|
||||
}
|
||||
}
|
||||
term_putc('\n');
|
||||
found++;
|
||||
}
|
||||
if (found == 0) term_puts_color("(empty)\n", DA_TEXT_DIM);
|
||||
}
|
||||
|
||||
static void cmd_cat(const char *args) {
|
||||
if (!args || !*args) {
|
||||
term_puts_color("cat: 需要文件名参数\n", DA_ERROR);
|
||||
return;
|
||||
}
|
||||
if (!g_ac.block_read) {
|
||||
term_puts_color("cat: 无块设备可用\n", DA_ERROR);
|
||||
return;
|
||||
}
|
||||
term_puts_color("cat: 文件读取暂未完整实现\n", DA_WARNING);
|
||||
}
|
||||
|
||||
static void execute_command(const char *cmd) {
|
||||
while (*cmd == ' ') cmd++;
|
||||
if (*cmd == 0) return;
|
||||
const char *args = cmd;
|
||||
while (*args && *args != ' ') args++;
|
||||
char name[32];
|
||||
int nlen = (int)(args - cmd);
|
||||
if (nlen >= 32) nlen = 31;
|
||||
for (int i = 0; i < nlen; i++) name[i] = cmd[i];
|
||||
name[nlen] = 0;
|
||||
while (*args == ' ') args++;
|
||||
|
||||
if (str_eq(name,"help")||str_eq(name,"?")) cmd_help();
|
||||
else if (str_eq(name,"version")||str_eq(name,"ver")) cmd_version();
|
||||
else if (str_eq(name,"uname")) cmd_uname();
|
||||
else if (str_eq(name,"about")) cmd_about();
|
||||
else if (str_eq(name,"clear")||str_eq(name,"cls")) cmd_clear();
|
||||
else if (str_eq(name,"reboot")) cmd_reboot();
|
||||
else if (str_eq(name,"halt")||str_eq(name,"shutdown")) cmd_halt();
|
||||
else if (str_eq(name,"echo")) cmd_echo(args);
|
||||
else if (str_eq(name,"date")||str_eq(name,"time")) cmd_date();
|
||||
else if (str_eq(name,"ls")||str_eq(name,"dir")) cmd_ls();
|
||||
else if (str_eq(name,"cat")) cmd_cat(args);
|
||||
else if (str_eq(name,"pwd")) { term_puts("/\n"); }
|
||||
else if (str_eq(name,"whoami")) { term_puts("root\n"); }
|
||||
else if (str_eq(name,"id")) { term_puts("uid=0(root) gid=0(root)\n"); }
|
||||
else {
|
||||
term_puts_color("未知命令: ", DA_ERROR);
|
||||
term_puts_color(name, DA_ERROR);
|
||||
term_putc('\n');
|
||||
term_puts_color("输入 'help' 查看可用命令\n", DA_TEXT_DIM);
|
||||
}
|
||||
}
|
||||
|
||||
static void add_history(const char *cmd) {
|
||||
if (hist_count < HIST_SIZE) {
|
||||
int i;
|
||||
for (i = 0; cmd[i] && i < 255; i++) history[hist_count][i] = cmd[i];
|
||||
history[hist_count][i] = 0;
|
||||
hist_count++;
|
||||
}
|
||||
hist_idx = hist_count;
|
||||
}
|
||||
|
||||
/* ---- 输入行重绘 ---- */
|
||||
static void redraw_input_line(void) {
|
||||
int plen = 0;
|
||||
while (PROMPT_STR[plen]) plen++;
|
||||
i64 y = term_y + (i64)cur_row * CHAR_H;
|
||||
da_fill_rect(&g_ac, term_x, y, (i64)term_w * CHAR_STEP, CHAR_H, DA_BG_PRIMARY);
|
||||
for (int i = 0; i < plen; i++)
|
||||
da_draw_char(&g_ac, PROMPT_STR[i], term_x + i * CHAR_STEP, y, DA_PROMPT_COLOR, DA_BG_PRIMARY);
|
||||
for (int i = 0; i < input_len; i++)
|
||||
da_draw_char(&g_ac, input_buf[i], term_x + (plen + i) * CHAR_STEP, y, DA_TEXT_PRIMARY, DA_BG_PRIMARY);
|
||||
cur_col = plen + input_cursor;
|
||||
/* 光标 */
|
||||
da_fill_rect(&g_ac, term_x + cur_col * CHAR_STEP, y + CHAR_H - 3, 11, 2, DA_ACCENT);
|
||||
}
|
||||
|
||||
/* ---- 主入口 ---- */
|
||||
__attribute__((visibility("default")))
|
||||
void dsk_entry(const da_boot_context *ctx) {
|
||||
__asm__ volatile("cli");
|
||||
da_slog("bash", "boot");
|
||||
|
||||
if (!ctx || ctx->magic != DA_BOOT_MAGIC) {
|
||||
da_slog("bash", "bad context");
|
||||
for(;;) __asm__("hlt");
|
||||
}
|
||||
|
||||
da_init(&g_ac, ctx);
|
||||
term_init_layout();
|
||||
term_clear();
|
||||
|
||||
/* 启动 banner */
|
||||
term_puts_color("=== Deshab Bash v0.1 ===\n", DA_ACCENT);
|
||||
term_puts_color("欢迎使用 Deshab 终端\n", DA_SUCCESS);
|
||||
term_puts_color("输入 'help' 查看可用命令 | Esc 退出\n", DA_TEXT_DIM);
|
||||
term_putc('\n');
|
||||
|
||||
redraw_all();
|
||||
|
||||
int shift = 0, e0 = 0;
|
||||
for (;;) {
|
||||
/* 绘制提示符 */
|
||||
term_puts_color(PROMPT_STR, DA_PROMPT_COLOR);
|
||||
input_len = 0; input_cursor = 0; input_buf[0] = 0;
|
||||
int plen = 0; while (PROMPT_STR[plen]) plen++;
|
||||
cur_col = plen;
|
||||
redraw_all();
|
||||
|
||||
int cmd_done = 0;
|
||||
while (!cmd_done) {
|
||||
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 & 0x80) { e0 = 0; continue; }
|
||||
|
||||
/* Esc 退出 */
|
||||
if (sc == 0x01 && !e0) {
|
||||
da_slog("bash", "exit");
|
||||
return;
|
||||
}
|
||||
|
||||
if (e0) {
|
||||
if (sc == 0x4B) { if (input_cursor > 0) input_cursor--; }
|
||||
else if (sc == 0x4D) { if (input_cursor < input_len) input_cursor++; }
|
||||
else if (sc == 0x47) { input_cursor = 0; }
|
||||
else if (sc == 0x4F) { input_cursor = input_len; }
|
||||
else if (sc == 0x48) { /* 上箭头 - 历史 */
|
||||
if (hist_idx > 0) {
|
||||
hist_idx--;
|
||||
int i; for (i = 0; history[hist_idx][i] && i < 255; i++) input_buf[i] = history[hist_idx][i];
|
||||
input_len = i; input_cursor = i; input_buf[i] = 0;
|
||||
}
|
||||
}
|
||||
else if (sc == 0x50) { /* 下箭头 - 历史 */
|
||||
if (hist_idx < hist_count - 1) {
|
||||
hist_idx++;
|
||||
int i; for (i = 0; history[hist_idx][i] && i < 255; i++) input_buf[i] = history[hist_idx][i];
|
||||
input_len = i; input_cursor = i; input_buf[i] = 0;
|
||||
} else if (hist_idx < hist_count) {
|
||||
hist_idx = hist_count;
|
||||
input_len = 0; input_cursor = 0; input_buf[0] = 0;
|
||||
}
|
||||
}
|
||||
e0 = 0;
|
||||
redraw_input_line();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sc == 0x1C) { /* Enter */
|
||||
input_buf[input_len] = 0;
|
||||
term_putc('\n');
|
||||
if (input_len > 0) add_history(input_buf);
|
||||
execute_command(input_buf);
|
||||
cmd_done = 1;
|
||||
continue;
|
||||
}
|
||||
if (sc == 0x0E) { /* Backspace */
|
||||
if (input_cursor > 0) {
|
||||
for (int i = input_cursor - 1; i < input_len - 1; i++)
|
||||
input_buf[i] = input_buf[i+1];
|
||||
input_len--; input_cursor--;
|
||||
input_buf[input_len] = 0;
|
||||
}
|
||||
redraw_input_line();
|
||||
continue;
|
||||
}
|
||||
char c = da_scan_to_ascii(sc, shift);
|
||||
if (c && c >= 32 && c <= 126 && input_len < 255) {
|
||||
for (int i = input_len; i > input_cursor; i--)
|
||||
input_buf[i] = input_buf[i-1];
|
||||
input_buf[input_cursor++] = c;
|
||||
input_len++;
|
||||
redraw_input_line();
|
||||
}
|
||||
}
|
||||
redraw_all();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
CC := clang
|
||||
LD := ld.lld
|
||||
|
||||
ROOT := ../../..
|
||||
OUT := $(ROOT)/SYSTEM/system/deshab64/tools/browser.elf
|
||||
|
||||
CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I.. -I../../UTSM/include -O2
|
||||
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
|
||||
|
||||
C_SOURCES := main.c
|
||||
OBJECTS := $(C_SOURCES:.c=.o)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(OUT)
|
||||
|
||||
$(OUT): $(OBJECTS) linker.ld
|
||||
@if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools"
|
||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
|
||||
@@ -0,0 +1,16 @@
|
||||
ENTRY(dsk_entry)
|
||||
|
||||
PHDRS
|
||||
{
|
||||
text PT_LOAD FLAGS(7);
|
||||
rodata PT_LOAD FLAGS(4);
|
||||
data PT_LOAD FLAGS(7);
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x1000;
|
||||
.text : ALIGN(4K) { *(.text*) } :text
|
||||
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
|
||||
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/* Deshab Browser — 全屏浏览器 (Stub)
|
||||
* 地址栏 + 导航按钮 + 内容渲染区域。
|
||||
* 当前显示 stub 提示,Esc 退出。
|
||||
*/
|
||||
|
||||
#include "../../firstInit/ascii_bitmaps.c"
|
||||
#include "../desktop_app.h"
|
||||
|
||||
#define CHAR_STEP 12
|
||||
#define CHAR_H 18
|
||||
|
||||
static da_app_context g_ac;
|
||||
static da_cursor g_cursor;
|
||||
static da_mouse g_mouse;
|
||||
|
||||
static void redraw_all(void) {
|
||||
da_fill_bg(&g_ac, DA_BG_PRIMARY);
|
||||
|
||||
/* 标题栏 */
|
||||
da_draw_titlebar(&g_ac, "Browser", (i64)g_ac.fb_w);
|
||||
|
||||
/* 地址栏 */
|
||||
int bar_y = DA_TITLEBAR_H + 6;
|
||||
int bar_h = 28;
|
||||
int bar_x = 12;
|
||||
int bar_w = (int)g_ac.fb_w - 24;
|
||||
da_fill_rounded_rect(&g_ac, bar_x, bar_y, bar_w, bar_h, DA_BG_TERTIARY, 6);
|
||||
da_rect_outline(&g_ac, bar_x, bar_y, bar_w, bar_h, DA_BORDER, 6);
|
||||
|
||||
/* 导航按钮 */
|
||||
int btn_w = 32;
|
||||
da_fill_rounded_rect(&g_ac, bar_x + 4, bar_y + 4, btn_w, bar_h - 8, DA_BG_SECONDARY, 4);
|
||||
da_draw_string(&g_ac, "<", bar_x + 14, bar_y + 5, DA_TEXT_PRIMARY, DA_BG_SECONDARY, CHAR_STEP);
|
||||
da_fill_rounded_rect(&g_ac, bar_x + 4 + btn_w + 4, bar_y + 4, btn_w, bar_h - 8, DA_BG_SECONDARY, 4);
|
||||
da_draw_string(&g_ac, ">", bar_x + 4 + btn_w + 4 + 12, bar_y + 5, DA_TEXT_PRIMARY, DA_BG_SECONDARY, CHAR_STEP);
|
||||
da_fill_rounded_rect(&g_ac, bar_x + 4 + (btn_w + 4) * 2, bar_y + 4, btn_w, bar_h - 8, DA_BG_SECONDARY, 4);
|
||||
da_draw_string(&g_ac, "R", bar_x + 4 + (btn_w + 4) * 2 + 12, bar_y + 5, DA_TEXT_PRIMARY, DA_BG_SECONDARY, CHAR_STEP);
|
||||
|
||||
/* URL 文本 */
|
||||
int url_x = bar_x + (btn_w + 4) * 3 + 8;
|
||||
da_draw_string(&g_ac, "deshab://home", url_x, bar_y + 5, DA_TEXT_DIM, DA_BG_TERTIARY, CHAR_STEP);
|
||||
|
||||
/* 内容区域 */
|
||||
int content_y = bar_y + bar_h + 12;
|
||||
int content_x = 16;
|
||||
int content_w = (int)g_ac.fb_w - 32;
|
||||
|
||||
/* Stub 页面 */
|
||||
da_fill_rounded_rect(&g_ac, content_x, content_y, content_w, 200, DA_BG_SECONDARY, 8);
|
||||
da_rect_outline(&g_ac, content_x, content_y, content_w, 200, DA_BORDER, 8);
|
||||
|
||||
/* 居中图标 */
|
||||
int icon_cx = content_x + content_w / 2 - 32;
|
||||
int icon_cy = content_y + 24;
|
||||
da_fill_rounded_rect(&g_ac, icon_cx, icon_cy, 64, 48, DA_BG_TERTIARY, 8);
|
||||
/* 地球图标简化:圆圈 */
|
||||
int cx = icon_cx + 32, cy = icon_cy + 24, r = 16;
|
||||
for (int dy = -r; dy <= r; dy++)
|
||||
for (int dx = -r; dx <= r; dx++)
|
||||
if (dx*dx+dy*dy <= r*r && dx*dx+dy*dy > (r-1)*(r-1))
|
||||
da_pixel(&g_ac, cx+dx, cy+dy, DA_ACCENT);
|
||||
/* 横线 */
|
||||
for (int dx = -r; dx <= r; dx++)
|
||||
if (dx*dx + 0 <= r*r)
|
||||
da_pixel(&g_ac, cx+dx, cy, DA_ACCENT);
|
||||
/* 竖线 */
|
||||
for (int dy = -r; dy <= r; dy++)
|
||||
if (0 + dy*dy <= r*r)
|
||||
da_pixel(&g_ac, cx, cy+dy, DA_ACCENT);
|
||||
|
||||
/* 提示文字 */
|
||||
da_draw_string(&g_ac, "Deshab Browser",
|
||||
content_x + content_w / 2 - 7 * CHAR_STEP, icon_cy + 60,
|
||||
DA_TEXT_PRIMARY, DA_BG_SECONDARY, CHAR_STEP);
|
||||
da_draw_string(&g_ac, "HTTP + HTML rendering required",
|
||||
content_x + content_w / 2 - 15 * CHAR_STEP, icon_cy + 82,
|
||||
DA_TEXT_DIM, DA_BG_SECONDARY, CHAR_STEP);
|
||||
da_draw_string(&g_ac, "Press Esc to exit",
|
||||
content_x + content_w / 2 - 9 * CHAR_STEP, icon_cy + 104,
|
||||
DA_TEXT_DIM, DA_BG_SECONDARY, CHAR_STEP);
|
||||
|
||||
/* 状态栏 */
|
||||
da_draw_statusbar(&g_ac, "Browser | Stub - No network stack", (i64)g_ac.fb_w, (i64)g_ac.fb_h);
|
||||
|
||||
/* 鼠标光标 */
|
||||
da_cursor_save(&g_ac, &g_cursor);
|
||||
da_cursor_draw(&g_ac, &g_cursor, DA_CURSOR_COLOR);
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
void dsk_entry(const da_boot_context *ctx) {
|
||||
__asm__ volatile("cli");
|
||||
da_slog("browser", "boot");
|
||||
|
||||
if (!ctx || ctx->magic != DA_BOOT_MAGIC) {
|
||||
da_slog("browser", "bad context");
|
||||
for(;;) __asm__("hlt");
|
||||
}
|
||||
|
||||
da_init(&g_ac, ctx);
|
||||
da_cursor_init(&g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
|
||||
da_mouse_init();
|
||||
redraw_all();
|
||||
|
||||
for (;;) {
|
||||
int need_redraw = 0;
|
||||
|
||||
/* 鼠标轮询 */
|
||||
if (da_mouse_poll(&g_mouse, &g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h))
|
||||
need_redraw = 1;
|
||||
|
||||
/* 键盘轮询 */
|
||||
u8 st = inb(0x64);
|
||||
if ((st & 1) && !(st & 0x20)) {
|
||||
u8 data = inb(0x60);
|
||||
if (!(data & 0x80) && data == 0x01) {
|
||||
da_slog("browser", "exit");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (need_redraw) {
|
||||
da_cursor_restore(&g_ac, &g_cursor);
|
||||
redraw_all();
|
||||
}
|
||||
__asm__("pause");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
CC := clang
|
||||
LD := ld.lld
|
||||
|
||||
ROOT := ../../..
|
||||
OUT := $(ROOT)/SYSTEM/system/deshab64/tools/curl.elf
|
||||
|
||||
CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I.. -I../../UTSM/include -O2
|
||||
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
|
||||
|
||||
C_SOURCES := main.c
|
||||
OBJECTS := $(C_SOURCES:.c=.o)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(OUT)
|
||||
|
||||
$(OUT): $(OBJECTS) linker.ld
|
||||
@if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools"
|
||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
|
||||
@@ -0,0 +1,16 @@
|
||||
ENTRY(dsk_entry)
|
||||
|
||||
PHDRS
|
||||
{
|
||||
text PT_LOAD FLAGS(7);
|
||||
rodata PT_LOAD FLAGS(4);
|
||||
data PT_LOAD FLAGS(7);
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x1000;
|
||||
.text : ALIGN(4K) { *(.text*) } :text
|
||||
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
|
||||
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
/* Deshab curl — 终端风格 HTTP 客户端 stub
|
||||
* 命令行输入 URL,显示 HTTP GET 请求结果。
|
||||
* 当前为 stub,提示需要 TCP/IP 栈支持。
|
||||
* Esc 键退出返回 DSK。
|
||||
*/
|
||||
|
||||
#include "../../firstInit/ascii_bitmaps.c"
|
||||
#include "../desktop_app.h"
|
||||
|
||||
/* ---- 终端参数 ---- */
|
||||
#define TERM_COLS 120
|
||||
#define TERM_ROWS 40
|
||||
#define TERM_MAX_CHARS (TERM_COLS * TERM_ROWS)
|
||||
#define CHAR_STEP 12
|
||||
#define CHAR_H 18
|
||||
#define PROMPT_STR "curl> "
|
||||
|
||||
static da_app_context g_ac;
|
||||
|
||||
/* 终端缓冲区 */
|
||||
static u8 term_ch[TERM_MAX_CHARS];
|
||||
static u32 term_fg[TERM_MAX_CHARS];
|
||||
static int term_w = TERM_COLS;
|
||||
static int term_h = TERM_ROWS;
|
||||
static int cur_col = 0;
|
||||
static int cur_row = 0;
|
||||
|
||||
/* 输入缓冲 */
|
||||
static char input_buf[256];
|
||||
static int input_len = 0;
|
||||
static int input_cursor = 0;
|
||||
|
||||
/* 命令历史 */
|
||||
#define HIST_SIZE 16
|
||||
static char history[HIST_SIZE][256];
|
||||
static int hist_count = 0;
|
||||
static int hist_idx = 0;
|
||||
|
||||
/* 坐标 */
|
||||
static int term_x, term_y;
|
||||
|
||||
static void term_init_layout(void) {
|
||||
term_x = 8;
|
||||
term_y = DA_TITLEBAR_H + 4;
|
||||
int avail_w = (int)g_ac.fb_w - 16;
|
||||
int avail_h = (int)g_ac.fb_h - term_y - DA_STATUSBAR_H - 8;
|
||||
term_w = avail_w / CHAR_STEP;
|
||||
term_h = avail_h / CHAR_H;
|
||||
if (term_w < 10) term_w = 10;
|
||||
if (term_h < 5) term_h = 5;
|
||||
if (term_w > TERM_COLS) term_w = TERM_COLS;
|
||||
if (term_h > TERM_ROWS) term_h = TERM_ROWS;
|
||||
}
|
||||
|
||||
static void term_clear(void) {
|
||||
for (int i = 0; i < TERM_MAX_CHARS; i++) {
|
||||
term_ch[i] = ' ';
|
||||
term_fg[i] = DA_TEXT_PRIMARY;
|
||||
}
|
||||
cur_col = 0; cur_row = 0;
|
||||
}
|
||||
|
||||
static void term_scroll(void) {
|
||||
for (int r = 1; r < term_h; r++) {
|
||||
for (int c = 0; c < term_w; c++) {
|
||||
term_ch[(r-1)*TERM_COLS+c] = term_ch[r*TERM_COLS+c];
|
||||
term_fg[(r-1)*TERM_COLS+c] = term_fg[r*TERM_COLS+c];
|
||||
}
|
||||
}
|
||||
for (int c = 0; c < term_w; c++) {
|
||||
term_ch[(term_h-1)*TERM_COLS+c] = ' ';
|
||||
term_fg[(term_h-1)*TERM_COLS+c] = DA_TEXT_PRIMARY;
|
||||
}
|
||||
cur_row = term_h - 1; cur_col = 0;
|
||||
}
|
||||
|
||||
static void term_newline(void) {
|
||||
cur_col = 0; cur_row++;
|
||||
if (cur_row >= term_h) term_scroll();
|
||||
}
|
||||
|
||||
static void term_putc_color(char c, u32 color) {
|
||||
if (c == '\n') { term_newline(); return; }
|
||||
if (c == '\r') { cur_col = 0; return; }
|
||||
if (c == '\t') { int sp = 4 - (cur_col % 4); for (int i = 0; i < sp; i++) term_putc_color(' ', color); return; }
|
||||
if (c < ' ' || c > '~') c = '?';
|
||||
if (cur_col >= term_w) term_newline();
|
||||
int idx = cur_row * TERM_COLS + cur_col;
|
||||
term_ch[idx] = (u8)c; term_fg[idx] = color;
|
||||
cur_col++;
|
||||
}
|
||||
|
||||
static void term_putc(char c) { term_putc_color(c, DA_TEXT_PRIMARY); }
|
||||
static void term_puts_color(const char *s, u32 color) { while (*s) term_putc_color(*s++, color); }
|
||||
static void term_puts(const char *s) { term_puts_color(s, DA_TEXT_PRIMARY); }
|
||||
|
||||
/* ---- 绘制 ---- */
|
||||
static void redraw_all(void) {
|
||||
da_fill_bg(&g_ac, DA_BG_PRIMARY);
|
||||
da_draw_titlebar(&g_ac, "curl", (i64)g_ac.fb_w);
|
||||
/* 终端字符 */
|
||||
for (int r = 0; r < term_h; r++) {
|
||||
for (int c = 0; c < term_w; c++) {
|
||||
int idx = r * TERM_COLS + c;
|
||||
if (term_ch[idx] == ' ') continue;
|
||||
da_draw_char(&g_ac, term_ch[idx],
|
||||
term_x + c * CHAR_STEP, term_y + r * CHAR_H,
|
||||
term_fg[idx], DA_BG_PRIMARY);
|
||||
}
|
||||
}
|
||||
/* 光标 */
|
||||
if (cur_col < term_w && cur_row < term_h) {
|
||||
da_fill_rect(&g_ac, term_x + cur_col * CHAR_STEP,
|
||||
term_y + cur_row * CHAR_H + CHAR_H - 3, 11, 2, DA_ACCENT);
|
||||
}
|
||||
/* 状态栏 */
|
||||
char status[64];
|
||||
int p = 0;
|
||||
const char *s1 = "HTTP client | Esc 退出 ";
|
||||
while (*s1) status[p++] = *s1++;
|
||||
u8 hh, mm; da_rtc_time(&hh, &mm);
|
||||
status[p++] = '0' + hh/10; status[p++] = '0' + hh%10;
|
||||
status[p++] = ':'; status[p++] = '0' + mm/10; status[p++] = '0' + mm%10;
|
||||
status[p] = 0;
|
||||
da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
|
||||
}
|
||||
|
||||
/* ---- URL 请求模拟 ---- */
|
||||
static void do_http_get(const char *url) {
|
||||
term_puts_color("GET ", DA_PROMPT_COLOR);
|
||||
term_puts_color(url, DA_ACCENT_LIGHT);
|
||||
term_putc('\n');
|
||||
term_puts_color(" -> 解析主机...\n", DA_TEXT_DIM);
|
||||
term_puts_color(" -> 建立 TCP 连接...\n", DA_TEXT_DIM);
|
||||
|
||||
/* stub: 检查 kernel_api 是否有 net 接口 */
|
||||
if (!g_ac.kernel_api) {
|
||||
term_puts_color(" [错误] 无内核 API\n", DA_ERROR);
|
||||
return;
|
||||
}
|
||||
/* 尝试读取 net API — 当前为 stub */
|
||||
u64 net_api = *(u64 *)((u64)g_ac.kernel_api + 0xC0);
|
||||
if (!net_api) {
|
||||
term_puts_color(" [错误] 需要 TCP/IP 栈支持\n", DA_ERROR);
|
||||
term_puts_color(" 当前内核未实现网络协议栈。\n", DA_WARNING);
|
||||
term_puts_color(" 请等待后续版本更新。\n", DA_TEXT_DIM);
|
||||
return;
|
||||
}
|
||||
/* 如果有 net API,此处应实现真正的 HTTP GET */
|
||||
term_puts_color(" [stub] 网络功能尚未实现\n", DA_WARNING);
|
||||
}
|
||||
|
||||
/* ---- 内置命令 ---- */
|
||||
static int str_eq(const char *a, const char *b) {
|
||||
while (*a && *b) { if (*a != *b) return 0; a++; b++; }
|
||||
return *a == *b;
|
||||
}
|
||||
|
||||
static void cmd_help(void) {
|
||||
term_puts_color("curl — 命令行 HTTP 客户端\n", DA_PROMPT_COLOR);
|
||||
term_puts(" <url> 发送 HTTP GET 请求\n");
|
||||
term_puts(" help 显示此帮助\n");
|
||||
term_puts(" clear 清屏\n");
|
||||
term_puts(" version 显示版本\n");
|
||||
term_puts(" Esc 退出\n");
|
||||
}
|
||||
|
||||
static void cmd_clear(void) { term_clear(); }
|
||||
|
||||
static void cmd_version(void) {
|
||||
term_puts_color("Deshab curl v0.1 (stub)\n", DA_SUCCESS);
|
||||
term_puts_color(" HTTP 客户端功能需要 TCP/IP 栈支持\n", DA_TEXT_DIM);
|
||||
}
|
||||
|
||||
static void execute_command(const char *cmd) {
|
||||
while (*cmd == ' ') cmd++;
|
||||
if (*cmd == 0) return;
|
||||
|
||||
if (str_eq(cmd, "help") || str_eq(cmd, "?")) { cmd_help(); return; }
|
||||
if (str_eq(cmd, "clear") || str_eq(cmd, "cls")) { cmd_clear(); return; }
|
||||
if (str_eq(cmd, "version") || str_eq(cmd, "ver")) { cmd_version(); return; }
|
||||
|
||||
/* 非内置命令视为 URL */
|
||||
if (cmd[0] == 'h' && cmd[1] == 't' && cmd[2] == 't' && cmd[3] == 'p') {
|
||||
do_http_get(cmd);
|
||||
} else {
|
||||
term_puts_color("无效 URL: ", DA_ERROR);
|
||||
term_puts_color(cmd, DA_ERROR);
|
||||
term_putc('\n');
|
||||
term_puts_color("请输入完整 URL (http://...)\n", DA_TEXT_DIM);
|
||||
}
|
||||
}
|
||||
|
||||
static void add_history(const char *cmd) {
|
||||
if (hist_count < HIST_SIZE) {
|
||||
int i;
|
||||
for (i = 0; cmd[i] && i < 255; i++) history[hist_count][i] = cmd[i];
|
||||
history[hist_count][i] = 0;
|
||||
hist_count++;
|
||||
}
|
||||
hist_idx = hist_count;
|
||||
}
|
||||
|
||||
/* ---- 输入行重绘 ---- */
|
||||
static void redraw_input_line(void) {
|
||||
int plen = 0;
|
||||
while (PROMPT_STR[plen]) plen++;
|
||||
i64 y = term_y + (i64)cur_row * CHAR_H;
|
||||
da_fill_rect(&g_ac, term_x, y, (i64)term_w * CHAR_STEP, CHAR_H, DA_BG_PRIMARY);
|
||||
for (int i = 0; i < plen; i++)
|
||||
da_draw_char(&g_ac, PROMPT_STR[i], term_x + i * CHAR_STEP, y, DA_PROMPT_COLOR, DA_BG_PRIMARY);
|
||||
for (int i = 0; i < input_len; i++)
|
||||
da_draw_char(&g_ac, input_buf[i], term_x + (plen + i) * CHAR_STEP, y, DA_TEXT_PRIMARY, DA_BG_PRIMARY);
|
||||
cur_col = plen + input_cursor;
|
||||
da_fill_rect(&g_ac, term_x + cur_col * CHAR_STEP, y + CHAR_H - 3, 11, 2, DA_ACCENT);
|
||||
}
|
||||
|
||||
/* ---- 主入口 ---- */
|
||||
__attribute__((visibility("default")))
|
||||
void dsk_entry(const da_boot_context *ctx) {
|
||||
__asm__ volatile("cli");
|
||||
da_slog("curl", "boot");
|
||||
|
||||
if (!ctx || ctx->magic != DA_BOOT_MAGIC) {
|
||||
da_slog("curl", "bad context");
|
||||
for(;;) __asm__("hlt");
|
||||
}
|
||||
|
||||
da_init(&g_ac, ctx);
|
||||
term_init_layout();
|
||||
term_clear();
|
||||
|
||||
/* 启动 banner */
|
||||
term_puts_color("=== Deshab curl v0.1 (stub) ===\n", DA_ACCENT);
|
||||
term_puts_color("HTTP 命令行客户端\n", DA_SUCCESS);
|
||||
term_puts_color("输入 URL 发送 GET 请求 | 输入 'help' 查看帮助 | Esc 退出\n", DA_TEXT_DIM);
|
||||
term_puts_color("注意: 当前需要 TCP/IP 栈支持\n", DA_WARNING);
|
||||
term_putc('\n');
|
||||
|
||||
redraw_all();
|
||||
|
||||
int shift = 0, e0 = 0;
|
||||
for (;;) {
|
||||
term_puts_color(PROMPT_STR, DA_PROMPT_COLOR);
|
||||
input_len = 0; input_cursor = 0; input_buf[0] = 0;
|
||||
int plen = 0; while (PROMPT_STR[plen]) plen++;
|
||||
cur_col = plen;
|
||||
redraw_all();
|
||||
|
||||
int cmd_done = 0;
|
||||
while (!cmd_done) {
|
||||
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 & 0x80) { e0 = 0; continue; }
|
||||
|
||||
/* Esc 退出 */
|
||||
if (sc == 0x01 && !e0) {
|
||||
da_slog("curl", "exit");
|
||||
return;
|
||||
}
|
||||
|
||||
if (e0) {
|
||||
if (sc == 0x4B) { if (input_cursor > 0) input_cursor--; }
|
||||
else if (sc == 0x4D) { if (input_cursor < input_len) input_cursor++; }
|
||||
else if (sc == 0x47) { input_cursor = 0; }
|
||||
else if (sc == 0x4F) { input_cursor = input_len; }
|
||||
else if (sc == 0x48) {
|
||||
if (hist_idx > 0) {
|
||||
hist_idx--;
|
||||
int i; for (i = 0; history[hist_idx][i] && i < 255; i++) input_buf[i] = history[hist_idx][i];
|
||||
input_len = i; input_cursor = i; input_buf[i] = 0;
|
||||
}
|
||||
}
|
||||
else if (sc == 0x50) {
|
||||
if (hist_idx < hist_count - 1) {
|
||||
hist_idx++;
|
||||
int i; for (i = 0; history[hist_idx][i] && i < 255; i++) input_buf[i] = history[hist_idx][i];
|
||||
input_len = i; input_cursor = i; input_buf[i] = 0;
|
||||
} else if (hist_idx < hist_count) {
|
||||
hist_idx = hist_count;
|
||||
input_len = 0; input_cursor = 0; input_buf[0] = 0;
|
||||
}
|
||||
}
|
||||
e0 = 0;
|
||||
redraw_input_line();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sc == 0x1C) { /* Enter */
|
||||
input_buf[input_len] = 0;
|
||||
term_putc('\n');
|
||||
if (input_len > 0) add_history(input_buf);
|
||||
execute_command(input_buf);
|
||||
cmd_done = 1;
|
||||
continue;
|
||||
}
|
||||
if (sc == 0x0E) { /* Backspace */
|
||||
if (input_cursor > 0) {
|
||||
for (int i = input_cursor - 1; i < input_len - 1; i++)
|
||||
input_buf[i] = input_buf[i+1];
|
||||
input_len--; input_cursor--;
|
||||
input_buf[input_len] = 0;
|
||||
}
|
||||
redraw_input_line();
|
||||
continue;
|
||||
}
|
||||
char c = da_scan_to_ascii(sc, shift);
|
||||
if (c && c >= 32 && c <= 126 && input_len < 255) {
|
||||
for (int i = input_len; i > input_cursor; i--)
|
||||
input_buf[i] = input_buf[i-1];
|
||||
input_buf[input_cursor++] = c;
|
||||
input_len++;
|
||||
redraw_input_line();
|
||||
}
|
||||
}
|
||||
redraw_all();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,374 @@
|
||||
/* desktop_app.h — Deshab 独立工具应用公共头文件
|
||||
*
|
||||
* 独立工具应用运行在 DSK 加载的 .elf 中,全屏独占 framebuffer。
|
||||
* 每个 .elf 实现 dsk_entry(dsk_boot_context *ctx) 入口点。
|
||||
* 通过 kernel_api 访问块设备、网络等内核服务。
|
||||
* PS/2 键盘/鼠标通过端口 I/O 直接轮询。
|
||||
*/
|
||||
|
||||
#ifndef DESHAB_DESKTOP_APP_H
|
||||
#define DESHAB_DESKTOP_APP_H
|
||||
|
||||
/* 基础类型 */
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
typedef long long i64;
|
||||
|
||||
/* 端口 I/O */
|
||||
static inline void outb(u16 port, u8 val) { __asm__ volatile("outb %0,%1"::"a"(val),"Nd"(port)); }
|
||||
static inline u8 inb(u16 port) { u8 v; __asm__ volatile("inb %1,%0":"=a"(v):"Nd"(port)); return v; }
|
||||
|
||||
/* COM1 串口日志 */
|
||||
#define DA_COM1 0x3F8
|
||||
static inline void da_sputc(char c) { for(unsigned i=0;i<100000;i++){if(inb(DA_COM1+5)&0x20)break;} outb(DA_COM1,(u8)c); }
|
||||
static inline void da_swrite(const char *s) { while(*s){if(*s=='\n')da_sputc('\r');da_sputc(*s++);} }
|
||||
static inline void da_slog(const char *tag, const char *msg) { da_swrite("["); da_swrite(tag); da_swrite("] "); da_swrite(msg); da_swrite("\n"); }
|
||||
|
||||
/* dsk_boot_context 定义(与 dsk.h 一致,自包含避免依赖) */
|
||||
#define DA_BOOT_MAGIC 0x44534B31424F4F54ULL
|
||||
typedef struct {
|
||||
u64 magic; u32 abi_version; u32 size; u64 flags;
|
||||
u64 hhdm_offset; u64 rsdp_address;
|
||||
u64 framebuffer_address; u64 framebuffer_width; u64 framebuffer_height;
|
||||
u64 framebuffer_pitch; u32 framebuffer_bpp; u32 reserved_fb;
|
||||
u64 boot_modules_response;
|
||||
u64 dkm_kernel_api; u64 dkm_driver_table; u64 dkm_driver_count;
|
||||
u64 utsm_state; u64 drr_state;
|
||||
u64 memory_map; u64 memory_map_count; u64 memory_map_entry_size;
|
||||
u64 kernel_stack_top; u64 reserved[8];
|
||||
} da_boot_context;
|
||||
|
||||
/* block_read 函数类型 */
|
||||
typedef int (*da_block_read_fn)(void *ctx, u64 lba, u32 count, void *buffer);
|
||||
|
||||
/* 应用上下文 — 从 dsk_boot_context 提取的常用数据 */
|
||||
typedef struct {
|
||||
u32 *fb; /* framebuffer 基址 */
|
||||
u64 fb_w; /* 宽度 */
|
||||
u64 fb_h; /* 高度 */
|
||||
u64 fb_pitch; /* 行宽(字节) */
|
||||
da_block_read_fn block_read;
|
||||
const void *kernel_api;
|
||||
} da_app_context;
|
||||
|
||||
/* 从 dsk_boot_context 初始化 da_app_context */
|
||||
static inline void da_init(da_app_context *ac, const da_boot_context *ctx) {
|
||||
ac->fb = (u32 *)ctx->framebuffer_address;
|
||||
ac->fb_w = ctx->framebuffer_width;
|
||||
ac->fb_h = ctx->framebuffer_height;
|
||||
ac->fb_pitch = ctx->framebuffer_pitch;
|
||||
ac->kernel_api = (const void *)ctx->dkm_kernel_api;
|
||||
/* block_read 从 kernel_api + 0xA8 获取 */
|
||||
u64 api = ctx->dkm_kernel_api;
|
||||
ac->block_read = (da_block_read_fn)*(u64 *)(api + 0xA8);
|
||||
if (ac->block_read)
|
||||
ac->block_read = (da_block_read_fn)*(u64 *)((u64)ac->block_read + 16);
|
||||
}
|
||||
|
||||
/* ========== 帧缓冲绘制 ========== */
|
||||
|
||||
static inline void da_pixel(da_app_context *ac, i64 x, i64 y, u32 color) {
|
||||
if (x < 0 || (u64)x >= ac->fb_w || y < 0 || (u64)y >= ac->fb_h) return;
|
||||
u32 *line = (u32 *)((u8 *)ac->fb + (u64)y * ac->fb_pitch);
|
||||
line[x] = color;
|
||||
}
|
||||
|
||||
static inline u32 da_pixel_read(da_app_context *ac, i64 x, i64 y) {
|
||||
if (x < 0 || (u64)x >= ac->fb_w || y < 0 || (u64)y >= ac->fb_h) return 0;
|
||||
u32 *line = (u32 *)((u8 *)ac->fb + (u64)y * ac->fb_pitch);
|
||||
return line[x];
|
||||
}
|
||||
|
||||
static inline u32 da_blend(u32 bg, u32 fg, u32 alpha) {
|
||||
u32 na = 256 - alpha;
|
||||
u32 r = ((bg & 0xFF) * na + (fg & 0xFF) * alpha) >> 8;
|
||||
u32 g = (((bg >> 8) & 0xFF) * na + ((fg >> 8) & 0xFF) * alpha) >> 8;
|
||||
u32 b = (((bg >> 16) & 0xFF) * na + ((fg >> 16) & 0xFF) * alpha) >> 8;
|
||||
return 0xFF000000u | (b << 16) | (g << 8) | r;
|
||||
}
|
||||
|
||||
static inline void da_fill_rect(da_app_context *ac, i64 x, i64 y, i64 w, i64 h, u32 color) {
|
||||
for (i64 r = 0; r < h; r++) {
|
||||
i64 yy = y + r;
|
||||
if (yy < 0 || (u64)yy >= ac->fb_h) continue;
|
||||
u32 *line = (u32 *)((u8 *)ac->fb + (u64)yy * ac->fb_pitch);
|
||||
for (i64 c = 0; c < w; c++) {
|
||||
i64 xx = x + c;
|
||||
if (xx < 0 || (u64)xx >= ac->fb_w) continue;
|
||||
line[xx] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void da_fill_bg(da_app_context *ac, u32 color) {
|
||||
da_fill_rect(ac, 0, 0, (i64)ac->fb_w, (i64)ac->fb_h, color);
|
||||
}
|
||||
|
||||
/* 圆角矩形 */
|
||||
static inline void da_fill_rounded_rect(da_app_context *ac, i64 x, i64 y, i64 w, i64 h, u32 color, i64 radius) {
|
||||
i64 r = radius;
|
||||
if (r > w/2) r = w/2;
|
||||
if (r > h/2) r = h/2;
|
||||
da_fill_rect(ac, x+r, y, w-2*r, h, color);
|
||||
da_fill_rect(ac, x, y+r, r, h-2*r, color);
|
||||
da_fill_rect(ac, x+w-r, y+r, r, h-2*r, color);
|
||||
for (i64 dy = 0; dy < r; dy++)
|
||||
for (i64 dx = 0; dx < r; dx++)
|
||||
if (dx*dx+dy*dy <= r*r) {
|
||||
da_pixel(ac, x+r-1-dx, y+r-1-dy, color);
|
||||
da_pixel(ac, x+w-r+dx, y+r-1-dy, color);
|
||||
da_pixel(ac, x+r-1-dx, y+h-r+dy, color);
|
||||
da_pixel(ac, x+w-r+dx, y+h-r+dy, color);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void da_rect_outline(da_app_context *ac, i64 x, i64 y, i64 w, i64 h, u32 color, i64 radius) {
|
||||
i64 r = radius;
|
||||
if (r > w/2) r = w/2;
|
||||
if (r > h/2) r = h/2;
|
||||
for (i64 c = r; c < w-r; c++) { da_pixel(ac, x+c, y, color); da_pixel(ac, x+c, y+h-1, color); }
|
||||
for (i64 rr = r; rr < h-r; rr++) { da_pixel(ac, x, y+rr, color); da_pixel(ac, x+w-1, y+rr, color); }
|
||||
for (i64 dy = 0; dy <= r; dy++)
|
||||
for (i64 dx = 0; dx <= r; dx++) {
|
||||
i64 d2 = dx*dx+dy*dy;
|
||||
if (d2 <= r*r && d2 > (r-1)*(r-1)) {
|
||||
da_pixel(ac, x+r-dx, y+r-dy, color);
|
||||
da_pixel(ac, x+w-1-r+dx, y+r-dy, color);
|
||||
da_pixel(ac, x+r-dx, y+h-1-r+dy, color);
|
||||
da_pixel(ac, x+w-1-r+dx, y+h-1-r+dy, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ========== ASCII 字体渲染 ==========
|
||||
*
|
||||
* 使用方法:先 #include ascii_bitmaps.c(定义 static g_ascii/g_ascii_w/g_ascii_h),
|
||||
* 再 #include desktop_app.h。da_draw_char/da_draw_string 直接引用 g_ascii。
|
||||
* 与 deshab_ui.h 使用相同的包含顺序约定。
|
||||
*/
|
||||
|
||||
static inline void da_draw_char(da_app_context *ac, char ch, i64 x, i64 y, u32 fg, u32 bg) {
|
||||
u32 idx = (u32)(ch - ' ');
|
||||
if (idx > 94) idx = 0;
|
||||
const u8 *glyph = g_ascii[idx];
|
||||
i64 gw = g_ascii_w, gh = g_ascii_h;
|
||||
for (i64 r = 0; r < gh; r++) {
|
||||
i64 yy = y + r;
|
||||
if (yy < 0 || (u64)yy >= ac->fb_h) continue;
|
||||
u32 *line = (u32 *)((u8 *)ac->fb + (u64)yy * ac->fb_pitch);
|
||||
for (i64 c = 0; c < gw; c++) {
|
||||
u8 a = glyph[r * gw + c];
|
||||
if (a == 0) continue;
|
||||
i64 xx = x + c;
|
||||
if (xx < 0 || (u64)xx >= ac->fb_w) continue;
|
||||
line[xx] = (a == 255) ? fg : da_blend(bg, fg, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void da_draw_string(da_app_context *ac, const char *s, i64 x, i64 y, u32 fg, u32 bg, i64 step) {
|
||||
i64 cx = x;
|
||||
while (*s) { da_draw_char(ac, *s, cx, y, fg, bg); cx += step; s++; }
|
||||
}
|
||||
|
||||
/* ========== 鼠标光标 ========== */
|
||||
#define DA_CURSOR_SIZE 24
|
||||
|
||||
static const u8 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 {
|
||||
u32 save[DA_CURSOR_SIZE * DA_CURSOR_SIZE];
|
||||
int saved;
|
||||
int old_x, old_y;
|
||||
int mx, my;
|
||||
int btn;
|
||||
} da_cursor;
|
||||
|
||||
static inline void da_cursor_init(da_cursor *c, i64 fb_w, i64 fb_h) {
|
||||
for (int i = 0; i < DA_CURSOR_SIZE*DA_CURSOR_SIZE; i++) c->save[i] = 0;
|
||||
c->saved = 0; c->old_x = -1; c->old_y = -1;
|
||||
c->mx = (int)(fb_w / 2); c->my = (int)(fb_h / 2); c->btn = 0;
|
||||
}
|
||||
|
||||
static inline void da_cursor_save(da_app_context *ac, da_cursor *c) {
|
||||
for (int r = 0; r < DA_CURSOR_SIZE; r++)
|
||||
for (int col = 0; col < DA_CURSOR_SIZE; col++) {
|
||||
int x = c->mx + col, y = c->my + r;
|
||||
c->save[r * DA_CURSOR_SIZE + col] = da_pixel_read(ac, x, y);
|
||||
}
|
||||
c->saved = 1; c->old_x = c->mx; c->old_y = c->my;
|
||||
}
|
||||
|
||||
static inline void da_cursor_restore(da_app_context *ac, da_cursor *c) {
|
||||
if (!c->saved) return;
|
||||
for (int r = 0; r < DA_CURSOR_SIZE; r++)
|
||||
for (int col = 0; col < DA_CURSOR_SIZE; col++) {
|
||||
int x = c->old_x + col, y = c->old_y + r;
|
||||
da_pixel(ac, x, y, c->save[r * DA_CURSOR_SIZE + col]);
|
||||
}
|
||||
c->saved = 0;
|
||||
}
|
||||
|
||||
static inline void da_cursor_draw(da_app_context *ac, da_cursor *c, u32 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])
|
||||
da_pixel(ac, c->mx + col, c->my + r, color);
|
||||
}
|
||||
|
||||
/* ========== PS/2 鼠标 ========== */
|
||||
typedef struct {
|
||||
u8 buf[3];
|
||||
int idx;
|
||||
int has_pkt;
|
||||
} da_mouse;
|
||||
|
||||
static inline void da_mouse_init(void) {
|
||||
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
|
||||
outb(0x64, 0xA8);
|
||||
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
|
||||
outb(0x64, 0x20);
|
||||
for (int t = 0; t < 100000; t++) { if (inb(0x64) & 1) break; }
|
||||
u8 cfg = inb(0x60);
|
||||
cfg |= 0x02; cfg &= ~0x20;
|
||||
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
|
||||
outb(0x64, 0x60);
|
||||
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
|
||||
outb(0x60, cfg);
|
||||
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
|
||||
outb(0x64, 0xD4);
|
||||
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
|
||||
outb(0x60, 0xFF);
|
||||
for (int t = 0; t < 100000; t++) { if (inb(0x64) & 1) break; }
|
||||
inb(0x60);
|
||||
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
|
||||
outb(0x64, 0xD4);
|
||||
for (int t = 0; t < 100000; t++) { if (!(inb(0x64) & 2)) break; }
|
||||
outb(0x60, 0xF4);
|
||||
for (int t = 0; t < 100000; t++) { if (inb(0x64) & 1) break; }
|
||||
inb(0x60);
|
||||
}
|
||||
|
||||
static inline int da_mouse_poll(da_mouse *m, da_cursor *c, i64 fb_w, i64 fb_h) {
|
||||
u8 st = inb(0x64);
|
||||
if (!(st & 1)) return 0;
|
||||
u8 data = inb(0x60);
|
||||
if (!(st & 0x20)) return 0;
|
||||
m->buf[m->idx++] = data;
|
||||
if (m->idx < 3) return 0;
|
||||
m->idx = 0;
|
||||
if (!(m->buf[0] & 0x08)) return 0;
|
||||
int dx = (int)(signed char)m->buf[1];
|
||||
int dy = (int)(signed char)m->buf[2];
|
||||
if (m->buf[0] & 0x10) dx = -dx;
|
||||
if (!(m->buf[0] & 0x20)) dy = -dy; else dy = -dy;
|
||||
dy = -dy;
|
||||
c->mx += dx; c->my += dy;
|
||||
if (c->mx < 0) c->mx = 0;
|
||||
if (c->my < 0) c->my = 0;
|
||||
if (c->mx >= (int)fb_w - DA_CURSOR_SIZE) c->mx = (int)fb_w - DA_CURSOR_SIZE;
|
||||
if (c->my >= (int)fb_h - DA_CURSOR_SIZE) c->my = (int)fb_h - DA_CURSOR_SIZE;
|
||||
c->btn = (m->buf[0] & 0x01) ? 1 : 0;
|
||||
m->has_pkt = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ========== 键盘 ========== */
|
||||
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];
|
||||
}
|
||||
|
||||
/* ========== 窗口概念 ========== */
|
||||
/* 独立工具全屏运行时的窗口装饰:标题栏 + 关闭按钮 */
|
||||
|
||||
#define DA_TITLEBAR_H 28
|
||||
#define DA_BORDER_W 2
|
||||
#define DA_STATUSBAR_H 20
|
||||
|
||||
/* 色板引用(与 deshab_ui.h 一致) */
|
||||
#define DA_BG_PRIMARY 0xFF0A1428u /* DP_ABYSS_900 */
|
||||
#define DA_BG_SECONDARY 0xFF0F1E38u /* DP_ABYSS_800 */
|
||||
#define DA_BG_TERTIARY 0xFF162848u /* DP_ABYSS_700 */
|
||||
#define DA_TEXT_PRIMARY 0xFFE8E8F0u /* DP_NEUTRAL_700 */
|
||||
#define DA_TEXT_DIM 0xFF6A6A88u /* DP_NEUTRAL_300 */
|
||||
#define DA_ACCENT 0xFF00A8CCu /* DP_SEAL_500 */
|
||||
#define DA_ACCENT_LIGHT 0xFF44CCF0u /* DP_SEAL_300 */
|
||||
#define DA_BORDER 0xFF284888u /* DP_ABYSS_500 */
|
||||
#define DA_BORDER_FOCUS 0xFF44CCF0u /* DP_SEAL_300 */
|
||||
#define DA_CURSOR_COLOR 0xFF00A8CCu /* DP_SEAL_500 */
|
||||
#define DA_PROMPT_COLOR 0xFF44CCF0u /* DP_SEAL_300 */
|
||||
#define DA_SUCCESS 0xFF40C880u /* DP_SUCCESS */
|
||||
#define DA_ERROR 0xFFFF4466u /* DP_ERROR */
|
||||
#define DA_WARNING 0xFFF0A030u /* DP_WARNING */
|
||||
|
||||
static inline void da_draw_titlebar(da_app_context *ac, const char *title, i64 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, i64 w, i64 h) {
|
||||
i64 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);
|
||||
}
|
||||
|
||||
/* ========== RTC 时钟 ========== */
|
||||
static inline void da_rtc_time(u8 *h, u8 *m) {
|
||||
outb(0x70, 4); *h = inb(0x71);
|
||||
outb(0x70, 2); *m = inb(0x71);
|
||||
*h = (u8)((*h >> 4) * 10 + (*h & 0xF));
|
||||
*m = (u8)((*m >> 4) * 10 + (*m & 0xF));
|
||||
}
|
||||
|
||||
/* ========== memset/memcpy ========== */
|
||||
static inline void *da_memset(void *d, int c, u64 n) { u8 *p=(u8*)d; while(n--)*p++=(u8)c; return d; }
|
||||
static inline void *da_memcpy(void *d, const void *s, u64 n) { u8 *dd=(u8*)d; const u8 *ss=(const u8*)s; while(n--)*dd++=*ss++; return d; }
|
||||
|
||||
#endif /* DESHAB_DESKTOP_APP_H */
|
||||
@@ -0,0 +1,25 @@
|
||||
CC := clang
|
||||
LD := ld.lld
|
||||
|
||||
ROOT := ../../..
|
||||
OUT := $(ROOT)/SYSTEM/system/deshab64/tools/editor.elf
|
||||
|
||||
CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I.. -I../../UTSM/include -O2
|
||||
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
|
||||
|
||||
C_SOURCES := main.c
|
||||
OBJECTS := $(C_SOURCES:.c=.o)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(OUT)
|
||||
|
||||
$(OUT): $(OBJECTS) linker.ld
|
||||
@if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools"
|
||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
|
||||
@@ -0,0 +1,16 @@
|
||||
ENTRY(dsk_entry)
|
||||
|
||||
PHDRS
|
||||
{
|
||||
text PT_LOAD FLAGS(7);
|
||||
rodata PT_LOAD FLAGS(4);
|
||||
data PT_LOAD FLAGS(7);
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x1000;
|
||||
.text : ALIGN(4K) { *(.text*) } :text
|
||||
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
|
||||
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
/* Deshab Editor — 全屏文本编辑器
|
||||
* PS/2 键盘输入,行号 + 光标 + 垂直滚动。
|
||||
* Ctrl+O 打开文件,Ctrl+S 保存(暂不支持),Esc 退出。
|
||||
*/
|
||||
|
||||
#include "../../firstInit/ascii_bitmaps.c"
|
||||
#include "../desktop_app.h"
|
||||
|
||||
#define CHAR_STEP 12
|
||||
#define CHAR_H 18
|
||||
#define EDITOR_BUF_SIZE 65536
|
||||
|
||||
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;
|
||||
|
||||
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 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 (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[64]; p = 0;
|
||||
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++; }
|
||||
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);
|
||||
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) { 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)) {
|
||||
/* Ctrl+O - stub */
|
||||
da_slog("editor", "Ctrl+O - open not yet");
|
||||
continue;
|
||||
}
|
||||
/* Ctrl+S: 保存(提示暂不支持) */
|
||||
if (ctrl_down && (sc == 0x1F)) {
|
||||
da_slog("editor", "Ctrl+S - save not yet");
|
||||
modified = 0;
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
CC := clang
|
||||
LD := ld.lld
|
||||
|
||||
ROOT := ../../..
|
||||
OUT := $(ROOT)/SYSTEM/system/deshab64/tools/fileman.elf
|
||||
|
||||
CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I.. -I../../UTSM/include -O2
|
||||
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
|
||||
|
||||
C_SOURCES := main.c
|
||||
OBJECTS := $(C_SOURCES:.c=.o)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(OUT)
|
||||
|
||||
$(OUT): $(OBJECTS) linker.ld
|
||||
@if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools"
|
||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
|
||||
@@ -0,0 +1,16 @@
|
||||
ENTRY(dsk_entry)
|
||||
|
||||
PHDRS
|
||||
{
|
||||
text PT_LOAD FLAGS(7);
|
||||
rodata PT_LOAD FLAGS(4);
|
||||
data PT_LOAD FLAGS(7);
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x1000;
|
||||
.text : ALIGN(4K) { *(.text*) } :text
|
||||
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
|
||||
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
/* Deshab File Manager — 全屏文件管理器
|
||||
* 文件列表视图,鼠标点击选择,键盘导航。
|
||||
* FAT32 根目录遍历,Esc 退出。
|
||||
*/
|
||||
|
||||
#include "../../firstInit/ascii_bitmaps.c"
|
||||
#include "../desktop_app.h"
|
||||
|
||||
#define CHAR_STEP 12
|
||||
#define CHAR_H 18
|
||||
#define MAX_FILES 256
|
||||
|
||||
static da_app_context g_ac;
|
||||
static da_cursor g_cursor;
|
||||
static da_mouse g_mouse;
|
||||
|
||||
typedef struct {
|
||||
char name[13]; /* 8.3 */
|
||||
int is_dir;
|
||||
u32 size;
|
||||
} file_entry;
|
||||
|
||||
static file_entry files[MAX_FILES];
|
||||
static int file_count = 0;
|
||||
static int selected = 0;
|
||||
static int scroll_off = 0;
|
||||
|
||||
static void load_root_dir(void) {
|
||||
file_count = 0;
|
||||
if (!g_ac.block_read) return;
|
||||
|
||||
u8 bpb[512];
|
||||
if (g_ac.block_read(0, 0, 1, bpb) != 0) return;
|
||||
if (bpb[510] != 0x55 || bpb[511] != 0xAA) return;
|
||||
|
||||
u32 root_cluster = *(u32 *)(bpb + 44);
|
||||
u8 sectors_per_cluster = bpb[13];
|
||||
u32 fat_start = *(u16 *)(bpb + 14);
|
||||
u32 data_start = fat_start + (*(u16 *)(bpb + 22)) * 2;
|
||||
|
||||
u8 dir_buf[8192];
|
||||
u32 lba = data_start + (root_cluster - 2) * sectors_per_cluster;
|
||||
if (g_ac.block_read(0, lba, sectors_per_cluster, dir_buf) != 0) return;
|
||||
|
||||
for (int i = 0; i < (int)(sectors_per_cluster * 512) && file_count < MAX_FILES; i += 32) {
|
||||
u8 first = dir_buf[i];
|
||||
if (first == 0x00) break;
|
||||
if (first == 0xE5) continue;
|
||||
if (dir_buf[i+11] & 0x08) continue;
|
||||
if (dir_buf[i+11] & 0x10) continue; /* 暂跳过目录 */
|
||||
|
||||
file_entry *f = &files[file_count];
|
||||
f->is_dir = (dir_buf[i+11] & 0x10) ? 1 : 0;
|
||||
f->size = *(u32 *)(dir_buf + i + 28);
|
||||
int p = 0;
|
||||
for (int j = 0; j < 8 && dir_buf[i+j] != ' '; j++)
|
||||
f->name[p++] = (char)dir_buf[i+j];
|
||||
if (dir_buf[i+8] != ' ') {
|
||||
f->name[p++] = '.';
|
||||
for (int j = 8; j < 11 && dir_buf[i+j] != ' '; j++)
|
||||
f->name[p++] = (char)dir_buf[i+j];
|
||||
}
|
||||
f->name[p] = 0;
|
||||
file_count++;
|
||||
}
|
||||
}
|
||||
|
||||
static void redraw_all(void) {
|
||||
da_fill_bg(&g_ac, DA_BG_PRIMARY);
|
||||
da_draw_titlebar(&g_ac, "Files", (i64)g_ac.fb_w);
|
||||
|
||||
int margin = 12;
|
||||
int top_y = DA_TITLEBAR_H + 4;
|
||||
int row_h = CHAR_H + 6;
|
||||
int visible_rows = ((int)g_ac.fb_h - top_y - DA_STATUSBAR_H - 8) / row_h;
|
||||
int col_name_x = margin + 32;
|
||||
int col_size_x = (int)g_ac.fb_w - 120;
|
||||
int col_type_x = (int)g_ac.fb_w - 60;
|
||||
|
||||
/* 列标题 */
|
||||
da_draw_string(&g_ac, "Name", col_name_x, top_y, DA_TEXT_DIM, DA_BG_PRIMARY, CHAR_STEP);
|
||||
da_draw_string(&g_ac, "Size", col_size_x, top_y, DA_TEXT_DIM, DA_BG_PRIMARY, CHAR_STEP);
|
||||
da_draw_string(&g_ac, "Type", col_type_x, top_y, DA_TEXT_DIM, DA_BG_PRIMARY, CHAR_STEP);
|
||||
da_fill_rect(&g_ac, margin, top_y + CHAR_H, (i64)g_ac.fb_w - margin * 2, 1, DA_BORDER);
|
||||
|
||||
/* 文件列表 */
|
||||
if (scroll_off < 0) scroll_off = 0;
|
||||
if (scroll_off > file_count - visible_rows && file_count > visible_rows) scroll_off = file_count - visible_rows;
|
||||
if (scroll_off < 0) scroll_off = 0;
|
||||
|
||||
for (int i = 0; i < visible_rows; i++) {
|
||||
int idx = scroll_off + i;
|
||||
if (idx >= file_count) break;
|
||||
int y = top_y + CHAR_H + 6 + i * row_h;
|
||||
|
||||
/* 选中高亮 */
|
||||
if (idx == selected) {
|
||||
da_fill_rect(&g_ac, margin, y - 2, (i64)g_ac.fb_w - margin * 2, row_h, DA_BG_TERTIARY);
|
||||
da_rect_outline(&g_ac, margin, y - 2, (i64)g_ac.fb_w - margin * 2, row_h, DA_ACCENT, 4);
|
||||
}
|
||||
|
||||
/* 图标 */
|
||||
u32 icon_color = files[idx].is_dir ? DA_WARNING : DA_ACCENT;
|
||||
da_fill_rounded_rect(&g_ac, margin + 4, y, 24, 16, icon_color, 3);
|
||||
|
||||
/* 文件名 */
|
||||
u32 name_color = (idx == selected) ? DA_ACCENT_LIGHT : DA_TEXT_PRIMARY;
|
||||
da_draw_string(&g_ac, files[idx].name, col_name_x, y, name_color,
|
||||
(idx == selected) ? DA_BG_TERTIARY : DA_BG_PRIMARY, CHAR_STEP);
|
||||
|
||||
/* 大小 */
|
||||
if (!files[idx].is_dir) {
|
||||
char sz[16]; int p = 0;
|
||||
u32 s = files[idx].size;
|
||||
if (s == 0) { sz[p++] = '0'; }
|
||||
else { char tmp[12]; int tl = 0; while(s>0){tmp[tl++]='0'+(s%10);s/=10;} for(int j=tl-1;j>=0;j--)sz[p++]=tmp[j]; }
|
||||
sz[p++] = ' '; sz[p++] = 'B'; sz[p] = 0;
|
||||
da_draw_string(&g_ac, sz, col_size_x, y, DA_TEXT_DIM,
|
||||
(idx == selected) ? DA_BG_TERTIARY : DA_BG_PRIMARY, CHAR_STEP);
|
||||
} else {
|
||||
da_draw_string(&g_ac, "--", col_size_x, y, DA_TEXT_DIM,
|
||||
(idx == selected) ? DA_BG_TERTIARY : DA_BG_PRIMARY, CHAR_STEP);
|
||||
}
|
||||
|
||||
/* 类型 */
|
||||
const char *type = files[idx].is_dir ? "DIR" : "FILE";
|
||||
da_draw_string(&g_ac, type, col_type_x, y, DA_TEXT_DIM,
|
||||
(idx == selected) ? DA_BG_TERTIARY : DA_BG_PRIMARY, CHAR_STEP);
|
||||
}
|
||||
|
||||
if (file_count == 0) {
|
||||
da_draw_string(&g_ac, "No files found (empty or no block device)",
|
||||
margin + 20, top_y + 60, DA_TEXT_DIM, DA_BG_PRIMARY, CHAR_STEP);
|
||||
}
|
||||
|
||||
/* 状态栏 */
|
||||
char status[64]; int p = 0;
|
||||
const char *p1 = "/ "; while (*p1) status[p++] = *p1++;
|
||||
status[p++] = '0' + file_count / 10; status[p++] = '0' + file_count % 10;
|
||||
const char *p2 = " items"; while (*p2) status[p++] = *p2++;
|
||||
status[p] = 0;
|
||||
da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
|
||||
|
||||
/* 鼠标光标 */
|
||||
da_cursor_save(&g_ac, &g_cursor);
|
||||
da_cursor_draw(&g_ac, &g_cursor, DA_CURSOR_COLOR);
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
void dsk_entry(const da_boot_context *ctx) {
|
||||
__asm__ volatile("cli");
|
||||
da_slog("fileman", "boot");
|
||||
|
||||
if (!ctx || ctx->magic != DA_BOOT_MAGIC) {
|
||||
da_slog("fileman", "bad context");
|
||||
for(;;) __asm__("hlt");
|
||||
}
|
||||
|
||||
da_init(&g_ac, ctx);
|
||||
da_cursor_init(&g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
|
||||
da_mouse_init();
|
||||
|
||||
load_root_dir();
|
||||
redraw_all();
|
||||
|
||||
int e0 = 0, shift = 0; (void)shift;
|
||||
for (;;) {
|
||||
int need_redraw = 0;
|
||||
|
||||
/* 鼠标轮询 */
|
||||
if (da_mouse_poll(&g_mouse, &g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h)) {
|
||||
need_redraw = 1;
|
||||
if (g_cursor.btn) {
|
||||
/* 点击选择文件 */
|
||||
int margin = 12;
|
||||
int top_y = DA_TITLEBAR_H + 4;
|
||||
int row_h = CHAR_H + 6;
|
||||
int click_y = g_cursor.my - top_y - CHAR_H - 6;
|
||||
if (click_y >= 0) {
|
||||
int idx = click_y / row_h + scroll_off;
|
||||
if (idx >= 0 && idx < file_count) {
|
||||
selected = idx;
|
||||
need_redraw = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 键盘轮询 */
|
||||
u8 st = inb(0x64);
|
||||
if ((st & 1) && !(st & 0x20)) {
|
||||
u8 data = inb(0x60);
|
||||
u8 sc = data;
|
||||
if (sc == 0xE0) { e0 = 1; goto skip; }
|
||||
if (sc == 0x2A || sc == 0x36) { shift = 1; goto skip; }
|
||||
if (sc == 0xAA || sc == 0xB6) { shift = 0; goto skip; }
|
||||
if (sc & 0x80) { e0 = 0; goto skip; }
|
||||
|
||||
if (sc == 0x01) { da_slog("fileman", "exit"); return; }
|
||||
|
||||
if (e0) {
|
||||
if (sc == 0x48) { /* Up */
|
||||
if (selected > 0) selected--;
|
||||
need_redraw = 1;
|
||||
} else if (sc == 0x50) { /* Down */
|
||||
if (selected < file_count - 1) selected++;
|
||||
need_redraw = 1;
|
||||
}
|
||||
e0 = 0;
|
||||
goto skip;
|
||||
}
|
||||
|
||||
if (sc == 0x1C) { /* Enter - 打开 */
|
||||
if (selected >= 0 && selected < file_count) {
|
||||
da_slog("fileman", "open file (stub)");
|
||||
}
|
||||
need_redraw = 1;
|
||||
} else if (sc == 0x0E) { /* Backspace - 返回上级 */
|
||||
da_slog("fileman", "back (root only)");
|
||||
need_redraw = 1;
|
||||
}
|
||||
}
|
||||
skip:
|
||||
if (need_redraw) {
|
||||
da_cursor_restore(&g_ac, &g_cursor);
|
||||
redraw_all();
|
||||
}
|
||||
__asm__("pause");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
CC := clang
|
||||
LD := ld.lld
|
||||
|
||||
ROOT := ../../..
|
||||
OUT := $(ROOT)/SYSTEM/system/deshab64/tools/ping.elf
|
||||
|
||||
CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I.. -I../../UTSM/include -O2
|
||||
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
|
||||
|
||||
C_SOURCES := main.c
|
||||
OBJECTS := $(C_SOURCES:.c=.o)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(OUT)
|
||||
|
||||
$(OUT): $(OBJECTS) linker.ld
|
||||
@if not exist "$(ROOT)\SYSTEM\system\deshab64\tools" mkdir "$(ROOT)\SYSTEM\system\deshab64\tools"
|
||||
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
|
||||
@@ -0,0 +1,16 @@
|
||||
ENTRY(dsk_entry)
|
||||
|
||||
PHDRS
|
||||
{
|
||||
text PT_LOAD FLAGS(7);
|
||||
rodata PT_LOAD FLAGS(4);
|
||||
data PT_LOAD FLAGS(7);
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x1000;
|
||||
.text : ALIGN(4K) { *(.text*) } :text
|
||||
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
|
||||
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
|
||||
}
|
||||
@@ -0,0 +1,339 @@
|
||||
/* Deshab ping — 终端风格 ICMP Echo 工具 stub
|
||||
* 命令行输入 IP 地址,发送 ICMP Echo Request。
|
||||
* 当前为 stub,提示需要 TCP/IP 栈支持。
|
||||
* Esc 键退出返回 DSK。
|
||||
*/
|
||||
|
||||
#include "../../firstInit/ascii_bitmaps.c"
|
||||
#include "../desktop_app.h"
|
||||
|
||||
/* ---- 终端参数 ---- */
|
||||
#define TERM_COLS 120
|
||||
#define TERM_ROWS 40
|
||||
#define TERM_MAX_CHARS (TERM_COLS * TERM_ROWS)
|
||||
#define CHAR_STEP 12
|
||||
#define CHAR_H 18
|
||||
#define PROMPT_STR "ping> "
|
||||
|
||||
static da_app_context g_ac;
|
||||
|
||||
/* 终端缓冲区 */
|
||||
static u8 term_ch[TERM_MAX_CHARS];
|
||||
static u32 term_fg[TERM_MAX_CHARS];
|
||||
static int term_w = TERM_COLS;
|
||||
static int term_h = TERM_ROWS;
|
||||
static int cur_col = 0;
|
||||
static int cur_row = 0;
|
||||
|
||||
/* 输入缓冲 */
|
||||
static char input_buf[256];
|
||||
static int input_len = 0;
|
||||
static int input_cursor = 0;
|
||||
|
||||
/* 命令历史 */
|
||||
#define HIST_SIZE 16
|
||||
static char history[HIST_SIZE][256];
|
||||
static int hist_count = 0;
|
||||
static int hist_idx = 0;
|
||||
|
||||
/* 坐标 */
|
||||
static int term_x, term_y;
|
||||
|
||||
static void term_init_layout(void) {
|
||||
term_x = 8;
|
||||
term_y = DA_TITLEBAR_H + 4;
|
||||
int avail_w = (int)g_ac.fb_w - 16;
|
||||
int avail_h = (int)g_ac.fb_h - term_y - DA_STATUSBAR_H - 8;
|
||||
term_w = avail_w / CHAR_STEP;
|
||||
term_h = avail_h / CHAR_H;
|
||||
if (term_w < 10) term_w = 10;
|
||||
if (term_h < 5) term_h = 5;
|
||||
if (term_w > TERM_COLS) term_w = TERM_COLS;
|
||||
if (term_h > TERM_ROWS) term_h = TERM_ROWS;
|
||||
}
|
||||
|
||||
static void term_clear(void) {
|
||||
for (int i = 0; i < TERM_MAX_CHARS; i++) {
|
||||
term_ch[i] = ' ';
|
||||
term_fg[i] = DA_TEXT_PRIMARY;
|
||||
}
|
||||
cur_col = 0; cur_row = 0;
|
||||
}
|
||||
|
||||
static void term_scroll(void) {
|
||||
for (int r = 1; r < term_h; r++) {
|
||||
for (int c = 0; c < term_w; c++) {
|
||||
term_ch[(r-1)*TERM_COLS+c] = term_ch[r*TERM_COLS+c];
|
||||
term_fg[(r-1)*TERM_COLS+c] = term_fg[r*TERM_COLS+c];
|
||||
}
|
||||
}
|
||||
for (int c = 0; c < term_w; c++) {
|
||||
term_ch[(term_h-1)*TERM_COLS+c] = ' ';
|
||||
term_fg[(term_h-1)*TERM_COLS+c] = DA_TEXT_PRIMARY;
|
||||
}
|
||||
cur_row = term_h - 1; cur_col = 0;
|
||||
}
|
||||
|
||||
static void term_newline(void) {
|
||||
cur_col = 0; cur_row++;
|
||||
if (cur_row >= term_h) term_scroll();
|
||||
}
|
||||
|
||||
static void term_putc_color(char c, u32 color) {
|
||||
if (c == '\n') { term_newline(); return; }
|
||||
if (c == '\r') { cur_col = 0; return; }
|
||||
if (c == '\t') { int sp = 4 - (cur_col % 4); for (int i = 0; i < sp; i++) term_putc_color(' ', color); return; }
|
||||
if (c < ' ' || c > '~') c = '?';
|
||||
if (cur_col >= term_w) term_newline();
|
||||
int idx = cur_row * TERM_COLS + cur_col;
|
||||
term_ch[idx] = (u8)c; term_fg[idx] = color;
|
||||
cur_col++;
|
||||
}
|
||||
|
||||
static void term_putc(char c) { term_putc_color(c, DA_TEXT_PRIMARY); }
|
||||
static void term_puts_color(const char *s, u32 color) { while (*s) term_putc_color(*s++, color); }
|
||||
static void term_puts(const char *s) { term_puts_color(s, DA_TEXT_PRIMARY); }
|
||||
|
||||
/* ---- 绘制 ---- */
|
||||
static void redraw_all(void) {
|
||||
da_fill_bg(&g_ac, DA_BG_PRIMARY);
|
||||
da_draw_titlebar(&g_ac, "Ping", (i64)g_ac.fb_w);
|
||||
/* 终端字符 */
|
||||
for (int r = 0; r < term_h; r++) {
|
||||
for (int c = 0; c < term_w; c++) {
|
||||
int idx = r * TERM_COLS + c;
|
||||
if (term_ch[idx] == ' ') continue;
|
||||
da_draw_char(&g_ac, term_ch[idx],
|
||||
term_x + c * CHAR_STEP, term_y + r * CHAR_H,
|
||||
term_fg[idx], DA_BG_PRIMARY);
|
||||
}
|
||||
}
|
||||
/* 光标 */
|
||||
if (cur_col < term_w && cur_row < term_h) {
|
||||
da_fill_rect(&g_ac, term_x + cur_col * CHAR_STEP,
|
||||
term_y + cur_row * CHAR_H + CHAR_H - 3, 11, 2, DA_ACCENT);
|
||||
}
|
||||
/* 状态栏 */
|
||||
char status[64];
|
||||
int p = 0;
|
||||
const char *s1 = "ICMP Echo | Esc 退出 ";
|
||||
while (*s1) status[p++] = *s1++;
|
||||
u8 hh, mm; da_rtc_time(&hh, &mm);
|
||||
status[p++] = '0' + hh/10; status[p++] = '0' + hh%10;
|
||||
status[p++] = ':'; status[p++] = '0' + mm/10; status[p++] = '0' + mm%10;
|
||||
status[p] = 0;
|
||||
da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
|
||||
}
|
||||
|
||||
/* ---- IP 格式校验 ---- */
|
||||
static int is_valid_ip(const char *s) {
|
||||
int dots = 0, digits = 0;
|
||||
while (*s) {
|
||||
if (*s == '.') { dots++; digits = 0; }
|
||||
else if (*s >= '0' && *s <= '9') { digits++; if (digits > 3) return 0; }
|
||||
else return 0;
|
||||
s++;
|
||||
}
|
||||
return dots == 3;
|
||||
}
|
||||
|
||||
/* ---- Ping 模拟 ---- */
|
||||
static void do_ping(const char *ip) {
|
||||
term_puts_color("PING ", DA_PROMPT_COLOR);
|
||||
term_puts_color(ip, DA_ACCENT_LIGHT);
|
||||
term_puts_color(" 56(84) bytes of data.\n", DA_TEXT_DIM);
|
||||
|
||||
/* stub: 检查 kernel_api 是否有 net 接口 */
|
||||
if (!g_ac.kernel_api) {
|
||||
term_puts_color(" [错误] 无内核 API\n", DA_ERROR);
|
||||
return;
|
||||
}
|
||||
u64 net_api = *(u64 *)((u64)g_ac.kernel_api + 0xC0);
|
||||
if (!net_api) {
|
||||
term_puts_color(" [错误] 需要 TCP/IP 栈支持\n", DA_ERROR);
|
||||
term_puts_color(" 当前内核未实现 ICMP 协议栈。\n", DA_WARNING);
|
||||
term_puts_color(" 请等待后续版本更新。\n", DA_TEXT_DIM);
|
||||
term_putc('\n');
|
||||
term_puts_color("--- ", DA_TEXT_DIM);
|
||||
term_puts_color(ip, DA_TEXT_DIM);
|
||||
term_puts_color(" ping statistics ---\n", DA_TEXT_DIM);
|
||||
term_puts_color(" 0 packets transmitted, 0 received, 100% packet loss\n", DA_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 如果有 net API,此处应实现真正的 ICMP Echo */
|
||||
term_puts_color(" [stub] 网络功能尚未实现\n", DA_WARNING);
|
||||
}
|
||||
|
||||
/* ---- 内置命令 ---- */
|
||||
static int str_eq(const char *a, const char *b) {
|
||||
while (*a && *b) { if (*a != *b) return 0; a++; b++; }
|
||||
return *a == *b;
|
||||
}
|
||||
|
||||
static void cmd_help(void) {
|
||||
term_puts_color("ping — ICMP Echo 请求工具\n", DA_PROMPT_COLOR);
|
||||
term_puts(" <IP> 发送 ICMP Echo Request\n");
|
||||
term_puts(" help 显示此帮助\n");
|
||||
term_puts(" clear 清屏\n");
|
||||
term_puts(" version 显示版本\n");
|
||||
term_puts(" Esc 退出\n");
|
||||
}
|
||||
|
||||
static void cmd_clear(void) { term_clear(); }
|
||||
|
||||
static void cmd_version(void) {
|
||||
term_puts_color("Deshab ping v0.1 (stub)\n", DA_SUCCESS);
|
||||
term_puts_color(" ICMP Echo 功能需要 TCP/IP 栈支持\n", DA_TEXT_DIM);
|
||||
}
|
||||
|
||||
static void execute_command(const char *cmd) {
|
||||
while (*cmd == ' ') cmd++;
|
||||
if (*cmd == 0) return;
|
||||
|
||||
if (str_eq(cmd, "help") || str_eq(cmd, "?")) { cmd_help(); return; }
|
||||
if (str_eq(cmd, "clear") || str_eq(cmd, "cls")) { cmd_clear(); return; }
|
||||
if (str_eq(cmd, "version") || str_eq(cmd, "ver")) { cmd_version(); return; }
|
||||
|
||||
/* 非内置命令视为 IP 地址 */
|
||||
if (is_valid_ip(cmd)) {
|
||||
do_ping(cmd);
|
||||
} else {
|
||||
term_puts_color("无效 IP 地址: ", DA_ERROR);
|
||||
term_puts_color(cmd, DA_ERROR);
|
||||
term_putc('\n');
|
||||
term_puts_color("请输入有效的 IPv4 地址 (如 192.168.1.1)\n", DA_TEXT_DIM);
|
||||
}
|
||||
}
|
||||
|
||||
static void add_history(const char *cmd) {
|
||||
if (hist_count < HIST_SIZE) {
|
||||
int i;
|
||||
for (i = 0; cmd[i] && i < 255; i++) history[hist_count][i] = cmd[i];
|
||||
history[hist_count][i] = 0;
|
||||
hist_count++;
|
||||
}
|
||||
hist_idx = hist_count;
|
||||
}
|
||||
|
||||
/* ---- 输入行重绘 ---- */
|
||||
static void redraw_input_line(void) {
|
||||
int plen = 0;
|
||||
while (PROMPT_STR[plen]) plen++;
|
||||
i64 y = term_y + (i64)cur_row * CHAR_H;
|
||||
da_fill_rect(&g_ac, term_x, y, (i64)term_w * CHAR_STEP, CHAR_H, DA_BG_PRIMARY);
|
||||
for (int i = 0; i < plen; i++)
|
||||
da_draw_char(&g_ac, PROMPT_STR[i], term_x + i * CHAR_STEP, y, DA_PROMPT_COLOR, DA_BG_PRIMARY);
|
||||
for (int i = 0; i < input_len; i++)
|
||||
da_draw_char(&g_ac, input_buf[i], term_x + (plen + i) * CHAR_STEP, y, DA_TEXT_PRIMARY, DA_BG_PRIMARY);
|
||||
cur_col = plen + input_cursor;
|
||||
da_fill_rect(&g_ac, term_x + cur_col * CHAR_STEP, y + CHAR_H - 3, 11, 2, DA_ACCENT);
|
||||
}
|
||||
|
||||
/* ---- 主入口 ---- */
|
||||
__attribute__((visibility("default")))
|
||||
void dsk_entry(const da_boot_context *ctx) {
|
||||
__asm__ volatile("cli");
|
||||
da_slog("ping", "boot");
|
||||
|
||||
if (!ctx || ctx->magic != DA_BOOT_MAGIC) {
|
||||
da_slog("ping", "bad context");
|
||||
for(;;) __asm__("hlt");
|
||||
}
|
||||
|
||||
da_init(&g_ac, ctx);
|
||||
term_init_layout();
|
||||
term_clear();
|
||||
|
||||
/* 启动 banner */
|
||||
term_puts_color("=== Deshab ping v0.1 (stub) ===\n", DA_ACCENT);
|
||||
term_puts_color("ICMP Echo 请求工具\n", DA_SUCCESS);
|
||||
term_puts_color("输入 IP 地址发送 ping | 输入 'help' 查看帮助 | Esc 退出\n", DA_TEXT_DIM);
|
||||
term_puts_color("注意: 当前需要 TCP/IP 栈支持\n", DA_WARNING);
|
||||
term_putc('\n');
|
||||
|
||||
redraw_all();
|
||||
|
||||
int shift = 0, e0 = 0;
|
||||
for (;;) {
|
||||
term_puts_color(PROMPT_STR, DA_PROMPT_COLOR);
|
||||
input_len = 0; input_cursor = 0; input_buf[0] = 0;
|
||||
int plen = 0; while (PROMPT_STR[plen]) plen++;
|
||||
cur_col = plen;
|
||||
redraw_all();
|
||||
|
||||
int cmd_done = 0;
|
||||
while (!cmd_done) {
|
||||
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 & 0x80) { e0 = 0; continue; }
|
||||
|
||||
/* Esc 退出 */
|
||||
if (sc == 0x01 && !e0) {
|
||||
da_slog("ping", "exit");
|
||||
return;
|
||||
}
|
||||
|
||||
if (e0) {
|
||||
if (sc == 0x4B) { if (input_cursor > 0) input_cursor--; }
|
||||
else if (sc == 0x4D) { if (input_cursor < input_len) input_cursor++; }
|
||||
else if (sc == 0x47) { input_cursor = 0; }
|
||||
else if (sc == 0x4F) { input_cursor = input_len; }
|
||||
else if (sc == 0x48) {
|
||||
if (hist_idx > 0) {
|
||||
hist_idx--;
|
||||
int i; for (i = 0; history[hist_idx][i] && i < 255; i++) input_buf[i] = history[hist_idx][i];
|
||||
input_len = i; input_cursor = i; input_buf[i] = 0;
|
||||
}
|
||||
}
|
||||
else if (sc == 0x50) {
|
||||
if (hist_idx < hist_count - 1) {
|
||||
hist_idx++;
|
||||
int i; for (i = 0; history[hist_idx][i] && i < 255; i++) input_buf[i] = history[hist_idx][i];
|
||||
input_len = i; input_cursor = i; input_buf[i] = 0;
|
||||
} else if (hist_idx < hist_count) {
|
||||
hist_idx = hist_count;
|
||||
input_len = 0; input_cursor = 0; input_buf[0] = 0;
|
||||
}
|
||||
}
|
||||
e0 = 0;
|
||||
redraw_input_line();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sc == 0x1C) { /* Enter */
|
||||
input_buf[input_len] = 0;
|
||||
term_putc('\n');
|
||||
if (input_len > 0) add_history(input_buf);
|
||||
execute_command(input_buf);
|
||||
cmd_done = 1;
|
||||
continue;
|
||||
}
|
||||
if (sc == 0x0E) { /* Backspace */
|
||||
if (input_cursor > 0) {
|
||||
for (int i = input_cursor - 1; i < input_len - 1; i++)
|
||||
input_buf[i] = input_buf[i+1];
|
||||
input_len--; input_cursor--;
|
||||
input_buf[input_len] = 0;
|
||||
}
|
||||
redraw_input_line();
|
||||
continue;
|
||||
}
|
||||
char c = da_scan_to_ascii(sc, shift);
|
||||
if (c && c >= 32 && c <= 126 && input_len < 255) {
|
||||
for (int i = input_len; i > input_cursor; i--)
|
||||
input_buf[i] = input_buf[i-1];
|
||||
input_buf[input_cursor++] = c;
|
||||
input_len++;
|
||||
redraw_input_line();
|
||||
}
|
||||
}
|
||||
redraw_all();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user