fix(gui): add window dragging, PS/2 keyboard input, fix cursor teleportation and boost responsiveness
This commit is contained in:
+20
-13
@@ -74,25 +74,32 @@ impl MouseState {
|
|||||||
let raw_dx = self.packet[1];
|
let raw_dx = self.packet[1];
|
||||||
let raw_dy = self.packet[2];
|
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
|
// Buttons: bit 0 = Left, bit 1 = Right, bit 2 = Middle
|
||||||
self.buttons = (flags & 0x07) as u32;
|
self.buttons = (flags & 0x07) as u32;
|
||||||
|
|
||||||
// X offset with 9-bit sign extension
|
// 8-bit signed two's complement deltas
|
||||||
let mut dx = raw_dx as i32;
|
let mut dx = raw_dx as i8 as i32;
|
||||||
if (flags & 0x10) != 0 {
|
let mut dy = raw_dy as i8 as i32;
|
||||||
dx |= !0xFF; // sign extend negative
|
|
||||||
}
|
|
||||||
|
|
||||||
// Y offset with 9-bit sign extension (PS/2 Y points upwards, screen points downwards)
|
// Strict delta clamping to prevent cursor jumps/teleportation at screen edges
|
||||||
let mut dy = raw_dy as i32;
|
if dx > 60 { dx = 60; }
|
||||||
if (flags & 0x20) != 0 {
|
if dx < -60 { dx = -60; }
|
||||||
dy |= !0xFF; // sign extend negative
|
if dy > 60 { dy = 60; }
|
||||||
}
|
if dy < -60 { dy = -60; }
|
||||||
|
|
||||||
self.x += dx;
|
// Smooth pointer scaling (responsive feel)
|
||||||
self.y -= dy; // Invert Y for screen coordinates
|
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 < 0 { self.x = 0; }
|
||||||
if self.x >= self.max_x { self.x = self.max_x - 1; }
|
if self.x >= self.max_x { self.x = self.max_x - 1; }
|
||||||
if self.y < 0 { self.y = 0; }
|
if self.y < 0 { self.y = 0; }
|
||||||
|
|||||||
+2
-2
@@ -78,10 +78,10 @@
|
|||||||
*====================*/
|
*====================*/
|
||||||
|
|
||||||
/*Default display refresh period. LVG will redraw changed areas with this period time*/
|
/*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*/
|
/*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.
|
/*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()`)*/
|
*It removes the need to manually update the tick with `lv_tick_inc()`)*/
|
||||||
|
|||||||
+96
-16
@@ -3,6 +3,8 @@
|
|||||||
* Windows / KDE-inspired Desktop Environment powered by LVGL v8.3.
|
* Windows / KDE-inspired Desktop Environment powered by LVGL v8.3.
|
||||||
* Features:
|
* Features:
|
||||||
* - Custom 16x24 high-contrast hardware arrow cursor on lv_layer_sys()
|
* - 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
|
* - Bottom Taskbar with Start Menu, Running App tabs & System Tray
|
||||||
* - Interactive Terminal (Console) application with real system commands
|
* - Interactive Terminal (Console) application with real system commands
|
||||||
* - Task Manager with real-time CPU chart & thread table
|
* - Task Manager with real-time CPU chart & thread table
|
||||||
@@ -26,8 +28,11 @@ extern "C" {
|
|||||||
// Framebuffer and display driver structures
|
// Framebuffer and display driver structures
|
||||||
static fb_info_t g_fb = {0, 0, 0, 0, 0};
|
static fb_info_t g_fb = {0, 0, 0, 0, 0};
|
||||||
static lv_disp_drv_t g_disp_drv;
|
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_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
|
// Double-buffering: 40-row raster bands
|
||||||
#define BUF_HEIGHT 40
|
#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;
|
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
|
// Initialize the 16x24 classic Windows / KDE arrow cursor
|
||||||
static void init_cursor_bitmap() {
|
static void init_cursor_bitmap() {
|
||||||
static const char *pattern[CURSOR_H] = {
|
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;
|
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)
|
// Window toggle helper (show/hide and bring to foreground)
|
||||||
static void toggle_window(lv_obj_t *win) {
|
static void toggle_window(lv_obj_t *win) {
|
||||||
if (lv_obj_has_flag(win, LV_OBJ_FLAG_HIDDEN)) {
|
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
|
// Window click focus
|
||||||
static void win_focus_event_cb(lv_event_t *e) {
|
static void win_focus_event_cb(lv_event_t *e) {
|
||||||
lv_obj_t *win = lv_event_get_target(e);
|
lv_obj_t *win = (lv_obj_t *)lv_event_get_user_data(e);
|
||||||
lv_obj_move_foreground(win);
|
if (win) {
|
||||||
|
lv_obj_move_foreground(win);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safe system poweroff
|
// Safe system poweroff
|
||||||
@@ -210,6 +262,10 @@ static void poweroff_event_cb(lv_event_t *e) {
|
|||||||
static void execute_console_command(const char *cmd) {
|
static void execute_console_command(const char *cmd) {
|
||||||
if (!cmd || !g_txt_term_output) return;
|
if (!cmd || !g_txt_term_output) return;
|
||||||
|
|
||||||
|
// Strip leading / trailing whitespace
|
||||||
|
while (*cmd == ' ') cmd++;
|
||||||
|
if (!*cmd) return;
|
||||||
|
|
||||||
char buf[512];
|
char buf[512];
|
||||||
snprintf(buf, sizeof(buf), "\nroot@opencoreC:~# %s\n", cmd);
|
snprintf(buf, sizeof(buf), "\nroot@opencoreC:~# %s\n", cmd);
|
||||||
lv_textarea_add_text(g_txt_term_output, buf);
|
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);
|
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) {
|
static void console_submit_event_cb(lv_event_t *e) {
|
||||||
(void)e;
|
(void)e;
|
||||||
const char *text = lv_textarea_get_text(g_ta_term_input);
|
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) {
|
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_t *win = lv_win_create(parent, 38);
|
||||||
lv_obj_set_size(win, w, h);
|
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_obj_set_style_border_width(header, 1, 0);
|
||||||
lv_win_add_title(win, title);
|
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
|
// Window controls
|
||||||
lv_obj_t *btn_min = lv_win_add_btn(win, LV_SYMBOL_MINUS, 28);
|
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);
|
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);
|
lv_obj_add_event_cb(btn_close, win_close_btn_event_cb, LV_EVENT_CLICKED, win);
|
||||||
|
|
||||||
// Allow clicking window to bring to front
|
// 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;
|
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_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_border_width(g_ta_term_input, 0, 0);
|
||||||
lv_obj_set_style_text_color(g_ta_term_input, lv_color_hex(0xffffff), 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_t *btn_run = lv_btn_create(input_cont);
|
||||||
lv_obj_set_size(btn_run, 95, 32);
|
lv_obj_set_size(btn_run, 95, 32);
|
||||||
@@ -897,28 +965,40 @@ int main() {
|
|||||||
lv_disp_drv_register(&g_disp_drv);
|
lv_disp_drv_register(&g_disp_drv);
|
||||||
|
|
||||||
// 4. Register Hardware PS/2 Mouse Input Driver
|
// 4. Register Hardware PS/2 Mouse Input Driver
|
||||||
lv_indev_drv_init(&g_indev_drv);
|
lv_indev_drv_init(&g_mouse_indev_drv);
|
||||||
g_indev_drv.type = LV_INDEV_TYPE_POINTER;
|
g_mouse_indev_drv.type = LV_INDEV_TYPE_POINTER;
|
||||||
g_indev_drv.read_cb = mouse_read_cb;
|
g_mouse_indev_drv.read_cb = mouse_read_cb;
|
||||||
g_mouse_indev = lv_indev_drv_register(&g_indev_drv);
|
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();
|
create_desktop_environment();
|
||||||
printf("[GUI-SERVER] Windows/KDE Desktop UI Initialized. Entering Compositor Event Loop.\n");
|
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;
|
uint32_t tick_counter = 0;
|
||||||
while (1) {
|
while (1) {
|
||||||
lv_task_handler();
|
lv_task_handler();
|
||||||
lv_tick_inc(5);
|
lv_tick_inc(10);
|
||||||
|
|
||||||
tick_counter++;
|
tick_counter++;
|
||||||
if (tick_counter % 100 == 0) { // Every ~500ms
|
if (tick_counter % 50 == 0) { // Every ~500ms
|
||||||
update_live_metrics();
|
update_live_metrics();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delay ~5ms (cooperative time slice yield)
|
// Cooperative yield to microkernel scheduler for snappy, silky smooth interaction
|
||||||
for (volatile int d = 0; d < 30000; d++);
|
register uint64_t rax __asm__("rax") = SYS_THREAD_YIELD;
|
||||||
|
__asm__ volatile("syscall" : "+r"(rax) : : "rcx", "r11", "memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user