// clock.c — Clock DXE application (main-loop with tsYield) // // Demonstrates a main-loop app: runs its own loop calling tsYield(), // updates a clock display every second, and invalidates its window. #include "dvxApp.h" #include "dvxWidget.h" #include "dvxDraw.h" #include "dvxVideo.h" #include "shellApp.h" #include "taskswitch.h" #include #include #include #include #include // ============================================================ // Module state // ============================================================ typedef struct { bool quit; char timeStr[32]; char dateStr[32]; time_t lastUpdate; } ClockStateT; static DxeAppContextT *sCtx = NULL; static WindowT *sWin = NULL; static ClockStateT sState; // ============================================================ // Prototypes // ============================================================ int32_t appMain(DxeAppContextT *ctx); void appShutdown(void); static void onClose(WindowT *win); static void onPaint(WindowT *win, RectT *dirty); static void updateTime(void); // ============================================================ // App descriptor // ============================================================ AppDescriptorT appDescriptor = { .name = "Clock", .hasMainLoop = true, .stackSize = 0, .priority = TS_PRIORITY_LOW }; // ============================================================ // Callbacks (fire in task 0 during dvxUpdate) // ============================================================ static void onClose(WindowT *win) { (void)win; sState.quit = true; } static void onPaint(WindowT *win, RectT *dirty) { (void)dirty; AppContextT *ac = sCtx->shellCtx; const BlitOpsT *ops = dvxGetBlitOps(ac); const BitmapFontT *font = dvxGetFont(ac); const ColorSchemeT *colors = dvxGetColors(ac); // Local display copy pointing at content buffer DisplayT cd = *dvxGetDisplay(ac); cd.backBuf = win->contentBuf; cd.width = win->contentW; cd.height = win->contentH; cd.pitch = win->contentPitch; cd.clipX = 0; cd.clipY = 0; cd.clipW = win->contentW; cd.clipH = win->contentH; // Background rectFill(&cd, ops, 0, 0, win->contentW, win->contentH, colors->contentBg); // Time string (large, centered) int32_t timeW = textWidth(font, sState.timeStr); int32_t timeX = (win->contentW - timeW) / 2; int32_t timeY = win->contentH / 3; drawText(&cd, ops, font, timeX, timeY, sState.timeStr, colors->contentFg, colors->contentBg, true); // Date string (centered below) int32_t dateW = textWidth(font, sState.dateStr); int32_t dateX = (win->contentW - dateW) / 2; int32_t dateY = timeY + font->charHeight + 8; drawText(&cd, ops, font, dateX, dateY, sState.dateStr, colors->contentFg, colors->contentBg, true); } // ============================================================ // Time update // ============================================================ static void updateTime(void) { time_t now = time(NULL); struct tm *tm = localtime(&now); if (!tm) { snprintf(sState.timeStr, sizeof(sState.timeStr), "--:--:--"); snprintf(sState.dateStr, sizeof(sState.dateStr), "----:--:--"); sState.lastUpdate = now; return; } snprintf(sState.timeStr, sizeof(sState.timeStr), "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); snprintf(sState.dateStr, sizeof(sState.dateStr), "%04d-%02d-%02d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); sState.lastUpdate = now; } // ============================================================ // Shutdown hook (optional DXE export) // ============================================================ void appShutdown(void) { sState.quit = true; } // ============================================================ // Entry point (runs in its own task) // ============================================================ int32_t appMain(DxeAppContextT *ctx) { sCtx = ctx; AppContextT *ac = ctx->shellCtx; memset(&sState, 0, sizeof(sState)); updateTime(); int32_t winW = 200; int32_t winH = 100; int32_t winX = ac->display.width - winW - 40; int32_t winY = 40; sWin = dvxCreateWindow(ac, "Clock", winX, winY, winW, winH, false); if (!sWin) { return -1; } sWin->onClose = onClose; sWin->onPaint = onPaint; // Initial paint into content buffer RectT full = {0, 0, sWin->contentW, sWin->contentH}; onPaint(sWin, &full); dvxInvalidateWindow(ac, sWin); // Main loop: update time, invalidate, yield while (!sState.quit) { time_t now = time(NULL); if (now != sState.lastUpdate) { updateTime(); onPaint(sWin, &full); dvxInvalidateWindow(ac, sWin); } tsYield(); } // Cleanup if (sWin) { dvxDestroyWindow(ac, sWin); sWin = NULL; } return 0; }