From e453b4d35aca60e98803db8eb2292324a934932a Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Mon, 9 Mar 2026 23:23:31 -0500 Subject: [PATCH] Icons now update to reflect window activity. --- dvx/dvxApp.c | 53 ++++++++++++++++++++++++++++++++++++++- dvx/dvxApp.h | 6 ++--- dvx/dvxTypes.h | 1 + dvx/dvxWm.c | 3 +++ dvx/widgets/widgetEvent.c | 1 + dvx/widgets/widgetOps.c | 1 + 6 files changed, 61 insertions(+), 4 deletions(-) diff --git a/dvx/dvxApp.c b/dvx/dvxApp.c index d65d1f6..5671d5e 100644 --- a/dvx/dvxApp.c +++ b/dvx/dvxApp.c @@ -8,7 +8,8 @@ #include #include -#define DBLCLICK_THRESHOLD (CLOCKS_PER_SEC / 2) +#define DBLCLICK_THRESHOLD (CLOCKS_PER_SEC / 2) +#define ICON_REFRESH_INTERVAL 8 // ============================================================ // Prototypes @@ -23,6 +24,7 @@ static void initColorScheme(AppContextT *ctx); static void initMouse(AppContextT *ctx); static void pollKeyboard(AppContextT *ctx); static void pollMouse(AppContextT *ctx); +static void refreshMinimizedIcons(AppContextT *ctx); static void updateCursorShape(AppContextT *ctx); @@ -495,6 +497,13 @@ bool dvxUpdate(AppContextT *ctx) { pollKeyboard(ctx); dispatchEvents(ctx); + // Periodically refresh one minimized window thumbnail (staggered) + ctx->frameCount++; + + if (ctx->frameCount % ICON_REFRESH_INTERVAL == 0) { + refreshMinimizedIcons(ctx); + } + if (ctx->dirty.count > 0) { compositeAndFlush(ctx); } else { @@ -830,6 +839,48 @@ static void pollMouse(AppContextT *ctx) { } +// ============================================================ +// refreshMinimizedIcons +// ============================================================ +// +// Dirty the next minimized window icon whose content has changed +// since the last refresh. Only considers windows without custom +// iconData. Called every ICON_REFRESH_INTERVAL frames to stagger. + +static void refreshMinimizedIcons(AppContextT *ctx) { + WindowStackT *ws = &ctx->stack; + DisplayT *d = &ctx->display; + int32_t count = 0; + int32_t iconIdx = 0; + + for (int32_t i = 0; i < ws->count; i++) { + WindowT *win = ws->windows[i]; + + if (!win->visible || !win->minimized) { + continue; + } + + if (!win->iconData && win->contentDirty) { + if (count >= ctx->iconRefreshIdx) { + int32_t ix = ICON_SPACING + iconIdx * (ICON_TOTAL_SIZE + ICON_SPACING); + int32_t iy = d->height - ICON_TOTAL_SIZE - ICON_SPACING; + dirtyListAdd(&ctx->dirty, ix, iy, ICON_TOTAL_SIZE, ICON_TOTAL_SIZE); + win->contentDirty = false; + ctx->iconRefreshIdx = count + 1; + return; + } + + count++; + } + + iconIdx++; + } + + // Wrapped past the end — reset for next cycle + ctx->iconRefreshIdx = 0; +} + + // ============================================================ // updateCursorShape // ============================================================ diff --git a/dvx/dvxApp.h b/dvx/dvxApp.h index 4473f04..b69ac19 100644 --- a/dvx/dvxApp.h +++ b/dvx/dvxApp.h @@ -37,6 +37,8 @@ typedef struct AppContextT { int32_t lastIconClickId; // window ID of last-clicked minimized icon (-1 = none) clock_t lastCloseClickTime; int32_t lastCloseClickId; // window ID of last-clicked close gadget (-1 = none) + int32_t iconRefreshIdx; // next minimized icon to refresh (staggered) + int32_t frameCount; // frame counter for periodic tasks } AppContextT; // Initialize the application (VESA mode, input, etc.) @@ -53,9 +55,7 @@ void dvxRun(AppContextT *ctx); bool dvxUpdate(AppContextT *ctx); // Create a window -WindowT *dvxCreateWindow(AppContextT *ctx, const char *title, - int32_t x, int32_t y, int32_t w, int32_t h, - bool resizable); +WindowT *dvxCreateWindow(AppContextT *ctx, const char *title, int32_t x, int32_t y, int32_t w, int32_t h, bool resizable); // Destroy a window void dvxDestroyWindow(AppContextT *ctx, WindowT *win); diff --git a/dvx/dvxTypes.h b/dvx/dvxTypes.h index 60446b1..03a4ab2 100644 --- a/dvx/dvxTypes.h +++ b/dvx/dvxTypes.h @@ -227,6 +227,7 @@ typedef struct WindowT { bool minimized; bool maximized; bool resizable; + bool contentDirty; // true when contentBuf has changed since last icon refresh int32_t maxW; // maximum width (-1 = screen width) int32_t maxH; // maximum height (-1 = screen height) int32_t preMaxX; // saved position before maximize diff --git a/dvx/dvxWm.c b/dvx/dvxWm.c index efc79ca..00d80de 100644 --- a/dvx/dvxWm.c +++ b/dvx/dvxWm.c @@ -1102,6 +1102,7 @@ void wmMaximize(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, WindowT if (win->onPaint) { RectT fullRect = {0, 0, win->contentW, win->contentH}; win->onPaint(win, &fullRect); + win->contentDirty = true; } // Mark new position dirty @@ -1461,6 +1462,7 @@ void wmResizeMove(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, int32_ if (win->onPaint) { RectT fullRect = {0, 0, win->contentW, win->contentH}; win->onPaint(win, &fullRect); + win->contentDirty = true; } stack->dragOffX = mouseX; @@ -1502,6 +1504,7 @@ void wmRestore(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, WindowT * if (win->onPaint) { RectT fullRect = {0, 0, win->contentW, win->contentH}; win->onPaint(win, &fullRect); + win->contentDirty = true; } // Mark restored position dirty diff --git a/dvx/widgets/widgetEvent.c b/dvx/widgets/widgetEvent.c index 740e0c4..56afbd4 100644 --- a/dvx/widgets/widgetEvent.c +++ b/dvx/widgets/widgetEvent.c @@ -566,5 +566,6 @@ void widgetOnScroll(WindowT *win, ScrollbarOrientE orient, int32_t value) { if (win->onPaint) { RectT fullRect = {0, 0, win->contentW, win->contentH}; win->onPaint(win, &fullRect); + win->contentDirty = true; } } diff --git a/dvx/widgets/widgetOps.c b/dvx/widgets/widgetOps.c index 5f50a1c..88c98a8 100644 --- a/dvx/widgets/widgetOps.c +++ b/dvx/widgets/widgetOps.c @@ -333,6 +333,7 @@ void wgtInvalidate(WidgetT *w) { // Repaint RectT fullRect = {0, 0, w->window->contentW, w->window->contentH}; widgetOnPaint(w->window, &fullRect); + w->window->contentDirty = true; // Dirty the window on screen dvxInvalidateWindow(ctx, w->window);