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
+3
View File
@@ -177,6 +177,9 @@ struct DesktopState {
Rect temp_icon_rect; Rect temp_icon_rect;
int screen_w, screen_h; 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 // 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 // been destroyed yet by their owning process. Prevents the poll loop
+3 -2
View File
@@ -12,16 +12,17 @@ namespace gui {
// Fast horizontal line // Fast horizontal line
inline void draw_hline(Framebuffer& fb, int x, int y, int w, Color c) { 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 // Fast vertical line
inline void draw_vline(Framebuffer& fb, int x, int y, int h, Color c) { 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 // Rectangle outline
inline void draw_rect(Framebuffer& fb, int x, int y, int w, int h, Color c) { 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, w, c);
draw_hline(fb, x, y + h - 1, w, c); draw_hline(fb, x, y + h - 1, w, c);
draw_vline(fb, x, y, h, c); draw_vline(fb, x, y, h, c);
+100 -33
View File
@@ -7,6 +7,7 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <montauk/syscall.h> #include <montauk/syscall.h>
#include <montauk/string.h>
#include "gui/gui.hpp" #include "gui/gui.hpp"
namespace gui { namespace gui {
@@ -18,6 +19,46 @@ class Framebuffer {
int fb_height; int fb_height;
int fb_pitch; // in bytes 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: public:
Framebuffer() : hw_fb(nullptr), back_buf(nullptr), fb_width(0), fb_height(0), fb_pitch(0) { Framebuffer() : hw_fb(nullptr), back_buf(nullptr), fb_width(0), fb_height(0), fb_pitch(0) {
Montauk::FbInfo info; Montauk::FbInfo info;
@@ -75,6 +116,8 @@ public:
} }
inline void fill_rect(int x, int y, int w, int h, Color c) { inline void fill_rect(int x, int y, int w, int h, Color c) {
if (!back_buf) return;
// Clip to screen bounds // Clip to screen bounds
int x0 = x < 0 ? 0 : x; int x0 = x < 0 ? 0 : x;
int y0 = y < 0 ? 0 : y; int y0 = y < 0 ? 0 : y;
@@ -88,9 +131,7 @@ public:
for (int row = y0; row < y1; row++) { for (int row = y0; row < y1; row++) {
uint32_t* dst = (uint32_t*)((uint8_t*)back_buf + row * fb_pitch) + x0; uint32_t* dst = (uint32_t*)((uint8_t*)back_buf + row * fb_pitch) + x0;
for (int col = 0; col < clipped_w; col++) { fill_pixels(dst, clipped_w, pixel);
dst[col] = pixel;
}
} }
} }
@@ -136,59 +177,85 @@ public:
} }
inline void blit(int x, int y, int w, int h, const uint32_t* pixels) { inline void blit(int x, int y, int w, int h, const uint32_t* pixels) {
for (int row = 0; row < h; row++) { if (!back_buf || !pixels || w <= 0 || h <= 0) return;
int dy = y + row;
if (dy < 0 || dy >= fb_height) continue; int src_x = 0;
for (int col = 0; col < w; col++) { int src_y = 0;
int dx = x + col; int dst_x = x;
if (dx < 0 || dx >= fb_width) continue; int dst_y = y;
uint32_t* dst_row = (uint32_t*)((uint8_t*)back_buf + dy * fb_pitch); int copy_w = w;
dst_row[dx] = pixels[row * w + col]; 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) { inline void blit_alpha(int x, int y, int w, int h, const uint32_t* pixels) {
for (int row = 0; row < h; row++) { if (!back_buf || !pixels || w <= 0 || h <= 0) return;
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 src = pixels[row * w + col]; int src_x = 0;
uint8_t sa = (src >> 24) & 0xFF; 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; if (sa == 0) continue;
uint8_t sr = (src >> 16) & 0xFF;
uint8_t sg = (src >> 8) & 0xFF;
uint8_t sb = src & 0xFF;
if (sa == 255) { if (sa == 255) {
uint32_t* dst_row = (uint32_t*)((uint8_t*)back_buf + dy * fb_pitch); dst[col] = s;
dst_row[dx] = src;
continue; continue;
} }
Color sc = {sr, sg, sb, sa}; dst[col] = blend_pixel(dst[col], s, sa);
put_pixel_alpha(dx, dy, sc);
} }
} }
} }
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) { inline void clear(Color c) {
fill_rect(0, 0, fb_width, fb_height, c); fill_rect(0, 0, fb_width, fb_height, c);
} }
inline void flip() { inline void flip() {
if (!hw_fb || !back_buf) return;
// Copy back buffer to hardware framebuffer, row by row (pitch may differ) // 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++) { for (int y = 0; y < fb_height; y++) {
uint32_t* src = (uint32_t*)((uint8_t*)back_buf + y * fb_pitch); uint32_t* src = (uint32_t*)((uint8_t*)back_buf + y * fb_pitch);
uint32_t* dst = (uint32_t*)((uint8_t*)hw_fb + y * fb_pitch); uint32_t* dst = (uint32_t*)((uint8_t*)hw_fb + y * fb_pitch);
for (int x = 0; x < row_pixels; x++) { montauk::memcpy(dst, src, row_bytes);
dst[x] = src[x];
}
} }
} }
}; };
+43 -6
View File
@@ -237,6 +237,13 @@ struct TrueTypeFont {
GlyphCache* gc = get_cache(pixel_size); GlyphCache* gc = get_cache(pixel_size);
int cx = x; int cx = x;
int baseline = y + gc->ascent; 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++) { for (int i = 0; text[i]; i++) {
CachedGlyph* g = get_glyph(gc, (unsigned char)text[i]); CachedGlyph* g = get_glyph(gc, (unsigned char)text[i]);
@@ -245,12 +252,41 @@ struct TrueTypeFont {
if (g->bitmap) { if (g->bitmap) {
int gx = cx + g->xoff; int gx = cx + g->xoff;
int gy = baseline + g->yoff; int gy = baseline + g->yoff;
for (int row = 0; row < g->height; row++) {
for (int col = 0; col < g->width; col++) { int row0 = gy < 0 ? -gy : 0;
uint8_t alpha = g->bitmap[row * g->width + col]; int col0 = gx < 0 ? -gx : 0;
if (alpha > 0) { int row1 = gy + g->height > fb_h ? fb_h - gy : g->height;
Color c = {color.r, color.g, color.b, alpha}; int col1 = gx + g->width > fb_w ? fb_w - gx : g->width;
fb.put_pixel_alpha(gx + col, gy + row, c);
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() { inline bool init() {
auto load = [](const char* path) -> TrueTypeFont* { auto load = [](const char* path) -> TrueTypeFont* {
TrueTypeFont* f = (TrueTypeFont*)montauk::malloc(sizeof(TrueTypeFont)); TrueTypeFont* f = (TrueTypeFont*)montauk::malloc(sizeof(TrueTypeFont));
if (!f) return nullptr;
montauk::memset(f, 0, sizeof(TrueTypeFont)); montauk::memset(f, 0, sizeof(TrueTypeFont));
if (!f->init(path)) { if (!f->init(path)) {
montauk::mfree(f); montauk::mfree(f);
@@ -8,6 +8,19 @@
namespace filemanager { 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) { void filemanager_do_copy(FileManagerState* fm) {
if (fm->selected < 0 || fm->selected >= fm->entry_count) return; if (fm->selected < 0 || fm->selected >= fm->entry_count) return;
if (filemanager_is_computer_view(fm) || filemanager_is_virtual_view(fm) || 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); const char* basename = path_basename(fm->clipboard_path);
char dst[512]; char dst[512];
filemanager_build_fullpath(dst, 512, fm->current_path, basename); 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 // Check if source is a directory by trying to readdir it
const char* probe[1]; const char* probe[1];
int probe_count = montauk::readdir(fm->clipboard_path, probe, 1); int probe_count = montauk::readdir(fm->clipboard_path, probe, 1);
bool src_is_dir = (probe_count >= 0); bool src_is_dir = (probe_count >= 0);
if (src_is_dir && path_is_same_or_descendant(dst, fm->clipboard_path)) return;
bool ok; bool ok;
if (src_is_dir) if (src_is_dir)
@@ -243,7 +243,7 @@ void filemanager_read_dir(FileManagerState* fm) {
// Sort: directories first, then alphabetical (case-insensitive) // Sort: directories first, then alphabetical (case-insensitive)
for (int i = 1; i < fm->entry_count; i++) { 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_type = fm->entry_types[i];
int tmp_size = fm->entry_sizes[i]; int tmp_size = fm->entry_sizes[i];
bool tmp_isdir = fm->is_dir[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) { bool filemanager_copy_file(const char* src, const char* dst) {
if (!src || !dst || montauk::streq(src, dst)) return false;
int sfd = montauk::open(src); int sfd = montauk::open(src);
if (sfd < 0) return false; if (sfd < 0) return false;
int size = (int)montauk::getsize(sfd); uint64_t size = montauk::getsize(sfd);
// Create destination // Create destination
int dfd = montauk::fcreate(dst); int dfd = montauk::fcreate(dst);
@@ -396,33 +398,46 @@ bool filemanager_copy_file(const char* src, const char* dst) {
return false; return false;
} }
bool ok = true;
if (size > 0) { if (size > 0) {
// Cap at 4 MB to avoid exhausting memory static constexpr uint64_t COPY_CHUNK = 64 * 1024;
if (size > 4 * 1024 * 1024) { uint64_t buf_size = size < COPY_CHUNK ? size : COPY_CHUNK;
montauk::close(sfd); uint8_t* buf = (uint8_t*)montauk::malloc(buf_size);
montauk::close(dfd);
montauk::fdelete(dst);
return false;
}
uint8_t* buf = (uint8_t*)montauk::malloc(size);
if (!buf) { if (!buf) {
montauk::close(sfd); montauk::close(sfd);
montauk::close(dfd); montauk::close(dfd);
montauk::fdelete(dst); montauk::fdelete(dst);
return false; 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::mfree(buf);
} }
montauk::close(sfd); montauk::close(sfd);
montauk::close(dfd); montauk::close(dfd);
if (!ok) {
montauk::fdelete(dst);
return false;
}
return true; return true;
} }
// Recursive directory copy // Recursive directory copy
bool filemanager_copy_dir_recursive(const char* src, const char* dst) { bool filemanager_copy_dir_recursive(const char* src, const char* dst) {
if (!src || !dst || montauk::streq(src, dst)) return false;
montauk::fmkdir(dst); montauk::fmkdir(dst);
const char* names[64]; 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)) { if (grad_option.contains(mx, cy)) {
s.bg_gradient = true; s.bg_gradient = true;
s.bg_image = false; s.bg_image = false;
desktop_mark_background_dirty(st->desktop);
settings_persist(st); settings_persist(st);
return true; return true;
} }
if (solid_option.contains(mx, cy)) { if (solid_option.contains(mx, cy)) {
s.bg_gradient = false; s.bg_gradient = false;
s.bg_image = false; s.bg_image = false;
desktop_mark_background_dirty(st->desktop);
settings_persist(st); settings_persist(st);
return true; 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); wallpaper_scan_home(st->desktop->home_dir, &st->wp_files);
st->wp_scanned = true; st->wp_scanned = true;
} }
desktop_mark_background_dirty(st->desktop);
settings_persist(st); settings_persist(st);
return true; 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); str_append(fullpath, st->wp_files.names[i], 256);
wallpaper_load(&s, fullpath, wallpaper_load(&s, fullpath,
st->desktop->screen_w, st->desktop->screen_h); st->desktop->screen_w, st->desktop->screen_h);
desktop_mark_background_dirty(st->desktop);
settings_persist(st); settings_persist(st);
return true; 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); int idx = mtk::hit_swatch_row(mx, cy, x + 70, y, SWATCH_COUNT, SWATCH_SIZE, SWATCH_GAP);
if (idx >= 0) { if (idx >= 0) {
s.bg_grad_top = bg_palette[idx]; s.bg_grad_top = bg_palette[idx];
desktop_mark_background_dirty(st->desktop);
settings_persist(st); settings_persist(st);
return true; 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); idx = mtk::hit_swatch_row(mx, cy, x + 70, y, SWATCH_COUNT, SWATCH_SIZE, SWATCH_GAP);
if (idx >= 0) { if (idx >= 0) {
s.bg_grad_bottom = bg_palette[idx]; s.bg_grad_bottom = bg_palette[idx];
desktop_mark_background_dirty(st->desktop);
settings_persist(st); settings_persist(st);
return true; 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); int idx = mtk::hit_swatch_row(mx, cy, x + 70, y, SWATCH_COUNT, SWATCH_SIZE, SWATCH_GAP);
if (idx >= 0) { if (idx >= 0) {
s.bg_solid = bg_palette[idx]; s.bg_solid = bg_palette[idx];
desktop_mark_background_dirty(st->desktop);
settings_persist(st); settings_persist(st);
return true; 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); int idx = mtk::hit_swatch_row(mx, cy, x + 110, y, SWATCH_COUNT, SWATCH_SIZE, SWATCH_GAP);
if (idx >= 0) { if (idx >= 0) {
s.panel_color = panel_palette[idx]; s.panel_color = panel_palette[idx];
desktop_mark_background_dirty(st->desktop);
settings_persist(st); settings_persist(st);
return true; return true;
} }
@@ -11,6 +11,8 @@
#include <gui/mtk.hpp> #include <gui/mtk.hpp>
#include <montauk/config.h> #include <montauk/config.h>
void desktop_mark_background_dirty(DesktopState* ds);
namespace settings_app { namespace settings_app {
struct SettingsState { struct SettingsState {
+45 -7
View File
@@ -149,26 +149,24 @@ void desktop_draw_lock_screen(DesktopState* ds) {
// Desktop Composition // Desktop Composition
// ============================================================================ // ============================================================================
void gui::desktop_compose(DesktopState* ds) { void desktop_mark_background_dirty(DesktopState* ds) {
Framebuffer& fb = ds->fb; 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 sw = ds->screen_w;
int sh = ds->screen_h; int sh = ds->screen_h;
int grad_start = PANEL_HEIGHT; int grad_start = PANEL_HEIGHT;
int grad_range = sh - grad_start; int grad_range = sh - grad_start;
if (grad_range < 1) grad_range = 1; if (grad_range < 1) grad_range = 1;
uint32_t* buf = fb.buffer();
int pitch = fb.pitch();
for (int y = 0; y < sh; y++) { for (int y = 0; y < sh; y++) {
uint32_t* row = (uint32_t*)((uint8_t*)buf + y * pitch); uint32_t* row = (uint32_t*)((uint8_t*)buf + y * pitch);
if (ds->settings.bg_image && ds->settings.bg_wallpaper) { if (ds->settings.bg_image && ds->settings.bg_wallpaper) {
// Wallpaper covers the entire screen; panel draws on top // Wallpaper covers the entire screen; panel draws on top
uint32_t* wp = ds->settings.bg_wallpaper + y * ds->settings.bg_wallpaper_w; 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; 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) { } else if (y < grad_start) {
// Panel area - will be overwritten by panel drawing // Panel area - will be overwritten by panel drawing
uint32_t px = ds->settings.panel_color.to_pixel(); 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; 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 // Lock screen: draw overlay and card, then cursor, and return early
if (ds->screen_locked) { if (ds->screen_locked) {
@@ -123,6 +123,7 @@ void desktop_draw_vol_popup(gui::DesktopState* ds);
// compose.cpp // compose.cpp
void desktop_draw_lock_screen(gui::DesktopState* ds); void desktop_draw_lock_screen(gui::DesktopState* ds);
void desktop_mark_background_dirty(gui::DesktopState* ds);
// main.cpp // main.cpp
void desktop_scan_apps(gui::DesktopState* ds); 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; } if (sz == 0 || sz > 4096) { montauk::close(fh); continue; }
char* text = (char*)montauk::malloc(sz + 1); 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); montauk::close(fh);
if (bytes_read != (int)sz) {
montauk::mfree(text);
continue;
}
text[sz] = '\0'; text[sz] = '\0';
auto doc = montauk::toml::parse(text); 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"); const char* category = doc.get_string("menu.category", "Applications");
bool visible = doc.get_bool("menu.visible", true); bool visible = doc.get_bool("menu.visible", true);
bool launch_with_home = doc.get_bool("launch.pass_home_dir", 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->name, name, sizeof(app->name));
montauk::strncpy(app->category, category, sizeof(app->category)); montauk::strncpy(app->category, category, sizeof(app->category));
@@ -154,6 +163,9 @@ void desktop_build_menu(DesktopState* ds) {
void gui::desktop_init(DesktopState* ds) { void gui::desktop_init(DesktopState* ds) {
ds->screen_w = ds->fb.width(); ds->screen_w = ds->fb.width();
ds->screen_h = ds->fb.height(); 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 // Immediately clear the screen to hide flanterm boot text
ds->fb.clear(colors::DESKTOP_BG); 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; 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) { bool desktop_poll_external_windows(DesktopState* ds) {
@@ -386,7 +430,12 @@ bool desktop_poll_external_windows(DesktopState* ds) {
changed = true; 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 // Update dirty flag and cursor
if (extWins[e].dirty) { if (extWins[e].dirty) {
ds->windows[i].dirty = true; ds->windows[i].dirty = true;
@@ -405,9 +454,8 @@ bool desktop_poll_external_windows(DesktopState* ds) {
currentVa = 0; currentVa = 0;
} }
uint64_t va = montauk::win_map(extId); uint64_t va = currentVa;
if (!remapRequested && va != 0 && va != currentVa) { if (remapRequested || currentVa == 0) {
montauk::win_unmap(extId);
va = montauk::win_map(extId); 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_w = extWins[e].width;
ds->windows[i].content_h = extWins[e].height; ds->windows[i].content_h = extWins[e].height;
ds->windows[i].dirty = true; ds->windows[i].dirty = true;
montauk::strncpy(ds->windows[i].title, extWins[e].title, MAX_TITLE_LEN);
changed = true; changed = true;
// Clear from closing list if the slot was reused // Clear from closing list if the slot was reused
for (int c = 0; c < ds->closing_ext_count; c++) { 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) { void gui::desktop_run(DesktopState* ds) {
uint64_t lastClockToken = 0; uint64_t lastClockToken = 0;
uint64_t nextClockPollMs = 0;
uint64_t lastLauncherBlinkToken = ~0ull; uint64_t lastLauncherBlinkToken = ~0ull;
uint64_t inputSerial = montauk::input_wait(0, 0); uint64_t inputSerial = montauk::input_wait(0, 0);
bool firstFrame = true; bool firstFrame = true;
@@ -588,7 +636,7 @@ void gui::desktop_run(DesktopState* ds) {
} }
uint64_t now = montauk::get_milliseconds(); 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); uint64_t launcherBlinkToken = desktop_launcher_blink_token(ds, now);
if (launcherBlinkToken != lastLauncherBlinkToken) { if (launcherBlinkToken != lastLauncherBlinkToken) {
@@ -596,10 +644,13 @@ void gui::desktop_run(DesktopState* ds) {
sceneChanged = true; sceneChanged = true;
} }
uint64_t clockToken = desktop_clock_token(); if (now >= nextClockPollMs) {
if (clockToken != lastClockToken) { nextClockPollMs = now + 1000;
lastClockToken = clockToken; uint64_t clockToken = desktop_clock_token();
sceneChanged = true; if (clockToken != lastClockToken) {
lastClockToken = clockToken;
sceneChanged = true;
}
} }
sceneChanged |= desktop_has_visible_dirty_window(ds); 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); draw_text(fb, date_x, clock_y, date_str, colors::PANEL_TEXT);
// Volume icon (to the left of the date) // 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_x = date_x - 16 - 12;
int vol_icon_y = (PANEL_HEIGHT - 16) / 2; int vol_icon_y = (PANEL_HEIGHT - 16) / 2;
ds->vol_icon_rect = {vol_icon_x, vol_icon_y, 16, 16}; 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) // 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_x = vol_icon_x - 16 - 10;
int net_icon_y = (PANEL_HEIGHT - 16) / 2; int net_icon_y = (PANEL_HEIGHT - 16) / 2;
ds->net_icon_rect = {net_icon_x, net_icon_y, 16, 16}; ds->net_icon_rect = {net_icon_x, net_icon_y, 16, 16};