feat(taskman): implement scrollbar and UI optimizations

This commit is contained in:
boreddevnl 2026-05-11 19:52:07 +02:00
parent d3a353c9f8
commit 309f68df48
2 changed files with 166 additions and 77 deletions

View file

@ -5,7 +5,7 @@ CC = x86_64-elf-gcc
AS = nasm AS = nasm
LD = x86_64-elf-ld LD = x86_64-elf-ld
CFLAGS = -Wall -Wextra -std=gnu11 -ffreestanding -O2 -fno-stack-protector -fno-stack-check -fno-lto -fno-pie -m64 -march=x86-64 -mno-red-zone -I. -Ilibc -I../sys CFLAGS = -Wall -Wextra -std=gnu11 -ffreestanding -O2 -fno-stack-protector -fno-stack-check -fno-lto -fno-pie -m64 -march=x86-64 -mno-red-zone -I. -Ilibc -I../sys -I../wm
LDFLAGS = -m elf_x86_64 -nostdlib -static -no-pie -Ttext=0x40000000 --no-dynamic-linker -z text -z max-page-size=0x1000 -e _start LDFLAGS = -m elf_x86_64 -nostdlib -static -no-pie -Ttext=0x40000000 --no-dynamic-linker -z text -z max-page-size=0x1000 -e _start
BIN_DIR = bin BIN_DIR = bin

View file

@ -5,6 +5,7 @@
#include "syscall.h" #include "syscall.h"
#include "libui.h" #include "libui.h"
#include "stdlib.h" #include "stdlib.h"
#include "libwidget.h"
#define COLOR_DARK_BG 0xFF121212 #define COLOR_DARK_BG 0xFF121212
#define COLOR_DARK_PANEL 0xFF1E1E1E #define COLOR_DARK_PANEL 0xFF1E1E1E
@ -20,7 +21,7 @@
#define GRAPH_POINTS 60 #define GRAPH_POINTS 60
static ui_window_t win_taskman; static ui_window_t win_taskman;
static ProcessInfo proc_list[32]; static ProcessInfo proc_list[64];
static int proc_count = 0; static int proc_count = 0;
static int selected_proc = -1; static int selected_proc = -1;
@ -36,14 +37,49 @@ static uint64_t used_mem_system = 0;
static char cpu_model_name[64] = "Unknown CPU"; static char cpu_model_name[64] = "Unknown CPU";
static int cpu_cores = 1; static int cpu_cores = 1;
// libwidget integration
static widget_context_t ctx;
static widget_scrollbar_t proc_sb;
static int scroll_offset = 0;
static void ctx_draw_rect(void *user_data, int x, int y, int w, int h, uint32_t color) {
ui_draw_rect((ui_window_t)(uintptr_t)user_data, x, y, w, h, color);
}
static void ctx_draw_rounded_rect_filled(void *user_data, int x, int y, int w, int h, int r, uint32_t color) {
ui_draw_rounded_rect_filled((ui_window_t)(uintptr_t)user_data, x, y, w, h, r, color);
}
static void ctx_draw_string(void *user_data, int x, int y, const char *str, uint32_t color) {
ui_draw_string((ui_window_t)(uintptr_t)user_data, x, y, str, color);
}
static int ctx_measure_string_width(void *user_data, const char *str) {
(void)user_data;
return (int)ui_get_string_width(str);
}
static void ctx_mark_dirty(void *user_data, int x, int y, int w, int h) {
ui_mark_dirty((ui_window_t)(uintptr_t)user_data, x, y, w, h);
}
static void on_scroll_proc(void *user_data, int new_scroll_y) {
(void)user_data;
scroll_offset = new_scroll_y;
}
static int find_value(const char *buf, const char *key) { static int find_value(const char *buf, const char *key) {
char *p = (char*)buf; char *p = (char*)buf;
int key_len = strlen(key); int key_len = strlen(key);
while (*p) { while (*p) {
if (memcmp(p, key, key_len) == 0 && p[key_len] == ':') { if (memcmp(p, key, key_len) == 0) {
p += key_len + 1; char *tp = p + key_len;
while (*p == ' ') p++; while (*tp == ' ' || *tp == '\t') tp++;
return atoi(p); if (*tp == ':') {
tp++;
while (*tp == ' ' || *tp == '\t') tp++;
return atoi(tp);
}
} }
while (*p && *p != '\n') p++; while (*p && *p != '\n') p++;
if (*p == '\n') p++; if (*p == '\n') p++;
@ -55,15 +91,19 @@ static void find_string(const char *buf, const char *key, char *out, int max_len
char *p = (char*)buf; char *p = (char*)buf;
int key_len = strlen(key); int key_len = strlen(key);
while (*p) { while (*p) {
if (memcmp(p, key, key_len) == 0 && p[key_len] == ':') { if (memcmp(p, key, key_len) == 0) {
p += key_len + 1; char *tp = p + key_len;
while (*p == ' ') p++; while (*tp == ' ' || *tp == '\t') tp++;
int i = 0; if (*tp == ':') {
while (*p && *p != '\n' && i < max_len - 1) { tp++;
out[i++] = *p++; while (*tp == ' ' || *tp == '\t') tp++;
int i = 0;
while (*tp && *tp != '\n' && i < max_len - 1) {
out[i++] = *tp++;
}
out[i] = 0;
return;
} }
out[i] = 0;
return;
} }
while (*p && *p != '\n') p++; while (*p && *p != '\n') p++;
if (*p == '\n') p++; if (*p == '\n') p++;
@ -72,8 +112,8 @@ static void find_string(const char *buf, const char *key, char *out, int max_len
} }
static void update_proc_list(void) { static void update_proc_list(void) {
FAT32_FileInfo entries[64]; FAT32_FileInfo entries[128];
int count = sys_list("/proc", entries, 64); int count = sys_list("/proc", entries, 128);
if (count < 0) return; if (count < 0) return;
proc_count = 0; proc_count = 0;
@ -105,18 +145,20 @@ static void update_proc_list(void) {
sys_close(fd); sys_close(fd);
if (bytes > 0) { if (bytes > 0) {
buf[bytes] = 0; buf[bytes] = 0;
proc_list[proc_count].pid = pid;
find_string(buf, "Name", proc_list[proc_count].name, 64);
proc_list[proc_count].used_memory = (size_t)find_value(buf, "Memory") * 1024;
uint64_t ticks = (uint64_t)find_value(buf, "Ticks"); uint64_t ticks = (uint64_t)find_value(buf, "Ticks");
proc_list[proc_count].ticks = ticks; bool is_idle = find_value(buf, "Idle") == 1;
total_ticks_now += ticks; total_ticks_now += ticks;
proc_list[proc_count].is_idle = find_value(buf, "Idle") == 1; if (proc_count < 64 && !is_idle) {
proc_list[proc_count].pid = pid;
if (!proc_list[proc_count].is_idle) user_ticks_now += ticks; find_string(buf, "Name", proc_list[proc_count].name, 64);
proc_count++; proc_list[proc_count].used_memory = (size_t)find_value(buf, "Memory") * 1024;
if (proc_count >= 32) break; proc_list[proc_count].ticks = ticks;
proc_list[proc_count].is_idle = is_idle;
user_ticks_now += ticks;
proc_count++;
}
} }
} }
} }
@ -268,18 +310,39 @@ static void draw_taskman(void) {
ui_draw_string(win_taskman, 60, 125, "NAME", COLOR_DIM_TEXT); ui_draw_string(win_taskman, 60, 125, "NAME", COLOR_DIM_TEXT);
ui_draw_string(win_taskman, 250, 125, "MEMORY", COLOR_DIM_TEXT); ui_draw_string(win_taskman, 250, 125, "MEMORY", COLOR_DIM_TEXT);
// Process Rows
int row = 0; int list_x = 10;
for (int i = 0; i < proc_count && row < MAX_VISIBLE_PROCS; i++) { int list_y = 150;
int list_w = 380;
int list_h = 480 - 150 - 80; // Leave space for kill button
int row_h = 26;
int content_h = proc_count * row_h;
widget_scrollbar_update(&proc_sb, content_h, scroll_offset);
// Draw scrollbar if content exceeds height
if (content_h > list_h) {
proc_sb.h = list_h;
proc_sb.x = list_x + list_w - 12;
proc_sb.y = list_y;
widget_scrollbar_draw(&ctx, &proc_sb);
list_w -= 15; // Shrink list area to make room for scrollbar
}
// Clipping and Drawing rows
for (int i = 0; i < proc_count; i++) {
if (proc_list[i].pid == 0xFFFFFFFF) continue; if (proc_list[i].pid == 0xFFFFFFFF) continue;
int ry = 150 + row * 26; int ry = list_y + (i * row_h) - scroll_offset;
uint32_t bg = (selected_proc == row) ? 0xFF334455 : COLOR_DARK_PANEL;
ui_draw_rounded_rect_filled(win_taskman, 10, ry, 380, 24, 4, bg); if (ry + row_h <= list_y || ry >= list_y + list_h) continue;
uint32_t bg = (selected_proc == i) ? 0xFF334455 : COLOR_DARK_PANEL;
ui_draw_rounded_rect_filled(win_taskman, list_x, ry, list_w, row_h - 2, 4, bg);
char pid_str[16]; char pid_str[16];
itoa(proc_list[i].pid, pid_str); itoa(proc_list[i].pid, pid_str);
ui_draw_string(win_taskman, 20, ry + 6, pid_str, COLOR_DARK_TEXT); ui_draw_string(win_taskman, list_x + 10, ry + 6, pid_str, COLOR_DARK_TEXT);
char name_disp[28]; char name_disp[28];
if (strlen(proc_list[i].name) > 22) { if (strlen(proc_list[i].name) > 22) {
@ -288,13 +351,11 @@ static void draw_taskman(void) {
} else { } else {
strcpy(name_disp, proc_list[i].name); strcpy(name_disp, proc_list[i].name);
} }
ui_draw_string(win_taskman, 65, ry + 6, name_disp, COLOR_DARK_TEXT); ui_draw_string(win_taskman, list_x + 55, ry + 6, name_disp, COLOR_DARK_TEXT);
char m_str[32]; char m_str[32];
format_mem_smart(proc_list[i].used_memory, m_str); format_mem_smart(proc_list[i].used_memory, m_str);
ui_draw_string(win_taskman, 255, ry + 6, m_str, COLOR_DARK_TEXT); ui_draw_string(win_taskman, list_x + 245, ry + 6, m_str, COLOR_DARK_TEXT);
row++;
} }
// Kill button (Positioned relative to window height) // Kill button (Positioned relative to window height)
@ -329,6 +390,18 @@ static void draw_taskman(void) {
int main(void) { int main(void) {
win_taskman = ui_window_create("Task Manager", 100, 100, 400, 480); win_taskman = ui_window_create("Task Manager", 100, 100, 400, 480);
// Initialize libwidget context
ctx.user_data = (void*)(uintptr_t)win_taskman;
ctx.draw_rect = ctx_draw_rect;
ctx.draw_rounded_rect_filled = ctx_draw_rounded_rect_filled;
ctx.draw_string = ctx_draw_string;
ctx.measure_string_width = ctx_measure_string_width;
ctx.mark_dirty = ctx_mark_dirty;
ctx.use_light_theme = false;
widget_scrollbar_init(&proc_sb, 388, 150, 12, 250);
proc_sb.on_scroll = on_scroll_proc;
int fd_c = sys_open("/proc/cpuinfo", "r"); int fd_c = sys_open("/proc/cpuinfo", "r");
if (fd_c >= 0) { if (fd_c >= 0) {
char buf[1024]; char buf[1024];
@ -336,8 +409,8 @@ int main(void) {
sys_close(fd_c); sys_close(fd_c);
if (bytes > 0) { if (bytes > 0) {
buf[bytes] = 0; buf[bytes] = 0;
find_string(buf, "Processor", cpu_model_name, 64); find_string(buf, "model name", cpu_model_name, 64);
int cores = find_value(buf, "Cores"); int cores = find_value(buf, "cpu cores");
if (cores > 0) cpu_cores = cores; if (cores > 0) cpu_cores = cores;
} }
} }
@ -346,60 +419,76 @@ int main(void) {
gui_event_t ev; gui_event_t ev;
uint64_t last_update = 30; // Force update on start
while (1) { while (1) {
bool needs_redraw = false;
// Drain events // Drain events
while (ui_get_event(win_taskman, &ev)) { while (ui_get_event(win_taskman, &ev)) {
if (ev.type == GUI_EVENT_CLOSE) { if (ev.type == GUI_EVENT_CLOSE) {
sys_exit(0); sys_exit(0);
} else if (ev.type == GUI_EVENT_CLICK) { } else if (ev.type == GUI_EVENT_CLICK || ev.type == GUI_EVENT_MOUSE_DOWN) {
int mx = ev.arg1; int mx = ev.arg1;
int my = ev.arg2; int my = ev.arg2;
bool clicked = (ev.type == GUI_EVENT_CLICK);
bool down = (ev.type == GUI_EVENT_MOUSE_DOWN);
if (mx >= 10 && mx < 390 && my >= 150 && my < 150 + MAX_VISIBLE_PROCS * 26) { // Handle scrollbar
int idx = (my - 150) / 26; if (widget_scrollbar_handle_mouse(&proc_sb, mx, my, down, NULL)) {
int valid_count = 0; needs_redraw = true;
int target_i = -1; } else if (clicked) {
for (int i = 0; i < proc_count; i++) { // Handle process selection
if (proc_list[i].pid != 0xFFFFFFFF) { int list_x = 10;
if (valid_count == idx) { target_i = i; break; } int list_y = 150;
valid_count++; int list_h = 480 - 150 - 80;
} int row_h = 26;
}
if (target_i != -1) selected_proc = idx;
else selected_proc = -1;
draw_taskman(); if (mx >= list_x && mx < list_x + 380 && my >= list_y && my < list_y + list_h) {
ui_mark_dirty(win_taskman, 0, 0, 400, 480); int idx = (my - list_y + scroll_offset) / row_h;
} else if (mx >= 290 && mx < 390 && my >= 410 && my < 440) { if (idx >= 0 && idx < proc_count) {
if (selected_proc != -1) { selected_proc = idx;
int valid_count = 0; } else {
for (int i = 0; i < proc_count; i++) { selected_proc = -1;
if (proc_list[i].pid != 0xFFFFFFFF) { }
if (valid_count == selected_proc) { needs_redraw = true;
if (proc_list[i].pid != 0) sys_kill(proc_list[i].pid); } else if (mx >= 290 && mx < 390 && my >= 410 && my < 440) {
break; // Kill button
} if (selected_proc != -1) {
valid_count++; if (proc_list[selected_proc].pid != 0) sys_kill(proc_list[selected_proc].pid);
} selected_proc = -1;
update_proc_list();
needs_redraw = true;
} }
selected_proc = -1;
update_proc_list();
draw_taskman();
ui_mark_dirty(win_taskman, 0, 0, 400, 480);
} }
} }
} else if (ev.type == GUI_EVENT_MOUSE_MOVE) {
if (proc_sb.is_dragging) {
widget_scrollbar_handle_mouse(&proc_sb, ev.arg1, ev.arg2, true, NULL);
needs_redraw = true;
}
} else if (ev.type == GUI_EVENT_MOUSE_WHEEL) {
scroll_offset -= (ev.arg1 * 20); // 20px per notch
if (scroll_offset < 0) scroll_offset = 0;
int max_scroll = (proc_count * 26) - (480 - 150 - 80);
if (max_scroll < 0) max_scroll = 0;
if (scroll_offset > max_scroll) scroll_offset = max_scroll;
needs_redraw = true;
} else if (ev.type == GUI_EVENT_PAINT) { } else if (ev.type == GUI_EVENT_PAINT) {
draw_taskman(); needs_redraw = true;
ui_mark_dirty(win_taskman, 0, 0, 400, 480);
} }
} }
update_proc_list(); if (last_update++ >= 30) {
draw_taskman(); update_proc_list();
ui_mark_dirty(win_taskman, 0, 0, 400, 480); needs_redraw = true;
last_update = 0;
}
// Proper blocking sleep (200ms) if (needs_redraw) {
sys_system(SYSTEM_CMD_SLEEP, 200, 0, 0, 0); draw_taskman();
ui_mark_dirty(win_taskman, 0, 0, 400, 480);
}
sys_system(SYSTEM_CMD_SLEEP, 16, 0, 0, 0);
} }
return 0; return 0;