From aa83fa2dcfa096e86290c9742e8498bec411732b Mon Sep 17 00:00:00 2001 From: Clemens von Molo Date: Tue, 5 Oct 2021 15:19:44 +0200 Subject: [PATCH 1/5] Add docs for app creation and code structure --- README.md | 3 +- doc/code/Apps.md | 70 +++++++++++++++++++++++++++++++++++++++++++++++ doc/code/Intro.md | 20 ++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 doc/code/Apps.md create mode 100644 doc/code/Intro.md diff --git a/README.md b/README.md index 9b9c3287..ee1fad00 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,8 @@ As of now, here is the list of achievements of this project: ## Documentation ### Develop - + - [Rough structure of the code](doc/code/Intro.md) + - [How to implement an application](doc/code/Apps.md) - [Generate the fonts and symbols](src/displayapp/fonts/README.md) - [Creating a stopwatch in Pinetime(article)](https://pankajraghav.com/2021/04/03/PINETIME-STOPCLOCK.html) diff --git a/doc/code/Apps.md b/doc/code/Apps.md new file mode 100644 index 00000000..b19d596d --- /dev/null +++ b/doc/code/Apps.md @@ -0,0 +1,70 @@ +# Apps +This page will teach you: +- what apps in InfiniTime are +- how to implement your own app + +## Theory +Apps are the things you can lauch from the app selection you get by swiping down, but also the app launcher itself or the clock. Settings are also implemented as apps. Most screens you see are their own app, except for apps that have multiple screens (settings launcher, app launcher). +Every app in InfiniTime is it's own class. An object of the class is created when the app is launched and destroyed when the user exits the app. They run inside the "displayapp" task (briefly discussed [here](./Intro.md)). They 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. + +## Interface +Every app class is has to be inside the namespace `Pinetime::Applications::Screens` and inherit from `Screen`. The constructor should have at least one parameter `DisplayApp* app`, which it needs for the constructor of its parent class Screen. Other parameters should be references to controllers that the app needs. A deconstructor 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 your app needs to be updated continuously, yo can do so by adding a `Refresh()` function to your class and calling `lv_task_create` inside the constructor. + +## Creating your own app +A minimal app could look like this:
+MyApp.h: +```cpp +#pragma once + +#include +#include "displayapp/screens/Screen.h" + +namespace PineTime { + namespace Applications { + namespace Screens { + class MyApp : public Screen { + public: + MyApp(DisplayApp* app); + ~MyApp() override; + } + } + } +} +``` + +MyApp.cpp: +```cpp +#include "MyApp.h" +#include "displayapp/DisplayApp.h" + +using namespace Pinetime::Applications::Screens; + +MyApp::MyApp(DisplayApp* app) : Screen(app) { + 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, 30, 60); + lv_obj_set_width(container1, LV_HOR_RES - 50); + lv_obj_set_height(container1, LV_VER_RES - 60); + lv_cont_set_layout(container1, LV_LAYOUT_COLUMN_LEFT); + + lv_obj_t * title = lv_label_create(lv_scr_act(), NULL); + lv_label_set_text_static(title,"My test application"); + lv_label_set_align(title, LV_LABEL_ALIGN_CENTER); + lv_obj_align(title, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 15, 15); +} + +MyApp::~MyApp() { + lv_obj_clean(lv_scr_act()); +} +``` +Both of these files should be in displayapp/screens/ or displayapp/screens/settings if it's a setting app. + +Now we have our very own app, but InfiniTime does not know about it yet. The first step is to include it in the compilation by adding it to CMakeLists.txt . The next step to making it launchable is to give your app an id. To do this, add an entry in the enum class `Pinetime::Applications::Apps`. Name this entry after your app. Add `#include "displayapp/screens/MyApp.h"` to the file displayapp/DisplayApp.cpp . Now, go to the function `DisplayApp::LoadApp` and add another case to the switch statement. 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 you want your app to be launched from the regular app launcher, go to displayapp/screens/ApplicationList.cpp. Add your app to one of the `CreateScreen` functions, or add another `CreateScreen` function if there are no empty spaces for your app.
+If your app is a setting, do the same procedure in displayapp/screens/settings/Settings.cpp . + +You should now be able to [build](../buildAndProgram.md) the firmware and flash it to your PineTime. Yay! diff --git a/doc/code/Intro.md b/doc/code/Intro.md new file mode 100644 index 00000000..3c7fee04 --- /dev/null +++ b/doc/code/Intro.md @@ -0,0 +1,20 @@ +# Introduction to the code +This page is meant to guide you through the source code, so you can find the relevant files for what you're working on. + +## FreeRTOS +Infinitime is based on FreeRTOS, a real-time operating system. FreeRTOS provides several quality of life abstractions (for example easy software timers) and most importantly supports multiple tasks. You can sort of imagine them as processes or threads on a full operating system. The main "process" creates at least one task and then starts the FreeRTOS task scheduler. This main "process" is the standard main() function inside main.cpp . The task scheduler is responsible for giving every task enough cpu time. As there is only one core on the SoC of the PineTime, real concurrency is impossible and the scheduler has to swap tasks in and out to emulate it. + +### Tasks +Tasks are created by calling `xTaskCreate` and passing a function with the signature `void functionName(void*)`. For more info on task creation see the [FreeRTOS Documentation](https://www.freertos.org/a00125.html). +In our case, main calls `systemTask.Start()`, which creates the **"MAIN" task**. The function running inside that task is `SystemTask::Work()`. Both functions are located inside systemtask/SystemTask.cpp . `SystemTask::Work()` initializes all the driver and controller objects. It also starts the **task "displayapp"**, which is responsible for launching and running apps, controlling the screen and handling touch events (or forwarding them to the active app). You can find the "displayapp" task inside displayapp/DisplayApp.cpp . +There are also other tasks that are responsible for Bluetooth ("ll" and "ble" inside libs/mynewt-nimble/porting/npl/freertos/src/nimble_port_freertos.c) and periodic tasks like heartrate measurements (heartratetask/HeartRateTask.cpp). + +## Controllers +Controllers in InfiniTime are singleton objects that can provide access to certain resources to apps. Some of them interface with drivers, others are the driver for the resource. The resources provided don't have to be hardware-based. They are declared in main.cpp and initialized in systemtask/SystemTask.cpp . Some controllers can be passed by reference to apps that need access to the resource (for example vibration motor). +They reside in components inside their own subfolder. + +## Apps +For more detail see the [Apps page](./Apps.md) + +## Bluetooth +Header files with short documentation for the functions are inside libs/mynewt-nimble/nimble/host/include/host/ . From b2141a9050c02bff6acd09b2f8dd824b2b5f8c34 Mon Sep 17 00:00:00 2001 From: Clemens von Molo Date: Tue, 5 Oct 2021 18:55:02 +0200 Subject: [PATCH 2/5] advise against unecessary additional tasks in docs --- doc/code/Intro.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/code/Intro.md b/doc/code/Intro.md index 3c7fee04..b7371f0b 100644 --- a/doc/code/Intro.md +++ b/doc/code/Intro.md @@ -6,8 +6,9 @@ Infinitime is based on FreeRTOS, a real-time operating system. FreeRTOS provides ### Tasks Tasks are created by calling `xTaskCreate` and passing a function with the signature `void functionName(void*)`. For more info on task creation see the [FreeRTOS Documentation](https://www.freertos.org/a00125.html). -In our case, main calls `systemTask.Start()`, which creates the **"MAIN" task**. The function running inside that task is `SystemTask::Work()`. Both functions are located inside systemtask/SystemTask.cpp . `SystemTask::Work()` initializes all the driver and controller objects. It also starts the **task "displayapp"**, which is responsible for launching and running apps, controlling the screen and handling touch events (or forwarding them to the active app). You can find the "displayapp" task inside displayapp/DisplayApp.cpp . -There are also other tasks that are responsible for Bluetooth ("ll" and "ble" inside libs/mynewt-nimble/porting/npl/freertos/src/nimble_port_freertos.c) and periodic tasks like heartrate measurements (heartratetask/HeartRateTask.cpp). +In our case, main calls `systemTask.Start()`, which creates the **"MAIN" task**. The function running inside that task is `SystemTask::Work()`. You may also see this task being referred to as the **work task**. Both functions are located inside systemtask/SystemTask.cpp . `SystemTask::Work()` initializes all the driver and controller objects. It also starts the **task "displayapp"**, which is responsible for launching and running apps, controlling the screen and handling touch events (or forwarding them to the active app). You can find the "displayapp" task inside displayapp/DisplayApp.cpp . +There are also other tasks that are responsible for Bluetooth ("ll" and "ble" inside libs/mynewt-nimble/porting/npl/freertos/src/nimble_port_freertos.c) and periodic tasks like heartrate measurements (heartratetask/HeartRateTask.cpp).
+While it is possible for you to create your own task when you need it, it is recommended to just add functionality to `SystemTask::Work()` if possible. If you absolutely need to create another task, try to guess how much stack space (in words/4-byte packets) it will need instead of just typing in a large-ish number. You can use the define `configMINIMAL_STACK_SIZE` which is currently set to 120 words. ## Controllers Controllers in InfiniTime are singleton objects that can provide access to certain resources to apps. Some of them interface with drivers, others are the driver for the resource. The resources provided don't have to be hardware-based. They are declared in main.cpp and initialized in systemtask/SystemTask.cpp . Some controllers can be passed by reference to apps that need access to the resource (for example vibration motor). From f1aae6af49c212d98afeb6e86615c6ee90ae771c Mon Sep 17 00:00:00 2001 From: Clemens von Molo Date: Wed, 6 Oct 2021 14:30:16 +0200 Subject: [PATCH 3/5] multiple improvements to code docs --- doc/code/Apps.md | 62 ++++++++++++++++++++++++----------------------- doc/code/Intro.md | 14 +++++------ 2 files changed, 39 insertions(+), 37 deletions(-) diff --git a/doc/code/Apps.md b/doc/code/Apps.md index b19d596d..6b4c383f 100644 --- a/doc/code/Apps.md +++ b/doc/code/Apps.md @@ -4,11 +4,21 @@ This page will teach you: - how to implement your own app ## Theory -Apps are the things you can lauch from the app selection you get by swiping down, but also the app launcher itself or the clock. Settings are also implemented as apps. Most screens you see are their own app, except for apps that have multiple screens (settings launcher, app launcher). +Apps are the things you can launch from the app selection you get by swiping up. +At the moment, settings and even the app launcher itself or the clock are implemented very similarly, this might change in the future though. Every app in InfiniTime is it's own class. An object of the class is created when the app is launched and destroyed when the user exits the app. They run inside the "displayapp" task (briefly discussed [here](./Intro.md)). They 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. ## Interface -Every app class is has to be inside the namespace `Pinetime::Applications::Screens` and inherit from `Screen`. The constructor should have at least one parameter `DisplayApp* app`, which it needs for the constructor of its parent class Screen. Other parameters should be references to controllers that the app needs. A deconstructor 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 your app needs to be updated continuously, yo can do so by adding a `Refresh()` function to your class and calling `lv_task_create` inside the constructor. +Every app class has to be inside the namespace `Pinetime::Applications::Screens` and inherit from `Screen`. The constructor should have at least one parameter `DisplayApp* app`, which it needs for the constructor of its parent class Screen. Other parameters should be references to controllers that the app needs. A deconstructor 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 are doing things. + +### Continuous updating +If your app needs to be updated continuously, yo can do so by adding a `Refresh()` function to your class and calling `lv_task_create` inside the constructor. An example call could look like this:
+`taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this);`
+With `taskRefresh` being a member variable of your class and of type `lv_task_t*`. Remember to delete the task again using `lv_task_del`. The function `RefreshTaskCallback` is inherited from screen and just calls your `Refresh` function. + +### Apps with multiple screens +InfiniTime provides a mini-library in [displayapp/screens/ScreenList.h](/src/displayapp/screens/ScreenList.h) which makes it relatively easy to add multiple screens to your app. To use it, #include it in the header file of your app and add a ScreenList member to your class. The template argument should be the number of screens you need. You will also need to add `CreateScreen` functions that return `std::unique_ptr` to your class, one for every screen you have. There are still some things left to to that I won't cover here. To figure them out, have a look at the "apps" ApplicationList, Settings and SystemInfo. + ## Creating your own app A minimal app could look like this:
@@ -16,19 +26,19 @@ MyApp.h: ```cpp #pragma once -#include #include "displayapp/screens/Screen.h" +#include namespace PineTime { - namespace Applications { - namespace Screens { - class MyApp : public Screen { - public: - MyApp(DisplayApp* app); - ~MyApp() override; - } - } + namespace Applications { + namespace Screens { + class MyApp : public Screen { + public: + MyApp(DisplayApp* app); + ~MyApp() override; + } } + } } ``` @@ -40,31 +50,23 @@ MyApp.cpp: using namespace Pinetime::Applications::Screens; MyApp::MyApp(DisplayApp* app) : Screen(app) { - 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, 30, 60); - lv_obj_set_width(container1, LV_HOR_RES - 50); - lv_obj_set_height(container1, LV_VER_RES - 60); - lv_cont_set_layout(container1, LV_LAYOUT_COLUMN_LEFT); - - lv_obj_t * title = lv_label_create(lv_scr_act(), NULL); - lv_label_set_text_static(title,"My test application"); - 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 * title = lv_label_create(lv_scr_act(), NULL); + lv_label_set_text_static(title,"My test application"); + lv_label_set_align(title, LV_LABEL_ALIGN_CENTER); + lv_obj_align(title, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); } MyApp::~MyApp() { - lv_obj_clean(lv_scr_act()); + lv_obj_clean(lv_scr_act()); } ``` -Both of these files should be in displayapp/screens/ or displayapp/screens/settings if it's a setting app. +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. The first step is to include it in the compilation by adding it to CMakeLists.txt . The next step to making it launchable is to give your app an id. To do this, add an entry in the enum class `Pinetime::Applications::Apps`. Name this entry after your app. Add `#include "displayapp/screens/MyApp.h"` to the file displayapp/DisplayApp.cpp . Now, go to the function `DisplayApp::LoadApp` and add another case to the switch statement. 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.
+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) 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. 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). Now, go to the function `DisplayApp::LoadApp` and add another case to the switch statement. 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 you want your app to be launched from the regular app launcher, go to displayapp/screens/ApplicationList.cpp. Add your app to one of the `CreateScreen` functions, or add another `CreateScreen` function if there are no empty spaces for your app.
-If your app is a setting, do the same procedure in displayapp/screens/settings/Settings.cpp . +If you want your app to be launched from the regular app launcher, go to [displayapp/screens/ApplicationList.cpp](/src/displayapp/screens/ApplicationList.cpp). Add your app to one of the `CreateScreen` functions, or add another `CreateScreen` function if there are no empty spaces for your app.
+If your app is a setting, do the same procedure in [displayapp/screens/settings/Settings.cpp](/src/displayapp/screens/settings/Settings.cpp). You should now be able to [build](../buildAndProgram.md) the firmware and flash it to your PineTime. Yay! diff --git a/doc/code/Intro.md b/doc/code/Intro.md index b7371f0b..87253516 100644 --- a/doc/code/Intro.md +++ b/doc/code/Intro.md @@ -2,20 +2,20 @@ This page is meant to guide you through the source code, so you can find the relevant files for what you're working on. ## FreeRTOS -Infinitime is based on FreeRTOS, a real-time operating system. FreeRTOS provides several quality of life abstractions (for example easy software timers) and most importantly supports multiple tasks. You can sort of imagine them as processes or threads on a full operating system. The main "process" creates at least one task and then starts the FreeRTOS task scheduler. This main "process" is the standard main() function inside main.cpp . The task scheduler is responsible for giving every task enough cpu time. As there is only one core on the SoC of the PineTime, real concurrency is impossible and the scheduler has to swap tasks in and out to emulate it. +Infinitime is based on FreeRTOS, a real-time operating system. FreeRTOS provides several quality of life abstractions (for example easy software timers) and most importantly supports multiple tasks. If you want to read up on real-time operating systems, you can look [here](https://www.freertos.org/implementation/a00002.html) and [here](https://www.freertos.org/features.html). The main "process" creates at least one task and then starts the FreeRTOS task scheduler. This main "process" is the standard main() function inside [main.cpp](/src/main.cpp). The task scheduler is responsible for giving every task enough cpu time. As there is only one core on the SoC of the PineTime, real concurrency is impossible and the scheduler has to swap tasks in and out to emulate it. ### Tasks Tasks are created by calling `xTaskCreate` and passing a function with the signature `void functionName(void*)`. For more info on task creation see the [FreeRTOS Documentation](https://www.freertos.org/a00125.html). -In our case, main calls `systemTask.Start()`, which creates the **"MAIN" task**. The function running inside that task is `SystemTask::Work()`. You may also see this task being referred to as the **work task**. Both functions are located inside systemtask/SystemTask.cpp . `SystemTask::Work()` initializes all the driver and controller objects. It also starts the **task "displayapp"**, which is responsible for launching and running apps, controlling the screen and handling touch events (or forwarding them to the active app). You can find the "displayapp" task inside displayapp/DisplayApp.cpp . -There are also other tasks that are responsible for Bluetooth ("ll" and "ble" inside libs/mynewt-nimble/porting/npl/freertos/src/nimble_port_freertos.c) and periodic tasks like heartrate measurements (heartratetask/HeartRateTask.cpp).
-While it is possible for you to create your own task when you need it, it is recommended to just add functionality to `SystemTask::Work()` if possible. If you absolutely need to create another task, try to guess how much stack space (in words/4-byte packets) it will need instead of just typing in a large-ish number. You can use the define `configMINIMAL_STACK_SIZE` which is currently set to 120 words. +In our case, main calls `systemTask.Start()`, which creates the **"MAIN" task**. The function running inside that task is `SystemTask::Work()`. You may also see this task being referred to as the **work task**. Both functions are located inside [systemtask/SystemTask.cpp](/src/systemtask/SystemTask.cpp). `SystemTask::Work()` initializes all the driver and controller objects. It also starts the **task "displayapp"**, which is responsible for launching and running apps, controlling the screen and handling touch events (or forwarding them to the active app). You can find the "displayapp" task inside [displayapp/DisplayApp.cpp](/src/displayapp/DisplayApp.cpp). +There are also other tasks that are responsible for Bluetooth ("ll" and "ble" inside [libs/mynewt-nimble/porting/npl/freertos/src/nimble_port_freertos.c](/src/libs/mynewt-nimble/porting/npl/freertos/src/nimble_port_freertos.c)) and periodic tasks like heartrate measurements ([heartratetask/HeartRateTask.cpp](/src/heartratetask/HeartRateTask.cpp)).
+While it is possible for you to create your own task when you need it, it is recommended to just add functionality to `SystemTask::Work()` if possible. If you absolutely need to create another task, try to guess how much [stack space](https://www.freertos.org/FAQMem.html#StackSize) (in words/4-byte packets) it will need instead of just typing in a large-ish number. You can use the define `configMINIMAL_STACK_SIZE` which is currently set to 120 words. ## Controllers -Controllers in InfiniTime are singleton objects that can provide access to certain resources to apps. Some of them interface with drivers, others are the driver for the resource. The resources provided don't have to be hardware-based. They are declared in main.cpp and initialized in systemtask/SystemTask.cpp . Some controllers can be passed by reference to apps that need access to the resource (for example vibration motor). -They reside in components inside their own subfolder. +Controllers in InfiniTime are singleton objects that can provide access to certain resources to apps. Some of them interface with drivers, others are the driver for the resource. The resources provided don't have to be hardware-based. They are declared in main.cpp and initialized in [systemtask/SystemTask.cpp](/src/systemtask/SystemTask.cpp). Some controllers can be passed by reference to apps that need access to the resource (for example vibration motor). +They reside in [components/](/src/components/) inside their own subfolder. ## Apps For more detail see the [Apps page](./Apps.md) ## Bluetooth -Header files with short documentation for the functions are inside libs/mynewt-nimble/nimble/host/include/host/ . +Header files with short documentation for the functions are inside [libs/mynewt-nimble/nimble/host/include/host/](/src/libs/mynewt-nimble/nimble/host/include/host/). From 884c89c61cf6568ddbc79346110494aacae1176b Mon Sep 17 00:00:00 2001 From: Clemens von Molo Date: Wed, 6 Oct 2021 18:29:52 +0200 Subject: [PATCH 4/5] minor corrections and notice about ui guidelines in docs --- doc/code/Apps.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/code/Apps.md b/doc/code/Apps.md index 6b4c383f..41c945a8 100644 --- a/doc/code/Apps.md +++ b/doc/code/Apps.md @@ -6,13 +6,13 @@ This page will teach you: ## Theory Apps are the things you can launch from the app selection you get by swiping up. At the moment, settings and even the app launcher itself or the clock are implemented very similarly, this might change in the future though. -Every app in InfiniTime is it's own class. An object of the class is created when the app is launched and destroyed when the user exits the app. They run inside the "displayapp" task (briefly discussed [here](./Intro.md)). They 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. +Every app in InfiniTime is it's own class. An instance of the class is created when the app is launched and destroyed when the user exits the app. They run inside the "displayapp" task (briefly discussed [here](./Intro.md)). They 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. ## Interface Every app class has to be inside the namespace `Pinetime::Applications::Screens` and inherit from `Screen`. The constructor should have at least one parameter `DisplayApp* app`, which it needs for the constructor of its parent class Screen. Other parameters should be references to controllers that the app needs. A deconstructor 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 are doing things. ### Continuous updating -If your app needs to be updated continuously, yo can do so by adding a `Refresh()` function to your class and calling `lv_task_create` inside the constructor. An example call could look like this:
+If your app needs to be updated continuously, yo can do so by overriding the `Refresh()` function in your class and calling `lv_task_create` inside the constructor. An example call could look like this:
`taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this);`
With `taskRefresh` being a member variable of your class and of type `lv_task_t*`. Remember to delete the task again using `lv_task_del`. The function `RefreshTaskCallback` is inherited from screen and just calls your `Refresh` function. @@ -50,8 +50,8 @@ MyApp.cpp: using namespace Pinetime::Applications::Screens; MyApp::MyApp(DisplayApp* app) : Screen(app) { - lv_obj_t * title = lv_label_create(lv_scr_act(), NULL); - lv_label_set_text_static(title,"My test application"); + lv_obj_t* title = lv_label_create(lv_scr_act(), NULL); + lv_label_set_text_static(title, "My test application"); lv_label_set_align(title, LV_LABEL_ALIGN_CENTER); lv_obj_align(title, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); } @@ -70,3 +70,5 @@ If you want your app to be launched from the regular app launcher, go to [displa If your app is a setting, do the same procedure in [displayapp/screens/settings/Settings.cpp](/src/displayapp/screens/settings/Settings.cpp). You should now be able to [build](../buildAndProgram.md) the firmware and flash it to your PineTime. Yay! + +Please remember to pay attention to the [UI guidelines](../ui_guidelines.md) when designing an app that you want to include in mainstream InfiniTime. From c3cc83ae17d7cbdd2707df12e89a600d76e5836d Mon Sep 17 00:00:00 2001 From: Clemens von Molo Date: Fri, 8 Oct 2021 14:59:45 +0200 Subject: [PATCH 5/5] More reasonable line lengths in Apps.md and Intro.md --- doc/code/Apps.md | 56 +++++++++++++++++++++++++++++++++++++---------- doc/code/Intro.md | 33 +++++++++++++++++++++++----- 2 files changed, 71 insertions(+), 18 deletions(-) diff --git a/doc/code/Apps.md b/doc/code/Apps.md index 41c945a8..b3bab042 100644 --- a/doc/code/Apps.md +++ b/doc/code/Apps.md @@ -6,18 +6,40 @@ This page will teach you: ## Theory Apps are the things you can launch from the app selection you get by swiping up. At the moment, settings and even the app launcher itself or the clock are implemented very similarly, this might change in the future though. -Every app in InfiniTime is it's own class. An instance of the class is created when the app is launched and destroyed when the user exits the app. They run inside the "displayapp" task (briefly discussed [here](./Intro.md)). They 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. +Every app in InfiniTime is it's own class. +An instance of the class is created when the app is launched and destroyed when the user exits the app. +They run inside the "displayapp" task (briefly discussed [here](./Intro.md)). +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. ## Interface -Every app class has to be inside the namespace `Pinetime::Applications::Screens` and inherit from `Screen`. The constructor should have at least one parameter `DisplayApp* app`, which it needs for the constructor of its parent class Screen. Other parameters should be references to controllers that the app needs. A deconstructor 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 are doing things. +Every app class has to be inside the namespace `Pinetime::Applications::Screens` and inherit from `Screen`. +The constructor should have at least one parameter `DisplayApp* app`, which it needs for the constructor of its parent class Screen. +Other parameters should be references to controllers that the app needs. +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 are doing things. ### Continuous updating -If your app needs to be updated continuously, yo can do so by overriding the `Refresh()` function in your class and calling `lv_task_create` inside the constructor. An example call could look like this:
+If your app needs to be updated continuously, yo can do so by overriding the `Refresh()` function in your class +and calling `lv_task_create` inside the constructor. +An example call could look like this:
`taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this);`
-With `taskRefresh` being a member variable of your class and of type `lv_task_t*`. Remember to delete the task again using `lv_task_del`. The function `RefreshTaskCallback` is inherited from screen and just calls your `Refresh` function. +With `taskRefresh` being a member variable of your class and of type `lv_task_t*`. +Remember to delete the task again using `lv_task_del`. +The function `RefreshTaskCallback` is inherited from screen and just calls your `Refresh` function. ### Apps with multiple screens -InfiniTime provides a mini-library in [displayapp/screens/ScreenList.h](/src/displayapp/screens/ScreenList.h) which makes it relatively easy to add multiple screens to your app. To use it, #include it in the header file of your app and add a ScreenList member to your class. The template argument should be the number of screens you need. You will also need to add `CreateScreen` functions that return `std::unique_ptr` to your class, one for every screen you have. There are still some things left to to that I won't cover here. To figure them out, have a look at the "apps" ApplicationList, Settings and SystemInfo. +InfiniTime provides a mini-library in [displayapp/screens/ScreenList.h](/src/displayapp/screens/ScreenList.h) +which makes it relatively easy to add multiple screens to your app. +To use it, #include it in the header file of your app and add a ScreenList member to your class. +The template argument should be the number of screens you need. +You will also need to add `CreateScreen` functions that return `std::unique_ptr` +to your class, one for every screen you have. +There are still some things left to to that I won't cover here. +To figure them out, have a look at the "apps" ApplicationList, Settings and SystemInfo. ## Creating your own app @@ -60,15 +82,25 @@ MyApp::~MyApp() { lv_obj_clean(lv_scr_act()); } ``` -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. +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. 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). -The next step to making it launchable 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)). -Name this entry after your app. Add `#include "displayapp/screens/MyApp.h"` to the file [displayapp/DisplayApp.cpp](/src/displayapp/DisplayApp.cpp). Now, go to the function `DisplayApp::LoadApp` and add another case to the switch statement. 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. +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) +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. +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). +Now, go to the function `DisplayApp::LoadApp` and add another case to the switch statement. +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 you want your app to be launched from the regular app launcher, go to [displayapp/screens/ApplicationList.cpp](/src/displayapp/screens/ApplicationList.cpp). Add your app to one of the `CreateScreen` functions, or add another `CreateScreen` function if there are no empty spaces for your app.
+If you want your app to be launched from the regular app launcher, go to [displayapp/screens/ApplicationList.cpp](/src/displayapp/screens/ApplicationList.cpp). +Add your app to one of the `CreateScreen` functions, or add another `CreateScreen` function if there are no empty spaces for your app.
If your app is a setting, do the same procedure in [displayapp/screens/settings/Settings.cpp](/src/displayapp/screens/settings/Settings.cpp). -You should now be able to [build](../buildAndProgram.md) the firmware and flash it to your PineTime. Yay! +You should now be able to [build](../buildAndProgram.md) the firmware +and flash it to your PineTime. Yay! -Please remember to pay attention to the [UI guidelines](../ui_guidelines.md) when designing an app that you want to include in mainstream InfiniTime. +Please remember to pay attention to the [UI guidelines](../ui_guidelines.md) +when designing an app that you want to include in mainstream InfiniTime. diff --git a/doc/code/Intro.md b/doc/code/Intro.md index 87253516..762102fe 100644 --- a/doc/code/Intro.md +++ b/doc/code/Intro.md @@ -2,16 +2,37 @@ This page is meant to guide you through the source code, so you can find the relevant files for what you're working on. ## FreeRTOS -Infinitime is based on FreeRTOS, a real-time operating system. FreeRTOS provides several quality of life abstractions (for example easy software timers) and most importantly supports multiple tasks. If you want to read up on real-time operating systems, you can look [here](https://www.freertos.org/implementation/a00002.html) and [here](https://www.freertos.org/features.html). The main "process" creates at least one task and then starts the FreeRTOS task scheduler. This main "process" is the standard main() function inside [main.cpp](/src/main.cpp). The task scheduler is responsible for giving every task enough cpu time. As there is only one core on the SoC of the PineTime, real concurrency is impossible and the scheduler has to swap tasks in and out to emulate it. +Infinitime is based on FreeRTOS, a real-time operating system. +FreeRTOS provides several quality of life abstractions (for example easy software timers) +and most importantly supports multiple tasks. +If you want to read up on real-time operating systems, you can look [here](https://www.freertos.org/implementation/a00002.html) and [here](https://www.freertos.org/features.html). +The main "process" creates at least one task and then starts the FreeRTOS task scheduler. +This main "process" is the standard main() function inside [main.cpp](/src/main.cpp). +The task scheduler is responsible for giving every task enough cpu time. +As there is only one core on the SoC of the PineTime, real concurrency is impossible and the scheduler has to swap tasks in and out to emulate it. ### Tasks -Tasks are created by calling `xTaskCreate` and passing a function with the signature `void functionName(void*)`. For more info on task creation see the [FreeRTOS Documentation](https://www.freertos.org/a00125.html). -In our case, main calls `systemTask.Start()`, which creates the **"MAIN" task**. The function running inside that task is `SystemTask::Work()`. You may also see this task being referred to as the **work task**. Both functions are located inside [systemtask/SystemTask.cpp](/src/systemtask/SystemTask.cpp). `SystemTask::Work()` initializes all the driver and controller objects. It also starts the **task "displayapp"**, which is responsible for launching and running apps, controlling the screen and handling touch events (or forwarding them to the active app). You can find the "displayapp" task inside [displayapp/DisplayApp.cpp](/src/displayapp/DisplayApp.cpp). -There are also other tasks that are responsible for Bluetooth ("ll" and "ble" inside [libs/mynewt-nimble/porting/npl/freertos/src/nimble_port_freertos.c](/src/libs/mynewt-nimble/porting/npl/freertos/src/nimble_port_freertos.c)) and periodic tasks like heartrate measurements ([heartratetask/HeartRateTask.cpp](/src/heartratetask/HeartRateTask.cpp)).
-While it is possible for you to create your own task when you need it, it is recommended to just add functionality to `SystemTask::Work()` if possible. If you absolutely need to create another task, try to guess how much [stack space](https://www.freertos.org/FAQMem.html#StackSize) (in words/4-byte packets) it will need instead of just typing in a large-ish number. You can use the define `configMINIMAL_STACK_SIZE` which is currently set to 120 words. +Tasks are created by calling `xTaskCreate` and passing a function with the signature `void functionName(void*)`. +For more info on task creation see the [FreeRTOS Documentation](https://www.freertos.org/a00125.html). +In our case, main calls `systemTask.Start()`, which creates the **"MAIN" task**. +The function running inside that task is `SystemTask::Work()`. +You may also see this task being referred to as the **work task**. +Both functions are located inside [systemtask/SystemTask.cpp](/src/systemtask/SystemTask.cpp). `SystemTask::Work()` initializes all the driver and controller objects. +It also starts the **task "displayapp"**, which is responsible for launching and running apps, controlling the screen and handling touch events (or forwarding them to the active app). +You can find the "displayapp" task inside [displayapp/DisplayApp.cpp](/src/displayapp/DisplayApp.cpp). +There are also other tasks that are responsible for Bluetooth ("ll" and "ble" inside [libs/mynewt-nimble/porting/npl/freertos/src/nimble_port_freertos.c](/src/libs/mynewt-nimble/porting/npl/freertos/src/nimble_port_freertos.c)) +and periodic tasks like heartrate measurements ([heartratetask/HeartRateTask.cpp](/src/heartratetask/HeartRateTask.cpp)).
+While it is possible for you to create your own task when you need it, it is recommended to just add functionality to `SystemTask::Work()` if possible. +If you absolutely need to create another task, try to guess how much [stack space](https://www.freertos.org/FAQMem.html#StackSize) (in words/4-byte packets) +it will need instead of just typing in a large-ish number. +You can use the define `configMINIMAL_STACK_SIZE` which is currently set to 120 words. ## Controllers -Controllers in InfiniTime are singleton objects that can provide access to certain resources to apps. Some of them interface with drivers, others are the driver for the resource. The resources provided don't have to be hardware-based. They are declared in main.cpp and initialized in [systemtask/SystemTask.cpp](/src/systemtask/SystemTask.cpp). Some controllers can be passed by reference to apps that need access to the resource (for example vibration motor). +Controllers in InfiniTime are singleton objects that can provide access to certain resources to apps. +Some of them interface with drivers, others are the driver for the resource. +The resources provided don't have to be hardware-based. +They are declared in main.cpp and initialized in [systemtask/SystemTask.cpp](/src/systemtask/SystemTask.cpp). +Some controllers can be passed by reference to apps that need access to the resource (for example vibration motor). They reside in [components/](/src/components/) inside their own subfolder. ## Apps