mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-02-18 19:03:16 +00:00
Lots of things "ported" over from the "enhanced" fork. With some adjustments here and there. KeyboardBacklight driver (for T-Deck only currently) Trackball driver (for T-Deck only currently) Keyboard backlight sleep/wake (for T-Deck only currently...also requires keyboard firmware update) Display sleep/wake Files - create file/folder Keyboard settings (for T-Deck only currently) Time & Date settings tweaks Locale settings tweaks Systeminfo additions Espnow wifi coexist initI2cDevices - moved to T-deck init.cpp / initBoot KeyboardInitService - removed, moved to T-deck init.cpp / initBoot Adjusted TIMER_UPDATE_INTERVAL to 2 seconds. Added lock to ActionCreateFolder Maybe missed some things in the list. Display wake could do with some kind of block on wake first touch to prevent UI elements being hit when waking device with touch. Same with encoder/trackball/keyboard press i guess. The original code was written by @cscott0108 at https://github.com/cscott0108/tactility-enhanced-t-deck
146 lines
3.9 KiB
C++
146 lines
3.9 KiB
C++
#include "Trackball.h"
|
|
#include <esp_log.h>
|
|
|
|
static const char* TAG = "Trackball";
|
|
|
|
namespace trackball {
|
|
|
|
static TrackballConfig g_config;
|
|
static lv_indev_t* g_indev = nullptr;
|
|
static bool g_initialized = false;
|
|
static bool g_enabled = true;
|
|
|
|
// Track last GPIO states for edge detection
|
|
static bool g_lastState[5] = {false, false, false, false, false};
|
|
|
|
static void read_cb(lv_indev_t* indev, lv_indev_data_t* data) {
|
|
if (!g_initialized || !g_enabled) {
|
|
data->state = LV_INDEV_STATE_RELEASED;
|
|
data->enc_diff = 0;
|
|
return;
|
|
}
|
|
|
|
const gpio_num_t pins[5] = {
|
|
g_config.pinRight,
|
|
g_config.pinUp,
|
|
g_config.pinLeft,
|
|
g_config.pinDown,
|
|
g_config.pinClick
|
|
};
|
|
|
|
// Read GPIO states and detect changes (active low with pull-up)
|
|
bool currentStates[5];
|
|
for (int i = 0; i < 5; i++) {
|
|
currentStates[i] = gpio_get_level(pins[i]) == 0;
|
|
}
|
|
|
|
// Process directional inputs as encoder steps
|
|
// Right/Down = positive diff (next item), Left/Up = negative diff (prev item)
|
|
int16_t diff = 0;
|
|
|
|
// Right pressed (rising edge)
|
|
if (currentStates[0] && !g_lastState[0]) {
|
|
diff += g_config.movementStep;
|
|
}
|
|
// Up pressed (rising edge)
|
|
if (currentStates[1] && !g_lastState[1]) {
|
|
diff -= g_config.movementStep;
|
|
}
|
|
// Left pressed (rising edge)
|
|
if (currentStates[2] && !g_lastState[2]) {
|
|
diff -= g_config.movementStep;
|
|
}
|
|
// Down pressed (rising edge)
|
|
if (currentStates[3] && !g_lastState[3]) {
|
|
diff += g_config.movementStep;
|
|
}
|
|
|
|
// Update last states
|
|
for (int i = 0; i < 5; i++) {
|
|
g_lastState[i] = currentStates[i];
|
|
}
|
|
|
|
// Update encoder diff and button state
|
|
data->enc_diff = diff;
|
|
data->state = currentStates[4] ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
|
|
|
|
// Trigger activity for wake-on-trackball
|
|
if (diff != 0 || currentStates[4]) {
|
|
lv_disp_trig_activity(nullptr);
|
|
}
|
|
}
|
|
|
|
lv_indev_t* init(const TrackballConfig& config) {
|
|
if (g_initialized) {
|
|
ESP_LOGW(TAG, "Trackball already initialized");
|
|
return g_indev;
|
|
}
|
|
|
|
g_config = config;
|
|
|
|
// Set default movement step if not specified
|
|
if (g_config.movementStep == 0) {
|
|
g_config.movementStep = 10;
|
|
}
|
|
|
|
// Configure all GPIO pins as inputs with pull-ups (active low)
|
|
const gpio_num_t pins[5] = {
|
|
config.pinRight,
|
|
config.pinUp,
|
|
config.pinLeft,
|
|
config.pinDown,
|
|
config.pinClick
|
|
};
|
|
|
|
gpio_config_t io_conf = {};
|
|
io_conf.intr_type = GPIO_INTR_DISABLE;
|
|
io_conf.mode = GPIO_MODE_INPUT;
|
|
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
|
|
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
io_conf.pin_bit_mask = (1ULL << pins[i]);
|
|
gpio_config(&io_conf);
|
|
g_lastState[i] = gpio_get_level(pins[i]) == 0;
|
|
}
|
|
|
|
// Register as LVGL encoder input device for group navigation
|
|
g_indev = lv_indev_create();
|
|
lv_indev_set_type(g_indev, LV_INDEV_TYPE_ENCODER);
|
|
lv_indev_set_read_cb(g_indev, read_cb);
|
|
|
|
if (g_indev) {
|
|
g_initialized = true;
|
|
ESP_LOGI(TAG, "Trackball initialized as encoder (R:%d U:%d L:%d D:%d Click:%d)",
|
|
config.pinRight, config.pinUp, config.pinLeft, config.pinDown,
|
|
config.pinClick);
|
|
return g_indev;
|
|
} else {
|
|
ESP_LOGE(TAG, "Failed to register LVGL input device");
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
void deinit() {
|
|
if (g_indev) {
|
|
lv_indev_delete(g_indev);
|
|
g_indev = nullptr;
|
|
}
|
|
g_initialized = false;
|
|
ESP_LOGI(TAG, "Trackball deinitialized");
|
|
}
|
|
|
|
void setMovementStep(uint8_t step) {
|
|
if (step > 0) {
|
|
g_config.movementStep = step;
|
|
ESP_LOGD(TAG, "Movement step set to %d", step);
|
|
}
|
|
}
|
|
|
|
void setEnabled(bool enabled) {
|
|
g_enabled = enabled;
|
|
ESP_LOGI(TAG, "Trackball %s", enabled ? "enabled" : "disabled");
|
|
}
|
|
|
|
}
|