fix: various bug fixes in desktop components

This commit is contained in:
2026-05-07 20:00:17 +02:00
parent ecf6edb826
commit 69fc184a4e
12 changed files with 311 additions and 85 deletions
@@ -8,6 +8,19 @@
namespace filemanager {
static bool path_is_same_or_descendant(const char* path, const char* root) {
if (!path || !root) return false;
int root_len = montauk::slen(root);
while (root_len > 0 && root[root_len - 1] == '/') root_len--;
if (root_len <= 0) return false;
for (int i = 0; i < root_len; i++) {
if (path[i] == '\0' || path[i] != root[i]) return false;
}
return path[root_len] == '\0' || path[root_len] == '/';
}
void filemanager_do_copy(FileManagerState* fm) {
if (fm->selected < 0 || fm->selected >= fm->entry_count) return;
if (filemanager_is_computer_view(fm) || filemanager_is_virtual_view(fm) ||
@@ -36,11 +49,13 @@ void filemanager_do_paste(FileManagerState* fm) {
const char* basename = path_basename(fm->clipboard_path);
char dst[512];
filemanager_build_fullpath(dst, 512, fm->current_path, basename);
if (montauk::streq(dst, fm->clipboard_path)) return;
// Check if source is a directory by trying to readdir it
const char* probe[1];
int probe_count = montauk::readdir(fm->clipboard_path, probe, 1);
bool src_is_dir = (probe_count >= 0);
if (src_is_dir && path_is_same_or_descendant(dst, fm->clipboard_path)) return;
bool ok;
if (src_is_dir)
@@ -243,7 +243,7 @@ void filemanager_read_dir(FileManagerState* fm) {
// Sort: directories first, then alphabetical (case-insensitive)
for (int i = 1; i < fm->entry_count; i++) {
char tmp_name[64];
char tmp_name[128];
int tmp_type = fm->entry_types[i];
int tmp_size = fm->entry_sizes[i];
bool tmp_isdir = fm->is_dir[i];
@@ -385,9 +385,11 @@ bool filemanager_delete_recursive(const char* path) {
}
bool filemanager_copy_file(const char* src, const char* dst) {
if (!src || !dst || montauk::streq(src, dst)) return false;
int sfd = montauk::open(src);
if (sfd < 0) return false;
int size = (int)montauk::getsize(sfd);
uint64_t size = montauk::getsize(sfd);
// Create destination
int dfd = montauk::fcreate(dst);
@@ -396,33 +398,46 @@ bool filemanager_copy_file(const char* src, const char* dst) {
return false;
}
bool ok = true;
if (size > 0) {
// Cap at 4 MB to avoid exhausting memory
if (size > 4 * 1024 * 1024) {
montauk::close(sfd);
montauk::close(dfd);
montauk::fdelete(dst);
return false;
}
uint8_t* buf = (uint8_t*)montauk::malloc(size);
static constexpr uint64_t COPY_CHUNK = 64 * 1024;
uint64_t buf_size = size < COPY_CHUNK ? size : COPY_CHUNK;
uint8_t* buf = (uint8_t*)montauk::malloc(buf_size);
if (!buf) {
montauk::close(sfd);
montauk::close(dfd);
montauk::fdelete(dst);
return false;
}
montauk::read(sfd, buf, 0, size);
montauk::fwrite(dfd, buf, 0, size);
uint64_t off = 0;
while (off < size) {
uint64_t todo64 = size - off;
if (todo64 > buf_size) todo64 = buf_size;
int todo = (int)todo64;
int got = montauk::read(sfd, buf, off, todo64);
if (got != todo) { ok = false; break; }
int wrote = montauk::fwrite(dfd, buf, off, todo64);
if (wrote != todo) { ok = false; break; }
off += todo64;
}
montauk::mfree(buf);
}
montauk::close(sfd);
montauk::close(dfd);
if (!ok) {
montauk::fdelete(dst);
return false;
}
return true;
}
// Recursive directory copy
bool filemanager_copy_dir_recursive(const char* src, const char* dst) {
if (!src || !dst || montauk::streq(src, dst)) return false;
montauk::fmkdir(dst);
const char* names[64];
@@ -130,12 +130,14 @@ bool settings_handle_appearance_click(Window* win, SettingsState* st, int mx, in
if (grad_option.contains(mx, cy)) {
s.bg_gradient = true;
s.bg_image = false;
desktop_mark_background_dirty(st->desktop);
settings_persist(st);
return true;
}
if (solid_option.contains(mx, cy)) {
s.bg_gradient = false;
s.bg_image = false;
desktop_mark_background_dirty(st->desktop);
settings_persist(st);
return true;
}
@@ -146,6 +148,7 @@ bool settings_handle_appearance_click(Window* win, SettingsState* st, int mx, in
wallpaper_scan_home(st->desktop->home_dir, &st->wp_files);
st->wp_scanned = true;
}
desktop_mark_background_dirty(st->desktop);
settings_persist(st);
return true;
}
@@ -162,6 +165,7 @@ bool settings_handle_appearance_click(Window* win, SettingsState* st, int mx, in
str_append(fullpath, st->wp_files.names[i], 256);
wallpaper_load(&s, fullpath,
st->desktop->screen_w, st->desktop->screen_h);
desktop_mark_background_dirty(st->desktop);
settings_persist(st);
return true;
}
@@ -173,6 +177,7 @@ bool settings_handle_appearance_click(Window* win, SettingsState* st, int mx, in
int idx = mtk::hit_swatch_row(mx, cy, x + 70, y, SWATCH_COUNT, SWATCH_SIZE, SWATCH_GAP);
if (idx >= 0) {
s.bg_grad_top = bg_palette[idx];
desktop_mark_background_dirty(st->desktop);
settings_persist(st);
return true;
}
@@ -181,6 +186,7 @@ bool settings_handle_appearance_click(Window* win, SettingsState* st, int mx, in
idx = mtk::hit_swatch_row(mx, cy, x + 70, y, SWATCH_COUNT, SWATCH_SIZE, SWATCH_GAP);
if (idx >= 0) {
s.bg_grad_bottom = bg_palette[idx];
desktop_mark_background_dirty(st->desktop);
settings_persist(st);
return true;
}
@@ -189,6 +195,7 @@ bool settings_handle_appearance_click(Window* win, SettingsState* st, int mx, in
int idx = mtk::hit_swatch_row(mx, cy, x + 70, y, SWATCH_COUNT, SWATCH_SIZE, SWATCH_GAP);
if (idx >= 0) {
s.bg_solid = bg_palette[idx];
desktop_mark_background_dirty(st->desktop);
settings_persist(st);
return true;
}
@@ -199,6 +206,7 @@ bool settings_handle_appearance_click(Window* win, SettingsState* st, int mx, in
int idx = mtk::hit_swatch_row(mx, cy, x + 110, y, SWATCH_COUNT, SWATCH_SIZE, SWATCH_GAP);
if (idx >= 0) {
s.panel_color = panel_palette[idx];
desktop_mark_background_dirty(st->desktop);
settings_persist(st);
return true;
}
@@ -11,6 +11,8 @@
#include <gui/mtk.hpp>
#include <montauk/config.h>
void desktop_mark_background_dirty(DesktopState* ds);
namespace settings_app {
struct SettingsState {
+45 -7
View File
@@ -149,26 +149,24 @@ void desktop_draw_lock_screen(DesktopState* ds) {
// Desktop Composition
// ============================================================================
void gui::desktop_compose(DesktopState* ds) {
Framebuffer& fb = ds->fb;
void desktop_mark_background_dirty(DesktopState* ds) {
if (ds) ds->background_cache_dirty = true;
}
// Desktop background gradient
static void desktop_render_background_to(uint32_t* buf, int pitch, DesktopState* ds) {
int sw = ds->screen_w;
int sh = ds->screen_h;
int grad_start = PANEL_HEIGHT;
int grad_range = sh - grad_start;
if (grad_range < 1) grad_range = 1;
uint32_t* buf = fb.buffer();
int pitch = fb.pitch();
for (int y = 0; y < sh; y++) {
uint32_t* row = (uint32_t*)((uint8_t*)buf + y * pitch);
if (ds->settings.bg_image && ds->settings.bg_wallpaper) {
// Wallpaper covers the entire screen; panel draws on top
uint32_t* wp = ds->settings.bg_wallpaper + y * ds->settings.bg_wallpaper_w;
int cw = sw < ds->settings.bg_wallpaper_w ? sw : ds->settings.bg_wallpaper_w;
for (int x = 0; x < cw; x++) row[x] = wp[x];
montauk::memcpy(row, wp, (uint64_t)cw * sizeof(uint32_t));
} else if (y < grad_start) {
// Panel area - will be overwritten by panel drawing
uint32_t px = ds->settings.panel_color.to_pixel();
@@ -187,6 +185,46 @@ void gui::desktop_compose(DesktopState* ds) {
for (int x = 0; x < sw; x++) row[x] = px;
}
}
}
static bool desktop_prepare_background_cache(DesktopState* ds) {
if (!ds) return false;
int pitch = ds->fb.pitch();
uint64_t bytes = (uint64_t)ds->screen_h * (uint64_t)pitch;
if (!ds->background_cache) {
ds->background_cache = (uint32_t*)montauk::alloc(bytes);
if (!ds->background_cache) return false;
ds->background_cache_pitch = pitch;
ds->background_cache_dirty = true;
}
if (ds->background_cache_pitch != pitch) {
montauk::free(ds->background_cache);
ds->background_cache = (uint32_t*)montauk::alloc(bytes);
if (!ds->background_cache) return false;
ds->background_cache_pitch = pitch;
ds->background_cache_dirty = true;
}
if (ds->background_cache_dirty) {
desktop_render_background_to(ds->background_cache, ds->background_cache_pitch, ds);
ds->background_cache_dirty = false;
}
return true;
}
void gui::desktop_compose(DesktopState* ds) {
Framebuffer& fb = ds->fb;
int sw = ds->screen_w;
int sh = ds->screen_h;
if (desktop_prepare_background_cache(ds))
fb.copy_from(ds->background_cache, ds->background_cache_pitch);
else
desktop_render_background_to(fb.buffer(), fb.pitch(), ds);
// Lock screen: draw overlay and card, then cursor, and return early
if (ds->screen_locked) {
@@ -123,6 +123,7 @@ void desktop_draw_vol_popup(gui::DesktopState* ds);
// compose.cpp
void desktop_draw_lock_screen(gui::DesktopState* ds);
void desktop_mark_background_dirty(gui::DesktopState* ds);
// main.cpp
void desktop_scan_apps(gui::DesktopState* ds);
+64 -13
View File
@@ -61,8 +61,13 @@ void desktop_scan_apps(DesktopState* ds) {
if (sz == 0 || sz > 4096) { montauk::close(fh); continue; }
char* text = (char*)montauk::malloc(sz + 1);
montauk::read(fh, (uint8_t*)text, 0, sz);
if (!text) { montauk::close(fh); continue; }
int bytes_read = montauk::read(fh, (uint8_t*)text, 0, sz);
montauk::close(fh);
if (bytes_read != (int)sz) {
montauk::mfree(text);
continue;
}
text[sz] = '\0';
auto doc = montauk::toml::parse(text);
@@ -77,6 +82,10 @@ void desktop_scan_apps(DesktopState* ds) {
const char* category = doc.get_string("menu.category", "Applications");
bool visible = doc.get_bool("menu.visible", true);
bool launch_with_home = doc.get_bool("launch.pass_home_dir", true);
if (binary[0] == '\0') {
doc.destroy();
continue;
}
montauk::strncpy(app->name, name, sizeof(app->name));
montauk::strncpy(app->category, category, sizeof(app->category));
@@ -154,6 +163,9 @@ void desktop_build_menu(DesktopState* ds) {
void gui::desktop_init(DesktopState* ds) {
ds->screen_w = ds->fb.width();
ds->screen_h = ds->fb.height();
ds->background_cache = nullptr;
ds->background_cache_pitch = ds->fb.pitch();
ds->background_cache_dirty = true;
// Immediately clear the screen to hide flanterm boot text
ds->fb.clear(colors::DESKTOP_BG);
@@ -355,9 +367,41 @@ static void desktop_clear_window_dirty(DesktopState* ds) {
}
}
static bool desktop_panel_refresh_due(const DesktopState* ds, uint64_t now) {
static bool desktop_netcfg_equal(const Montauk::NetCfg& a, const Montauk::NetCfg& b) {
if (a.ipAddress != b.ipAddress || a.subnetMask != b.subnetMask ||
a.gateway != b.gateway || a.dnsServer != b.dnsServer) {
return false;
}
for (int i = 0; i < 6; i++) {
if (a.macAddress[i] != b.macAddress[i]) return false;
}
return true;
}
static bool desktop_refresh_panel_state(DesktopState* ds, uint64_t now) {
if (ds->screen_locked) return false;
return (now - ds->net_cfg_last_poll > 5000) || (now - ds->vol_last_poll > 5000);
bool changed = false;
if (now - ds->net_cfg_last_poll > 5000) {
Montauk::NetCfg next;
montauk::get_netcfg(&next);
if (!desktop_netcfg_equal(next, ds->cached_net_cfg)) {
ds->cached_net_cfg = next;
changed = true;
}
ds->net_cfg_last_poll = now;
}
if (now - ds->vol_last_poll > 5000) {
int v = montauk::audio_get_volume(0);
if (v >= 0 && !ds->vol_muted && v != ds->vol_level) {
ds->vol_level = v;
changed = true;
}
ds->vol_last_poll = now;
}
return changed;
}
bool desktop_poll_external_windows(DesktopState* ds) {
@@ -386,7 +430,12 @@ bool desktop_poll_external_windows(DesktopState* ds) {
changed = true;
}
}
montauk::strncpy(ds->windows[i].title, extWins[e].title, MAX_TITLE_LEN);
if (!montauk::streq(ds->windows[i].title, extWins[e].title)) {
montauk::strncpy(ds->windows[i].title, extWins[e].title, MAX_TITLE_LEN);
if (ds->windows[i].state != WIN_MINIMIZED && ds->windows[i].state != WIN_CLOSED) {
changed = true;
}
}
// Update dirty flag and cursor
if (extWins[e].dirty) {
ds->windows[i].dirty = true;
@@ -405,9 +454,8 @@ bool desktop_poll_external_windows(DesktopState* ds) {
currentVa = 0;
}
uint64_t va = montauk::win_map(extId);
if (!remapRequested && va != 0 && va != currentVa) {
montauk::win_unmap(extId);
uint64_t va = currentVa;
if (remapRequested || currentVa == 0) {
va = montauk::win_map(extId);
}
@@ -416,7 +464,6 @@ bool desktop_poll_external_windows(DesktopState* ds) {
ds->windows[i].content_w = extWins[e].width;
ds->windows[i].content_h = extWins[e].height;
ds->windows[i].dirty = true;
montauk::strncpy(ds->windows[i].title, extWins[e].title, MAX_TITLE_LEN);
changed = true;
// Clear from closing list if the slot was reused
for (int c = 0; c < ds->closing_ext_count; c++) {
@@ -532,6 +579,7 @@ bool desktop_poll_external_windows(DesktopState* ds) {
void gui::desktop_run(DesktopState* ds) {
uint64_t lastClockToken = 0;
uint64_t nextClockPollMs = 0;
uint64_t lastLauncherBlinkToken = ~0ull;
uint64_t inputSerial = montauk::input_wait(0, 0);
bool firstFrame = true;
@@ -588,7 +636,7 @@ void gui::desktop_run(DesktopState* ds) {
}
uint64_t now = montauk::get_milliseconds();
sceneChanged |= desktop_panel_refresh_due(ds, now);
sceneChanged |= desktop_refresh_panel_state(ds, now);
uint64_t launcherBlinkToken = desktop_launcher_blink_token(ds, now);
if (launcherBlinkToken != lastLauncherBlinkToken) {
@@ -596,10 +644,13 @@ void gui::desktop_run(DesktopState* ds) {
sceneChanged = true;
}
uint64_t clockToken = desktop_clock_token();
if (clockToken != lastClockToken) {
lastClockToken = clockToken;
sceneChanged = true;
if (now >= nextClockPollMs) {
nextClockPollMs = now + 1000;
uint64_t clockToken = desktop_clock_token();
if (clockToken != lastClockToken) {
lastClockToken = clockToken;
sceneChanged = true;
}
}
sceneChanged |= desktop_has_visible_dirty_window(ds);
-12
View File
@@ -106,13 +106,6 @@ void gui::desktop_draw_panel(DesktopState* ds) {
draw_text(fb, date_x, clock_y, date_str, colors::PANEL_TEXT);
// Volume icon (to the left of the date)
uint64_t now = montauk::get_milliseconds();
if (now - ds->vol_last_poll > 5000) {
int v = montauk::audio_get_volume(0);
if (v >= 0 && !ds->vol_muted) ds->vol_level = v;
ds->vol_last_poll = now;
}
int vol_icon_x = date_x - 16 - 12;
int vol_icon_y = (PANEL_HEIGHT - 16) / 2;
ds->vol_icon_rect = {vol_icon_x, vol_icon_y, 16, 16};
@@ -135,11 +128,6 @@ void gui::desktop_draw_panel(DesktopState* ds) {
}
// Network icon (to the left of the volume icon)
if (now - ds->net_cfg_last_poll > 5000) {
montauk::get_netcfg(&ds->cached_net_cfg);
ds->net_cfg_last_poll = now;
}
int net_icon_x = vol_icon_x - 16 - 10;
int net_icon_y = (PANEL_HEIGHT - 16) / 2;
ds->net_icon_rect = {net_icon_x, net_icon_y, 16, 16};