18#include "freertos/FreeRTOS.h"
19#include "freertos/task.h"
20#include <goodisplay/gdey029T94.h>
25static const char*
TAG =
"LockScreen";
55 memset(name_, 0,
sizeof(name_));
56 memset(info_, 0,
sizeof(info_));
57 memset(info2_, 0,
sizeof(info2_));
58 strcpy(clock_,
"--:--");
59 memset(date_, 0,
sizeof(date_));
71 const char* incoming =
name ?
name :
"";
87 const char* incoming = info ? info :
"";
103 const char* incoming = info2 ? info2 :
"";
104 if (strncmp(info2_, incoming,
MAX_TEXT_LEN) == 0)
return;
119 const char* incoming = clock ? clock :
"--:--";
120 if (strncmp(clock_, incoming,
sizeof(clock_)) == 0)
return;
122 strncpy(clock_, clock,
sizeof(clock_) - 1);
123 clock_[
sizeof(clock_) - 1] =
'\0';
125 strcpy(clock_,
"--:--");
135 const char* incoming = date ? date :
"";
136 if (strncmp(date_, incoming,
sizeof(date_)) == 0)
return;
138 strncpy(date_, date,
sizeof(date_) - 1);
139 date_[
sizeof(date_) - 1] =
'\0';
151 if (percent > 100) percent = 100;
152 if (batteryPercent_ != percent) {
153 batteryPercent_ = percent;
163 if (statusIcons_ != icons) {
164 statusIcons_ = icons;
183 static_cast<uint16_t
>(statusIcons_) & ~
static_cast<uint16_t
>(icon)
235 if (
display->isBacklightOn()) {
261 wifi.setUserEnabled(!wifi.isConnected());
323 "module context callback count must match MAX_MODULE_CONTEXT_ITEMS");
357 uint8_t itemCount = 0;
401 checkDeepSleepTrigger(nowMs);
408void LockScreenView::checkDeepSleepTrigger(uint32_t nowMs) {
415 if (nPressStartMs_ == 0) {
417 nPressStartMs_ = nowMs;
420 uint32_t elapsed = nowMs - nPressStartMs_;
422 LOG_I(
TAG,
"Long-press N detected, entering deep sleep...");
425 renderDeepSleepScreen();
434 vTaskDelay(pdMS_TO_TICKS(2000));
439 sleep->enterDeepSleep();
457 if (deepSleepMode_)
return ui::tr(
"core.deep_sleep");
458 return ui::tr(
"core.press_any_key");
467void LockScreenView::renderBattery(
void* gfxPtr,
int x,
int y) {
468 auto* gfx =
static_cast<Gdey029T94*
>(gfxPtr);
479 int fillWidth = (batteryPercent_ * (
BAT_WIDTH - 4)) / 100;
481 gfx->fillRect(x + 2, y + 2, fillWidth,
BAT_HEIGHT - 4, EPD_BLACK);
506void LockScreenView::renderStatusIcons(
void* gfxPtr,
int x,
int y) {
507 auto* gfx =
static_cast<Gdey029T94*
>(gfxPtr);
509 const int iconSpacing = 14;
514 gfx->drawRect(iconX, y + 4, 8, 6, EPD_BLACK);
515 gfx->drawCircle(iconX + 4, y + 3, 3, EPD_BLACK);
516 iconX -= iconSpacing;
521 const int cx = iconX + 5;
522 const int cy = y + 10;
525 gfx->drawLine(cx - 2, cy - 8, cx + 2, cy - 8, EPD_BLACK);
526 gfx->drawPixel(cx - 3, cy - 7, EPD_BLACK);
527 gfx->drawPixel(cx + 3, cy - 7, EPD_BLACK);
528 gfx->drawPixel(cx - 4, cy - 6, EPD_BLACK);
529 gfx->drawPixel(cx + 4, cy - 6, EPD_BLACK);
532 gfx->drawLine(cx - 1, cy - 5, cx + 1, cy - 5, EPD_BLACK);
533 gfx->drawPixel(cx - 2, cy - 4, EPD_BLACK);
534 gfx->drawPixel(cx + 2, cy - 4, EPD_BLACK);
537 gfx->drawPixel(cx, cy - 2, EPD_BLACK);
540 gfx->fillRect(cx, cy, 2, 2, EPD_BLACK);
542 iconX -= iconSpacing;
548 gfx->drawLine(iconX + 4, y, iconX + 4, y + 10, EPD_BLACK);
549 gfx->drawLine(iconX + 4, y, iconX + 8, y + 3, EPD_BLACK);
550 gfx->drawLine(iconX + 8, y + 3, iconX + 2, y + 7, EPD_BLACK);
551 gfx->drawLine(iconX + 2, y + 3, iconX + 8, y + 7, EPD_BLACK);
552 gfx->drawLine(iconX + 8, y + 7, iconX + 4, y + 10, EPD_BLACK);
553 iconX -= iconSpacing;
558 int ux = iconX, uy = y;
560 gfx->drawLine(ux + 5, uy + 4, ux + 5, uy + 12, EPD_BLACK);
562 gfx->drawLine(ux + 2, uy + 4, ux + 8, uy + 4, EPD_BLACK);
564 gfx->drawLine(ux + 2, uy + 4, ux + 2, uy, EPD_BLACK);
565 gfx->fillCircle(ux + 2, uy, 1, EPD_BLACK);
567 gfx->drawLine(ux + 8, uy + 4, ux + 8, uy + 2, EPD_BLACK);
568 gfx->fillRect(ux + 6, uy, 4, 3, EPD_BLACK);
570 gfx->drawLine(ux + 5, uy + 12, ux + 3, uy + 10, EPD_BLACK);
571 gfx->drawLine(ux + 5, uy + 12, ux + 7, uy + 10, EPD_BLACK);
572 iconX -= iconSpacing;
577 gfx->fillCircle(iconX + 4, y + 5, 2, EPD_BLACK);
578 for (
int i = 0; i < 8; i++) {
580 int dx = (angle == 0 || angle == 180) ? 4 : (angle == 90 || angle == 270) ? 0 : 3;
581 int dy = (angle == 90 || angle == 270) ? 4 : (angle == 0 || angle == 180) ? 0 : 3;
582 if (angle > 90 && angle < 270) dx = -dx;
583 if (angle > 0 && angle < 180) dy = -dy;
584 gfx->drawPixel(iconX + 4 + dx, y + 5 + dy, EPD_BLACK);
586 iconX -= iconSpacing;
591 gfx->setCursor(iconX, y + 2);
593 iconX -= iconSpacing + 10;
595 gfx->setCursor(iconX, y + 2);
597 iconX -= iconSpacing;
602 int cx = iconX, cy = y;
604 gfx->drawRect(cx, cy + 3, 8, 7, EPD_BLACK);
606 gfx->drawLine(cx + 8, cy + 4, cx + 10, cy + 4, EPD_BLACK);
607 gfx->drawLine(cx + 10, cy + 4, cx + 10, cy + 8, EPD_BLACK);
608 gfx->drawLine(cx + 8, cy + 8, cx + 10, cy + 8, EPD_BLACK);
610 gfx->drawPixel(cx + 2, cy + 1, EPD_BLACK);
611 gfx->drawPixel(cx + 3, cy, EPD_BLACK);
612 gfx->drawPixel(cx + 5, cy + 1, EPD_BLACK);
613 gfx->drawPixel(cx + 6, cy, EPD_BLACK);
614 iconX -= iconSpacing;
619 int bx = iconX, by = y;
620 gfx->drawRect(bx, by + 1, 11, 11, EPD_BLACK);
621 const int cy = by + 6;
622 for (
int row = 0; row <= 8; row++) {
623 int rowY = by + 2 + row;
624 int r = rowY > cy ? rowY - cy : cy - rowY;
625 int rightX = bx + 8 - r;
626 if (rightX >= bx + 3) {
627 gfx->drawLine(bx + 3, rowY, rightX, rowY, EPD_BLACK);
630 iconX -= iconSpacing;
638void LockScreenView::renderDeepSleepScreen() {
639 deepSleepMode_ =
true;
667 auto* gfx =
static_cast<Gdey029T94*
>(
display->getNativeHandle());
671 gfx->fillScreen(EPD_WHITE);
674 gfx->setTextColor(EPD_BLACK);
677 gfx->setFont(
nullptr);
684 gfx->setCursor(5,
DATE_Y);
695 auto fitAndDraw = [&](
const char* text,
int baselineY,
696 const GFXfont*
const* candidates,
size_t count) {
697 if (!text || !text[0])
return;
699 gfx, text, FIT_BUDGET, candidates, count,
true);
701 uint16_t w = 0, h = 0;
703 int x = (
display->getWidth() - w) / 2;
704 gfx->setCursor(x, baselineY);
709 static const GFXfont*
const NAME_FONTS[] = {
714 static const GFXfont*
const INFO_FONTS[] = {
720 fitAndDraw(name_,
NAME_Y, NAME_FONTS, std::size(NAME_FONTS));
723 fitAndDraw(info_,
INFO_Y, INFO_FONTS, std::size(INFO_FONTS));
726 fitAndDraw(info2_,
INFO2_Y, INFO_FONTS, std::size(INFO_FONTS));
729 gfx->setFont(
nullptr);
732 if (hint && hint[0]) {
735 gfx->getTextBounds(hint, 0, 0, &x1, &y1, &w, &h);
736 int hintX = (
display->getWidth() - w) / 2;
737 gfx->setCursor(hintX,
display->getHeight() - 10);
Internationalization with English fallbacks in code and overlay translations loaded at runtime from a...
static constexpr int DATE_Y
static constexpr int BAT_TIP_WIDTH
static constexpr int BATTERY_X
static constexpr int DISPLAY_WIDTH
static constexpr int BATTERY_Y
static constexpr int INFO2_Y
static constexpr int BAT_WIDTH
Battery icon dimensions.
static constexpr int ICONS_Y
static constexpr int BAT_TIP_HEIGHT
static constexpr int BAT_HEIGHT
static constexpr int INFO_Y
static constexpr int NAME_Y
static constexpr int CLOCK_Y
Display layout constants.
Discovers, loads, runs and unloads WASM plugins on the badge.
char name[cdc::hal::ISecureElement::RMEM_NAME_LEN]
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_I(tag, fmt,...)
static ModuleRegistry & instance()
Returns the singleton module registry instance.
uint8_t getLockscreenItems(LockscreenItem *out, uint8_t max) const
Snapshot of all plugin lockscreen items. Returns the number written.
static PluginManager & instance() noexcept
void triggerLockscreenItem(const LockscreenItem &item)
Fire plugin_on_action(item.action_id, 0, 0) on the owning plugin.
void removeStatusIcon(StatusIcon icon)
Removes one status icon flag.
void onResume() override
Handles returning to lock screen and reapplies backlight policy.
void onTick(uint32_t nowMs) override
Per-tick handler for long-press deep-sleep detection.
void setBatteryPercent(uint8_t percent)
Updates battery percentage indicator.
void render(bool partial) override
Renders complete lock-screen layout.
const char * getFooterHint() const override
Returns footer hint based on current lock-screen mode.
InputResult onKey(char key) override
Handles lock-screen key actions.
void setClock(const char *clock)
Sets clock text shown in the header.
void init()
Initializes lock-screen state fields to defaults.
static constexpr uint8_t MAX_TEXT_LEN
void addStatusIcon(StatusIcon icon)
Adds one status icon flag.
void setDate(const char *date)
Sets date text shown below clock.
void setStatusIcons(StatusIcon icons)
Replaces full status-icon bitmask.
void toggleBacklight()
Toggles display backlight and corresponding status icon.
void setInfo2(const char *info2)
Sets second informational line.
void setInfo(const char *info)
Sets first informational line.
void setDisplayName(const char *name)
Sets primary display name shown on lock screen.
void onEnter(void *context) override
Handles entering lock screen and updates backlight behavior.
static WifiHandlers & instance()
Returns singleton Wi-Fi handlers instance.
bool isConnected() const
Returns whether Wi-Fi is currently connected.
IDisplay * getDisplayInstance()
Returns lazily created singleton display instance.
IKeypad * getKeypadInstance()
Returns the singleton keypad service instance.
ISleepController * getSleepControllerInstance()
Returns the singleton sleep controller service instance.
void drawText(Gdey029T94 *gfx, const char *text, const GFXfont *font)
Draws CP437-encoded text correctly for the given font: the built-in glcdfont (font == nullptr) is CP4...
void measureText(Gdey029T94 *gfx, const char *text, const GFXfont *font, int16_t x0, int16_t y0, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h)
Measures CP437 text exactly as drawText would render it with font, so width-based layout (centering,...
const GFXfont * pickFontThatFits(Gdey029T94 *gfx, const char *text, int maxWidthPx, const GFXfont *const *candidates, size_t count, bool cp437=false)
Picks the largest font from candidates whose rendered width of text fits within maxWidthPx....
void printText(Gdey029T94 *gfx, const char *text)
Draws CP437 text with the built-in 6x8 glyph font, byte-for-byte.
Centralized key-code constants for cdc_views.
static void moduleContextCallback5()
Wrapper callback for module context item at index 5.
const char * tr(const char *key)
Look up a translation by string key.
static void pluginContextCallback1()
const GFXfont * getGfxFont(FontId id)
Resolves a FontId to its underlying GFX font pointer.
static uint8_t s_pluginContextCount
static void onLightMenuCallback()
Context-menu callback toggling lock-screen backlight mode.
static constexpr uint8_t MAX_MODULE_CONTEXT_ITEMS
static cdc::plugin_manager::PluginManager::LockscreenItem s_pluginContextItems[MAX_PLUGIN_ITEMS]
static void pluginContextCallback2()
static void(*const s_moduleCallbacks[])()
static constexpr char KEY_MENU
Open context menu / digit '3'.
static ContextMenuItem s_contextItems[MAX_CONTEXT_ITEMS]
static void moduleContextCallback3()
Wrapper callback for module context item at index 3.
static void(*const s_pluginCallbacks[MAX_PLUGIN_ITEMS])()
static constexpr uint32_t DEEP_SLEEP_HOLD_MS
static LockScreenView * s_lockScreenInstance
Static lock-screen instance pointer for C-style callbacks.
FontId
Canonical font identifier shared by firmware UI and plugin host API.
@ Bold12pt
FreeMonoBold 12pt. Latin-1 indexed; same mapping path as Bold9pt.
@ Builtin
Adafruit-GFX 6x8. Umlauts present at CP437 codepoints (0x84..0x9C). Pass raw CP437 bytes to draw them...
@ Bold9pt
FreeMonoBold 9pt. Latin-1 indexed; render via drawCp437Text() to map CP437 input.
static void moduleContextCallback1()
Wrapper callback for module context item at index 1.
static void moduleContextCallback2()
Wrapper callback for module context item at index 2.
static void moduleContextCallback4()
Wrapper callback for module context item at index 4.
static constexpr uint8_t MAX_CONTEXT_ITEMS
Storage for dynamic context-menu items contributed by modules.
void hideContextMenu()
Hides the active context menu modal.
static uint8_t s_moduleContextCount
ContextMenuView * showContextMenu(const char *title, const ContextMenuItem *items, uint8_t count)
Shows the shared context menu instance as modal.
static void moduleContextCallback0()
Wrapper callback for module context item at index 0.
static void pluginContextCallback0()
static void onWifiToggleCallback()
Context-menu callback toggling WiFi on/off via the user intent flag.
static void moduleContextCallback6()
Wrapper callback for module context item at index 6.
static constexpr uint8_t MAX_PLUGIN_ITEMS
static void pluginContextCallback3()
static core::LockScreenContextItem s_moduleContextItems[MAX_MODULE_CONTEXT_ITEMS]
Lock screen context menu item registered by a module.