LilyGo T-Deck keyboard support & display driver improvements (#19)
* LilyGo T-Deck keyboard support * reverse logic * docs and readability * cleanup * optimize driver buffer * cleanup
This commit is contained in:
parent
14eb43211d
commit
ccbe6b7ab8
@ -26,9 +26,11 @@ Like `some_feature_i.h`
|
|||||||
|
|
||||||
Names are snake-case.
|
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.
|
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_`.
|
Public functions have the feature name after `tt_`.
|
||||||
|
|
||||||
If a feature has setters or getters, it's added after the feature name part.
|
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:
|
Example:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
void tt_feature_get_name() {
|
void tt_counter_get_limit() {
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@ -1,29 +1,61 @@
|
|||||||
#include "esp_log.h"
|
#include "config.h"
|
||||||
#include "driver/gpio.h"
|
#include "keyboard.h"
|
||||||
#include "kernel.h"
|
#include "kernel.h"
|
||||||
#include "esp_lvgl_port.h"
|
#include "log.h"
|
||||||
|
|
||||||
#define TAG "lilygo_tdeck_bootstrap"
|
#define TAG "tdeck_bootstrap"
|
||||||
#define TDECK_PERI_POWERON GPIO_NUM_10
|
|
||||||
|
|
||||||
lv_disp_t* lilygo_tdeck_init_display();
|
lv_disp_t* lilygo_tdeck_init_display();
|
||||||
|
|
||||||
static void tdeck_power_on() {
|
static bool tdeck_power_on() {
|
||||||
ESP_LOGI(TAG, "power on");
|
ESP_LOGI(TAG, "power on");
|
||||||
gpio_config_t device_power_signal_config = {
|
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,
|
.mode = GPIO_MODE_OUTPUT,
|
||||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||||
.intr_type = GPIO_INTR_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() {
|
static bool init_i2c() {
|
||||||
tdeck_power_on();
|
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
|
// Give keyboard's ESP time to boot
|
||||||
// It uses I2C and seems to interfere with the touch driver
|
// It uses I2C and seems to interfere with the touch driver
|
||||||
tt_delay_ms(500);
|
tt_delay_ms(500);
|
||||||
|
|
||||||
|
if (!init_i2c()) {
|
||||||
|
TT_LOG_E(TAG, "failed to init I2C");
|
||||||
|
}
|
||||||
|
|
||||||
|
keyboard_wait_for_response();
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
7
boards/lilygo_tdeck/config.h
Normal file
7
boards/lilygo_tdeck/config.h
Normal file
@ -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
|
||||||
@ -6,7 +6,7 @@
|
|||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_lvgl_port.h"
|
#include "esp_lvgl_port.h"
|
||||||
|
|
||||||
#define TAG "lilygo_tdeck_display"
|
#define TAG "tdeck_display"
|
||||||
|
|
||||||
#define LCD_SPI_HOST SPI2_HOST
|
#define LCD_SPI_HOST SPI2_HOST
|
||||||
#define LCD_PIN_SCLK GPIO_NUM_40
|
#define LCD_PIN_SCLK GPIO_NUM_40
|
||||||
@ -21,6 +21,7 @@
|
|||||||
#define LCD_VERTICAL_RESOLUTION 240
|
#define LCD_VERTICAL_RESOLUTION 240
|
||||||
#define LCD_BITS_PER_PIXEL 16
|
#define LCD_BITS_PER_PIXEL 16
|
||||||
#define LCD_DRAW_BUFFER_HEIGHT (LCD_VERTICAL_RESOLUTION / 10)
|
#define LCD_DRAW_BUFFER_HEIGHT (LCD_VERTICAL_RESOLUTION / 10)
|
||||||
|
#define LCD_SPI_TRANSFER_HEIGHT (LCD_VERTICAL_RESOLUTION / 10)
|
||||||
|
|
||||||
// Backlight PWM
|
// Backlight PWM
|
||||||
#define LCD_BACKLIGHT_LEDC_TIMER LEDC_TIMER_0
|
#define LCD_BACKLIGHT_LEDC_TIMER LEDC_TIMER_0
|
||||||
@ -60,7 +61,7 @@ static void tdeck_backlight() {
|
|||||||
lv_disp_t* lilygo_tdeck_init_display() {
|
lv_disp_t* lilygo_tdeck_init_display() {
|
||||||
ESP_LOGI(TAG, "creating 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 = {
|
spi_bus_config_t bus_config = {
|
||||||
.sclk_io_num = LCD_PIN_SCLK,
|
.sclk_io_num = LCD_PIN_SCLK,
|
||||||
@ -68,7 +69,7 @@ lv_disp_t* lilygo_tdeck_init_display() {
|
|||||||
.miso_io_num = LCD_PIN_MISO,
|
.miso_io_num = LCD_PIN_MISO,
|
||||||
.quadwp_io_num = -1, // Quad SPI LCD driver is not yet supported
|
.quadwp_io_num = -1, // Quad SPI LCD driver is not yet supported
|
||||||
.quadhd_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) {
|
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,
|
.io_handle = io_handle,
|
||||||
.panel_handle = panel_handle,
|
.panel_handle = panel_handle,
|
||||||
.buffer_size = LCD_HORIZONTAL_RESOLUTION * LCD_DRAW_BUFFER_HEIGHT * (LCD_BITS_PER_PIXEL / 8),
|
.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,
|
.hres = LCD_HORIZONTAL_RESOLUTION,
|
||||||
.vres = LCD_VERTICAL_RESOLUTION,
|
.vres = LCD_VERTICAL_RESOLUTION,
|
||||||
.monochrome = false,
|
.monochrome = false,
|
||||||
.rotation = {
|
.rotation = {
|
||||||
.swap_xy = true, // TODO: check if code above is still needed
|
.swap_xy = true,
|
||||||
.mirror_x = true,
|
.mirror_x = true,
|
||||||
.mirror_y = false,
|
.mirror_y = false,
|
||||||
},
|
},
|
||||||
.flags = {
|
.flags = {
|
||||||
.buff_dma = true,
|
.buff_dma = false,
|
||||||
|
.buff_spiram = true,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
95
boards/lilygo_tdeck/keyboard.c
Normal file
95
boards/lilygo_tdeck/keyboard.c
Normal file
@ -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 <driver/i2c.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
18
boards/lilygo_tdeck/keyboard.h
Normal file
18
boards/lilygo_tdeck/keyboard.h
Normal file
@ -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
|
||||||
@ -1,9 +1,10 @@
|
|||||||
#include "esp_lvgl_port.h"
|
#include "esp_lvgl_port.h"
|
||||||
|
#include "keyboard.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "ui/lvgl_sync.h"
|
#include "ui/lvgl_sync.h"
|
||||||
#include <thread.h>
|
#include <thread.h>
|
||||||
|
|
||||||
#define TAG "lilygo_tdeck_lvgl"
|
#define TAG "tdeck_lvgl"
|
||||||
|
|
||||||
lv_disp_t* lilygo_tdeck_init_display();
|
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);
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Add touch
|
// Add touch
|
||||||
if (!lilygo_tdeck_init_touch(&touch_io_handle, &touch_handle)) {
|
if (!lilygo_tdeck_init_touch(&touch_io_handle, &touch_handle)) {
|
||||||
return false;
|
return false;
|
||||||
@ -53,5 +53,7 @@ bool lilygo_init_lvgl() {
|
|||||||
// Set syncing functions
|
// Set syncing functions
|
||||||
tt_lvgl_sync_set(&lvgl_port_lock, &lvgl_port_unlock);
|
tt_lvgl_sync_set(&lvgl_port_lock, &lvgl_port_unlock);
|
||||||
|
|
||||||
|
keyboard_alloc(display);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,37 +1,16 @@
|
|||||||
|
#include "config.h"
|
||||||
|
#include "driver/i2c.h"
|
||||||
|
#include "esp_err.h"
|
||||||
#include "esp_lcd_touch_gt911.h"
|
#include "esp_lcd_touch_gt911.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_err.h"
|
|
||||||
#include "driver/i2c.h"
|
|
||||||
|
|
||||||
#define TOUCH_I2C_PORT 0
|
#define TAG "tdeck_touch"
|
||||||
|
|
||||||
#define TAG "lilygo_tdeck_touch"
|
|
||||||
|
|
||||||
bool lilygo_tdeck_init_touch(esp_lcd_panel_io_handle_t* io_handle, esp_lcd_touch_handle_t* touch_handle) {
|
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");
|
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();
|
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)TDECK_I2C_BUS_HANDLE, &touch_io_config, io_handle) != ESP_OK) {
|
||||||
if (esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)TOUCH_I2C_PORT, &touch_io_config, io_handle) != ESP_OK) {
|
|
||||||
ESP_LOGE(TAG, "touch io i2c creation failed");
|
ESP_LOGE(TAG, "touch io i2c creation failed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,9 @@
|
|||||||
- Replace FreeRTOS semaphore from `Loader` with internal `Mutex`
|
- Replace FreeRTOS semaphore from `Loader` with internal `Mutex`
|
||||||
- Create unit tests for `tactility-core` and `tactility` (PC-only for now)
|
- Create unit tests for `tactility-core` and `tactility` (PC-only for now)
|
||||||
- Have a way to deinit LVGL drivers that are created from `HardwareConfig`
|
- 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
|
# 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.
|
- 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
|
- BadUSB
|
||||||
- IR transceiver app
|
- IR transceiver app
|
||||||
- GPIO status viewer
|
- GPIO status viewer
|
||||||
- BlueTooth keyboard app
|
- BlueTooth keyboard app
|
||||||
|
- Investigate CSI https://stevenmhernandez.github.io/ESP32-CSI-Tool/
|
||||||
@ -4,6 +4,7 @@
|
|||||||
#include "lvgl.h"
|
#include "lvgl.h"
|
||||||
#include "services/gui/gui.h"
|
#include "services/gui/gui.h"
|
||||||
#include "services/wifi/wifi_credentials.h"
|
#include "services/wifi/wifi_credentials.h"
|
||||||
|
#include "ui/lvgl_keypad.h"
|
||||||
#include "ui/spacer.h"
|
#include "ui/spacer.h"
|
||||||
#include "ui/style.h"
|
#include "ui/style.h"
|
||||||
#include "wifi_connect.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);
|
wifi_connect_view_create_bottom_buttons(wifi, parent);
|
||||||
|
|
||||||
lv_obj_add_event_cb(view->ssid_textarea, show_keyboard, LV_EVENT_FOCUSED, NULL);
|
if (gui_keyboard_is_enabled()) {
|
||||||
lv_obj_add_event_cb(view->ssid_textarea, hide_keyboard, LV_EVENT_DEFOCUSED, NULL);
|
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_READY, NULL);
|
lv_obj_add_event_cb(view->ssid_textarea, hide_keyboard, LV_EVENT_DEFOCUSED, NULL);
|
||||||
lv_obj_add_event_cb(view->password_textarea, show_keyboard, LV_EVENT_FOCUSED, NULL);
|
lv_obj_add_event_cb(view->ssid_textarea, hide_keyboard, LV_EVENT_READY, NULL);
|
||||||
lv_obj_add_event_cb(view->password_textarea, hide_keyboard, LV_EVENT_DEFOCUSED, 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_READY, 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
|
// Init from app parameters
|
||||||
Bundle* _Nullable bundle = tt_app_get_parameters(app);
|
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);
|
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) {
|
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(
|
void wifi_connect_view_update(
|
||||||
|
|||||||
@ -15,6 +15,7 @@ typedef struct {
|
|||||||
lv_obj_t* connect_button;
|
lv_obj_t* connect_button;
|
||||||
lv_obj_t* cancel_button;
|
lv_obj_t* cancel_button;
|
||||||
lv_obj_t* remember_switch;
|
lv_obj_t* remember_switch;
|
||||||
|
lv_group_t* group;
|
||||||
} WifiConnectView;
|
} WifiConnectView;
|
||||||
|
|
||||||
void wifi_connect_view_create(App app, void* wifi, lv_obj_t* parent);
|
void wifi_connect_view_create(App app, void* wifi, lv_obj_t* parent);
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
#include "app_i.h"
|
#include "app_i.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
static AppFlags tt_app_get_flags_default(AppType type);
|
static AppFlags tt_app_get_flags_default(AppType type);
|
||||||
|
|||||||
@ -6,9 +6,9 @@
|
|||||||
void tt_hardware_init(const HardwareConfig* config) {
|
void tt_hardware_init(const HardwareConfig* config) {
|
||||||
if (config->bootstrap != NULL) {
|
if (config->bootstrap != NULL) {
|
||||||
TT_LOG_I(TAG, "Bootstrapping");
|
TT_LOG_I(TAG, "Bootstrapping");
|
||||||
config->bootstrap();
|
tt_check(config->bootstrap(), "bootstrap failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
tt_check(config->init_lvgl);
|
tt_check(config->init_lvgl, "lvlg init not set");
|
||||||
config->init_lvgl();
|
tt_check(config->init_lvgl(), "lvgl init failed");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,8 @@
|
|||||||
#include "gui_i.h"
|
#include "gui_i.h"
|
||||||
|
|
||||||
#include "check.h"
|
#include "tactility.h"
|
||||||
#include "core_extra_defines.h"
|
|
||||||
#include "ui/lvgl_sync.h"
|
#include "ui/lvgl_sync.h"
|
||||||
#include "kernel.h"
|
#include "ui/lvgl_keypad.h"
|
||||||
#include "log.h"
|
|
||||||
|
|
||||||
#ifdef ESP_PLATFORM
|
#ifdef ESP_PLATFORM
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
@ -30,7 +28,7 @@ Gui* gui_alloc() {
|
|||||||
&gui_main,
|
&gui_main,
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
instance->mutex = tt_mutex_alloc(MutexTypeNormal);
|
instance->mutex = tt_mutex_alloc(MutexTypeRecursive);
|
||||||
instance->keyboard = NULL;
|
instance->keyboard = NULL;
|
||||||
|
|
||||||
tt_check(tt_lvgl_lock(1000 / portTICK_PERIOD_MS));
|
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() {
|
void gui_hide_app() {
|
||||||
gui_lock();
|
gui_lock();
|
||||||
ViewPort* view_port = gui->app_view_port;
|
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);
|
ThreadId thread_id = tt_thread_get_id(gui->thread);
|
||||||
tt_thread_flags_set(thread_id, GUI_THREAD_FLAG_EXIT);
|
tt_thread_flags_set(thread_id, GUI_THREAD_FLAG_EXIT);
|
||||||
tt_thread_join(gui->thread);
|
tt_thread_join(gui->thread);
|
||||||
|
tt_thread_free(gui->thread);
|
||||||
|
|
||||||
gui_unlock();
|
gui_unlock();
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
#include "app.h"
|
#include "app.h"
|
||||||
#include "service_manifest.h"
|
#include "service_manifest.h"
|
||||||
#include "view_port.h"
|
#include "view_port.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -10,14 +11,43 @@ extern "C" {
|
|||||||
|
|
||||||
typedef struct Gui Gui;
|
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);
|
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();
|
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);
|
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();
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -313,6 +313,7 @@ static void loader_stop(TT_UNUSED Service service) {
|
|||||||
};
|
};
|
||||||
tt_message_queue_put(loader_singleton->queue, &message, TtWaitForever);
|
tt_message_queue_put(loader_singleton->queue, &message, TtWaitForever);
|
||||||
tt_thread_join(loader_singleton->thread);
|
tt_thread_join(loader_singleton->thread);
|
||||||
|
tt_thread_free(loader_singleton->thread);
|
||||||
|
|
||||||
loader_free();
|
loader_free();
|
||||||
loader_singleton = NULL;
|
loader_singleton = NULL;
|
||||||
|
|||||||
@ -21,9 +21,9 @@ static void register_system_apps() {
|
|||||||
tt_app_manifest_registry_add(&system_info_app);
|
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");
|
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];
|
const AppManifest* manifest = apps[i];
|
||||||
if (manifest != NULL) {
|
if (manifest != NULL) {
|
||||||
tt_app_manifest_registry_add(manifest);
|
tt_app_manifest_registry_add(manifest);
|
||||||
@ -46,9 +46,9 @@ static void start_system_services() {
|
|||||||
tt_service_registry_start(loader_service.id);
|
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");
|
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];
|
const ServiceManifest* manifest = services[i];
|
||||||
if (manifest != NULL) {
|
if (manifest != NULL) {
|
||||||
tt_service_registry_add(manifest);
|
tt_service_registry_add(manifest);
|
||||||
|
|||||||
@ -3,19 +3,17 @@
|
|||||||
#include "app_manifest.h"
|
#include "app_manifest.h"
|
||||||
#include "hardware_config.h"
|
#include "hardware_config.h"
|
||||||
#include "service_manifest.h"
|
#include "service_manifest.h"
|
||||||
|
#include "tactility_config.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define CONFIG_APPS_LIMIT 32
|
|
||||||
#define CONFIG_SERVICES_LIMIT 32
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const HardwareConfig* hardware;
|
const HardwareConfig* hardware;
|
||||||
// List of user applications
|
// List of user applications
|
||||||
const AppManifest* const apps[CONFIG_APPS_LIMIT];
|
const AppManifest* const apps[TT_CONFIG_APPS_LIMIT];
|
||||||
const ServiceManifest* const services[CONFIG_SERVICES_LIMIT];
|
const ServiceManifest* const services[TT_CONFIG_SERVICES_LIMIT];
|
||||||
const char* auto_start_app_id;
|
const char* auto_start_app_id;
|
||||||
} Config;
|
} Config;
|
||||||
|
|
||||||
|
|||||||
6
tactility/src/tactility_config.h
Normal file
6
tactility/src/tactility_config.h
Normal file
@ -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
|
||||||
23
tactility/src/ui/lvgl_keypad.c
Normal file
23
tactility/src/ui/lvgl_keypad.c
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
16
tactility/src/ui/lvgl_keypad.h
Normal file
16
tactility/src/ui/lvgl_keypad.h
Normal file
@ -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
|
||||||
Loading…
x
Reference in New Issue
Block a user