CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
LockScreenView.cpp
Go to the documentation of this file.
1
6
9#include "cdc_ui/I18n.h"
11#include "cdc_views/KeyCodes.h"
12#include "cdc_hal/IDisplay.h"
13#include "cdc_hal/IKeypad.h"
17#include "cdc_log.h"
18#include "freertos/FreeRTOS.h"
19#include "freertos/task.h"
20#include <goodisplay/gdey029T94.h>
21#include "cdc_views/Fonts.h"
23#include <cstring>
24
25static const char* TAG = "LockScreen";
26
30static constexpr int CLOCK_Y = 5;
31static constexpr int DATE_Y = 22;
32static constexpr int ICONS_Y = 5;
33static constexpr int NAME_Y = 60; // Name position (FreeFont baseline)
34static constexpr int INFO_Y = 80; // Info line 1 (small text)
35static constexpr int INFO2_Y = 96; // Info line 2 (small text)
36static constexpr int BATTERY_X = 260;
37static constexpr int BATTERY_Y = 5;
38static constexpr int DISPLAY_WIDTH = 296;
39
40
44static constexpr int BAT_WIDTH = 28;
45static constexpr int BAT_HEIGHT = 12;
46static constexpr int BAT_TIP_WIDTH = 3;
47static constexpr int BAT_TIP_HEIGHT = 6;
48
49namespace cdc::ui {
50
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_));
60 batteryPercent_ = 0;
61 statusIcons_ = StatusIcon::NONE;
62 nPressStartMs_ = 0;
63 dirty_ = true;
64}
65
71 const char* incoming = name ? name : "";
72 if (strncmp(name_, incoming, MAX_TEXT_LEN) == 0) return;
73 if (name) {
74 strncpy(name_, name, MAX_TEXT_LEN - 1);
75 name_[MAX_TEXT_LEN - 1] = '\0';
76 } else {
77 name_[0] = '\0';
78 }
79 dirty_ = true;
80}
81
86void LockScreenView::setInfo(const char* info) {
87 const char* incoming = info ? info : "";
88 if (strncmp(info_, incoming, MAX_TEXT_LEN) == 0) return;
89 if (info) {
90 strncpy(info_, info, MAX_TEXT_LEN - 1);
91 info_[MAX_TEXT_LEN - 1] = '\0';
92 } else {
93 info_[0] = '\0';
94 }
95 dirty_ = true;
96}
97
102void LockScreenView::setInfo2(const char* info2) {
103 const char* incoming = info2 ? info2 : "";
104 if (strncmp(info2_, incoming, MAX_TEXT_LEN) == 0) return;
105 if (info2) {
106 strncpy(info2_, info2, MAX_TEXT_LEN - 1);
107 info2_[MAX_TEXT_LEN - 1] = '\0';
108 } else {
109 info2_[0] = '\0';
110 }
111 dirty_ = true;
112}
113
118void LockScreenView::setClock(const char* clock) {
119 const char* incoming = clock ? clock : "--:--";
120 if (strncmp(clock_, incoming, sizeof(clock_)) == 0) return;
121 if (clock) {
122 strncpy(clock_, clock, sizeof(clock_) - 1);
123 clock_[sizeof(clock_) - 1] = '\0';
124 } else {
125 strcpy(clock_, "--:--");
126 }
127 dirty_ = true;
128}
129
134void LockScreenView::setDate(const char* date) {
135 const char* incoming = date ? date : "";
136 if (strncmp(date_, incoming, sizeof(date_)) == 0) return;
137 if (date) {
138 strncpy(date_, date, sizeof(date_) - 1);
139 date_[sizeof(date_) - 1] = '\0';
140 } else {
141 date_[0] = '\0';
142 }
143 dirty_ = true;
144}
145
151 if (percent > 100) percent = 100;
152 if (batteryPercent_ != percent) {
153 batteryPercent_ = percent;
154 dirty_ = true;
155 }
156}
157
163 if (statusIcons_ != icons) {
164 statusIcons_ = icons;
165 dirty_ = true;
166 }
167}
168
174 setStatusIcons(statusIcons_ | icon);
175}
176
182 setStatusIcons(static_cast<StatusIcon>(
183 static_cast<uint16_t>(statusIcons_) & ~static_cast<uint16_t>(icon)
184 ));
185}
186
191
196void LockScreenView::onEnter(void* context) {
197 (void)context;
199 nPressStartMs_ = 0; // Reset deep sleep trigger
200
201 // Turn off backlight when entering lock screen (unless persistent light is on)
202 if ((statusIcons_ & StatusIcon::BACKLIGHT) == StatusIcon::NONE) {
204 if (display) {
205 display->backlightOff();
206 }
207 }
208 dirty_ = true;
209}
210
215 // Called when returning to lock screen (e.g., via N from main menu)
216 nPressStartMs_ = 0; // Reset deep sleep trigger
217
218 // Turn off backlight (unless persistent light is on)
219 if ((statusIcons_ & StatusIcon::BACKLIGHT) == StatusIcon::NONE) {
221 if (display) {
222 display->backlightOff();
223 }
224 }
225 dirty_ = true;
226}
227
233 if (!display) return;
234
235 if (display->isBacklightOn()) {
236 display->backlightOff();
238 } else {
239 display->backlightOn();
241 }
242 dirty_ = true;
243}
244
248static void onLightMenuCallback() {
250 s_lockScreenInstance->toggleBacklight();
251 }
253}
254
258static void onWifiToggleCallback() {
260 auto& wifi = WifiHandlers::instance();
261 wifi.setUserEnabled(!wifi.isConnected());
262}
263
267static constexpr uint8_t MAX_CONTEXT_ITEMS = 12;
268static constexpr uint8_t MAX_PLUGIN_ITEMS = 4;
269// Number of module context-menu wrapper callbacks defined below. The module
270// item array and the registry request are both bounded by this so that
271// s_moduleCallbacks is never indexed out of range. Keep in sync with the
272// s_moduleCallbacks[] table (enforced by static_assert after it).
273static constexpr uint8_t MAX_MODULE_CONTEXT_ITEMS = 7;
276static uint8_t s_moduleContextCount = 0;
277
279static uint8_t s_pluginContextCount = 0;
280
285static void moduleContextCallback0() { if (s_moduleContextItems[0].callback) { s_moduleContextItems[0].callback(); } hideContextMenu(); }
290static void moduleContextCallback1() { if (s_moduleContextItems[1].callback) { s_moduleContextItems[1].callback(); } hideContextMenu(); }
295static void moduleContextCallback2() { if (s_moduleContextItems[2].callback) { s_moduleContextItems[2].callback(); } hideContextMenu(); }
300static void moduleContextCallback3() { if (s_moduleContextItems[3].callback) { s_moduleContextItems[3].callback(); } hideContextMenu(); }
305static void moduleContextCallback4() { if (s_moduleContextItems[4].callback) { s_moduleContextItems[4].callback(); } hideContextMenu(); }
310static void moduleContextCallback5() { if (s_moduleContextItems[5].callback) { s_moduleContextItems[5].callback(); } hideContextMenu(); }
315static void moduleContextCallback6() { if (s_moduleContextItems[6].callback) { s_moduleContextItems[6].callback(); } hideContextMenu(); }
316
322static_assert(sizeof(s_moduleCallbacks) / sizeof(s_moduleCallbacks[0]) == MAX_MODULE_CONTEXT_ITEMS,
323 "module context callback count must match MAX_MODULE_CONTEXT_ITEMS");
324
345
355 // KEY_MENU ('3') opens context menu for light toggle + module items
356 if (key == KEY_MENU) {
357 uint8_t itemCount = 0;
358
359 // First item: Light toggle (built-in)
360 s_contextItems[itemCount++] = {ui::tr("core.light"), onLightMenuCallback};
361
362 // Second item: WiFi toggle (built-in); label reflects current state
363 const char* wifiLabel = WifiHandlers::instance().isConnected()
364 ? ui::tr("core.wifi_off") : ui::tr("core.wifi_on");
365 s_contextItems[itemCount++] = {wifiLabel, onWifiToggleCallback};
366
367 // Get module items from registry
368 auto& moduleReg = core::ModuleRegistry::instance();
369 s_moduleContextCount = moduleReg.getLockScreenContextItems(s_moduleContextItems, MAX_MODULE_CONTEXT_ITEMS);
370
371 // Add module items to context menu
372 for (uint8_t i = 0; i < s_moduleContextCount && itemCount < MAX_CONTEXT_ITEMS; i++) {
373 const char* label = s_moduleContextItems[i].getLabel ? s_moduleContextItems[i].getLabel() : "???";
374 s_contextItems[itemCount++] = {label, s_moduleCallbacks[i]};
375 }
376
377 // Plugin-contributed lockscreen items.
380 for (uint8_t i = 0; i < s_pluginContextCount && itemCount < MAX_CONTEXT_ITEMS; i++) {
381 s_contextItems[itemCount++] = {s_pluginContextItems[i].label, s_pluginCallbacks[i]};
382 }
383
384 showContextMenu(ui::tr("core.actions"), s_contextItems, itemCount);
386 }
387
388 // Any other key triggers unlock
389 if (onUnlock_) {
390 onUnlock_();
391 }
393}
394
399void LockScreenView::onTick(uint32_t nowMs) {
400 // Check for long-press N -> deep sleep (flight mode)
401 checkDeepSleepTrigger(nowMs);
402}
403
408void LockScreenView::checkDeepSleepTrigger(uint32_t nowMs) {
409 auto* keypad = hal::getKeypadInstance();
410 if (!keypad) return;
411
412 bool nPressed = keypad->isKeyPressed(hal::Key::KEY_NO);
413
414 if (nPressed) {
415 if (nPressStartMs_ == 0) {
416 // N key just pressed, start timing
417 nPressStartMs_ = nowMs;
418 } else {
419 // Check if held long enough
420 uint32_t elapsed = nowMs - nPressStartMs_;
421 if (elapsed >= DEEP_SLEEP_HOLD_MS) {
422 LOG_I(TAG, "Long-press N detected, entering deep sleep...");
423
424 // Render deep sleep screen and push to e-paper
425 renderDeepSleepScreen();
426
427 // Turn off backlight
429 if (display) {
430 display->backlightOff();
431 }
432
433 // Wait 2s so user can release button without triggering wakeup
434 vTaskDelay(pdMS_TO_TICKS(2000));
435
436 // Enter deep sleep (does not return - causes reset on wake)
437 auto* sleep = hal::getSleepControllerInstance();
438 if (sleep) {
439 sleep->enterDeepSleep();
440 }
441
442 // Should not reach here
443 nPressStartMs_ = 0;
444 }
445 }
446 } else {
447 // N released, reset timer
448 nPressStartMs_ = 0;
449 }
450}
451
456const char* LockScreenView::getFooterHint() const {
457 if (deepSleepMode_) return ui::tr("core.deep_sleep");
458 return ui::tr("core.press_any_key");
459}
460
467void LockScreenView::renderBattery(void* gfxPtr, int x, int y) {
468 auto* gfx = static_cast<Gdey029T94*>(gfxPtr);
469 // Battery outline
470 gfx->drawRect(x, y, BAT_WIDTH, BAT_HEIGHT, EPD_BLACK);
471
472 // Battery tip
473 gfx->fillRect(x + BAT_WIDTH, y + (BAT_HEIGHT - BAT_TIP_HEIGHT) / 2,
474 BAT_TIP_WIDTH, BAT_TIP_HEIGHT, EPD_BLACK);
475
476 // Fill level (skip if no battery - show empty outline)
477 bool noBattery = (statusIcons_ & StatusIcon::NO_BATTERY) != StatusIcon::NONE;
478 if (!noBattery) {
479 int fillWidth = (batteryPercent_ * (BAT_WIDTH - 4)) / 100;
480 if (fillWidth > 0) {
481 gfx->fillRect(x + 2, y + 2, fillWidth, BAT_HEIGHT - 4, EPD_BLACK);
482 }
483 }
484
485 // Charging indicator
486 if ((statusIcons_ & StatusIcon::CHARGING) != StatusIcon::NONE) {
487 // Draw lightning bolt
488 gfx->drawLine(x + BAT_WIDTH / 2 + 2, y + 1, x + BAT_WIDTH / 2 - 2, y + BAT_HEIGHT / 2, EPD_BLACK);
489 gfx->drawLine(x + BAT_WIDTH / 2 - 2, y + BAT_HEIGHT / 2, x + BAT_WIDTH / 2 + 2, y + BAT_HEIGHT / 2, EPD_BLACK);
490 gfx->drawLine(x + BAT_WIDTH / 2 + 2, y + BAT_HEIGHT / 2, x + BAT_WIDTH / 2 - 2, y + BAT_HEIGHT - 2, EPD_BLACK);
491 }
492
493 // No battery indicator - diagonal strike-through
494 if ((statusIcons_ & StatusIcon::NO_BATTERY) != StatusIcon::NONE) {
495 gfx->drawLine(x - 2, y + BAT_HEIGHT + 2, x + BAT_WIDTH + BAT_TIP_WIDTH + 2, y - 2, EPD_BLACK);
496 gfx->drawLine(x - 2, y + BAT_HEIGHT + 3, x + BAT_WIDTH + BAT_TIP_WIDTH + 2, y - 1, EPD_BLACK);
497 }
498}
499
506void LockScreenView::renderStatusIcons(void* gfxPtr, int x, int y) {
507 auto* gfx = static_cast<Gdey029T94*>(gfxPtr);
508 int iconX = x;
509 const int iconSpacing = 14;
510
511 // Lock icon
512 if ((statusIcons_ & StatusIcon::LOCK) != StatusIcon::NONE) {
513 // Draw padlock
514 gfx->drawRect(iconX, y + 4, 8, 6, EPD_BLACK);
515 gfx->drawCircle(iconX + 4, y + 3, 3, EPD_BLACK);
516 iconX -= iconSpacing;
517 }
518
519 // WiFi icon - three stacked ripple arcs with a base dot
520 if ((statusIcons_ & StatusIcon::WIFI) != StatusIcon::NONE) {
521 const int cx = iconX + 5;
522 const int cy = y + 10;
523
524 // Arc 3 (large): 5px horizontal cap with stepped sides
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);
530
531 // Arc 2 (medium): 3px horizontal cap with stepped sides
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);
535
536 // Arc 1 (small): single-pixel peak
537 gfx->drawPixel(cx, cy - 2, EPD_BLACK);
538
539 // Base dot (2x2)
540 gfx->fillRect(cx, cy, 2, 2, EPD_BLACK);
541
542 iconX -= iconSpacing;
543 }
544
545 // BLE icon
546 if ((statusIcons_ & StatusIcon::BLE) != StatusIcon::NONE) {
547 // Bluetooth rune
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;
554 }
555
556 // USB icon (simplified USB trident)
557 if ((statusIcons_ & StatusIcon::USB) != StatusIcon::NONE) {
558 int ux = iconX, uy = y;
559 // Main stem
560 gfx->drawLine(ux + 5, uy + 4, ux + 5, uy + 12, EPD_BLACK);
561 // Top horizontal
562 gfx->drawLine(ux + 2, uy + 4, ux + 8, uy + 4, EPD_BLACK);
563 // Left branch with circle
564 gfx->drawLine(ux + 2, uy + 4, ux + 2, uy, EPD_BLACK);
565 gfx->fillCircle(ux + 2, uy, 1, EPD_BLACK);
566 // Right branch with rectangle
567 gfx->drawLine(ux + 8, uy + 4, ux + 8, uy + 2, EPD_BLACK);
568 gfx->fillRect(ux + 6, uy, 4, 3, EPD_BLACK);
569 // Bottom arrow
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;
573 }
574
575 // Backlight icon (sun)
576 if ((statusIcons_ & StatusIcon::BACKLIGHT) != StatusIcon::NONE) {
577 gfx->fillCircle(iconX + 4, y + 5, 2, EPD_BLACK);
578 for (int i = 0; i < 8; i++) {
579 int angle = i * 45;
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);
585 }
586 iconX -= iconSpacing;
587 }
588
589 // Sleep icons
590 if ((statusIcons_ & StatusIcon::DEEP_SLEEP) != StatusIcon::NONE) {
591 gfx->setCursor(iconX, y + 2);
592 gfx->print("zzZ");
593 iconX -= iconSpacing + 10;
594 } else if ((statusIcons_ & StatusIcon::LIGHT_SLEEP) != StatusIcon::NONE) {
595 gfx->setCursor(iconX, y + 2);
596 gfx->print("z");
597 iconX -= iconSpacing;
598 }
599
600 // Caffeinated icon (sleep inhibited) - coffee cup
601 if ((statusIcons_ & StatusIcon::CAFFEINATED) != StatusIcon::NONE) {
602 int cx = iconX, cy = y;
603 // Cup body
604 gfx->drawRect(cx, cy + 3, 8, 7, EPD_BLACK);
605 // Cup handle
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);
609 // Steam (wavy lines)
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;
615 }
616
617 // Background plugin running - play triangle inside a square frame
618 if ((statusIcons_ & StatusIcon::BACKGROUND) != StatusIcon::NONE) {
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);
628 }
629 }
630 iconX -= iconSpacing;
631 }
632 (void)iconX;
633}
634
638void LockScreenView::renderDeepSleepScreen() {
639 deepSleepMode_ = true;
640
641 // Clear clock and status icons for minimal screen
642 setClock("");
643 setDate("");
644 statusIcons_ = StatusIcon::NONE;
645
646 // Render normal lockscreen (with deep sleep footer) and push to display
647 render(false);
648
650 if (display) {
652 }
653}
654
659void LockScreenView::render(bool partial) {
660 if (preRenderCb_) {
661 preRenderCb_();
662 }
663
665 if (!display) return;
666
667 auto* gfx = static_cast<Gdey029T94*>(display->getNativeHandle());
668 if (!gfx) return;
669
670 if (!partial) {
671 gfx->fillScreen(EPD_WHITE);
672 }
673
674 gfx->setTextColor(EPD_BLACK);
675
676 // === Top Left: Clock ===
677 gfx->setFont(nullptr);
678 gfx->setTextSize(2); // Size 2 for better fit
679 gfx->setCursor(5, CLOCK_Y);
680 gfx->print(clock_);
681
682 // === Below clock: Date ===
683 gfx->setTextSize(1);
684 gfx->setCursor(5, DATE_Y);
685 render::printText(gfx, date_);
686
687 // === Top Right: Battery ===
688 renderBattery(gfx, BATTERY_X, BATTERY_Y);
689
690 // === Right of battery: Status icons ===
691 renderStatusIcons(gfx, BATTERY_X - 20, ICONS_Y);
692
693 constexpr int FIT_BUDGET = DISPLAY_WIDTH - 10;
694
695 auto fitAndDraw = [&](const char* text, int baselineY,
696 const GFXfont* const* candidates, size_t count) {
697 if (!text || !text[0]) return;
698 const GFXfont* f = cdc::ui::render::pickFontThatFits(
699 gfx, text, FIT_BUDGET, candidates, count, true);
700 int16_t x1, y1;
701 uint16_t w = 0, h = 0;
702 cdc::ui::render::measureText(gfx, text, f, 0, 0, &x1, &y1, &w, &h);
703 int x = (display->getWidth() - w) / 2;
704 gfx->setCursor(x, baselineY);
705 cdc::ui::render::drawText(gfx, text, f);
706 };
707
708 using cdc::ui::FontId;
709 static const GFXfont* const NAME_FONTS[] = {
713 };
714 static const GFXfont* const INFO_FONTS[] = {
717 };
718
719 // === Center: Name (size 3 = 12pt, fallback to smaller) ===
720 fitAndDraw(name_, NAME_Y, NAME_FONTS, std::size(NAME_FONTS));
721
722 // === Info line 1 (size 2 = 9pt, fallback to 1) ===
723 fitAndDraw(info_, INFO_Y, INFO_FONTS, std::size(INFO_FONTS));
724
725 // === Info line 2 (size 2 = 9pt, fallback to 1) ===
726 fitAndDraw(info2_, INFO2_Y, INFO_FONTS, std::size(INFO_FONTS));
727
728 // === Bottom: Footer hint (size 1 = built-in 6x8) ===
729 gfx->setFont(nullptr);
730 gfx->setTextSize(1);
731 const char* hint = getFooterHint();
732 if (hint && hint[0]) {
733 int16_t x1, y1;
734 uint16_t w, h;
735 gfx->getTextBounds(hint, 0, 0, &x1, &y1, &w, &h);
736 int hintX = (display->getWidth() - w) / 2;
737 gfx->setCursor(hintX, display->getHeight() - 10);
738 render::printText(gfx, hint);
739 }
740
741 dirty_ = false;
742}
743
744} // namespace cdc::ui
static const char * TAG
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,...)
Definition cdc_log.h:147
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.
Definition IModule.h:8
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.
Definition I18n.h:208
static void pluginContextCallback1()
const GFXfont * getGfxFont(FontId id)
Resolves a FontId to its underlying GFX font pointer.
Definition Fonts.cpp:26
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'.
Definition KeyCodes.h:47
static ContextMenuItem s_contextItems[MAX_CONTEXT_ITEMS]
static void moduleContextCallback3()
Wrapper callback for module context item at index 3.
Gdey029T94 * display
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.
Definition Fonts.h:15
@ Bold12pt
FreeMonoBold 12pt. Latin-1 indexed; same mapping path as Bold9pt.
Definition Fonts.h:18
@ Builtin
Adafruit-GFX 6x8. Umlauts present at CP437 codepoints (0x84..0x9C). Pass raw CP437 bytes to draw them...
Definition Fonts.h:16
@ Bold9pt
FreeMonoBold 9pt. Latin-1 indexed; render via drawCp437Text() to map CP437 input.
Definition Fonts.h:17
InputResult
Definition IView.h:10
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.
Definition IModule.h:42