PixelUI separates time input, state updates, and framebuffer submission. With the exception of one narrow ISR entry point, all UI operations belong to the same UI task.
Scheduling entry points
| API | Purpose |
|---|---|
heartbeat(elapsedMs) |
Add the real elapsed milliseconds from normal context |
tickFromISR(elapsedMs) |
Atomically accumulate time in a timer ISR and optionally wake the UI task |
process() |
Consume elapsed time and update due deadline sources, Animations, Popups, and Coroutines |
renderer() |
Call process(), then submit a frame when dirty, fading, or continuously drawing |
handler(frameIntervalMs) |
Run one complete scheduling pass and return the delay until the next wakeup |
Use either heartbeat() or tickFromISR() as the time source. Mixing them counts the same time twice. The return value of process() only reports whether elapsed time was consumed; the return value of renderer() reports whether a frame was actually submitted.
Dirty-state coalescing
1 | sensorValue = newValue; |
markDirty() moves the UI from clean to dirty. Several consecutive calls require only one wakeup. setRenderRequestCallback() should only post a notification; it must not synchronously re-enter renderer() from the state-changing call stack.
RTOS task boundary
1 | timer ISR: tickFromISR() ----+ |
Widget setters, handleInput(), markDirty(), Popups, Animations, and ViewManager navigation must run in the UI task. A timer ISR may use the platform callback configured through setTaskNotifyFromISR() to wake that task. Configure it before enabling interrupts, and do not replace the callback or context while the interrupt can run.
Periodic and tickless modes [EXPERIMENTAL]
With PIXELUI_ENABLE_TICKLESS=0, handler() always returns the host-provided period. In tickless mode it combines the deadlines of registered sources, including BlinkState, Animation, Popup, Coroutine, and fade, and returns the earliest one. 0 means the task should run again immediately. PixelUI::WAIT_FOREVER means the UI timer may stop until an external event wakes the task.
UiDeadlineScheduler connects objects that make one discrete state change at a future time. A source registers and unregisters automatically during construction and destruction. The scheduler calls it only when due, and an actual state change decides whether to mark the UI dirty. It should not replace continuous animation.
The returned delay does not replace external event notification. A low-power port must handle both time deadlines and wakeups caused by input or new data.
State queries
nextWakeupMs(periodicTickMs)queries the next delay without updating or drawing.needsHeartbeat()reports that time-driven work still exists; it does not mean a frame is currently pending.hasPendingFrame()reports a dirty, fade, or continuous frame; it does not mean that no future deadline exists.
Use setContinuousDraw(true) only for visuals whose state must advance every frame and cannot be expressed through dirty state or deadlines. Regular Animations and BlinkState do not need it.
