diff --git a/kernel/src/drivers/mouse.rs b/kernel/src/drivers/mouse.rs index 848ce49..8ca7763 100644 --- a/kernel/src/drivers/mouse.rs +++ b/kernel/src/drivers/mouse.rs @@ -74,25 +74,32 @@ 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 { + return; + } + // Buttons: bit 0 = Left, bit 1 = Right, bit 2 = Middle self.buttons = (flags & 0x07) as u32; - // X offset with 9-bit sign extension - let mut dx = raw_dx as i32; - if (flags & 0x10) != 0 { - dx |= !0xFF; // sign extend negative - } + // 8-bit signed two's complement deltas + let mut dx = raw_dx as i8 as i32; + let mut dy = raw_dy as i8 as i32; - // Y offset with 9-bit sign extension (PS/2 Y points upwards, screen points downwards) - let mut dy = raw_dy as i32; - if (flags & 0x20) != 0 { - dy |= !0xFF; // sign extend negative - } + // 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; } - self.x += dx; - self.y -= dy; // Invert Y for screen coordinates + // 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 }; - // Clamp to screen boundaries + self.x += step_x; + self.y -= step_y; // Invert Y for screen coordinates + + // Clamp strictly 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; } diff --git a/lib/lvgl/lv_conf.h b/lib/lvgl/lv_conf.h index a0cb630..580f656 100644 --- a/lib/lvgl/lv_conf.h +++ b/lib/lvgl/lv_conf.h @@ -78,10 +78,10 @@ *====================*/ /*Default display refresh period. LVG will redraw changed areas with this period time*/ -#define LV_DISP_DEF_REFR_PERIOD 30 /*[ms]*/ +#define LV_DISP_DEF_REFR_PERIOD 12 /*[ms] (~80 FPS)*/ /*Input device read period in milliseconds*/ -#define LV_INDEV_DEF_READ_PERIOD 30 /*[ms]*/ +#define LV_INDEV_DEF_READ_PERIOD 10 /*[ms] (100 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()`)*/ diff --git a/servers/gui_server/main.cpp b/servers/gui_server/main.cpp index fb03ed5..073a8b0 100644 --- a/servers/gui_server/main.cpp +++ b/servers/gui_server/main.cpp @@ -3,6 +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 * - 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 @@ -26,8 +28,11 @@ extern "C" { // Framebuffer and display driver structures static fb_info_t g_fb = {0, 0, 0, 0, 0}; static lv_disp_drv_t g_disp_drv; -static lv_indev_drv_t g_indev_drv; +static lv_indev_drv_t g_mouse_indev_drv; +static lv_indev_drv_t g_kbd_indev_drv; static lv_indev_t *g_mouse_indev = NULL; +static lv_indev_t *g_kbd_indev = NULL; +static lv_group_t *g_input_group = NULL; // Double-buffering: 40-row raster bands #define BUF_HEIGHT 40 @@ -94,6 +99,29 @@ 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 +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"); + + if (ch != 0) { + data->state = LV_INDEV_STATE_PRESSED; + if (ch == '\r' || ch == '\n') { + data->key = LV_KEY_ENTER; + } else if (ch == '\b' || ch == 127) { + data->key = LV_KEY_BACKSPACE; + } else if (ch == 27) { + data->key = LV_KEY_ESC; + } else { + data->key = (uint32_t)ch; + } + } else { + data->state = LV_INDEV_STATE_RELEASED; + } +} + // Initialize the 16x24 classic Windows / KDE arrow cursor static void init_cursor_bitmap() { static const char *pattern[CURSOR_H] = { @@ -152,6 +180,28 @@ static void init_cursor_bitmap() { g_cursor_dsc.data = (const uint8_t *)g_cursor_map; } +// Window drag callback: dragging by titlebar smoothly moves window +static void win_drag_event_cb(lv_event_t *e) { + lv_obj_t *win = (lv_obj_t *)lv_event_get_user_data(e); + lv_indev_t *indev = lv_indev_get_act(); + if (!indev || !win) return; + + lv_point_t vect; + lv_indev_get_vect(indev, &vect); + + lv_coord_t x = lv_obj_get_x(win) + vect.x; + lv_coord_t y = lv_obj_get_y(win) + vect.y; + + // Clamp window to stay within visible screen area + if (x < 0) x = 0; + if (x > 1280 - 100) x = 1280 - 100; + if (y < 0) y = 0; + if (y > 800 - 80) y = 800 - 80; + + lv_obj_set_pos(win, x, y); + lv_obj_move_foreground(win); +} + // Window toggle helper (show/hide and bring to foreground) static void toggle_window(lv_obj_t *win) { if (lv_obj_has_flag(win, LV_OBJ_FLAG_HIDDEN)) { @@ -195,8 +245,10 @@ static void win_close_btn_event_cb(lv_event_t *e) { // Window click focus static void win_focus_event_cb(lv_event_t *e) { - lv_obj_t *win = lv_event_get_target(e); - lv_obj_move_foreground(win); + lv_obj_t *win = (lv_obj_t *)lv_event_get_user_data(e); + if (win) { + lv_obj_move_foreground(win); + } } // Safe system poweroff @@ -210,6 +262,10 @@ 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 + while (*cmd == ' ') cmd++; + if (!*cmd) return; + char buf[512]; snprintf(buf, sizeof(buf), "\nroot@opencoreC:~# %s\n", cmd); lv_textarea_add_text(g_txt_term_output, buf); @@ -310,7 +366,7 @@ static void console_chip_event_cb(lv_event_t *e) { execute_console_command(cmd); } -// Console input submit callback +// Console input submit callback (triggered by Run button or Enter key) static void console_submit_event_cb(lv_event_t *e) { (void)e; const char *text = lv_textarea_get_text(g_ta_term_input); @@ -346,7 +402,7 @@ static void ping_btn_event_cb(lv_event_t *e) { } } -// Create a modern styled window with title, minimize, and close buttons +// Create a modern styled window with title, minimize, close buttons, and titlebar dragging static lv_obj_t *create_styled_window(lv_obj_t *parent, const char *title, int32_t w, int32_t h, int32_t x, int32_t y) { lv_obj_t *win = lv_win_create(parent, 38); lv_obj_set_size(win, w, h); @@ -366,6 +422,10 @@ 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! + lv_obj_add_flag(header, LV_OBJ_FLAG_CLICKABLE); + lv_obj_add_event_cb(header, win_drag_event_cb, LV_EVENT_PRESSING, win); + // Window controls lv_obj_t *btn_min = lv_win_add_btn(win, LV_SYMBOL_MINUS, 28); lv_obj_set_style_bg_color(btn_min, lv_color_hex(0x334155), 0); @@ -376,7 +436,7 @@ static lv_obj_t *create_styled_window(lv_obj_t *parent, const char *title, int32 lv_obj_add_event_cb(btn_close, win_close_btn_event_cb, LV_EVENT_CLICKED, win); // Allow clicking window to bring to front - lv_obj_add_event_cb(win, win_focus_event_cb, LV_EVENT_PRESSED, NULL); + lv_obj_add_event_cb(win, win_focus_event_cb, LV_EVENT_PRESSED, win); return win; } @@ -463,6 +523,14 @@ static void create_desktop_environment() { 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); + // Bind Enter key event + lv_obj_add_event_cb(g_ta_term_input, console_submit_event_cb, LV_EVENT_READY, NULL); + + // Add to input group for direct keyboard typing + if (g_input_group) { + lv_group_add_obj(g_input_group, g_ta_term_input); + lv_group_focus_obj(g_ta_term_input); + } lv_obj_t *btn_run = lv_btn_create(input_cont); lv_obj_set_size(btn_run, 95, 32); @@ -897,28 +965,40 @@ int main() { lv_disp_drv_register(&g_disp_drv); // 4. Register Hardware PS/2 Mouse Input Driver - lv_indev_drv_init(&g_indev_drv); - g_indev_drv.type = LV_INDEV_TYPE_POINTER; - g_indev_drv.read_cb = mouse_read_cb; - g_mouse_indev = lv_indev_drv_register(&g_indev_drv); + lv_indev_drv_init(&g_mouse_indev_drv); + g_mouse_indev_drv.type = LV_INDEV_TYPE_POINTER; + g_mouse_indev_drv.read_cb = mouse_read_cb; + g_mouse_indev = lv_indev_drv_register(&g_mouse_indev_drv); - // 5. Build the Windows / KDE Desktop Environment + // 5. Register Hardware PS/2 Keyboard Input Driver + lv_indev_drv_init(&g_kbd_indev_drv); + g_kbd_indev_drv.type = LV_INDEV_TYPE_KEYPAD; + g_kbd_indev_drv.read_cb = keyboard_read_cb; + g_kbd_indev = lv_indev_drv_register(&g_kbd_indev_drv); + + // Default focus group for keyboard input + g_input_group = lv_group_create(); + lv_indev_set_group(g_kbd_indev, g_input_group); + lv_group_set_default(g_input_group); + + // 6. Build the Windows / KDE Desktop Environment create_desktop_environment(); printf("[GUI-SERVER] Windows/KDE Desktop UI Initialized. Entering Compositor Event Loop.\n"); - // 6. Compositor Event Loop + // 7. Compositor Event Loop uint32_t tick_counter = 0; while (1) { lv_task_handler(); - lv_tick_inc(5); + lv_tick_inc(10); tick_counter++; - if (tick_counter % 100 == 0) { // Every ~500ms + if (tick_counter % 50 == 0) { // Every ~500ms update_live_metrics(); } - // Delay ~5ms (cooperative time slice yield) - for (volatile int d = 0; d < 30000; d++); + // 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"); } return 0;