diff --git a/boredos.iso b/boredos.iso index 8233200..02bdf8f 100644 Binary files a/boredos.iso and b/boredos.iso differ diff --git a/build/cmd.o b/build/cmd.o index d5a96fb..800cd0e 100644 Binary files a/build/cmd.o and b/build/cmd.o differ diff --git a/build/explorer.o b/build/explorer.o index 3783df7..eee6b02 100644 Binary files a/build/explorer.o and b/build/explorer.o differ diff --git a/build/idt.o b/build/idt.o index ce48bd2..2a0666e 100644 Binary files a/build/idt.o and b/build/idt.o differ diff --git a/build/main.o b/build/main.o index b5766f4..b06f10a 100644 Binary files a/build/main.o and b/build/main.o differ diff --git a/build/platform.o b/build/platform.o index b9c04ae..9b393b9 100644 Binary files a/build/platform.o and b/build/platform.o differ diff --git a/build/ps2.o b/build/ps2.o index 0390d56..1cf1627 100644 Binary files a/build/ps2.o and b/build/ps2.o differ diff --git a/src/kernel/platform.c b/src/kernel/platform.c index a009f98..6878011 100644 --- a/src/kernel/platform.c +++ b/src/kernel/platform.c @@ -51,3 +51,21 @@ uint64_t v2p(uint64_t virt) { } return virt; } +void platform_get_cpu_model(char *model) { + uint32_t brand[12]; + uint32_t eax, ebx, ecx, edx; + + for (uint32_t i = 0; i < 3; i++) { + asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(0x80000002 + i)); + brand[i * 4 + 0] = eax; + brand[i * 4 + 1] = ebx; + brand[i * 4 + 2] = ecx; + brand[i * 4 + 3] = edx; + } + + char *p = (char *)brand; + for (int i = 0; i < 48; i++) { + model[i] = p[i]; + } + model[48] = '\0'; +} diff --git a/src/kernel/platform.h b/src/kernel/platform.h index 7cd6781..4f9c595 100644 --- a/src/kernel/platform.h +++ b/src/kernel/platform.h @@ -9,5 +9,6 @@ void platform_init(void); uint64_t p2v(uint64_t phys); uint64_t v2p(uint64_t virt); +void platform_get_cpu_model(char *model); #endif diff --git a/src/kernel/process.c b/src/kernel/process.c index 529e98f..5337624 100644 --- a/src/kernel/process.c +++ b/src/kernel/process.c @@ -15,12 +15,16 @@ extern void cmd_write(const char *str); extern void serial_write(const char *str); #define MAX_PROCESSES 16 -static process_t processes[MAX_PROCESSES] __attribute__((aligned(16))); -static int process_count = 0; +process_t processes[MAX_PROCESSES] __attribute__((aligned(16))); +int process_count = 0; static process_t* current_process = NULL; static uint32_t next_pid = 0; void process_init(void) { + for (int i = 0; i < MAX_PROCESSES; i++) { + processes[i].pid = 0xFFFFFFFF; + } + // Current kernel execution is PID 0 process_t *kernel_proc = &processes[process_count++]; kernel_proc->pid = next_pid++; @@ -36,6 +40,10 @@ void process_init(void) { for (int i = 0; i < MAX_PROCESS_FDS; i++) kernel_proc->fds[i] = NULL; + extern void mem_memcpy(void *dest, const void *src, size_t len); + mem_memcpy(kernel_proc->name, "kernel", 7); + kernel_proc->ticks = 0; + kernel_proc->next = kernel_proc; // Circular linked list current_process = kernel_proc; } @@ -121,9 +129,12 @@ void process_create(void* entry_point, bool is_user) { asm volatile("fninit"); new_proc->fpu_initialized = true; - // Add to linked list + // Add to linked list (Critical Section) + uint64_t rflags; + asm volatile("pushfq; pop %0; cli" : "=r"(rflags)); new_proc->next = current_process->next; current_process->next = new_proc; + asm volatile("push %0; popfq" : : "r"(rflags)); } process_t* process_create_elf(const char* filepath, const char* args_str) { @@ -165,11 +176,23 @@ process_t* process_create_elf(const char* filepath, const char* args_str) { return NULL; } + // Set process name from filepath + int last_slash = -1; + for (int i = 0; filepath[i]; i++) if (filepath[i] == '/') last_slash = i; + const char *filename = (last_slash == -1) ? filepath : (filepath + last_slash + 1); + int ni = 0; + while (filename[ni] && ni < 63) { + new_proc->name[ni] = filename[ni]; + ni++; + } + new_proc->name[ni] = 0; + new_proc->ticks = 0; + // 3. Allocate generic User stack and Kernel stack for interrupts // Increase to 256KB to prevent stack smashing on heavy networking size_t user_stack_size = 262144; void* stack = kmalloc_aligned(user_stack_size, 4096); - void* kernel_stack = kmalloc_aligned(32768, 32768); + void* kernel_stack = kmalloc_aligned(65536, 65536); // Map User stack to 0x800000 for (uint64_t i = 0; i < (user_stack_size / 4096); i++) { @@ -248,7 +271,7 @@ process_t* process_create_elf(const char* filepath, const char* args_str) { current_user_sp &= ~15ULL; // 4. Build Stack Frame for context switch via IRETQ - uint64_t* stack_ptr = (uint64_t*)((uint64_t)kernel_stack + 32768); + uint64_t* stack_ptr = (uint64_t*)((uint64_t)kernel_stack + 65536); *(--stack_ptr) = 0x1B; // SS (User Mode Data) *(--stack_ptr) = current_user_sp; // RSP (Updated user stack pointer) *(--stack_ptr) = 0x202; // RFLAGS (Interrupts Enabled) @@ -279,7 +302,7 @@ process_t* process_create_elf(const char* filepath, const char* args_str) { asm volatile("fninit"); asm volatile("fxsave %0" : "=m"(*stack_ptr)); - new_proc->kernel_stack = (uint64_t)kernel_stack + 32768; + new_proc->kernel_stack = (uint64_t)kernel_stack + 65536; new_proc->kernel_stack_alloc = kernel_stack; new_proc->user_stack_alloc = stack; new_proc->rsp = (uint64_t)stack_ptr; @@ -288,11 +311,12 @@ process_t* process_create_elf(const char* filepath, const char* args_str) { asm volatile("fninit"); new_proc->fpu_initialized = true; - // Slot is already counted in process_count if new, or reused. - - // Add to linked list + // Add to linked list (Critical Section) + uint64_t rflags; + asm volatile("pushfq; pop %0; cli" : "=r"(rflags)); new_proc->next = current_process->next; current_process->next = new_proc; + asm volatile("push %0; popfq" : : "r"(rflags)); serial_write("[PROCESS] Spawned ELF Executable: "); serial_write(filepath); @@ -308,13 +332,24 @@ uint64_t process_schedule(uint64_t current_rsp) { if (!current_process || !current_process->next || current_process == current_process->next) return current_rsp; - // serial_write("SCHED\n"); - - // Save/Restore context + // Save context current_process->rsp = current_rsp; - // Switch process - current_process = current_process->next; + // Switch to next ready process + extern uint32_t wm_get_ticks(void); + uint32_t now = wm_get_ticks(); + + process_t *start = current_process; + process_t *next_proc = current_process->next; + + while (next_proc != start) { + if (next_proc->pid == 0 || next_proc->sleep_until == 0 || next_proc->sleep_until <= now) { + break; + } + next_proc = next_proc->next; + } + + current_process = next_proc; // Update Kernel Stack for User Mode interrupts and System Calls if (current_process->is_user && current_process->kernel_stack) { @@ -326,9 +361,18 @@ uint64_t process_schedule(uint64_t current_rsp) { // Switch page table paging_switch_directory(current_process->pml4_phys); + current_process->ticks++; + return current_process->rsp; } +process_t* process_get_by_pid(uint32_t pid) { + for (int i = 0; i < MAX_PROCESSES; i++) { + if (processes[i].pid == pid) return &processes[i]; + } + return NULL; +} + static void process_cleanup_inner(process_t *proc) { if (!proc || proc->pid == 0xFFFFFFFF) return; @@ -356,7 +400,7 @@ static void process_cleanup_inner(process_t *proc) { } void process_terminate(process_t *to_delete) { - if (!to_delete || to_delete->pid == 0xFFFFFFFF) return; + if (!to_delete || to_delete->pid == 0xFFFFFFFF || to_delete->pid == 0) return; uint64_t rflags; asm volatile("pushfq; pop %0; cli" : "=r"(rflags)); @@ -389,8 +433,9 @@ void process_terminate(process_t *to_delete) { to_delete->pid = 0xFFFFFFFF; if (to_delete->user_stack_alloc) kfree(to_delete->user_stack_alloc); + if (to_delete->kernel_stack_alloc) kfree(to_delete->kernel_stack_alloc); to_delete->user_stack_alloc = NULL; - to_delete->kernel_stack_alloc = NULL; + to_delete->kernel_stack_alloc = NULL; asm volatile("push %0; popfq" : : "r"(rflags)); } @@ -399,7 +444,7 @@ uint64_t process_terminate_current(void) { uint64_t rflags; asm volatile("pushfq; pop %0; cli" : "=r"(rflags)); - if (!current_process) { + if (!current_process || current_process->pid == 0) { asm volatile("push %0; popfq" : : "r"(rflags)); return 0; } diff --git a/src/kernel/process.h b/src/kernel/process.h index cdd3f29..09812fe 100644 --- a/src/kernel/process.h +++ b/src/kernel/process.h @@ -6,6 +6,7 @@ #include #include +#include #include "gui_ipc.h" #define MAX_GUI_EVENTS 32 @@ -13,9 +14,8 @@ struct FAT32_FileHandle; -// Registers saved on the stack by interrupts/exceptions typedef struct registers_t { - uint8_t fxsave_region[512]; // SSE/FPU state, MUST be at the bottom (lowest address) + uint8_t fxsave_region[512]; uint64_t r15, r14, r13, r12, r11, r10, r9, r8; uint64_t rbp, rdi, rsi, rdx, rcx, rbx, rax; uint64_t int_no, err_code; @@ -24,31 +24,42 @@ typedef struct registers_t { typedef struct process { uint32_t pid; - uint64_t rsp; // Current stack pointer representing context - uint64_t pml4_phys; // Physical address of the page table - uint64_t kernel_stack; // Ring 0 stack pointer for user mode switches + uint64_t rsp; + uint64_t pml4_phys; + uint64_t kernel_stack; bool is_user; gui_event_t gui_events[MAX_GUI_EVENTS]; int gui_event_head; int gui_event_tail; - void *ui_window; // Pointer to the active Window + void *ui_window; uint64_t heap_start; uint64_t heap_end; void *fds[MAX_PROCESS_FDS]; - void *kernel_stack_alloc; // Original pointer from kmalloc for freeing - void *user_stack_alloc; // Original pointer from kmalloc for freeing + void *kernel_stack_alloc; + void *user_stack_alloc; bool is_terminal_proc; struct process *next; bool fpu_initialized; + + char name[64]; + uint64_t ticks; + uint64_t sleep_until; } __attribute__((aligned(16))) process_t; +typedef struct { + uint32_t pid; + char name[64]; + uint64_t ticks; + size_t used_memory; +} ProcessInfo; + void process_init(void); void process_create(void* entry_point, bool is_user); process_t* process_create_elf(const char* filepath, const char* args_str); @@ -56,6 +67,7 @@ process_t* process_get_current(void); uint64_t process_schedule(uint64_t current_rsp); uint64_t process_terminate_current(void); void process_terminate(process_t *proc); +process_t* process_get_by_pid(uint32_t pid); void process_push_gui_event(process_t *proc, gui_event_t *ev); process_t* process_get_by_ui_window(void* win); diff --git a/src/kernel/ps2.c b/src/kernel/ps2.c index 1a8c943..3649764 100644 --- a/src/kernel/ps2.c +++ b/src/kernel/ps2.c @@ -14,11 +14,11 @@ extern void serial_print_hex(uint64_t n); volatile uint64_t kernel_ticks = 0; uint64_t timer_handler(registers_t *regs) { - outb(0x20, 0x20); // EOI as fast as possible kernel_ticks++; wm_timer_tick(); network_process_frames(); + outb(0x20, 0x20); // EOI after processing to prevent nested timer interrupts extern uint64_t process_schedule(uint64_t current_rsp); return process_schedule((uint64_t)regs); } diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index d6743c7..b72bcad 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -811,6 +811,7 @@ static uint64_t syscall_handler_inner(registers_t *regs) { return old_end; } else if (syscall_num == 5) { // SYS_SYSTEM int cmd = (int)arg1; + process_t *proc = process_get_current(); if (cmd == 1) { // SYSTEM_CMD_SET_BG_COLOR uint32_t color = (uint32_t)arg2; extern void graphics_set_bg_color(uint32_t color); @@ -1068,6 +1069,51 @@ static uint64_t syscall_handler_inner(registers_t *regs) { size_t max_len = (size_t)arg3; extern int network_tcp_recv_nb(void *buf, size_t max_len); return (uint64_t)network_tcp_recv_nb(buf, max_len); + } else if (cmd == SYSTEM_CMD_PROCESS_LIST) { + ProcessInfo *out = (ProcessInfo *)arg2; + int max_procs = (int)arg3; + if (!out) return 0; + + extern process_t processes[]; + extern int process_count; + int count = 0; + for (int i = 0; i < 16; i++) { // MAX_PROCESSES is 16 + if (processes[i].pid != 0xFFFFFFFF) { + out[count].pid = processes[i].pid; + extern void mem_memcpy(void *dest, const void *src, size_t len); + mem_memcpy(out[count].name, processes[i].name, 64); + out[count].ticks = processes[i].ticks; + + // Memory estimation: heap + stacks + size_t mem = 0; + if (processes[i].heap_end > processes[i].heap_start) + mem += (processes[i].heap_end - processes[i].heap_start); + + if (processes[i].pid == 0) { + // For kernel, we can report a more realistic figure if we want, + // but 32KB is specifically its stack. Let's keep it but maybe + // add a note in documentation. + mem = 32768; + } else { + if (processes[i].is_user) mem += 262144; // User stack + mem += 32768; // Kernel stack + } + + out[count].used_memory = mem; + + count++; + if (count >= max_procs) break; + } + } + return (uint64_t)count; + } else if (cmd == SYSTEM_CMD_GET_CPU_MODEL) { + char *user_buf = (char *)arg2; + if (!user_buf) return -1; + char model[64]; + platform_get_cpu_model(model); + extern void mem_memcpy(void *dest, const void *src, size_t len); + mem_memcpy(user_buf, model, 49); + return 0; } return -1; } @@ -1079,16 +1125,47 @@ uint64_t syscall_handler_c(registers_t *regs) { uint64_t syscall_num = regs->rax; // Check for context-switching syscalls - if (syscall_num == 0 || syscall_num == 60 || syscall_num == 10) { // EXIT or KILL + if (syscall_num == 0 || syscall_num == 60) { // EXIT return process_terminate_current(); } + if (syscall_num == 10) { // KILL + uint32_t target_pid = (uint32_t)regs->rdi; + process_t *current = process_get_current(); + if (target_pid == 0) { + // Protect kernel process + regs->rax = -1; + return (uint64_t)regs; + } + if (target_pid == 0xFFFFFFFF || target_pid == current->pid) { + return process_terminate_current(); + } else { + process_t *target = process_get_by_pid(target_pid); + if (target) { + process_terminate(target); + } + regs->rax = 0; + return (uint64_t)regs; + } + } + if (syscall_num == 5 && regs->rdi == 43) { // SYSTEM_CMD_YIELD extern uint64_t process_schedule(uint64_t current_rsp); regs->rax = 0; return process_schedule((uint64_t)regs); } + if (syscall_num == 5 && regs->rdi == 46) { // SYSTEM_CMD_SLEEP + uint32_t ms = (uint32_t)regs->rsi; + process_t *proc = process_get_current(); + extern uint32_t wm_get_ticks(void); + uint32_t ticks = ms / 16; + if (ticks == 0 && ms > 0) ticks = 1; + proc->sleep_until = wm_get_ticks() + ticks; + regs->rax = 0; + return process_schedule((uint64_t)regs); + } + // Normal syscalls regs->rax = syscall_handler_inner(regs); diff --git a/src/kernel/syscall.h b/src/kernel/syscall.h index 78fd788..df24444 100644 --- a/src/kernel/syscall.h +++ b/src/kernel/syscall.h @@ -39,6 +39,13 @@ typedef struct registers_t registers_t; #define FS_CMD_CHDIR 13 #define FS_CMD_GET_INFO 14 +#define SYSTEM_CMD_SET_RAW_MODE 41 +#define SYSTEM_CMD_TCP_RECV_NB 42 +#define SYSTEM_CMD_YIELD 43 +#define SYSTEM_CMD_PROCESS_LIST 44 +#define SYSTEM_CMD_GET_CPU_MODEL 45 +#define SYSTEM_CMD_SLEEP 46 + void syscall_init(void); uint64_t syscall_handler_c(registers_t *regs); diff --git a/src/kernel/syscalls.asm b/src/kernel/syscalls.asm index 5cf4429..0f3f3b5 100644 --- a/src/kernel/syscalls.asm +++ b/src/kernel/syscalls.asm @@ -15,7 +15,9 @@ section .text ; R9 = arg5 syscall_entry: - ; 1. Switch to Kernel Stack + ; 1. Switch to Kernel Stack safely + ; Note: For true SMP safety, we need per-CPU storage (via swapgs). + ; For now, we use a global scratch which is only safe because we mask interrupts on entry. mov [rel user_rsp_scratch], rsp mov rsp, [rel kernel_syscall_stack] @@ -52,9 +54,7 @@ syscall_entry: ; 4. Call C handler with registers_t* mov rdi, rsp - sti call syscall_handler_c - cli ; 5. Switch to the resulting RSP (might be different if task switched) mov rsp, rax @@ -80,6 +80,11 @@ syscall_entry: pop rbx pop rax add rsp, 16 ; drop int_no/err_code + + ; Debug: check RIP before iretq + ; We can't easily print from here without destroying registers, + ; but we can at least check if it's canonical. + iretq section .bss diff --git a/src/kernel/userland/about.c b/src/kernel/userland/about.c index 765bfa9..604f700 100644 --- a/src/kernel/userland/about.c +++ b/src/kernel/userland/about.c @@ -2,8 +2,9 @@ // This software is released under the GNU General Public License v3.0. See LICENSE file for details. // This header needs to maintain in any file it is present in, as per the GPL license terms. #include "syscall.h" -#include "libui.h" -#include +#include "libc/libui.h" +#include "libc/stdlib.h" +#include static uint32_t ansi_to_boredos_color(int code) { uint32_t default_color = 0xFFFFFFFF; @@ -121,7 +122,7 @@ int main(void) { } } else { // Avoid high CPU usage - for(volatile int i=0; i<10000; i++); + sleep(10); } } diff --git a/src/kernel/userland/browser.c b/src/kernel/userland/browser.c index 3e2758a..9836f8a 100644 --- a/src/kernel/userland/browser.c +++ b/src/kernel/userland/browser.c @@ -6,7 +6,7 @@ #include #include #include -#include +#include "libc/stdlib.h" static int win_w = 1280; static int win_h = 960; @@ -1734,9 +1734,9 @@ int main(int argc, char **argv) { } if (gif_updated) { browser_paint(); ui_mark_dirty(win_browser, 0, 0, win_w, win_h); + } else { + sleep(10); } - - for(volatile int x=0; x<10000; x++); } return 0; } \ No newline at end of file diff --git a/src/kernel/userland/calculator.c b/src/kernel/userland/calculator.c index eae4ebb..83aa2da 100644 --- a/src/kernel/userland/calculator.c +++ b/src/kernel/userland/calculator.c @@ -4,6 +4,7 @@ #include "syscall.h" #include "libui.h" #include +#include "stdlib.h" #define SCALE 1000000LL @@ -254,8 +255,7 @@ int main(void) { sys_exit(0); } } else { - // Avoid high CPU usage - for(volatile int i=0; i<10000; i++); + sleep(10); } } diff --git a/src/kernel/userland/clock.c b/src/kernel/userland/clock.c index f87b96c..882cb08 100644 --- a/src/kernel/userland/clock.c +++ b/src/kernel/userland/clock.c @@ -256,7 +256,7 @@ int main(void) { } else { long long now = sys_system(16, 0, 0, 0, 0); if (now - last_rep >= 6) { clock_paint(); ui_mark_dirty(win_clock, 0, 0, WIN_W, WIN_H); last_rep = now; } - for(volatile int i=0; i<5000; i++); + sleep(10); } } return 0; diff --git a/src/kernel/userland/libc/stdlib.c b/src/kernel/userland/libc/stdlib.c index 9768734..f517741 100644 --- a/src/kernel/userland/libc/stdlib.c +++ b/src/kernel/userland/libc/stdlib.c @@ -292,8 +292,7 @@ char* getcwd(char *buf, int size) { } void sleep(int ms) { - - (void)ms; + sys_system(46, ms, 0, 0, 0); } void exit(int status) { diff --git a/src/kernel/userland/libc/syscall.h b/src/kernel/userland/libc/syscall.h index 2341f91..1f07196 100644 --- a/src/kernel/userland/libc/syscall.h +++ b/src/kernel/userland/libc/syscall.h @@ -68,6 +68,9 @@ #define SYSTEM_CMD_DNS_LOOKUP 37 #define SYSTEM_CMD_SET_DNS 38 #define SYSTEM_CMD_NET_UNLOCK 39 +#define SYSTEM_CMD_PROCESS_LIST 44 +#define SYSTEM_CMD_GET_CPU_MODEL 45 +#define SYSTEM_CMD_SLEEP 46 #define SYSTEM_CMD_SET_RAW_MODE 41 #define SYSTEM_CMD_TCP_RECV_NB 42 #define SYSTEM_CMD_YIELD 43 @@ -113,6 +116,13 @@ typedef struct { int sys_list(const char *path, FAT32_FileInfo *entries, int max_entries); int sys_get_file_info(const char *path, FAT32_FileInfo *info); +typedef struct { + uint32_t pid; + char name[64]; + uint64_t ticks; + size_t used_memory; +} ProcessInfo; + // Network API typedef struct { uint8_t bytes[6]; } net_mac_address_t; typedef struct { uint8_t bytes[4]; } net_ipv4_address_t; diff --git a/src/kernel/userland/markdown.c b/src/kernel/userland/markdown.c index 3387ba3..4438704 100644 --- a/src/kernel/userland/markdown.c +++ b/src/kernel/userland/markdown.c @@ -3,6 +3,7 @@ // This header needs to maintain in any file it is present in, as per the GPL license terms. #include "libc/syscall.h" #include "libc/libui.h" +#include "libc/stdlib.h" #include #include #include @@ -441,6 +442,8 @@ int main(int argc, char **argv) { } else if (ev.type == GUI_EVENT_CLOSE) { sys_exit(0); } + } else { + sleep(10); } } return 0; diff --git a/src/kernel/userland/minesweeper.c b/src/kernel/userland/minesweeper.c index 7222bb6..3c5ce96 100644 --- a/src/kernel/userland/minesweeper.c +++ b/src/kernel/userland/minesweeper.c @@ -6,6 +6,7 @@ #include #include #include +#include "libc/stdlib.h" #define COLOR_DARK_BG 0xFF121212 #define COLOR_DARK_PANEL 0xFF202020 diff --git a/src/kernel/userland/notepad.c b/src/kernel/userland/notepad.c index b4515cd..4230300 100644 --- a/src/kernel/userland/notepad.c +++ b/src/kernel/userland/notepad.c @@ -3,6 +3,7 @@ // This header needs to maintain in any file it is present in, as per the GPL license terms. #include "libc/syscall.h" #include "libc/libui.h" +#include "libc/stdlib.h" #include "libc/syscall_user.h" #include @@ -257,8 +258,7 @@ int main(int argc, char **argv) { sys_exit(0); } } else { - - for(volatile int i=0; i<10000; i++); + sleep(10); } } diff --git a/src/kernel/userland/paint.c b/src/kernel/userland/paint.c index aa1b799..4153735 100644 --- a/src/kernel/userland/paint.c +++ b/src/kernel/userland/paint.c @@ -247,6 +247,8 @@ int main(int argc, char **argv) { } else if (ev.type == GUI_EVENT_CLOSE) { sys_exit(0); } + } else { + sleep(10); } } return 0; diff --git a/src/kernel/userland/settings.c b/src/kernel/userland/settings.c index 8ab15d8..b894cff 100644 --- a/src/kernel/userland/settings.c +++ b/src/kernel/userland/settings.c @@ -825,6 +825,8 @@ int main(int argc, char **argv) { } else if (ev.type == GUI_EVENT_CLOSE) { sys_exit(0); } + } else { + sleep(10); } } return 0; diff --git a/src/kernel/userland/sort.c b/src/kernel/userland/sort.c index 666c531..d8cd07e 100644 --- a/src/kernel/userland/sort.c +++ b/src/kernel/userland/sort.c @@ -138,8 +138,7 @@ int main(void) { goto exit_app; } } - // No explicit sleep, rendering takes some time and provides natural visual delay. - // If it's too fast, we'll add sleep(1) here later. + sleep(2); } } @@ -168,13 +167,14 @@ int main(void) { goto exit_app; } } + sleep(2); } } start = start + 1; } else { // Sort is done, just render and idle render_state(win); - sleep(50); + sleep(200); } } diff --git a/src/kernel/userland/taskman.c b/src/kernel/userland/taskman.c new file mode 100644 index 0000000..8657d02 --- /dev/null +++ b/src/kernel/userland/taskman.c @@ -0,0 +1,306 @@ +// Copyright (c) 2023-2026 Chris (boreddevnl) +// This software is released under the GNU General Public License v3.0. See LICENSE file for details. +#include "syscall.h" +#include "libui.h" +#include "stdlib.h" + +#define COLOR_DARK_BG 0xFF121212 +#define COLOR_DARK_PANEL 0xFF1E1E1E +#define COLOR_DARK_TEXT 0xFFE0E0E0 +#define COLOR_DARK_BORDER 0xFF333333 +#define COLOR_ACCENT 0xFF4FC3F7 +#define COLOR_CPU 0xFF81C784 +#define COLOR_MEM 0xFFFFB74D +#define COLOR_KILL 0xFFE57373 +#define COLOR_DIM_TEXT 0xFF999999 + +#define MAX_VISIBLE_PROCS 10 +#define GRAPH_POINTS 60 + +static ui_window_t win_taskman; +static ProcessInfo proc_list[32]; +static int proc_count = 0; +static int selected_proc = -1; + +// History as fixed-point (x100) +static int cpu_history[GRAPH_POINTS]; +static int mem_history[GRAPH_POINTS]; +static int history_idx = 0; + +static uint64_t uptime_prev = 0; +static uint64_t kernel_ticks_prev = 0; +static uint64_t total_mem_system = 0; +static uint64_t used_mem_system = 0; +static char cpu_model_name[64] = "Unknown CPU"; + +typedef struct { + size_t total_memory; + size_t used_memory; + size_t available_memory; + size_t allocated_blocks; + size_t free_blocks; + size_t largest_free_block; + size_t smallest_free_block; + size_t fragmentation_percent; + size_t peak_memory_used; +} MemStats; + +static void update_proc_list(void) { + proc_count = sys_system(SYSTEM_CMD_PROCESS_LIST, (uint64_t)proc_list, 32, 0, 0); + + uint64_t uptime_now = sys_system(SYSTEM_CMD_UPTIME, 0, 0, 0, 0); + uint64_t kernel_ticks_now = 0; + + for (int i = 0; i < proc_count; i++) { + if (proc_list[i].pid == 0) { + kernel_ticks_now = proc_list[i].ticks; + break; + } + } + + if (uptime_prev > 0) { + uint64_t total_delta = uptime_now - uptime_prev; + if (total_delta > 0) { + uint64_t kernel_delta = kernel_ticks_now - kernel_ticks_prev; + if (kernel_delta > total_delta) kernel_delta = total_delta; + + uint64_t used_delta = total_delta - kernel_delta; + int usage = (int)((used_delta * 100) / total_delta); + cpu_history[history_idx] = usage; + } + } + + uptime_prev = uptime_now; + kernel_ticks_prev = kernel_ticks_now; + + MemStats stats; + sys_system(SYSTEM_CMD_MEMINFO, (uint64_t)&stats, 0, 0, 0); + total_mem_system = stats.total_memory; + used_mem_system = stats.used_memory; + mem_history[history_idx] = (int)(stats.used_memory / 1024); + + history_idx = (history_idx + 1) % GRAPH_POINTS; +} + +static void draw_graph(int x, int y, int w, int h, int *data, uint32_t color, int max_val) { + ui_draw_rect(win_taskman, x, y, w, h, COLOR_DARK_PANEL); + ui_draw_rect(win_taskman, x, y, w, 1, COLOR_DARK_BORDER); + ui_draw_rect(win_taskman, x, y + h - 1, w, 1, COLOR_DARK_BORDER); + + if (max_val == 0) max_val = 1; + + for (int i = 0; i < GRAPH_POINTS - 1; i++) { + int idx1 = (history_idx + i) % GRAPH_POINTS; + + long long val = (long long)data[idx1]; + int h_val = (int)((val * h) / max_val); + if (h_val > h) h_val = h; + if (h_val < 0) h_val = 0; + + int x1 = x + (i * w) / GRAPH_POINTS; + int next_x = x + ((i + 1) * w) / GRAPH_POINTS; + int draw_w = next_x - x1; + if (draw_w <= 0) draw_w = 1; + + ui_draw_rect(win_taskman, x1, y + h - h_val, draw_w, h_val ? h_val : 1, color); + } +} + +static void format_gib(uint64_t bytes, char *out) { + uint64_t gib_int = bytes / (1024 * 1024 * 1024); + uint64_t gib_frac = ((bytes % (1024 * 1024 * 1024)) * 100) / (1024 * 1024 * 1024); + + char s_int[16], s_frac[16]; + itoa((int)gib_int, s_int); + itoa((int)gib_frac, s_frac); + + out[0] = 0; + strcat(out, s_int); + strcat(out, "."); + if (gib_frac < 10) strcat(out, "0"); + strcat(out, s_frac); + strcat(out, " GiB"); +} + +static void draw_taskman(void) { + int win_w = 400; + int win_h = 480; + + ui_draw_rect(win_taskman, 0, 0, win_w, win_h, COLOR_DARK_BG); + + // CPU Graph Area + ui_draw_string(win_taskman, 10, 10, "PROCESSOR", COLOR_CPU); + char cpu_label[16]; + int current_cpu = cpu_history[(history_idx + GRAPH_POINTS - 1) % GRAPH_POINTS]; + itoa(current_cpu, cpu_label); + strcat(cpu_label, "%"); + ui_draw_string(win_taskman, 140, 10, cpu_label, COLOR_CPU); + draw_graph(10, 25, 185, 60, cpu_history, COLOR_CPU, 100); + + // CPU Model (Safe truncation) + char model_disp[32]; + int mlen = strlen(cpu_model_name); + if (mlen > 22) { + memcpy(model_disp, cpu_model_name, 19); + model_disp[19] = '.'; model_disp[20] = '.'; model_disp[21] = '.'; model_disp[22] = 0; + } else { + strcpy(model_disp, cpu_model_name); + } + ui_draw_string(win_taskman, 10, 92, model_disp, COLOR_DIM_TEXT); + + // Memory Graph Area + ui_draw_string(win_taskman, 205, 10, "MEMORY", COLOR_MEM); + char mem_pct_label[16]; + int current_mem_pct = 0; + if (total_mem_system > 0) current_mem_pct = (int)((used_mem_system * 100) / total_mem_system); + itoa(current_mem_pct, mem_pct_label); + strcat(mem_pct_label, "%"); + ui_draw_string(win_taskman, 340, 10, mem_pct_label, COLOR_MEM); + + int max_mem_kb = (int)(total_mem_system / 1024); + draw_graph(205, 25, 185, 60, mem_history, COLOR_MEM, max_mem_kb); + + // Memory GiB usage + char s_used[24], s_total[24], mem_text[64]; + format_gib(used_mem_system, s_used); + format_gib(total_mem_system, s_total); + mem_text[0] = 0; + strcat(mem_text, s_used); + strcat(mem_text, " / "); + strcat(mem_text, s_total); + + ui_draw_string(win_taskman, 205, 92, mem_text, COLOR_DIM_TEXT); + + // Process List Header + ui_draw_rect(win_taskman, 10, 120, 380, 24, COLOR_DARK_PANEL); + ui_draw_string(win_taskman, 15, 125, "PID", 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); + + // Process Rows + int row = 0; + for (int i = 0; i < proc_count && row < MAX_VISIBLE_PROCS; 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); + + char pid_str[16]; + itoa(proc_list[i].pid, pid_str); + ui_draw_string(win_taskman, 20, ry + 6, pid_str, COLOR_DARK_TEXT); + + char name_disp[28]; + if (strlen(proc_list[i].name) > 22) { + memcpy(name_disp, proc_list[i].name, 19); + name_disp[19] = '.'; name_disp[20] = '.'; name_disp[21] = '.'; name_disp[22] = 0; + } else { + strcpy(name_disp, proc_list[i].name); + } + ui_draw_string(win_taskman, 65, ry + 6, name_disp, COLOR_DARK_TEXT); + + char m_str[32]; + itoa((int)(proc_list[i].used_memory / 1024), m_str); + strcat(m_str, " KB"); + ui_draw_string(win_taskman, 255, ry + 6, m_str, COLOR_DARK_TEXT); + + row++; + } + + // Kill button (Positioned relative to window height) + int btn_x = 400 - 110; + int btn_y = 480 - 70; + int btn_w = 100; + int btn_h = 30; + + // Disable kill for PID 0 + bool can_kill = (selected_proc != -1); + if (can_kill) { + int v_cnt = 0; + for (int i = 0; i < proc_count; i++) { + if (proc_list[i].pid != 0xFFFFFFFF) { + if (v_cnt == selected_proc) { + if (proc_list[i].pid == 0) can_kill = false; + break; + } + v_cnt++; + } + } + } + + ui_draw_rounded_rect_filled(win_taskman, btn_x, btn_y, btn_w, btn_h, 6, can_kill ? COLOR_KILL : COLOR_DARK_BORDER); + + const char *btn_text = "FORCE KILL"; + int tx = btn_x + (btn_w - 80) / 2; + int ty = btn_y + (btn_h - 12) / 2; + ui_draw_string(win_taskman, tx, ty, btn_text, can_kill ? 0xFFFFFFFF : 0xFF666666); +} + +int main(void) { + win_taskman = ui_window_create("Task Manager", 100, 100, 400, 480); + + // Fetch CPU model + sys_system(SYSTEM_CMD_GET_CPU_MODEL, (uint64_t)cpu_model_name, 0, 0, 0); + + for(int i=0; i= 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++; + } + } + 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++; + } + } + selected_proc = -1; + update_proc_list(); + draw_taskman(); + ui_mark_dirty(win_taskman, 0, 0, 400, 480); + } + } + } else if (ev.type == GUI_EVENT_PAINT) { + draw_taskman(); + ui_mark_dirty(win_taskman, 0, 0, 400, 480); + } + } + + update_proc_list(); + draw_taskman(); + ui_mark_dirty(win_taskman, 0, 0, 400, 480); + + // Proper blocking sleep (200ms) + sys_system(46, 200, 0, 0, 0); // SYSTEM_CMD_SLEEP + } + + return 0; +} diff --git a/src/kernel/userland/txtedit.c b/src/kernel/userland/txtedit.c index 3a9c20c..d6e103e 100644 --- a/src/kernel/userland/txtedit.c +++ b/src/kernel/userland/txtedit.c @@ -3,6 +3,7 @@ // This header needs to maintain in any file it is present in, as per the GPL license terms. #include "libc/syscall.h" #include "libc/libui.h" +#include "libc/stdlib.h" #include #define COLOR_DARK_PANEL 0xFF202020 @@ -426,6 +427,8 @@ int main(int argc, char **argv) { } else if (ev.type == GUI_EVENT_CLOSE) { sys_exit(0); } + } else { + sleep(10); } } return 0; diff --git a/src/kernel/userland/viewer.c b/src/kernel/userland/viewer.c index 5021014..7b401a3 100644 --- a/src/kernel/userland/viewer.c +++ b/src/kernel/userland/viewer.c @@ -307,7 +307,7 @@ int main(int argc, char **argv) { } } // Small sleep to avoid eating 100% CPU - for (volatile int i = 0; i < 10000; i++); + sleep(10); } } return 0; diff --git a/src/kernel/wm.c b/src/kernel/wm.c index c13e36c..37bc45d 100644 --- a/src/kernel/wm.c +++ b/src/kernel/wm.c @@ -409,6 +409,8 @@ static void draw_dock_terminal(int x, int y); static void draw_dock_minesweeper(int x, int y); static void draw_dock_paint(int x, int y); static void draw_dock_clock(int x, int y); +static void draw_dock_taskman(int x, int y); +static void draw_dock_editor(int x, int y); static void draw_dock_editor(int x, int y); static void draw_filled_circle(int cx, int cy, int r, uint32_t color); @@ -795,6 +797,22 @@ void draw_paint_icon(int x, int y, const char *label) { draw_icon_label(x, y, label); } +static void draw_dock_taskman(int x, int y) { + draw_rounded_rect_filled(x, y, 48, 48, 12, 0xFF37474F); // Dark blue-grey + draw_rounded_rect_filled(x+4, y+4, 40, 40, 8, 0xFF455A64); + + // Draw "Activity" lines + draw_rect(x+8, y+24, 6, 12, 0xFF4FC3F7); // Light blue bar + draw_rect(x+16, y+16, 6, 20, 0xFF81C784); // Green bar + draw_rect(x+24, y+20, 6, 16, 0xFFFFB74D); // Orange bar + draw_rect(x+32, y+10, 6, 26, 0xFFE57373); // Red bar +} + +void draw_taskman_icon(int x, int y, const char *label) { + draw_scaled_icon(x, y, draw_dock_taskman); + draw_icon_label(x, y, label); +} + static void draw_filled_circle(int cx, int cy, int r, uint32_t color); // Draw traffic light (close button - red) @@ -1299,7 +1317,7 @@ void wm_paint(void) { int dock_y = sh - dock_h - 6; int dock_item_size = 48; int dock_spacing = 10; - int total_dock_width = 9 * (dock_item_size + dock_spacing); + int total_dock_width = 10 * (dock_item_size + dock_spacing); int dock_bg_x = (sw - total_dock_width) / 2 - 12; int dock_bg_w = total_dock_width + 24; draw_rounded_rect_filled(dock_bg_x, dock_y, dock_bg_w, dock_h, 18, COLOR_DOCK_BG); @@ -1323,6 +1341,8 @@ void wm_paint(void) { dock_x += dock_item_size + dock_spacing; draw_dock_browser(dock_x, dock_item_y); dock_x += dock_item_size + dock_spacing; + draw_dock_taskman(dock_x, dock_item_y); + dock_x += dock_item_size + dock_spacing; draw_dock_clock(dock_x, dock_item_y); // Editor removed from dock @@ -1867,7 +1887,7 @@ void wm_handle_right_click(int x, int y) { int dock_y = sh - dock_h - 6; int dock_item_size = 48; int dock_spacing = 10; - int total_dock_width = 9 * (dock_item_size + dock_spacing); + int total_dock_width = 10 * (dock_item_size + dock_spacing); int dock_bg_x = (sw - total_dock_width) / 2 - 12; int dock_bg_w = total_dock_width + 24; @@ -1886,7 +1906,8 @@ void wm_handle_right_click(int x, int y) { else if (item == 5) start_menu_pending_app = "Minesweeper"; else if (item == 6) start_menu_pending_app = "Paint"; else if (item == 7) start_menu_pending_app = "Browser"; - else if (item == 8) start_menu_pending_app = "Clock"; + else if (item == 8) start_menu_pending_app = "Task Manager"; + else if (item == 9) start_menu_pending_app = "Clock"; } } else { wm_handle_click(mx, my); @@ -2040,6 +2061,10 @@ void wm_handle_right_click(int x, int y) { else process_create_elf("/bin/browser.elf", NULL); } else if (str_starts_with(start_menu_pending_app, "About")) { process_create_elf("/bin/about.elf", NULL); + } else if (str_starts_with(start_menu_pending_app, "Task Manager")) { + Window *existing = wm_find_window_by_title("Task Manager"); + if (existing) wm_bring_to_front(existing); + else process_create_elf("/bin/taskman.elf", NULL); } else if (str_starts_with(start_menu_pending_app, "Shutdown")) { k_shutdown(); } else if (str_starts_with(start_menu_pending_app, "Restart")) {