// 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_mem_smart(uint64_t bytes, char *out) { if (bytes < 1024) { itoa((int)bytes, out); strcat(out, " B"); } else if (bytes < 1024 * 1024) { itoa((int)(bytes / 1024), out); strcat(out, " KB"); } else if (bytes < 1024 * 1024 * 1024) { // Show MiB with two decimal places uint64_t mib_int = bytes / (1024 * 1024); uint64_t mib_frac = ((bytes % (1024 * 1024)) * 100) / (1024 * 1024); char s_int[16], s_frac[16]; itoa((int)mib_int, s_int); itoa((int)mib_frac, s_frac); strcpy(out, s_int); strcat(out, "."); if (mib_frac < 10) strcat(out, "0"); strcat(out, s_frac); strcat(out, " MiB"); } else { // Show GiB with two decimal places 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); strcpy(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[32], s_total[32], mem_text[64]; format_mem_smart(used_mem_system, s_used); format_mem_smart(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]; format_mem_smart(proc_list[i].used_memory, m_str); 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; }