Files
MontaukOS/programs/src/desktop/apps/app_procmgr.cpp
T

247 lines
7.6 KiB
C++

/*
* app_procmgr.cpp
* MontaukOS Desktop - Process Manager
* Copyright (c) 2026 Daniel Hammer
*/
#include "apps_common.hpp"
// ============================================================================
// Process Manager state
// ============================================================================
static constexpr int PM_TOOLBAR_H = 36;
static constexpr int PM_HEADER_H = 24;
static constexpr int PM_ITEM_H = 28;
static constexpr int PM_MAX_PROCS = 16;
static constexpr int PM_POLL_MS = 1000;
struct ProcMgrState {
DesktopState* desktop;
Montauk::ProcInfo procs[PM_MAX_PROCS];
int proc_count;
int selected; // selected row index (-1 = none)
uint64_t last_poll_ms;
};
// ============================================================================
// Callbacks
// ============================================================================
static void procmgr_on_poll(Window* win) {
ProcMgrState* pm = (ProcMgrState*)win->app_data;
if (!pm) return;
uint64_t now = montauk::get_milliseconds();
if (now - pm->last_poll_ms < PM_POLL_MS) return;
pm->last_poll_ms = now;
// Remember previously selected PID
int prev_pid = -1;
if (pm->selected >= 0 && pm->selected < pm->proc_count) {
prev_pid = pm->procs[pm->selected].pid;
}
pm->proc_count = montauk::proclist(pm->procs, PM_MAX_PROCS);
// Restore selection by matching PID
pm->selected = -1;
if (prev_pid >= 0) {
for (int i = 0; i < pm->proc_count; i++) {
if (pm->procs[i].pid == prev_pid) {
pm->selected = i;
break;
}
}
}
win->dirty = true;
}
static void procmgr_on_draw(Window* win, Framebuffer& fb) {
ProcMgrState* pm = (ProcMgrState*)win->app_data;
if (!pm) return;
Canvas c(win);
c.fill(colors::WINDOW_BG);
int fh = system_font_height();
// --- Toolbar ---
c.fill_rect(0, 0, c.w, PM_TOOLBAR_H, Color::from_rgb(0xF5, 0xF5, 0xF5));
c.hline(0, PM_TOOLBAR_H - 1, c.w, colors::BORDER);
// "End Process" button
int btn_w = 100;
int btn_h = 26;
int btn_x = 8;
int btn_y = (PM_TOOLBAR_H - btn_h) / 2;
Color btn_bg = (pm->selected >= 0 && pm->procs[pm->selected].pid != 0)
? Color::from_rgb(0xCC, 0x33, 0x33)
: Color::from_rgb(0xAA, 0xAA, 0xAA);
c.button(btn_x, btn_y, btn_w, btn_h, "End Process", btn_bg, colors::WHITE, 4);
// Process count on right
char count_str[24];
snprintf(count_str, sizeof(count_str), "%d processes", pm->proc_count);
int cw = text_width(count_str);
c.text(c.w - cw - 12, (PM_TOOLBAR_H - fh) / 2, count_str, colors::TEXT_COLOR);
// --- Header row ---
int header_y = PM_TOOLBAR_H;
c.fill_rect(0, header_y, c.w, PM_HEADER_H, Color::from_rgb(0xF0, 0xF0, 0xF0));
int col_pid = 12;
int col_name = 64;
int col_mem = c.w - 120;
int col_state = c.w - 52;
int ty = header_y + (PM_HEADER_H - fh) / 2;
c.text(col_pid, ty, "PID", Color::from_rgb(0x66, 0x66, 0x66));
c.text(col_name, ty, "Name", Color::from_rgb(0x66, 0x66, 0x66));
c.text(col_mem, ty, "Mem", Color::from_rgb(0x66, 0x66, 0x66));
c.text(col_state, ty, "St", Color::from_rgb(0x66, 0x66, 0x66));
c.hline(0, header_y + PM_HEADER_H - 1, c.w, colors::BORDER);
// --- Process rows ---
int list_y = PM_TOOLBAR_H + PM_HEADER_H;
for (int i = 0; i < pm->proc_count; i++) {
int row_y = list_y + i * PM_ITEM_H;
if (row_y + PM_ITEM_H > c.h) break;
// Selected row highlight
if (i == pm->selected) {
c.fill_rect(0, row_y, c.w, PM_ITEM_H, colors::MENU_HOVER);
}
int ry = row_y + (PM_ITEM_H - fh) / 2;
// PID (right-aligned in PID column)
char pid_str[8];
snprintf(pid_str, sizeof(pid_str), "%d", (int)pm->procs[i].pid);
int pid_w = text_width(pid_str);
c.text(col_pid + 32 - pid_w, ry, pid_str, colors::TEXT_COLOR);
// Name (truncated to fit)
c.text(col_name, ry, pm->procs[i].name, colors::TEXT_COLOR);
// Memory
char mem_str[16];
format_size(mem_str, (int)pm->procs[i].heapUsed);
c.text(col_mem, ry, mem_str, colors::TEXT_COLOR);
// State
const char* st_str;
Color st_color;
switch (pm->procs[i].state) {
case 2: // Running
st_str = "Run";
st_color = Color::from_rgb(0x22, 0x88, 0x22);
break;
case 1: // Ready
st_str = "Rdy";
st_color = Color::from_rgb(0x33, 0x66, 0xCC);
break;
case 3: // Terminated
st_str = "Term";
st_color = Color::from_rgb(0xCC, 0x33, 0x33);
break;
default:
st_str = "?";
st_color = colors::TEXT_COLOR;
break;
}
c.text(col_state, ry, st_str, st_color);
}
}
static void procmgr_on_mouse(Window* win, MouseEvent& ev) {
ProcMgrState* pm = (ProcMgrState*)win->app_data;
if (!pm) return;
Rect cr = win->content_rect();
int lx = ev.x - cr.x;
int ly = ev.y - cr.y;
if (ev.left_pressed()) {
// Check "End Process" button click
int btn_w = 100;
int btn_h = 26;
int btn_x = 8;
int btn_y = (PM_TOOLBAR_H - btn_h) / 2;
Rect btn_rect = {btn_x, btn_y, btn_w, btn_h};
if (btn_rect.contains(lx, ly)) {
if (pm->selected >= 0 && pm->selected < pm->proc_count
&& pm->procs[pm->selected].pid != 0) {
montauk::kill(pm->procs[pm->selected].pid);
pm->last_poll_ms = 0; // force refresh
}
return;
}
// Check row click
int list_y = PM_TOOLBAR_H + PM_HEADER_H;
if (ly >= list_y) {
int row = (ly - list_y) / PM_ITEM_H;
if (row >= 0 && row < pm->proc_count) {
pm->selected = row;
} else {
pm->selected = -1;
}
}
}
}
static void procmgr_on_key(Window* win, const Montauk::KeyEvent& key) {
ProcMgrState* pm = (ProcMgrState*)win->app_data;
if (!pm || !key.pressed) return;
if (key.scancode == 0x48) { // Up arrow
if (pm->selected > 0) pm->selected--;
else if (pm->proc_count > 0) pm->selected = 0;
} else if (key.scancode == 0x50) { // Down arrow
if (pm->selected < pm->proc_count - 1) pm->selected++;
} else if (key.scancode == 0x53) { // Delete key
if (pm->selected >= 0 && pm->selected < pm->proc_count
&& pm->procs[pm->selected].pid != 0) {
montauk::kill(pm->procs[pm->selected].pid);
pm->last_poll_ms = 0; // force refresh
}
}
}
static void procmgr_on_close(Window* win) {
if (win->app_data) {
montauk::mfree(win->app_data);
win->app_data = nullptr;
}
}
// ============================================================================
// Process Manager launcher
// ============================================================================
void open_procmgr(DesktopState* ds) {
int idx = desktop_create_window(ds, "Processes", 180, 80, 520, 400);
if (idx < 0) return;
Window* win = &ds->windows[idx];
ProcMgrState* pm = (ProcMgrState*)montauk::malloc(sizeof(ProcMgrState));
montauk::memset(pm, 0, sizeof(ProcMgrState));
pm->desktop = ds;
pm->selected = -1;
pm->last_poll_ms = 0;
// Initial poll
pm->proc_count = montauk::proclist(pm->procs, PM_MAX_PROCS);
win->app_data = pm;
win->on_draw = procmgr_on_draw;
win->on_mouse = procmgr_on_mouse;
win->on_key = procmgr_on_key;
win->on_close = procmgr_on_close;
win->on_poll = procmgr_on_poll;
}