#include "logic/display_controller.h" #include "config.h" #include "logic/schedule.h" DisplayController::DisplayController(DisplayPower &power, Frames &frames) : _power(power), _frames(frames) { } void DisplayController::setSettings(const ScheduleSettings &schedule, const ClockSettings &clock) { _schedule = schedule; _clock = clock; _content_revision++; // e.g. the No WiFi setting changed: redraw } void DisplayController::setEffectTime(uint32_t ms) { _effect_time = ms; } void DisplayController::setPage(Page page) { _page = page; _content_revision++; } void DisplayController::pageChanged() { _content_revision++; } void DisplayController::showLive(uint32_t now) { _live = true; _live_since = now; _content_revision++; } void DisplayController::stopLive() { _live = false; _content_revision++; } bool DisplayController::liveActive() const { return _live; } void DisplayController::setPowerEnabled(bool enabled) { _power_enabled = enabled; } bool DisplayController::powerEnabled() const { return _power_enabled; } void DisplayController::update(uint32_t now, bool time_valid, const LocalTime &time, bool wifi_connected) { if (wifi_connected) _wifi_lost = false; else if (!_wifi_lost) { _wifi_lost = true; _wifi_lost_since = now; } bool show_no_wifi = _clock.no_wifi_screen && _wifi_lost && now - _wifi_lost_since >= NO_WIFI_SHOW_TIME; if (_live && now - _live_since >= LIVE_TIMEOUT) stopLive(); // Switched off elsewhere (power timer, Nano flashing): the Nano lost its frame if (!_power.isOn()) _screen = Screen::Unknown; if (_off_timer.expired(now)) { _power.off(); _screen = Screen::Unknown; return; } if (!_power_enabled) { if (_power.isOn()) { _showOnce(Screen::Off); _off_timer.request(now, CLEAR_BEFORE_OFF_TIME + _effect_time); } return; } // Live mode from the web UI: shown right away, whatever the time if (_live) { _off_timer.cancel(); _ensureOn(); _showOnce(Screen::Live); return; } // Keep the boot screen until the time is known if (!time_valid) return; switch (scheduleFor(time.hour, time.minute, _schedule)) { case ScheduleMode::Night: if (_power.isOn()) { _showOnce(Screen::Off); _off_timer.request(now, POWER_OFF_DELAY); } break; case ScheduleMode::Clock: _off_timer.cancel(); _ensureOn(); if (_page == Page::Dynamic) _showDynamic(now, time, show_no_wifi); else _showOnce(Screen::Page); break; case ScheduleMode::Standby: // Flipdots keep their image without power, so the last image stays visible if (_power.isOn()) _off_timer.request(now, POWER_OFF_DELAY); break; } } void DisplayController::_ensureOn() { if (_power.isOn()) return; _power.on(); _screen = Screen::Unknown; } // Drawn again each minute (time, date and background can change), when the content changed, // and as often as a moving page asks for (scrolling, seconds) void DisplayController::_showDynamic(uint32_t now, const LocalTime &time, bool no_wifi) { Screen screen = no_wifi ? Screen::NoWifi : Screen::Dynamic; bool redraw = _screen != screen || _shown_revision != _content_revision; bool due = screen == Screen::Dynamic && _dynamic_moving && (int32_t)(now - _dynamic_due) >= 0; if (!redraw && !due && _shown_hour == time.hour && _shown_minute == time.minute) return; if (no_wifi) { _frames.noWifi(time.hour, time.minute); _dynamic_moving = false; } else { uint32_t wait = _frames.dynamicPage(time, now, redraw); _dynamic_moving = wait != 0; _dynamic_due = now + wait; } _screen = screen; _shown_hour = time.hour; _shown_minute = time.minute; _shown_revision = _content_revision; } // Screens without the time: only sent again when they or their content changed void DisplayController::_showOnce(Screen screen) { if (_screen == screen && _shown_revision == _content_revision) return; switch (screen) { case Screen::Page: _frames.page(); break; case Screen::Live: _frames.live(); break; default: _frames.off(); break; } _screen = screen; _shown_revision = _content_revision; }