mirror of
https://github.com/InfiniTimeOrg/InfiniTime.git
synced 2024-10-22 23:21:53 +02:00
Merge branch 'main' into button-unlock
This commit is contained in:
commit
99d16861a8
|
@ -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 \
|
||||||
|
|
10
.github/workflows/main.yml
vendored
10
.github/workflows/main.yml
vendored
|
@ -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
|
||||||
|
@ -61,10 +65,10 @@ jobs:
|
||||||
build-simulator:
|
build-simulator:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Install SDL2 development package
|
- name: Install SDL2 and libpng development package
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get -y install libsdl2-dev
|
sudo apt-get -y install libsdl2-dev libpng-dev
|
||||||
|
|
||||||
- name: Install Ninja
|
- name: Install Ninja
|
||||||
run: |
|
run: |
|
||||||
|
@ -82,7 +86,7 @@ jobs:
|
||||||
- name: Get InfiniSim repo
|
- name: Get InfiniSim repo
|
||||||
run: |
|
run: |
|
||||||
git clone https://github.com/InfiniTimeOrg/InfiniSim.git --depth 1 --branch main
|
git clone https://github.com/InfiniTimeOrg/InfiniSim.git --depth 1 --branch main
|
||||||
git -C InfiniSim submodule update --init lv_drivers libpng
|
git -C InfiniSim submodule update --init lv_drivers
|
||||||
|
|
||||||
- name: CMake
|
- name: CMake
|
||||||
# disable BUILD_RESOURCES as this is already done when building the firmware
|
# disable BUILD_RESOURCES as this is already done when building the firmware
|
||||||
|
|
|
@ -5,7 +5,7 @@ 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.0 LANGUAGES C CXX ASM)
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 99)
|
set(CMAKE_C_STANDARD 99)
|
||||||
set(CMAKE_CXX_STANDARD 14)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
|
||||||
# set(CMAKE_GENERATOR "Unix Makefiles")
|
# set(CMAKE_GENERATOR "Unix Makefiles")
|
||||||
set(CMAKE_C_EXTENSIONS OFF)
|
set(CMAKE_C_EXTENSIONS OFF)
|
||||||
|
|
|
@ -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**
|
||||||
|
|
122
doc/code/Apps.md
122
doc/code/Apps.md
|
@ -9,59 +9,114 @@ This page will teach you:
|
||||||
|
|
||||||
The user interface of InfiniTime is made up of **screens**.
|
The user interface of InfiniTime is made up of **screens**.
|
||||||
Screens that are opened from the app launcher are considered **apps**.
|
Screens that are opened from the app launcher are considered **apps**.
|
||||||
Every app in InfiniTime is it's own class.
|
Every app in InfiniTime is its own class.
|
||||||
An instance of the class is created when the app is launched, and destroyed when the user exits the app.
|
An instance of the class is created when the app is launched, and destroyed when the user exits the app.
|
||||||
Apps run inside the "displayapp" task (briefly discussed [here](./Intro.md)).
|
Apps run inside the `DisplayApp` task (briefly discussed [here](./Intro.md)).
|
||||||
Apps are responsible for everything drawn on the screen when they are running.
|
Apps are responsible for everything drawn on the screen when they are running.
|
||||||
By default, apps only do something (as in a function is executed) when they are created or when a touch event is detected.
|
Apps can be refreshed periodically and reacts to external events (touch or button).
|
||||||
|
|
||||||
## Interface
|
## Interface
|
||||||
|
|
||||||
Every app class has to be inside the namespace `Pinetime::Applications::Screens` and inherit from `Screen`.
|
Every app class is declared inside the namespace `Pinetime::Applications::Screens`
|
||||||
The constructor should have at least one parameter `DisplayApp* app`, which it needs for the constructor of its parent class Screen.
|
and inherits
|
||||||
Other parameters should be references to controllers that the app needs.
|
from [`Pinetime::Applications::Screens::Screen`](https://github.com/InfiniTimeOrg/InfiniTime/blob/main/src/displayapp/screens/Screen.h).
|
||||||
A destructor is needed to clean up LVGL and restore any changes (for example re-enable sleeping).
|
|
||||||
App classes can override `bool OnButtonPushed()`, `bool OnTouchEvent(TouchEvents event)` and `bool OnTouchEvent(uint16_t x, uint16_t y)` to implement their own functionality for those events.
|
|
||||||
If an app only needs to display some text and do something upon a touch screen button press,
|
|
||||||
it does not need to override any of these functions, as LVGL can also handle touch events for you.
|
|
||||||
If you have any doubts, you can always look at how the other apps function for reference.
|
|
||||||
|
|
||||||
### Continuous updating
|
Each app defines its own constructor.
|
||||||
|
The constructors mostly take references to InfiniTime `Controllers` (ex: Alarm, DateTime, BLE services, Settings,...)
|
||||||
|
the app needs for its operations. The constructor is responsible for initializing the UI of the app.
|
||||||
|
|
||||||
If your app needs to be updated continuously, you can do so by overriding the `Refresh()` function in your class
|
The **destructor** cleans up LVGL and restores any changes (for example re-enable sleeping).
|
||||||
and calling `lv_task_create` inside the constructor.
|
|
||||||
|
|
||||||
An example call could look like this:
|
App classes can override `bool OnButtonPushed()`, `bool OnTouchEvent(TouchEvents event)`
|
||||||
|
and `bool OnTouchEvent(uint16_t x, uint16_t y)` to implement their own functionality for those events.
|
||||||
|
|
||||||
```cpp
|
Apps that need to be refreshed periodically create an `lv_task` (using `lv_task_create()`)
|
||||||
taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this);
|
that will call the method `Refresh()` periodically.
|
||||||
|
|
||||||
|
## App types
|
||||||
|
|
||||||
|
There are basically 2 types of applications : **system** apps and **user** apps.
|
||||||
|
|
||||||
|
**System** applications are always built into InfiniTime, and InfiniTime cannot work properly without those apps.
|
||||||
|
The watchfaces, settings, notifications and the application launcher are examples of such system applications.
|
||||||
|
|
||||||
|
**User** applications are optionally built into the firmware. They extend the functionalities of the system.
|
||||||
|
|
||||||
|
The distinction between **system** and **user** applications allows for more flexibility and customization.
|
||||||
|
This allows to easily select which user applications must be built into the firmware
|
||||||
|
without overflowing the system memory.
|
||||||
|
|
||||||
|
## Apps initialization
|
||||||
|
|
||||||
|
Apps are created by `DisplayApp` in `DisplayApp::LoadScreen()`.
|
||||||
|
This method simply call the creates an instance of the class that corresponds to the app specified in parameters.
|
||||||
|
|
||||||
|
The constructor of **system** apps is called directly. If the application is a **user** app,
|
||||||
|
the corresponding `AppDescription` is first retrieved from `userApps`
|
||||||
|
and then the function `create` is called to create an instance of the app.
|
||||||
|
|
||||||
|
## User application selection at build time
|
||||||
|
|
||||||
|
The list of user applications is generated at build time by the `consteval` function `CreateAppDescriptions()`
|
||||||
|
in `UserApps.h`. This method takes the list of applications that must be built into the firmware image.
|
||||||
|
This list of applications is defined as a list `Apps` enum values named `UserAppTypes` in `Apps.h`.
|
||||||
|
For each application listed in `UserAppTypes`, an entry of type `AppDescription` is added to the array `userApps`.
|
||||||
|
This entry is created by using the information provided by a template `AppTraits`
|
||||||
|
that is customized for every user application.
|
||||||
|
|
||||||
|
Here is an example of an AppTraits customized for the Alarm application.
|
||||||
|
It defines the type of application, its icon and a function that returns an instance of the application.
|
||||||
|
|
||||||
|
```c++
|
||||||
|
template <>
|
||||||
|
struct AppTraits<Apps::Alarm> {
|
||||||
|
static constexpr Apps app = Apps::Alarm;
|
||||||
|
static constexpr const char* icon = Screens::Symbols::clock;
|
||||||
|
|
||||||
|
static Screens::Screen* Create(AppControllers& controllers) {
|
||||||
|
return new Screens::Alarm(controllers.alarmController,
|
||||||
|
controllers.settingsController.GetClockType(),
|
||||||
|
*controllers.systemTask,
|
||||||
|
controllers.motorController);
|
||||||
|
};
|
||||||
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
With `taskRefresh` being a member variable of your class and of type `lv_task_t*`.
|
This array `userApps` is used by `DisplayApp` to create the applications and the `AppLauncher`
|
||||||
Remember to delete the task again using `lv_task_del`.
|
to list all available applications.
|
||||||
The function `RefreshTaskCallback` is inherited from `Screen` and just calls your `Refresh` function.
|
|
||||||
|
|
||||||
## Creating your own app
|
## Creating your own app
|
||||||
|
|
||||||
A minimal app could look like this:
|
A minimal user app could look like this:
|
||||||
|
|
||||||
MyApp.h:
|
MyApp.h:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "displayapp/Apps.h"
|
||||||
#include "displayapp/screens/Screen.h"
|
#include "displayapp/screens/Screen.h"
|
||||||
#include <lvgl/lvgl.h>
|
#include "displayapp/Controllers.h"
|
||||||
|
#include "Symbols.h"
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Applications {
|
namespace Applications {
|
||||||
namespace Screens {
|
namespace Screens {
|
||||||
class MyApp : public Screen {
|
class MyApp : public Screen {
|
||||||
public:
|
public:
|
||||||
MyApp(DisplayApp* app);
|
MyApp();
|
||||||
~MyApp() override;
|
~MyApp() override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct AppTraits<Apps:MyApp> {
|
||||||
|
static constexpr Apps app = Apps::MyApp;
|
||||||
|
static constexpr const char* icon = Screens::Symbol::myApp;
|
||||||
|
static Screens::Screens* Create(AppController& controllers) {
|
||||||
|
return new Screens::MyApp();
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -70,11 +125,10 @@ MyApp.cpp:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include "displayapp/screens/MyApp.h"
|
#include "displayapp/screens/MyApp.h"
|
||||||
#include "displayapp/DisplayApp.h"
|
|
||||||
|
|
||||||
using namespace Pinetime::Applications::Screens;
|
using namespace Pinetime::Applications::Screens;
|
||||||
|
|
||||||
MyApp::MyApp(DisplayApp* app) : Screen(app) {
|
MyApp::MyApp() {
|
||||||
lv_obj_t* title = lv_label_create(lv_scr_act(), nullptr);
|
lv_obj_t* title = lv_label_create(lv_scr_act(), nullptr);
|
||||||
lv_label_set_text_static(title, "My test application");
|
lv_label_set_text_static(title, "My test application");
|
||||||
lv_label_set_align(title, LV_LABEL_ALIGN_CENTER);
|
lv_label_set_align(title, LV_LABEL_ALIGN_CENTER);
|
||||||
|
@ -86,20 +140,24 @@ MyApp::~MyApp() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Both of these files should be in [displayapp/screens/](/src/displayapp/screens/)
|
Both of these files should be in [displayapp/screens/](/src/displayapp/screens/).
|
||||||
or [displayapp/screens/settings/](/src/displayapp/screens/settings/) if it's a setting app.
|
|
||||||
|
|
||||||
Now we have our very own app, but InfiniTime does not know about it yet.
|
Now we have our very own app, but InfiniTime does not know about it yet.
|
||||||
The first step is to include your MyApp.cpp (or any new cpp files for that matter)
|
The first step is to include your `MyApp.cpp` (or any new cpp files for that matter)
|
||||||
in the compilation by adding it to [CMakeLists.txt](/CMakeLists.txt).
|
in the compilation by adding it to [CMakeLists.txt](/CMakeLists.txt).
|
||||||
The next step to making it launchable is to give your app an id.
|
The next step to making it launch-able is to give your app an id.
|
||||||
To do this, add an entry in the enum class `Pinetime::Applications::Apps` ([displayapp/Apps.h](/src/displayapp/Apps.h)).
|
To do this, add an entry in the enum class `Pinetime::Applications::Apps` ([displayapp/Apps.h](/src/displayapp/Apps.h)).
|
||||||
Name this entry after your app. Add `#include "displayapp/screens/MyApp.h"` to the file [displayapp/DisplayApp.cpp](/src/displayapp/DisplayApp.cpp).
|
Name this entry after your app. Add `#include "displayapp/screens/MyApp.h"`
|
||||||
Now, go to the function `DisplayApp::LoadScreen` and add another case to the switch statement.
|
to the file [displayapp/DisplayApp.cpp](/src/displayapp/DisplayApp.cpp).
|
||||||
|
|
||||||
|
If your application is a **system** application, go to the function `DisplayApp::LoadScreen`
|
||||||
|
and add another case to the switch statement.
|
||||||
The case will be the id you gave your app earlier.
|
The case will be the id you gave your app earlier.
|
||||||
If your app needs any additional arguments, this is the place to pass them.
|
If your app needs any additional arguments, this is the place to pass them.
|
||||||
|
|
||||||
If you want to add your app in the app launcher, add your app in [displayapp/screens/ApplicationList.h](/src/displayapp/screens/ApplicationList.h) to the array containing the applications and their corresponding symbol. If your app is a setting, do the same procedure in [displayapp/screens/settings/Settings.h](/src/displayapp/screens/settings/Settings.h).
|
If your application is a **user** application, you don't need to add anything in DisplayApp,
|
||||||
|
everything will be automatically generated for you.
|
||||||
|
The user application will also be automatically be added to the app launcher menu.
|
||||||
|
|
||||||
You should now be able to [build](../buildAndProgram.md) the firmware
|
You should now be able to [build](../buildAndProgram.md) the firmware
|
||||||
and flash it to your PineTime. Yay!
|
and flash it to your PineTime. Yay!
|
||||||
|
|
|
@ -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/
|
||||||
|
|
||||||
|
|
|
@ -394,7 +394,6 @@ list(APPEND SOURCE_FILES
|
||||||
displayapp/screens/Notifications.cpp
|
displayapp/screens/Notifications.cpp
|
||||||
displayapp/screens/Twos.cpp
|
displayapp/screens/Twos.cpp
|
||||||
displayapp/screens/HeartRate.cpp
|
displayapp/screens/HeartRate.cpp
|
||||||
displayapp/screens/Motion.cpp
|
|
||||||
displayapp/screens/FlashLight.cpp
|
displayapp/screens/FlashLight.cpp
|
||||||
displayapp/screens/List.cpp
|
displayapp/screens/List.cpp
|
||||||
displayapp/screens/CheckboxList.cpp
|
displayapp/screens/CheckboxList.cpp
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,7 +141,7 @@ void Gfx::SetBackgroundColor(uint16_t color) {
|
||||||
bool Gfx::GetNextBuffer(uint8_t** data, size_t& size) {
|
bool Gfx::GetNextBuffer(uint8_t** data, size_t& size) {
|
||||||
if (!state.busy)
|
if (!state.busy)
|
||||||
return false;
|
return false;
|
||||||
state.remainingIterations--;
|
state.remainingIterations = state.remainingIterations - 1;
|
||||||
if (state.remainingIterations == 0) {
|
if (state.remainingIterations == 0) {
|
||||||
state.busy = false;
|
state.busy = false;
|
||||||
NotifyEndOfTransfer(state.taskToNotify);
|
NotifyEndOfTransfer(state.taskToNotify);
|
||||||
|
@ -170,7 +170,7 @@ bool Gfx::GetNextBuffer(uint8_t** data, size_t& size) {
|
||||||
size = bytes_in_line * 8 * 2;
|
size = bytes_in_line * 8 * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.currentIteration++;
|
state.currentIteration = state.currentIteration + 1;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <cstddef>
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Applications {
|
namespace Applications {
|
||||||
enum class Apps {
|
enum class Apps {
|
||||||
|
@ -37,7 +37,33 @@ namespace Pinetime {
|
||||||
SettingChimes,
|
SettingChimes,
|
||||||
SettingShakeThreshold,
|
SettingShakeThreshold,
|
||||||
SettingBluetooth,
|
SettingBluetooth,
|
||||||
Error
|
Error,
|
||||||
|
Weather
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <Apps>
|
||||||
|
struct AppTraits {};
|
||||||
|
|
||||||
|
template <Apps... As>
|
||||||
|
struct TypeList {
|
||||||
|
static constexpr size_t Count = sizeof...(As);
|
||||||
|
};
|
||||||
|
|
||||||
|
using UserAppTypes = TypeList<Apps::Alarm,
|
||||||
|
Apps::HeartRate,
|
||||||
|
Apps::Paint,
|
||||||
|
Apps::Metronome,
|
||||||
|
Apps::Music,
|
||||||
|
Apps::Navigation,
|
||||||
|
Apps::Paddle,
|
||||||
|
Apps::Steps,
|
||||||
|
Apps::StopWatch,
|
||||||
|
Apps::Timer,
|
||||||
|
Apps::Twos
|
||||||
|
/*
|
||||||
|
Apps::Weather,
|
||||||
|
Apps::Motion
|
||||||
|
*/
|
||||||
|
>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
56
src/displayapp/Controllers.h
Normal file
56
src/displayapp/Controllers.h
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Pinetime {
|
||||||
|
namespace Applications {
|
||||||
|
class DisplayApp;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Components {
|
||||||
|
class LittleVgl;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Controllers {
|
||||||
|
class Battery;
|
||||||
|
class Ble;
|
||||||
|
class DateTime;
|
||||||
|
class NotificationManager;
|
||||||
|
class HeartRateController;
|
||||||
|
class Settings;
|
||||||
|
class MotorController;
|
||||||
|
class MotionController;
|
||||||
|
class AlarmController;
|
||||||
|
class BrightnessController;
|
||||||
|
class WeatherService;
|
||||||
|
class FS;
|
||||||
|
class Timer;
|
||||||
|
class MusicService;
|
||||||
|
class NavigationService;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace System {
|
||||||
|
class SystemTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Applications {
|
||||||
|
struct AppControllers {
|
||||||
|
const Pinetime::Controllers::Battery& batteryController;
|
||||||
|
const Pinetime::Controllers::Ble& bleController;
|
||||||
|
Pinetime::Controllers::DateTime& dateTimeController;
|
||||||
|
Pinetime::Controllers::NotificationManager& notificationManager;
|
||||||
|
Pinetime::Controllers::HeartRateController& heartRateController;
|
||||||
|
Pinetime::Controllers::Settings& settingsController;
|
||||||
|
Pinetime::Controllers::MotorController& motorController;
|
||||||
|
Pinetime::Controllers::MotionController& motionController;
|
||||||
|
Pinetime::Controllers::AlarmController& alarmController;
|
||||||
|
Pinetime::Controllers::BrightnessController& brightnessController;
|
||||||
|
Pinetime::Controllers::WeatherService* weatherController;
|
||||||
|
Pinetime::Controllers::FS& filesystem;
|
||||||
|
Pinetime::Controllers::Timer& timer;
|
||||||
|
Pinetime::System::SystemTask* systemTask;
|
||||||
|
Pinetime::Applications::DisplayApp* displayApp;
|
||||||
|
Pinetime::Components::LittleVgl& lvgl;
|
||||||
|
Pinetime::Controllers::MusicService* musicService;
|
||||||
|
Pinetime::Controllers::NavigationService* navigationService;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -50,6 +50,7 @@
|
||||||
#include "displayapp/screens/settings/SettingBluetooth.h"
|
#include "displayapp/screens/settings/SettingBluetooth.h"
|
||||||
|
|
||||||
#include "libs/lv_conf.h"
|
#include "libs/lv_conf.h"
|
||||||
|
#include "UserApps.h"
|
||||||
|
|
||||||
using namespace Pinetime::Applications;
|
using namespace Pinetime::Applications;
|
||||||
using namespace Pinetime::Applications::Display;
|
using namespace Pinetime::Applications::Display;
|
||||||
|
@ -97,7 +98,25 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd,
|
||||||
filesystem {filesystem},
|
filesystem {filesystem},
|
||||||
lvgl {lcd, filesystem},
|
lvgl {lcd, filesystem},
|
||||||
timer(this, TimerCallback),
|
timer(this, TimerCallback),
|
||||||
popupMessage {"Touch input\nis ignored,\npush button\nto unlock."} {
|
popupMessage {"Touch input\nis ignored,\npush button\nto unlock."},
|
||||||
|
controllers {batteryController,
|
||||||
|
bleController,
|
||||||
|
dateTimeController,
|
||||||
|
notificationManager,
|
||||||
|
heartRateController,
|
||||||
|
settingsController,
|
||||||
|
motorController,
|
||||||
|
motionController,
|
||||||
|
alarmController,
|
||||||
|
brightnessController,
|
||||||
|
nullptr,
|
||||||
|
filesystem,
|
||||||
|
timer,
|
||||||
|
nullptr,
|
||||||
|
this,
|
||||||
|
lvgl,
|
||||||
|
nullptr,
|
||||||
|
nullptr} {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisplayApp::Start(System::BootErrors error) {
|
void DisplayApp::Start(System::BootErrors error) {
|
||||||
|
@ -409,14 +428,20 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio
|
||||||
SetFullRefresh(direction);
|
SetFullRefresh(direction);
|
||||||
|
|
||||||
switch (app) {
|
switch (app) {
|
||||||
case Apps::Launcher:
|
case Apps::Launcher: {
|
||||||
currentScreen =
|
std::array<Screens::Tile::Applications, UserAppTypes::Count> apps;
|
||||||
std::make_unique<Screens::ApplicationList>(this, settingsController, batteryController, bleController, dateTimeController, filesystem);
|
int i = 0;
|
||||||
break;
|
for (const auto& userApp : userApps) {
|
||||||
case Apps::Motion:
|
apps[i++] = Screens::Tile::Applications {userApp.icon, userApp.app, true};
|
||||||
// currentScreen = std::make_unique<Screens::Motion>(motionController);
|
}
|
||||||
// break;
|
currentScreen = std::make_unique<Screens::ApplicationList>(this,
|
||||||
case Apps::None:
|
settingsController,
|
||||||
|
batteryController,
|
||||||
|
bleController,
|
||||||
|
dateTimeController,
|
||||||
|
filesystem,
|
||||||
|
std::move(apps));
|
||||||
|
} break;
|
||||||
case Apps::Clock:
|
case Apps::Clock:
|
||||||
currentScreen = std::make_unique<Screens::Clock>(dateTimeController,
|
currentScreen = std::make_unique<Screens::Clock>(dateTimeController,
|
||||||
batteryController,
|
batteryController,
|
||||||
|
@ -428,7 +453,6 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio
|
||||||
systemTask->nimble().weather(),
|
systemTask->nimble().weather(),
|
||||||
filesystem);
|
filesystem);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Apps::Error:
|
case Apps::Error:
|
||||||
currentScreen = std::make_unique<Screens::Error>(bootError);
|
currentScreen = std::make_unique<Screens::Error>(bootError);
|
||||||
break;
|
break;
|
||||||
|
@ -460,14 +484,6 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio
|
||||||
*systemTask,
|
*systemTask,
|
||||||
Screens::Notifications::Modes::Preview);
|
Screens::Notifications::Modes::Preview);
|
||||||
break;
|
break;
|
||||||
case Apps::Timer:
|
|
||||||
currentScreen = std::make_unique<Screens::Timer>(timer);
|
|
||||||
break;
|
|
||||||
case Apps::Alarm:
|
|
||||||
currentScreen = std::make_unique<Screens::Alarm>(alarmController, settingsController.GetClockType(), *systemTask, motorController);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Settings
|
|
||||||
case Apps::QuickSettings:
|
case Apps::QuickSettings:
|
||||||
currentScreen = std::make_unique<Screens::QuickSettings>(this,
|
currentScreen = std::make_unique<Screens::QuickSettings>(this,
|
||||||
batteryController,
|
batteryController,
|
||||||
|
@ -523,38 +539,25 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio
|
||||||
case Apps::FlashLight:
|
case Apps::FlashLight:
|
||||||
currentScreen = std::make_unique<Screens::FlashLight>(*systemTask, brightnessController);
|
currentScreen = std::make_unique<Screens::FlashLight>(*systemTask, brightnessController);
|
||||||
break;
|
break;
|
||||||
case Apps::StopWatch:
|
default: {
|
||||||
currentScreen = std::make_unique<Screens::StopWatch>(*systemTask);
|
const auto* d = std::find_if(userApps.begin(), userApps.end(), [app](const AppDescription& appDescription) {
|
||||||
break;
|
return appDescription.app == app;
|
||||||
case Apps::Twos:
|
});
|
||||||
currentScreen = std::make_unique<Screens::Twos>();
|
if (d != userApps.end())
|
||||||
break;
|
currentScreen.reset(d->create(controllers));
|
||||||
case Apps::Paint:
|
else {
|
||||||
currentScreen = std::make_unique<Screens::InfiniPaint>(lvgl, motorController);
|
currentScreen = std::make_unique<Screens::Clock>(dateTimeController,
|
||||||
break;
|
batteryController,
|
||||||
case Apps::Paddle:
|
bleController,
|
||||||
currentScreen = std::make_unique<Screens::Paddle>(lvgl);
|
notificationManager,
|
||||||
break;
|
settingsController,
|
||||||
case Apps::Music:
|
heartRateController,
|
||||||
currentScreen = std::make_unique<Screens::Music>(systemTask->nimble().music());
|
motionController,
|
||||||
break;
|
systemTask->nimble().weather(),
|
||||||
case Apps::Navigation:
|
filesystem);
|
||||||
currentScreen = std::make_unique<Screens::Navigation>(systemTask->nimble().navigation());
|
}
|
||||||
break;
|
|
||||||
case Apps::HeartRate:
|
|
||||||
currentScreen = std::make_unique<Screens::HeartRate>(heartRateController, *systemTask);
|
|
||||||
break;
|
|
||||||
case Apps::Metronome:
|
|
||||||
currentScreen = std::make_unique<Screens::Metronome>(motorController, *systemTask);
|
|
||||||
break;
|
|
||||||
/* Weather debug app
|
|
||||||
case Apps::Weather:
|
|
||||||
currentScreen = std::make_unique<Screens::Weather>(this, systemTask->nimble().weather());
|
|
||||||
break;
|
|
||||||
*/
|
|
||||||
case Apps::Steps:
|
|
||||||
currentScreen = std::make_unique<Screens::Steps>(motionController, settingsController);
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
currentApp = app;
|
currentApp = app;
|
||||||
}
|
}
|
||||||
|
@ -567,7 +570,15 @@ void DisplayApp::PushMessage(Messages msg) {
|
||||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
xQueueSend(msgQueue, &msg, portMAX_DELAY);
|
TickType_t timeout = portMAX_DELAY;
|
||||||
|
// Make xQueueSend() non-blocking if the message is a Notification message. We do this to avoid
|
||||||
|
// deadlock between SystemTask and DisplayApp when their respective message queues are getting full
|
||||||
|
// when a lot of notifications are received on a very short time span.
|
||||||
|
if (msg == Messages::NewNotification) {
|
||||||
|
timeout = static_cast<TickType_t>(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
xQueueSend(msgQueue, &msg, timeout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -604,6 +615,19 @@ void DisplayApp::PushMessageToSystemTask(Pinetime::System::Messages message) {
|
||||||
|
|
||||||
void DisplayApp::Register(Pinetime::System::SystemTask* systemTask) {
|
void DisplayApp::Register(Pinetime::System::SystemTask* systemTask) {
|
||||||
this->systemTask = systemTask;
|
this->systemTask = systemTask;
|
||||||
|
this->controllers.systemTask = systemTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisplayApp::Register(Pinetime::Controllers::WeatherService* weatherService) {
|
||||||
|
this->controllers.weatherController = weatherService;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisplayApp::Register(Pinetime::Controllers::MusicService* musicService) {
|
||||||
|
this->controllers.musicService = musicService;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisplayApp::Register(Pinetime::Controllers::NavigationService* NavigationService) {
|
||||||
|
this->controllers.navigationService = NavigationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisplayApp::ApplyBrightness() {
|
void DisplayApp::ApplyBrightness() {
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "BootErrors.h"
|
#include "BootErrors.h"
|
||||||
|
|
||||||
#include "utility/StaticStack.h"
|
#include "utility/StaticStack.h"
|
||||||
|
#include "displayapp/Controllers.h"
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
|
|
||||||
|
@ -74,6 +75,9 @@ namespace Pinetime {
|
||||||
void SetFullRefresh(FullRefreshDirections direction);
|
void SetFullRefresh(FullRefreshDirections direction);
|
||||||
|
|
||||||
void Register(Pinetime::System::SystemTask* systemTask);
|
void Register(Pinetime::System::SystemTask* systemTask);
|
||||||
|
void Register(Pinetime::Controllers::WeatherService* weatherService);
|
||||||
|
void Register(Pinetime::Controllers::MusicService* musicService);
|
||||||
|
void Register(Pinetime::Controllers::NavigationService* NavigationService);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Pinetime::Drivers::St7789& lcd;
|
Pinetime::Drivers::St7789& lcd;
|
||||||
|
@ -99,6 +103,7 @@ namespace Pinetime {
|
||||||
|
|
||||||
Pinetime::Applications::Widgets::PopupMessage popupMessage;
|
Pinetime::Applications::Widgets::PopupMessage popupMessage;
|
||||||
|
|
||||||
|
AppControllers controllers;
|
||||||
TaskHandle_t taskHandle;
|
TaskHandle_t taskHandle;
|
||||||
|
|
||||||
States state = States::Running;
|
States state = States::Running;
|
||||||
|
|
|
@ -121,3 +121,12 @@ void DisplayApp::PushMessage(Display::Messages msg) {
|
||||||
|
|
||||||
void DisplayApp::Register(Pinetime::System::SystemTask* /*systemTask*/) {
|
void DisplayApp::Register(Pinetime::System::SystemTask* /*systemTask*/) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DisplayApp::Register(Pinetime::Controllers::WeatherService* /*weatherService*/) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisplayApp::Register(Pinetime::Controllers::MusicService* /*musicService*/) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisplayApp::Register(Pinetime::Controllers::NavigationService* /*NavigationService*/) {
|
||||||
|
}
|
||||||
|
|
|
@ -34,6 +34,9 @@ namespace Pinetime {
|
||||||
class AlarmController;
|
class AlarmController;
|
||||||
class BrightnessController;
|
class BrightnessController;
|
||||||
class FS;
|
class FS;
|
||||||
|
class WeatherService;
|
||||||
|
class MusicService;
|
||||||
|
class NavigationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace System {
|
namespace System {
|
||||||
|
@ -66,6 +69,9 @@ namespace Pinetime {
|
||||||
|
|
||||||
void PushMessage(Pinetime::Applications::Display::Messages msg);
|
void PushMessage(Pinetime::Applications::Display::Messages msg);
|
||||||
void Register(Pinetime::System::SystemTask* systemTask);
|
void Register(Pinetime::System::SystemTask* systemTask);
|
||||||
|
void Register(Pinetime::Controllers::WeatherService* weatherService);
|
||||||
|
void Register(Pinetime::Controllers::MusicService* musicService);
|
||||||
|
void Register(Pinetime::Controllers::NavigationService* NavigationService);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TaskHandle_t taskHandle;
|
TaskHandle_t taskHandle;
|
||||||
|
|
36
src/displayapp/UserApps.h
Normal file
36
src/displayapp/UserApps.h
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#pragma once
|
||||||
|
#include "Apps.h"
|
||||||
|
#include "Controllers.h"
|
||||||
|
|
||||||
|
#include "displayapp/screens/Alarm.h"
|
||||||
|
#include "displayapp/screens/Timer.h"
|
||||||
|
#include "displayapp/screens/Twos.h"
|
||||||
|
#include "displayapp/screens/Tile.h"
|
||||||
|
#include "displayapp/screens/ApplicationList.h"
|
||||||
|
#include "displayapp/screens/Clock.h"
|
||||||
|
|
||||||
|
namespace Pinetime {
|
||||||
|
namespace Applications {
|
||||||
|
namespace Screens {
|
||||||
|
class Screen;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AppDescription {
|
||||||
|
Apps app;
|
||||||
|
const char* icon;
|
||||||
|
Screens::Screen* (*create)(AppControllers& controllers);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <Apps t>
|
||||||
|
consteval AppDescription CreateAppDescription() {
|
||||||
|
return {AppTraits<t>::app, AppTraits<t>::icon, &AppTraits<t>::Create};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <template <Apps...> typename T, Apps... ts>
|
||||||
|
consteval std::array<AppDescription, sizeof...(ts)> CreateAppDescriptions(T<ts...>) {
|
||||||
|
return {CreateAppDescription<ts>()...};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto userApps = CreateAppDescriptions(UserAppTypes {});
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
||||||
|
|
|
@ -19,6 +19,10 @@
|
||||||
#include "displayapp/screens/Screen.h"
|
#include "displayapp/screens/Screen.h"
|
||||||
#include "displayapp/screens/Symbols.h"
|
#include "displayapp/screens/Symbols.h"
|
||||||
#include "displayapp/InfiniTimeTheme.h"
|
#include "displayapp/InfiniTimeTheme.h"
|
||||||
|
#include "components/settings/Settings.h"
|
||||||
|
#include "components/alarm/AlarmController.h"
|
||||||
|
#include "components/motor/MotorController.h"
|
||||||
|
#include "systemtask/SystemTask.h"
|
||||||
|
|
||||||
using namespace Pinetime::Applications::Screens;
|
using namespace Pinetime::Applications::Screens;
|
||||||
using Pinetime::Controllers::AlarmController;
|
using Pinetime::Controllers::AlarmController;
|
||||||
|
|
|
@ -17,21 +17,22 @@
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "displayapp/Apps.h"
|
||||||
|
#include "components/settings/Settings.h"
|
||||||
#include "displayapp/screens/Screen.h"
|
#include "displayapp/screens/Screen.h"
|
||||||
#include "systemtask/SystemTask.h"
|
|
||||||
#include "displayapp/LittleVgl.h"
|
|
||||||
#include "components/alarm/AlarmController.h"
|
|
||||||
#include "displayapp/widgets/Counter.h"
|
#include "displayapp/widgets/Counter.h"
|
||||||
|
#include "displayapp/Controllers.h"
|
||||||
|
#include "Symbols.h"
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Applications {
|
namespace Applications {
|
||||||
namespace Screens {
|
namespace Screens {
|
||||||
class Alarm : public Screen {
|
class Alarm : public Screen {
|
||||||
public:
|
public:
|
||||||
Alarm(Controllers::AlarmController& alarmController,
|
explicit Alarm(Controllers::AlarmController& alarmController,
|
||||||
Controllers::Settings::ClockType clockType,
|
Controllers::Settings::ClockType clockType,
|
||||||
System::SystemTask& systemTask,
|
System::SystemTask& systemTask,
|
||||||
Controllers::MotorController& motorController);
|
Controllers::MotorController& motorController);
|
||||||
~Alarm() override;
|
~Alarm() override;
|
||||||
void SetAlerting();
|
void SetAlerting();
|
||||||
void OnButtonEvent(lv_obj_t* obj, lv_event_t event);
|
void OnButtonEvent(lv_obj_t* obj, lv_event_t event);
|
||||||
|
@ -63,6 +64,19 @@ namespace Pinetime {
|
||||||
Widgets::Counter hourCounter = Widgets::Counter(0, 23, jetbrains_mono_76);
|
Widgets::Counter hourCounter = Widgets::Counter(0, 23, jetbrains_mono_76);
|
||||||
Widgets::Counter minuteCounter = Widgets::Counter(0, 59, jetbrains_mono_76);
|
Widgets::Counter minuteCounter = Widgets::Counter(0, 59, jetbrains_mono_76);
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct AppTraits<Apps::Alarm> {
|
||||||
|
static constexpr Apps app = Apps::Alarm;
|
||||||
|
static constexpr const char* icon = Screens::Symbols::clock;
|
||||||
|
|
||||||
|
static Screens::Screen* Create(AppControllers& controllers) {
|
||||||
|
return new Screens::Alarm(controllers.alarmController,
|
||||||
|
controllers.settingsController.GetClockType(),
|
||||||
|
*controllers.systemTask,
|
||||||
|
controllers.motorController);
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
#include "displayapp/screens/ApplicationList.h"
|
#include "displayapp/screens/ApplicationList.h"
|
||||||
|
#include "displayapp/screens/Tile.h"
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include "displayapp/Apps.h"
|
#include <algorithm>
|
||||||
#include "displayapp/DisplayApp.h"
|
#include "components/settings/Settings.h"
|
||||||
|
|
||||||
using namespace Pinetime::Applications::Screens;
|
using namespace Pinetime::Applications::Screens;
|
||||||
|
|
||||||
|
@ -16,18 +17,20 @@ auto ApplicationList::CreateScreenList() const {
|
||||||
return screens;
|
return screens;
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplicationList::ApplicationList(Pinetime::Applications::DisplayApp* app,
|
ApplicationList::ApplicationList(DisplayApp* app,
|
||||||
Pinetime::Controllers::Settings& settingsController,
|
Pinetime::Controllers::Settings& settingsController,
|
||||||
const Pinetime::Controllers::Battery& batteryController,
|
const Pinetime::Controllers::Battery& batteryController,
|
||||||
const Pinetime::Controllers::Ble& bleController,
|
const Pinetime::Controllers::Ble& bleController,
|
||||||
Controllers::DateTime& dateTimeController,
|
Controllers::DateTime& dateTimeController,
|
||||||
Pinetime::Controllers::FS& filesystem)
|
Pinetime::Controllers::FS& filesystem,
|
||||||
|
std::array<Tile::Applications, UserAppTypes::Count>&& apps)
|
||||||
: app {app},
|
: app {app},
|
||||||
settingsController {settingsController},
|
settingsController {settingsController},
|
||||||
batteryController {batteryController},
|
batteryController {batteryController},
|
||||||
bleController {bleController},
|
bleController {bleController},
|
||||||
dateTimeController {dateTimeController},
|
dateTimeController {dateTimeController},
|
||||||
filesystem{filesystem},
|
filesystem {filesystem},
|
||||||
|
apps {std::move(apps)},
|
||||||
screens {app, settingsController.GetAppMenu(), CreateScreenList(), Screens::ScreenListModes::UpDown} {
|
screens {app, settingsController.GetAppMenu(), CreateScreenList(), Screens::ScreenListModes::UpDown} {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,9 +43,14 @@ bool ApplicationList::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Screen> ApplicationList::CreateScreen(unsigned int screenNum) const {
|
std::unique_ptr<Screen> ApplicationList::CreateScreen(unsigned int screenNum) const {
|
||||||
std::array<Tile::Applications, appsPerScreen> apps;
|
std::array<Tile::Applications, appsPerScreen> pageApps;
|
||||||
|
|
||||||
for (int i = 0; i < appsPerScreen; i++) {
|
for (int i = 0; i < appsPerScreen; i++) {
|
||||||
apps[i] = applications[screenNum * appsPerScreen + i];
|
if (i + (screenNum * appsPerScreen) >= apps.size()) {
|
||||||
|
pageApps[i] = {"", Pinetime::Applications::Apps::None, false};
|
||||||
|
} else {
|
||||||
|
pageApps[i] = apps[i + (screenNum * appsPerScreen)];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_unique<Screens::Tile>(screenNum,
|
return std::make_unique<Screens::Tile>(screenNum,
|
||||||
|
@ -52,5 +60,5 @@ std::unique_ptr<Screen> ApplicationList::CreateScreen(unsigned int screenNum) co
|
||||||
batteryController,
|
batteryController,
|
||||||
bleController,
|
bleController,
|
||||||
dateTimeController,
|
dateTimeController,
|
||||||
apps);
|
pageApps);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,15 +2,12 @@
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include "displayapp/Apps.h"
|
||||||
#include "displayapp/screens/Screen.h"
|
#include "Screen.h"
|
||||||
#include "displayapp/screens/ScreenList.h"
|
#include "ScreenList.h"
|
||||||
#include "components/datetime/DateTimeController.h"
|
#include "displayapp/Controllers.h"
|
||||||
#include "components/settings/Settings.h"
|
#include "Symbols.h"
|
||||||
#include "components/battery/BatteryController.h"
|
#include "Tile.h"
|
||||||
#include "displayapp/screens/Symbols.h"
|
|
||||||
#include "displayapp/screens/Tile.h"
|
|
||||||
#include "displayapp/screens/Navigation.h"
|
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Applications {
|
namespace Applications {
|
||||||
|
@ -22,7 +19,8 @@ namespace Pinetime {
|
||||||
const Pinetime::Controllers::Battery& batteryController,
|
const Pinetime::Controllers::Battery& batteryController,
|
||||||
const Pinetime::Controllers::Ble& bleController,
|
const Pinetime::Controllers::Ble& bleController,
|
||||||
Controllers::DateTime& dateTimeController,
|
Controllers::DateTime& dateTimeController,
|
||||||
Pinetime::Controllers::FS& filesystem);
|
Pinetime::Controllers::FS& filesystem,
|
||||||
|
std::array<Tile::Applications, UserAppTypes::Count>&& apps);
|
||||||
~ApplicationList() override;
|
~ApplicationList() override;
|
||||||
bool OnTouchEvent(TouchEvents event) override;
|
bool OnTouchEvent(TouchEvents event) override;
|
||||||
|
|
||||||
|
@ -36,29 +34,13 @@ namespace Pinetime {
|
||||||
const Pinetime::Controllers::Ble& bleController;
|
const Pinetime::Controllers::Ble& bleController;
|
||||||
Controllers::DateTime& dateTimeController;
|
Controllers::DateTime& dateTimeController;
|
||||||
Pinetime::Controllers::FS& filesystem;
|
Pinetime::Controllers::FS& filesystem;
|
||||||
|
std::array<Tile::Applications, UserAppTypes::Count> apps;
|
||||||
|
|
||||||
static constexpr int appsPerScreen = 6;
|
static constexpr int appsPerScreen = 6;
|
||||||
|
|
||||||
// Increment this when more space is needed
|
// Increment this when more space is needed
|
||||||
static constexpr int nScreens = 2;
|
static constexpr int nScreens = (UserAppTypes::Count / appsPerScreen) + 1;
|
||||||
|
|
||||||
std::array<Tile::Applications, appsPerScreen * nScreens> applications {{
|
|
||||||
{Symbols::stopWatch, Apps::StopWatch, true},
|
|
||||||
{Symbols::clock, Apps::Alarm, true},
|
|
||||||
{Symbols::hourGlass, Apps::Timer, true},
|
|
||||||
{Symbols::shoe, Apps::Steps, true},
|
|
||||||
{Symbols::heartBeat, Apps::HeartRate, true},
|
|
||||||
{Symbols::music, Apps::Music, true},
|
|
||||||
|
|
||||||
{Symbols::paintbrush, Apps::Paint, true},
|
|
||||||
{Symbols::paddle, Apps::Paddle, true},
|
|
||||||
{"2", Apps::Twos, true},
|
|
||||||
{Symbols::drum, Apps::Metronome, true},
|
|
||||||
{Symbols::map, Apps::Navigation, Applications::Screens::Navigation::IsAvailable(filesystem)},
|
|
||||||
{Symbols::none, Apps::None, false},
|
|
||||||
|
|
||||||
// {"M", Apps::Motion},
|
|
||||||
}};
|
|
||||||
ScreenList<nScreens> screens;
|
ScreenList<nScreens> screens;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <lvgl/src/lv_core/lv_obj.h>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <components/heartrate/HeartRateController.h>
|
#include "displayapp/Controllers.h"
|
||||||
#include "displayapp/screens/Screen.h"
|
#include "displayapp/screens/Screen.h"
|
||||||
#include "components/datetime/DateTimeController.h"
|
#include "displayapp/Apps.h"
|
||||||
#include "components/ble/weather/WeatherService.h"
|
#include "Symbols.h"
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Controllers {
|
namespace Controllers {
|
||||||
|
@ -16,6 +15,10 @@ namespace Pinetime {
|
||||||
class Ble;
|
class Ble;
|
||||||
class NotificationManager;
|
class NotificationManager;
|
||||||
class MotionController;
|
class MotionController;
|
||||||
|
class DateTime;
|
||||||
|
class HeartRateController;
|
||||||
|
class WeatherService;
|
||||||
|
class FS;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Applications {
|
namespace Applications {
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include "displayapp/screens/Screen.h"
|
#include "displayapp/screens/Screen.h"
|
||||||
#include "systemtask/SystemTask.h"
|
#include "systemtask/SystemTask.h"
|
||||||
|
#include "Symbols.h"
|
||||||
#include <lvgl/src/lv_core/lv_style.h>
|
#include <lvgl/src/lv_core/lv_style.h>
|
||||||
#include <lvgl/src/lv_core/lv_obj.h>
|
#include <lvgl/src/lv_core/lv_obj.h>
|
||||||
|
|
||||||
|
@ -37,5 +38,15 @@ namespace Pinetime {
|
||||||
lv_task_t* taskRefresh;
|
lv_task_t* taskRefresh;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct AppTraits<Apps::HeartRate> {
|
||||||
|
static constexpr Apps app = Apps::HeartRate;
|
||||||
|
static constexpr const char* icon = Screens::Symbols::heartBeat;
|
||||||
|
|
||||||
|
static Screens::Screen* Create(AppControllers& controllers) {
|
||||||
|
return new Screens::HeartRate(controllers.heartRateController, *controllers.systemTask);
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
#include <algorithm> // std::fill
|
#include <algorithm> // std::fill
|
||||||
#include "displayapp/screens/Screen.h"
|
#include "displayapp/screens/Screen.h"
|
||||||
#include "components/motor/MotorController.h"
|
#include "components/motor/MotorController.h"
|
||||||
|
#include "Symbols.h"
|
||||||
|
#include <displayapp/Apps.h>
|
||||||
|
#include <displayapp/Controllers.h>
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Components {
|
namespace Components {
|
||||||
|
@ -35,5 +38,15 @@ namespace Pinetime {
|
||||||
uint8_t color = 2;
|
uint8_t color = 2;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct AppTraits<Apps::Paint> {
|
||||||
|
static constexpr Apps app = Apps::Paint;
|
||||||
|
static constexpr const char* icon = Screens::Symbols::paintbrush;
|
||||||
|
|
||||||
|
static Screens::Screen* Create(AppControllers& controllers) {
|
||||||
|
return new Screens::InfiniPaint(controllers.lvgl, controllers.motorController);
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "systemtask/SystemTask.h"
|
#include "systemtask/SystemTask.h"
|
||||||
#include "components/motor/MotorController.h"
|
#include "components/motor/MotorController.h"
|
||||||
#include "displayapp/screens/Screen.h"
|
#include "displayapp/screens/Screen.h"
|
||||||
|
#include "Symbols.h"
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Applications {
|
namespace Applications {
|
||||||
|
@ -36,5 +37,15 @@ namespace Pinetime {
|
||||||
lv_task_t* taskRefresh;
|
lv_task_t* taskRefresh;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct AppTraits<Apps::Metronome> {
|
||||||
|
static constexpr Apps app = Apps::Metronome;
|
||||||
|
static constexpr const char* icon = Screens::Symbols::drum;
|
||||||
|
|
||||||
|
static Screens::Screen* Create(AppControllers& controllers) {
|
||||||
|
return new Screens::Metronome(controllers.motorController, *controllers.systemTask);
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
#include <lvgl/src/lv_core/lv_style.h>
|
#include <lvgl/src/lv_core/lv_style.h>
|
||||||
#include <lvgl/src/lv_core/lv_obj.h>
|
#include <lvgl/src/lv_core/lv_obj.h>
|
||||||
#include <components/motion/MotionController.h>
|
#include <components/motion/MotionController.h>
|
||||||
|
#include "displayapp/Controllers.h"
|
||||||
|
#include "displayapp/Apps.h"
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Applications {
|
namespace Applications {
|
||||||
|
@ -30,5 +32,15 @@ namespace Pinetime {
|
||||||
lv_task_t* taskRefresh;
|
lv_task_t* taskRefresh;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct AppTraits<Apps::Motion> {
|
||||||
|
static constexpr Apps app = Apps::Motion;
|
||||||
|
static constexpr const char* icon = "M";
|
||||||
|
|
||||||
|
static Screens::Screen* Create(AppControllers& controllers) {
|
||||||
|
return new Screens::Motion(controllers.motionController);
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,9 @@
|
||||||
#include <lvgl/src/lv_core/lv_obj.h>
|
#include <lvgl/src/lv_core/lv_obj.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "displayapp/screens/Screen.h"
|
#include "displayapp/screens/Screen.h"
|
||||||
|
#include "displayapp/Apps.h"
|
||||||
|
#include "displayapp/Controllers.h"
|
||||||
|
#include "Symbols.h"
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Controllers {
|
namespace Controllers {
|
||||||
|
@ -82,5 +85,15 @@ namespace Pinetime {
|
||||||
/** Watchapp */
|
/** Watchapp */
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct AppTraits<Apps::Music> {
|
||||||
|
static constexpr Apps app = Apps::Music;
|
||||||
|
static constexpr const char* icon = Screens::Symbols::music;
|
||||||
|
|
||||||
|
static Screens::Screen* Create(AppControllers& controllers) {
|
||||||
|
return new Screens::Music(*controllers.musicService);
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -22,6 +22,9 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "displayapp/screens/Screen.h"
|
#include "displayapp/screens/Screen.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include "displayapp/Apps.h"
|
||||||
|
#include "displayapp/Controllers.h"
|
||||||
|
#include "Symbols.h"
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Controllers {
|
namespace Controllers {
|
||||||
|
@ -55,5 +58,15 @@ namespace Pinetime {
|
||||||
lv_task_t* taskRefresh;
|
lv_task_t* taskRefresh;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct AppTraits<Apps::Navigation> {
|
||||||
|
static constexpr Apps app = Apps::Navigation;
|
||||||
|
static constexpr const char* icon = Screens::Symbols::map;
|
||||||
|
|
||||||
|
static Screens::Screen* Create(AppControllers& controllers) {
|
||||||
|
return new Screens::Navigation(*controllers.navigationService);
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include "displayapp/screens/Screen.h"
|
#include "displayapp/screens/Screen.h"
|
||||||
|
#include "displayapp/Apps.h"
|
||||||
|
#include "displayapp/Controllers.h"
|
||||||
|
#include "Symbols.h"
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Components {
|
namespace Components {
|
||||||
|
@ -45,5 +48,15 @@ namespace Pinetime {
|
||||||
lv_task_t* taskRefresh;
|
lv_task_t* taskRefresh;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct AppTraits<Apps::Paddle> {
|
||||||
|
static constexpr Apps app = Apps::Paddle;
|
||||||
|
static constexpr const char* icon = Screens::Symbols::paddle;
|
||||||
|
|
||||||
|
static Screens::Screen* Create(AppControllers& controllers) {
|
||||||
|
return new Screens::Paddle(controllers.lvgl);
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,9 @@
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
#include "displayapp/screens/Screen.h"
|
#include "displayapp/screens/Screen.h"
|
||||||
#include <components/motion/MotionController.h>
|
#include <components/motion/MotionController.h>
|
||||||
|
#include "displayapp/Apps.h"
|
||||||
|
#include "displayapp/Controllers.h"
|
||||||
|
#include "Symbols.h"
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
|
|
||||||
|
@ -39,5 +42,15 @@ namespace Pinetime {
|
||||||
lv_task_t* taskRefresh;
|
lv_task_t* taskRefresh;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct AppTraits<Apps::Steps> {
|
||||||
|
static constexpr Apps app = Apps::Steps;
|
||||||
|
static constexpr const char* icon = Screens::Symbols::shoe;
|
||||||
|
|
||||||
|
static Screens::Screen* Create(AppControllers& controllers) {
|
||||||
|
return new Screens::Steps(controllers.motionController, controllers.settingsController);
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,50 +7,67 @@
|
||||||
#include "portmacro_cmsis.h"
|
#include "portmacro_cmsis.h"
|
||||||
|
|
||||||
#include "systemtask/SystemTask.h"
|
#include "systemtask/SystemTask.h"
|
||||||
|
#include "displayapp/Apps.h"
|
||||||
|
#include "displayapp/Controllers.h"
|
||||||
|
#include "Symbols.h"
|
||||||
|
|
||||||
namespace Pinetime::Applications::Screens {
|
namespace Pinetime {
|
||||||
|
namespace Applications {
|
||||||
|
namespace Screens {
|
||||||
|
|
||||||
enum class States { Init, Running, Halted };
|
enum class States { Init, Running, Halted };
|
||||||
|
|
||||||
struct TimeSeparated_t {
|
struct TimeSeparated_t {
|
||||||
int hours;
|
int hours;
|
||||||
int mins;
|
int mins;
|
||||||
int secs;
|
int secs;
|
||||||
int hundredths;
|
int hundredths;
|
||||||
};
|
};
|
||||||
|
|
||||||
class StopWatch : public Screen {
|
class StopWatch : public Screen {
|
||||||
public:
|
public:
|
||||||
explicit StopWatch(System::SystemTask& systemTask);
|
explicit StopWatch(System::SystemTask& systemTask);
|
||||||
~StopWatch() override;
|
~StopWatch() override;
|
||||||
void Refresh() override;
|
void Refresh() override;
|
||||||
|
|
||||||
void playPauseBtnEventHandler();
|
void playPauseBtnEventHandler();
|
||||||
void stopLapBtnEventHandler();
|
void stopLapBtnEventHandler();
|
||||||
bool OnButtonPushed() override;
|
bool OnButtonPushed() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetInterfacePaused();
|
void SetInterfacePaused();
|
||||||
void SetInterfaceRunning();
|
void SetInterfaceRunning();
|
||||||
void SetInterfaceStopped();
|
void SetInterfaceStopped();
|
||||||
|
|
||||||
void Reset();
|
void Reset();
|
||||||
void Start();
|
void Start();
|
||||||
void Pause();
|
void Pause();
|
||||||
|
|
||||||
Pinetime::System::SystemTask& systemTask;
|
Pinetime::System::SystemTask& systemTask;
|
||||||
States currentState = States::Init;
|
States currentState = States::Init;
|
||||||
TickType_t startTime;
|
TickType_t startTime;
|
||||||
TickType_t oldTimeElapsed = 0;
|
TickType_t oldTimeElapsed = 0;
|
||||||
TickType_t blinkTime = 0;
|
TickType_t blinkTime = 0;
|
||||||
static constexpr int maxLapCount = 20;
|
static constexpr int maxLapCount = 20;
|
||||||
TickType_t laps[maxLapCount + 1];
|
TickType_t laps[maxLapCount + 1];
|
||||||
static constexpr int displayedLaps = 2;
|
static constexpr int displayedLaps = 2;
|
||||||
int lapsDone = 0;
|
int lapsDone = 0;
|
||||||
lv_obj_t *time, *msecTime, *btnPlayPause, *btnStopLap, *txtPlayPause, *txtStopLap;
|
lv_obj_t *time, *msecTime, *btnPlayPause, *btnStopLap, *txtPlayPause, *txtStopLap;
|
||||||
lv_obj_t* lapText;
|
lv_obj_t* lapText;
|
||||||
bool isHoursLabelUpdated = false;
|
bool isHoursLabelUpdated = false;
|
||||||
|
|
||||||
lv_task_t* taskRefresh;
|
lv_task_t* taskRefresh;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct AppTraits<Apps::StopWatch> {
|
||||||
|
static constexpr Apps app = Apps::StopWatch;
|
||||||
|
static constexpr const char* icon = Screens::Symbols::stopWatch;
|
||||||
|
|
||||||
|
static Screens::Screen* Create(AppControllers& controllers) {
|
||||||
|
return new Screens::StopWatch(*controllers.systemTask);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#include "displayapp/screens/Tile.h"
|
#include "displayapp/screens/Tile.h"
|
||||||
#include "displayapp/DisplayApp.h"
|
|
||||||
#include "displayapp/screens/BatteryIcon.h"
|
#include "displayapp/screens/BatteryIcon.h"
|
||||||
#include "components/ble/BleController.h"
|
#include "components/ble/BleController.h"
|
||||||
#include "displayapp/InfiniTimeTheme.h"
|
#include "displayapp/InfiniTimeTheme.h"
|
||||||
|
|
|
@ -62,7 +62,7 @@ Timer::Timer(Controllers::Timer& timerController) : timer {timerController} {
|
||||||
txtPlayPause = lv_label_create(lv_scr_act(), nullptr);
|
txtPlayPause = lv_label_create(lv_scr_act(), nullptr);
|
||||||
lv_obj_align(txtPlayPause, btnPlayPause, LV_ALIGN_CENTER, 0, 0);
|
lv_obj_align(txtPlayPause, btnPlayPause, LV_ALIGN_CENTER, 0, 0);
|
||||||
|
|
||||||
if (timerController.IsRunning()) {
|
if (timer.IsRunning()) {
|
||||||
SetTimerRunning();
|
SetTimerRunning();
|
||||||
} else {
|
} else {
|
||||||
SetTimerStopped();
|
SetTimerStopped();
|
||||||
|
|
|
@ -8,38 +8,51 @@
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
|
|
||||||
#include "components/timer/Timer.h"
|
#include "components/timer/Timer.h"
|
||||||
|
#include "Symbols.h"
|
||||||
|
|
||||||
namespace Pinetime::Applications::Screens {
|
namespace Pinetime::Applications {
|
||||||
class Timer : public Screen {
|
namespace Screens {
|
||||||
public:
|
class Timer : public Screen {
|
||||||
Timer(Controllers::Timer& timerController);
|
public:
|
||||||
~Timer() override;
|
Timer(Controllers::Timer& timerController);
|
||||||
void Refresh() override;
|
~Timer() override;
|
||||||
void Reset();
|
void Refresh() override;
|
||||||
void ToggleRunning();
|
void Reset();
|
||||||
void ButtonPressed();
|
void ToggleRunning();
|
||||||
void MaskReset();
|
void ButtonPressed();
|
||||||
|
void MaskReset();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetTimerRunning();
|
void SetTimerRunning();
|
||||||
void SetTimerStopped();
|
void SetTimerStopped();
|
||||||
void UpdateMask();
|
void UpdateMask();
|
||||||
Controllers::Timer& timer;
|
Pinetime::Controllers::Timer& timer;
|
||||||
|
|
||||||
lv_obj_t* btnPlayPause;
|
lv_obj_t* btnPlayPause;
|
||||||
lv_obj_t* txtPlayPause;
|
lv_obj_t* txtPlayPause;
|
||||||
|
|
||||||
lv_obj_t* btnObjectMask;
|
lv_obj_t* btnObjectMask;
|
||||||
lv_obj_t* highlightObjectMask;
|
lv_obj_t* highlightObjectMask;
|
||||||
lv_objmask_mask_t* btnMask;
|
lv_objmask_mask_t* btnMask;
|
||||||
lv_objmask_mask_t* highlightMask;
|
lv_objmask_mask_t* highlightMask;
|
||||||
|
|
||||||
lv_task_t* taskRefresh;
|
lv_task_t* taskRefresh;
|
||||||
Widgets::Counter minuteCounter = Widgets::Counter(0, 59, jetbrains_mono_76);
|
Widgets::Counter minuteCounter = Widgets::Counter(0, 59, jetbrains_mono_76);
|
||||||
Widgets::Counter secondCounter = Widgets::Counter(0, 59, jetbrains_mono_76);
|
Widgets::Counter secondCounter = Widgets::Counter(0, 59, jetbrains_mono_76);
|
||||||
|
|
||||||
bool buttonPressing = false;
|
bool buttonPressing = false;
|
||||||
lv_coord_t maskPosition = 0;
|
lv_coord_t maskPosition = 0;
|
||||||
TickType_t pressTime = 0;
|
TickType_t pressTime = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct AppTraits<Apps::Timer> {
|
||||||
|
static constexpr Apps app = Apps::Timer;
|
||||||
|
static constexpr const char* icon = Screens::Symbols::hourGlass;
|
||||||
|
|
||||||
|
static Screens::Screen* Create(AppControllers& controllers) {
|
||||||
|
return new Screens::Timer(controllers.timer);
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <lvgl/src/lv_core/lv_obj.h>
|
#include "displayapp/Apps.h"
|
||||||
#include "displayapp/screens/Screen.h"
|
#include "displayapp/screens/Screen.h"
|
||||||
|
#include "displayapp/Controllers.h"
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Applications {
|
namespace Applications {
|
||||||
|
@ -35,5 +36,15 @@ namespace Pinetime {
|
||||||
bool placeNewTile();
|
bool placeNewTile();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct AppTraits<Apps::Twos> {
|
||||||
|
static constexpr Apps app = Apps::Twos;
|
||||||
|
static constexpr const char* icon = "2";
|
||||||
|
|
||||||
|
static Screens::Screen* Create(AppControllers& /*controllers*/) {
|
||||||
|
return new Screens::Twos();
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <components/ble/weather/WeatherService.h>
|
#include "components/ble/weather/WeatherService.h"
|
||||||
#include "Screen.h"
|
#include "Screen.h"
|
||||||
#include "ScreenList.h"
|
#include "ScreenList.h"
|
||||||
|
#include "displayapp/Apps.h"
|
||||||
|
#include "displayapp/Controllers.h"
|
||||||
|
#include "Symbols.h"
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Applications {
|
namespace Applications {
|
||||||
|
@ -41,5 +44,15 @@ namespace Pinetime {
|
||||||
std::unique_ptr<Screen> CreateScreenHumidity();
|
std::unique_ptr<Screen> CreateScreenHumidity();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct AppTraits<Apps::Weather> {
|
||||||
|
static constexpr Apps app = Apps::Weather;
|
||||||
|
static constexpr const char* icon = Screens::Symbols::sun;
|
||||||
|
|
||||||
|
static Screens::Screen* Create(AppControllers& controllers) {
|
||||||
|
return new Screens::Weather(controllers.displayApp, *controllers.weatherController);
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,8 +131,8 @@ void SpiMaster::OnEndEvent() {
|
||||||
if (s > 0) {
|
if (s > 0) {
|
||||||
auto currentSize = std::min((size_t) 255, s);
|
auto currentSize = std::min((size_t) 255, s);
|
||||||
PrepareTx(currentBufferAddr, currentSize);
|
PrepareTx(currentBufferAddr, currentSize);
|
||||||
currentBufferAddr += currentSize;
|
currentBufferAddr = currentBufferAddr + currentSize;
|
||||||
currentBufferSize -= currentSize;
|
currentBufferSize = currentBufferSize - currentSize;
|
||||||
|
|
||||||
spiBaseAddress->TASKS_START = 1;
|
spiBaseAddress->TASKS_START = 1;
|
||||||
} else {
|
} else {
|
||||||
|
@ -153,7 +153,7 @@ void SpiMaster::OnEndEvent() {
|
||||||
void SpiMaster::OnStartedEvent() {
|
void SpiMaster::OnStartedEvent() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpiMaster::PrepareTx(const volatile uint32_t bufferAddress, const volatile size_t size) {
|
void SpiMaster::PrepareTx(const uint32_t bufferAddress, const size_t size) {
|
||||||
spiBaseAddress->TXD.PTR = bufferAddress;
|
spiBaseAddress->TXD.PTR = bufferAddress;
|
||||||
spiBaseAddress->TXD.MAXCNT = size;
|
spiBaseAddress->TXD.MAXCNT = size;
|
||||||
spiBaseAddress->TXD.LIST = 0;
|
spiBaseAddress->TXD.LIST = 0;
|
||||||
|
@ -163,7 +163,7 @@ void SpiMaster::PrepareTx(const volatile uint32_t bufferAddress, const volatile
|
||||||
spiBaseAddress->EVENTS_END = 0;
|
spiBaseAddress->EVENTS_END = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpiMaster::PrepareRx(const volatile uint32_t bufferAddress, const volatile size_t size) {
|
void SpiMaster::PrepareRx(const uint32_t bufferAddress, const size_t size) {
|
||||||
spiBaseAddress->TXD.PTR = 0;
|
spiBaseAddress->TXD.PTR = 0;
|
||||||
spiBaseAddress->TXD.MAXCNT = 0;
|
spiBaseAddress->TXD.MAXCNT = 0;
|
||||||
spiBaseAddress->TXD.LIST = 0;
|
spiBaseAddress->TXD.LIST = 0;
|
||||||
|
@ -195,8 +195,8 @@ bool SpiMaster::Write(uint8_t pinCsn, const uint8_t* data, size_t size) {
|
||||||
|
|
||||||
auto currentSize = std::min((size_t) 255, (size_t) currentBufferSize);
|
auto currentSize = std::min((size_t) 255, (size_t) currentBufferSize);
|
||||||
PrepareTx(currentBufferAddr, currentSize);
|
PrepareTx(currentBufferAddr, currentSize);
|
||||||
currentBufferSize -= currentSize;
|
currentBufferSize = currentBufferSize - currentSize;
|
||||||
currentBufferAddr += currentSize;
|
currentBufferAddr = currentBufferAddr + currentSize;
|
||||||
spiBaseAddress->TASKS_START = 1;
|
spiBaseAddress->TASKS_START = 1;
|
||||||
|
|
||||||
if (size == 1) {
|
if (size == 1) {
|
||||||
|
|
|
@ -41,7 +41,7 @@ namespace {
|
||||||
// RRED (Reload Register Enable) is a bitfield of 8 bits. Each bit represent
|
// RRED (Reload Register Enable) is a bitfield of 8 bits. Each bit represent
|
||||||
// one of the eight reload registers available.
|
// one of the eight reload registers available.
|
||||||
// In this case, we enable only the first one.
|
// In this case, we enable only the first one.
|
||||||
NRF_WDT->RREN |= 1;
|
NRF_WDT->RREN = NRF_WDT->RREN | 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the reset reason provided by the POWER subsystem
|
/// Returns the reset reason provided by the POWER subsystem
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
193
src/resources/lv_img_conv.py
Executable 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())
|
|
@ -136,6 +136,9 @@ void SystemTask::Work() {
|
||||||
settingsController.Init();
|
settingsController.Init();
|
||||||
|
|
||||||
displayApp.Register(this);
|
displayApp.Register(this);
|
||||||
|
displayApp.Register(&nimbleController.weather());
|
||||||
|
displayApp.Register(&nimbleController.music());
|
||||||
|
displayApp.Register(&nimbleController.navigation());
|
||||||
displayApp.Start(bootError);
|
displayApp.Start(bootError);
|
||||||
|
|
||||||
heartRateSensor.Init();
|
heartRateSensor.Init();
|
||||||
|
|
Loading…
Reference in a new issue