diff options
author | ConfuSomu | 2021-03-01 23:07:55 -0500 |
---|---|---|
committer | ConfuSomu | 2021-03-01 23:07:55 -0500 |
commit | 888675141118cb1cabb774cbe0a598ab9b5ca19d (patch) | |
tree | 6bd342169c4cd84365981ae071369f931f4f62f1 | |
parent | 06d5f492eca8e57bc8ce7479ec6149fbc6a15d08 (diff) | |
parent | b9971cb3b7779d31889379c56afeddc24ff50d04 (diff) | |
download | pico-watch-888675141118cb1cabb774cbe0a598ab9b5ca19d.tar pico-watch-888675141118cb1cabb774cbe0a598ab9b5ca19d.tar.gz pico-watch-888675141118cb1cabb774cbe0a598ab9b5ca19d.zip |
Merge branch 'api-class'
-rw-r--r-- | .vscode/settings.json | 14 | ||||
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | api.cpp | 153 | ||||
-rw-r--r-- | api.hpp | 72 | ||||
-rw-r--r-- | apps/home_menu.cpp | 35 | ||||
-rw-r--r-- | apps/home_menu.hpp | 12 | ||||
-rw-r--r-- | apps/main_clock.cpp | 30 | ||||
-rw-r--r-- | apps/main_clock.hpp | 12 | ||||
-rw-r--r-- | buttons.cpp | 4 | ||||
-rw-r--r-- | init.cpp | 10 | ||||
-rw-r--r-- | init.hpp | 3 | ||||
-rw-r--r-- | oled/ss_oled.c | 29 | ||||
-rw-r--r-- | pico-watch.cpp | 38 |
13 files changed, 325 insertions, 89 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json index 4cbb933..e63b566 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -19,5 +19,17 @@ "cmake.buildBeforeRun": true, // This may need to be arm-none-eabi-gdb depending on your system "cortex-debug.gdbPath": "gdb-multiarch", - "C_Cpp.errorSquiggles": "Disabled" + "C_Cpp.errorSquiggles": "Disabled", + "files.associations": { + "cctype": "cpp", + "cmath": "cpp", + "cstdarg": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "type_traits": "cpp", + "limits": "cpp", + "*.tcc": "cpp", + "typeinfo": "cpp" + } }
\ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 758ac2d..5ba1f5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,8 @@ add_executable(pico-watch init.hpp buttons.cpp buttons.hpp + api.cpp + api.hpp apps/home_menu.cpp apps/home_menu.hpp apps/main_clock.cpp @@ -0,0 +1,153 @@ +#include <stdio.h> +#include "pico/stdlib.h" +extern "C" { +#include "hardware/rtc.h" +} + +#include "api.hpp" +#include "init.hpp" + +void Api::init() { + if (!m_init_done) { + init_display(); + m_init_done = 1; + } +} + +void Api::init_display() { + oledInit(&m_oled, OLED_128x64, 0x3d, 0, 0, 1, SDA_PIN, SCL_PIN, RESET_PIN, 1000000L); + oledFill(&m_oled, 0,1); + oledSetContrast(&m_oled, OLED_DEFAULT_CONTRAST); + oledSetBackBuffer(&m_oled, m_ucBuffer); // Seems to be required to draw lines, rectangles… + //oledSetTextWrap(&oled, true); +} + +int Api::display_write_string(int iScrollX, int x, int y, char *szMsg, int iSize, int bInvert, int bRender) { + return oledWriteString(&m_oled, iScrollX, x, y, szMsg, iSize, bInvert, bRender); +} + +void Api::display_fill(unsigned char ucData, int bRender) { + oledFill(&m_oled, ucData, bRender); +} + +void Api::display_draw_line(int x1, int y1, int x2, int y2, int bRender) { + oledDrawLine(&m_oled, x1, y1, x2, y2, bRender); +} + +void Api::display_draw_rectange(int x1, int y1, int x2, int y2, uint8_t ucColor, uint8_t bFilled) { + oledRectangle(&m_oled, x1, y1, x2, y2, ucColor, bFilled); + oledDumpBuffer(&m_oled, m_ucBuffer); // Write the back buffer, after experimentation, seems to be required when drawing this shape +} + +void Api::display_draw_ellipse(int iCenterX, int iCenterY, int32_t iRadiusX, int32_t iRadiusY, uint8_t ucColor, uint8_t bFilled) { + oledEllipse(&m_oled, iCenterX, iCenterY, iRadiusX, iRadiusY, ucColor, bFilled); + oledDumpBuffer(&m_oled, m_ucBuffer); +} + +void Api::display_write_buffer(uint8_t *pBuffer) { + oledDumpBuffer(&m_oled, pBuffer); +} + +int Api::display_write_pixel(int x, int y, unsigned char ucColor, int bRender) { + return oledSetPixel(&m_oled, x, y, ucColor, bRender); +} + +bool Api::gui_popup_text(std::string title, std::string body){ +#define CHARS_PER_LINE 13 + m_button_last_pressed = 0; + m_send_button_press_to_app = false; + + oledRectangle(&m_oled, 9,7, 119,63, 0, 1); // Background + oledRectangle(&m_oled, 9,7, 119,63, 1, 0); // Popup border + oledRectangle(&m_oled, 9,7, 119,16, 1, 1); // Title background, FIXME pixel bleeding + oledDumpBuffer(&m_oled, m_ucBuffer); // Display rectangle + + // Truncate longer strings to avoid wasting time in for loop and drawing on OLED + if (title.size() > 13) + title.resize(13); + if (body.size() > 78) + body.resize(78); + + // Make body fit by adding '\n' at a regular interval + int since_nl = 0; // Characters since newline + for (std::string::size_type i = 0; i < body.size(); ++i) { + if (body[i] == '\n') + since_nl = 0; + else if (since_nl++ == CHARS_PER_LINE) { + body.insert(i, 1, '\n'); + since_nl = 0; // Probably unneeded + } + } + // See https://stackoverflow.com/questions/1986966/does-s0-point-to-contiguous-characters-in-a-stdstring + oledWriteString(&m_oled, 0, 15,1, &title[0], FONT_8x8, 1, 1); // Draw title + oledWriteString(&m_oled, 0, 13,2, &body[0], FONT_8x8, 0, 1); // Draw body + while (m_button_last_pressed != BUTTON_SELECT) + sleep_ms(50); + // Give back control to running app + oledFill(&m_oled, 0, 1); + m_send_button_press_to_app = true; +} + +bool Api::gui_footer_text(std::string text, int offset_x, int offset_row, int invert) { + // Max chars per line for FONT_8x8 is 16 chars + // Max chars per line for FONT_6x8 is 21 chars + // Truncate longer text + if (text.size() > 21) + text.resize(21); + + // Choose most adapted font size + int font; + if (text.size() > 16) + font = FONT_6x8; + else + font = FONT_8x8; + + oledRectangle(&m_oled, 0,56, 128,64, invert, 1); + oledDumpBuffer(&m_oled, m_ucBuffer); + oledWriteString(&m_oled, 0,offset_x,7-offset_row, &text[0], font, invert, 1); +} + +bool Api::gui_header_text(std::string text, int offset_x, int offset_row, int invert) { + // Max chars per line for FONT_8x8 is 16 chars + // Max chars per line for FONT_6x8 is 21 chars + // Truncate longer text + if (text.size() > 21) + text.resize(21); + + // Choose most adapted font size + int font; + if (text.size() > 16) + font = FONT_6x8; + else + font = FONT_8x8; + + oledRectangle(&m_oled, 0,0, 128,8, invert, 1); + oledDumpBuffer(&m_oled, m_ucBuffer); + oledWriteString(&m_oled, 0,offset_x,0+offset_row, &text[0], font, invert, 1); +} + +bool Api::performance_set(int perf) { + return false; +} + +bool Api::datetime_get(datetime_t *t) { + return rtc_get_datetime(t); +} + +int Api::performance_render_interval_get() { + return m_app_render_interval; +} + +void Api::performance_render_interval_set(int interval) { + if (interval < 10) + interval = 10; + m_app_render_interval = interval; +} + +uint Api::button_last_get() { + return m_button_last_pressed; +} + +void Api::button_last_set(uint gpio) { + m_button_last_pressed = gpio; +} @@ -0,0 +1,72 @@ +#ifndef __API_H__ +#define __API_H__ + +#include <iostream> +#include "pico/util/datetime.h" +#include "oled/ss_oled.h" + +#include "buttons.hpp" + +class Api { + private: + SSOLED m_oled; + u_char m_init_done = 0; + uint8_t m_ucBuffer[1024] = {0}; + uint m_button_last_pressed = 0; + int m_app_render_interval = 500; + void init_display(); + public: + bool m_send_button_press_to_app = true; + enum perf_modes { + LOW_POWER, + NORMAL_PERF, + HIGH_PERF + }; + void init(); + int display_write_string(int iScrollX, int x, int y, char *szMsg, int iSize, int bInvert, int bRender); + void display_fill(unsigned char ucData, int bRender); + void display_draw_line(int x1, int y1, int x2, int y2, int bRender); + void display_draw_rectange(int x1, int y1, int x2, int y2, uint8_t ucColor, uint8_t bFilled); + void display_draw_ellipse(int iCenterX, int iCenterY, int32_t iRadiusX, int32_t iRadiusY, uint8_t ucColor, uint8_t bFilled); + void display_write_buffer(uint8_t *pBuffer); + int display_write_pixel(int x, int y, unsigned char ucColor, int bRender); + // Display a popup over the current view and wait for select button to be pressed. + // This is a blocking function and should be used only in the app's render method. + // \param title Popup's title, length should not exceed 13 characters. + // \param body String containing the popup's body. The zone has a size of 13×6 characters, so body should not be longer than 78 characters. Newline allows going to the next line and the text is automatically wrapped. + // \note Strings longer than 13 and 78 respectively will be truncated. + bool gui_popup_text(std::string title, std::string body); + // Display text at the bottom of the screen. + // The font size is automatically choosen based on the text lenght. + // \param text Text to display. Text longer than 21 will be truncated. + // \param offset_x Set a horizental offset, to allow, for example, centering the text + // \param offset_row Allow rendering the text higher. For example, one line higher when `offset_row = 1`. + // \param invert allow inverting text and background color. + bool gui_footer_text(std::string text, int offset_x = 0, int offset_row = 0, int invert = 0); + // Display text at the top of the screen. + // The font size is automatically choosen based on the text lenght. + // \param text Text to display. Text longer than 21 will be truncated. + // \param offset_x Set a horizental offset, to allow, for example, centering the text + // \param offset_row Render text lines lower. For example, one text line lower with `offset_row = 1`. + // \param invert Invert text and background color. + bool gui_header_text(std::string text, int offset_x = 0, int offset_row = 0, int invert = 0); + // Set performance mode. + // FIXME: function currently does nothing! + // An app should choose the lowest performance that can make it function. Set in init(). Only when required, higher performance should be used. + // \param perf See Api::perf_modes enum for possible values + bool performance_set(int perf); + bool datetime_get(datetime_t *t); + // Get app's current render interval + // \return Value in millisec + int performance_render_interval_get(); + // Same spirit as performance_set, the app should use the smallest value possible that does not cause a lack of responsiveness in the app's UI. + // \param interval Time to wait in millisec between calls of the app's render function. + void performance_render_interval_set(int interval); + // Get last button pressed, see buttons.hpp for values + // \return Last button pressed + uint button_last_get(); + // Set last button pressed, should only be called by button gpio interrupt. + void button_last_set(uint gpio); +}; + +#endif
\ No newline at end of file diff --git a/apps/home_menu.cpp b/apps/home_menu.cpp index 7f53447..910ebac 100644 --- a/apps/home_menu.cpp +++ b/apps/home_menu.cpp @@ -1,12 +1,8 @@ #include <stdio.h> #include "pico/stdlib.h" -extern "C" { -#include "hardware/rtc.h" -} -#include "pico/util/datetime.h" -#include "../oled/ss_oled.h" #include "home_menu.hpp" +#include "../api.hpp" #include "../buttons.hpp" extern void app_switch(int old_appid, int new_appid); @@ -15,7 +11,7 @@ extern bool rtc_get_datetime(datetime_t *t); #define NUMBER_OF_APPS 2 namespace app_home_menu { - const char* APPS_NAME[NUMBER_OF_APPS][12] = {"Home", "Clock"}; + char *APPS_NAME[NUMBER_OF_APPS] = {"Home", "Clock"}; char *pressed_button; int *selected_app; @@ -28,28 +24,28 @@ namespace app_home_menu { t->sec); }; - void show_title(SSOLED *oled) { + void show_title(Api *app_api) { char datetime_buf[256]; char *datetime_str = &datetime_buf[0]; datetime_t t; - rtc_get_datetime(&t); + app_api->datetime_get(&t); // title with time title_str(datetime_str, sizeof(datetime_buf), &t); - oledWriteString(oled, 0,0,0, datetime_str, FONT_8x8, 0, 1); + app_api->gui_header_text((std::string)datetime_str); } // Rendering of app - int render(SSOLED *oled) { - show_title(oled); - oledWriteString(oled, 0,0,2, pressed_button, FONT_6x8, 0, 1); - oledWriteString(oled, 0,5,3, const_cast<char*>(APPS_NAME[0][*selected_app]), FONT_12x16, 0, 1); + int render(Api *app_api) { + show_title(app_api); + app_api->display_write_string(0,0,2, pressed_button, FONT_6x8, 0, 1); + app_api->display_write_string(0,5,3, APPS_NAME[*selected_app], FONT_12x16, 0, 1); return 0; } // Example of how button inputs could be interpreted. // Drawing on screen should be done in the render function. - int btnpressed(SSOLED *oled, uint gpio) { + int btnpressed(Api *app_api, uint gpio) { switch (gpio) { case BUTTON_HOME: *pressed_button = 'H'; break; @@ -79,19 +75,20 @@ namespace app_home_menu { } // Initlisation of the app. - int init(SSOLED *oled) { - pressed_button = new char; *pressed_button = '*'; - selected_app = new int; + int init(Api *app_api) { + app_api->performance_set(Api::perf_modes::LOW_POWER); + pressed_button = new char; *pressed_button = '*'; + selected_app = new int; *selected_app = 0; // Make sure to init the values to something known! return 0; // return 1 when function not implemented } // Processor intensive operations and functions related to drawing to the screen should only be done when the app is in_foreground(=1). This function is only called when the app is init. - int bgrefresh(SSOLED *oled, char in_foreground) { + int bgrefresh(Api *app_api, char in_foreground) { return 1; } // Destruction of app, deinitlisation should be done here. This is only called if the app's APPS_DESTROY_ON_EXIT is set to 1. When it is not a "service" app. - int destroy(SSOLED *oled) { + int destroy(Api *app_api) { delete pressed_button; pressed_button = nullptr; delete selected_app; selected_app = nullptr; return 1; diff --git a/apps/home_menu.hpp b/apps/home_menu.hpp index ebcc451..033dc6b 100644 --- a/apps/home_menu.hpp +++ b/apps/home_menu.hpp @@ -4,12 +4,14 @@ #include "pico/util/datetime.h" #include "../oled/ss_oled.h" +#include "../api.hpp" + namespace app_home_menu { - int init(SSOLED *oled); - int render(SSOLED *oled); - int btnpressed(SSOLED *oled, uint gpio); - int bgrefresh(SSOLED *oled, char in_foreground); - int destroy(SSOLED *oled); + int init(Api *app_api); + int render(Api *app_api); + int btnpressed(Api *app_api, uint gpio); + int bgrefresh(Api *app_api, char in_foreground); + int destroy(Api *app_api); } #endif diff --git a/apps/main_clock.cpp b/apps/main_clock.cpp index f9be3e5..895a7b9 100644 --- a/apps/main_clock.cpp +++ b/apps/main_clock.cpp @@ -1,12 +1,8 @@ #include <stdio.h> #include "pico/stdlib.h" -extern "C" { -#include "hardware/rtc.h" -} -#include "pico/util/datetime.h" -#include "../oled/ss_oled.h" #include "main_clock.hpp" +#include "../api.hpp" #include "../buttons.hpp" namespace app_main_clock { @@ -39,46 +35,46 @@ namespace app_main_clock { DATETIME_DOWS[t->dotw - 1]); }; - void show_datetime(SSOLED *oled) { + void show_datetime(Api *app_api) { char datetime_buf[256]; char *datetime_str = &datetime_buf[0]; datetime_t t; - rtc_get_datetime(&t); + app_api->datetime_get(&t); // time time_as_str(datetime_str, sizeof(datetime_buf), &t); - oledWriteString(oled, 0,10,3, datetime_str, FONT_12x16, 0, 1); + app_api->display_write_string(0,10,3, datetime_str, FONT_12x16, 0, 1); // date date_as_str(datetime_str, sizeof(datetime_buf), &t); - oledWriteString(oled, 0,0,7, datetime_str, FONT_8x8, 0, 1); + app_api->gui_footer_text((std::string)datetime_str); } // Rendering of the app - int render(SSOLED *oled) { - oledWriteString(oled, 0,15,0, (char *)"Test clock", FONT_8x8, 0, 1); - show_datetime(oled); - //oledWriteString(oled, 0,0,0, &data[0], FONT_6x8, 0, 1); + int render(Api *app_api) { + app_api->gui_header_text("Test clock", 17); + show_datetime(app_api); return 0; } // Interpretation of button inputs - int btnpressed(SSOLED *oled, uint gpio) { + int btnpressed(Api *app_api, uint gpio) { return 0; } // Initlisation of the app. - int init(SSOLED *oled) { + int init(Api *app_api) { + app_api->performance_set(Api::perf_modes::LOW_POWER); return 1; // return 1 when function not implemented } // Processor intensive operations and functions related to drawing to the screen should only be done when the app is in_foreground(=1). This function is only called when the app is init. - int bgrefresh(SSOLED *oled, char in_foreground) { + int bgrefresh(Api *app_api, char in_foreground) { return 1; } // Destruction of app, deinitlisation should be done here. This is only called if the app's APPS_DESTROY_ON_EXIT is set to 1. When it is not a "service" app. - int destroy(SSOLED *oled) { + int destroy(Api *app_api) { return 1; } }
\ No newline at end of file diff --git a/apps/main_clock.hpp b/apps/main_clock.hpp index 887dfa1..5440c1f 100644 --- a/apps/main_clock.hpp +++ b/apps/main_clock.hpp @@ -4,12 +4,14 @@ #include "pico/util/datetime.h" #include "../oled/ss_oled.h" +#include "../api.hpp" + namespace app_main_clock { - int init(SSOLED *oled); - int render(SSOLED *oled); - int btnpressed(SSOLED *oled, uint gpio); - int bgrefresh(SSOLED *oled, char in_foreground); - int destroy(SSOLED *oled); + int init(Api *app_api); + int render(Api *app_api); + int btnpressed(Api *app_api, uint gpio); + int bgrefresh(Api *app_api, char in_foreground); + int destroy(Api *app_api); } #endif
\ No newline at end of file diff --git a/buttons.cpp b/buttons.cpp index 8ba7b81..cd94504 100644 --- a/buttons.cpp +++ b/buttons.cpp @@ -2,10 +2,12 @@ #include "pico/stdlib.h" #include "buttons.hpp" +#include "api.hpp" // From pico-watch.c: extern int app_btnpressed(int app_id, uint gpio); extern void app_switch(int old_appid, int new_appid); extern int current_app; +extern Api app_api; // Debounce control // See https://www.raspberrypi.org/forums/viewtopic.php?f=145&t=301522#p1812063 @@ -21,7 +23,7 @@ void gpio_interrupt_cb(uint gpio, uint32_t events) { app_switch(current_app, 0); else app_btnpressed(current_app, gpio); - + app_api.button_last_set(gpio); button_time = to_ms_since_boot(get_absolute_time()); } } @@ -4,17 +4,8 @@ extern "C" { #include "hardware/rtc.h" } -#include "oled/ss_oled.h" #include "init.hpp" -SSOLED oled; - -void init_display() { - oledInit(&oled, OLED_128x64, 0x3d, 0, 0, 1, SDA_PIN, SCL_PIN, RESET_PIN, 1000000L); - oledFill(&oled, 0,1); - oledSetContrast(&oled, OLED_DEFAULT_CONTRAST); - //oledSetTextWrap(&oled, true); -} void init_rtc() { datetime_t init_date = { @@ -31,6 +22,5 @@ void init_rtc() { void init_all() { stdio_init_all(); - init_display(); init_rtc(); } @@ -7,7 +7,6 @@ #define SCL_PIN 7 #define RESET_PIN -1 #define OLED_DEFAULT_CONTRAST 40 -extern SSOLED oled; // Initial date & time // The idea is to have the compiler set the date at compile-time. @@ -23,8 +22,6 @@ extern SSOLED oled; // Init every componement void init_all(); -// Init OLED display -void init_display(); // Init onboard RTC void init_rtc(); diff --git a/oled/ss_oled.c b/oled/ss_oled.c index 3cd9132..3375bde 100644 --- a/oled/ss_oled.c +++ b/oled/ss_oled.c @@ -1486,20 +1486,23 @@ unsigned char c, *s, ucTemp[40]; if (iScroll < 8) // only display visible characters { c = (unsigned char)szMsg[i]; - iFontOff = (int)(c-32) * 7; - // we can't directly use the pointer to FLASH memory, so copy to a local buffer - ucTemp[0] = 0; - memcpy(&ucTemp[1], &ucFont[iFontOff], 7); - if (bInvert) InvertBytes(ucTemp, 8); - // oledCachedWrite(ucTemp, 8); - iLen = 8 - iFontSkip; - if (pOLED->iCursorX + iLen > pOLED->oled_x) // clip right edge - iLen = pOLED->oled_x - pOLED->iCursorX; - oledWriteDataBlock(pOLED, &ucTemp[iFontSkip], iLen, bRender); // write character pattern - pOLED->iCursorX += iLen; - if (pOLED->iCursorX >= pOLED->oled_x-7 && pOLED->oled_wrap) // word wrap enabled? + if (c != '\n') { // Do not draw the new line char + iFontOff = (int)(c-32) * 7; + // we can't directly use the pointer to FLASH memory, so copy to a local buffer + ucTemp[0] = 0; + memcpy(&ucTemp[1], &ucFont[iFontOff], 7); + if (bInvert) InvertBytes(ucTemp, 8); + // oledCachedWrite(ucTemp, 8); + iLen = 8 - iFontSkip; + if (pOLED->iCursorX + iLen > pOLED->oled_x) // clip right edge + iLen = pOLED->oled_x - pOLED->iCursorX; + oledWriteDataBlock(pOLED, &ucTemp[iFontSkip], iLen, bRender); // write character pattern + pOLED->iCursorX += iLen; + } + + if ((pOLED->iCursorX >= pOLED->oled_x-7 && pOLED->oled_wrap) || c == '\n') // word wrap enabled? or new line char { - pOLED->iCursorX = 0; // start at the beginning of the next line + pOLED->iCursorX = x; // start at the beginning of the next line pOLED->iCursorY++; oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY, bRender); } diff --git a/pico-watch.cpp b/pico-watch.cpp index 243f1e1..9df6799 100644 --- a/pico-watch.cpp +++ b/pico-watch.cpp @@ -3,52 +3,56 @@ #include "hardware/i2c.h" #include "hardware/rtc.h" #include "pico/util/datetime.h" -#include "oled/ss_oled.h" #include "init.hpp" +#include "api.hpp" #include "buttons.hpp" #include "apps/main_clock.hpp" #include "apps/home_menu.hpp" int current_app = 0; +bool app_ready = true; +Api app_api; #define NUMBER_OF_APPS 2 #define APP_DATA_BUFFER_LEN 256 -int (*APPS_FUNC_INIT[NUMBER_OF_APPS])(SSOLED *oled) = {app_home_menu::init, app_main_clock::init}; -int (*APPS_FUNC_RENDER[NUMBER_OF_APPS])(SSOLED *oled) = {app_home_menu::render, app_main_clock::render}; -int (*APPS_FUNC_BTNPRESS[NUMBER_OF_APPS])(SSOLED *oled, uint gpio) = {app_home_menu::btnpressed, app_main_clock::btnpressed}; -int (*APPS_FUNC_BGREFRESH[NUMBER_OF_APPS])(SSOLED *oled, char in_foreground) = {app_home_menu::bgrefresh, app_main_clock::bgrefresh}; -int (*APPS_FUNC_DESTROY[NUMBER_OF_APPS])(SSOLED *oled) = {app_home_menu::destroy, app_main_clock::destroy}; +int (*APPS_FUNC_INIT[NUMBER_OF_APPS])(Api *app_api) = {app_home_menu::init, app_main_clock::init}; +int (*APPS_FUNC_RENDER[NUMBER_OF_APPS])(Api *app_api) = {app_home_menu::render, app_main_clock::render}; +int (*APPS_FUNC_BTNPRESS[NUMBER_OF_APPS])(Api *app_api, uint gpio) = {app_home_menu::btnpressed, app_main_clock::btnpressed}; +int (*APPS_FUNC_BGREFRESH[NUMBER_OF_APPS])(Api *app_api, char in_foreground) = {app_home_menu::bgrefresh, app_main_clock::bgrefresh}; +int (*APPS_FUNC_DESTROY[NUMBER_OF_APPS])(Api *app_api) = {app_home_menu::destroy, app_main_clock::destroy}; int APPS_DESTROY_ON_EXIT[NUMBER_OF_APPS] = {0, 1}; int APPS_IS_INIT[NUMBER_OF_APPS] = {0, 0}; // Only run in background if init int app_init(int app_id) { - oledFill(&oled, 0,1); // Clear OLED + app_api.display_fill(0,1); // Clear OLED + app_api.performance_render_interval_set(500); // Reset interval if (!APPS_IS_INIT[app_id]) { APPS_IS_INIT[app_id] = 1; - return (*APPS_FUNC_INIT[app_id])(&oled); + return (*APPS_FUNC_INIT[app_id])(&app_api); } } int app_render(int app_id) { - return (*APPS_FUNC_RENDER[app_id])(&oled); + return (*APPS_FUNC_RENDER[app_id])(&app_api); } int app_btnpressed(int app_id, uint gpio) { - return (*APPS_FUNC_BTNPRESS[app_id])(&oled, gpio); + if (app_api.m_send_button_press_to_app) + return (*APPS_FUNC_BTNPRESS[app_id])(&app_api, gpio); + return 2; } int app_destroy(int app_id) { - // TODO: reset APPS_DATA for the app if (APPS_IS_INIT[app_id]) { APPS_IS_INIT[app_id] = 0; - return (*APPS_FUNC_DESTROY[app_id])(&oled); + return (*APPS_FUNC_DESTROY[app_id])(&app_api); } } int app_bgrefresh(int app_id) { if (APPS_IS_INIT[app_id]) - return (*APPS_FUNC_BGREFRESH[app_id])(&oled, app_id==current_app); + return (*APPS_FUNC_BGREFRESH[app_id])(&app_api, app_id==current_app); } bool apps_bgrefresh(struct repeating_timer *t) { // TODO: Refresh done on core1 @@ -59,24 +63,28 @@ bool apps_bgrefresh(struct repeating_timer *t) { // TODO: Refresh done on core1 } void app_switch(int old_appid, int new_appid) { + app_ready = false; if (APPS_DESTROY_ON_EXIT[old_appid]) { app_destroy(old_appid); } app_init(new_appid); current_app = new_appid; + app_ready = true; } int main() { init_all(); init_buttons(); + app_api.init(); struct repeating_timer timer; add_repeating_timer_ms(250, apps_bgrefresh, NULL, &timer); app_init(current_app); while (1) { - app_render(current_app); - sleep_ms(500); + if (app_ready) + app_render(current_app); // FIXME: This may cause race conditions when switching app + sleep_ms(app_api.performance_render_interval_get()); } return 0; } |