From bb02a08f2a61d27e5fd5504e140b6d992d167aa4 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Wed, 3 May 2023 22:16:52 +0200 Subject: [PATCH 1/7] SystemTask: implement screen locked until button is pressed When the device is woken through pressing the button everything is the same. But when woken through other means like single-tap or raise-to-wake, then the screen is locked (and showing a lock screen on touch input) until the button is pressed. Only exception is when the alarm wakes the screen, then touch input is still valid for the user to be able to press the red "stop alarm" button. Co-authored-by: NeroBurner --- src/CMakeLists.txt | 1 + src/displayapp/DisplayApp.cpp | 6 +++ src/displayapp/DisplayApp.h | 2 + src/displayapp/Messages.h | 2 + src/displayapp/widgets/PopupMessage.cpp | 56 +++++++++++++++++++++++++ src/displayapp/widgets/PopupMessage.h | 20 +++++++++ src/systemtask/SystemTask.cpp | 24 ++++++++++- src/systemtask/SystemTask.h | 2 + 8 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 src/displayapp/widgets/PopupMessage.cpp create mode 100644 src/displayapp/widgets/PopupMessage.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e4a354df64..cb5e87b8b9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -403,6 +403,7 @@ list(APPEND SOURCE_FILES displayapp/widgets/PageIndicator.cpp displayapp/widgets/DotIndicator.cpp displayapp/widgets/StatusIcons.cpp + displayapp/widgets/PopupMessage.cpp ## Settings displayapp/screens/settings/QuickSettings.cpp diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index 84fa603622..dec31ff978 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -485,6 +485,12 @@ void DisplayApp::Refresh() { LoadNewScreen(Apps::Clock, DisplayApp::FullRefreshDirections::None); motorController.RunForDuration(35); break; + case Messages::ShowIgnoreTouchPopup: + popupMessage.SetHidden(false); + break; + case Messages::HideIgnoreTouchPopup: + popupMessage.SetHidden(true); + break; } } diff --git a/src/displayapp/DisplayApp.h b/src/displayapp/DisplayApp.h index 016f91d3b6..dd1995d8b0 100644 --- a/src/displayapp/DisplayApp.h +++ b/src/displayapp/DisplayApp.h @@ -16,6 +16,7 @@ #include "components/stopwatch/StopWatchController.h" #include "components/alarm/AlarmController.h" #include "touchhandler/TouchHandler.h" +#include "displayapp/widgets/PopupMessage.h" #include "displayapp/Messages.h" #include "BootErrors.h" @@ -105,6 +106,7 @@ namespace Pinetime { Pinetime::Controllers::FirmwareValidator validator; Pinetime::Components::LittleVgl lvgl; Pinetime::Controllers::Timer timer; + Pinetime::Applications::Widgets::PopupMessage popupMessage; AppControllers controllers; TaskHandle_t taskHandle; diff --git a/src/displayapp/Messages.h b/src/displayapp/Messages.h index 1fcd72d278..76750a96e8 100644 --- a/src/displayapp/Messages.h +++ b/src/displayapp/Messages.h @@ -24,6 +24,8 @@ namespace Pinetime { AlarmTriggered, Chime, BleRadioEnableToggle, + ShowIgnoreTouchPopup, + HideIgnoreTouchPopup }; } } diff --git a/src/displayapp/widgets/PopupMessage.cpp b/src/displayapp/widgets/PopupMessage.cpp new file mode 100644 index 0000000000..ffcecdca4e --- /dev/null +++ b/src/displayapp/widgets/PopupMessage.cpp @@ -0,0 +1,56 @@ +#include "displayapp/widgets/PopupMessage.h" +#include "displayapp/InfiniTimeTheme.h" +#include + +using namespace Pinetime::Applications::Widgets; + +PopupMessage::PopupMessage() { +} + +void PopupMessage::Create() { + popup = lv_obj_create(lv_scr_act(), nullptr); + lv_obj_set_size(popup, 90, 90); + lv_obj_align(popup, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); + lv_obj_set_style_local_bg_color(popup, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, Colors::bg); + lv_obj_set_style_local_bg_opa(popup, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_60); + lv_obj_t* lockBody = lv_obj_create(popup, nullptr); + lv_obj_set_size(lockBody, 55, 50); + lv_obj_align(lockBody, popup, LV_ALIGN_CENTER, 0, 10); + + lv_obj_set_style_local_bg_color(lockBody, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); + lv_obj_set_style_local_bg_opa(lockBody, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_0); + lv_obj_set_style_local_border_color(lockBody, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); + lv_obj_set_style_local_border_width(lockBody, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, 22); + lv_obj_set_style_local_border_side(lockBody, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_BORDER_SIDE_FULL); + lv_obj_set_style_local_border_opa(lockBody, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_100); + + lv_obj_t* lockTop = lv_obj_create(popup, nullptr); + lv_obj_set_size(lockTop, 30, 35); + lv_obj_align(lockTop, popup, LV_ALIGN_CENTER, 0, -20); + lv_obj_set_style_local_bg_color(lockTop, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); + lv_obj_set_style_local_bg_opa(lockTop, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_0); + lv_obj_set_style_local_border_color(lockTop, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); + lv_obj_set_style_local_border_width(lockTop, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, 6); + lv_obj_set_style_local_border_side(lockTop, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_BORDER_SIDE_FULL); + lv_obj_set_style_local_border_opa(lockTop, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_100); + + lv_obj_set_hidden(popup, isHidden); +} + +void PopupMessage::SetHidden(bool hidden) { + if (isHidden == hidden) { + return; + } + isHidden = hidden; + // create/delete on demand + if (popup == nullptr && !isHidden) { + Create(); + } else if (popup != nullptr) { + lv_obj_del(popup); + popup = nullptr; + } +} + +bool PopupMessage::IsHidden() { + return isHidden; +} diff --git a/src/displayapp/widgets/PopupMessage.h b/src/displayapp/widgets/PopupMessage.h new file mode 100644 index 0000000000..39e16b2c8f --- /dev/null +++ b/src/displayapp/widgets/PopupMessage.h @@ -0,0 +1,20 @@ +#pragma once +#include + +namespace Pinetime { + namespace Applications { + namespace Widgets { + class PopupMessage { + public: + PopupMessage(); + void Create(); + void SetHidden(bool hidden); + bool IsHidden(); + + private: + lv_obj_t* popup = nullptr; + bool isHidden = true; + }; + } + } +} diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index 56bf9273e1..2eb19c95ce 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -232,6 +232,7 @@ void SystemTask::Work() { } break; case Messages::SetOffAlarm: + unlockedByButton = true; // unlock so it is possible to press red stop button GoToRunning(); displayApp.PushMessage(Pinetime::Applications::Display::Messages::AlarmTriggered); break; @@ -241,6 +242,7 @@ void SystemTask::Work() { bleDiscoveryTimer = 5; break; case Messages::BleFirmwareUpdateStarted: + unlockedByButton = true; // prevent no screen-locked popup on firmware update GoToRunning(); wakeLocksHeld++; displayApp.PushMessage(Pinetime::Applications::Display::Messages::BleFirmwareUpdateStarted); @@ -268,7 +270,14 @@ void SystemTask::Work() { break; } if (state == SystemTaskState::Running) { - displayApp.PushMessage(Pinetime::Applications::Display::Messages::TouchEvent); + if (unlockedByButton) { + displayApp.PushMessage(Pinetime::Applications::Display::Messages::TouchEvent); + } else { + auto gesture = touchHandler.GestureGet(); + if (gesture != Pinetime::Applications::TouchEvents::None) { + displayApp.PushMessage(Pinetime::Applications::Display::Messages::ShowIgnoreTouchPopup); + } + } } else { // If asleep, check for touch panel wake triggers auto gesture = touchHandler.GestureGet(); @@ -290,6 +299,7 @@ void SystemTask::Work() { action = buttonHandler.HandleEvent(Controllers::ButtonHandler::Events::Press); // This is for faster wakeup, sacrificing special longpress and doubleclick handling while sleeping if (IsSleeping()) { + unlockedByButton = true; fastWakeUpDone = true; GoToRunning(); break; @@ -331,6 +341,8 @@ void SystemTask::Work() { } else { state = SystemTaskState::AODSleeping; } + // lock when going to sleep + unlockedByButton = false; break; case Messages::OnNewDay: motionSensor.ResetStepCounter(); @@ -443,6 +455,8 @@ void SystemTask::GoToSleep() { displayApp.PushMessage(Pinetime::Applications::Display::Messages::GoToSleep); } heartRateApp.PushMessage(Pinetime::Applications::HeartRateTask::Messages::GoToSleep); + unlockedByButton = false; + displayApp.PushMessage(Pinetime::Applications::Display::Messages::HideIgnoreTouchPopup); state = SystemTaskState::GoingToSleep; }; @@ -482,7 +496,13 @@ void SystemTask::HandleButtonAction(Controllers::ButtonActions action) { case Actions::Click: // If the first action after fast wakeup is a click, it should be ignored. if (!fastWakeUpDone) { - displayApp.PushMessage(Applications::Display::Messages::ButtonPushed); + if (!unlockedByButton) { + // the first button event unlocks the touch input + unlockedByButton = true; + displayApp.PushMessage(Pinetime::Applications::Display::Messages::HideIgnoreTouchPopup); + } else { + displayApp.PushMessage(Applications::Display::Messages::ButtonPushed); + } } break; case Actions::DoubleClick: diff --git a/src/systemtask/SystemTask.h b/src/systemtask/SystemTask.h index 606ddd3492..abc5f80491 100644 --- a/src/systemtask/SystemTask.h +++ b/src/systemtask/SystemTask.h @@ -144,6 +144,8 @@ namespace Pinetime { void UpdateMotion(); static constexpr TickType_t batteryMeasurementPeriod = pdMS_TO_TICKS(10 * 60 * 1000); + bool unlockedByButton = true; + SystemMonitor monitor; }; } From 41d1c867f9849e5b5e7643261bef30691b322de8 Mon Sep 17 00:00:00 2001 From: Reinhold Gschweicher Date: Sat, 24 May 2025 21:40:04 +0200 Subject: [PATCH 2/7] PopupMessage: little cleanup, more const --- src/displayapp/widgets/PopupMessage.cpp | 7 ++----- src/displayapp/widgets/PopupMessage.h | 26 +++++++++++-------------- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/src/displayapp/widgets/PopupMessage.cpp b/src/displayapp/widgets/PopupMessage.cpp index ffcecdca4e..56545f5143 100644 --- a/src/displayapp/widgets/PopupMessage.cpp +++ b/src/displayapp/widgets/PopupMessage.cpp @@ -4,9 +4,6 @@ using namespace Pinetime::Applications::Widgets; -PopupMessage::PopupMessage() { -} - void PopupMessage::Create() { popup = lv_obj_create(lv_scr_act(), nullptr); lv_obj_set_size(popup, 90, 90); @@ -37,7 +34,7 @@ void PopupMessage::Create() { lv_obj_set_hidden(popup, isHidden); } -void PopupMessage::SetHidden(bool hidden) { +void PopupMessage::SetHidden(const bool hidden) { if (isHidden == hidden) { return; } @@ -51,6 +48,6 @@ void PopupMessage::SetHidden(bool hidden) { } } -bool PopupMessage::IsHidden() { +bool PopupMessage::IsHidden() const { return isHidden; } diff --git a/src/displayapp/widgets/PopupMessage.h b/src/displayapp/widgets/PopupMessage.h index 39e16b2c8f..6a43d5525b 100644 --- a/src/displayapp/widgets/PopupMessage.h +++ b/src/displayapp/widgets/PopupMessage.h @@ -1,20 +1,16 @@ #pragma once #include -namespace Pinetime { - namespace Applications { - namespace Widgets { - class PopupMessage { - public: - PopupMessage(); - void Create(); - void SetHidden(bool hidden); - bool IsHidden(); +namespace Pinetime::Applications::Widgets { + class PopupMessage { + public: + PopupMessage() = default; + void Create(); + void SetHidden(bool hidden); + bool IsHidden() const; - private: - lv_obj_t* popup = nullptr; - bool isHidden = true; - }; - } - } + private: + lv_obj_t* popup = nullptr; + bool isHidden = true; + }; } From 1743fc8f0542b6468bef6a87d1bad7dc4c40f036 Mon Sep 17 00:00:00 2001 From: Reinhold Gschweicher Date: Thu, 29 May 2025 22:32:31 +0200 Subject: [PATCH 3/7] SystemTask: clang-format --- src/systemtask/SystemTask.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index 2eb19c95ce..d97e6b95cd 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -232,7 +232,7 @@ void SystemTask::Work() { } break; case Messages::SetOffAlarm: - unlockedByButton = true; // unlock so it is possible to press red stop button + unlockedByButton = true; // unlock so it is possible to press red stop button GoToRunning(); displayApp.PushMessage(Pinetime::Applications::Display::Messages::AlarmTriggered); break; @@ -242,7 +242,7 @@ void SystemTask::Work() { bleDiscoveryTimer = 5; break; case Messages::BleFirmwareUpdateStarted: - unlockedByButton = true; // prevent no screen-locked popup on firmware update + unlockedByButton = true; // prevent no screen-locked popup on firmware update GoToRunning(); wakeLocksHeld++; displayApp.PushMessage(Pinetime::Applications::Display::Messages::BleFirmwareUpdateStarted); From 8dfed36e99201401fa9b555e5939e52677bf94b5 Mon Sep 17 00:00:00 2001 From: sim Date: Sun, 20 Jul 2025 12:57:45 +0200 Subject: [PATCH 4/7] Always hide lock popup when changing appview It fixes a potential double-free when removing lock popup --- src/displayapp/DisplayApp.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index dec31ff978..f65f3d5714 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -514,6 +514,11 @@ void DisplayApp::LoadNewScreen(Apps app, DisplayApp::FullRefreshDirections direc // This is mainly to fix an issue with receiving two notifications at the same time // and shouldn't happen otherwise. if (app != currentApp) { + // We need to remove the popup + // If we keep the popup linked to the previous view, and this view is deleted, a bug will occur if we try to re-remove the popup. + // Not removing the popup will also prevent the popup to be raised on top of + // the new app + popupMessage.SetHidden(true); returnAppStack.Push(currentApp); appStackDirections.Push(direction); } From 6805ad11dcf078a940ab856fead15667a85f1280 Mon Sep 17 00:00:00 2001 From: Reinhold Gschweicher Date: Fri, 22 Aug 2025 23:04:24 +0200 Subject: [PATCH 5/7] SystemTask: unlock at double button press as well, fixes segfault --- src/systemtask/SystemTask.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index d97e6b95cd..7172ceab5b 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -506,6 +506,11 @@ void SystemTask::HandleButtonAction(Controllers::ButtonActions action) { } break; case Actions::DoubleClick: + if (!unlockedByButton) { + // the first button event unlocks the touch input + unlockedByButton = true; + displayApp.PushMessage(Pinetime::Applications::Display::Messages::HideIgnoreTouchPopup); + } displayApp.PushMessage(Applications::Display::Messages::ButtonDoubleClicked); break; case Actions::LongPress: From a507abc544170873f30907fdee74768afeda8a9b Mon Sep 17 00:00:00 2001 From: Reinhold Gschweicher Date: Thu, 4 Sep 2025 19:39:03 +0200 Subject: [PATCH 6/7] SystemTask: hide lock before going to sleep --- src/systemtask/SystemTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index 7172ceab5b..fe848bad46 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -449,6 +449,7 @@ void SystemTask::GoToSleep() { return; } NRF_LOG_INFO("[systemtask] Going to sleep"); + displayApp.PushMessage(Pinetime::Applications::Display::Messages::HideIgnoreTouchPopup); if (settingsController.GetAlwaysOnDisplay()) { displayApp.PushMessage(Pinetime::Applications::Display::Messages::GoToAOD); } else { @@ -456,7 +457,6 @@ void SystemTask::GoToSleep() { } heartRateApp.PushMessage(Pinetime::Applications::HeartRateTask::Messages::GoToSleep); unlockedByButton = false; - displayApp.PushMessage(Pinetime::Applications::Display::Messages::HideIgnoreTouchPopup); state = SystemTaskState::GoingToSleep; }; From f1ed6d8bc535431259bff4eefae1235a7238e26e Mon Sep 17 00:00:00 2001 From: Reinhold Gschweicher Date: Wed, 7 Jan 2026 22:22:46 +0100 Subject: [PATCH 7/7] PopupMessage: update popup pointer when object is deleted Add a lvlg event handler when the `popup` object is deleted. This helps to prevent double free memory errors due to `PopupMessage` class not recognizing that the `popup` object was deleted by someone else (for example when switching away from a notification) --- src/displayapp/widgets/PopupMessage.cpp | 22 ++++++++++++++++++++++ src/displayapp/widgets/PopupMessage.h | 2 ++ 2 files changed, 24 insertions(+) diff --git a/src/displayapp/widgets/PopupMessage.cpp b/src/displayapp/widgets/PopupMessage.cpp index 56545f5143..429bac00ed 100644 --- a/src/displayapp/widgets/PopupMessage.cpp +++ b/src/displayapp/widgets/PopupMessage.cpp @@ -4,6 +4,24 @@ using namespace Pinetime::Applications::Widgets; +namespace { + void event_handler(lv_obj_t* obj, lv_event_t event) { + auto* popupMessage = static_cast(obj->user_data); + if (event == LV_EVENT_DELETE) { + popupMessage->HandleDelete(obj, event); + } + } +} + +void PopupMessage::HandleDelete(lv_obj_t* object, lv_event_t event) { + if (object == popup && event == LV_EVENT_DELETE) { + // make sure to update the state of the popup pointer when someone + // deletes the popup object + popup = nullptr; + isHidden = true; + } +} + void PopupMessage::Create() { popup = lv_obj_create(lv_scr_act(), nullptr); lv_obj_set_size(popup, 90, 90); @@ -31,6 +49,10 @@ void PopupMessage::Create() { lv_obj_set_style_local_border_side(lockTop, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_BORDER_SIDE_FULL); lv_obj_set_style_local_border_opa(lockTop, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_100); + // handler to keep popup pointer in sync + popup->user_data = this; + lv_obj_set_event_cb(popup, event_handler); + lv_obj_set_hidden(popup, isHidden); } diff --git a/src/displayapp/widgets/PopupMessage.h b/src/displayapp/widgets/PopupMessage.h index 6a43d5525b..15710f3cbb 100644 --- a/src/displayapp/widgets/PopupMessage.h +++ b/src/displayapp/widgets/PopupMessage.h @@ -8,6 +8,8 @@ namespace Pinetime::Applications::Widgets { void Create(); void SetHidden(bool hidden); bool IsHidden() const; + // public function only to be used by lvgl handler + void HandleDelete(lv_obj_t* object, lv_event_t event); private: lv_obj_t* popup = nullptr;