2026-07-28 12:00:02 +08:00
|
|
|
|
/* Deshab Browser — 全屏浏览器
|
|
|
|
|
|
* 接入 net_stack.h 共享协议栈,实现 HTTP GET 与简单内容显示。
|
|
|
|
|
|
* 地址栏输入 URL(如 http://10.0.2.2:8000/ 或 10.0.2.2),
|
|
|
|
|
|
* Enter 发起请求,内容区显示 HTTP 响应(分离 header/body,移除 HTML 标签)。
|
|
|
|
|
|
* Esc 退出。
|
2026-07-03 21:13:04 +08:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include "../../firstInit/ascii_bitmaps.c"
|
|
|
|
|
|
#include "../desktop_app.h"
|
2026-07-28 12:00:02 +08:00
|
|
|
|
#include "../net_stack.h"
|
2026-07-03 21:13:04 +08:00
|
|
|
|
|
|
|
|
|
|
#define CHAR_STEP 12
|
|
|
|
|
|
#define CHAR_H 18
|
2026-07-28 12:00:02 +08:00
|
|
|
|
#define URL_BUF_SIZE 128
|
|
|
|
|
|
#define CONTENT_BUF_SIZE 32768
|
|
|
|
|
|
#define DISPLAY_LINES 200
|
|
|
|
|
|
|
|
|
|
|
|
/* 前向声明(do_fetch 引用 redraw_all_imm 和 br_ns_init) */
|
|
|
|
|
|
static void redraw_all_imm(void);
|
|
|
|
|
|
static int br_ns_init(void);
|
2026-07-03 21:13:04 +08:00
|
|
|
|
|
|
|
|
|
|
static da_app_context g_ac;
|
|
|
|
|
|
static da_cursor g_cursor;
|
|
|
|
|
|
static da_mouse g_mouse;
|
|
|
|
|
|
|
2026-07-28 12:00:02 +08:00
|
|
|
|
/* 地址栏 */
|
|
|
|
|
|
static char url_buf[URL_BUF_SIZE] = "http://10.0.2.2:8000/";
|
|
|
|
|
|
static int url_len = 22;
|
|
|
|
|
|
static int url_focused = 1; /* 启动时地址栏聚焦 */
|
|
|
|
|
|
|
|
|
|
|
|
/* 内容缓冲 */
|
|
|
|
|
|
static u8 content_buf[CONTENT_BUF_SIZE];
|
|
|
|
|
|
static u32 content_len = 0;
|
|
|
|
|
|
static int content_scroll = 0;
|
|
|
|
|
|
static int fetching = 0; /* 1=正在请求 */
|
|
|
|
|
|
static char status_msg[64] = "Ready";
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- 状态消息 ---- */
|
|
|
|
|
|
static void br_set_status(const char *s) {
|
|
|
|
|
|
int i = 0;
|
|
|
|
|
|
while (*s && i < 63) status_msg[i++] = *s++;
|
|
|
|
|
|
status_msg[i] = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- HTTP 请求(复制 shell cmd_curl 核心逻辑) ---- */
|
|
|
|
|
|
static void do_fetch(void) {
|
|
|
|
|
|
if (url_len == 0) { br_set_status("Empty URL"); return; }
|
|
|
|
|
|
|
|
|
|
|
|
br_set_status("Initializing net...");
|
|
|
|
|
|
redraw_all_imm();
|
|
|
|
|
|
|
|
|
|
|
|
if (br_ns_init() != 0) {
|
|
|
|
|
|
br_set_status("Net init failed");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 解析 URL: [http://]host[:port][/path] */
|
|
|
|
|
|
const char *p = url_buf;
|
|
|
|
|
|
if (p[0]=='h'&&p[1]=='t'&&p[2]=='t'&&p[3]=='p'&&p[4]==':'&&p[5]=='/'&&p[6]=='/') p += 7;
|
|
|
|
|
|
|
|
|
|
|
|
char host[64]; int hl = 0;
|
|
|
|
|
|
while (*p && *p != ':' && *p != '/' && hl < 63) host[hl++] = *p++;
|
|
|
|
|
|
host[hl] = 0;
|
|
|
|
|
|
if (!hl) { br_set_status("Bad host"); return; }
|
|
|
|
|
|
|
|
|
|
|
|
u16 port = 80;
|
|
|
|
|
|
if (*p == ':') {
|
|
|
|
|
|
p++;
|
|
|
|
|
|
u32 pv = 0; int digits = 0;
|
|
|
|
|
|
while (*p >= '0' && *p <= '9') { pv = pv * 10 + (u32)(*p - '0'); p++; digits++; }
|
|
|
|
|
|
if (digits && pv <= 65535) port = (u16)pv;
|
|
|
|
|
|
}
|
|
|
|
|
|
char path[128]; int pl = 0;
|
|
|
|
|
|
if (*p == '/') {
|
|
|
|
|
|
while (*p && *p != ' ' && pl < 127) path[pl++] = *p++;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!pl) { path[0] = '/'; pl = 1; }
|
|
|
|
|
|
path[pl] = 0;
|
|
|
|
|
|
|
|
|
|
|
|
/* DNS 解析 */
|
|
|
|
|
|
br_set_status("Resolving...");
|
|
|
|
|
|
redraw_all_imm();
|
|
|
|
|
|
u32 ip;
|
|
|
|
|
|
if (ns_dns_resolve(host, &ip) != 0) {
|
|
|
|
|
|
br_set_status("DNS failed");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* TCP 连接 */
|
|
|
|
|
|
br_set_status("Connecting...");
|
|
|
|
|
|
redraw_all_imm();
|
|
|
|
|
|
if (ns_tcp_connect(ip, port, 3000) != 0) {
|
|
|
|
|
|
br_set_status("TCP connect failed");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 构造 HTTP/1.0 请求 */
|
|
|
|
|
|
static char req[512];
|
|
|
|
|
|
int r = 0;
|
|
|
|
|
|
const char *m = "GET "; while (*m) req[r++] = *m++;
|
|
|
|
|
|
for (int i = 0; i < pl; i++) req[r++] = path[i];
|
|
|
|
|
|
m = " HTTP/1.0\r\nHost: "; while (*m) req[r++] = *m++;
|
|
|
|
|
|
for (int i = 0; i < hl; i++) req[r++] = host[i];
|
|
|
|
|
|
m = "\r\nConnection: close\r\n\r\n"; while (*m) req[r++] = *m++;
|
|
|
|
|
|
req[r] = 0;
|
|
|
|
|
|
|
|
|
|
|
|
br_set_status("Sending request...");
|
|
|
|
|
|
redraw_all_imm();
|
|
|
|
|
|
if (ns_tcp_send((const u8 *)req, (u32)r) != 0) {
|
|
|
|
|
|
br_set_status("Send failed");
|
|
|
|
|
|
ns_tcp_close();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
br_set_status("Receiving...");
|
|
|
|
|
|
redraw_all_imm();
|
|
|
|
|
|
int n = ns_tcp_recv(0, 0, 5000);
|
|
|
|
|
|
ns_tcp_close();
|
|
|
|
|
|
|
|
|
|
|
|
if (n < 0) { br_set_status("Connection reset"); return; }
|
|
|
|
|
|
if (n == 0) { br_set_status("No response (timeout)"); return; }
|
|
|
|
|
|
|
|
|
|
|
|
/* 复制响应到 content_buf */
|
|
|
|
|
|
u32 total = ns_tcp_rx_len;
|
|
|
|
|
|
u32 copy = (total < CONTENT_BUF_SIZE) ? total : CONTENT_BUF_SIZE;
|
|
|
|
|
|
for (u32 i = 0; i < copy; i++) content_buf[i] = ns_tcp_rx[i];
|
|
|
|
|
|
content_len = copy;
|
|
|
|
|
|
content_scroll = 0;
|
|
|
|
|
|
|
|
|
|
|
|
char sm[32]; int sp = 0;
|
|
|
|
|
|
const char *ok = "OK "; while (*ok && sp < 30) sm[sp++] = *ok++;
|
|
|
|
|
|
char nb[12]; ns_u32_dec(nb, copy);
|
|
|
|
|
|
for (int i = 0; nb[i] && sp < 30; i++) sm[sp++] = nb[i];
|
|
|
|
|
|
sm[sp] = 0;
|
|
|
|
|
|
br_set_status(sm);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- 极简 HTML 文本提取:移除 <...> 标签,保留纯文本 ---- */
|
|
|
|
|
|
static int html_to_text(const u8 *in, u32 in_len, u8 *out, u32 out_cap) {
|
|
|
|
|
|
u32 oi = 0;
|
|
|
|
|
|
int in_tag = 0;
|
|
|
|
|
|
int prev_space = 0;
|
|
|
|
|
|
for (u32 i = 0; i < in_len && oi < out_cap - 1; i++) {
|
|
|
|
|
|
u8 c = in[i];
|
|
|
|
|
|
if (c == '<') { in_tag = 1; continue; }
|
|
|
|
|
|
if (c == '>') { in_tag = 0; continue; }
|
|
|
|
|
|
if (in_tag) continue;
|
|
|
|
|
|
/* 跳过 HTTP header(找到 \r\n\r\n 后才开始) */
|
|
|
|
|
|
/* 将 等实体简化为空格 */
|
|
|
|
|
|
if (c == '\r') continue;
|
|
|
|
|
|
if (c == '\t') c = ' ';
|
|
|
|
|
|
/* 连续空格压缩 */
|
|
|
|
|
|
if (c == ' ') {
|
|
|
|
|
|
if (prev_space) continue;
|
|
|
|
|
|
prev_space = 1;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
prev_space = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
out[oi++] = c;
|
|
|
|
|
|
}
|
|
|
|
|
|
out[oi] = 0;
|
|
|
|
|
|
return (int)oi;
|
|
|
|
|
|
}
|
2026-07-03 21:13:04 +08:00
|
|
|
|
|
2026-07-28 12:00:02 +08:00
|
|
|
|
/* ---- 立即重绘(无鼠标光标保存/恢复,用于 fetching 过程) ---- */
|
|
|
|
|
|
static void redraw_all_imm(void) {
|
|
|
|
|
|
da_fill_bg(&g_ac, DA_BG_PRIMARY);
|
2026-07-03 21:13:04 +08:00
|
|
|
|
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;
|
2026-07-28 12:00:02 +08:00
|
|
|
|
u32 bar_bg = url_focused ? DA_BG_TERTIARY : DA_BG_SECONDARY;
|
|
|
|
|
|
da_fill_rounded_rect(&g_ac, bar_x, bar_y, bar_w, bar_h, bar_bg, 6);
|
|
|
|
|
|
da_rect_outline(&g_ac, bar_x, bar_y, bar_w, bar_h, url_focused ? DA_ACCENT : DA_BORDER, 6);
|
|
|
|
|
|
|
|
|
|
|
|
/* URL 文本 + 光标 */
|
|
|
|
|
|
int url_x = bar_x + 12;
|
|
|
|
|
|
da_draw_string(&g_ac, url_buf, url_x, bar_y + 5, DA_TEXT_PRIMARY, bar_bg, CHAR_STEP);
|
|
|
|
|
|
if (url_focused) {
|
|
|
|
|
|
int cx = url_x + url_len * CHAR_STEP;
|
|
|
|
|
|
da_fill_rect(&g_ac, cx, bar_y + 5, 2, CHAR_H, DA_ACCENT_LIGHT);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 提示 */
|
|
|
|
|
|
int hint_y = bar_y + bar_h + 4;
|
|
|
|
|
|
const char *hint = "Enter: fetch Esc: exit Up/Down: scroll";
|
|
|
|
|
|
da_draw_string(&g_ac, hint, 12, hint_y, DA_TEXT_DIM, DA_BG_PRIMARY, CHAR_STEP);
|
|
|
|
|
|
|
|
|
|
|
|
/* 内容区 */
|
|
|
|
|
|
int content_y = hint_y + CHAR_H + 4;
|
|
|
|
|
|
int content_x = 12;
|
|
|
|
|
|
int content_w = (int)g_ac.fb_w - 24;
|
|
|
|
|
|
int content_h = (int)g_ac.fb_h - content_y - DA_STATUSBAR_H - 8;
|
|
|
|
|
|
da_fill_rounded_rect(&g_ac, content_x, content_y, content_w, content_h, DA_BG_SECONDARY, 4);
|
|
|
|
|
|
da_rect_outline(&g_ac, content_x, content_y, content_w, content_h, DA_BORDER, 4);
|
|
|
|
|
|
|
|
|
|
|
|
/* 内容显示 */
|
|
|
|
|
|
if (content_len > 0) {
|
|
|
|
|
|
/* 提取 body(跳过 HTTP header) */
|
|
|
|
|
|
u32 body_off = 0;
|
|
|
|
|
|
for (u32 i = 0; i + 3 < content_len; i++) {
|
|
|
|
|
|
if (content_buf[i]=='\r' && content_buf[i+1]=='\n' &&
|
|
|
|
|
|
content_buf[i+2]=='\r' && content_buf[i+3]=='\n') {
|
|
|
|
|
|
body_off = i + 4;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/* HTML 转纯文本 */
|
|
|
|
|
|
static u8 plain[CONTENT_BUF_SIZE];
|
|
|
|
|
|
int plain_len = html_to_text(content_buf + body_off, content_len - body_off, plain, CONTENT_BUF_SIZE);
|
|
|
|
|
|
|
|
|
|
|
|
/* 按行显示 */
|
|
|
|
|
|
int visible_cols = (content_w - 16) / CHAR_STEP;
|
|
|
|
|
|
int visible_rows = (content_h - 8) / CHAR_H;
|
|
|
|
|
|
int y = content_y + 4;
|
|
|
|
|
|
int pos = 0;
|
|
|
|
|
|
int line = 0;
|
|
|
|
|
|
/* 跳过 scroll 行 */
|
|
|
|
|
|
int skip = content_scroll;
|
|
|
|
|
|
while (pos < plain_len && skip > 0) {
|
|
|
|
|
|
if (plain[pos] == '\n') skip--;
|
|
|
|
|
|
pos++;
|
|
|
|
|
|
}
|
|
|
|
|
|
while (pos < plain_len && line < visible_rows) {
|
|
|
|
|
|
int ls = pos;
|
|
|
|
|
|
while (pos < plain_len && plain[pos] != '\n' && pos - ls < visible_cols) pos++;
|
|
|
|
|
|
int llen = pos - ls;
|
|
|
|
|
|
for (int i = 0; i < llen; i++) {
|
|
|
|
|
|
char c = (char)plain[ls + i];
|
|
|
|
|
|
if (c < ' ' || c > '~') c = '.';
|
|
|
|
|
|
char buf[2]; buf[0] = c; buf[1] = 0;
|
|
|
|
|
|
da_draw_string(&g_ac, buf, content_x + 8 + i * CHAR_STEP, y, DA_TEXT_PRIMARY, DA_BG_SECONDARY, CHAR_STEP);
|
|
|
|
|
|
}
|
|
|
|
|
|
y += CHAR_H;
|
|
|
|
|
|
line++;
|
|
|
|
|
|
if (pos < plain_len && plain[pos] == '\n') pos++;
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if (!fetching) {
|
|
|
|
|
|
const char *empty = "No content. Enter URL and press Enter to fetch.";
|
|
|
|
|
|
da_draw_string(&g_ac, empty, content_x + 16, content_y + 16, DA_TEXT_DIM, DA_BG_SECONDARY, CHAR_STEP);
|
|
|
|
|
|
}
|
2026-07-03 21:13:04 +08:00
|
|
|
|
|
|
|
|
|
|
/* 状态栏 */
|
2026-07-28 12:00:02 +08:00
|
|
|
|
char status[96]; int p = 0;
|
|
|
|
|
|
const char *s1 = "Browser | "; while (*s1 && p < 95) status[p++] = *s1++;
|
|
|
|
|
|
for (int i = 0; status_msg[i] && p < 95; i++) status[p++] = status_msg[i];
|
|
|
|
|
|
status[p] = 0;
|
|
|
|
|
|
da_draw_statusbar(&g_ac, status, (i64)g_ac.fb_w, (i64)g_ac.fb_h);
|
|
|
|
|
|
}
|
2026-07-03 21:13:04 +08:00
|
|
|
|
|
2026-07-28 12:00:02 +08:00
|
|
|
|
static void redraw_all(void) {
|
|
|
|
|
|
redraw_all_imm();
|
2026-07-03 21:13:04 +08:00
|
|
|
|
da_cursor_save(&g_ac, &g_cursor);
|
|
|
|
|
|
da_cursor_draw(&g_ac, &g_cursor, DA_CURSOR_COLOR);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-28 12:00:02 +08:00
|
|
|
|
/* ---- net_stack.h 的 ns_init 需要 dkm_kernel_api ---- */
|
|
|
|
|
|
/* 重写 ns_init 以从 da_app_context 获取 kernel_api */
|
|
|
|
|
|
static int br_ns_init(void) {
|
|
|
|
|
|
if (ns_ready) return 0;
|
|
|
|
|
|
u64 dkm_kernel_api = (u64)g_ac.kernel_api;
|
|
|
|
|
|
if (!dkm_kernel_api) return -1;
|
|
|
|
|
|
return ns_init(dkm_kernel_api);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-03 21:13:04 +08:00
|
|
|
|
__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();
|
2026-07-28 12:00:02 +08:00
|
|
|
|
|
|
|
|
|
|
/* 初始化网络协议栈 */
|
|
|
|
|
|
br_ns_init();
|
|
|
|
|
|
|
|
|
|
|
|
br_set_status("Ready");
|
2026-07-03 21:13:04 +08:00
|
|
|
|
redraw_all();
|
|
|
|
|
|
|
2026-07-28 12:00:02 +08:00
|
|
|
|
int e0 = 0, shift = 0;
|
2026-07-03 21:13:04 +08:00
|
|
|
|
for (;;) {
|
|
|
|
|
|
int need_redraw = 0;
|
|
|
|
|
|
|
|
|
|
|
|
/* 鼠标轮询 */
|
2026-07-28 12:00:02 +08:00
|
|
|
|
if (da_mouse_poll(&g_mouse, &g_cursor, (i64)g_ac.fb_w, (i64)g_ac.fb_h)) {
|
2026-07-03 21:13:04 +08:00
|
|
|
|
need_redraw = 1;
|
2026-07-28 12:00:02 +08:00
|
|
|
|
/* 点击地址栏聚焦 */
|
|
|
|
|
|
int bar_y = DA_TITLEBAR_H + 6;
|
|
|
|
|
|
int bar_h = 28;
|
|
|
|
|
|
if (g_cursor.btn &&
|
|
|
|
|
|
g_cursor.my >= bar_y && g_cursor.my < bar_y + bar_h &&
|
|
|
|
|
|
g_cursor.mx >= 12 && g_cursor.mx < (int)g_ac.fb_w - 12) {
|
|
|
|
|
|
url_focused = 1;
|
|
|
|
|
|
need_redraw = 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-07-03 21:13:04 +08:00
|
|
|
|
|
|
|
|
|
|
/* 键盘轮询 */
|
|
|
|
|
|
u8 st = inb(0x64);
|
|
|
|
|
|
if ((st & 1) && !(st & 0x20)) {
|
|
|
|
|
|
u8 data = inb(0x60);
|
2026-07-28 12:00:02 +08:00
|
|
|
|
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) { /* Esc */
|
2026-07-03 21:13:04 +08:00
|
|
|
|
da_slog("browser", "exit");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-28 12:00:02 +08:00
|
|
|
|
/* 地址栏输入模式 */
|
|
|
|
|
|
if (url_focused) {
|
|
|
|
|
|
if (sc == 0x1C) { /* Enter - 发起请求 */
|
|
|
|
|
|
url_focused = 0;
|
|
|
|
|
|
fetching = 1;
|
|
|
|
|
|
br_set_status("Fetching...");
|
|
|
|
|
|
redraw_all();
|
|
|
|
|
|
do_fetch();
|
|
|
|
|
|
fetching = 0;
|
|
|
|
|
|
need_redraw = 1;
|
|
|
|
|
|
goto skip;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (sc == 0x0E) { /* Backspace */
|
|
|
|
|
|
if (url_len > 0) { url_len--; url_buf[url_len] = 0; }
|
|
|
|
|
|
need_redraw = 1;
|
|
|
|
|
|
goto skip;
|
|
|
|
|
|
}
|
|
|
|
|
|
char c = da_scan_to_ascii(sc, shift);
|
|
|
|
|
|
if (c && c >= 32 && c <= 126 && url_len < URL_BUF_SIZE - 1) {
|
|
|
|
|
|
/* 小写转大写便于 URL 输入(除协议头) */
|
|
|
|
|
|
url_buf[url_len++] = c;
|
|
|
|
|
|
url_buf[url_len] = 0;
|
|
|
|
|
|
need_redraw = 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
goto skip;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 内容浏览模式 */
|
|
|
|
|
|
if (e0) {
|
|
|
|
|
|
if (sc == 0x48) { if (content_scroll > 0) content_scroll--; need_redraw = 1; }
|
|
|
|
|
|
else if (sc == 0x50) { content_scroll++; need_redraw = 1; }
|
|
|
|
|
|
e0 = 0;
|
|
|
|
|
|
goto skip;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (sc == 0x49) { content_scroll -= 10; if (content_scroll < 0) content_scroll = 0; need_redraw = 1; }
|
|
|
|
|
|
else if (sc == 0x51) { content_scroll += 10; need_redraw = 1; }
|
|
|
|
|
|
else if (sc == 0x1C) { url_focused = 1; need_redraw = 1; } /* Enter 回到地址栏 */
|
|
|
|
|
|
}
|
|
|
|
|
|
skip:
|
2026-07-03 21:13:04 +08:00
|
|
|
|
if (need_redraw) {
|
|
|
|
|
|
da_cursor_restore(&g_ac, &g_cursor);
|
|
|
|
|
|
redraw_all();
|
|
|
|
|
|
}
|
|
|
|
|
|
__asm__("pause");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|