Fixes and improvements (#616)
This commit is contained in:
committed by
GitHub
parent
f943c4dd69
commit
b03759a111
@@ -0,0 +1,133 @@
|
||||
#include <Tactility/lvgl/KeyboardDeviceListener.h>
|
||||
#include <Tactility/Mutex.h>
|
||||
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/device_listener.h>
|
||||
#include <tactility/drivers/keyboard.h>
|
||||
#include <tactility/log.h>
|
||||
|
||||
#include <lvgl/devices/keyboard.h>
|
||||
#include <lvgl/lvgl.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
constexpr auto* TAG = "KeyboardDeviceListener";
|
||||
|
||||
namespace {
|
||||
|
||||
struct KeyboardBinding {
|
||||
Device* device;
|
||||
lv_indev_t* indev;
|
||||
};
|
||||
|
||||
Mutex& bindingsMutex() {
|
||||
static Mutex mutex;
|
||||
return mutex;
|
||||
}
|
||||
|
||||
std::vector<KeyboardBinding>& bindings() {
|
||||
static std::vector<KeyboardBinding> list;
|
||||
return list;
|
||||
}
|
||||
|
||||
// Guards, under bindingsMutex, against onKeyboardDeviceStarted() registering a binding after
|
||||
// stopKeyboardDeviceListener() has already drained bindings() and returned.
|
||||
bool& listenerActive() {
|
||||
static bool active = false;
|
||||
return active;
|
||||
}
|
||||
|
||||
void onKeyboardDeviceStarted(Device* device) {
|
||||
// Held for the whole add+register step so a same-device STOPPING can't observe the binding
|
||||
// as neither-registered-nor-added: onKeyboardDeviceStopped() and shutdown's drain both take
|
||||
// this same lock, so they see either the fully-registered binding or nothing at all.
|
||||
auto lock = bindingsMutex().asScopedLock();
|
||||
lock.lock();
|
||||
if (!listenerActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
lv_indev_t* indev = nullptr;
|
||||
lvgl_lock();
|
||||
error_t error = lvgl_keyboard_add(device, lv_display_get_default(), &indev);
|
||||
lvgl_unlock();
|
||||
if (error != ERROR_NONE) {
|
||||
LOG_E(TAG, "failed to bind keyboard device %s to LVGL", device->name);
|
||||
return;
|
||||
}
|
||||
bindings().push_back({ device, indev });
|
||||
}
|
||||
|
||||
void onKeyboardDeviceStopped(Device* device) {
|
||||
auto lock = bindingsMutex().asScopedLock();
|
||||
lock.lock();
|
||||
lv_indev_t* indev = nullptr;
|
||||
auto& list = bindings();
|
||||
for (auto it = list.begin(); it != list.end(); ++it) {
|
||||
if (it->device == device) {
|
||||
indev = it->indev;
|
||||
list.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (indev == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
lvgl_lock();
|
||||
lvgl_keyboard_remove(indev);
|
||||
lvgl_unlock();
|
||||
}
|
||||
|
||||
void onDeviceEvent(Device* device, DeviceEvent event, void* context) {
|
||||
(void)context;
|
||||
if (device_get_type(device) != &KEYBOARD_TYPE) {
|
||||
return;
|
||||
}
|
||||
// Detach on STOPPING (before stop_device() frees the device's resources), not STOPPED (which
|
||||
// only fires after stop_device() already ran) - see DEVICE_EVENT_STOPPING's doc comment.
|
||||
if (event == DEVICE_EVENT_STARTED) {
|
||||
onKeyboardDeviceStarted(device);
|
||||
} else if (event == DEVICE_EVENT_STOPPING) {
|
||||
onKeyboardDeviceStopped(device);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void startKeyboardDeviceListener() {
|
||||
auto lock = bindingsMutex().asScopedLock();
|
||||
lock.lock();
|
||||
listenerActive() = true;
|
||||
device_listener_add(onDeviceEvent, nullptr);
|
||||
}
|
||||
|
||||
void stopKeyboardDeviceListener() {
|
||||
std::vector<KeyboardBinding> remaining;
|
||||
{
|
||||
// device_listener_remove() doesn't wait for an in-flight onDeviceEvent() to finish, so a
|
||||
// STARTED callback already past this point can still land after we drain below; it takes
|
||||
// this same lock and checks listenerActive() before registering, so it backs off instead
|
||||
// of adding a binding nothing will ever remove.
|
||||
auto lock = bindingsMutex().asScopedLock();
|
||||
lock.lock();
|
||||
listenerActive() = false;
|
||||
device_listener_remove(onDeviceEvent);
|
||||
remaining = std::move(bindings());
|
||||
bindings().clear();
|
||||
}
|
||||
|
||||
if (remaining.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
lvgl_lock();
|
||||
for (auto& binding : remaining) {
|
||||
lvgl_keyboard_remove(binding.indev);
|
||||
}
|
||||
lvgl_unlock();
|
||||
}
|
||||
|
||||
} // namespace tt::lvgl
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <lvgl/devices/keyboard.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <new>
|
||||
|
||||
namespace tt::lvgl {
|
||||
|
||||
@@ -286,12 +287,19 @@ static void usbHidInputTask(void* arg) {
|
||||
void startUsbHidInput() {
|
||||
if (s_ctx != nullptr) return;
|
||||
|
||||
auto* ctx = new UsbHidInputCtx();
|
||||
static constexpr MemoryPolicy CTX_POLICY = { 0, MEMORY_CAPABILITY_EXTERNAL, 0 };
|
||||
auto* ctx_mem = memory_alloc_with_policy(sizeof(UsbHidInputCtx), &CTX_POLICY);
|
||||
if (!ctx_mem) {
|
||||
LOG_E(TAG, "failed to allocate context");
|
||||
return;
|
||||
}
|
||||
auto* ctx = new (ctx_mem) UsbHidInputCtx();
|
||||
|
||||
ctx->hid_queue = xQueueCreate(HID_EVENT_QUEUE_SIZE, sizeof(UsbHidEvent));
|
||||
if (!ctx->hid_queue) {
|
||||
LOG_E(TAG, "failed to create HID event queue");
|
||||
delete ctx;
|
||||
ctx->~UsbHidInputCtx();
|
||||
memory_free(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -299,7 +307,8 @@ void startUsbHidInput() {
|
||||
if (!ctx->key_queue) {
|
||||
LOG_E(TAG, "failed to create key event queue");
|
||||
vQueueDelete(ctx->hid_queue);
|
||||
delete ctx;
|
||||
ctx->~UsbHidInputCtx();
|
||||
memory_free(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -308,7 +317,8 @@ void startUsbHidInput() {
|
||||
LOG_E(TAG, "failed to create task done semaphore");
|
||||
vQueueDelete(ctx->hid_queue);
|
||||
vQueueDelete(ctx->key_queue);
|
||||
delete ctx;
|
||||
ctx->~UsbHidInputCtx();
|
||||
memory_free(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -371,7 +381,8 @@ void startUsbHidInput() {
|
||||
vQueueDelete(ctx->hid_queue);
|
||||
vQueueDelete(ctx->key_queue);
|
||||
vSemaphoreDelete(ctx->task_done);
|
||||
delete ctx;
|
||||
ctx->~UsbHidInputCtx();
|
||||
memory_free(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -429,7 +440,8 @@ void stopUsbHidInput() {
|
||||
vQueueDelete(ctx->hid_queue);
|
||||
vQueueDelete(ctx->key_queue);
|
||||
vSemaphoreDelete(ctx->task_done);
|
||||
delete ctx;
|
||||
ctx->~UsbHidInputCtx();
|
||||
memory_free(ctx);
|
||||
|
||||
LOG_I(TAG, "stopped");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user