diff --git a/src/userland/Makefile b/src/userland/Makefile index a6ac9ab..5b80484 100644 --- a/src/userland/Makefile +++ b/src/userland/Makefile @@ -5,7 +5,7 @@ CC = x86_64-elf-gcc AS = nasm 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 BIN_DIR = bin diff --git a/src/userland/gui/taskman.c b/src/userland/gui/taskman.c index dc533ff..e2f2464 100644 --- a/src/userland/gui/taskman.c +++ b/src/userland/gui/taskman.c @@ -5,6 +5,7 @@ #include "syscall.h" #include "libui.h" #include "stdlib.h" +#include "libwidget.h" #define COLOR_DARK_BG 0xFF121212 #define COLOR_DARK_PANEL 0xFF1E1E1E @@ -20,7 +21,7 @@ #define GRAPH_POINTS 60 static ui_window_t win_taskman; -static ProcessInfo proc_list[32]; +static ProcessInfo proc_list[64]; static int proc_count = 0; 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 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) { char *p = (char*)buf; int key_len = strlen(key); while (*p) { - if (memcmp(p, key, key_len) == 0 && p[key_len] == ':') { - p += key_len + 1; - while (*p == ' ') p++; - return atoi(p); + if (memcmp(p, key, key_len) == 0) { + char *tp = p + key_len; + while (*tp == ' ' || *tp == '\t') tp++; + if (*tp == ':') { + tp++; + while (*tp == ' ' || *tp == '\t') tp++; + return atoi(tp); + } } while (*p && *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; int key_len = strlen(key); while (*p) { - if (memcmp(p, key, key_len) == 0 && p[key_len] == ':') { - p += key_len + 1; - while (*p == ' ') p++; - int i = 0; - while (*p && *p != '\n' && i < max_len - 1) { - out[i++] = *p++; + if (memcmp(p, key, key_len) == 0) { + char *tp = p + key_len; + while (*tp == ' ' || *tp == '\t') tp++; + if (*tp == ':') { + tp++; + 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++; 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) { - FAT32_FileInfo entries[64]; - int count = sys_list("/proc", entries, 64); + FAT32_FileInfo entries[128]; + int count = sys_list("/proc", entries, 128); if (count < 0) return; proc_count = 0; @@ -105,18 +145,20 @@ static void update_proc_list(void) { sys_close(fd); if (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"); - proc_list[proc_count].ticks = ticks; + bool is_idle = find_value(buf, "Idle") == 1; + total_ticks_now += ticks; - - proc_list[proc_count].is_idle = find_value(buf, "Idle") == 1; - - if (!proc_list[proc_count].is_idle) user_ticks_now += ticks; - proc_count++; - if (proc_count >= 32) break; + + if (proc_count < 64 && !is_idle) { + 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; + 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, 250, 125, "MEMORY", COLOR_DIM_TEXT); - // Process Rows - int row = 0; - for (int i = 0; i < proc_count && row < MAX_VISIBLE_PROCS; i++) { + + int list_x = 10; + 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; - int ry = 150 + row * 26; - uint32_t bg = (selected_proc == row) ? 0xFF334455 : COLOR_DARK_PANEL; - ui_draw_rounded_rect_filled(win_taskman, 10, ry, 380, 24, 4, bg); + int ry = list_y + (i * row_h) - scroll_offset; + + 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]; 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]; if (strlen(proc_list[i].name) > 22) { @@ -288,13 +351,11 @@ static void draw_taskman(void) { } else { 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]; format_mem_smart(proc_list[i].used_memory, m_str); - ui_draw_string(win_taskman, 255, ry + 6, m_str, COLOR_DARK_TEXT); - - row++; + ui_draw_string(win_taskman, list_x + 245, ry + 6, m_str, COLOR_DARK_TEXT); } // Kill button (Positioned relative to window height) @@ -329,6 +390,18 @@ static void draw_taskman(void) { int main(void) { 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"); if (fd_c >= 0) { char buf[1024]; @@ -336,8 +409,8 @@ int main(void) { sys_close(fd_c); if (bytes > 0) { buf[bytes] = 0; - find_string(buf, "Processor", cpu_model_name, 64); - int cores = find_value(buf, "Cores"); + find_string(buf, "model name", cpu_model_name, 64); + int cores = find_value(buf, "cpu cores"); if (cores > 0) cpu_cores = cores; } } @@ -346,60 +419,76 @@ int main(void) { gui_event_t ev; + uint64_t last_update = 30; // Force update on start while (1) { + bool needs_redraw = false; // Drain events while (ui_get_event(win_taskman, &ev)) { if (ev.type == GUI_EVENT_CLOSE) { 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 my = ev.arg2; - - if (mx >= 10 && mx < 390 && my >= 150 && my < 150 + MAX_VISIBLE_PROCS * 26) { - int idx = (my - 150) / 26; - int valid_count = 0; - int target_i = -1; - for (int i = 0; i < proc_count; i++) { - if (proc_list[i].pid != 0xFFFFFFFF) { - if (valid_count == idx) { target_i = i; break; } - valid_count++; + bool clicked = (ev.type == GUI_EVENT_CLICK); + bool down = (ev.type == GUI_EVENT_MOUSE_DOWN); + + // Handle scrollbar + if (widget_scrollbar_handle_mouse(&proc_sb, mx, my, down, NULL)) { + needs_redraw = true; + } else if (clicked) { + // Handle process selection + int list_x = 10; + int list_y = 150; + int list_h = 480 - 150 - 80; + int row_h = 26; + + if (mx >= list_x && mx < list_x + 380 && my >= list_y && my < list_y + list_h) { + int idx = (my - list_y + scroll_offset) / row_h; + if (idx >= 0 && idx < proc_count) { + selected_proc = idx; + } else { + selected_proc = -1; } - } - if (target_i != -1) selected_proc = idx; - else selected_proc = -1; - - draw_taskman(); - ui_mark_dirty(win_taskman, 0, 0, 400, 480); - } else if (mx >= 290 && mx < 390 && my >= 410 && my < 440) { - if (selected_proc != -1) { - int valid_count = 0; - for (int i = 0; i < proc_count; i++) { - if (proc_list[i].pid != 0xFFFFFFFF) { - if (valid_count == selected_proc) { - if (proc_list[i].pid != 0) sys_kill(proc_list[i].pid); - break; - } - valid_count++; - } + needs_redraw = true; + } else if (mx >= 290 && mx < 390 && my >= 410 && my < 440) { + // Kill button + if (selected_proc != -1) { + 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) { - draw_taskman(); - ui_mark_dirty(win_taskman, 0, 0, 400, 480); + needs_redraw = true; } } - update_proc_list(); - draw_taskman(); - ui_mark_dirty(win_taskman, 0, 0, 400, 480); + if (last_update++ >= 30) { + update_proc_list(); + needs_redraw = true; + last_update = 0; + } - // Proper blocking sleep (200ms) - sys_system(SYSTEM_CMD_SLEEP, 200, 0, 0, 0); + if (needs_redraw) { + draw_taskman(); + ui_mark_dirty(win_taskman, 0, 0, 400, 480); + } + + sys_system(SYSTEM_CMD_SLEEP, 16, 0, 0, 0); } return 0;