Compare commits

...

48 Commits

Author SHA1 Message Date
tofasthacker
77f1a3e230 fixed watchface memery error 2023-11-13 23:11:09 -05:00
tofasthacker
11acaeadd3 Merge remote-tracking branch 'origin/faceface' into faceface 2023-11-13 21:38:42 -05:00
4fa8e02289 Incriment version 2023-11-13 21:37:00 -05:00
0cc8017cbd Small rename 2023-11-13 21:37:00 -05:00
d758394ce9 Working face watchface 2023-11-13 21:37:00 -05:00
dd5d65d315 Added duplicate of analog face 2023-11-13 21:37:00 -05:00
2bffcf99f0 Merge branch 'main' of https://github.com/InfiniTimeOrg/InfiniTime 2023-11-13 16:57:53 -05:00
Ben Merritt
9b8eb75f34
docker: Install Node.js in a non-deprecated way (#1849) 2023-11-12 14:09:41 +01:00
Kieran Cawthray
b191a30947 Tidy up 2023-11-11 18:07:07 +01:00
Kieran Cawthray
d930fd4fa2 Initial commit 2023-11-11 18:07:07 +01:00
Reinhold Gschweicher
e6b96c2863 CI: install build resource dependency python3-pil package
Used by script `lv_img_conv.py`, should be provided by docker image, but
until then explicitly install in workflow.
2023-10-26 22:45:01 +02:00
Reinhold Gschweicher
77546c9fe2 lv_img_conv_py: minimal python port of node module
Create a minimal python port of the node.js module `lv_img_conv`. Only
the currently in use color formats `CF_INDEXED_1_BIT` and
`CF_TRUE_COLOR_ALPHA` are implemented.

Output only as binary with format `ARGB8565_RBSWAP`.

This is enough to create the `resources-1.13.0.zip`.

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)

Update CMake file in `resources` folder to call `lv_img_conf.py` instead of
node module.

For docker-files install `python3-pil` package for `lv_img_conv.py` script.
And remove the `lv_img_conv` node installation.

---

gen_img: special handling for python lv_img_conv script

Not needed on Linux systems, as the shebang of the python script is read
and used. But just to be sure use the python interpreter found by CMake.
Also helps if tried to run on Windows host.

---

doc: buildAndProgram: remove node script lv_img_conv mention

Remove node script `lv_img_conv` mention and replace it for
runtime-depency `python3-pil` of python script `lv_img_conv.py`.
2023-10-26 22:45:01 +02:00
e24323b454 Incriment version 2023-10-25 22:33:30 -04:00
79820669b6 Small rename 2023-10-25 22:32:28 -04:00
1ff782cb0e Update version to 1.13.1
(cherry picked from commit 5d7e1ae1f3)
2023-10-22 23:42:06 -04:00
5d7e1ae1f3 Update version to 1.13.1 2023-10-22 23:41:43 -04:00
415ff6c05c Update reademe todo
(cherry picked from commit cbfd82959e)
2023-10-22 23:34:53 -04:00
2cb36d6e80 Working face watchface 2023-10-22 23:34:22 -04:00
cbfd82959e Update reademe todo 2023-10-22 23:31:15 -04:00
ce136ea148 Added duplicate of analog face 2023-10-22 22:37:53 -04:00
ab03504d0c Fix bug causing quickring to be missing in settings and some settings not remembered correctly 2023-10-22 21:38:52 -04:00
e4b4fba5a1 cleanup code and fix 12hour time on digital face 2023-10-21 23:45:41 -04:00
2ffc63b1d8 cleanup code and fix 12h time not working on digital face 2023-10-21 23:44:29 -04:00
31250fd579 Merge branch 'music_player' 2023-10-21 23:29:35 -04:00
94044d2fdc Removed battery percent from digital watch face 2023-10-21 23:14:41 -04:00
tofasthacker
858bf4f96e fix arrangement of alarm and timer 2023-10-21 22:01:12 -04:00
tofasthacker
1487a16f4a compiles on watch 2023-10-21 21:55:18 -04:00
tofasthacker
13af2bb96c Fixed battary order in digital watch face 2023-10-21 21:24:58 -04:00
e0e2126c29 Merge branch 'digital-face-mods' 2023-10-21 20:49:45 -04:00
6a772e7e8e Finish adding seconds 2023-10-21 20:16:20 -04:00
191f99904c Heartrate removed, steps moved, and seconds label added 2023-10-21 19:15:42 -04:00
8ada9410a1 continue swapping variables 2023-10-21 14:17:25 -04:00
bbfef9ee5c continue swapping variables 2023-10-19 22:39:49 -04:00
886e425461 Start swapping steps with heart rate variables 2023-10-15 23:18:20 -04:00
tofasthacker
6bbdb581cc Merge branch 'motor_pattern' 2023-10-14 23:13:37 -04:00
tofasthacker
270d15a8ea switched red flash light off night default
Some checks failed
Code formatting / test-format (pull_request) Failing after 20s
Code formatting / test-clang-tidy (pull_request) Successful in 7m31s
CI / build-firmware (pull_request) Successful in 5m39s
CI / build-simulator (pull_request) Failing after 5s
CI / get-base-ref-size (pull_request) Successful in 15m27s
CI / Compare build size (pull_request) Successful in 5s
PR comment / comment (pull_request) Failing after 17s
2023-10-14 22:55:41 -04:00
tofasthacker
bf8c4dfef8 change motor patern on half hour 2023-10-14 22:50:41 -04:00
tofasthacker
ecbcce41d7 Merge branch 'chime' into motor_pattern 2023-10-09 16:49:59 -04:00
Riku Isokoski
3d75a7dc9f added motor pattern 2023-10-09 16:49:35 -04:00
tofasthacker
9d749434b5 added red flashlight 2023-10-07 11:41:36 -04:00
tofasthacker
8ff6bd9cae changed chime 2023-10-06 21:04:42 -04:00
FintasticMan
eac460f030
weather: Fix GetCurrent* functions returning future events (#1879) 2023-10-06 19:54:20 +02:00
Steve Amor
46b664b528 Corrects typo for make option for recovery-loader 2023-10-04 20:24:37 +02:00
tofasthacker
b488811f7f working sim 2023-10-03 19:02:33 -04:00
tofasthacker
e19e870290 created a working quick ring setting gui 2023-10-01 17:47:08 -04:00
cdd97b3871 Added battery percent to digital watch face 2023-09-29 23:15:07 -04:00
b95823e745 Fixed second $ in terminal watch face 2023-09-23 19:32:01 -04:00
292b7e3fb1 Replaced am/pm with seconds on digital watchface 2023-09-21 21:01:37 -04:00
34 changed files with 1008 additions and 123 deletions

View File

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

View File

@ -31,6 +31,10 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
submodules: recursive submodules: recursive
- name: Install resource build dependencies
run: |
apt-get update
apt-get -y install --no-install-recommends python3-pil
- name: Build - name: Build
shell: bash shell: bash
run: /opt/build.sh all 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") set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose Debug or Release")
project(pinetime VERSION 1.13.0 LANGUAGES C CXX ASM) project(pinetime VERSION 1.13.1.1 LANGUAGES C CXX ASM)
set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD 14)

View File

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

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`| **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` **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_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 [lv_img_conv](https://github.com/lvgl/lv_img_conv). |`-DBUILD_RESOURCES=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`
**TARGET_DEVICE**|Target device, used for hardware configuration. Allowed: `PINETIME, MOY-TFK5, MOY-TIN5, MOY-TON5, MOY-UNK`|`-DTARGET_DEVICE=PINETIME` (Default) **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** #### (\*) 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-image** : MCUBoot image of the firmware
- **pinetime-mcuboot-app-dfu** : DFU file of the firmware - **pinetime-mcuboot-app-dfu** : DFU file of the firmware
The same files are generated for **pinetime-recovery** and **pinetime-recoveryloader** The same files are generated for **pinetime-recovery** and **pinetime-recovery-loader**

View File

@ -1,7 +1,13 @@
FROM ubuntu:22.04 FROM ubuntu:22.04
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive
ARG NODE_MAJOR=20
RUN apt-get update -qq \ 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 \ && apt-get install -y \
# x86_64 / generic packages # x86_64 / generic packages
bash \ bash \
@ -9,13 +15,14 @@ RUN apt-get update -qq \
cmake \ cmake \
git \ git \
make \ make \
nodejs \
python3 \ python3 \
python3-pip \ python3-pip \
python3-pil \
python-is-python3 \ python-is-python3 \
tar \ tar \
unzip \ unzip \
wget \ wget \
curl \
# aarch64 packages # aarch64 packages
libffi-dev \ libffi-dev \
libssl-dev \ libssl-dev \
@ -28,8 +35,6 @@ RUN apt-get update -qq \
libpango-1.0-0 \ libpango-1.0-0 \
ibpango1.0-dev \ ibpango1.0-dev \
libpangocairo-1.0-0 \ 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/*; && rm -rf /var/cache/apt/* /var/lib/apt/lists/*;
# Git needed for PROJECT_GIT_COMMIT_HASH variable setting # Git needed for PROJECT_GIT_COMMIT_HASH variable setting
@ -39,10 +44,6 @@ RUN pip3 install -Iv cryptography==3.3
RUN pip3 install cbor RUN pip3 install cbor
RUN npm i lv_font_conv@1.5.2 -g 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 # build.sh knows how to compile
COPY build.sh /opt/ COPY build.sh /opt/

View File

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

View File

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

View File

@ -5,14 +5,58 @@
using namespace Pinetime::Controllers; 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() { void MotorController::Init() {
nrf_gpio_cfg_output(PinMap::Motor); nrf_gpio_cfg_output(PinMap::Motor);
nrf_gpio_pin_set(PinMap::Motor); nrf_gpio_pin_set(PinMap::Motor);
vibTimer = xTimerCreate("vibration", 1, pdFALSE, this, PatternStep);
shortVib = xTimerCreate("shortVib", 1, pdFALSE, nullptr, StopMotor); shortVib = xTimerCreate("shortVib", 1, pdFALSE, nullptr, StopMotor);
longVib = xTimerCreate("longVib", pdMS_TO_TICKS(1000), pdTRUE, this, Ring); longVib = xTimerCreate("longVib", pdMS_TO_TICKS(1000), pdTRUE, this, Ring);
} }
void MotorController::PatternFinished() {
patternPlaying = false;
}
void MotorController::Ring(TimerHandle_t xTimer) { void MotorController::Ring(TimerHandle_t xTimer) {
auto* motorController = static_cast<MotorController*>(pvTimerGetTimerID(xTimer)); auto* motorController = static_cast<MotorController*>(pvTimerGetTimerID(xTimer));
motorController->RunForDuration(50); motorController->RunForDuration(50);
@ -24,6 +68,15 @@ void MotorController::RunForDuration(uint8_t motorDuration) {
} }
} }
bool MotorController::StartPattern() {
if (!patternPlaying) {
patternPlaying = true;
PatternStep(vibTimer);
return true;
}
return false;
}
void MotorController::StartRinging() { void MotorController::StartRinging() {
RunForDuration(50); RunForDuration(50);
xTimerStart(longVib, 0); xTimerStart(longVib, 0);

View File

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

View File

@ -13,6 +13,7 @@ namespace Pinetime {
enum class Notification : uint8_t { On, Off, Sleep }; enum class Notification : uint8_t { On, Off, Sleep };
enum class ChimesOption : uint8_t { None, Hours, HalfHours }; 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 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 { enum class Colors : uint8_t {
White, White,
Silver, Silver,
@ -241,6 +242,14 @@ namespace Pinetime {
return getWakeUpModes()[static_cast<size_t>(mode)]; return getWakeUpModes()[static_cast<size_t>(mode)];
} }
void SetBrightness(Controllers::BrightnessController::Levels level) { void SetBrightness(Controllers::BrightnessController::Levels level) {
if (level != settings.brightLevel) { if (level != settings.brightLevel) {
settingsChanged = true; settingsChanged = true;
@ -271,6 +280,26 @@ namespace Pinetime {
return bleRadioEnabled; 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: private:
Pinetime::Controllers::FS& fs; Pinetime::Controllers::FS& fs;
@ -279,6 +308,7 @@ namespace Pinetime {
struct SettingsData { struct SettingsData {
uint32_t version = settingsVersion; uint32_t version = settingsVersion;
uint32_t stepsGoal = 10000; uint32_t stepsGoal = 10000;
uint32_t setquickr = 10000;
uint32_t screenTimeOut = 15000; uint32_t screenTimeOut = 15000;
ClockType clockType = ClockType::H24; ClockType clockType = ClockType::H24;
@ -292,6 +322,7 @@ namespace Pinetime {
WatchFaceInfineat watchFaceInfineat; WatchFaceInfineat watchFaceInfineat;
std::bitset<5> wakeUpMode {0}; std::bitset<5> wakeUpMode {0};
std::bitset<5> quickApp {0};
uint16_t shakeWakeThreshold = 150; uint16_t shakeWakeThreshold = 150;
Controllers::BrightnessController::Levels brightLevel = Controllers::BrightnessController::Levels::Medium; Controllers::BrightnessController::Levels brightLevel = Controllers::BrightnessController::Levels::Medium;

View File

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

View File

@ -51,6 +51,7 @@
#include "displayapp/screens/settings/SettingChimes.h" #include "displayapp/screens/settings/SettingChimes.h"
#include "displayapp/screens/settings/SettingShakeThreshold.h" #include "displayapp/screens/settings/SettingShakeThreshold.h"
#include "displayapp/screens/settings/SettingBluetooth.h" #include "displayapp/screens/settings/SettingBluetooth.h"
#include "displayapp/screens/settings/SettingQuickR.h"
#include "libs/lv_conf.h" #include "libs/lv_conf.h"
@ -294,7 +295,7 @@ void DisplayApp::Refresh() {
}; };
if (!currentScreen->OnTouchEvent(gesture)) { if (!currentScreen->OnTouchEvent(gesture)) {
if (currentApp == Apps::Clock || currentApp == Apps::Music || currentApp == Apps::Calculator || currentApp == Apps::QuickSettings) { 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])) {
switch (gesture) { switch (gesture) {
case TouchEvents::SwipeUp: case TouchEvents::SwipeUp:
if (currentApp == Apps::Clock) { if (currentApp == Apps::Clock) {
@ -311,26 +312,10 @@ void DisplayApp::Refresh() {
} }
break; break;
case TouchEvents::SwipeRight: case TouchEvents::SwipeRight:
if (currentApp == Apps::Clock) { gotoquickapp(-1);
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; break;
case TouchEvents::SwipeLeft: case TouchEvents::SwipeLeft:
if (currentApp == Apps::Clock) { gotoquickapp(1);
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; break;
case TouchEvents::DoubleTap: case TouchEvents::DoubleTap:
PushMessageToSystemTask(System::Messages::GoToSleep); PushMessageToSystemTask(System::Messages::GoToSleep);
@ -349,6 +334,7 @@ void DisplayApp::Refresh() {
if (!currentScreen->OnButtonPushed()) { if (!currentScreen->OnButtonPushed()) {
if (currentApp == Apps::Clock) { if (currentApp == Apps::Clock) {
PushMessageToSystemTask(System::Messages::GoToSleep); PushMessageToSystemTask(System::Messages::GoToSleep);
} else { } else {
LoadPreviousScreen(); LoadPreviousScreen();
} }
@ -388,8 +374,19 @@ void DisplayApp::Refresh() {
// What should happen here? // What should happen here?
break; break;
case Messages::Chime: case Messages::Chime:
LoadNewScreen(Apps::Clock, DisplayApp::FullRefreshDirections::None);
motorController.RunForDuration(35); 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);
break; break;
case Messages::OnChargingEvent: case Messages::OnChargingEvent:
RestoreBrightness(); RestoreBrightness();
@ -442,6 +439,7 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio
// break; // break;
case Apps::None: case Apps::None:
case Apps::Clock: case Apps::Clock:
currentQ_app = 2;
currentScreen = std::make_unique<Screens::Clock>(dateTimeController, currentScreen = std::make_unique<Screens::Clock>(dateTimeController,
batteryController, batteryController,
bleController, bleController,
@ -583,6 +581,9 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio
case Apps::Calculator: case Apps::Calculator:
currentScreen = std::make_unique<Screens::Calculator>(); currentScreen = std::make_unique<Screens::Calculator>();
break; break;
case Apps::SettingQuickR:
currentScreen = std::make_unique<Screens::SettingQuickR>(settingsController);
break;
} }
currentApp = app; currentApp = app;
} }
@ -642,3 +643,84 @@ void DisplayApp::ApplyBrightness() {
} }
brightnessController.Set(brightness); 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,6 +118,8 @@ namespace Pinetime {
void LoadNewScreen(Apps app, DisplayApp::FullRefreshDirections direction); void LoadNewScreen(Apps app, DisplayApp::FullRefreshDirections direction);
void LoadScreen(Apps app, DisplayApp::FullRefreshDirections direction); void LoadScreen(Apps app, DisplayApp::FullRefreshDirections direction);
void PushMessageToSystemTask(Pinetime::System::Messages message); void PushMessageToSystemTask(Pinetime::System::Messages message);
void gotoquickapp(int app_step);
Apps nextApp = Apps::None; Apps nextApp = Apps::None;
DisplayApp::FullRefreshDirections nextDirection; DisplayApp::FullRefreshDirections nextDirection;
@ -128,7 +130,10 @@ namespace Pinetime {
Utility::StaticStack<Apps, returnAppStackSize> returnAppStack; Utility::StaticStack<Apps, returnAppStackSize> returnAppStack;
Utility::StaticStack<FullRefreshDirections, returnAppStackSize> appStackDirections; Utility::StaticStack<FullRefreshDirections, returnAppStackSize> appStackDirections;
int time_var;
bool isDimmed = false; bool isDimmed = false;
int currentQ_app = 2;
std::bitset<5> quick_app;
}; };
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -203,19 +203,21 @@ Navigation::Navigation(Pinetime::Controllers::NavigationService& nav) : navServi
lv_obj_align(imgFlag, nullptr, LV_ALIGN_CENTER, 0, -60); lv_obj_align(imgFlag, nullptr, LV_ALIGN_CENTER, 0, -60);
txtNarrative = lv_label_create(lv_scr_act(), nullptr); txtNarrative = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_long_mode(txtNarrative, LV_LABEL_LONG_BREAK); lv_label_set_long_mode(txtNarrative, LV_LABEL_LONG_DOT);
lv_obj_set_width(txtNarrative, LV_HOR_RES); lv_obj_set_width(txtNarrative, LV_HOR_RES);
lv_obj_set_height(txtNarrative, 80);
lv_label_set_text_static(txtNarrative, "Navigation"); lv_label_set_text_static(txtNarrative, "Navigation");
lv_label_set_align(txtNarrative, LV_LABEL_ALIGN_CENTER); lv_label_set_align(txtNarrative, LV_LABEL_ALIGN_CENTER);
lv_obj_align(txtNarrative, nullptr, LV_ALIGN_CENTER, 0, 10); lv_obj_align(txtNarrative, nullptr, LV_ALIGN_CENTER, 0, 30);
txtManDist = lv_label_create(lv_scr_act(), nullptr); txtManDist = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_long_mode(txtManDist, LV_LABEL_LONG_BREAK); 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_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_obj_set_width(txtManDist, LV_HOR_RES);
lv_label_set_text_static(txtManDist, "--M"); lv_label_set_text_static(txtManDist, "--M");
lv_label_set_align(txtManDist, LV_LABEL_ALIGN_CENTER); lv_label_set_align(txtManDist, LV_LABEL_ALIGN_CENTER);
lv_obj_align(txtManDist, nullptr, LV_ALIGN_CENTER, 0, 60); lv_obj_align(txtManDist, nullptr, LV_ALIGN_CENTER, 0, 90);
// Route Progress // Route Progress
barProgress = lv_bar_create(lv_scr_act(), nullptr); barProgress = lv_bar_create(lv_scr_act(), nullptr);

View File

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

View File

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

View File

@ -35,6 +35,10 @@ WatchFaceDigital::WatchFaceDigital(Controllers::DateTime& dateTimeController,
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(false)); lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(false));
lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0); 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); label_date = lv_label_create(lv_scr_act(), nullptr);
lv_obj_align(label_date, lv_scr_act(), LV_ALIGN_CENTER, 0, 60); 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)); lv_obj_set_style_local_text_color(label_date, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x999999));
@ -44,10 +48,6 @@ WatchFaceDigital::WatchFaceDigital(Controllers::DateTime& dateTimeController,
lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, 0, 0); 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); heartbeatIcon = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(heartbeatIcon, Symbols::heartBeat); 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)); lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xCE1B1B));
@ -85,23 +85,19 @@ void WatchFaceDigital::Refresh() {
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(notificationState.Get())); lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(notificationState.Get()));
} }
currentDateTime = std::chrono::time_point_cast<std::chrono::minutes>(dateTimeController.CurrentDateTime()); currentDateTime = std::chrono::time_point_cast<std::chrono::seconds>(dateTimeController.CurrentDateTime());
if (currentDateTime.IsUpdated()) { if (currentDateTime.IsUpdated()) {
uint8_t hour = dateTimeController.Hours(); uint8_t hour = dateTimeController.Hours();
uint8_t minute = dateTimeController.Minutes(); uint8_t minute = dateTimeController.Minutes();
uint8_t second = dateTimeController.Seconds();
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) { if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
char ampmChar[3] = "AM";
if (hour == 0) { if (hour == 0) {
hour = 12; hour = 12;
} else if (hour == 12) {
ampmChar[0] = 'P';
} else if (hour > 12) { } else if (hour > 12) {
hour = 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_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); lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, 0, 0);
} else { } else {
@ -109,6 +105,8 @@ void WatchFaceDigital::Refresh() {
lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); 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()); currentDate = std::chrono::time_point_cast<days>(currentDateTime.Get());
if (currentDate.IsUpdated()) { if (currentDate.IsUpdated()) {
uint16_t year = dateTimeController.Year(); uint16_t year = dateTimeController.Year();

View File

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

View File

@ -0,0 +1,274 @@
#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

@ -0,0 +1,95 @@
#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

@ -0,0 +1,77 @@
#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

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

View File

@ -29,7 +29,7 @@ namespace Pinetime {
static constexpr int entriesPerScreen = 4; static constexpr int entriesPerScreen = 4;
// Increment this when more space is needed // Increment this when more space is needed
static constexpr int nScreens = 3; static constexpr int nScreens = 4;
static constexpr std::array<List::Applications, entriesPerScreen * nScreens> entries {{ static constexpr std::array<List::Applications, entriesPerScreen * nScreens> entries {{
{Symbols::sun, "Display", Apps::SettingDisplay}, {Symbols::sun, "Display", Apps::SettingDisplay},
@ -40,11 +40,14 @@ namespace Pinetime {
{Symbols::shoe, "Steps", Apps::SettingSteps}, {Symbols::shoe, "Steps", Apps::SettingSteps},
{Symbols::clock, "Date&Time", Apps::SettingSetDateTime}, {Symbols::clock, "Date&Time", Apps::SettingSetDateTime},
{Symbols::batteryHalf, "Battery", Apps::BatteryInfo}, {Symbols::batteryHalf, "Battery", Apps::BatteryInfo},
{Symbols::clock, "Chimes", Apps::SettingChimes}, {Symbols::check, "QuickRing", Apps::SettingQuickR},
{Symbols::clock, "Chimes", Apps::SettingChimes},
{Symbols::tachometer, "Shake Calib.", Apps::SettingShakeThreshold}, {Symbols::tachometer, "Shake Calib.", Apps::SettingShakeThreshold},
{Symbols::check, "Firmware", Apps::FirmwareValidation}, {Symbols::check, "Firmware", Apps::FirmwareValidation},
{Symbols::bluetooth, "Bluetooth", Apps::SettingBluetooth}, {Symbols::bluetooth, "Bluetooth", Apps::SettingBluetooth},
{Symbols::list, "About", Apps::SysInfo}, {Symbols::list, "About", Apps::SysInfo},
// {Symbols::none, "None", Apps::None}, // {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") HINTS "${CMAKE_SOURCE_DIR}/node_modules/.bin")
message(STATUS "Using ${LV_FONT_CONV} to generate font files") message(STATUS "Using ${LV_FONT_CONV} to generate font files")
find_program(LV_IMG_CONV "lv_img_conv" NO_CACHE REQUIRED find_program(LV_IMG_CONV "lv_img_conv.py" NO_CACHE REQUIRED
HINTS "${CMAKE_SOURCE_DIR}/node_modules/.bin") HINTS "${CMAKE_CURRENT_SOURCE_DIR}")
message(STATUS "Using ${LV_IMG_CONV} to generate font files") message(STATUS "Using ${LV_IMG_CONV} to generate font files")
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12) if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12)

View File

@ -11,6 +11,9 @@ import subprocess
def gen_lvconv_line(lv_img_conv: str, dest: str, color_format: str, output_format: str, binary_format: str, sources: str): 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] 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 return args

193
src/resources/lv_img_conv.py Executable file
View File

@ -0,0 +1,193 @@
#!/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())