From 69fc184a4e829f4d3731ea2b8c19dee6684dbfee Mon Sep 17 00:00:00 2001 From: Daniel Hammer Date: Thu, 7 May 2026 20:00:17 +0200 Subject: [PATCH] fix: various bug fixes in desktop components --- programs/include/gui/desktop.hpp | 3 + programs/include/gui/draw.hpp | 5 +- programs/include/gui/framebuffer.hpp | 133 +++++++++++++----- programs/include/gui/truetype.hpp | 49 ++++++- .../src/desktop/apps/filemanager/actions.cpp | 15 ++ .../desktop/apps/filemanager/filesystem.cpp | 39 +++-- .../src/desktop/apps/settings/appearance.cpp | 8 ++ .../apps/settings/settings_internal.hpp | 2 + programs/src/desktop/compose.cpp | 52 ++++++- programs/src/desktop/desktop_internal.hpp | 1 + programs/src/desktop/main.cpp | 77 ++++++++-- programs/src/desktop/panel.cpp | 12 -- 12 files changed, 311 insertions(+), 85 deletions(-) diff --git a/programs/include/gui/desktop.hpp b/programs/include/gui/desktop.hpp index f4a3f5d..044fe9b 100644 --- a/programs/include/gui/desktop.hpp +++ b/programs/include/gui/desktop.hpp @@ -177,6 +177,9 @@ struct DesktopState { Rect temp_icon_rect; int screen_w, screen_h; + uint32_t* background_cache; + int background_cache_pitch; + bool background_cache_dirty; // IDs of external windows we've sent a close event to but that haven't // been destroyed yet by their owning process. Prevents the poll loop diff --git a/programs/include/gui/draw.hpp b/programs/include/gui/draw.hpp index ab77a9c..a2875c8 100644 --- a/programs/include/gui/draw.hpp +++ b/programs/include/gui/draw.hpp @@ -12,16 +12,17 @@ namespace gui { // Fast horizontal line inline void draw_hline(Framebuffer& fb, int x, int y, int w, Color c) { - for (int i = 0; i < w; i++) fb.put_pixel(x + i, y, c); + fb.fill_rect(x, y, w, 1, c); } // Fast vertical line inline void draw_vline(Framebuffer& fb, int x, int y, int h, Color c) { - for (int i = 0; i < h; i++) fb.put_pixel(x, y + i, c); + fb.fill_rect(x, y, 1, h, c); } // Rectangle outline inline void draw_rect(Framebuffer& fb, int x, int y, int w, int h, Color c) { + if (w <= 0 || h <= 0) return; draw_hline(fb, x, y, w, c); draw_hline(fb, x, y + h - 1, w, c); draw_vline(fb, x, y, h, c); diff --git a/programs/include/gui/framebuffer.hpp b/programs/include/gui/framebuffer.hpp index 9a1a4ab..65bfb2b 100644 --- a/programs/include/gui/framebuffer.hpp +++ b/programs/include/gui/framebuffer.hpp @@ -7,6 +7,7 @@ #pragma once #include #include +#include #include "gui/gui.hpp" namespace gui { @@ -18,6 +19,46 @@ class Framebuffer { int fb_height; int fb_pitch; // in bytes + static inline void fill_pixels(uint32_t* dst, int count, uint32_t pixel) { + if (!dst || count <= 0) return; + + uint64_t pixel64 = ((uint64_t)pixel << 32) | pixel; + if (((uint64_t)dst & 4) && count > 0) { + *dst++ = pixel; + count--; + } + + uint64_t* dst64 = (uint64_t*)dst; + int pairs = count / 2; + for (int i = 0; i < pairs; i++) { + dst64[i] = pixel64; + } + + if (count & 1) { + ((uint32_t*)(dst64 + pairs))[0] = pixel; + } + } + + static inline uint32_t blend_pixel(uint32_t dst, uint32_t src, uint32_t alpha) { + uint32_t inv_a = 255 - alpha; + uint32_t sr = (src >> 16) & 0xFF; + uint32_t sg = (src >> 8) & 0xFF; + uint32_t sb = src & 0xFF; + uint32_t dr = (dst >> 16) & 0xFF; + uint32_t dg = (dst >> 8) & 0xFF; + uint32_t db = dst & 0xFF; + + uint32_t rr = alpha * sr + inv_a * dr; + uint32_t gg = alpha * sg + inv_a * dg; + uint32_t bb = alpha * sb + inv_a * db; + + rr = (rr + 1 + (rr >> 8)) >> 8; + gg = (gg + 1 + (gg >> 8)) >> 8; + bb = (bb + 1 + (bb >> 8)) >> 8; + + return 0xFF000000 | (rr << 16) | (gg << 8) | bb; + } + public: Framebuffer() : hw_fb(nullptr), back_buf(nullptr), fb_width(0), fb_height(0), fb_pitch(0) { Montauk::FbInfo info; @@ -75,6 +116,8 @@ public: } inline void fill_rect(int x, int y, int w, int h, Color c) { + if (!back_buf) return; + // Clip to screen bounds int x0 = x < 0 ? 0 : x; int y0 = y < 0 ? 0 : y; @@ -88,9 +131,7 @@ public: for (int row = y0; row < y1; row++) { uint32_t* dst = (uint32_t*)((uint8_t*)back_buf + row * fb_pitch) + x0; - for (int col = 0; col < clipped_w; col++) { - dst[col] = pixel; - } + fill_pixels(dst, clipped_w, pixel); } } @@ -136,59 +177,85 @@ public: } inline void blit(int x, int y, int w, int h, const uint32_t* pixels) { - for (int row = 0; row < h; row++) { - int dy = y + row; - if (dy < 0 || dy >= fb_height) continue; - for (int col = 0; col < w; col++) { - int dx = x + col; - if (dx < 0 || dx >= fb_width) continue; - uint32_t* dst_row = (uint32_t*)((uint8_t*)back_buf + dy * fb_pitch); - dst_row[dx] = pixels[row * w + col]; - } + if (!back_buf || !pixels || w <= 0 || h <= 0) return; + + int src_x = 0; + int src_y = 0; + int dst_x = x; + int dst_y = y; + int copy_w = w; + int copy_h = h; + + if (dst_x < 0) { src_x = -dst_x; copy_w += dst_x; dst_x = 0; } + if (dst_y < 0) { src_y = -dst_y; copy_h += dst_y; dst_y = 0; } + if (dst_x + copy_w > fb_width) copy_w = fb_width - dst_x; + if (dst_y + copy_h > fb_height) copy_h = fb_height - dst_y; + if (copy_w <= 0 || copy_h <= 0) return; + + uint64_t row_bytes = (uint64_t)copy_w * sizeof(uint32_t); + for (int row = 0; row < copy_h; row++) { + uint32_t* dst = (uint32_t*)((uint8_t*)back_buf + (dst_y + row) * fb_pitch) + dst_x; + const uint32_t* src = pixels + (src_y + row) * w + src_x; + montauk::memcpy(dst, src, row_bytes); } } inline void blit_alpha(int x, int y, int w, int h, const uint32_t* pixels) { - for (int row = 0; row < h; row++) { - int dy = y + row; - if (dy < 0 || dy >= fb_height) continue; - for (int col = 0; col < w; col++) { - int dx = x + col; - if (dx < 0 || dx >= fb_width) continue; + if (!back_buf || !pixels || w <= 0 || h <= 0) return; - uint32_t src = pixels[row * w + col]; - uint8_t sa = (src >> 24) & 0xFF; + int src_x = 0; + int src_y = 0; + int dst_x = x; + int dst_y = y; + int copy_w = w; + int copy_h = h; + + if (dst_x < 0) { src_x = -dst_x; copy_w += dst_x; dst_x = 0; } + if (dst_y < 0) { src_y = -dst_y; copy_h += dst_y; dst_y = 0; } + if (dst_x + copy_w > fb_width) copy_w = fb_width - dst_x; + if (dst_y + copy_h > fb_height) copy_h = fb_height - dst_y; + if (copy_w <= 0 || copy_h <= 0) return; + + for (int row = 0; row < copy_h; row++) { + uint32_t* dst = (uint32_t*)((uint8_t*)back_buf + (dst_y + row) * fb_pitch) + dst_x; + const uint32_t* src = pixels + (src_y + row) * w + src_x; + for (int col = 0; col < copy_w; col++) { + uint32_t s = src[col]; + uint8_t sa = (s >> 24) & 0xFF; if (sa == 0) continue; - - uint8_t sr = (src >> 16) & 0xFF; - uint8_t sg = (src >> 8) & 0xFF; - uint8_t sb = src & 0xFF; - if (sa == 255) { - uint32_t* dst_row = (uint32_t*)((uint8_t*)back_buf + dy * fb_pitch); - dst_row[dx] = src; + dst[col] = s; continue; } - Color sc = {sr, sg, sb, sa}; - put_pixel_alpha(dx, dy, sc); + dst[col] = blend_pixel(dst[col], s, sa); } } } + inline void copy_from(const uint32_t* pixels, int src_pitch_bytes) { + if (!back_buf || !pixels || src_pitch_bytes <= 0) return; + uint64_t row_bytes = (uint64_t)fb_width * sizeof(uint32_t); + for (int row = 0; row < fb_height; row++) { + uint32_t* dst = (uint32_t*)((uint8_t*)back_buf + row * fb_pitch); + const uint32_t* src = (const uint32_t*)((const uint8_t*)pixels + row * src_pitch_bytes); + montauk::memcpy(dst, src, row_bytes); + } + } + inline void clear(Color c) { fill_rect(0, 0, fb_width, fb_height, c); } inline void flip() { + if (!hw_fb || !back_buf) return; + // Copy back buffer to hardware framebuffer, row by row (pitch may differ) - int row_pixels = fb_width; + uint64_t row_bytes = (uint64_t)fb_width * sizeof(uint32_t); for (int y = 0; y < fb_height; y++) { uint32_t* src = (uint32_t*)((uint8_t*)back_buf + y * fb_pitch); uint32_t* dst = (uint32_t*)((uint8_t*)hw_fb + y * fb_pitch); - for (int x = 0; x < row_pixels; x++) { - dst[x] = src[x]; - } + montauk::memcpy(dst, src, row_bytes); } } }; diff --git a/programs/include/gui/truetype.hpp b/programs/include/gui/truetype.hpp index f39eb85..90ac56c 100644 --- a/programs/include/gui/truetype.hpp +++ b/programs/include/gui/truetype.hpp @@ -237,6 +237,13 @@ struct TrueTypeFont { GlyphCache* gc = get_cache(pixel_size); int cx = x; int baseline = y + gc->ascent; + int fb_w = fb.width(); + int fb_h = fb.height(); + int fb_pitch = fb.pitch(); + uint32_t* fb_pixels = fb.buffer(); + if (!fb_pixels) return; + uint32_t src_rgb = 0xFF000000 | ((uint32_t)color.r << 16) | + ((uint32_t)color.g << 8) | color.b; for (int i = 0; text[i]; i++) { CachedGlyph* g = get_glyph(gc, (unsigned char)text[i]); @@ -245,12 +252,41 @@ struct TrueTypeFont { if (g->bitmap) { int gx = cx + g->xoff; int gy = baseline + g->yoff; - for (int row = 0; row < g->height; row++) { - for (int col = 0; col < g->width; col++) { - uint8_t alpha = g->bitmap[row * g->width + col]; - if (alpha > 0) { - Color c = {color.r, color.g, color.b, alpha}; - fb.put_pixel_alpha(gx + col, gy + row, c); + + int row0 = gy < 0 ? -gy : 0; + int col0 = gx < 0 ? -gx : 0; + int row1 = gy + g->height > fb_h ? fb_h - gy : g->height; + int col1 = gx + g->width > fb_w ? fb_w - gx : g->width; + + if (row0 < row1 && col0 < col1) { + for (int row = row0; row < row1; row++) { + uint8_t* alpha_row = g->bitmap + row * g->width + col0; + uint32_t* dst = (uint32_t*)((uint8_t*)fb_pixels + (gy + row) * fb_pitch) + gx + col0; + + for (int col = col0; col < col1; col++) { + uint8_t alpha = *alpha_row++; + if (alpha == 0) { + dst++; + continue; + } + if (alpha == 255) { + *dst++ = src_rgb; + continue; + } + + uint32_t d = *dst; + uint32_t inv_a = 255 - alpha; + uint32_t dr = (d >> 16) & 0xFF; + uint32_t dg = (d >> 8) & 0xFF; + uint32_t db = d & 0xFF; + uint32_t rr = alpha * color.r + inv_a * dr; + uint32_t gg = alpha * color.g + inv_a * dg; + uint32_t bb = alpha * color.b + inv_a * db; + + rr = (rr + 1 + (rr >> 8)) >> 8; + gg = (gg + 1 + (gg >> 8)) >> 8; + bb = (bb + 1 + (bb >> 8)) >> 8; + *dst++ = 0xFF000000 | (rr << 16) | (gg << 8) | bb; } } } @@ -427,6 +463,7 @@ namespace fonts { inline bool init() { auto load = [](const char* path) -> TrueTypeFont* { TrueTypeFont* f = (TrueTypeFont*)montauk::malloc(sizeof(TrueTypeFont)); + if (!f) return nullptr; montauk::memset(f, 0, sizeof(TrueTypeFont)); if (!f->init(path)) { montauk::mfree(f); diff --git a/programs/src/desktop/apps/filemanager/actions.cpp b/programs/src/desktop/apps/filemanager/actions.cpp index 6a1d5c6..2396b4c 100644 --- a/programs/src/desktop/apps/filemanager/actions.cpp +++ b/programs/src/desktop/apps/filemanager/actions.cpp @@ -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) diff --git a/programs/src/desktop/apps/filemanager/filesystem.cpp b/programs/src/desktop/apps/filemanager/filesystem.cpp index b166f57..ce67eef 100644 --- a/programs/src/desktop/apps/filemanager/filesystem.cpp +++ b/programs/src/desktop/apps/filemanager/filesystem.cpp @@ -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]; diff --git a/programs/src/desktop/apps/settings/appearance.cpp b/programs/src/desktop/apps/settings/appearance.cpp index a728aca..ae25966 100644 --- a/programs/src/desktop/apps/settings/appearance.cpp +++ b/programs/src/desktop/apps/settings/appearance.cpp @@ -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; } diff --git a/programs/src/desktop/apps/settings/settings_internal.hpp b/programs/src/desktop/apps/settings/settings_internal.hpp index c607c4b..784b963 100644 --- a/programs/src/desktop/apps/settings/settings_internal.hpp +++ b/programs/src/desktop/apps/settings/settings_internal.hpp @@ -11,6 +11,8 @@ #include #include +void desktop_mark_background_dirty(DesktopState* ds); + namespace settings_app { struct SettingsState { diff --git a/programs/src/desktop/compose.cpp b/programs/src/desktop/compose.cpp index be5e08c..ee6026e 100644 --- a/programs/src/desktop/compose.cpp +++ b/programs/src/desktop/compose.cpp @@ -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) { diff --git a/programs/src/desktop/desktop_internal.hpp b/programs/src/desktop/desktop_internal.hpp index 1a7a08c..1d33778 100644 --- a/programs/src/desktop/desktop_internal.hpp +++ b/programs/src/desktop/desktop_internal.hpp @@ -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); diff --git a/programs/src/desktop/main.cpp b/programs/src/desktop/main.cpp index 679b196..57eaaf4 100644 --- a/programs/src/desktop/main.cpp +++ b/programs/src/desktop/main.cpp @@ -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); diff --git a/programs/src/desktop/panel.cpp b/programs/src/desktop/panel.cpp index 0c48258..a667bc5 100644 --- a/programs/src/desktop/panel.cpp +++ b/programs/src/desktop/panel.cpp @@ -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};