50c0a14a93
- Added kernel base drivers for: display, pointer (touch, mouse, etc.), keyboard, adc, power supply and backlight - Implement new kernel driver modules: `st7789-module` and `gt911-module` - Implement ESP32 ADC "oneshot" kernel driver - Implement ESP32 backlight kernel driver with ledc API - Implemented `battery-sense` driver that allows for voltage measurement and creates a power supply child device - Updated github actions - Updated flash scripts - Fix for esp32 legacy driver conflict with ADC - Created separate `lilygo-tdeck` and `lilygo-tdeck-plus` devices - Created `lilygo-module` with LilyGO kernel drivers - Fix for intermittent errors in build related to code generation of `devicetree.c` - `lvgl-module` can now map kernel drivers onto LVGL devices - Created `KernelDisplayApp` as a mirror of `HalDisplayApp` (formerly `DisplayApp`) - Removed `struct` and `enum` prefix in a lot of kernel driver cpp source files - `lilygo-tdeck` and `lilygo-tdeck-plus` are now fully relying on kernel drivers and don't use any of the old HAL
200 lines
5.3 KiB
C++
200 lines
5.3 KiB
C++
#include <lilygo/drivers/trackball.h>
|
|
#include <lilygo/drivers/tdeck_trackball.h>
|
|
|
|
#include <Tactility/Assets.h>
|
|
|
|
#include <tactility/device.h>
|
|
#include <tactility/log.h>
|
|
|
|
|
|
constexpr auto* TAG = "Trackball";
|
|
|
|
namespace trackball {
|
|
|
|
static lv_indev_t* g_indev = nullptr;
|
|
static Device* g_device = nullptr;
|
|
static bool g_enabled = true;
|
|
static Mode g_mode = Mode::Encoder;
|
|
static uint8_t g_encoderSensitivity = 1;
|
|
static uint8_t g_pointerSensitivity = 10;
|
|
|
|
// Pointer mode cursor position (screen-relative)
|
|
static int32_t g_cursorX = 160;
|
|
static int32_t g_cursorY = 120;
|
|
|
|
static lv_obj_t* g_cursor = nullptr;
|
|
|
|
// Screen dimensions (T-Deck: 320x240)
|
|
static constexpr int32_t SCREEN_WIDTH = 320;
|
|
static constexpr int32_t SCREEN_HEIGHT = 240;
|
|
static constexpr int32_t CURSOR_SIZE = 16;
|
|
|
|
static inline int32_t clamp(int32_t val, int32_t minVal, int32_t maxVal) {
|
|
if (val < minVal) return minVal;
|
|
if (val > maxVal) return maxVal;
|
|
return val;
|
|
}
|
|
|
|
// Note: must be called from the LVGL thread (main thread), same as the setters below.
|
|
static void read_cb(lv_indev_t* /*indev*/, lv_indev_data_t* data) {
|
|
// Always drain accumulated movement so it doesn't jump on re-enable, but discard it while disabled.
|
|
int32_t dx = 0;
|
|
int32_t dy = 0;
|
|
tdeck_trackball_read_delta(g_device, &dx, &dy);
|
|
if (!g_enabled) {
|
|
dx = 0;
|
|
dy = 0;
|
|
}
|
|
|
|
if (g_mode == Mode::Encoder) {
|
|
int32_t ticks = (dx + dy) * static_cast<int32_t>(g_encoderSensitivity);
|
|
data->enc_diff = static_cast<int16_t>(clamp(ticks, INT16_MIN, INT16_MAX));
|
|
if (ticks != 0) {
|
|
lv_display_trigger_activity(nullptr);
|
|
}
|
|
} else {
|
|
g_cursorX = clamp(g_cursorX + dx * static_cast<int32_t>(g_pointerSensitivity), 0, SCREEN_WIDTH - CURSOR_SIZE - 1);
|
|
g_cursorY = clamp(g_cursorY + dy * static_cast<int32_t>(g_pointerSensitivity), 0, SCREEN_HEIGHT - CURSOR_SIZE - 1);
|
|
data->point.x = static_cast<int16_t>(g_cursorX);
|
|
data->point.y = static_cast<int16_t>(g_cursorY);
|
|
}
|
|
|
|
bool pressed = false;
|
|
if (g_enabled) {
|
|
tdeck_trackball_get_button_pressed(g_device, &pressed);
|
|
}
|
|
data->state = pressed ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
|
|
|
|
if (pressed) {
|
|
lv_display_trigger_activity(nullptr);
|
|
}
|
|
}
|
|
|
|
lv_indev_t* init() {
|
|
if (g_indev != nullptr) {
|
|
LOG_W(TAG, "Already initialized");
|
|
return g_indev;
|
|
}
|
|
|
|
g_device = device_find_first_active_by_type(&TDECK_TRACKBALL_TYPE);
|
|
if (g_device == nullptr) {
|
|
LOG_E(TAG, "tdeck_trackball kernel device not found or not started");
|
|
return nullptr;
|
|
}
|
|
|
|
g_cursorX = SCREEN_WIDTH / 2;
|
|
g_cursorY = SCREEN_HEIGHT / 2;
|
|
|
|
g_indev = lv_indev_create();
|
|
if (g_indev == nullptr) {
|
|
LOG_E(TAG, "Failed to register LVGL input device");
|
|
g_device = nullptr;
|
|
return nullptr;
|
|
}
|
|
|
|
lv_indev_set_type(g_indev, LV_INDEV_TYPE_ENCODER);
|
|
lv_indev_set_read_cb(g_indev, read_cb);
|
|
LOG_I(TAG, "Initialized");
|
|
|
|
return g_indev;
|
|
}
|
|
|
|
// Create cursor for pointer mode
|
|
static void createCursor() {
|
|
if (g_cursor != nullptr || g_indev == nullptr) return;
|
|
|
|
g_cursor = lv_image_create(lv_layer_sys());
|
|
if (g_cursor != nullptr) {
|
|
lv_obj_remove_flag(g_cursor, LV_OBJ_FLAG_CLICKABLE);
|
|
lv_image_set_src(g_cursor, TT_ASSETS_UI_CURSOR);
|
|
lv_indev_set_cursor(g_indev, g_cursor);
|
|
LOG_D(TAG, "Cursor created");
|
|
}
|
|
}
|
|
|
|
// Destroy cursor when switching back to encoder mode
|
|
static void destroyCursor() {
|
|
if (g_cursor == nullptr) return;
|
|
|
|
// Delete the cursor object - this automatically detaches it from the indev
|
|
lv_obj_delete(g_cursor);
|
|
g_cursor = nullptr;
|
|
LOG_D(TAG, "Cursor destroyed");
|
|
}
|
|
|
|
void deinit() {
|
|
if (g_indev == nullptr) return;
|
|
|
|
destroyCursor();
|
|
|
|
lv_indev_delete(g_indev);
|
|
g_indev = nullptr;
|
|
g_device = nullptr;
|
|
|
|
g_mode = Mode::Encoder;
|
|
g_enabled = true;
|
|
LOG_I(TAG, "Deinitialized");
|
|
}
|
|
|
|
void setEncoderSensitivity(uint8_t sensitivity) {
|
|
if (sensitivity > 0) {
|
|
g_encoderSensitivity = sensitivity;
|
|
LOG_D(TAG, "Encoder sensitivity set to %d", sensitivity);
|
|
}
|
|
}
|
|
|
|
void setPointerSensitivity(uint8_t sensitivity) {
|
|
if (sensitivity > 0) {
|
|
g_pointerSensitivity = sensitivity;
|
|
LOG_D(TAG, "Pointer sensitivity set to %d", sensitivity);
|
|
}
|
|
}
|
|
|
|
void setEnabled(bool enabled) {
|
|
g_enabled = enabled;
|
|
|
|
if (g_cursor != nullptr) {
|
|
if (enabled) {
|
|
lv_obj_clear_flag(g_cursor, LV_OBJ_FLAG_HIDDEN);
|
|
} else {
|
|
lv_obj_add_flag(g_cursor, LV_OBJ_FLAG_HIDDEN);
|
|
}
|
|
}
|
|
|
|
LOG_I(TAG, "%s", enabled ? "Enabled" : "Disabled");
|
|
}
|
|
|
|
void setMode(Mode mode) {
|
|
if (g_indev == nullptr) {
|
|
LOG_W(TAG, "Cannot set mode - not initialized");
|
|
return;
|
|
}
|
|
|
|
if (g_mode == mode) {
|
|
return;
|
|
}
|
|
|
|
g_mode = mode;
|
|
|
|
if (mode == Mode::Pointer) {
|
|
lv_indev_set_type(g_indev, LV_INDEV_TYPE_POINTER);
|
|
createCursor();
|
|
if (!g_enabled && g_cursor != nullptr) {
|
|
lv_obj_add_flag(g_cursor, LV_OBJ_FLAG_HIDDEN);
|
|
}
|
|
g_cursorX = SCREEN_WIDTH / 2;
|
|
g_cursorY = SCREEN_HEIGHT / 2;
|
|
LOG_I(TAG, "Switched to Pointer mode");
|
|
} else {
|
|
destroyCursor();
|
|
lv_indev_set_type(g_indev, LV_INDEV_TYPE_ENCODER);
|
|
LOG_I(TAG, "Switched to Encoder mode");
|
|
}
|
|
}
|
|
|
|
Mode getMode() {
|
|
return g_mode;
|
|
}
|
|
|
|
}
|