#include "DisplayApp.h" #include #include #include #include #include #include "components/battery/BatteryController.h" #include "components/ble/BleController.h" #include "components/datetime/DateTimeController.h" #include "components/ble/NotificationManager.h" #include "components/motion/MotionController.h" #include "components/motor/MotorController.h" #include "displayapp/screens/ApplicationList.h" #include "displayapp/screens/Brightness.h" #include "displayapp/screens/Clock.h" #include "displayapp/screens/FirmwareUpdate.h" #include "displayapp/screens/FirmwareValidation.h" #include "displayapp/screens/InfiniPaint.h" #include "displayapp/screens/Paddle.h" #include "displayapp/screens/StopWatch.h" #include "displayapp/screens/Meter.h" #include "displayapp/screens/Metronome.h" #include "displayapp/screens/Music.h" #include "displayapp/screens/Navigation.h" #include "displayapp/screens/Notifications.h" #include "displayapp/screens/SystemInfo.h" #include "displayapp/screens/Tile.h" #include "displayapp/screens/Twos.h" #include "displayapp/screens/FlashLight.h" #include "displayapp/screens/BatteryInfo.h" #include "displayapp/screens/Steps.h" #include "drivers/Cst816s.h" #include "drivers/St7789.h" #include "drivers/Watchdog.h" #include "systemtask/SystemTask.h" #include "systemtask/Messages.h" #include "displayapp/screens/settings/QuickSettings.h" #include "displayapp/screens/settings/Settings.h" #include "displayapp/screens/settings/SettingWatchFace.h" #include "displayapp/screens/settings/SettingTimeFormat.h" #include "displayapp/screens/settings/SettingWakeUp.h" #include "displayapp/screens/settings/SettingDisplay.h" #include "displayapp/screens/settings/SettingSteps.h" #include "libs/lv_conf.h" using namespace Pinetime::Applications; using namespace Pinetime::Applications::Display; namespace { static inline bool in_isr(void) { return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) != 0; } TouchEvents ConvertGesture(Pinetime::Drivers::Cst816S::Gestures gesture) { switch (gesture) { case Pinetime::Drivers::Cst816S::Gestures::SingleTap: return TouchEvents::Tap; case Pinetime::Drivers::Cst816S::Gestures::LongPress: return TouchEvents::LongTap; case Pinetime::Drivers::Cst816S::Gestures::DoubleTap: return TouchEvents::DoubleTap; case Pinetime::Drivers::Cst816S::Gestures::SlideRight: return TouchEvents::SwipeRight; case Pinetime::Drivers::Cst816S::Gestures::SlideLeft: return TouchEvents::SwipeLeft; case Pinetime::Drivers::Cst816S::Gestures::SlideDown: return TouchEvents::SwipeDown; case Pinetime::Drivers::Cst816S::Gestures::SlideUp: return TouchEvents::SwipeUp; case Pinetime::Drivers::Cst816S::Gestures::None: default: return TouchEvents::None; } } } DisplayApp::DisplayApp(Drivers::St7789& lcd, Components::LittleVgl& lvgl, Drivers::Cst816S& touchPanel, Controllers::Battery& batteryController, Controllers::Ble& bleController, Controllers::DateTime& dateTimeController, Drivers::WatchdogView& watchdog, Pinetime::Controllers::NotificationManager& notificationManager, Pinetime::Controllers::HeartRateController& heartRateController, Controllers::Settings& settingsController, Pinetime::Controllers::MotorController& motorController, Pinetime::Controllers::MotionController& motionController, Pinetime::Controllers::TimerController& timerController, Pinetime::Controllers::AlarmController& alarmController, Pinetime::Controllers::TouchHandler& touchHandler) : lcd {lcd}, lvgl {lvgl}, touchPanel {touchPanel}, batteryController {batteryController}, bleController {bleController}, dateTimeController {dateTimeController}, watchdog {watchdog}, notificationManager {notificationManager}, heartRateController {heartRateController}, settingsController {settingsController}, motorController {motorController}, motionController {motionController}, timerController {timerController}, alarmController {alarmController}, touchHandler {touchHandler} { } void DisplayApp::Start() { msgQueue = xQueueCreate(queueSize, itemSize); // Start clock when smartwatch boots LoadApp(Apps::Clock, DisplayApp::FullRefreshDirections::None); if (pdPASS != xTaskCreate(DisplayApp::Process, "displayapp", 800, this, 0, &taskHandle)) { APP_ERROR_HANDLER(NRF_ERROR_NO_MEM); } } void DisplayApp::Process(void* instance) { auto* app = static_cast(instance); NRF_LOG_INFO("displayapp task started!"); app->InitHw(); // Send a dummy notification to unlock the lvgl display driver for the first iteration xTaskNotifyGive(xTaskGetCurrentTaskHandle()); while (true) { app->Refresh(); } } void DisplayApp::InitHw() { brightnessController.Init(); brightnessController.Set(settingsController.GetBrightness()); } void DisplayApp::Refresh() { TickType_t queueTimeout; switch (state) { case States::Idle: queueTimeout = portMAX_DELAY; break; case States::Running: if (!currentScreen->IsRunning()) { LoadApp(returnToApp, returnDirection); } queueTimeout = lv_task_handler(); break; default: queueTimeout = portMAX_DELAY; break; } Messages msg; if (xQueueReceive(msgQueue, &msg, queueTimeout)) { switch (msg) { case Messages::DimScreen: // Backup brightness is the brightness to return to after dimming or sleeping brightnessController.Backup(); brightnessController.Set(Controllers::BrightnessController::Levels::Low); break; case Messages::RestoreBrightness: brightnessController.Restore(); break; case Messages::GoToSleep: while (brightnessController.Level() != Controllers::BrightnessController::Levels::Off) { brightnessController.Lower(); vTaskDelay(100); } lcd.DisplayOff(); PushMessageToSystemTask(Pinetime::System::Messages::OnDisplayTaskSleeping); state = States::Idle; break; case Messages::GoToRunning: lcd.DisplayOn(); brightnessController.Restore(); state = States::Running; break; case Messages::UpdateTimeOut: PushMessageToSystemTask(System::Messages::UpdateTimeOut); break; case Messages::UpdateBleConnection: // clockScreen.SetBleConnectionState(bleController.IsConnected() ? Screens::Clock::BleConnectionStates::Connected : // Screens::Clock::BleConnectionStates::NotConnected); break; case Messages::NewNotification: LoadApp(Apps::NotificationsPreview, DisplayApp::FullRefreshDirections::Down); break; case Messages::TimerDone: if (currentApp == Apps::Timer) { auto* timer = static_cast(currentScreen.get()); timer->setDone(); } else { LoadApp(Apps::Timer, DisplayApp::FullRefreshDirections::Down); } break; case Messages::AlarmTriggered: if (currentApp == Apps::Alarm) { auto* alarm = static_cast(currentScreen.get()); alarm->SetAlerting(); } else { LoadApp(Apps::Alarm, DisplayApp::FullRefreshDirections::None); } case Messages::TouchEvent: { if (state != States::Running) { break; } auto gesture = ConvertGesture(touchHandler.GestureGet()); if (gesture == TouchEvents::None) { break; } if (!currentScreen->OnTouchEvent(gesture)) { if (currentApp == Apps::Clock) { switch (gesture) { case TouchEvents::SwipeUp: LoadApp(Apps::Launcher, DisplayApp::FullRefreshDirections::Up); break; case TouchEvents::SwipeDown: LoadApp(Apps::Notifications, DisplayApp::FullRefreshDirections::Down); break; case TouchEvents::SwipeRight: LoadApp(Apps::QuickSettings, DisplayApp::FullRefreshDirections::RightAnim); break; case TouchEvents::DoubleTap: PushMessageToSystemTask(System::Messages::GoToSleep); break; default: break; } } else if (returnTouchEvent == gesture) { LoadApp(returnToApp, returnDirection); brightnessController.Set(settingsController.GetBrightness()); brightnessController.Backup(); } } else { touchHandler.CancelTap(); } } break; case Messages::ButtonPushed: if (currentApp == Apps::Clock) { PushMessageToSystemTask(System::Messages::GoToSleep); } else { if (!currentScreen->OnButtonPushed()) { LoadApp(returnToApp, returnDirection); brightnessController.Set(settingsController.GetBrightness()); brightnessController.Backup(); } } break; case Messages::BleFirmwareUpdateStarted: LoadApp(Apps::FirmwareUpdate, DisplayApp::FullRefreshDirections::Down); break; case Messages::UpdateDateTime: // Added to remove warning // What should happen here? break; } } if (nextApp != Apps::None) { LoadApp(nextApp, nextDirection); nextApp = Apps::None; } if (touchHandler.IsTouching()) { currentScreen->OnTouchEvent(touchHandler.GetX(), touchHandler.GetY()); } } void DisplayApp::StartApp(Apps app, DisplayApp::FullRefreshDirections direction) { nextApp = app; nextDirection = direction; } void DisplayApp::ReturnApp(Apps app, DisplayApp::FullRefreshDirections direction, TouchEvents touchEvent) { returnToApp = app; returnDirection = direction; returnTouchEvent = touchEvent; } void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) { touchHandler.CancelTap(); currentScreen.reset(nullptr); SetFullRefresh(direction); // default return to launcher ReturnApp(Apps::Launcher, FullRefreshDirections::Down, TouchEvents::SwipeDown); switch (app) { case Apps::Launcher: currentScreen = std::make_unique(this, settingsController, batteryController, dateTimeController); ReturnApp(Apps::Clock, FullRefreshDirections::Down, TouchEvents::SwipeDown); break; case Apps::None: case Apps::Clock: currentScreen = std::make_unique(this, dateTimeController, batteryController, bleController, notificationManager, settingsController, heartRateController, motionController); break; case Apps::FirmwareValidation: currentScreen = std::make_unique(this, validator); ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown); break; case Apps::FirmwareUpdate: currentScreen = std::make_unique(this, bleController); ReturnApp(Apps::Clock, FullRefreshDirections::Down, TouchEvents::None); break; case Apps::Notifications: currentScreen = std::make_unique( this, notificationManager, systemTask->nimble().alertService(), motorController, Screens::Notifications::Modes::Normal); ReturnApp(Apps::Clock, FullRefreshDirections::Up, TouchEvents::SwipeUp); break; case Apps::NotificationsPreview: currentScreen = std::make_unique( this, notificationManager, systemTask->nimble().alertService(), motorController, Screens::Notifications::Modes::Preview); ReturnApp(Apps::Clock, FullRefreshDirections::Up, TouchEvents::SwipeUp); break; case Apps::Timer: currentScreen = std::make_unique(this, timerController); break; case Apps::Alarm: currentScreen = std::make_unique(this, alarmController); break; // Settings case Apps::QuickSettings: currentScreen = std::make_unique( this, batteryController, dateTimeController, brightnessController, motorController, settingsController); ReturnApp(Apps::Clock, FullRefreshDirections::LeftAnim, TouchEvents::SwipeLeft); break; case Apps::Settings: currentScreen = std::make_unique(this, settingsController); ReturnApp(Apps::QuickSettings, FullRefreshDirections::Down, TouchEvents::SwipeDown); break; case Apps::SettingWatchFace: currentScreen = std::make_unique(this, settingsController); ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown); break; case Apps::SettingTimeFormat: currentScreen = std::make_unique(this, settingsController); ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown); break; case Apps::SettingWakeUp: currentScreen = std::make_unique(this, settingsController); ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown); break; case Apps::SettingDisplay: currentScreen = std::make_unique(this, settingsController); ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown); break; case Apps::SettingSteps: currentScreen = std::make_unique(this, settingsController); ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown); break; case Apps::BatteryInfo: currentScreen = std::make_unique(this, batteryController); ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown); break; case Apps::SysInfo: currentScreen = std::make_unique( this, dateTimeController, batteryController, brightnessController, bleController, watchdog, motionController); ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown); break; case Apps::FlashLight: currentScreen = std::make_unique(this, *systemTask, brightnessController); ReturnApp(Apps::Clock, FullRefreshDirections::Down, TouchEvents::None); break; case Apps::StopWatch: currentScreen = std::make_unique(this, *systemTask); break; case Apps::Twos: currentScreen = std::make_unique(this); break; case Apps::Paint: currentScreen = std::make_unique(this, lvgl); break; case Apps::Paddle: currentScreen = std::make_unique(this, lvgl); break; case Apps::Music: currentScreen = std::make_unique(this, systemTask->nimble().music()); break; case Apps::Navigation: currentScreen = std::make_unique(this, systemTask->nimble().navigation()); break; case Apps::HeartRate: currentScreen = std::make_unique(this, heartRateController, *systemTask); break; case Apps::Metronome: currentScreen = std::make_unique(this, motorController, *systemTask); ReturnApp(Apps::Launcher, FullRefreshDirections::Down, TouchEvents::None); break; case Apps::Motion: currentScreen = std::make_unique(this, motionController); break; case Apps::Steps: currentScreen = std::make_unique(this, motionController, settingsController); break; } currentApp = app; } void DisplayApp::PushMessage(Messages msg) { if (in_isr()) { BaseType_t xHigherPriorityTaskWoken; xHigherPriorityTaskWoken = pdFALSE; xQueueSendFromISR(msgQueue, &msg, &xHigherPriorityTaskWoken); if (xHigherPriorityTaskWoken) { portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } } else { xQueueSend(msgQueue, &msg, portMAX_DELAY); } } void DisplayApp::SetFullRefresh(DisplayApp::FullRefreshDirections direction) { switch (direction) { case DisplayApp::FullRefreshDirections::Down: lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::Down); break; case DisplayApp::FullRefreshDirections::Up: lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::Up); break; case DisplayApp::FullRefreshDirections::Left: lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::Left); break; case DisplayApp::FullRefreshDirections::Right: lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::Right); break; case DisplayApp::FullRefreshDirections::LeftAnim: lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::LeftAnim); break; case DisplayApp::FullRefreshDirections::RightAnim: lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::RightAnim); break; default: break; } } void DisplayApp::PushMessageToSystemTask(Pinetime::System::Messages message) { if (systemTask != nullptr) systemTask->PushMessage(message); } void DisplayApp::Register(Pinetime::System::SystemTask* systemTask) { this->systemTask = systemTask; }