lockscreen show progressbar only on hold

This commit is contained in:
2025-10-19 23:48:14 +02:00
parent be2629a008
commit 7c492627f0

View File

@@ -17,7 +17,8 @@ namespace {
using cardboy::sdk::AppContext; using cardboy::sdk::AppContext;
constexpr std::uint32_t kRefreshIntervalMs = 500; constexpr std::uint32_t kRefreshIntervalMs = 100;
constexpr std::uint32_t kUnlockHoldMs = 1500;
using Framebuffer = typename AppContext::Framebuffer; using Framebuffer = typename AppContext::Framebuffer;
using Clock = typename AppContext::Clock; using Clock = typename AppContext::Clock;
@@ -40,8 +41,10 @@ public:
void onStart() override { void onStart() override {
cancelRefreshTimer(); cancelRefreshTimer();
lastSnapshot = {}; lastSnapshot = {};
dirty = true; holdActive = false;
holdProgressMs = 0;
dirty = true;
const auto snap = captureTime(); const auto snap = captureTime();
renderIfNeeded(snap); renderIfNeeded(snap);
lastSnapshot = snap; lastSnapshot = snap;
@@ -53,12 +56,13 @@ public:
void handleEvent(const cardboy::sdk::AppEvent& event) override { void handleEvent(const cardboy::sdk::AppEvent& event) override {
switch (event.type) { switch (event.type) {
case cardboy::sdk::AppEventType::Button: case cardboy::sdk::AppEventType::Button:
if (anyNewPress(event.button)) handleButtonEvent(event.button);
context.requestAppSwitchByName(kMenuAppName);
break; break;
case cardboy::sdk::AppEventType::Timer: case cardboy::sdk::AppEventType::Timer:
if (event.timer.handle == refreshTimer) if (event.timer.handle == refreshTimer) {
advanceHoldProgress();
updateDisplay(); updateDisplay();
}
break; break;
} }
} }
@@ -71,6 +75,8 @@ private:
bool dirty = false; bool dirty = false;
cardboy::sdk::AppTimerHandle refreshTimer = cardboy::sdk::kInvalidAppTimer; cardboy::sdk::AppTimerHandle refreshTimer = cardboy::sdk::kInvalidAppTimer;
TimeSnapshot lastSnapshot{}; TimeSnapshot lastSnapshot{};
bool holdActive = false;
std::uint32_t holdProgressMs = 0;
void cancelRefreshTimer() { void cancelRefreshTimer() {
if (refreshTimer != cardboy::sdk::kInvalidAppTimer) { if (refreshTimer != cardboy::sdk::kInvalidAppTimer) {
@@ -79,12 +85,45 @@ private:
} }
} }
static bool anyNewPress(const cardboy::sdk::AppButtonEvent& button) { static bool comboPressed(const cardboy::sdk::InputState& state) { return state.a && state.select; }
const auto& current = button.current;
const auto& previous = button.previous; void handleButtonEvent(const cardboy::sdk::AppButtonEvent& button) {
return (current.a && !previous.a) || (current.b && !previous.b) || (current.start && !previous.start) || const bool comboNow = comboPressed(button.current);
(current.select && !previous.select) || (current.up && !previous.up) || (current.down && !previous.down) || updateHoldState(comboNow);
(current.left && !previous.left) || (current.right && !previous.right); updateDisplay();
}
void updateHoldState(bool comboNow) {
if (comboNow) {
if (!holdActive) {
holdActive = true;
dirty = true;
}
} else {
if (holdActive || holdProgressMs != 0) {
holdActive = false;
holdProgressMs = 0;
dirty = true;
}
}
}
void advanceHoldProgress() {
if (holdActive) {
const std::uint32_t next =
std::min<std::uint32_t>(holdProgressMs + kRefreshIntervalMs, kUnlockHoldMs);
if (next != holdProgressMs) {
holdProgressMs = next;
dirty = true;
}
if (holdProgressMs >= kUnlockHoldMs) {
holdActive = false;
context.requestAppSwitchByName(kMenuAppName);
}
} else if (holdProgressMs != 0) {
holdProgressMs = 0;
dirty = true;
}
} }
void updateDisplay() { void updateDisplay() {
@@ -133,6 +172,29 @@ private:
font16x8::drawText(fb, x, y, text, scale, true, letterSpacing); font16x8::drawText(fb, x, y, text, scale, true, letterSpacing);
} }
static void drawRectOutline(Framebuffer& fb, int x, int y, int w, int h) {
if (w <= 0 || h <= 0)
return;
for (int dx = 0; dx < w; ++dx) {
fb.drawPixel(x + dx, y, true);
fb.drawPixel(x + dx, y + h - 1, true);
}
for (int dy = 0; dy < h; ++dy) {
fb.drawPixel(x, y + dy, true);
fb.drawPixel(x + w - 1, y + dy, true);
}
}
static void fillRect(Framebuffer& fb, int x, int y, int w, int h) {
if (w <= 0 || h <= 0)
return;
for (int dy = 0; dy < h; ++dy) {
for (int dx = 0; dx < w; ++dx) {
fb.drawPixel(x + dx, y + dy, true);
}
}
}
static std::string formatDate(const TimeSnapshot& snap) { static std::string formatDate(const TimeSnapshot& snap) {
static const char* kWeekdays[] = {"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"}; static const char* kWeekdays[] = {"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"};
if (!snap.hasWallTime) if (!snap.hasWallTime)
@@ -169,7 +231,24 @@ private:
const std::string dateLine = formatDate(snap); const std::string dateLine = formatDate(snap);
drawCenteredText(framebuffer, timeY + font16x8::kGlyphHeight * scaleTime + 24, dateLine, scaleSmall, 1); drawCenteredText(framebuffer, timeY + font16x8::kGlyphHeight * scaleTime + 24, dateLine, scaleSmall, 1);
drawCenteredText(framebuffer, framebuffer.height() - 40, "PRESS ANY BUTTON", scaleSmall, 1); const char* instruction = holdActive ? "KEEP HOLDING A+SELECT" : "HOLD A+SELECT";
drawCenteredText(framebuffer, framebuffer.height() - 52, instruction, scaleSmall, 1);
if (holdActive || holdProgressMs > 0) {
const int barWidth = framebuffer.width() - 64;
const int barHeight = 14;
const int barX = (framebuffer.width() - barWidth) / 2;
const int barY = framebuffer.height() - 32;
const int innerWidth = barWidth - 2;
const int innerHeight = barHeight - 2;
drawRectOutline(framebuffer, barX, barY, barWidth, barHeight);
const float ratio =
std::clamp(holdProgressMs / static_cast<float>(kUnlockHoldMs), 0.0f, 1.0f);
const int fillWidth = static_cast<int>(ratio * innerWidth + 0.5f);
if (fillWidth > 0)
fillRect(framebuffer, barX + 1, barY + 1, fillWidth, innerHeight);
}
framebuffer.sendFrame(); framebuffer.sendFrame();
} }