From ccbe6b7ab8649720da358bbf9922b70d8d03f27c Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Sat, 27 Jan 2024 12:34:02 +0100 Subject: [PATCH] LilyGo T-Deck keyboard support & display driver improvements (#19) * LilyGo T-Deck keyboard support * reverse logic * docs and readability * cleanup * optimize driver buffer * cleanup --- CODING_STYLE.md | 6 +- boards/lilygo_tdeck/bootstrap.c | 54 ++++++++--- boards/lilygo_tdeck/config.h | 7 ++ boards/lilygo_tdeck/display.c | 14 +-- boards/lilygo_tdeck/keyboard.c | 95 +++++++++++++++++++ boards/lilygo_tdeck/keyboard.h | 18 ++++ boards/lilygo_tdeck/lvgl.c | 6 +- boards/lilygo_tdeck/touch.c | 31 +----- docs/ideas.md | 6 +- .../system/wifi_connect/wifi_connect_view.c | 25 +++-- .../system/wifi_connect/wifi_connect_view.h | 1 + tactility/src/app.c | 1 - tactility/src/hardware.c | 6 +- tactility/src/services/gui/gui.c | 13 ++- tactility/src/services/gui/gui.h | 30 ++++++ tactility/src/services/loader/loader.c | 1 + tactility/src/tactility.c | 8 +- tactility/src/tactility.h | 8 +- tactility/src/tactility_config.h | 6 ++ tactility/src/ui/lvgl_keypad.c | 23 +++++ tactility/src/ui/lvgl_keypad.h | 16 ++++ 21 files changed, 302 insertions(+), 73 deletions(-) create mode 100644 boards/lilygo_tdeck/config.h create mode 100644 boards/lilygo_tdeck/keyboard.c create mode 100644 boards/lilygo_tdeck/keyboard.h create mode 100644 tactility/src/tactility_config.h create mode 100644 tactility/src/ui/lvgl_keypad.c create mode 100644 tactility/src/ui/lvgl_keypad.h diff --git a/CODING_STYLE.md b/CODING_STYLE.md index 1dfb4311..11578c30 100644 --- a/CODING_STYLE.md +++ b/CODING_STYLE.md @@ -26,9 +26,11 @@ Like `some_feature_i.h` Names are snake-case. -Public functions are prefixed with `tt_` for `tactility-core` and `tactility` projects. +The `tt_` prefix is used for public functions that are part of `tactility/` or `tactility-core/` Internal/static functions don't have prefix requirements, but prefixes are allowed. +The prefix is **not** used for drivers, services and apps. + Public functions have the feature name after `tt_`. If a feature has setters or getters, it's added after the feature name part. @@ -36,7 +38,7 @@ If a feature has setters or getters, it's added after the feature name part. Example: ```c -void tt_feature_get_name() { +void tt_counter_get_limit() { // ... } ``` diff --git a/boards/lilygo_tdeck/bootstrap.c b/boards/lilygo_tdeck/bootstrap.c index 039599c3..c1577071 100644 --- a/boards/lilygo_tdeck/bootstrap.c +++ b/boards/lilygo_tdeck/bootstrap.c @@ -1,29 +1,61 @@ -#include "esp_log.h" -#include "driver/gpio.h" +#include "config.h" +#include "keyboard.h" #include "kernel.h" -#include "esp_lvgl_port.h" +#include "log.h" -#define TAG "lilygo_tdeck_bootstrap" -#define TDECK_PERI_POWERON GPIO_NUM_10 +#define TAG "tdeck_bootstrap" lv_disp_t* lilygo_tdeck_init_display(); -static void tdeck_power_on() { +static bool tdeck_power_on() { ESP_LOGI(TAG, "power on"); gpio_config_t device_power_signal_config = { - .pin_bit_mask = BIT64(TDECK_PERI_POWERON), + .pin_bit_mask = BIT64(TDECK_POWERON_GPIO), .mode = GPIO_MODE_OUTPUT, .pull_up_en = GPIO_PULLUP_DISABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, .intr_type = GPIO_INTR_DISABLE, }; - gpio_config(&device_power_signal_config); - gpio_set_level(TDECK_PERI_POWERON, 1); + + if (gpio_config(&device_power_signal_config) != ESP_OK) { + return false; + } + + if (gpio_set_level(TDECK_POWERON_GPIO, 1) != ESP_OK) { + return false; + } + + return true; } -void lilygo_tdeck_bootstrap() { - tdeck_power_on(); +static bool init_i2c() { + const i2c_config_t i2c_conf = { + .mode = I2C_MODE_MASTER, + .sda_io_num = GPIO_NUM_18, + .sda_pullup_en = GPIO_PULLUP_DISABLE, + .scl_io_num = GPIO_NUM_8, + .scl_pullup_en = GPIO_PULLUP_DISABLE, + .master.clk_speed = 400000 + }; + + return i2c_param_config(TDECK_I2C_BUS_HANDLE, &i2c_conf) == ESP_OK + && i2c_driver_install(TDECK_I2C_BUS_HANDLE, i2c_conf.mode, 0, 0, 0) == ESP_OK; +} + +bool lilygo_tdeck_bootstrap() { + if (!tdeck_power_on()) { + TT_LOG_E(TAG, "failed to power on device"); + } + // Give keyboard's ESP time to boot // It uses I2C and seems to interfere with the touch driver tt_delay_ms(500); + + if (!init_i2c()) { + TT_LOG_E(TAG, "failed to init I2C"); + } + + keyboard_wait_for_response(); + + return true; } diff --git a/boards/lilygo_tdeck/config.h b/boards/lilygo_tdeck/config.h new file mode 100644 index 00000000..d99fec1b --- /dev/null +++ b/boards/lilygo_tdeck/config.h @@ -0,0 +1,7 @@ +#pragma once + +#include "driver/i2c.h" +#include "driver/gpio.h" + +#define TDECK_I2C_BUS_HANDLE (0) +#define TDECK_POWERON_GPIO GPIO_NUM_10 diff --git a/boards/lilygo_tdeck/display.c b/boards/lilygo_tdeck/display.c index e7086022..d83111e2 100644 --- a/boards/lilygo_tdeck/display.c +++ b/boards/lilygo_tdeck/display.c @@ -6,7 +6,7 @@ #include "esp_log.h" #include "esp_lvgl_port.h" -#define TAG "lilygo_tdeck_display" +#define TAG "tdeck_display" #define LCD_SPI_HOST SPI2_HOST #define LCD_PIN_SCLK GPIO_NUM_40 @@ -21,6 +21,7 @@ #define LCD_VERTICAL_RESOLUTION 240 #define LCD_BITS_PER_PIXEL 16 #define LCD_DRAW_BUFFER_HEIGHT (LCD_VERTICAL_RESOLUTION / 10) +#define LCD_SPI_TRANSFER_HEIGHT (LCD_VERTICAL_RESOLUTION / 10) // Backlight PWM #define LCD_BACKLIGHT_LEDC_TIMER LEDC_TIMER_0 @@ -60,7 +61,7 @@ static void tdeck_backlight() { lv_disp_t* lilygo_tdeck_init_display() { ESP_LOGI(TAG, "creating display"); - int draw_buffer_size = LCD_HORIZONTAL_RESOLUTION * LCD_DRAW_BUFFER_HEIGHT * (LCD_BITS_PER_PIXEL / 8); + int max_transfer_size = LCD_HORIZONTAL_RESOLUTION * LCD_SPI_TRANSFER_HEIGHT * (LCD_BITS_PER_PIXEL / 8); spi_bus_config_t bus_config = { .sclk_io_num = LCD_PIN_SCLK, @@ -68,7 +69,7 @@ lv_disp_t* lilygo_tdeck_init_display() { .miso_io_num = LCD_PIN_MISO, .quadwp_io_num = -1, // Quad SPI LCD driver is not yet supported .quadhd_io_num = -1, // Quad SPI LCD driver is not yet supported - .max_transfer_sz = draw_buffer_size, + .max_transfer_sz = max_transfer_size, }; if (spi_bus_initialize(LCD_SPI_HOST, &bus_config, SPI_DMA_CH_AUTO) != ESP_OK) { @@ -154,17 +155,18 @@ lv_disp_t* lilygo_tdeck_init_display() { .io_handle = io_handle, .panel_handle = panel_handle, .buffer_size = LCD_HORIZONTAL_RESOLUTION * LCD_DRAW_BUFFER_HEIGHT * (LCD_BITS_PER_PIXEL / 8), - .double_buffer = false, + .double_buffer = true, // Disable to free up SPIRAM .hres = LCD_HORIZONTAL_RESOLUTION, .vres = LCD_VERTICAL_RESOLUTION, .monochrome = false, .rotation = { - .swap_xy = true, // TODO: check if code above is still needed + .swap_xy = true, .mirror_x = true, .mirror_y = false, }, .flags = { - .buff_dma = true, + .buff_dma = false, + .buff_spiram = true, } }; diff --git a/boards/lilygo_tdeck/keyboard.c b/boards/lilygo_tdeck/keyboard.c new file mode 100644 index 00000000..fa049a9d --- /dev/null +++ b/boards/lilygo_tdeck/keyboard.c @@ -0,0 +1,95 @@ +#include "keyboard.h" +#include "config.h" +#include "hal/lv_hal.h" +#include "tactility_core.h" +#include "ui/lvgl_keypad.h" +#include + +#define TAG "tdeck_keyboard" +#define KEYBOARD_SLAVE_ADDRESS 0x55 + +typedef struct { + lv_indev_drv_t* driver; + lv_indev_t* device; +} KeyboardData; + +static inline esp_err_t keyboard_i2c_read(uint8_t* output) { + return i2c_master_read_from_device( + TDECK_I2C_BUS_HANDLE, + KEYBOARD_SLAVE_ADDRESS, + output, + 1, + configTICK_RATE_HZ / 10 + ); +} + +void keyboard_wait_for_response() { + TT_LOG_I(TAG, "wake await..."); + bool awake = false; + uint8_t read_buffer = 0x00; + do { + awake = keyboard_i2c_read(&read_buffer) == ESP_OK; + if (!awake) { + tt_delay_ms(100); + } + } while (!awake); + TT_LOG_I(TAG, "awake"); +} + +/** + * The callback simulates press and release events, because the T-Deck + * keyboard only publishes press events on I2C. + * LVGL currently works without those extra release events, but they + * are implemented for correctness and future compatibility. + * + * @param indev_drv + * @param data + */ +static void keyboard_read_callback(TT_UNUSED struct _lv_indev_drv_t* indev_drv, lv_indev_data_t* data) { + static uint8_t last_buffer = 0x00; + uint8_t read_buffer = 0x00; + + // Defaults + data->key = 0; + data->state = LV_INDEV_STATE_RELEASED; + + if (keyboard_i2c_read(&read_buffer) == ESP_OK) { + if (read_buffer == 0 && read_buffer != last_buffer) { + TT_LOG_I(TAG, "released %d", last_buffer); + data->key = last_buffer; + data->state = LV_INDEV_STATE_RELEASED; + } else if (read_buffer != 0) { + TT_LOG_I(TAG, "pressed %d", read_buffer); + data->key = read_buffer; + data->state = LV_INDEV_STATE_PRESSED; + } + } + + last_buffer = read_buffer; +} + +Keyboard keyboard_alloc(_Nullable lv_disp_t* display) { + KeyboardData* data = malloc(sizeof(KeyboardData)); + + data->driver = malloc(sizeof(lv_indev_drv_t)); + memset(data->driver, 0, sizeof(lv_indev_drv_t)); + lv_indev_drv_init(data->driver); + + data->driver->type = LV_INDEV_TYPE_KEYPAD; + data->driver->read_cb = &keyboard_read_callback; + data->driver->disp = display; + + data->device = lv_indev_drv_register(data->driver); + tt_check(data->device != NULL); + + tt_lvgl_keypad_set_indev(data->device); + + return data; +} + +void keyboard_free(Keyboard keyboard) { + KeyboardData* data = (KeyboardData*)keyboard; + lv_indev_delete(data->device); + free(data->driver); + free(data); +} diff --git a/boards/lilygo_tdeck/keyboard.h b/boards/lilygo_tdeck/keyboard.h new file mode 100644 index 00000000..fda164f3 --- /dev/null +++ b/boards/lilygo_tdeck/keyboard.h @@ -0,0 +1,18 @@ +#pragma once + +#include "lvgl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void keyboard_wait_for_response(); + +typedef void* Keyboard; + +Keyboard keyboard_alloc(_Nullable lv_disp_t* display); +void keyboard_free(Keyboard keyboard); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/boards/lilygo_tdeck/lvgl.c b/boards/lilygo_tdeck/lvgl.c index 597f4210..18a8d8d4 100644 --- a/boards/lilygo_tdeck/lvgl.c +++ b/boards/lilygo_tdeck/lvgl.c @@ -1,9 +1,10 @@ #include "esp_lvgl_port.h" +#include "keyboard.h" #include "log.h" #include "ui/lvgl_sync.h" #include -#define TAG "lilygo_tdeck_lvgl" +#define TAG "tdeck_lvgl" lv_disp_t* lilygo_tdeck_init_display(); bool lilygo_tdeck_init_touch(esp_lcd_panel_io_handle_t* io_handle, esp_lcd_touch_handle_t* touch_handle); @@ -33,7 +34,6 @@ bool lilygo_init_lvgl() { return false; } - // Add touch if (!lilygo_tdeck_init_touch(&touch_io_handle, &touch_handle)) { return false; @@ -53,5 +53,7 @@ bool lilygo_init_lvgl() { // Set syncing functions tt_lvgl_sync_set(&lvgl_port_lock, &lvgl_port_unlock); + keyboard_alloc(display); + return true; } diff --git a/boards/lilygo_tdeck/touch.c b/boards/lilygo_tdeck/touch.c index adeab833..89d304bf 100644 --- a/boards/lilygo_tdeck/touch.c +++ b/boards/lilygo_tdeck/touch.c @@ -1,37 +1,16 @@ +#include "config.h" +#include "driver/i2c.h" +#include "esp_err.h" #include "esp_lcd_touch_gt911.h" #include "esp_log.h" -#include "esp_err.h" -#include "driver/i2c.h" -#define TOUCH_I2C_PORT 0 - -#define TAG "lilygo_tdeck_touch" +#define TAG "tdeck_touch" bool lilygo_tdeck_init_touch(esp_lcd_panel_io_handle_t* io_handle, esp_lcd_touch_handle_t* touch_handle) { ESP_LOGI(TAG, "creating touch"); - const i2c_config_t i2c_conf = { - .mode = I2C_MODE_MASTER, - .sda_io_num = GPIO_NUM_18, - .sda_pullup_en = GPIO_PULLUP_DISABLE, - .scl_io_num = GPIO_NUM_8, - .scl_pullup_en = GPIO_PULLUP_DISABLE, - .master.clk_speed = 400000 - }; - - if (i2c_param_config(TOUCH_I2C_PORT, &i2c_conf) != ESP_OK) { - ESP_LOGE(TAG, "i2c config failed"); - return false; - } - - if (i2c_driver_install(TOUCH_I2C_PORT, i2c_conf.mode, 0, 0, 0) != ESP_OK) { - ESP_LOGE(TAG, "i2c driver install failed"); - return false; - } - const esp_lcd_panel_io_i2c_config_t touch_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG(); - - if (esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)TOUCH_I2C_PORT, &touch_io_config, io_handle) != ESP_OK) { + if (esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)TDECK_I2C_BUS_HANDLE, &touch_io_config, io_handle) != ESP_OK) { ESP_LOGE(TAG, "touch io i2c creation failed"); return false; } diff --git a/docs/ideas.md b/docs/ideas.md index 210d8fed..1c8560d8 100644 --- a/docs/ideas.md +++ b/docs/ideas.md @@ -4,6 +4,9 @@ - Replace FreeRTOS semaphore from `Loader` with internal `Mutex` - Create unit tests for `tactility-core` and `tactility` (PC-only for now) - Have a way to deinit LVGL drivers that are created from `HardwareConfig` +- Thread is broken: `tt_thread_join()` always hangs because `tt_thread_cleanup_tcb_event()` +is not automatically called. This is normally done by a hook in `FreeRTOSConfig.h` +but that seems to not work with ESP32. I should investigate task cleanup hooks further. # Core Ideas - Make a HAL? It would mainly be there to support PC development. It's a lot of effort for supporting what's effectively a dev-only feature. @@ -15,4 +18,5 @@ - BadUSB - IR transceiver app - GPIO status viewer -- BlueTooth keyboard app \ No newline at end of file +- BlueTooth keyboard app +- Investigate CSI https://stevenmhernandez.github.io/ESP32-CSI-Tool/ \ No newline at end of file diff --git a/tactility-esp/src/apps/system/wifi_connect/wifi_connect_view.c b/tactility-esp/src/apps/system/wifi_connect/wifi_connect_view.c index c574404c..fdc70a9a 100644 --- a/tactility-esp/src/apps/system/wifi_connect/wifi_connect_view.c +++ b/tactility-esp/src/apps/system/wifi_connect/wifi_connect_view.c @@ -4,6 +4,7 @@ #include "lvgl.h" #include "services/gui/gui.h" #include "services/wifi/wifi_credentials.h" +#include "ui/lvgl_keypad.h" #include "ui/spacer.h" #include "ui/style.h" #include "wifi_connect.h" @@ -103,12 +104,14 @@ void wifi_connect_view_create(App app, void* wifi, lv_obj_t* parent) { wifi_connect_view_create_bottom_buttons(wifi, parent); - lv_obj_add_event_cb(view->ssid_textarea, show_keyboard, LV_EVENT_FOCUSED, NULL); - lv_obj_add_event_cb(view->ssid_textarea, hide_keyboard, LV_EVENT_DEFOCUSED, NULL); - lv_obj_add_event_cb(view->ssid_textarea, hide_keyboard, LV_EVENT_READY, NULL); - lv_obj_add_event_cb(view->password_textarea, show_keyboard, LV_EVENT_FOCUSED, NULL); - lv_obj_add_event_cb(view->password_textarea, hide_keyboard, LV_EVENT_DEFOCUSED, NULL); - lv_obj_add_event_cb(view->password_textarea, hide_keyboard, LV_EVENT_READY, NULL); + if (gui_keyboard_is_enabled()) { + lv_obj_add_event_cb(view->ssid_textarea, show_keyboard, LV_EVENT_FOCUSED, NULL); + lv_obj_add_event_cb(view->ssid_textarea, hide_keyboard, LV_EVENT_DEFOCUSED, NULL); + lv_obj_add_event_cb(view->ssid_textarea, hide_keyboard, LV_EVENT_READY, NULL); + lv_obj_add_event_cb(view->password_textarea, show_keyboard, LV_EVENT_FOCUSED, NULL); + lv_obj_add_event_cb(view->password_textarea, hide_keyboard, LV_EVENT_DEFOCUSED, NULL); + lv_obj_add_event_cb(view->password_textarea, hide_keyboard, LV_EVENT_READY, NULL); + } // Init from app parameters Bundle* _Nullable bundle = tt_app_get_parameters(app); @@ -123,10 +126,18 @@ void wifi_connect_view_create(App app, void* wifi, lv_obj_t* parent) { lv_textarea_set_text(view->password_textarea, password); } } + + // Hardware keyboard("keypad") requires a group + view->group = lv_group_create(); + lv_group_add_obj(view->group, view->ssid_textarea); + lv_group_add_obj(view->group, view->password_textarea); + tt_lvgl_keypad_activate(view->group); } void wifi_connect_view_destroy(TT_UNUSED WifiConnectView* view) { - // NO-OP + // Cleanup keypad group + tt_lvgl_keypad_deactivate(); + lv_group_del(view->group); } void wifi_connect_view_update( diff --git a/tactility-esp/src/apps/system/wifi_connect/wifi_connect_view.h b/tactility-esp/src/apps/system/wifi_connect/wifi_connect_view.h index b534c81d..432e9d5a 100644 --- a/tactility-esp/src/apps/system/wifi_connect/wifi_connect_view.h +++ b/tactility-esp/src/apps/system/wifi_connect/wifi_connect_view.h @@ -15,6 +15,7 @@ typedef struct { lv_obj_t* connect_button; lv_obj_t* cancel_button; lv_obj_t* remember_switch; + lv_group_t* group; } WifiConnectView; void wifi_connect_view_create(App app, void* wifi, lv_obj_t* parent); diff --git a/tactility/src/app.c b/tactility/src/app.c index b93473a6..7b675351 100644 --- a/tactility/src/app.c +++ b/tactility/src/app.c @@ -1,6 +1,5 @@ #include "app_i.h" -#include #include static AppFlags tt_app_get_flags_default(AppType type); diff --git a/tactility/src/hardware.c b/tactility/src/hardware.c index 25c63d2d..256a4249 100644 --- a/tactility/src/hardware.c +++ b/tactility/src/hardware.c @@ -6,9 +6,9 @@ void tt_hardware_init(const HardwareConfig* config) { if (config->bootstrap != NULL) { TT_LOG_I(TAG, "Bootstrapping"); - config->bootstrap(); + tt_check(config->bootstrap(), "bootstrap failed"); } - tt_check(config->init_lvgl); - config->init_lvgl(); + tt_check(config->init_lvgl, "lvlg init not set"); + tt_check(config->init_lvgl(), "lvgl init failed"); } diff --git a/tactility/src/services/gui/gui.c b/tactility/src/services/gui/gui.c index 47d502fd..2b317100 100644 --- a/tactility/src/services/gui/gui.c +++ b/tactility/src/services/gui/gui.c @@ -1,10 +1,8 @@ #include "gui_i.h" -#include "check.h" -#include "core_extra_defines.h" +#include "tactility.h" #include "ui/lvgl_sync.h" -#include "kernel.h" -#include "log.h" +#include "ui/lvgl_keypad.h" #ifdef ESP_PLATFORM #include "freertos/FreeRTOS.h" @@ -30,7 +28,7 @@ Gui* gui_alloc() { &gui_main, NULL ); - instance->mutex = tt_mutex_alloc(MutexTypeNormal); + instance->mutex = tt_mutex_alloc(MutexTypeRecursive); instance->keyboard = NULL; tt_check(tt_lvgl_lock(1000 / portTICK_PERIOD_MS)); @@ -102,6 +100,10 @@ void gui_keyboard_hide() { } } +bool gui_keyboard_is_enabled() { + return !tt_lvgl_keypad_is_available() || TT_CONFIG_FORCE_ONSCREEN_KEYBOARD; +} + void gui_hide_app() { gui_lock(); ViewPort* view_port = gui->app_view_port; @@ -158,6 +160,7 @@ static void gui_stop(TT_UNUSED Service service) { ThreadId thread_id = tt_thread_get_id(gui->thread); tt_thread_flags_set(thread_id, GUI_THREAD_FLAG_EXIT); tt_thread_join(gui->thread); + tt_thread_free(gui->thread); gui_unlock(); diff --git a/tactility/src/services/gui/gui.h b/tactility/src/services/gui/gui.h index a3d1b2cd..f9ed7226 100644 --- a/tactility/src/services/gui/gui.h +++ b/tactility/src/services/gui/gui.h @@ -3,6 +3,7 @@ #include "app.h" #include "service_manifest.h" #include "view_port.h" +#include #ifdef __cplusplus extern "C" { @@ -10,14 +11,43 @@ extern "C" { typedef struct Gui Gui; +/** + * Set the app viewport in the gui state and request the gui to draw it. + * + * @param app + * @param on_show + * @param on_hide + */ void gui_show_app(App app, ViewPortShowCallback on_show, ViewPortHideCallback on_hide); +/** + * Hide the current app's viewport. + * Does not request a re-draw because after hiding the current app, + * we always show the previous app, and there is always at least 1 app running. + */ void gui_hide_app(); +/** + * Show the on-screen keyboard. + * @param textarea the textarea to focus the input for + */ void gui_keyboard_show(lv_obj_t* textarea); +/** + * Hide the on-screen keyboard. + * Has no effect when the keyboard is not visible. + */ void gui_keyboard_hide(); +/** + * This function is to facilitate hardware keyboards like the one on Lilygo T-Deck. + * The software keyboard is only shown when both of these conditions are true: + * - there is no hardware keyboard + * - TT_CONFIG_FORCE_ONSCREEN_KEYBOARD is set to true in tactility_config.h + * @return if we should show a keyboard for text input inside our apps + */ +bool gui_keyboard_is_enabled(); + #ifdef __cplusplus } #endif diff --git a/tactility/src/services/loader/loader.c b/tactility/src/services/loader/loader.c index 9530496b..f518fe14 100644 --- a/tactility/src/services/loader/loader.c +++ b/tactility/src/services/loader/loader.c @@ -313,6 +313,7 @@ static void loader_stop(TT_UNUSED Service service) { }; tt_message_queue_put(loader_singleton->queue, &message, TtWaitForever); tt_thread_join(loader_singleton->thread); + tt_thread_free(loader_singleton->thread); loader_free(); loader_singleton = NULL; diff --git a/tactility/src/tactility.c b/tactility/src/tactility.c index 48a4e905..82367757 100644 --- a/tactility/src/tactility.c +++ b/tactility/src/tactility.c @@ -21,9 +21,9 @@ static void register_system_apps() { tt_app_manifest_registry_add(&system_info_app); } -static void register_user_apps(const AppManifest* const apps[CONFIG_APPS_LIMIT]) { +static void register_user_apps(const AppManifest* const apps[TT_CONFIG_APPS_LIMIT]) { TT_LOG_I(TAG, "Registering user apps"); - for (size_t i = 0; i < CONFIG_APPS_LIMIT; i++) { + for (size_t i = 0; i < TT_CONFIG_APPS_LIMIT; i++) { const AppManifest* manifest = apps[i]; if (manifest != NULL) { tt_app_manifest_registry_add(manifest); @@ -46,9 +46,9 @@ static void start_system_services() { tt_service_registry_start(loader_service.id); } -static void register_and_start_user_services(const ServiceManifest* const services[CONFIG_SERVICES_LIMIT]) { +static void register_and_start_user_services(const ServiceManifest* const services[TT_CONFIG_SERVICES_LIMIT]) { TT_LOG_I(TAG, "Registering and starting user services"); - for (size_t i = 0; i < CONFIG_SERVICES_LIMIT; i++) { + for (size_t i = 0; i < TT_CONFIG_SERVICES_LIMIT; i++) { const ServiceManifest* manifest = services[i]; if (manifest != NULL) { tt_service_registry_add(manifest); diff --git a/tactility/src/tactility.h b/tactility/src/tactility.h index efd8c89b..311d08ee 100644 --- a/tactility/src/tactility.h +++ b/tactility/src/tactility.h @@ -3,19 +3,17 @@ #include "app_manifest.h" #include "hardware_config.h" #include "service_manifest.h" +#include "tactility_config.h" #ifdef __cplusplus extern "C" { #endif -#define CONFIG_APPS_LIMIT 32 -#define CONFIG_SERVICES_LIMIT 32 - typedef struct { const HardwareConfig* hardware; // List of user applications - const AppManifest* const apps[CONFIG_APPS_LIMIT]; - const ServiceManifest* const services[CONFIG_SERVICES_LIMIT]; + const AppManifest* const apps[TT_CONFIG_APPS_LIMIT]; + const ServiceManifest* const services[TT_CONFIG_SERVICES_LIMIT]; const char* auto_start_app_id; } Config; diff --git a/tactility/src/tactility_config.h b/tactility/src/tactility_config.h new file mode 100644 index 00000000..41780321 --- /dev/null +++ b/tactility/src/tactility_config.h @@ -0,0 +1,6 @@ +#pragma once + +#define TT_CONFIG_APPS_LIMIT 32 +#define TT_CONFIG_SERVICES_LIMIT 32 + +#define TT_CONFIG_FORCE_ONSCREEN_KEYBOARD false \ No newline at end of file diff --git a/tactility/src/ui/lvgl_keypad.c b/tactility/src/ui/lvgl_keypad.c new file mode 100644 index 00000000..38d9d583 --- /dev/null +++ b/tactility/src/ui/lvgl_keypad.c @@ -0,0 +1,23 @@ +#include "lvgl_keypad.h" + +static lv_indev_t* keyboard_device = NULL; + +bool tt_lvgl_keypad_is_available() { + return keyboard_device != NULL; +} + +void tt_lvgl_keypad_set_indev(lv_indev_t* device) { + keyboard_device = device; +} + +void tt_lvgl_keypad_activate(lv_group_t* group) { + if (keyboard_device != NULL) { + lv_indev_set_group(keyboard_device, group); + } +} + +void tt_lvgl_keypad_deactivate() { + if (keyboard_device != NULL) { + lv_indev_set_group(keyboard_device, NULL); + } +} diff --git a/tactility/src/ui/lvgl_keypad.h b/tactility/src/ui/lvgl_keypad.h new file mode 100644 index 00000000..c531c4a4 --- /dev/null +++ b/tactility/src/ui/lvgl_keypad.h @@ -0,0 +1,16 @@ +#pragma once + +#include "lvgl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool tt_lvgl_keypad_is_available(); +void tt_lvgl_keypad_set_indev(lv_indev_t* device); +void tt_lvgl_keypad_activate(lv_group_t* group); +void tt_lvgl_keypad_deactivate(); + +#ifdef __cplusplus +} +#endif \ No newline at end of file