fix(gui): isolate keyboard buffer between sh and gui, eliminate cursor desync with sign validation, and optimize event loop timing
This commit is contained in:
@@ -250,12 +250,41 @@ pub extern "C" fn kernel_syscall_dispatcher(regs: &mut SyscallRegisters) {
|
||||
// SYS_KEY_READ = 10 (Checks both Serial COM1 and PS/2 Keyboard buffer)
|
||||
10 => {
|
||||
unsafe {
|
||||
// 1. Check Serial Port (COM1) for direct terminal input in QEMU stdio
|
||||
let sched = &*core::ptr::addr_of!(SCHEDULER);
|
||||
let cur_tid = sched.current_tid;
|
||||
let cur_name = sched.threads[cur_tid].name;
|
||||
let is_gui = cur_name.starts_with("gui");
|
||||
let is_sh = cur_name.starts_with("sh");
|
||||
let gui_running = sched.threads.iter().any(|t| t.name.starts_with("gui") && t.state != crate::sched::thread::ThreadState::Unused);
|
||||
|
||||
let mode = regs.rdi; // Optional explicit mode: 1 = Serial, 2 = Keyboard
|
||||
|
||||
if mode == 2 || is_gui {
|
||||
// GUI Server reads exclusively from PS/2 Keyboard buffer
|
||||
let kbuf = &mut *core::ptr::addr_of_mut!(KEY_BUFFER);
|
||||
if let Some(ch) = kbuf.pop() {
|
||||
regs.rax = ch as u64;
|
||||
return;
|
||||
}
|
||||
regs.rax = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if mode == 1 || (is_sh && gui_running) {
|
||||
// Shell reads exclusively from Serial COM1 when GUI is running
|
||||
if let Some(ch) = SerialPort.read_byte() {
|
||||
regs.rax = ch as u64;
|
||||
return;
|
||||
}
|
||||
regs.rax = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback (e.g. headless CLI mode): check Serial first, then Keyboard
|
||||
if let Some(ch) = SerialPort.read_byte() {
|
||||
regs.rax = ch as u64;
|
||||
return;
|
||||
}
|
||||
// 2. Check PS/2 Keyboard buffer (Hardware / GUI)
|
||||
let kbuf = &mut *core::ptr::addr_of_mut!(KEY_BUFFER);
|
||||
if let Some(ch) = kbuf.pop() {
|
||||
regs.rax = ch as u64;
|
||||
|
||||
+32
-16
@@ -74,8 +74,25 @@ impl MouseState {
|
||||
let raw_dx = self.packet[1];
|
||||
let raw_dy = self.packet[2];
|
||||
|
||||
// Discard invalid overflow packets (bit 6 = X overflow, bit 7 = Y overflow)
|
||||
if (flags & 0xC0) != 0 {
|
||||
// 1. Validate sync bit (bit 3 MUST be 1) and no overflow (bits 6, 7 MUST be 0)
|
||||
if (flags & 0x08) == 0 || (flags & 0xC0) != 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Reject reserved/error overflow values (0x80 = -128)
|
||||
if raw_dx == 0x80 || raw_dy == 0x80 {
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Verify sign bits in flags match bit 7 of raw_dx and raw_dy
|
||||
// In standard PS/2: bit 4 is X sign, bit 5 is Y sign
|
||||
let dx_neg_flag = (flags & 0x10) != 0;
|
||||
let dy_neg_flag = (flags & 0x20) != 0;
|
||||
let raw_dx_neg = (raw_dx & 0x80) != 0;
|
||||
let raw_dy_neg = (raw_dy & 0x80) != 0;
|
||||
|
||||
// If signs don't match, this is a corrupt/desynchronized packet! Discard!
|
||||
if dx_neg_flag != raw_dx_neg || dy_neg_flag != raw_dy_neg {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -87,19 +104,16 @@ impl MouseState {
|
||||
let mut dy = raw_dy as i8 as i32;
|
||||
|
||||
// Strict delta clamping to prevent cursor jumps/teleportation at screen edges
|
||||
if dx > 60 { dx = 60; }
|
||||
if dx < -60 { dx = -60; }
|
||||
if dy > 60 { dy = 60; }
|
||||
if dy < -60 { dy = -60; }
|
||||
if dx > 30 { dx = 30; }
|
||||
if dx < -30 { dx = -30; }
|
||||
if dy > 30 { dy = 30; }
|
||||
if dy < -30 { dy = -30; }
|
||||
|
||||
// Smooth pointer scaling (responsive feel)
|
||||
let step_x = if dx.abs() > 3 { dx * 3 / 2 } else { dx };
|
||||
let step_y = if dy.abs() > 3 { dy * 3 / 2 } else { dy };
|
||||
// Apply movement (Invert Y for screen coordinates: PS/2 +Y is up, screen +Y is down)
|
||||
self.x += dx;
|
||||
self.y -= dy;
|
||||
|
||||
self.x += step_x;
|
||||
self.y -= step_y; // Invert Y for screen coordinates
|
||||
|
||||
// Clamp strictly to screen boundaries
|
||||
// Strict clamp to screen boundaries
|
||||
if self.x < 0 { self.x = 0; }
|
||||
if self.x >= self.max_x { self.x = self.max_x - 1; }
|
||||
if self.y < 0 { self.y = 0; }
|
||||
@@ -155,8 +169,8 @@ pub fn init(screen_w: i32, screen_h: i32) {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn mouse_interrupt_handler() {
|
||||
unsafe {
|
||||
while (inb(PS2_COMMAND_PORT) & 0x01) != 0 {
|
||||
let status = inb(PS2_COMMAND_PORT);
|
||||
if (status & 0x01) != 0 {
|
||||
let data = inb(PS2_DATA_PORT);
|
||||
if (status & 0x20) != 0 {
|
||||
MOUSE_STATE.handle_byte(data);
|
||||
@@ -168,11 +182,13 @@ pub extern "C" fn mouse_interrupt_handler() {
|
||||
|
||||
pub fn get_mouse_info() -> (i32, i32, u32) {
|
||||
unsafe {
|
||||
// Drain any pending bytes in 8042 buffer
|
||||
core::arch::asm!("cli");
|
||||
while (inb(PS2_COMMAND_PORT) & 0x21) == 0x21 {
|
||||
let data = inb(PS2_DATA_PORT);
|
||||
MOUSE_STATE.handle_byte(data);
|
||||
}
|
||||
(MOUSE_STATE.x, MOUSE_STATE.y, MOUSE_STATE.buttons)
|
||||
let res = (MOUSE_STATE.x, MOUSE_STATE.y, MOUSE_STATE.buttons);
|
||||
core::arch::asm!("sti");
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,8 +130,7 @@ impl Scheduler {
|
||||
if self.threads[self.current_tid].state == ThreadState::Running {
|
||||
self.threads[self.current_tid].state = ThreadState::Ready;
|
||||
}
|
||||
self.quantum_remaining = DEFAULT_QUANTUM_TICKS;
|
||||
self.schedule();
|
||||
self.quantum_remaining = 0;
|
||||
}
|
||||
|
||||
/// Preemptive tick called directly from timer ISR handler
|
||||
|
||||
+2
-2
@@ -78,10 +78,10 @@
|
||||
*====================*/
|
||||
|
||||
/*Default display refresh period. LVG will redraw changed areas with this period time*/
|
||||
#define LV_DISP_DEF_REFR_PERIOD 12 /*[ms] (~80 FPS)*/
|
||||
#define LV_DISP_DEF_REFR_PERIOD 16 /*[ms] (60 FPS)*/
|
||||
|
||||
/*Input device read period in milliseconds*/
|
||||
#define LV_INDEV_DEF_READ_PERIOD 10 /*[ms] (100 Hz polling)*/
|
||||
#define LV_INDEV_DEF_READ_PERIOD 16 /*[ms] (60 Hz polling)*/
|
||||
|
||||
/*Use a custom tick source that tells the elapsed time in milliseconds.
|
||||
*It removes the need to manually update the tick with `lv_tick_inc()`)*/
|
||||
|
||||
+27
-16
@@ -3,8 +3,8 @@
|
||||
* Windows / KDE-inspired Desktop Environment powered by LVGL v8.3.
|
||||
* Features:
|
||||
* - Custom 16x24 high-contrast hardware arrow cursor on lv_layer_sys()
|
||||
* - Window dragging by titlebar with smooth clamping
|
||||
* - Full PS/2 keyboard integration for typing in terminal
|
||||
* - Smooth window dragging with boundary clamping
|
||||
* - Dedicated PS/2 keyboard integration for terminal
|
||||
* - Bottom Taskbar with Start Menu, Running App tabs & System Tray
|
||||
* - Interactive Terminal (Console) application with real system commands
|
||||
* - Task Manager with real-time CPU chart & thread table
|
||||
@@ -99,12 +99,13 @@ static void mouse_read_cb(lv_indev_drv_t *drv, lv_indev_data_t *data) {
|
||||
data->state = (mouse.buttons & 1) ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
|
||||
}
|
||||
|
||||
// Keyboard read callback: queries microkernel SYS_KEY_READ
|
||||
// Keyboard read callback: queries microkernel SYS_KEY_READ in Mode 2 (exclusive PS/2)
|
||||
static void keyboard_read_cb(lv_indev_drv_t *drv, lv_indev_data_t *data) {
|
||||
(void)drv;
|
||||
uint64_t ch = 0;
|
||||
register uint64_t rax __asm__("rax") = SYS_KEY_READ;
|
||||
__asm__ volatile("syscall" : "=a"(ch) : "a"(rax) : "rcx", "r11", "memory");
|
||||
register uint64_t rdi __asm__("rdi") = 2; // Mode 2: PS/2 Keyboard buffer only
|
||||
__asm__ volatile("syscall" : "=a"(ch) : "a"(rax), "r"(rdi) : "rcx", "r11", "memory");
|
||||
|
||||
if (ch != 0) {
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
@@ -207,6 +208,9 @@ static void toggle_window(lv_obj_t *win) {
|
||||
if (lv_obj_has_flag(win, LV_OBJ_FLAG_HIDDEN)) {
|
||||
lv_obj_clear_flag(win, LV_OBJ_FLAG_HIDDEN);
|
||||
lv_obj_move_foreground(win);
|
||||
if (win == g_win_console && g_input_group && g_ta_term_input) {
|
||||
lv_group_focus_obj(g_ta_term_input);
|
||||
}
|
||||
} else {
|
||||
lv_obj_add_flag(win, LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
@@ -216,6 +220,9 @@ static void toggle_window(lv_obj_t *win) {
|
||||
static void open_window(lv_obj_t *win) {
|
||||
lv_obj_clear_flag(win, LV_OBJ_FLAG_HIDDEN);
|
||||
lv_obj_move_foreground(win);
|
||||
if (win == g_win_console && g_input_group && g_ta_term_input) {
|
||||
lv_group_focus_obj(g_ta_term_input);
|
||||
}
|
||||
}
|
||||
|
||||
// Start menu toggle
|
||||
@@ -248,6 +255,9 @@ static void win_focus_event_cb(lv_event_t *e) {
|
||||
lv_obj_t *win = (lv_obj_t *)lv_event_get_user_data(e);
|
||||
if (win) {
|
||||
lv_obj_move_foreground(win);
|
||||
if (win == g_win_console && g_input_group && g_ta_term_input) {
|
||||
lv_group_focus_obj(g_ta_term_input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,7 +272,7 @@ static void poweroff_event_cb(lv_event_t *e) {
|
||||
static void execute_console_command(const char *cmd) {
|
||||
if (!cmd || !g_txt_term_output) return;
|
||||
|
||||
// Strip leading / trailing whitespace
|
||||
// Strip leading whitespace
|
||||
while (*cmd == ' ') cmd++;
|
||||
if (!*cmd) return;
|
||||
|
||||
@@ -422,7 +432,7 @@ static lv_obj_t *create_styled_window(lv_obj_t *parent, const char *title, int32
|
||||
lv_obj_set_style_border_width(header, 1, 0);
|
||||
lv_win_add_title(win, title);
|
||||
|
||||
// Make titlebar draggable to move window!
|
||||
// Make titlebar draggable to move window smoothly!
|
||||
lv_obj_add_flag(header, LV_OBJ_FLAG_CLICKABLE);
|
||||
lv_obj_add_event_cb(header, win_drag_event_cb, LV_EVENT_PRESSING, win);
|
||||
|
||||
@@ -520,6 +530,7 @@ static void create_desktop_environment() {
|
||||
lv_obj_align(g_ta_term_input, LV_ALIGN_LEFT_MID, 160, 0);
|
||||
lv_textarea_set_one_line(g_ta_term_input, true);
|
||||
lv_textarea_set_placeholder_text(g_ta_term_input, "Enter command...");
|
||||
lv_textarea_set_cursor_click_pos(g_ta_term_input, true);
|
||||
lv_obj_set_style_bg_color(g_ta_term_input, lv_color_hex(0x1a2233), 0);
|
||||
lv_obj_set_style_border_width(g_ta_term_input, 0, 0);
|
||||
lv_obj_set_style_text_color(g_ta_term_input, lv_color_hex(0xffffff), 0);
|
||||
@@ -846,7 +857,7 @@ static void create_desktop_environment() {
|
||||
lv_indev_set_cursor(g_mouse_indev, cursor_obj);
|
||||
}
|
||||
|
||||
// Background metrics updater (called every ~500ms)
|
||||
// Background metrics updater (called once every ~1 second)
|
||||
static void update_live_metrics() {
|
||||
char buf[128];
|
||||
|
||||
@@ -886,7 +897,8 @@ static void update_live_metrics() {
|
||||
lv_label_set_text(g_lbl_tray_clock, buf);
|
||||
}
|
||||
|
||||
// 3. CPU Load History Chart
|
||||
// 3. CPU Load History Chart & Process Table (ONLY when Task Manager is visible!)
|
||||
if (g_win_taskmgr && !lv_obj_has_flag(g_win_taskmgr, LV_OBJ_FLAG_HIDDEN)) {
|
||||
static uint32_t s_prev_ticks[16] = {0};
|
||||
thread_info_t threads[16];
|
||||
memset(threads, 0, sizeof(threads));
|
||||
@@ -914,7 +926,6 @@ static void update_live_metrics() {
|
||||
lv_chart_set_next_value(g_chart_cpu, g_ser_cpu, (lv_coord_t)cpu_percent);
|
||||
}
|
||||
|
||||
// 4. Update Process Table
|
||||
if (g_tbl_procs) {
|
||||
lv_table_set_row_cnt(g_tbl_procs, count + 1);
|
||||
for (int i = 0; i < count; i++) {
|
||||
@@ -933,6 +944,7 @@ static void update_live_metrics() {
|
||||
lv_table_set_cell_value(g_tbl_procs, i + 1, 3, tick_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
@@ -986,19 +998,18 @@ int main() {
|
||||
printf("[GUI-SERVER] Windows/KDE Desktop UI Initialized. Entering Compositor Event Loop.\n");
|
||||
|
||||
// 7. Compositor Event Loop
|
||||
uint32_t tick_counter = 0;
|
||||
uint32_t loop_count = 0;
|
||||
while (1) {
|
||||
lv_task_handler();
|
||||
lv_tick_inc(10);
|
||||
lv_tick_inc(5);
|
||||
|
||||
tick_counter++;
|
||||
if (tick_counter % 50 == 0) { // Every ~500ms
|
||||
loop_count++;
|
||||
if (loop_count % 200 == 0) { // Every ~1000ms
|
||||
update_live_metrics();
|
||||
}
|
||||
|
||||
// Cooperative yield to microkernel scheduler for snappy, silky smooth interaction
|
||||
register uint64_t rax __asm__("rax") = SYS_THREAD_YIELD;
|
||||
__asm__ volatile("syscall" : "+r"(rax) : : "rcx", "r11", "memory");
|
||||
// Calibrated delay (~5ms) for smooth interaction without high CPU load
|
||||
for (volatile int d = 0; d < 8000; d++);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user