Files
Deshab-OS/CODE/desktop/input.c
T

307 lines
10 KiB
C
Raw Normal View History

/* input.c — PS/2 鼠标键盘、光标管理、RTC/TSC 时钟(M0 自 main.c 平移)
*/
#include "desktop.h"
/* ---- 全局状态 ---- */
int g_mouse_x = 400, g_mouse_y = 300;
int g_mouse_btn = 0; /* bit0=左 bit1=右 */
int g_left_pressed = 0, g_left_released = 0, g_right_pressed = 0;
static u8 g_mouse_buf[3];
static int g_mouse_idx = 0;
static int g_mouse_has_pkt = 0;
/* 光标背景保存 */
static u32 g_cursor_save[CURSOR_SIZE * CURSOR_SIZE];
int g_cursor_saved = 0;
int g_cursor_old_x = -1, g_cursor_old_y = -1;
/* 键盘状态 */
int g_shift = 0, g_ctrl = 0, g_alt = 0;
int g_e0 = 0;
/* 时钟 */
int g_rtc_boot_sec = 0; /* 开机时刻(秒级当天秒) */
int g_last_sec = -1;
u64 g_tsc_per_sec = 0;
/* ============================================================
* 箭头光标形状(24×24,1=前景 0=透明)
* ============================================================ */
static const u8 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,1,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,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,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,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,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},
};
/* ============================================================
* RTC 时钟
* ============================================================ */
static int bcd2bin(u8 v) { return (v >> 4) * 10 + (v & 0xF); }
void rtc_read(int *yy, int *mo, int *dd, int *hh, int *mi, int *ss) {
outb(0x70, 0x00); *ss = bcd2bin(inb(0x71));
outb(0x70, 0x02); *mi = bcd2bin(inb(0x71));
outb(0x70, 0x04); *hh = bcd2bin(inb(0x71));
outb(0x70, 0x07); *dd = bcd2bin(inb(0x71));
outb(0x70, 0x08); *mo = bcd2bin(inb(0x71));
outb(0x70, 0x09); *yy = bcd2bin(inb(0x71));
}
int rtc_day_sec(void) {
int y, mo, d, h, mi, s;
rtc_read(&y, &mo, &d, &h, &mi, &s);
return h * 3600 + mi * 60 + s;
}
/* 格式化 HH:MM:SS */
void fmt_hms(char *out, int sec) {
int h = sec / 3600, m = (sec / 60) % 60, s = sec % 60;
out[0] = '0' + h / 10; out[1] = '0' + h % 10; out[2] = ':';
out[3] = '0' + m / 10; out[4] = '0' + m % 10; out[5] = ':';
out[6] = '0' + s / 10; out[7] = '0' + s % 10; out[8] = 0;
}
/* 校准 TSC(约 1 秒):用于双击间隔判定 */
void tsc_calibrate(void) {
int s0 = rtc_day_sec();
while (rtc_day_sec() == s0) __asm__("pause");
u64 t0 = rdtsc();
int s1 = rtc_day_sec();
while (rtc_day_sec() == s1) __asm__("pause");
g_tsc_per_sec = rdtsc() - t0;
}
/* ============================================================
* 光标管理
* ============================================================ */
void cursor_save_bg(int mx, int my) {
for (int r = 0; r < CURSOR_SIZE; r++) {
for (int c = 0; c < CURSOR_SIZE; c++) {
int x = mx + c, y = my + r;
if (x >= 0 && (u64)x < g_fb_w && y >= 0 && (u64)y < g_fb_h) {
u32 *line = (u32 *)((u8 *)g_fb.fb + (u64)y * g_fb.pitch);
g_cursor_save[r * CURSOR_SIZE + c] = line[x];
} else {
g_cursor_save[r * CURSOR_SIZE + c] = 0;
}
}
}
g_cursor_saved = 1;
g_cursor_old_x = mx;
g_cursor_old_y = my;
}
void cursor_restore_bg(void) {
if (!g_cursor_saved) return;
for (int r = 0; r < CURSOR_SIZE; r++) {
for (int c = 0; c < CURSOR_SIZE; c++) {
int x = g_cursor_old_x + c, y = g_cursor_old_y + r;
if (x >= 0 && (u64)x < g_fb_w && y >= 0 && (u64)y < g_fb_h) {
u32 *line = (u32 *)((u8 *)g_fb.fb + (u64)y * g_fb.pitch);
line[x] = g_cursor_save[r * CURSOR_SIZE + c];
}
}
}
g_cursor_saved = 0;
}
void cursor_draw(int mx, int my) {
for (int r = 0; r < CURSOR_SIZE; r++) {
for (int c = 0; c < CURSOR_SIZE; c++) {
if (cursor_shape[r][c]) {
du_pixel(&g_fb, mx + c, my + r, KS_ACCENT);
}
}
}
}
/* ============================================================
* PS/2 鼠标处理
* ============================================================ */
static int ps2_wait_write(void) {
for (int t = 0; t < 200000; t++) {
if (!(inb(0x64) & 0x02)) return 0;
__asm__ volatile("pause");
}
return -1;
}
static int ps2_wait_read(void) {
for (int t = 0; t < 200000; t++) {
if (inb(0x64) & 0x01) return 0;
__asm__ volatile("pause");
}
return -1;
}
static void ps2_drain(void) {
for (int i = 0; i < 16; i++) {
if (!(inb(0x64) & 0x01)) break;
inb(0x60);
}
}
void ps2_mouse_init(void) {
ps2_drain();
ps2_wait_write();
outb(0x64, 0xA8);
ps2_wait_write();
outb(0x64, 0x20);
ps2_wait_read();
u8 cfg = inb(0x60);
cfg &= ~0x20;
cfg |= 0x02;
cfg |= 0x40;
ps2_wait_write();
outb(0x64, 0x60);
ps2_wait_write();
outb(0x60, cfg);
ps2_wait_write();
outb(0x64, 0xD4);
ps2_wait_write();
outb(0x60, 0xFF);
for (int i = 0; i < 8; i++) {
if (ps2_wait_read() != 0) break;
inb(0x60);
}
ps2_wait_write();
outb(0x64, 0xD4);
ps2_wait_write();
outb(0x60, 0xF4);
for (int i = 0; i < 4; i++) {
if (ps2_wait_read() != 0) break;
inb(0x60);
}
ps2_drain();
g_mouse_idx = 0;
g_mouse_has_pkt = 0;
slog("mouse init ok");
}
int ps2_mouse_poll(void) {
u8 st = inb(0x64);
if (!(st & 1)) return 0;
if (!(st & 0x20)) return 0;
u8 data = inb(0x60);
g_mouse_buf[g_mouse_idx++] = data;
if (g_mouse_idx < 3) return 0;
g_mouse_idx = 0;
if (!(g_mouse_buf[0] & 0x08)) return 0;
int dx = (int)(i8)g_mouse_buf[1];
int dy = (int)(i8)g_mouse_buf[2];
dy = -dy;
g_mouse_x += dx;
g_mouse_y += dy;
if (g_mouse_x < 0) g_mouse_x = 0;
if (g_mouse_y < 0) g_mouse_y = 0;
if (g_mouse_x >= (int)g_fb_w - CURSOR_SIZE) g_mouse_x = (int)g_fb_w - CURSOR_SIZE;
if (g_mouse_y >= (int)g_fb_h - CURSOR_SIZE) g_mouse_y = (int)g_fb_h - CURSOR_SIZE;
int old_btn = g_mouse_btn;
g_mouse_btn = g_mouse_buf[0] & 0x03;
if ((g_mouse_btn & 1) && !(old_btn & 1)) g_left_pressed = 1;
if (!(g_mouse_btn & 1) && (old_btn & 1)) g_left_released = 1;
if ((g_mouse_btn & 2) && !(old_btn & 2)) g_right_pressed = 1;
if (dx == 0 && dy == 0 && g_mouse_btn == old_btn) return 0;
/* VSCode Phase 2: IDE attached 时把相对位移 + 按钮状态镜像到 Linux guest。
* dy 已在上方取反为屏幕坐标系(正=下),与 Linux REL_Y 约定一致;
* input_forward_mouse 内部与上次状态比较,仅产生变化按钮事件 + SYN。
* 即使宿主光标已抵边界被钳位,原始 dx/dy 仍转发,guest 光标自由移动。 */
if (g_input_forward_enabled && g_lxc_svc && g_lxc_svc->input_forward_mouse) {
g_lxc_svc->input_forward_mouse(dx, dy, (u8)g_mouse_btn);
}
g_mouse_has_pkt = 1;
return 1;
}
/* ============================================================
* 键盘扫描码 → ASCII
* ============================================================ */
char 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];
}
/* VSCode Phase 2: PS/2 set-1 扫描码 → Linux keycode(linux/input-event-codes.h)。
* base : 已去除 0x80 release 位的基础扫描码(0x01..0x39 主键区 + 少数扩展)
* e0 : 上一字节为 0xE0 前缀时置 1,选取扩展键变体
* 返回 0 表示该扫描码无映射(调用方应丢弃)。
*
* 巧合:主键区 0x01..0x39 的 Linux keycode 与扫描码数值完全一致
* (IBM XT 布局历史遗留),故直接透传;仅 E0 扩展键需单独查表。 */
u16 scancode_to_linux_keycode(u8 base, int e0) {
if (e0) {
switch (base) {
case 0x1D: return 97; /* KEY_RIGHTCTRL */
case 0x38: return 100; /* KEY_RIGHTALT */
case 0x48: return 103; /* KEY_UP */
case 0x4B: return 105; /* KEY_LEFT */
case 0x4D: return 106; /* KEY_RIGHT */
case 0x50: return 108; /* KEY_DOWN */
case 0x52: return 110; /* KEY_INSERT */
case 0x53: return 111; /* KEY_DELETE */
case 0x47: return 102; /* KEY_HOME */
case 0x4F: return 107; /* KEY_END */
case 0x49: return 104; /* KEY_PAGEUP */
case 0x51: return 109; /* KEY_PAGEDOWN */
default: return 0;
}
}
/* 主键区:0x01..0x39 透传,超出范围无映射 */
if (base >= 0x01 && base <= 0x39) return (u16)base;
return 0;
}