summaryrefslogtreecommitdiff
path: root/src/input
diff options
context:
space:
mode:
Diffstat (limited to 'src/input')
-rw-r--r--src/input/include/input_device.hpp4
-rw-r--r--src/input/include/input_hook.hpp10
-rw-r--r--src/input/include/input_nav_buttons.hpp2
-rw-r--r--src/input/include/input_touch_dpad.hpp2
-rw-r--r--src/input/include/input_touch_wheel.hpp2
-rw-r--r--src/input/include/input_volume_buttons.hpp2
-rw-r--r--src/input/include/lvgl_input_driver.hpp52
-rw-r--r--src/input/input_hook.cpp19
-rw-r--r--src/input/input_nav_buttons.cpp3
-rw-r--r--src/input/input_touch_dpad.cpp3
-rw-r--r--src/input/input_touch_wheel.cpp3
-rw-r--r--src/input/input_volume_buttons.cpp3
-rw-r--r--src/input/lvgl_input_driver.cpp137
13 files changed, 212 insertions, 30 deletions
diff --git a/src/input/include/input_device.hpp b/src/input/include/input_device.hpp
index 59456351..d944c3bf 100644
--- a/src/input/include/input_device.hpp
+++ b/src/input/include/input_device.hpp
@@ -29,7 +29,9 @@ class IInputDevice {
virtual auto read(lv_indev_data_t* data) -> void = 0;
virtual auto name() -> std::string = 0;
- virtual auto hooks() -> std::vector<TriggerHooks> { return {}; }
+ virtual auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> {
+ return {};
+ }
};
} // namespace input
diff --git a/src/input/include/input_hook.hpp b/src/input/include/input_hook.hpp
index 81eb80d9..a257c04a 100644
--- a/src/input/include/input_hook.hpp
+++ b/src/input/include/input_hook.hpp
@@ -32,6 +32,10 @@ class Hook {
auto name() const -> const std::string& { return name_; }
auto callback() -> std::optional<HookCallback>;
+ // Not copyable or movable.
+ Hook(const Hook&) = delete;
+ Hook& operator=(const Hook&) = delete;
+
private:
std::string name_;
std::optional<HookCallback> default_;
@@ -51,7 +55,11 @@ class TriggerHooks {
auto override(Trigger::State, std::optional<HookCallback>) -> void;
auto name() const -> const std::string&;
- auto pushHooks(lua_State*) -> void;
+ auto hooks() -> std::vector<std::reference_wrapper<Hook>>;
+
+ // Not copyable or movable.
+ TriggerHooks(const TriggerHooks&) = delete;
+ TriggerHooks& operator=(const TriggerHooks&) = delete;
private:
std::string name_;
diff --git a/src/input/include/input_nav_buttons.hpp b/src/input/include/input_nav_buttons.hpp
index 4e4952c9..9feeb375 100644
--- a/src/input/include/input_nav_buttons.hpp
+++ b/src/input/include/input_nav_buttons.hpp
@@ -26,7 +26,7 @@ class NavButtons : public IInputDevice {
auto read(lv_indev_data_t* data) -> void override;
auto name() -> std::string override;
- auto hooks() -> std::vector<TriggerHooks> override;
+ auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> override;
private:
drivers::IGpios& gpios_;
diff --git a/src/input/include/input_touch_dpad.hpp b/src/input/include/input_touch_dpad.hpp
index 691e3243..0c45b2d9 100644
--- a/src/input/include/input_touch_dpad.hpp
+++ b/src/input/include/input_touch_dpad.hpp
@@ -25,7 +25,7 @@ class TouchDPad : public IInputDevice {
auto read(lv_indev_data_t* data) -> void override;
auto name() -> std::string override;
- auto hooks() -> std::vector<TriggerHooks> override;
+ auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> override;
private:
drivers::TouchWheel& wheel_;
diff --git a/src/input/include/input_touch_wheel.hpp b/src/input/include/input_touch_wheel.hpp
index 1f116da9..764cc68d 100644
--- a/src/input/include/input_touch_wheel.hpp
+++ b/src/input/include/input_touch_wheel.hpp
@@ -28,7 +28,7 @@ class TouchWheel : public IInputDevice {
auto read(lv_indev_data_t* data) -> void override;
auto name() -> std::string override;
- auto hooks() -> std::vector<TriggerHooks> override;
+ auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> override;
auto sensitivity() -> lua::Property&;
diff --git a/src/input/include/input_volume_buttons.hpp b/src/input/include/input_volume_buttons.hpp
index a684aa48..e3246df4 100644
--- a/src/input/include/input_volume_buttons.hpp
+++ b/src/input/include/input_volume_buttons.hpp
@@ -25,7 +25,7 @@ class VolumeButtons : public IInputDevice {
auto read(lv_indev_data_t* data) -> void override;
auto name() -> std::string override;
- auto hooks() -> std::vector<TriggerHooks> override;
+ auto triggers() -> std::vector<std::reference_wrapper<TriggerHooks>> override;
private:
drivers::IGpios& gpios_;
diff --git a/src/input/include/lvgl_input_driver.hpp b/src/input/include/lvgl_input_driver.hpp
index 9f43d27f..9adaf143 100644
--- a/src/input/include/lvgl_input_driver.hpp
+++ b/src/input/include/lvgl_input_driver.hpp
@@ -18,6 +18,8 @@
#include "hal/lv_hal_indev.h"
#include "input_device.hpp"
+#include "input_hook.hpp"
+#include "lua_thread.hpp"
#include "nvs.hpp"
#include "property.hpp"
#include "touchwheel.hpp"
@@ -54,6 +56,56 @@ class LvglInputDriver {
std::vector<std::shared_ptr<IInputDevice>> inputs_;
std::vector<std::shared_ptr<IFeedbackDevice>> feedbacks_;
+ /*
+ * Key for identifying which device, trigger, and specific hook are being
+ * overriden by Lua.
+ */
+ struct OverrideSelector {
+ std::string device_name;
+ std::string trigger_name;
+ std::string hook_name;
+
+ friend bool operator<(const OverrideSelector& l,
+ const OverrideSelector& r) {
+ return std::tie(l.device_name, l.trigger_name, l.hook_name) <
+ std::tie(r.device_name, r.trigger_name, r.hook_name);
+ }
+ };
+
+ /* Userdata object for tracking the Lua mirror of a TriggerHooks object. */
+ class LuaTrigger {
+ public:
+ LuaTrigger(LvglInputDriver&, IInputDevice&, TriggerHooks&);
+
+ static auto get(lua_State*, int idx) -> LuaTrigger&;
+ static auto luaGc(lua_State*) -> int;
+ static auto luaToString(lua_State*) -> int;
+ static auto luaNewIndex(lua_State*) -> int;
+
+ static constexpr struct luaL_Reg kFuncs[] = {{"__gc", luaGc},
+ {"__tostring", luaToString},
+ {"__newindex", luaNewIndex},
+ {NULL, NULL}};
+
+ private:
+ LvglInputDriver* driver_;
+
+ std::string device_;
+ std::string trigger_;
+ std::map<std::string, std::string> hooks_;
+ };
+
+ /* A hook override implemented as a lua callback */
+ struct LuaOverride {
+ lua_State* L;
+ int ref;
+ };
+
+ std::map<OverrideSelector, LuaOverride> overrides_;
+
+ auto setOverride(lua_State* L, const OverrideSelector&) -> void;
+ auto applyOverride(const OverrideSelector&, LuaOverride&) -> void;
+
bool is_locked_;
};
diff --git a/src/input/input_hook.cpp b/src/input/input_hook.cpp
index 1bb92196..48d6c2a4 100644
--- a/src/input/input_hook.cpp
+++ b/src/input/input_hook.cpp
@@ -85,23 +85,8 @@ auto TriggerHooks::name() const -> const std::string& {
return name_;
}
-auto TriggerHooks::pushHooks(lua_State* L) -> void {
- lua_newtable(L);
-
- auto add_trigger = [&](Hook& h) {
- lua_pushlstring(L, h.name().data(), h.name().size());
- auto cb = h.callback();
- if (cb) {
- lua_pushlstring(L, cb->name.data(), cb->name.size());
- } else {
- lua_pushnil(L);
- }
- lua_rawset(L, -3);
- };
-
- add_trigger(click_);
- add_trigger(long_press_);
- add_trigger(repeat_);
+auto TriggerHooks::hooks() -> std::vector<std::reference_wrapper<Hook>> {
+ return {click_, long_press_, repeat_};
}
} // namespace input
diff --git a/src/input/input_nav_buttons.cpp b/src/input/input_nav_buttons.cpp
index 7e579a16..522f8d6e 100644
--- a/src/input/input_nav_buttons.cpp
+++ b/src/input/input_nav_buttons.cpp
@@ -27,7 +27,8 @@ auto NavButtons::name() -> std::string {
return "buttons";
}
-auto NavButtons::hooks() -> std::vector<TriggerHooks> {
+auto NavButtons::triggers()
+ -> std::vector<std::reference_wrapper<TriggerHooks>> {
return {up_, down_};
}
diff --git a/src/input/input_touch_dpad.cpp b/src/input/input_touch_dpad.cpp
index f0805993..d8eff09b 100644
--- a/src/input/input_touch_dpad.cpp
+++ b/src/input/input_touch_dpad.cpp
@@ -55,7 +55,8 @@ auto TouchDPad::name() -> std::string {
return "dpad";
}
-auto TouchDPad::hooks() -> std::vector<TriggerHooks> {
+auto TouchDPad::triggers()
+ -> std::vector<std::reference_wrapper<TriggerHooks>> {
return {centre_, up_, right_, down_, left_};
}
diff --git a/src/input/input_touch_wheel.cpp b/src/input/input_touch_wheel.cpp
index 121b1ee5..67cab3bf 100644
--- a/src/input/input_touch_wheel.cpp
+++ b/src/input/input_touch_wheel.cpp
@@ -92,7 +92,8 @@ auto TouchWheel::name() -> std::string {
return "wheel";
}
-auto TouchWheel::hooks() -> std::vector<TriggerHooks> {
+auto TouchWheel::triggers()
+ -> std::vector<std::reference_wrapper<TriggerHooks>> {
return {centre_, up_, right_, down_, left_};
}
diff --git a/src/input/input_volume_buttons.cpp b/src/input/input_volume_buttons.cpp
index 607f81f1..37cf90e5 100644
--- a/src/input/input_volume_buttons.cpp
+++ b/src/input/input_volume_buttons.cpp
@@ -25,7 +25,8 @@ auto VolumeButtons::name() -> std::string {
return "buttons";
}
-auto VolumeButtons::hooks() -> std::vector<TriggerHooks> {
+auto VolumeButtons::triggers()
+ -> std::vector<std::reference_wrapper<TriggerHooks>> {
return {up_, down_};
}
diff --git a/src/input/lvgl_input_driver.cpp b/src/input/lvgl_input_driver.cpp
index 61a85fa5..a82b7438 100644
--- a/src/input/lvgl_input_driver.cpp
+++ b/src/input/lvgl_input_driver.cpp
@@ -13,17 +13,22 @@
#include "device_factory.hpp"
#include "feedback_haptics.hpp"
+#include "input_hook.hpp"
#include "input_touch_wheel.hpp"
#include "input_trigger.hpp"
#include "input_volume_buttons.hpp"
#include "lauxlib.h"
#include "lua.h"
+#include "lua_thread.hpp"
#include "lvgl.h"
#include "nvs.hpp"
#include "property.hpp"
[[maybe_unused]] static constexpr char kTag[] = "input";
+static constexpr char kLuaTriggerMetatableName[] = "input_trigger";
+static constexpr char kLuaOverrideText[] = "lua_callback";
+
namespace input {
static void read_cb(lv_indev_drv_t* drv, lv_indev_data_t* data) {
@@ -106,22 +111,148 @@ auto LvglInputDriver::feedback(uint8_t event) -> void {
}
}
+LvglInputDriver::LuaTrigger::LuaTrigger(LvglInputDriver& driver,
+ IInputDevice& dev,
+ TriggerHooks& trigger)
+ : driver_(&driver), device_(dev.name()), trigger_(trigger.name()) {
+ for (auto& hook : trigger.hooks()) {
+ auto cb = hook.get().callback();
+ if (cb) {
+ hooks_[hook.get().name()] = hook.get().callback()->name;
+ } else {
+ hooks_[hook.get().name()] = "";
+ }
+ }
+}
+
+auto LvglInputDriver::LuaTrigger::get(lua_State* L, int idx) -> LuaTrigger& {
+ return **reinterpret_cast<LuaTrigger**>(
+ luaL_checkudata(L, idx, kLuaTriggerMetatableName));
+}
+
+auto LvglInputDriver::LuaTrigger::luaGc(lua_State* L) -> int {
+ LuaTrigger& trigger = LuaTrigger::get(L, 1);
+ delete &trigger;
+ return 0;
+}
+
+auto LvglInputDriver::LuaTrigger::luaToString(lua_State* L) -> int {
+ LuaTrigger& trigger = LuaTrigger::get(L, 1);
+ std::stringstream out;
+ out << "{ ";
+ for (const auto& hook : trigger.hooks_) {
+ if (!hook.second.empty()) {
+ out << hook.first << "=" << hook.second << " ";
+ }
+ }
+ out << "}";
+ lua_pushlstring(L, out.str().data(), out.str().size());
+ return 1;
+}
+
+auto LvglInputDriver::LuaTrigger::luaNewIndex(lua_State* L) -> int {
+ LuaTrigger& trigger = LuaTrigger::get(L, 1);
+ luaL_checktype(L, 3, LUA_TFUNCTION);
+
+ size_t len = 0;
+ const char* str = luaL_checklstring(L, 2, &len);
+ if (!str) {
+ return 0;
+ }
+ OverrideSelector selector{
+ .device_name = trigger.device_,
+ .trigger_name = trigger.trigger_,
+ .hook_name = std::string{str, len},
+ };
+ for (const auto& hook : trigger.hooks_) {
+ if (hook.first == selector.hook_name) {
+ trigger.driver_->setOverride(L, selector);
+ trigger.hooks_[hook.first] = kLuaOverrideText;
+ return 0;
+ }
+ }
+ return 0;
+}
+
auto LvglInputDriver::pushHooks(lua_State* L) -> int {
+ if (luaL_getmetatable(L, kLuaTriggerMetatableName) == LUA_TNIL) {
+ luaL_newmetatable(L, kLuaTriggerMetatableName);
+ luaL_setfuncs(L, LuaTrigger::kFuncs, 0);
+ lua_pop(L, 1);
+ }
+ lua_pop(L, 1);
+
lua_newtable(L);
for (auto& dev : inputs_) {
lua_pushlstring(L, dev->name().data(), dev->name().size());
lua_newtable(L);
- for (auto& hook : dev->hooks()) {
- lua_pushlstring(L, hook.name().data(), hook.name().size());
- hook.pushHooks(L);
+ for (auto& trigger : dev->triggers()) {
+ lua_pushlstring(L, trigger.get().name().data(),
+ trigger.get().name().size());
+ LuaTrigger** lua_obj = reinterpret_cast<LuaTrigger**>(
+ lua_newuserdatauv(L, sizeof(LuaTrigger*), 0));
+ *lua_obj = new LuaTrigger(*this, *dev, trigger);
+ luaL_setmetatable(L, kLuaTriggerMetatableName);
lua_rawset(L, -3);
}
lua_rawset(L, -3);
}
+
return 1;
}
+auto LvglInputDriver::setOverride(lua_State* L,
+ const OverrideSelector& selector) -> void {
+ if (overrides_.contains(selector)) {
+ LuaOverride& prev = overrides_[selector];
+ luaL_unref(prev.L, LUA_REGISTRYINDEX, prev.ref);
+ }
+
+ int ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ LuaOverride override{
+ .L = L,
+ .ref = ref,
+ };
+ overrides_[selector] = override;
+ applyOverride(selector, override);
+}
+
+auto LvglInputDriver::applyOverride(const OverrideSelector& selector,
+ LuaOverride& override) -> void {
+ // In general, this algorithm is a very slow approach. We could do better
+ // by maintaing maps from [device|trigger|hook]_name to the relevant
+ // trigger, but in practice I expect maybe like 5 overrides total ever,
+ // spread across 2 devices with 2 or 5 hooks each. So it's not that big a
+ // deal. Don't worry about it!!
+
+ // Look for a matching device.
+ for (auto& device : inputs_) {
+ if (device->name() != selector.device_name) {
+ continue;
+ }
+ // Look for a matching trigger
+ for (auto& trigger : device->triggers()) {
+ if (trigger.get().name() != selector.trigger_name) {
+ continue;
+ }
+ // Look for a matching hook
+ for (auto& hook : trigger.get().hooks()) {
+ if (hook.get().name() != selector.hook_name) {
+ continue;
+ }
+ // We found the target! Apply the override.
+ auto lua_callback = [=](lv_indev_data_t* d) {
+ lua_rawgeti(override.L, LUA_REGISTRYINDEX, override.ref);
+ lua::CallProtected(override.L, 0, 0);
+ };
+ hook.get().override(
+ HookCallback{.name = kLuaOverrideText, .fn = lua_callback});
+ }
+ }
+ }
+}
+
} // namespace input