feat: new Window Server helpers and app migrations

This commit is contained in:
2026-03-25 21:06:58 +01:00
parent 1ad9117fc1
commit afe0442d57
16 changed files with 654 additions and 778 deletions
+33 -92
View File
@@ -8,6 +8,7 @@
#include <montauk/string.h>
#include <montauk/heap.h>
#include <gui/gui.hpp>
#include <gui/standalone.hpp>
#include <gui/truetype.hpp>
extern "C" {
@@ -90,60 +91,6 @@ static int tb_fit_x0, tb_fit_x1;
static int tb_actual_x0, tb_actual_x1;
static int tb_open_x0, tb_open_x1;
// ============================================================================
// Helpers
// ============================================================================
static void px_fill(uint32_t* px, int bw, int bh,
int x, int y, int w, int h, Color c) {
uint32_t v = c.to_pixel();
int x0 = x < 0 ? 0 : x, y0 = y < 0 ? 0 : y;
int x1 = x + w > bw ? bw : x + w;
int y1 = y + h > bh ? bh : y + h;
for (int row = y0; row < y1; row++)
for (int col = x0; col < x1; col++)
px[row * bw + col] = v;
}
static void px_hline(uint32_t* px, int bw, int bh, int x, int y, int w, Color c) {
if (y < 0 || y >= bh) return;
uint32_t v = c.to_pixel();
int x0 = x < 0 ? 0 : x;
int x1 = x + w > bw ? bw : x + w;
for (int col = x0; col < x1; col++)
px[y * bw + col] = v;
}
static void px_vline(uint32_t* px, int bw, int bh, int x, int y, int h, Color c) {
if (x < 0 || x >= bw) return;
uint32_t v = c.to_pixel();
int y0 = y < 0 ? 0 : y;
int y1 = y + h > bh ? bh : y + h;
for (int row = y0; row < y1; row++)
px[row * bw + x] = v;
}
static void px_fill_rounded(uint32_t* px, int bw, int bh,
int x, int y, int w, int h, int r, Color c) {
uint32_t v = c.to_pixel();
for (int row = 0; row < h; row++) {
int dy = y + row;
if (dy < 0 || dy >= bh) continue;
for (int col = 0; col < w; col++) {
int dx = x + col;
if (dx < 0 || dx >= bw) continue;
// Check corners
bool skip = false;
int cx, cy;
if (col < r && row < r) { cx = r - col - 1; cy = r - row - 1; if (cx*cx + cy*cy >= r*r) skip = true; }
else if (col >= w - r && row < r) { cx = col - (w - r); cy = r - row - 1; if (cx*cx + cy*cy >= r*r) skip = true; }
else if (col < r && row >= h - r) { cx = r - col - 1; cy = row - (h - r); if (cx*cx + cy*cy >= r*r) skip = true; }
else if (col >= w - r && row >= h - r) { cx = col - (w - r); cy = row - (h - r); if (cx*cx + cy*cy >= r*r) skip = true; }
if (!skip) px[dy * bw + dx] = v;
}
}
}
static const char* basename(const char* path) {
const char* last = path;
for (const char* p = path; *p; p++) {
@@ -313,12 +260,12 @@ static void zoom_actual() {
// Rendering
// ============================================================================
static void render(uint32_t* pixels) {
static void render(Canvas& canvas) {
int vp_y0 = TOOLBAR_H;
int vp_y1 = g_win_h - STATUS_BAR_H;
// Fill viewport background
px_fill(pixels, g_win_w, g_win_h, 0, vp_y0, g_win_w, vp_y1 - vp_y0, BG_COLOR);
canvas.fill_rect(0, vp_y0, g_win_w, vp_y1 - vp_y0, BG_COLOR);
// Draw scaled image
if (g_image && g_load_ok) {
@@ -339,7 +286,7 @@ static void render(uint32_t* pixels) {
if (src_y >= g_img_h) src_y = g_img_h - 1;
const uint32_t* src_row = &g_image[src_y * g_img_w];
uint32_t* dst_row = &pixels[dy * g_win_w];
uint32_t* dst_row = &canvas.pixels[dy * g_win_w];
for (int dx = draw_x0; dx < draw_x1; dx++) {
int src_x = (int)((dx - g_pan_x) * inv_zoom);
@@ -376,30 +323,29 @@ static void render(uint32_t* pixels) {
}
}
} else if (!g_load_ok && g_font) {
g_font->draw_to_buffer(pixels, g_win_w, g_win_h,
20, vp_y0 + (vp_y1 - vp_y0) / 2 - 8, g_status, ERR_COLOR, 15);
draw_text(canvas, g_font, 20, vp_y0 + (vp_y1 - vp_y0) / 2 - 8, g_status, ERR_COLOR, 15);
}
// ---- Toolbar ----
px_fill(pixels, g_win_w, g_win_h, 0, 0, g_win_w, TOOLBAR_H, TOOLBAR_BG);
px_hline(pixels, g_win_w, g_win_h, 0, TOOLBAR_H - 1, g_win_w, GRID_COLOR);
canvas.fill_rect(0, 0, g_win_w, TOOLBAR_H, TOOLBAR_BG);
canvas.hline(0, TOOLBAR_H - 1, g_win_w, GRID_COLOR);
int bx = 4;
auto tb_btn = [&](int w, bool active, const char* label, int& x0_out, int& x1_out) {
x0_out = bx;
x1_out = bx + w;
Color bg = active ? TB_BTN_ACTIVE : TB_BTN_BG;
px_fill_rounded(pixels, g_win_w, g_win_h, bx, TB_BTN_Y, w, TB_BTN_SIZE, TB_BTN_RAD, bg);
canvas.fill_rounded_rect(bx, TB_BTN_Y, w, TB_BTN_SIZE, TB_BTN_RAD, bg);
if (g_font && label[0]) {
int tw = g_font->measure_text(label, HEADER_FONT);
g_font->draw_to_buffer(pixels, g_win_w, g_win_h,
bx + (w - tw) / 2, TB_BTN_Y + (TB_BTN_SIZE - HEADER_FONT) / 2,
label, HEADER_TEXT, HEADER_FONT);
draw_text(canvas, g_font,
bx + (w - tw) / 2, TB_BTN_Y + (TB_BTN_SIZE - HEADER_FONT) / 2,
label, HEADER_TEXT, HEADER_FONT);
}
bx += w + 4;
};
auto tb_sep = [&]() {
px_vline(pixels, g_win_w, g_win_h, bx, 6, TOOLBAR_H - 12, TB_SEP_COLOR);
canvas.vline(bx, 6, TOOLBAR_H - 12, TB_SEP_COLOR);
bx += 8;
};
@@ -418,9 +364,8 @@ static void render(uint32_t* pixels) {
snprintf(zoom_label, 16, "%d%%", pct);
if (g_font) {
int tw = g_font->measure_text(zoom_label, HEADER_FONT);
g_font->draw_to_buffer(pixels, g_win_w, g_win_h,
bx, TB_BTN_Y + (TB_BTN_SIZE - HEADER_FONT) / 2,
zoom_label, HEADER_TEXT, HEADER_FONT);
draw_text(canvas, g_font, bx, TB_BTN_Y + (TB_BTN_SIZE - HEADER_FONT) / 2,
zoom_label, HEADER_TEXT, HEADER_FONT);
bx += tw + 8;
}
}
@@ -433,18 +378,16 @@ static void render(uint32_t* pixels) {
// ---- Status bar ----
int sy = g_win_h - STATUS_BAR_H;
px_fill(pixels, g_win_w, g_win_h, 0, sy, g_win_w, STATUS_BAR_H, STATUS_BG);
canvas.fill_rect(0, sy, g_win_w, STATUS_BAR_H, STATUS_BG);
if (g_font) {
int sty = sy + (STATUS_BAR_H - HEADER_FONT) / 2;
// Left: filename and dimensions
if (g_load_ok && g_status[0]) {
g_font->draw_to_buffer(pixels, g_win_w, g_win_h, 8, sty,
g_status, STATUS_TEXT, HEADER_FONT);
draw_text(canvas, g_font, 8, sty, g_status, STATUS_TEXT, HEADER_FONT);
} else if (!g_load_ok) {
g_font->draw_to_buffer(pixels, g_win_w, g_win_h, 8, sty,
"No image loaded", STATUS_TEXT, HEADER_FONT);
draw_text(canvas, g_font, 8, sty, "No image loaded", STATUS_TEXT, HEADER_FONT);
}
// Right: zoom level
@@ -453,8 +396,7 @@ static void render(uint32_t* pixels) {
int pct = (int)(g_zoom * 100 + 0.5f);
snprintf(right, 32, "%d%% ", pct);
int rw = g_font->measure_text(right, HEADER_FONT);
g_font->draw_to_buffer(pixels, g_win_w, g_win_h,
g_win_w - rw - 8, sty, right, STATUS_TEXT, HEADER_FONT);
draw_text(canvas, g_font, g_win_w - rw - 8, sty, right, STATUS_TEXT, HEADER_FONT);
}
}
}
@@ -528,13 +470,11 @@ extern "C" void _start() {
}
}
// Create window
Montauk::WinCreateResult wres;
if (montauk::win_create(title, INIT_W, INIT_H, &wres) < 0 || wres.id < 0)
WsWindow win;
if (!win.create(title, INIT_W, INIT_H))
montauk::exit(1);
int win_id = wres.id;
uint32_t* pixels = (uint32_t*)(uintptr_t)wres.pixelVa;
Canvas canvas = win.canvas();
// Auto-fit on load if image is larger than viewport
if (g_load_ok) {
@@ -545,13 +485,13 @@ extern "C" void _start() {
}
}
render(pixels);
montauk::win_present(win_id);
render(canvas);
win.present();
// Event loop
while (true) {
Montauk::WinEvent ev;
int r = montauk::win_poll(win_id, &ev);
int r = win.poll(&ev);
if (r < 0) break;
if (r == 0) { montauk::sleep_ms(16); continue; }
@@ -560,12 +500,12 @@ extern "C" void _start() {
// Resize
if (ev.type == 2) {
g_win_w = ev.resize.w;
g_win_h = ev.resize.h;
pixels = (uint32_t*)(uintptr_t)montauk::win_resize(win_id, g_win_w, g_win_h);
g_win_w = win.width;
g_win_h = win.height;
if (g_load_ok) clamp_pan();
render(pixels);
montauk::win_present(win_id);
canvas = win.canvas();
render(canvas);
win.present();
continue;
}
@@ -646,12 +586,13 @@ extern "C" void _start() {
}
if (redraw) {
render(pixels);
montauk::win_present(win_id);
canvas = win.canvas();
render(canvas);
win.present();
}
}
if (g_image) montauk::mfree(g_image);
montauk::win_destroy(win_id);
win.destroy();
montauk::exit(0);
}