feat(lvgl): adapt LVGL for opencoreC with SSE2 SIMD blit, hardware tick, and desktop toast IPC

This commit is contained in:
RarDog
2026-09-02 21:44:55 +03:00
parent 74bfa45eea
commit e9d9b8e365
8 changed files with 196 additions and 23 deletions
+3 -2
View File
@@ -28,6 +28,7 @@ COMMON_FLAGS := -Wall -Wextra -O2 -ffreestanding -fno-stack-protector \
CFLAGS := $(COMMON_FLAGS) -std=c11
CXXFLAGS := $(COMMON_FLAGS) -std=c++20 -fno-exceptions -fno-rtti -I$(LIB_DIR)/libipc_cpp/include
GUI_CXXFLAGS := $(filter-out -mno-sse -mno-sse2 -mno-avx, $(CXXFLAGS)) -msse -msse2
# Outputs
KERNEL_BIN := target/x86_64-unknown-none/release/opencore_kernel
@@ -79,7 +80,7 @@ hal: libc
lvgl: $(LVGL_LIB)
$(LVGL_LIB):
$(LVGL_LIB): lib/lvgl/lv_conf.h
@mkdir -p $(BUILD_DIR)/lvgl
@echo "[CC] Compiling LVGL v8.3 Graphics Library..."
@python3 scripts/build_lvgl.py $(CC) "$(CFLAGS)" $(BUILD_DIR)
@@ -106,7 +107,7 @@ servers: libc hal $(LVGL_LIB)
@$(LD) -T $(CONFIG_DIR)/user.ld -nostdlib -static -o $(PROCMGR) \
$(BUILD_DIR)/procmgr_main.o $(BUILD_DIR)/ipc.o $(LIBC_OBJS)
@# Desktop GUI Server (LVGL v8.3)
@$(CXX) $(CXXFLAGS) -Ilib/lvgl -c $(SERVERS_DIR)/gui_server/main.cpp -o $(BUILD_DIR)/gui_main.o
@$(CXX) $(GUI_CXXFLAGS) -Ilib/lvgl -c $(SERVERS_DIR)/gui_server/main.cpp -o $(BUILD_DIR)/gui_main.o
@$(LD) -T $(CONFIG_DIR)/user.ld -nostdlib -static -o $(GUI_SERVER) \
$(BUILD_DIR)/gui_main.o $(BUILD_DIR)/ipc.o $(NET_OBJS) $(LVGL_LIB) $(LIBC_OBJS)
+2
View File
@@ -27,6 +27,8 @@
#define SYS_GETRANDOM 22 /* Hardware RNG: fill buffer with random bytes */
#define SYS_ACPI_INFO 23 /* Query ACPI power and table statistics */
#define SYS_CPU_INFO 24 /* Query multi-core CPU core count and current core ID */
#define SYS_NOTIFY_SEND 25 /* Post desktop notification to GUI compositor */
#define SYS_NOTIFY_RECV 26 /* Poll pending desktop notifications non-blockingly */
/* POSIX Process Signals */
#define SIGHUP 1
+34
View File
@@ -577,12 +577,46 @@ pub extern "C" fn kernel_syscall_dispatcher(regs: &mut SyscallRegisters) {
}
regs.rax = (-1i64) as u64;
}
// SYS_NOTIFY_SEND = 25 (Post desktop notification to GUI compositor)
25 => {
let title = regs.rdi;
let body = regs.rsi;
unsafe {
if NOTIF_COUNT < 8 {
let idx = (NOTIF_HEAD + NOTIF_COUNT) % 8;
NOTIF_QUEUE[idx] = (title, body);
NOTIF_COUNT += 1;
regs.rax = 0;
return;
}
}
regs.rax = (-1i64) as u64;
}
// SYS_NOTIFY_RECV = 26 (Poll pending desktop notifications non-blockingly)
26 => {
unsafe {
if NOTIF_COUNT > 0 {
let (title, body) = NOTIF_QUEUE[NOTIF_HEAD];
NOTIF_HEAD = (NOTIF_HEAD + 1) % 8;
NOTIF_COUNT -= 1;
regs.rax = 1; // Notification available
regs.rdi = title;
regs.rsi = body;
return;
}
}
regs.rax = 0; // Queue empty
}
_ => {
regs.rax = (-1i64) as u64; // SYS_ERR_INVALID_ARG
}
}
}
static mut NOTIF_QUEUE: [(u64, u64); 8] = [(0, 0); 8];
static mut NOTIF_HEAD: usize = 0;
static mut NOTIF_COUNT: usize = 0;
#[repr(C)]
pub struct AcpiInfoPayload {
pub found: u32,
+4 -1
View File
@@ -96,8 +96,11 @@ pub unsafe fn load_elf(elf_bytes: &[u8], hhdm_offset: u64) -> Result<LoadedProce
(*vmm_ptr).map_page(pml4_paddr, page_vaddr, page_paddr, flags)
.map_err(|_| "Failed to map user ELF page")?;
// Copy data from file
// Zero the destination page first (ensures .bss is clean)
let dest = (page_paddr + hhdm_offset) as *mut u8;
core::ptr::write_bytes(dest, 0, PAGE_SIZE);
// Copy data from file if within filesz
let page_file_start = page * PAGE_SIZE;
if page_file_start < filesz {
let copy_len = core::cmp::min(PAGE_SIZE, filesz - page_file_start);
+3 -6
View File
@@ -85,13 +85,10 @@
/*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()`)*/
#define LV_TICK_CUSTOM 0
#define LV_TICK_CUSTOM 1
#if LV_TICK_CUSTOM
#define LV_TICK_CUSTOM_INCLUDE "Arduino.h" /*Header for the system time function*/
#define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis()) /*Expression evaluating to current system time in ms*/
/*If using lvgl as ESP32 component*/
// #define LV_TICK_CUSTOM_INCLUDE "esp_timer.h"
// #define LV_TICK_CUSTOM_SYS_TIME_EXPR ((esp_timer_get_time() / 1000LL))
#define LV_TICK_CUSTOM_INCLUDE "lv_kernel_tick.h" /*Header for the system time function*/
#define LV_TICK_CUSTOM_SYS_TIME_EXPR (custom_tick_get()) /*Expression evaluating to current system time in ms*/
#endif /*LV_TICK_CUSTOM*/
/*Default Dot Per Inch. Used to initialize default sizes such as widgets sized, style paddings.
+16
View File
@@ -0,0 +1,16 @@
#ifndef LV_KERNEL_TICK_H
#define LV_KERNEL_TICK_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
uint32_t custom_tick_get(void);
#ifdef __cplusplus
}
#endif
#endif // LV_KERNEL_TICK_H
+121 -9
View File
@@ -15,10 +15,26 @@
#include "lvgl.h"
#include "abi/syscalls.h"
#include "abi/ipc.h"
#include "abi/net.h"
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include <emmintrin.h>
// Accurate hardware tick source for LVGL (LV_TICK_CUSTOM)
extern "C" uint32_t custom_tick_get(void) {
uint32_t lo, hi;
__asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi));
uint64_t tsc = ((uint64_t)hi << 32) | lo;
static uint64_t s_base_tsc = 0;
if (s_base_tsc == 0) {
s_base_tsc = tsc;
return 0;
}
// Scale by ~2.0 GHz virtual CPU cycle frequency: 2,000,000 cycles per ms
return (uint32_t)((tsc - s_base_tsc) / 2000000ULL);
}
// Framebuffer and display driver structures
static fb_info_t g_fb = {0, 0, 0, 0, 0};
@@ -29,8 +45,8 @@ 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: 80-row raster bands for high-speed flush
#define BUF_HEIGHT 80
// Double-buffering: 200-row raster bands for high-speed full-band flush (4 sweeps for 1280x800)
#define BUF_HEIGHT 200
static lv_color_t g_buf1[1280 * BUF_HEIGHT];
static lv_color_t g_buf2[1280 * BUF_HEIGHT];
static lv_disp_draw_buf_t g_draw_buf;
@@ -48,6 +64,40 @@ static lv_obj_t *g_win_taskmgr = NULL;
static lv_obj_t *g_win_network = NULL;
static lv_obj_t *g_win_about = NULL;
// Desktop Toast Notification
static lv_obj_t *g_toast_card = NULL;
static uint32_t g_toast_expiry = 0;
static void show_desktop_toast(const char *title, const char *msg) {
if (g_toast_card) {
lv_obj_del(g_toast_card);
g_toast_card = NULL;
}
g_toast_card = lv_obj_create(lv_layer_top());
lv_obj_set_size(g_toast_card, 340, 85);
lv_obj_align(g_toast_card, LV_ALIGN_BOTTOM_RIGHT, -20, -65);
lv_obj_set_style_bg_color(g_toast_card, lv_color_hex(0x181825), 0);
lv_obj_set_style_border_color(g_toast_card, lv_color_hex(0x89b4fa), 0);
lv_obj_set_style_border_width(g_toast_card, 2, 0);
lv_obj_set_style_radius(g_toast_card, 8, 0);
lv_obj_set_style_shadow_width(g_toast_card, 0, 0);
lv_obj_clear_flag(g_toast_card, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_t *lbl_t = lv_label_create(g_toast_card);
lv_label_set_text(lbl_t, title);
lv_obj_set_style_text_color(lbl_t, lv_color_hex(0x89b4fa), 0);
lv_obj_align(lbl_t, LV_ALIGN_TOP_LEFT, 4, 0);
lv_obj_t *lbl_m = lv_label_create(g_toast_card);
lv_label_set_text(lbl_m, msg);
lv_obj_set_style_text_color(lbl_m, lv_color_hex(0xcdd6f4), 0);
lv_obj_align(lbl_m, LV_ALIGN_BOTTOM_LEFT, 4, -4);
lv_obj_move_foreground(g_toast_card);
g_toast_expiry = custom_tick_get() + 8000; // 8 seconds duration
}
// Taskbar buttons
static lv_obj_t *g_btn_tb_console = NULL;
static lv_obj_t *g_btn_tb_taskmgr = NULL;
@@ -66,16 +116,43 @@ static lv_obj_t *g_lbl_ram_detail = NULL;
static lv_obj_t *g_tbl_procs = NULL;
static lv_obj_t *g_lbl_net_ping_res = NULL;
// Flush callback: writes dirty regions directly to Limine linear framebuffer
// Hardware-accelerated SSE2 SIMD blitter with direct streaming stores
static inline void sse2_blit_row(uint32_t *dst, const uint32_t *src, size_t count) {
size_t i = 0;
// Process 16 pixels (64 bytes) per unrolled step
for (; i + 15 < count; i += 16) {
__m128i p0 = _mm_loadu_si128((const __m128i *)(src + i + 0));
__m128i p1 = _mm_loadu_si128((const __m128i *)(src + i + 4));
__m128i p2 = _mm_loadu_si128((const __m128i *)(src + i + 8));
__m128i p3 = _mm_loadu_si128((const __m128i *)(src + i + 12));
_mm_storeu_si128((__m128i *)(dst + i + 0), p0);
_mm_storeu_si128((__m128i *)(dst + i + 4), p1);
_mm_storeu_si128((__m128i *)(dst + i + 8), p2);
_mm_storeu_si128((__m128i *)(dst + i + 12), p3);
}
// Process remaining 4 pixels (16 bytes)
for (; i + 3 < count; i += 4) {
__m128i p = _mm_loadu_si128((const __m128i *)(src + i));
_mm_storeu_si128((__m128i *)(dst + i), p);
}
// Remainder pixels
for (; i < count; i++) {
dst[i] = src[i];
}
}
// Flush callback: writes dirty regions directly to Limine linear framebuffer using SSE2
static void disp_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_p) {
uint32_t *fb = (uint32_t *)g_fb.fb_addr;
uint32_t pitch_pixels = (uint32_t)(g_fb.pitch / 4);
int32_t w = area->x2 - area->x1 + 1;
const uint32_t *src = (const uint32_t *)color_p;
for (int32_t y = area->y1; y <= area->y2; y++) {
uint32_t *dst = fb + (y * pitch_pixels) + area->x1;
memcpy(dst, color_p, w * sizeof(uint32_t));
color_p += w;
sse2_blit_row(dst, src, (size_t)w);
src += w;
}
lv_disp_flush_ready(drv);
@@ -332,6 +409,10 @@ static void execute_console_command(const char *raw_cmd) {
} else if (strncmp(raw_cmd, "echo ", 5) == 0) {
snprintf(buf, sizeof(buf), "%s\n", raw_cmd + 5);
lv_textarea_add_text(g_txt_term_output, buf);
} else if (strncmp(raw_cmd, "notify ", 7) == 0) {
show_desktop_toast("Terminal Notification", raw_cmd + 7);
snprintf(buf, sizeof(buf), "Dispatched desktop notification: '%s'\n", raw_cmd + 7);
lv_textarea_add_text(g_txt_term_output, buf);
} else if (strcmp(raw_cmd, "mem") == 0 || strcmp(raw_cmd, "free") == 0) {
sysinfo_data_t info = {0, 0, 0, 0, 0};
register uint64_t rax __asm__("rax") = SYS_SYSINFO;
@@ -1045,10 +1126,13 @@ int main() {
g_fb.width, g_fb.height, g_fb.bpp, g_fb.pitch, g_fb.fb_addr);
// 2. Initialize LVGL v8.3 Library and Network subsystem
printf("[GUI-SERVER] Calling lv_init()...\n");
lv_init();
printf("[GUI-SERVER] lv_init() OK. Calling net_init()...\n");
net_init();
printf("[GUI-SERVER] net_init() OK.\n");
// 3. Register Framebuffer Display Driver (Double-buffered with 80 rows)
// 3. Register Framebuffer Display Driver (Double-buffered with 200 rows)
lv_disp_draw_buf_init(&g_draw_buf, g_buf1, g_buf2, 1280 * BUF_HEIGHT);
lv_disp_drv_init(&g_disp_drv);
g_disp_drv.hor_res = (lv_coord_t)g_fb.width;
@@ -1056,6 +1140,7 @@ int main() {
g_disp_drv.flush_cb = disp_flush_cb;
g_disp_drv.draw_buf = &g_draw_buf;
lv_disp_drv_register(&g_disp_drv);
printf("[GUI-SERVER] Display driver registered.\n");
// 4. Register Hardware PS/2 Mouse Input Driver
lv_indev_drv_init(&g_mouse_indev_drv);
@@ -1078,18 +1163,45 @@ int main() {
create_desktop_environment();
printf("[GUI-SERVER] Windows/KDE Desktop UI Initialized. Entering Compositor Event Loop.\n");
// Welcome Desktop Notification
show_desktop_toast("opencoreC Desktop", "System online. SMP 4 Cores active.");
// 7. Compositor Event Loop
uint32_t loop_count = 0;
while (1) {
lv_task_handler();
lv_tick_inc(5);
if (loop_count == 1) {
printf("[GUI-SERVER] Compositor Loop Running! LVGL Tick=%u\n", custom_tick_get());
}
// 8. Auto-expire desktop toast notification
if (g_toast_card && custom_tick_get() > g_toast_expiry) {
lv_obj_del(g_toast_card);
g_toast_card = NULL;
}
// 9. Fast-Path Window Messaging & Notification Listener (SYS_NOTIFY_RECV)
register uint64_t rax __asm__("rax") = SYS_NOTIFY_RECV;
register uint64_t rdi __asm__("rdi");
register uint64_t rsi __asm__("rsi");
__asm__ volatile("syscall" : "+r"(rax), "=r"(rdi), "=r"(rsi) : : "rcx", "r11", "memory");
if ((int64_t)rax > 0) {
uint64_t title_val = rdi;
uint64_t body_val = rsi;
char title[9] = {0};
char body[9] = {0};
memcpy(title, &title_val, 8);
memcpy(body, &body_val, 8);
show_desktop_toast(title[0] ? title : "Notification", body[0] ? body : "System event");
}
loop_count++;
if (loop_count % 200 == 0) { // Every ~1 second
if (loop_count % 60 == 0) { // Every ~1 second
update_live_metrics();
}
// Small delay (~4ms) for smooth interaction without excessive CPU spinning
// Delay (~4ms) for smooth interaction without excessive CPU spinning
for (volatile int d = 0; d < 6000; d++);
}
+13 -5
View File
@@ -522,11 +522,19 @@ static void handle_command(char *cmd) {
printf("%u packets transmitted, %u received, %u%% packet loss\n",
(unsigned int)transmitted, (unsigned int)received,
(unsigned int)(transmitted > 0 ? ((transmitted - received) * 100 / transmitted) : 0));
} else if (strcmp(cmd, "gui") == 0) {
puts("[GUI] opencoreC LVGL Desktop Compositor is active (TID 5).");
puts("[GUI] Framebuffer: 1280x800 @ 32bpp, PS/2 Hardware Mouse Tracking: Active.");
puts("[GUI] Windows: Task Manager / Resource Monitor, Interactive Terminal, Network Diagnostics.");
puts("[GUI] -> To open the interactive desktop window with mouse & keyboard, run: 'make gui'");
} else if (strncmp(cmd, "notify ", 7) == 0) {
const char *text = cmd + 7;
uint64_t title_packed = 0;
uint64_t body_packed = 0;
const char title_str[] = "Terminal";
memcpy(&title_packed, title_str, sizeof(title_str) - 1);
memcpy(&body_packed, text, strlen(text) < 8 ? strlen(text) : 8);
register uint64_t rax __asm__("rax") = SYS_NOTIFY_SEND;
register uint64_t rdi __asm__("rdi") = title_packed;
register uint64_t rsi __asm__("rsi") = body_packed;
__asm__ volatile("syscall" : "+r"(rax) : "r"(rdi), "r"(rsi) : "rcx", "r11", "memory");
printf("[NOTIFY] Dispatched desktop notification to GUI Compositor: '%s'\n", text);
} else if (strncmp(cmd, "write ", 6) == 0) {
const char *msg = cmd + 6;
ipc::Endpoint uart_service(UART_ENDPOINT);