Compare commits

..

No commits in common. "main" and "Watch_Binary" have entirely different histories.

34 changed files with 123 additions and 1008 deletions

View File

@ -11,7 +11,6 @@ RUN apt-get update -qq \
make \
python3 \
python3-pip \
python3-pil \
tar \
unzip \
wget \

View File

@ -31,10 +31,6 @@ jobs:
uses: actions/checkout@v3
with:
submodules: recursive
- name: Install resource build dependencies
run: |
apt-get update
apt-get -y install --no-install-recommends python3-pil
- name: Build
shell: bash
run: /opt/build.sh all

View File

@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.10)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose Debug or Release")
project(pinetime VERSION 1.13.1.1 LANGUAGES C CXX ASM)
project(pinetime VERSION 1.13.0 LANGUAGES C CXX ASM)
set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 14)

View File

@ -1,8 +1,5 @@
# TODO
## Completed
### Pending version
### v1.13.1
- [x] Make $s in terminal watch face green
- [x] Swipe left to access music control
- [x] Flashlight starts on
@ -12,8 +9,6 @@
- [x] Swipe left/right goes to watchface/another app
- [x] Rearrange quick acces and apps
- [x] Exponent button on calculator
- [x] Seconds on digital watchface
- [x] Add quick ring settings
## Pending merge
@ -22,17 +17,18 @@
- [ ] Countdown timer presistant buzz
### Josh
- [ ] Smiley face watchface :)
- [ ] Remap button double click
- [ ] Seconds on digital watchface
- [ ] Battery on digital watchface
- [ ] Remap button double click (to music app)
### Moses
- [ ] Add quick ring settings
## Medium priority
- [ ] Battery on digital watchface
- [ ] More watchfaces!
## Lower priority
- [ ] Temp sensor w/ arduino
- [ ] Homeassistant control
## Lower priority
- [ ] Maybe some prank watchfaces

View File

@ -42,7 +42,7 @@ CMake configures the project according to variables you specify the command line
**NRF5_SDK_PATH**|path to the NRF52 SDK|`-DNRF5_SDK_PATH=/home/jf/nrf52/Pinetime/sdk`|
**CMAKE_BUILD_TYPE (\*)**| Build type (Release or Debug). Release is applied by default if this variable is not specified.|`-DCMAKE_BUILD_TYPE=Debug`
**BUILD_DFU (\*\*)**|Build DFU files while building (needs [adafruit-nrfutil](https://github.com/adafruit/Adafruit_nRF52_nrfutil)).|`-DBUILD_DFU=1`
**BUILD_RESOURCES (\*\*)**| Generate external resource while building (needs [lv_font_conv](https://github.com/lvgl/lv_font_conv) and [python3-pil/pillow](https://pillow.readthedocs.io) module). |`-DBUILD_RESOURCES=1`
**BUILD_RESOURCES (\*\*)**| Generate external resource while building (needs [lv_font_conv](https://github.com/lvgl/lv_font_conv) and [lv_img_conv](https://github.com/lvgl/lv_img_conv). |`-DBUILD_RESOURCES=1`
**TARGET_DEVICE**|Target device, used for hardware configuration. Allowed: `PINETIME, MOY-TFK5, MOY-TIN5, MOY-TON5, MOY-UNK`|`-DTARGET_DEVICE=PINETIME` (Default)
#### (\*) Note about **CMAKE_BUILD_TYPE**
@ -98,4 +98,4 @@ Binary files are generated into the folder `src`:
- **pinetime-mcuboot-app-image** : MCUBoot image of the firmware
- **pinetime-mcuboot-app-dfu** : DFU file of the firmware
The same files are generated for **pinetime-recovery** and **pinetime-recovery-loader**
The same files are generated for **pinetime-recovery** and **pinetime-recoveryloader**

View File

@ -1,13 +1,7 @@
FROM ubuntu:22.04
ARG DEBIAN_FRONTEND=noninteractive
ARG NODE_MAJOR=20
RUN apt-get update -qq \
&& apt-get install -y ca-certificates curl gnupg \
&& mkdir -p /etc/apt/keyrings \
&& curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
&& echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \
&& apt-get update -qq \
&& apt-get install -y \
# x86_64 / generic packages
bash \
@ -15,14 +9,13 @@ RUN apt-get update -qq \
cmake \
git \
make \
nodejs \
python3 \
python3-pip \
python3-pil \
python-is-python3 \
tar \
unzip \
wget \
curl \
# aarch64 packages
libffi-dev \
libssl-dev \
@ -35,6 +28,8 @@ RUN apt-get update -qq \
libpango-1.0-0 \
ibpango1.0-dev \
libpangocairo-1.0-0 \
&& curl -sL https://deb.nodesource.com/setup_18.x | bash - \
&& apt-get install -y nodejs \
&& rm -rf /var/cache/apt/* /var/lib/apt/lists/*;
# Git needed for PROJECT_GIT_COMMIT_HASH variable setting
@ -44,6 +39,10 @@ RUN pip3 install -Iv cryptography==3.3
RUN pip3 install cbor
RUN npm i lv_font_conv@1.5.2 -g
RUN npm i ts-node@10.9.1 -g
RUN npm i @swc/core -g
RUN npm i lv_img_conv@0.3.0 -g
# build.sh knows how to compile
COPY build.sh /opt/

View File

@ -426,7 +426,6 @@ list(APPEND SOURCE_FILES
displayapp/screens/settings/SettingChimes.cpp
displayapp/screens/settings/SettingShakeThreshold.cpp
displayapp/screens/settings/SettingBluetooth.cpp
displayapp/screens/settings/SettingQuickR.cpp
## Watch faces
displayapp/screens/WatchFaceAnalog.cpp
@ -435,7 +434,6 @@ list(APPEND SOURCE_FILES
displayapp/screens/WatchFaceTerminal.cpp
displayapp/screens/WatchFacePineTimeStyle.cpp
displayapp/screens/WatchFaceCasioStyleG7710.cpp
displayapp/screens/WatchFaceFace.cpp
##

View File

@ -404,8 +404,7 @@ namespace Pinetime {
std::unique_ptr<WeatherData::Clouds>& WeatherService::GetCurrentClouds() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Clouds && currentTimestamp >= header->timestamp &&
IsEventStillValid(header, currentTimestamp)) {
if (header->eventType == WeatherData::eventtype::Clouds && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Clouds>&>(header);
}
}
@ -416,8 +415,7 @@ namespace Pinetime {
std::unique_ptr<WeatherData::Obscuration>& WeatherService::GetCurrentObscuration() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Obscuration && currentTimestamp >= header->timestamp &&
IsEventStillValid(header, currentTimestamp)) {
if (header->eventType == WeatherData::eventtype::Obscuration && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Obscuration>&>(header);
}
}
@ -428,8 +426,7 @@ namespace Pinetime {
std::unique_ptr<WeatherData::Precipitation>& WeatherService::GetCurrentPrecipitation() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Precipitation && currentTimestamp >= header->timestamp &&
IsEventStillValid(header, currentTimestamp)) {
if (header->eventType == WeatherData::eventtype::Precipitation && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Precipitation>&>(header);
}
}
@ -440,8 +437,7 @@ namespace Pinetime {
std::unique_ptr<WeatherData::Wind>& WeatherService::GetCurrentWind() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Wind && currentTimestamp >= header->timestamp &&
IsEventStillValid(header, currentTimestamp)) {
if (header->eventType == WeatherData::eventtype::Wind && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Wind>&>(header);
}
}
@ -452,8 +448,7 @@ namespace Pinetime {
std::unique_ptr<WeatherData::Temperature>& WeatherService::GetCurrentTemperature() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Temperature && currentTimestamp >= header->timestamp &&
IsEventStillValid(header, currentTimestamp)) {
if (header->eventType == WeatherData::eventtype::Temperature && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Temperature>&>(header);
}
}
@ -464,8 +459,7 @@ namespace Pinetime {
std::unique_ptr<WeatherData::Humidity>& WeatherService::GetCurrentHumidity() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Humidity && currentTimestamp >= header->timestamp &&
IsEventStillValid(header, currentTimestamp)) {
if (header->eventType == WeatherData::eventtype::Humidity && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Humidity>&>(header);
}
}
@ -476,8 +470,7 @@ namespace Pinetime {
std::unique_ptr<WeatherData::Pressure>& WeatherService::GetCurrentPressure() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Pressure && currentTimestamp >= header->timestamp &&
IsEventStillValid(header, currentTimestamp)) {
if (header->eventType == WeatherData::eventtype::Pressure && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Pressure>&>(header);
}
}
@ -488,8 +481,7 @@ namespace Pinetime {
std::unique_ptr<WeatherData::Location>& WeatherService::GetCurrentLocation() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Location && currentTimestamp >= header->timestamp &&
IsEventStillValid(header, currentTimestamp)) {
if (header->eventType == WeatherData::eventtype::Location && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Location>&>(header);
}
}
@ -500,8 +492,7 @@ namespace Pinetime {
std::unique_ptr<WeatherData::AirQuality>& WeatherService::GetCurrentQuality() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::AirQuality && currentTimestamp >= header->timestamp &&
IsEventStillValid(header, currentTimestamp)) {
if (header->eventType == WeatherData::eventtype::AirQuality && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::AirQuality>&>(header);
}
}

View File

@ -5,58 +5,14 @@
using namespace Pinetime::Controllers;
namespace {
TimerHandle_t vibTimer;
void PatternStep(TimerHandle_t xTimer) {
/* Vibration pattern format:
* {
* durationOfVibration,
* durationOfPause,
* durationOfVibration,
* durationOfPause,
* ...,
* durationOfVibration,
* zeroTerminator
* }
*
* Patterns can be any length
* The pattern must end with a duration of vibration and a terminator.
*/
static constexpr uint8_t vibrationPattern[] = {10, 100, 50, 200, 10, 0};
static size_t patternPosition = 0;
if (vibrationPattern[patternPosition] != 0 && xTimerChangePeriod(vibTimer, vibrationPattern[patternPosition] << 1, 0) == pdPASS &&
xTimerStart(vibTimer, 0) == pdPASS) {
if (patternPosition % 2 == 0) {
nrf_gpio_pin_clear(Pinetime::PinMap::Motor);
} else {
nrf_gpio_pin_set(Pinetime::PinMap::Motor);
}
patternPosition++;
} else {
patternPosition = 0;
nrf_gpio_pin_set(Pinetime::PinMap::Motor);
auto* motorController = static_cast<MotorController*>(pvTimerGetTimerID(xTimer));
motorController->PatternFinished();
}
}
}
void MotorController::Init() {
nrf_gpio_cfg_output(PinMap::Motor);
nrf_gpio_pin_set(PinMap::Motor);
vibTimer = xTimerCreate("vibration", 1, pdFALSE, this, PatternStep);
shortVib = xTimerCreate("shortVib", 1, pdFALSE, nullptr, StopMotor);
longVib = xTimerCreate("longVib", pdMS_TO_TICKS(1000), pdTRUE, this, Ring);
}
void MotorController::PatternFinished() {
patternPlaying = false;
}
void MotorController::Ring(TimerHandle_t xTimer) {
auto* motorController = static_cast<MotorController*>(pvTimerGetTimerID(xTimer));
motorController->RunForDuration(50);
@ -68,15 +24,6 @@ void MotorController::RunForDuration(uint8_t motorDuration) {
}
}
bool MotorController::StartPattern() {
if (!patternPlaying) {
patternPlaying = true;
PatternStep(vibTimer);
return true;
}
return false;
}
void MotorController::StartRinging() {
RunForDuration(50);
xTimerStart(longVib, 0);

View File

@ -15,13 +15,10 @@ namespace Pinetime {
void RunForDuration(uint8_t motorDuration);
void StartRinging();
void StopRinging();
void PatternFinished();
bool StartPattern();
private:
static void Ring(TimerHandle_t xTimer);
static void StopMotor(TimerHandle_t xTimer);
bool patternPlaying;
TimerHandle_t shortVib;
TimerHandle_t longVib;
};

View File

@ -13,7 +13,6 @@ namespace Pinetime {
enum class Notification : uint8_t { On, Off, Sleep };
enum class ChimesOption : uint8_t { None, Hours, HalfHours };
enum class WakeUpMode : uint8_t { SingleTap = 0, DoubleTap = 1, RaiseWrist = 2, Shake = 3, LowerWrist = 4 };
enum class QuickApp : uint8_t { MusicPlayer = 0, Calculator = 1, Alarm = 2, Timer = 3, HeartRate = 4 }; //, Alarm = 5, Timer = 6, Stopwatch = 7 };
enum class Colors : uint8_t {
White,
Silver,
@ -242,14 +241,6 @@ namespace Pinetime {
return getWakeUpModes()[static_cast<size_t>(mode)];
}
void SetBrightness(Controllers::BrightnessController::Levels level) {
if (level != settings.brightLevel) {
settingsChanged = true;
@ -280,26 +271,6 @@ namespace Pinetime {
return bleRadioEnabled;
};
// New Settings
void SetQuickRModes(QuickApp App_now, bool enabled) {
if (enabled != isQuickROn(App_now)) {
settingsChanged = true;
}
settings.quickApp.set(static_cast<size_t>(App_now), enabled);
};
std::bitset<5> getQuickRModes() const {
return settings.quickApp;
}
bool isQuickROn(const QuickApp app_holder) const {
return getQuickRModes()[static_cast<size_t>(app_holder)];
}
private:
Pinetime::Controllers::FS& fs;
@ -308,7 +279,6 @@ namespace Pinetime {
struct SettingsData {
uint32_t version = settingsVersion;
uint32_t stepsGoal = 10000;
uint32_t setquickr = 10000;
uint32_t screenTimeOut = 15000;
ClockType clockType = ClockType::H24;
@ -322,7 +292,6 @@ namespace Pinetime {
WatchFaceInfineat watchFaceInfineat;
std::bitset<5> wakeUpMode {0};
std::bitset<5> quickApp {0};
uint16_t shakeWakeThreshold = 150;
Controllers::BrightnessController::Levels brightLevel = Controllers::BrightnessController::Levels::Medium;

View File

@ -38,8 +38,7 @@ namespace Pinetime {
SettingShakeThreshold,
SettingBluetooth,
Error,
Calculator,
SettingQuickR
Calculator
};
}
}

View File

@ -51,7 +51,6 @@
#include "displayapp/screens/settings/SettingChimes.h"
#include "displayapp/screens/settings/SettingShakeThreshold.h"
#include "displayapp/screens/settings/SettingBluetooth.h"
#include "displayapp/screens/settings/SettingQuickR.h"
#include "libs/lv_conf.h"
@ -293,9 +292,9 @@ void DisplayApp::Refresh() {
return TouchEvents::SwipeLeft;
}
};
if (!currentScreen->OnTouchEvent(gesture)) {
if (currentApp == Apps::Clock || currentApp == Apps::QuickSettings || (currentApp == Apps::Music && quick_app[0]) || (currentApp == Apps::Calculator && quick_app[1]) || (currentApp == Apps::Alarm && quick_app[2]) || (currentApp == Apps::Timer && quick_app[3]) || (currentApp == Apps::HeartRate && quick_app[4])) {
if (currentApp == Apps::Clock || currentApp == Apps::Music || currentApp == Apps::Calculator || currentApp == Apps::QuickSettings) {
switch (gesture) {
case TouchEvents::SwipeUp:
if (currentApp == Apps::Clock) {
@ -312,10 +311,26 @@ void DisplayApp::Refresh() {
}
break;
case TouchEvents::SwipeRight:
gotoquickapp(-1);
if (currentApp == Apps::Clock) {
LoadNewScreen(Apps::QuickSettings, DisplayApp::FullRefreshDirections::RightAnim);
} else if (currentApp == Apps::QuickSettings) {
LoadNewScreen(Apps::Calculator, DisplayApp::FullRefreshDirections::RightAnim);
} else if (currentApp == Apps::Calculator) {
LoadNewScreen(Apps::Music, DisplayApp::FullRefreshDirections::RightAnim);
} else {
LoadNewScreen(Apps::Clock, DisplayApp::FullRefreshDirections::RightAnim);
}
break;
case TouchEvents::SwipeLeft:
gotoquickapp(1);
if (currentApp == Apps::Clock) {
LoadNewScreen(Apps::Music, DisplayApp::FullRefreshDirections::LeftAnim);
} else if (currentApp == Apps::Music) {
LoadNewScreen(Apps::Calculator, DisplayApp::FullRefreshDirections::LeftAnim);
} else if (currentApp == Apps::Calculator) {
LoadNewScreen(Apps::QuickSettings, DisplayApp::FullRefreshDirections::LeftAnim);
} else {
LoadNewScreen(Apps::Clock, DisplayApp::FullRefreshDirections::LeftAnim);
}
break;
case TouchEvents::DoubleTap:
PushMessageToSystemTask(System::Messages::GoToSleep);
@ -334,7 +349,6 @@ void DisplayApp::Refresh() {
if (!currentScreen->OnButtonPushed()) {
if (currentApp == Apps::Clock) {
PushMessageToSystemTask(System::Messages::GoToSleep);
} else {
LoadPreviousScreen();
}
@ -374,19 +388,8 @@ void DisplayApp::Refresh() {
// What should happen here?
break;
case Messages::Chime:
time_var = dateTimeController.Minutes();
if (time_var == 30){
//NRF_LOG_INFO("Short: %d", time_var);
motorController.StartPattern();
}
else
{
//NRF_LOG_INFO("Long: %d", time_var);
motorController.RunForDuration(200);
}
//LoadNewScreen(Apps::Clock, DisplayApp::FullRefreshDirections::None);
LoadNewScreen(Apps::Clock, DisplayApp::FullRefreshDirections::None);
motorController.RunForDuration(35);
break;
case Messages::OnChargingEvent:
RestoreBrightness();
@ -439,7 +442,6 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio
// break;
case Apps::None:
case Apps::Clock:
currentQ_app = 2;
currentScreen = std::make_unique<Screens::Clock>(dateTimeController,
batteryController,
bleController,
@ -581,9 +583,6 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio
case Apps::Calculator:
currentScreen = std::make_unique<Screens::Calculator>();
break;
case Apps::SettingQuickR:
currentScreen = std::make_unique<Screens::SettingQuickR>(settingsController);
break;
}
currentApp = app;
}
@ -643,84 +642,3 @@ void DisplayApp::ApplyBrightness() {
}
brightnessController.Set(brightness);
}
void DisplayApp::gotoquickapp(int app_step){
NRF_LOG_INFO("QuickRing Swiped");
currentQ_app += app_step;
quick_app = settingsController.getQuickRModes();
while(true)
{
if(currentQ_app > 7)
{
currentQ_app = 1;
} else if(currentQ_app < 1)
{
currentQ_app = 7;
}
if(currentQ_app != 1 && currentQ_app != 2)
{
if(quick_app[currentQ_app-3])
{
break;
}
else
{
currentQ_app += (app_step);
}
}
else
{
break;
}
}
int quickringtotal = 2;
for(int i = 0; i < 5; i++){
quickringtotal += (quick_app[i] ? 1 : 0 );
}
NRF_LOG_INFO("case number current Q: %i", currentQ_app);
Apps app = Apps::Clock;
switch (currentQ_app) {
case 1:
app = Apps::QuickSettings;
break;
case 2:
app = Apps::Clock;
break;
case 3:
app = Apps::Music;
break;
case 4:
app = Apps::Calculator;
break;
case 5:
app = Apps::Alarm;
break;
case 6:
app = Apps::Timer;
break;
case 7:
app = Apps::HeartRate;
break;
default:
break;
}
if(app_step < 0){
LoadNewScreen(app, DisplayApp::FullRefreshDirections::RightAnim);
}
else
{
LoadNewScreen(app, DisplayApp::FullRefreshDirections::LeftAnim);
}
//appStackDirections.Pop();
//returnAppStack.Pop();
}

View File

@ -118,8 +118,6 @@ namespace Pinetime {
void LoadNewScreen(Apps app, DisplayApp::FullRefreshDirections direction);
void LoadScreen(Apps app, DisplayApp::FullRefreshDirections direction);
void PushMessageToSystemTask(Pinetime::System::Messages message);
void gotoquickapp(int app_step);
Apps nextApp = Apps::None;
DisplayApp::FullRefreshDirections nextDirection;
@ -130,10 +128,7 @@ namespace Pinetime {
Utility::StaticStack<Apps, returnAppStackSize> returnAppStack;
Utility::StaticStack<FullRefreshDirections, returnAppStackSize> appStackDirections;
int time_var;
bool isDimmed = false;
int currentQ_app = 2;
std::bitset<5> quick_app;
};
}
}

View File

@ -9,7 +9,6 @@ namespace Pinetime {
Terminal = 3,
Infineat = 4,
CasioStyleG7710 = 5,
FaceFace = 6,
};
}
}

View File

@ -18,7 +18,7 @@
"sources": [
{
"file": "JetBrainsMono-Regular.ttf",
"range": "0x25, 0x2b, 0x2d, 0x30-0x3a, 0x4b-0x4d, 0x66, 0x69, 0x6b, 0x6d, 0x74"
"range": "0x25, 0x2b, 0x2d, 0x30-0x3a"
}
],
"bpp": 1,

View File

@ -13,7 +13,6 @@
#include "displayapp/screens/WatchFaceAnalog.h"
#include "displayapp/screens/WatchFacePineTimeStyle.h"
#include "displayapp/screens/WatchFaceCasioStyleG7710.h"
#include "displayapp/screens/WatchFaceFace.h"
using namespace Pinetime::Applications::Screens;
using namespace Pinetime::Applications;
@ -56,9 +55,6 @@ Clock::Clock(Controllers::DateTime& dateTimeController,
case WatchFace::CasioStyleG7710:
return WatchFaceCasioStyleG7710();
break;
case WatchFace::FaceFace:
return WatchFaceFaceScreen();
break;
}
return WatchFaceDigitalScreen();
}()} {
@ -131,15 +127,7 @@ std::unique_ptr<Screen> Clock::WatchFaceCasioStyleG7710() {
bleController,
notificationManager,
settingsController,
heartRateController,
motionController,
filesystem);
}
std::unique_ptr<Screen> Clock::WatchFaceFaceScreen() {
return std::make_unique<Screens::WatchFaceFace>(dateTimeController,
batteryController,
bleController,
notificationManager,
settingsController);
}

View File

@ -54,7 +54,6 @@ namespace Pinetime {
std::unique_ptr<Screen> WatchFaceTerminalScreen();
std::unique_ptr<Screen> WatchFaceInfineatScreen();
std::unique_ptr<Screen> WatchFaceCasioStyleG7710();
std::unique_ptr<Screen> WatchFaceFaceScreen();
};
}
}

View File

@ -2,7 +2,6 @@
#include "displayapp/DisplayApp.h"
#include "displayapp/screens/Symbols.h"
#include "displayapp/InfiniTimeTheme.h"
#include <libraries/log/nrf_log.h>
using namespace Pinetime::Applications::Screens;
@ -59,17 +58,8 @@ FlashLight::~FlashLight() {
}
void FlashLight::SetColors() {
lv_color_t bgColor = LV_COLOR_BLACK;
if(State_l == 0){
bgColor = LV_COLOR_BLACK;
} else if (State_l == 1){
bgColor = LV_COLOR_WHITE;
}
else {
bgColor = lv_color_hex(0xff0000);
}
lv_color_t fgColor = State_l ? Colors::lightGray : LV_COLOR_WHITE;
lv_color_t bgColor = isOn ? LV_COLOR_WHITE : LV_COLOR_BLACK;
lv_color_t fgColor = isOn ? Colors::lightGray : LV_COLOR_WHITE;
lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, bgColor);
lv_obj_set_style_local_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, fgColor);
@ -96,11 +86,9 @@ void FlashLight::SetIndicators() {
}
void FlashLight::Toggle() {
State_l++;
if(State_l > 2)
State_l = 0;
isOn = !isOn;
SetColors();
if (State_l) {
if (isOn) {
brightnessController.Set(brightnessLevel);
} else {
brightnessController.Set(Controllers::BrightnessController::Levels::Low);
@ -111,7 +99,7 @@ bool FlashLight::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
using namespace Pinetime::Controllers;
auto SetState = [this]() {
if (State_l) {
if (isOn) {
brightnessController.Set(brightnessLevel);
}
SetIndicators();
@ -139,4 +127,4 @@ bool FlashLight::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
}
return false;
}
}

View File

@ -26,13 +26,12 @@ namespace Pinetime {
Pinetime::System::SystemTask& systemTask;
Controllers::BrightnessController& brightnessController;
Controllers::BrightnessController::Levels brightnessLevel = Controllers::BrightnessController::Levels::High;
lv_obj_t* flashLight;
lv_obj_t* backgroundAction;
lv_obj_t* indicators[3];
int State_l = 0;
bool isOn = false;
};
}
}

View File

@ -203,21 +203,19 @@ Navigation::Navigation(Pinetime::Controllers::NavigationService& nav) : navServi
lv_obj_align(imgFlag, nullptr, LV_ALIGN_CENTER, 0, -60);
txtNarrative = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_long_mode(txtNarrative, LV_LABEL_LONG_DOT);
lv_label_set_long_mode(txtNarrative, LV_LABEL_LONG_BREAK);
lv_obj_set_width(txtNarrative, LV_HOR_RES);
lv_obj_set_height(txtNarrative, 80);
lv_label_set_text_static(txtNarrative, "Navigation");
lv_label_set_align(txtNarrative, LV_LABEL_ALIGN_CENTER);
lv_obj_align(txtNarrative, nullptr, LV_ALIGN_CENTER, 0, 30);
lv_obj_align(txtNarrative, nullptr, LV_ALIGN_CENTER, 0, 10);
txtManDist = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_long_mode(txtManDist, LV_LABEL_LONG_BREAK);
lv_obj_set_style_local_text_color(txtManDist, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN);
lv_obj_set_style_local_text_font(txtManDist, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42);
lv_obj_set_width(txtManDist, LV_HOR_RES);
lv_label_set_text_static(txtManDist, "--M");
lv_label_set_align(txtManDist, LV_LABEL_ALIGN_CENTER);
lv_obj_align(txtManDist, nullptr, LV_ALIGN_CENTER, 0, 90);
lv_obj_align(txtManDist, nullptr, LV_ALIGN_CENTER, 0, 60);
// Route Progress
barProgress = lv_bar_create(lv_scr_act(), nullptr);

View File

@ -9,6 +9,7 @@
#include "components/battery/BatteryController.h"
#include "components/ble/BleController.h"
#include "components/ble/NotificationManager.h"
#include "components/heartrate/HeartRateController.h"
#include "components/motion/MotionController.h"
#include "components/settings/Settings.h"
using namespace Pinetime::Applications::Screens;
@ -18,6 +19,7 @@ WatchFaceCasioStyleG7710::WatchFaceCasioStyleG7710(Controllers::DateTime& dateTi
const Controllers::Ble& bleController,
Controllers::NotificationManager& notificatioManager,
Controllers::Settings& settingsController,
Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController,
Controllers::FS& filesystem)
: currentDateTime {{}},
@ -27,6 +29,7 @@ WatchFaceCasioStyleG7710::WatchFaceCasioStyleG7710(Controllers::DateTime& dateTi
bleController {bleController},
notificatioManager {notificatioManager},
settingsController {settingsController},
heartRateController {heartRateController},
motionController {motionController} {
lfs_file f = {};
@ -145,20 +148,25 @@ WatchFaceCasioStyleG7710::WatchFaceCasioStyleG7710(Controllers::DateTime& dateTi
lv_obj_set_pos(backgroundLabel, 0, 0);
lv_label_set_text_static(backgroundLabel, "");
stepIcon = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(stepIcon, Symbols::shoe);
lv_obj_set_style_local_text_color(stepIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
lv_obj_align(stepIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 5, -2);
heartbeatIcon = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(heartbeatIcon, Symbols::heartBeat);
lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
lv_obj_align(heartbeatIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 5, -2);
heartbeatValue = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(heartbeatValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
lv_label_set_text_static(heartbeatValue, "");
lv_obj_align(heartbeatValue, heartbeatIcon, LV_ALIGN_OUT_RIGHT_MID, 5, 0);
stepValue = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(stepValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
lv_label_set_text_static(stepValue, "");
lv_obj_align(stepValue, stepIcon, LV_ALIGN_OUT_RIGHT_MID, 5, 0);
lv_label_set_text_static(stepValue, "0");
lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -5, -2);
label_seconds = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(label_seconds, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
lv_label_set_text_static(label_seconds, "0");
lv_obj_align(label_seconds, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -9, -2);
stepIcon = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(stepIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
lv_label_set_text_static(stepIcon, Symbols::shoe);
lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0);
taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this);
Refresh();
@ -214,11 +222,10 @@ void WatchFaceCasioStyleG7710::Refresh() {
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(notificationState.Get()));
}
currentDateTime = std::chrono::time_point_cast<std::chrono::seconds>(dateTimeController.CurrentDateTime());
currentDateTime = std::chrono::time_point_cast<std::chrono::minutes>(dateTimeController.CurrentDateTime());
if (currentDateTime.IsUpdated()) {
uint8_t hour = dateTimeController.Hours();
uint8_t minute = dateTimeController.Minutes();
uint8_t second = dateTimeController.Seconds();
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
char ampmChar[2] = "A";
@ -237,9 +244,6 @@ void WatchFaceCasioStyleG7710::Refresh() {
}
lv_obj_realign(label_time);
lv_label_set_text_fmt(label_seconds, "%02d", second);
lv_obj_realign(label_seconds);
currentDate = std::chrono::time_point_cast<days>(currentDateTime.Get());
if (currentDate.IsUpdated()) {
const char* weekNumberFormat = "%V";
@ -285,11 +289,26 @@ void WatchFaceCasioStyleG7710::Refresh() {
}
}
heartbeat = heartRateController.HeartRate();
heartbeatRunning = heartRateController.State() != Controllers::HeartRateController::States::Stopped;
if (heartbeat.IsUpdated() || heartbeatRunning.IsUpdated()) {
if (heartbeatRunning.Get()) {
lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
lv_label_set_text_fmt(heartbeatValue, "%d", heartbeat.Get());
} else {
lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x1B1B1B));
lv_label_set_text_static(heartbeatValue, "");
}
lv_obj_realign(heartbeatIcon);
lv_obj_realign(heartbeatValue);
}
stepCount = motionController.NbSteps();
if (stepCount.IsUpdated()) {
lv_label_set_text_fmt(stepValue, "%lu", stepCount.Get());
lv_obj_realign(stepValue);
lv_obj_realign(stepValue);
lv_obj_realign(stepIcon);
}
}

View File

@ -16,6 +16,7 @@ namespace Pinetime {
class Battery;
class Ble;
class NotificationManager;
class HeartRateController;
class MotionController;
}
@ -29,6 +30,7 @@ namespace Pinetime {
const Controllers::Ble& bleController,
Controllers::NotificationManager& notificatioManager,
Controllers::Settings& settingsController,
Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController,
Controllers::FS& filesystem);
~WatchFaceCasioStyleG7710() override;
@ -42,8 +44,10 @@ namespace Pinetime {
Utility::DirtyValue<bool> powerPresent {};
Utility::DirtyValue<bool> bleState {};
Utility::DirtyValue<bool> bleRadioEnabled {};
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>> currentDateTime {};
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::minutes>> currentDateTime {};
Utility::DirtyValue<uint32_t> stepCount {};
Utility::DirtyValue<uint8_t> heartbeat {};
Utility::DirtyValue<bool> heartbeatRunning {};
Utility::DirtyValue<bool> notificationState {};
using days = std::chrono::duration<int32_t, std::ratio<86400>>; // TODO: days is standard in c++20
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, days>> currentDate;
@ -60,7 +64,6 @@ namespace Pinetime {
lv_style_t style_border;
lv_obj_t* label_time;
lv_obj_t* label_seconds;
lv_obj_t* line_time;
lv_obj_t* label_time_ampm;
lv_obj_t* label_date;
@ -74,6 +77,8 @@ namespace Pinetime {
lv_obj_t* bleIcon;
lv_obj_t* batteryPlug;
lv_obj_t* label_battery_value;
lv_obj_t* heartbeatIcon;
lv_obj_t* heartbeatValue;
lv_obj_t* stepIcon;
lv_obj_t* stepValue;
lv_obj_t* notificationIcon;
@ -86,6 +91,7 @@ namespace Pinetime {
const Controllers::Ble& bleController;
Controllers::NotificationManager& notificatioManager;
Controllers::Settings& settingsController;
Controllers::HeartRateController& heartRateController;
Controllers::MotionController& motionController;
lv_task_t* taskRefresh;

View File

@ -34,11 +34,7 @@ WatchFaceDigital::WatchFaceDigital(Controllers::DateTime& dateTimeController,
lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_LIME);
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(false));
lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
label_seconds = lv_label_create(lv_scr_act(), nullptr); // for secs
lv_obj_align(label_seconds, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, 0, -55);
lv_obj_set_style_local_text_color(label_seconds, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x999999));
label_date = lv_label_create(lv_scr_act(), nullptr);
lv_obj_align(label_date, lv_scr_act(), LV_ALIGN_CENTER, 0, 60);
lv_obj_set_style_local_text_color(label_date, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x999999));
@ -48,6 +44,10 @@ WatchFaceDigital::WatchFaceDigital(Controllers::DateTime& dateTimeController,
lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, 0, 0);
label_time_ampm = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(label_time_ampm, "");
lv_obj_align(label_time_ampm, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, -30, -55);
heartbeatIcon = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(heartbeatIcon, Symbols::heartBeat);
lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xCE1B1B));
@ -85,27 +85,29 @@ void WatchFaceDigital::Refresh() {
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(notificationState.Get()));
}
currentDateTime = std::chrono::time_point_cast<std::chrono::seconds>(dateTimeController.CurrentDateTime());
currentDateTime = std::chrono::time_point_cast<std::chrono::minutes>(dateTimeController.CurrentDateTime());
if (currentDateTime.IsUpdated()) {
uint8_t hour = dateTimeController.Hours();
uint8_t minute = dateTimeController.Minutes();
uint8_t second = dateTimeController.Seconds();
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
char ampmChar[3] = "AM";
if (hour == 0) {
hour = 12;
} else if (hour == 12) {
ampmChar[0] = 'P';
} else if (hour > 12) {
hour = hour - 12;
ampmChar[0] = 'P';
}
lv_label_set_text(label_time_ampm, ampmChar);
lv_label_set_text_fmt(label_time, "%2d:%02d", hour, minute);
lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, 0, 0);
} else {
lv_label_set_text_fmt(label_time, "%02d:%02d", hour, minute);
lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
}
lv_label_set_text_fmt(label_seconds, "%02d", second); // for secs
currentDate = std::chrono::time_point_cast<days>(currentDateTime.Get());
if (currentDate.IsUpdated()) {

View File

@ -44,7 +44,7 @@ namespace Pinetime {
Utility::DirtyValue<bool> powerPresent {};
Utility::DirtyValue<bool> bleState {};
Utility::DirtyValue<bool> bleRadioEnabled {};
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>> currentDateTime {};
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::minutes>> currentDateTime {};
Utility::DirtyValue<uint32_t> stepCount {};
Utility::DirtyValue<uint8_t> heartbeat {};
Utility::DirtyValue<bool> heartbeatRunning {};
@ -53,8 +53,7 @@ namespace Pinetime {
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, days>> currentDate;
lv_obj_t* label_time;
lv_obj_t* label_seconds; //for secs
lv_obj_t* batteryValue;
lv_obj_t* label_time_ampm;
lv_obj_t* label_date;
lv_obj_t* heartbeatIcon;
lv_obj_t* heartbeatValue;

View File

@ -1,274 +0,0 @@
#include "displayapp/screens/WatchFaceFace.h"
#include <cmath>
#include <lvgl/lvgl.h>
#include "displayapp/screens/BatteryIcon.h"
#include "displayapp/screens/BleIcon.h"
#include "displayapp/screens/Symbols.h"
#include "displayapp/screens/NotificationIcon.h"
#include "components/settings/Settings.h"
#include "displayapp/InfiniTimeTheme.h"
using namespace Pinetime::Applications::Screens;
namespace {
constexpr int16_t HourLength = 100;
constexpr int16_t MinuteLength = 90;
constexpr int16_t SecondLength = 110;
// sin(90) = 1 so the value of _lv_trigo_sin(90) is the scaling factor
const auto LV_TRIG_SCALE = _lv_trigo_sin(90);
int16_t Cosine(int16_t angle) {
return _lv_trigo_sin(angle + 90);
}
int16_t Sine(int16_t angle) {
return _lv_trigo_sin(angle);
}
int16_t CoordinateXRelocate(int16_t x) {
return (x + LV_HOR_RES / 2);
}
int16_t CoordinateYRelocate(int16_t y) {
return std::abs(y - LV_HOR_RES / 2);
}
lv_point_t CoordinateRelocate(int16_t radius, int16_t angle) {
return lv_point_t {.x = CoordinateXRelocate(radius * static_cast<int32_t>(Sine(angle)) / LV_TRIG_SCALE),
.y = CoordinateYRelocate(radius * static_cast<int32_t>(Cosine(angle)) / LV_TRIG_SCALE)};
}
}
WatchFaceFace::WatchFaceFace(Controllers::DateTime& dateTimeController,
const Controllers::Battery& batteryController,
const Controllers::Ble& bleController,
Controllers::NotificationManager& notificationManager,
Controllers::Settings& settingsController)
: currentDateTime {{}},
batteryIcon(true),
dateTimeController {dateTimeController},
batteryController {batteryController},
bleController {bleController},
notificationManager {notificationManager},
settingsController {settingsController} {
sHour = 99;
sMinute = 99;
sSecond = 99;
mouth = lv_arc_create(lv_scr_act(), nullptr);
lv_arc_set_angles(mouth, 30, 110);
lv_arc_set_bg_angles(mouth, 45, 135);
lv_obj_set_size(mouth, 130, 130);
lv_obj_align(mouth, nullptr, LV_ALIGN_CENTER, 0, 0);
leye = lv_arc_create(lv_scr_act(), nullptr);
lv_arc_set_angles(leye, 0, 360);
lv_obj_set_size(leye, 30, 30);
lv_obj_align(leye, nullptr, LV_ALIGN_CENTER, -35, -35);
reye = lv_arc_create(lv_scr_act(), nullptr);
lv_arc_set_angles(reye, 0, 360);
lv_obj_set_size(reye, 30, 30);
lv_obj_align(reye, nullptr, LV_ALIGN_CENTER, 35, -35);
minor_scales = lv_linemeter_create(lv_scr_act(), nullptr);
lv_linemeter_set_scale(minor_scales, 300, 51);
lv_linemeter_set_angle_offset(minor_scales, 180);
lv_obj_set_size(minor_scales, 240, 240);
lv_obj_align(minor_scales, nullptr, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_local_bg_opa(minor_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
lv_obj_set_style_local_scale_width(minor_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 4);
lv_obj_set_style_local_scale_end_line_width(minor_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 1);
lv_obj_set_style_local_scale_end_color(minor_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
major_scales = lv_linemeter_create(lv_scr_act(), nullptr);
lv_linemeter_set_scale(major_scales, 300, 11);
lv_linemeter_set_angle_offset(major_scales, 180);
lv_obj_set_size(major_scales, 240, 240);
lv_obj_align(major_scales, nullptr, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_local_bg_opa(major_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
lv_obj_set_style_local_scale_width(major_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 6);
lv_obj_set_style_local_scale_end_line_width(major_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 4);
lv_obj_set_style_local_scale_end_color(major_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
large_scales = lv_linemeter_create(lv_scr_act(), nullptr);
lv_linemeter_set_scale(large_scales, 180, 3);
lv_linemeter_set_angle_offset(large_scales, 180);
lv_obj_set_size(large_scales, 240, 240);
lv_obj_align(large_scales, nullptr, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_local_bg_opa(large_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
lv_obj_set_style_local_scale_width(large_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 20);
lv_obj_set_style_local_scale_end_line_width(large_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 4);
lv_obj_set_style_local_scale_end_color(large_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_AQUA);
twelve = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_align(twelve, LV_LABEL_ALIGN_CENTER);
lv_label_set_text_static(twelve, "12");
lv_obj_set_pos(twelve, 110, 10);
lv_obj_set_style_local_text_color(twelve, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_AQUA);
batteryIcon.Create(lv_scr_act());
lv_obj_align(batteryIcon.GetObject(), nullptr, LV_ALIGN_IN_TOP_RIGHT, 0, 0);
plugIcon = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(plugIcon, Symbols::plug);
lv_obj_align(plugIcon, nullptr, LV_ALIGN_IN_TOP_RIGHT, 0, 0);
bleIcon = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(bleIcon, "");
lv_obj_align(bleIcon, nullptr, LV_ALIGN_IN_TOP_RIGHT, -30, 0);
notificationIcon = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_LIME);
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(false));
lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
// Date - Day / Week day
label_date_day = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(label_date_day, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::orange);
lv_label_set_text_fmt(label_date_day, "%s\n%02i", dateTimeController.DayOfWeekShortToString(), dateTimeController.Day());
lv_label_set_align(label_date_day, LV_LABEL_ALIGN_CENTER);
lv_obj_align(label_date_day, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
minute_body = lv_line_create(lv_scr_act(), nullptr);
minute_body_trace = lv_line_create(lv_scr_act(), nullptr);
hour_body = lv_line_create(lv_scr_act(), nullptr);
hour_body_trace = lv_line_create(lv_scr_act(), nullptr);
second_body = lv_line_create(lv_scr_act(), nullptr);
lv_style_init(&second_line_style);
lv_style_set_line_width(&second_line_style, LV_STATE_DEFAULT, 3);
lv_style_set_line_color(&second_line_style, LV_STATE_DEFAULT, LV_COLOR_RED);
lv_style_set_line_rounded(&second_line_style, LV_STATE_DEFAULT, true);
lv_obj_add_style(second_body, LV_LINE_PART_MAIN, &second_line_style);
lv_style_init(&minute_line_style);
lv_style_set_line_width(&minute_line_style, LV_STATE_DEFAULT, 3);
lv_style_set_line_color(&minute_line_style, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_style_set_line_rounded(&minute_line_style, LV_STATE_DEFAULT, true);
lv_obj_add_style(minute_body, LV_LINE_PART_MAIN, &minute_line_style);
lv_style_init(&minute_line_style_trace);
lv_style_set_line_width(&minute_line_style_trace, LV_STATE_DEFAULT, 3);
lv_style_set_line_color(&minute_line_style_trace, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_style_set_line_rounded(&minute_line_style_trace, LV_STATE_DEFAULT, false);
lv_obj_add_style(minute_body_trace, LV_LINE_PART_MAIN, &minute_line_style_trace);
lv_style_init(&hour_line_style);
lv_style_set_line_width(&hour_line_style, LV_STATE_DEFAULT, 7);
lv_style_set_line_color(&hour_line_style, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_style_set_line_rounded(&hour_line_style, LV_STATE_DEFAULT, true);
lv_obj_add_style(hour_body, LV_LINE_PART_MAIN, &hour_line_style);
lv_style_init(&hour_line_style_trace);
lv_style_set_line_width(&hour_line_style_trace, LV_STATE_DEFAULT, 3);
lv_style_set_line_color(&hour_line_style_trace, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_style_set_line_rounded(&hour_line_style_trace, LV_STATE_DEFAULT, false);
lv_obj_add_style(hour_body_trace, LV_LINE_PART_MAIN, &hour_line_style_trace);
taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this);
Refresh();
}
WatchFaceFace::~WatchFaceFace() {
lv_task_del(taskRefresh);
lv_style_reset(&hour_line_style);
lv_style_reset(&hour_line_style_trace);
lv_style_reset(&minute_line_style);
lv_style_reset(&minute_line_style_trace);
lv_style_reset(&second_line_style);
lv_obj_clean(lv_scr_act());
}
void WatchFaceFace::UpdateClock() {
uint8_t hour = dateTimeController.Hours();
uint8_t minute = dateTimeController.Minutes();
uint8_t second = dateTimeController.Seconds();
if (sMinute != minute) {
auto const angle = minute * 6;
minute_point[0] = CoordinateRelocate(10, angle - 90);
minute_point[1] = CoordinateRelocate(MinuteLength, angle);
minute_point[2] = CoordinateRelocate(10, angle + 90);
lv_line_set_points(minute_body, minute_point, 3);
}
if (sHour != hour || sMinute != minute) {
sHour = hour;
sMinute = minute;
auto const angle = (hour * 30 + minute / 2);
hour_point[0] = CoordinateRelocate(90, angle);
hour_point[1] = CoordinateRelocate(HourLength, angle);
lv_line_set_points(hour_body, hour_point, 2);
}
if (sSecond != second) {
sSecond = second;
auto const angle = second * 6;
second_point[0] = CoordinateRelocate(105, angle);
second_point[1] = CoordinateRelocate(SecondLength, angle);
lv_line_set_points(second_body, second_point, 2);
}//TODO: redo seconds
}
void WatchFaceFace::SetBatteryIcon() {
auto batteryPercent = batteryPercentRemaining.Get();
batteryIcon.SetBatteryPercentage(batteryPercent);
}
void WatchFaceFace::Refresh() {
isCharging = batteryController.IsCharging();
if (isCharging.IsUpdated()) {
if (isCharging.Get()) {
lv_obj_set_hidden(batteryIcon.GetObject(), true);
lv_obj_set_hidden(plugIcon, false);
} else {
lv_obj_set_hidden(batteryIcon.GetObject(), false);
lv_obj_set_hidden(plugIcon, true);
SetBatteryIcon();
}
}
if (!isCharging.Get()) {
batteryPercentRemaining = batteryController.PercentRemaining();
if (batteryPercentRemaining.IsUpdated()) {
SetBatteryIcon();
}
}
bleState = bleController.IsConnected();
if (bleState.IsUpdated()) {
if (bleState.Get()) {
lv_label_set_text_static(bleIcon, Symbols::bluetooth);
} else {
lv_label_set_text_static(bleIcon, "");
}
}
notificationState = notificationManager.AreNewNotificationsAvailable();
if (notificationState.IsUpdated()) {
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(notificationState.Get()));
}
currentDateTime = dateTimeController.CurrentDateTime();
if (currentDateTime.IsUpdated()) {
UpdateClock();
currentDate = std::chrono::time_point_cast<days>(currentDateTime.Get());
if (currentDate.IsUpdated()) {
lv_label_set_text_fmt(label_date_day, "%s\n%02i", dateTimeController.DayOfWeekShortToString(), dateTimeController.Day());
}
}
}

View File

@ -1,95 +0,0 @@
#pragma once
#include <lvgl/src/lv_core/lv_obj.h>
#include <chrono>
#include <cstdint>
#include <memory>
#include "displayapp/screens/Screen.h"
#include "components/datetime/DateTimeController.h"
#include "components/battery/BatteryController.h"
#include "components/ble/BleController.h"
#include "components/ble/NotificationManager.h"
#include "displayapp/screens/BatteryIcon.h"
#include "utility/DirtyValue.h"
namespace Pinetime {
namespace Controllers {
class Settings;
class Battery;
class Ble;
class NotificationManager;
}
namespace Applications {
namespace Screens {
class WatchFaceFace : public Screen {
public:
WatchFaceFace(Controllers::DateTime& dateTimeController,
const Controllers::Battery& batteryController,
const Controllers::Ble& bleController,
Controllers::NotificationManager& notificationManager,
Controllers::Settings& settingsController);
~WatchFaceFace() override;
void Refresh() override;
private:
uint8_t sHour, sMinute, sSecond;
Utility::DirtyValue<uint8_t> batteryPercentRemaining {0};
Utility::DirtyValue<bool> isCharging {};
Utility::DirtyValue<bool> bleState {};
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime;
Utility::DirtyValue<bool> notificationState {false};
using days = std::chrono::duration<int32_t, std::ratio<86400>>; // TODO: days is standard in c++20
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, days>> currentDate;
lv_obj_t* minor_scales;
lv_obj_t* major_scales;
lv_obj_t* large_scales;
lv_obj_t* twelve;
lv_obj_t* hour_body;
lv_obj_t* hour_body_trace;
lv_obj_t* minute_body;
lv_obj_t* minute_body_trace;
lv_obj_t* second_body;
lv_point_t hour_point[2];
lv_point_t minute_point[3];
lv_point_t second_point[2];
lv_style_t hour_line_style;
lv_style_t hour_line_style_trace;
lv_style_t minute_line_style;
lv_style_t minute_line_style_trace;
lv_style_t second_line_style;
lv_obj_t* label_date_day;
lv_obj_t* plugIcon;
lv_obj_t* notificationIcon;
lv_obj_t* bleIcon;
lv_obj_t* mouth;
lv_obj_t* leye;
lv_obj_t* reye;
BatteryIcon batteryIcon;
const Controllers::DateTime& dateTimeController;
const Controllers::Battery& batteryController;
const Controllers::Ble& bleController;
Controllers::NotificationManager& notificationManager;
Controllers::Settings& settingsController;
void UpdateClock();
void SetBatteryIcon();
lv_task_t* taskRefresh;
};
}
}
}

View File

@ -1,77 +0,0 @@
#include "displayapp/screens/settings/SettingQuickR.h"
#include <lvgl/lvgl.h>
#include "displayapp/DisplayApp.h"
#include "displayapp/screens/Symbols.h"
#include "displayapp/InfiniTimeTheme.h"
using namespace Pinetime::Applications::Screens;
constexpr std::array<SettingQuickR::Option, 5> SettingQuickR::options;
namespace {
void event_handler(lv_obj_t* obj, lv_event_t event) {
auto* screen = static_cast<SettingQuickR*>(obj->user_data);
if (event == LV_EVENT_VALUE_CHANGED) {
screen->UpdateSelected(obj);
}
}
}
SettingQuickR::SettingQuickR(Pinetime::Controllers::Settings& settingsController) : settingsController {settingsController} {
lv_obj_t* container1 = lv_cont_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_bg_opa(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
lv_obj_set_style_local_pad_all(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 10);
lv_obj_set_style_local_pad_inner(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 5);
lv_obj_set_style_local_border_width(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_pos(container1, 10, 35);
lv_obj_set_width(container1, LV_HOR_RES - 20);
lv_obj_set_height(container1, LV_VER_RES - 20);
lv_cont_set_layout(container1, LV_LAYOUT_COLUMN_LEFT);
lv_obj_t* title = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(title, "Quick Ring");
lv_label_set_align(title, LV_LABEL_ALIGN_CENTER);
lv_obj_align(title, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 15, 15);
lv_obj_t* icon = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(icon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_ORANGE);
lv_label_set_text_static(icon, Symbols::check);
lv_label_set_align(icon, LV_LABEL_ALIGN_CENTER);
lv_obj_align(icon, title, LV_ALIGN_OUT_LEFT_MID, -10, 0);
for (unsigned int i = 0; i < 5; i++) {
cbOption[i] = lv_checkbox_create(container1, nullptr);
lv_checkbox_set_text(cbOption[i], options[i].name);
if (settingsController.isQuickROn(static_cast<Controllers::Settings::QuickApp>(i))) {
lv_checkbox_set_checked(cbOption[i], true);
}
cbOption[i]->user_data = this;
lv_obj_set_event_cb(cbOption[i], event_handler);
}
}
SettingQuickR::~SettingQuickR() {
lv_obj_clean(lv_scr_act());
settingsController.SaveSettings();
}
void SettingQuickR::UpdateSelected(lv_obj_t* object) {
// Find the index of the checkbox that triggered the event
for (size_t i = 0; i < 5; i++) {
if (cbOption[i] == object) {
bool currentState = settingsController.isQuickROn(options[i].quickApp);
settingsController.SetQuickRModes(options[i].quickApp, !currentState);
break;
}
}
// Update checkbox according to current wakeup modes.
// This is needed because we can have extra logic when setting or unsetting wakeup modes,
// for example, when setting SingleTap, DoubleTap is unset and vice versa.
auto modes = settingsController.getQuickRModes();
for (size_t i = 0; i < 5; ++i) {
lv_checkbox_set_checked(cbOption[i], modes[i]);
}
}

View File

@ -1,39 +0,0 @@
#pragma once
#include <array>
#include <cstdint>
#include <lvgl/lvgl.h>
#include "components/settings/Settings.h"
#include "displayapp/screens/Screen.h"
namespace Pinetime {
namespace Applications {
namespace Screens {
class SettingQuickR : public Screen {
public:
SettingQuickR(Pinetime::Controllers::Settings& settingsController);
~SettingQuickR() override;
void UpdateSelected(lv_obj_t* object);
private:
struct Option {
Controllers::Settings::QuickApp quickApp;
const char* name;
};
static constexpr std::array<Option, 5> options = {{
{Controllers::Settings::QuickApp::MusicPlayer, "Music Player"},
{Controllers::Settings::QuickApp::Calculator, "Calculator"},
{Controllers::Settings::QuickApp::Alarm, "Alarms"},
{Controllers::Settings::QuickApp::Timer, "Timer"},
{Controllers::Settings::QuickApp::HeartRate, "Heart Rate"},
}};
lv_obj_t* cbOption[5];
Controllers::Settings& settingsController;
};
}
}
}

View File

@ -11,7 +11,6 @@
#include "displayapp/screens/CheckboxList.h"
#include "displayapp/screens/WatchFaceInfineat.h"
#include "displayapp/screens/WatchFaceCasioStyleG7710.h"
#include "displayapp/screens/WatchFaceFace.h"
namespace Pinetime {
@ -48,9 +47,8 @@ namespace Pinetime {
{"Terminal", true},
{"Infineat face", Applications::Screens::WatchFaceInfineat::IsAvailable(filesystem)},
{"Casio G7710", Applications::Screens::WatchFaceCasioStyleG7710::IsAvailable(filesystem)},
{"Face face", true},
{"", false}
}};
{"", false},
{"", false}}};
ScreenList<nScreens> screens;
};
}

View File

@ -29,7 +29,7 @@ namespace Pinetime {
static constexpr int entriesPerScreen = 4;
// Increment this when more space is needed
static constexpr int nScreens = 4;
static constexpr int nScreens = 3;
static constexpr std::array<List::Applications, entriesPerScreen * nScreens> entries {{
{Symbols::sun, "Display", Apps::SettingDisplay},
@ -40,14 +40,11 @@ namespace Pinetime {
{Symbols::shoe, "Steps", Apps::SettingSteps},
{Symbols::clock, "Date&Time", Apps::SettingSetDateTime},
{Symbols::batteryHalf, "Battery", Apps::BatteryInfo},
{Symbols::check, "QuickRing", Apps::SettingQuickR},
{Symbols::clock, "Chimes", Apps::SettingChimes},
{Symbols::tachometer, "Shake Calib.", Apps::SettingShakeThreshold},
{Symbols::check, "Firmware", Apps::FirmwareValidation},
{Symbols::bluetooth, "Bluetooth", Apps::SettingBluetooth},
{Symbols::list, "About", Apps::SysInfo},
// {Symbols::none, "None", Apps::None},

View File

@ -3,8 +3,8 @@ find_program(LV_FONT_CONV "lv_font_conv" NO_CACHE REQUIRED
HINTS "${CMAKE_SOURCE_DIR}/node_modules/.bin")
message(STATUS "Using ${LV_FONT_CONV} to generate font files")
find_program(LV_IMG_CONV "lv_img_conv.py" NO_CACHE REQUIRED
HINTS "${CMAKE_CURRENT_SOURCE_DIR}")
find_program(LV_IMG_CONV "lv_img_conv" NO_CACHE REQUIRED
HINTS "${CMAKE_SOURCE_DIR}/node_modules/.bin")
message(STATUS "Using ${LV_IMG_CONV} to generate font files")
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12)

View File

@ -11,9 +11,6 @@ import subprocess
def gen_lvconv_line(lv_img_conv: str, dest: str, color_format: str, output_format: str, binary_format: str, sources: str):
args = [lv_img_conv, sources, '--force', '--output-file', dest, '--color-format', color_format, '--output-format', output_format, '--binary-format', binary_format]
if lv_img_conv.endswith(".py"):
# lv_img_conv is a python script, call with current python executable
args = [sys.executable] + args
return args

View File

@ -1,193 +0,0 @@
#!/usr/bin/env python3
import argparse
import pathlib
import sys
import decimal
from PIL import Image
def classify_pixel(value, bits):
def round_half_up(v):
"""python3 implements "propper" "banker's rounding" by rounding to the nearest
even number. Javascript rounds to the nearest integer.
To have the same output as the original JavaScript implementation add a custom
rounding function, which does "school" rounding (to the nearest integer).
see: https://stackoverflow.com/questions/43851273/how-to-round-float-0-5-up-to-1-0-while-still-rounding-0-45-to-0-0-as-the-usual
"""
return int(decimal.Decimal(v).quantize(decimal.Decimal('1'), rounding=decimal.ROUND_HALF_UP))
tmp = 1 << (8 - bits)
val = round_half_up(value / tmp) * tmp
if val < 0:
val = 0
return val
def test_classify_pixel():
# test difference between round() and round_half_up()
assert classify_pixel(18, 5) == 16
# school rounding 4.5 to 5, but banker's rounding 4.5 to 4
assert classify_pixel(18, 6) == 20
def main():
parser = argparse.ArgumentParser()
parser.add_argument("img",
help="Path to image to convert to C header file")
parser.add_argument("-o", "--output-file",
help="output file path (for single-image conversion)",
required=True)
parser.add_argument("-f", "--force",
help="allow overwriting the output file",
action="store_true")
parser.add_argument("-i", "--image-name",
help="name of image structure (not implemented)")
parser.add_argument("-c", "--color-format",
help="color format of image",
default="CF_TRUE_COLOR_ALPHA",
choices=[
"CF_ALPHA_1_BIT", "CF_ALPHA_2_BIT", "CF_ALPHA_4_BIT",
"CF_ALPHA_8_BIT", "CF_INDEXED_1_BIT", "CF_INDEXED_2_BIT", "CF_INDEXED_4_BIT",
"CF_INDEXED_8_BIT", "CF_RAW", "CF_RAW_CHROMA", "CF_RAW_ALPHA",
"CF_TRUE_COLOR", "CF_TRUE_COLOR_ALPHA", "CF_TRUE_COLOR_CHROMA", "CF_RGB565A8",
],
required=True)
parser.add_argument("-t", "--output-format",
help="output format of image",
default="bin", # default in original is 'c'
choices=["c", "bin"])
parser.add_argument("--binary-format",
help="binary color format (needed if output-format is binary)",
default="ARGB8565_RBSWAP",
choices=["ARGB8332", "ARGB8565", "ARGB8565_RBSWAP", "ARGB8888"])
parser.add_argument("-s", "--swap-endian",
help="swap endian of image (not implemented)",
action="store_true")
parser.add_argument("-d", "--dither",
help="enable dither (not implemented)",
action="store_true")
args = parser.parse_args()
img_path = pathlib.Path(args.img)
out = pathlib.Path(args.output_file)
if not img_path.is_file():
print(f"Input file is missing: '{args.img}'")
return 1
print(f"Beginning conversion of {args.img}")
if out.exists():
if args.force:
print(f"overwriting {args.output_file}")
else:
pritn(f"Error: refusing to overwrite {args.output_file} without -f specified.")
return 1
out.touch()
# only implemented the bare minimum, everything else is not implemented
if args.color_format not in ["CF_INDEXED_1_BIT", "CF_TRUE_COLOR_ALPHA"]:
raise NotImplementedError(f"argument --color-format '{args.color_format}' not implemented")
if args.output_format != "bin":
raise NotImplementedError(f"argument --output-format '{args.output_format}' not implemented")
if args.binary_format not in ["ARGB8565_RBSWAP", "ARGB8888"]:
raise NotImplementedError(f"argument --binary-format '{args.binary_format}' not implemented")
if args.image_name:
raise NotImplementedError(f"argument --image-name not implemented")
if args.swap_endian:
raise NotImplementedError(f"argument --swap-endian not implemented")
if args.dither:
raise NotImplementedError(f"argument --dither not implemented")
# open image using Pillow
img = Image.open(img_path)
img_height = img.height
img_width = img.width
if args.color_format == "CF_TRUE_COLOR_ALPHA" and args.binary_format == "ARGB8888":
buf = bytearray(img_height*img_width*4) # 4 bytes (32 bit) per pixel
for y in range(img_height):
for x in range(img_width):
i = (y*img_width + x)*4 # buffer-index
pixel = img.getpixel((x,y))
r, g, b, a = pixel
buf[i + 0] = r
buf[i + 1] = g
buf[i + 2] = b
buf[i + 3] = a
elif args.color_format == "CF_TRUE_COLOR_ALPHA" and args.binary_format == "ARGB8565_RBSWAP":
buf = bytearray(img_height*img_width*3) # 3 bytes (24 bit) per pixel
for y in range(img_height):
for x in range(img_width):
i = (y*img_width + x)*3 # buffer-index
pixel = img.getpixel((x,y))
r_act = classify_pixel(pixel[0], 5)
g_act = classify_pixel(pixel[1], 6)
b_act = classify_pixel(pixel[2], 5)
a = pixel[3]
r_act = min(r_act, 0xF8)
g_act = min(g_act, 0xFC)
b_act = min(b_act, 0xF8)
c16 = ((r_act) << 8) | ((g_act) << 3) | ((b_act) >> 3) # RGR565
buf[i + 0] = (c16 >> 8) & 0xFF
buf[i + 1] = c16 & 0xFF
buf[i + 2] = a
elif args.color_format == "CF_INDEXED_1_BIT": # ignore binary format, use color format as binary format
w = img_width >> 3
if img_width & 0x07:
w+=1
max_p = w * (img_height-1) + ((img_width-1) >> 3) + 8 # +8 for the palette
buf = bytearray(max_p+1)
for y in range(img_height):
for x in range(img_width):
c, a = img.getpixel((x,y))
p = w * y + (x >> 3) + 8 # +8 for the palette
buf[p] |= (c & 0x1) << (7 - (x & 0x7))
# write palette information, for indexed-1-bit we need palette with two values
# write 8 palette bytes
buf[0] = 0
buf[1] = 0
buf[2] = 0
buf[3] = 0
# Normally there is much math behind this, but for the current use case this is close enough
# only needs to be more complicated if we have more than 2 colors in the palette
buf[4] = 255
buf[5] = 255
buf[6] = 255
buf[7] = 255
else:
# raise just to be sure
raise NotImplementedError(f"args.color_format '{args.color_format}' with args.binary_format '{args.binary_format}' not implemented")
# write header
match args.color_format:
case "CF_TRUE_COLOR_ALPHA":
lv_cf = 5
case "CF_INDEXED_1_BIT":
lv_cf = 7
case _:
# raise just to be sure
raise NotImplementedError(f"args.color_format '{args.color_format}' not implemented")
header_32bit = lv_cf | (img_width << 10) | (img_height << 21)
buf_out = bytearray(4 + len(buf))
buf_out[0] = header_32bit & 0xFF
buf_out[1] = (header_32bit & 0xFF00) >> 8
buf_out[2] = (header_32bit & 0xFF0000) >> 16
buf_out[3] = (header_32bit & 0xFF000000) >> 24
buf_out[4:] = buf
# write byte buffer to file
with open(out, "wb") as f:
f.write(buf_out)
return 0
if __name__ == '__main__':
if "--test" in sys.argv:
# run small set of tests and exit
print("running tests")
test_classify_pixel()
print("success!")
sys.exit(0)
# run normal program
sys.exit(main())