Migrate all existing control schemes to the cool new world
This commit is contained in:
@@ -24,6 +24,10 @@ struct TouchWheelData {
|
||||
|
||||
class TouchWheel {
|
||||
public:
|
||||
static auto isAngleWithin(int16_t wheel_angle,
|
||||
int16_t target_angle,
|
||||
int threshold) -> bool;
|
||||
|
||||
static auto Create() -> TouchWheel* { return new TouchWheel(); }
|
||||
TouchWheel();
|
||||
~TouchWheel();
|
||||
|
||||
@@ -28,6 +28,13 @@ namespace drivers {
|
||||
static const uint8_t kTouchWheelAddress = 0x1C;
|
||||
static const gpio_num_t kIntPin = GPIO_NUM_25;
|
||||
|
||||
auto TouchWheel::isAngleWithin(int16_t wheel_angle,
|
||||
int16_t target_angle,
|
||||
int threshold) -> bool {
|
||||
int16_t difference = (wheel_angle - target_angle + 127 + 255) % 255 - 127;
|
||||
return difference <= threshold && difference >= -threshold;
|
||||
}
|
||||
|
||||
TouchWheel::TouchWheel() {
|
||||
gpio_config_t int_config{
|
||||
.pin_bit_mask = 1ULL << kIntPin,
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
idf_component_register(
|
||||
SRCS "input_touch_wheel.cpp" "input_touch_dpad.cpp"
|
||||
SRCS "input_touch_wheel.cpp" "input_touch_dpad.cpp" "input_trigger.cpp"
|
||||
"input_volume_buttons.cpp" "lvgl_input_driver.cpp" "feedback_haptics.cpp"
|
||||
"device_factory.cpp" "input_nav_buttons.cpp"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES "drivers" "lvgl" "events" "system_fsm")
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2023 jacqueline <me@jacqueline.id.au>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
#include "device_factory.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "feedback_haptics.hpp"
|
||||
#include "input_device.hpp"
|
||||
#include "input_nav_buttons.hpp"
|
||||
#include "input_touch_dpad.hpp"
|
||||
#include "input_touch_wheel.hpp"
|
||||
#include "input_volume_buttons.hpp"
|
||||
|
||||
namespace input {
|
||||
|
||||
DeviceFactory::DeviceFactory(
|
||||
std::shared_ptr<system_fsm::ServiceLocator> services)
|
||||
: services_(services) {
|
||||
if (services->touchwheel()) {
|
||||
wheel_ =
|
||||
std::make_shared<TouchWheel>(services->nvs(), **services->touchwheel());
|
||||
}
|
||||
}
|
||||
|
||||
auto DeviceFactory::createInputs(drivers::NvsStorage::InputModes mode)
|
||||
-> std::vector<std::shared_ptr<IInputDevice>> {
|
||||
std::vector<std::shared_ptr<IInputDevice>> ret;
|
||||
switch (mode) {
|
||||
case drivers::NvsStorage::InputModes::kButtonsOnly:
|
||||
ret.push_back(std::make_shared<NavButtons>(services_->gpios()));
|
||||
break;
|
||||
case drivers::NvsStorage::InputModes::kDirectionalWheel:
|
||||
ret.push_back(std::make_shared<VolumeButtons>(services_->gpios()));
|
||||
if (services_->touchwheel()) {
|
||||
ret.push_back(std::make_shared<TouchDPad>(**services_->touchwheel()));
|
||||
}
|
||||
break;
|
||||
case drivers::NvsStorage::InputModes::kRotatingWheel:
|
||||
default: // Don't break input over a bad enum value.
|
||||
ret.push_back(std::make_shared<VolumeButtons>(services_->gpios()));
|
||||
if (wheel_) {
|
||||
ret.push_back(wheel_);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto DeviceFactory::createFeedbacks()
|
||||
-> std::vector<std::shared_ptr<IFeedbackDevice>> {
|
||||
return {std::make_shared<Haptics>(services_->haptics())};
|
||||
}
|
||||
|
||||
} // namespace input
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2023 jacqueline <me@jacqueline.id.au>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "feedback_device.hpp"
|
||||
#include "input_device.hpp"
|
||||
#include "input_touch_wheel.hpp"
|
||||
#include "nvs.hpp"
|
||||
#include "service_locator.hpp"
|
||||
|
||||
namespace input {
|
||||
|
||||
class DeviceFactory {
|
||||
public:
|
||||
DeviceFactory(std::shared_ptr<system_fsm::ServiceLocator>);
|
||||
|
||||
auto createInputs(drivers::NvsStorage::InputModes mode)
|
||||
-> std::vector<std::shared_ptr<IInputDevice>>;
|
||||
|
||||
auto createFeedbacks() -> std::vector<std::shared_ptr<IFeedbackDevice>>;
|
||||
|
||||
auto touch_wheel() -> std::shared_ptr<TouchWheel> { return wheel_; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<system_fsm::ServiceLocator> services_;
|
||||
|
||||
// HACK: the touchwheel is current a special case, since it's the only input
|
||||
// device that has some kind of setting/configuration; scroll sensitivity.
|
||||
std::shared_ptr<TouchWheel> wheel_;
|
||||
};
|
||||
|
||||
} // namespace input
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "hal/lv_hal_indev.h"
|
||||
#include "property.hpp"
|
||||
|
||||
namespace input {
|
||||
|
||||
@@ -28,6 +29,11 @@ class IInputDevice {
|
||||
|
||||
// TODO: Add hooks and configuration (or are hooks just one kind of
|
||||
// configuration?)
|
||||
|
||||
virtual auto settings()
|
||||
-> std::vector<std::pair<std::string, lua::Property>> {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace input
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2024 jacqueline <me@jacqueline.id.au>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "gpios.hpp"
|
||||
#include "hal/lv_hal_indev.h"
|
||||
|
||||
#include "haptics.hpp"
|
||||
#include "input_device.hpp"
|
||||
#include "input_trigger.hpp"
|
||||
#include "touchwheel.hpp"
|
||||
|
||||
namespace input {
|
||||
|
||||
class NavButtons : public IInputDevice {
|
||||
public:
|
||||
NavButtons(drivers::IGpios&);
|
||||
|
||||
auto read(lv_indev_data_t* data) -> void override;
|
||||
|
||||
private:
|
||||
drivers::IGpios& gpios_;
|
||||
|
||||
Trigger up_;
|
||||
Trigger down_;
|
||||
};
|
||||
|
||||
} // namespace input
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include "haptics.hpp"
|
||||
#include "input_device.hpp"
|
||||
#include "input_trigger.hpp"
|
||||
#include "touchwheel.hpp"
|
||||
|
||||
namespace input {
|
||||
@@ -25,6 +26,11 @@ class TouchDPad : public IInputDevice {
|
||||
|
||||
private:
|
||||
drivers::TouchWheel& wheel_;
|
||||
|
||||
Trigger up_;
|
||||
Trigger right_;
|
||||
Trigger down_;
|
||||
Trigger left_;
|
||||
};
|
||||
|
||||
} // namespace input
|
||||
|
||||
@@ -6,29 +6,43 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/_stdint.h>
|
||||
#include <cstdint>
|
||||
|
||||
#include "hal/lv_hal_indev.h"
|
||||
|
||||
#include "haptics.hpp"
|
||||
#include "input_device.hpp"
|
||||
#include "input_trigger.hpp"
|
||||
#include "nvs.hpp"
|
||||
#include "property.hpp"
|
||||
#include "touchwheel.hpp"
|
||||
|
||||
namespace input {
|
||||
|
||||
class TouchWheel : public IInputDevice {
|
||||
public:
|
||||
TouchWheel(drivers::TouchWheel&);
|
||||
TouchWheel(drivers::NvsStorage&, drivers::TouchWheel&);
|
||||
|
||||
auto read(lv_indev_data_t* data) -> void override;
|
||||
|
||||
private:
|
||||
auto calculate_ticks(const drivers::TouchWheelData& data) -> int8_t;
|
||||
auto sensitivity() -> lua::Property&;
|
||||
|
||||
private:
|
||||
auto calculateTicks(const drivers::TouchWheelData& data) -> int8_t;
|
||||
auto calculateThreshold(uint8_t sensitivity) -> uint8_t;
|
||||
|
||||
drivers::NvsStorage& nvs_;
|
||||
drivers::TouchWheel& wheel_;
|
||||
|
||||
lua::Property sensitivity_;
|
||||
|
||||
Trigger up_;
|
||||
Trigger right_;
|
||||
Trigger down_;
|
||||
Trigger left_;
|
||||
|
||||
bool is_scrolling_;
|
||||
uint8_t sensitivity_;
|
||||
uint8_t threshold_;
|
||||
bool is_first_read_;
|
||||
uint8_t last_angle_;
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2024 jacqueline <me@jacqueline.id.au>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
#include "hal/lv_hal_indev.h"
|
||||
|
||||
namespace input {
|
||||
|
||||
const uint16_t kLongPressDelayMs = LV_INDEV_DEF_LONG_PRESS_TIME;
|
||||
const uint16_t kRepeatDelayMs = LV_INDEV_DEF_LONG_PRESS_REP_TIME;
|
||||
|
||||
class Trigger {
|
||||
public:
|
||||
enum class State {
|
||||
kNone,
|
||||
kClick,
|
||||
kLongPress,
|
||||
kRepeatPress,
|
||||
};
|
||||
|
||||
Trigger();
|
||||
|
||||
auto update(bool is_pressed) -> State;
|
||||
|
||||
private:
|
||||
std::optional<uint64_t> touch_time_ms_;
|
||||
uint16_t times_fired_;
|
||||
};
|
||||
|
||||
} // namespace input
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include "haptics.hpp"
|
||||
#include "input_device.hpp"
|
||||
#include "input_trigger.hpp"
|
||||
#include "touchwheel.hpp"
|
||||
|
||||
namespace input {
|
||||
@@ -25,6 +26,9 @@ class VolumeButtons : public IInputDevice {
|
||||
|
||||
private:
|
||||
drivers::IGpios& gpios_;
|
||||
|
||||
Trigger up_;
|
||||
Trigger down_;
|
||||
};
|
||||
|
||||
} // namespace input
|
||||
|
||||
@@ -6,19 +6,20 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
#include "core/lv_group.h"
|
||||
#include "device_factory.hpp"
|
||||
#include "feedback_device.hpp"
|
||||
#include "gpios.hpp"
|
||||
#include "hal/lv_hal_indev.h"
|
||||
|
||||
#include "input_device.hpp"
|
||||
#include "nvs.hpp"
|
||||
#include "service_locator.hpp"
|
||||
#include "property.hpp"
|
||||
#include "touchwheel.hpp"
|
||||
|
||||
namespace input {
|
||||
@@ -30,7 +31,9 @@ namespace input {
|
||||
*/
|
||||
class LvglInputDriver {
|
||||
public:
|
||||
LvglInputDriver(std::shared_ptr<system_fsm::ServiceLocator>);
|
||||
LvglInputDriver(drivers::NvsStorage& nvs, DeviceFactory&);
|
||||
|
||||
auto mode() -> lua::Property& { return mode_; }
|
||||
|
||||
auto read(lv_indev_data_t* data) -> void;
|
||||
auto feedback(uint8_t) -> void;
|
||||
@@ -39,13 +42,15 @@ class LvglInputDriver {
|
||||
auto lock(bool l) -> void { is_locked_ = l; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<system_fsm::ServiceLocator> services_;
|
||||
drivers::NvsStorage& nvs_;
|
||||
DeviceFactory& factory_;
|
||||
|
||||
lua::Property mode_;
|
||||
lv_indev_drv_t driver_;
|
||||
lv_indev_t* registration_;
|
||||
|
||||
std::vector<std::unique_ptr<IInputDevice>> inputs_;
|
||||
std::vector<std::unique_ptr<IFeedbackDevice>> feedbacks_;
|
||||
std::vector<std::shared_ptr<IInputDevice>> inputs_;
|
||||
std::vector<std::shared_ptr<IFeedbackDevice>> feedbacks_;
|
||||
|
||||
bool is_locked_;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2024 jacqueline <me@jacqueline.id.au>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
#include "input_nav_buttons.hpp"
|
||||
|
||||
#include "event_queue.hpp"
|
||||
#include "gpios.hpp"
|
||||
#include "hal/lv_hal_indev.h"
|
||||
|
||||
namespace input {
|
||||
|
||||
NavButtons::NavButtons(drivers::IGpios& gpios) : gpios_(gpios) {}
|
||||
|
||||
auto NavButtons::read(lv_indev_data_t* data) -> void {
|
||||
bool vol_up = gpios_.Get(drivers::IGpios::Pin::kKeyUp);
|
||||
switch (up_.update(!vol_up)) {
|
||||
case Trigger::State::kClick:
|
||||
data->enc_diff = -1;
|
||||
break;
|
||||
case Trigger::State::kLongPress:
|
||||
events::Ui().Dispatch(ui::internal::BackPressed{});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
bool vol_down = gpios_.Get(drivers::IGpios::Pin::kKeyDown);
|
||||
switch (down_.update(!vol_down)) {
|
||||
case Trigger::State::kClick:
|
||||
data->enc_diff = 1;
|
||||
break;
|
||||
case Trigger::State::kLongPress:
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
break;
|
||||
default:
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace input
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "hal/lv_hal_indev.h"
|
||||
|
||||
#include "event_queue.hpp"
|
||||
#include "haptics.hpp"
|
||||
#include "input_device.hpp"
|
||||
#include "input_touch_dpad.hpp"
|
||||
@@ -28,8 +29,47 @@ TouchDPad::TouchDPad(drivers::TouchWheel& wheel) : wheel_(wheel) {}
|
||||
|
||||
auto TouchDPad::read(lv_indev_data_t* data) -> void {
|
||||
wheel_.Update();
|
||||
auto wheel_data = wheel_.GetTouchWheelData();
|
||||
|
||||
// TODO: reimplement
|
||||
if (wheel_data.is_button_touched) {
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
} else {
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
}
|
||||
|
||||
switch (up_.update(
|
||||
wheel_data.is_wheel_touched &&
|
||||
drivers::TouchWheel::isAngleWithin(wheel_data.wheel_position, 0, 32))) {
|
||||
case Trigger::State::kNone:
|
||||
break;
|
||||
default:
|
||||
data->enc_diff = -1;
|
||||
break;
|
||||
}
|
||||
switch (right_.update(
|
||||
wheel_data.is_wheel_touched &&
|
||||
drivers::TouchWheel::isAngleWithin(wheel_data.wheel_position, 192, 32))) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (down_.update(
|
||||
wheel_data.is_wheel_touched &&
|
||||
drivers::TouchWheel::isAngleWithin(wheel_data.wheel_position, 128, 32))) {
|
||||
case Trigger::State::kNone:
|
||||
break;
|
||||
default:
|
||||
data->enc_diff = 1;
|
||||
break;
|
||||
}
|
||||
switch (left_.update(
|
||||
wheel_data.is_wheel_touched &&
|
||||
drivers::TouchWheel::isAngleWithin(wheel_data.wheel_position, 64, 32))) {
|
||||
case Trigger::State::kLongPress:
|
||||
events::Ui().Dispatch(ui::internal::BackPressed{});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace input
|
||||
|
||||
@@ -8,27 +8,46 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <variant>
|
||||
|
||||
#include "event_queue.hpp"
|
||||
#include "hal/lv_hal_indev.h"
|
||||
|
||||
#include "haptics.hpp"
|
||||
#include "input_device.hpp"
|
||||
#include "input_trigger.hpp"
|
||||
#include "nvs.hpp"
|
||||
#include "property.hpp"
|
||||
#include "touchwheel.hpp"
|
||||
#include "ui_events.hpp"
|
||||
|
||||
namespace input {
|
||||
|
||||
TouchWheel::TouchWheel(drivers::TouchWheel& wheel)
|
||||
: wheel_(wheel),
|
||||
TouchWheel::TouchWheel(drivers::NvsStorage& nvs, drivers::TouchWheel& wheel)
|
||||
: nvs_(nvs),
|
||||
wheel_(wheel),
|
||||
sensitivity_(static_cast<int>(nvs.ScrollSensitivity()),
|
||||
[&](const lua::LuaValue& val) {
|
||||
if (!std::holds_alternative<int>(val)) {
|
||||
return false;
|
||||
}
|
||||
int int_val = std::get<int>(val);
|
||||
if (int_val < 0 || int_val > UINT8_MAX) {
|
||||
return false;
|
||||
}
|
||||
nvs.ScrollSensitivity(int_val);
|
||||
threshold_ = calculateThreshold(int_val);
|
||||
return true;
|
||||
}),
|
||||
is_scrolling_(false),
|
||||
sensitivity_(128),
|
||||
threshold_(10),
|
||||
threshold_(calculateThreshold(nvs.ScrollSensitivity())),
|
||||
is_first_read_(true),
|
||||
last_angle_(0) {}
|
||||
|
||||
auto TouchWheel::read(lv_indev_data_t* data) -> void {
|
||||
wheel_.Update();
|
||||
auto wheel_data = wheel_.GetTouchWheelData();
|
||||
int8_t ticks = calculate_ticks(wheel_data);
|
||||
int8_t ticks = calculateTicks(wheel_data);
|
||||
|
||||
if (!wheel_data.is_wheel_touched) {
|
||||
// User has released the wheel.
|
||||
@@ -49,10 +68,47 @@ auto TouchWheel::read(lv_indev_data_t* data) -> void {
|
||||
} else {
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
}
|
||||
|
||||
// If the user is touching the wheel but not scrolling, then they may be
|
||||
// clicking on one of the wheel's cardinal directions.
|
||||
bool pressing = wheel_data.is_wheel_touched && !is_scrolling_;
|
||||
|
||||
switch (up_.update(pressing && drivers::TouchWheel::isAngleWithin(
|
||||
wheel_data.wheel_position, 0, 32))) {
|
||||
case Trigger::State::kLongPress:
|
||||
data->enc_diff = INT16_MIN;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (right_.update(pressing && drivers::TouchWheel::isAngleWithin(
|
||||
wheel_data.wheel_position, 192, 32))) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (down_.update(pressing && drivers::TouchWheel::isAngleWithin(
|
||||
wheel_data.wheel_position, 128, 32))) {
|
||||
case Trigger::State::kLongPress:
|
||||
data->enc_diff = INT16_MAX;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (left_.update(pressing && drivers::TouchWheel::isAngleWithin(
|
||||
wheel_data.wheel_position, 64, 32))) {
|
||||
case Trigger::State::kLongPress:
|
||||
events::Ui().Dispatch(ui::internal::BackPressed{});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto TouchWheel::calculate_ticks(const drivers::TouchWheelData& data)
|
||||
-> int8_t {
|
||||
auto TouchWheel::sensitivity() -> lua::Property& {
|
||||
return sensitivity_;
|
||||
}
|
||||
|
||||
auto TouchWheel::calculateTicks(const drivers::TouchWheelData& data) -> int8_t {
|
||||
if (!data.is_wheel_touched) {
|
||||
is_first_read_ = true;
|
||||
return 0;
|
||||
@@ -78,4 +134,10 @@ auto TouchWheel::calculate_ticks(const drivers::TouchWheelData& data)
|
||||
}
|
||||
}
|
||||
|
||||
auto TouchWheel::calculateThreshold(uint8_t sensitivity) -> uint8_t {
|
||||
int tmax = 35;
|
||||
int tmin = 5;
|
||||
return (((255. - sensitivity) / 255.) * (tmax - tmin) + tmin);
|
||||
}
|
||||
|
||||
} // namespace input
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2024 jacqueline <me@jacqueline.id.au>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
#include "input_trigger.hpp"
|
||||
#include <sys/_stdint.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
|
||||
namespace input {
|
||||
|
||||
Trigger::Trigger() : touch_time_ms_(), times_fired_(0) {}
|
||||
|
||||
auto Trigger::update(bool is_pressed) -> State {
|
||||
// Bail out early if we're in a steady-state of not pressed.
|
||||
if (!is_pressed && !touch_time_ms_) {
|
||||
return State::kNone;
|
||||
}
|
||||
|
||||
uint64_t now_ms = esp_timer_get_time() / 1000;
|
||||
|
||||
// Initial press of this key: record the current time, and report that we
|
||||
// haven't triggered yet.
|
||||
if (is_pressed && !touch_time_ms_) {
|
||||
touch_time_ms_ = now_ms;
|
||||
times_fired_ = 0;
|
||||
return State::kNone;
|
||||
}
|
||||
|
||||
// The key was released. If there were no long-press events fired during the
|
||||
// press, then this was a standard click.
|
||||
if (!is_pressed && touch_time_ms_) {
|
||||
touch_time_ms_.reset();
|
||||
if (times_fired_ == 0) {
|
||||
return State::kClick;
|
||||
} else {
|
||||
return State::kNone;
|
||||
}
|
||||
}
|
||||
|
||||
// Now the more complicated case: the user is continuing to press the button.
|
||||
if (times_fired_ == 0) {
|
||||
// We haven't fired yet, so we wait for the long-press event.
|
||||
if (now_ms - *touch_time_ms_ >= kLongPressDelayMs) {
|
||||
times_fired_++;
|
||||
return State::kLongPress;
|
||||
}
|
||||
} else {
|
||||
// We've already fired at least once. How long has the user been holding
|
||||
// the key for?
|
||||
uint64_t time_since_long_press =
|
||||
now_ms - (*touch_time_ms_ + kLongPressDelayMs);
|
||||
|
||||
// How many times should we have fired?
|
||||
// 1 initial fire (for the long-press), plus one additional fire every
|
||||
// kRepeatDelayMs since the long-press event.
|
||||
uint16_t expected_times_fired =
|
||||
1 + (time_since_long_press / kRepeatDelayMs);
|
||||
if (times_fired_ < expected_times_fired) {
|
||||
times_fired_++;
|
||||
return State::kRepeatPress;
|
||||
}
|
||||
}
|
||||
|
||||
return State::kNone;
|
||||
}
|
||||
|
||||
} // namespace input
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include "input_volume_buttons.hpp"
|
||||
#include "event_queue.hpp"
|
||||
#include "gpios.hpp"
|
||||
|
||||
namespace input {
|
||||
@@ -13,13 +14,21 @@ VolumeButtons::VolumeButtons(drivers::IGpios& gpios) : gpios_(gpios) {}
|
||||
|
||||
auto VolumeButtons::read(lv_indev_data_t* data) -> void {
|
||||
bool vol_up = gpios_.Get(drivers::IGpios::Pin::kKeyUp);
|
||||
if (!vol_up) {
|
||||
ESP_LOGI("volume", "vol up");
|
||||
switch (up_.update(!vol_up)) {
|
||||
case Trigger::State::kNone:
|
||||
break;
|
||||
default:
|
||||
events::Audio().Dispatch(audio::StepUpVolume{});
|
||||
break;
|
||||
}
|
||||
|
||||
bool vol_down = gpios_.Get(drivers::IGpios::Pin::kKeyDown);
|
||||
if (!vol_down) {
|
||||
ESP_LOGI("volume", "vol down");
|
||||
switch (down_.update(!vol_down)) {
|
||||
case Trigger::State::kNone:
|
||||
break;
|
||||
default:
|
||||
events::Audio().Dispatch(audio::StepDownVolume{});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,12 +9,16 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <variant>
|
||||
|
||||
#include "device_factory.hpp"
|
||||
#include "feedback_haptics.hpp"
|
||||
#include "input_touch_wheel.hpp"
|
||||
#include "input_trigger.hpp"
|
||||
#include "input_volume_buttons.hpp"
|
||||
#include "lvgl.h"
|
||||
#include "service_locator.hpp"
|
||||
#include "nvs.hpp"
|
||||
#include "property.hpp"
|
||||
|
||||
[[maybe_unused]] static constexpr char kTag[] = "input";
|
||||
|
||||
@@ -32,33 +36,52 @@ static void feedback_cb(lv_indev_drv_t* drv, uint8_t event) {
|
||||
instance->feedback(event);
|
||||
}
|
||||
|
||||
LvglInputDriver::LvglInputDriver(
|
||||
std::shared_ptr<system_fsm::ServiceLocator> services)
|
||||
: services_(services),
|
||||
auto intToMode(int raw) -> std::optional<drivers::NvsStorage::InputModes> {
|
||||
switch (raw) {
|
||||
case 0:
|
||||
return drivers::NvsStorage::InputModes::kButtonsOnly;
|
||||
case 1:
|
||||
return drivers::NvsStorage::InputModes::kButtonsWithWheel;
|
||||
case 2:
|
||||
return drivers::NvsStorage::InputModes::kDirectionalWheel;
|
||||
case 3:
|
||||
return drivers::NvsStorage::InputModes::kRotatingWheel;
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
LvglInputDriver::LvglInputDriver(drivers::NvsStorage& nvs,
|
||||
DeviceFactory& factory)
|
||||
: nvs_(nvs),
|
||||
factory_(factory),
|
||||
mode_(static_cast<int>(nvs.PrimaryInput()),
|
||||
[&](const lua::LuaValue& val) {
|
||||
if (!std::holds_alternative<int>(val)) {
|
||||
return false;
|
||||
}
|
||||
auto mode = intToMode(std::get<int>(val));
|
||||
if (!mode) {
|
||||
return false;
|
||||
}
|
||||
nvs.PrimaryInput(*mode);
|
||||
inputs_ = factory.createInputs(*mode);
|
||||
return true;
|
||||
}),
|
||||
driver_(),
|
||||
registration_(nullptr),
|
||||
inputs_(),
|
||||
feedbacks_(),
|
||||
inputs_(factory.createInputs(nvs.PrimaryInput())),
|
||||
feedbacks_(factory.createFeedbacks()),
|
||||
is_locked_(false) {
|
||||
lv_indev_drv_init(&driver_);
|
||||
driver_.type = LV_INDEV_TYPE_ENCODER;
|
||||
driver_.read_cb = read_cb;
|
||||
driver_.feedback_cb = feedback_cb;
|
||||
driver_.user_data = this;
|
||||
driver_.long_press_time = kLongPressDelayMs;
|
||||
driver_.long_press_repeat_time = kRepeatDelayMs;
|
||||
|
||||
registration_ = lv_indev_drv_register(&driver_);
|
||||
|
||||
// TODO: Make these devices configurable. I'm thinking each device gets an id
|
||||
// and then we have:
|
||||
// - a factory to create instance given an id
|
||||
// - add/remove device methods on LvglInputDriver that operate on ids
|
||||
// - the user's enabled devices (+ their configuration) stored in NVS.
|
||||
auto touchwheel = services_->touchwheel();
|
||||
if (touchwheel) {
|
||||
inputs_.push_back(std::make_unique<TouchWheel>(**touchwheel));
|
||||
}
|
||||
inputs_.push_back(std::make_unique<VolumeButtons>(services_->gpios()));
|
||||
feedbacks_.push_back(std::make_unique<Haptics>(services_->haptics()));
|
||||
}
|
||||
|
||||
auto LvglInputDriver::read(lv_indev_data_t* data) -> void {
|
||||
|
||||
@@ -32,7 +32,6 @@ struct DumpLuaStack : tinyfsm::Event {};
|
||||
|
||||
namespace internal {
|
||||
|
||||
struct ControlSchemeChanged : tinyfsm::Event {};
|
||||
struct ReindexDatabase : tinyfsm::Event {};
|
||||
|
||||
struct BackPressed : tinyfsm::Event {};
|
||||
|
||||
@@ -13,8 +13,12 @@
|
||||
#include "audio_events.hpp"
|
||||
#include "battery.hpp"
|
||||
#include "db_events.hpp"
|
||||
#include "device_factory.hpp"
|
||||
#include "display.hpp"
|
||||
#include "feedback_haptics.hpp"
|
||||
#include "gpios.hpp"
|
||||
#include "input_touch_wheel.hpp"
|
||||
#include "input_volume_buttons.hpp"
|
||||
#include "lua_thread.hpp"
|
||||
#include "lvgl_input_driver.hpp"
|
||||
#include "lvgl_task.hpp"
|
||||
@@ -67,7 +71,6 @@ class UiState : public tinyfsm::Fsm<UiState> {
|
||||
void react(const system_fsm::SamdUsbStatusChanged&);
|
||||
|
||||
void react(const internal::DismissAlerts&);
|
||||
void react(const internal::ControlSchemeChanged&);
|
||||
|
||||
void react(const database::event::UpdateStarted&);
|
||||
void react(const database::event::UpdateProgress&){};
|
||||
@@ -91,7 +94,9 @@ class UiState : public tinyfsm::Fsm<UiState> {
|
||||
static std::unique_ptr<UiTask> sTask;
|
||||
static std::shared_ptr<system_fsm::ServiceLocator> sServices;
|
||||
static std::unique_ptr<drivers::Display> sDisplay;
|
||||
|
||||
static std::shared_ptr<input::LvglInputDriver> sInput;
|
||||
static std::unique_ptr<input::DeviceFactory> sDeviceFactory;
|
||||
|
||||
static std::stack<std::shared_ptr<Screen>> sScreens;
|
||||
static std::shared_ptr<Screen> sCurrentScreen;
|
||||
@@ -125,8 +130,6 @@ class UiState : public tinyfsm::Fsm<UiState> {
|
||||
|
||||
static lua::Property sDisplayBrightness;
|
||||
|
||||
static lua::Property sControlsScheme;
|
||||
static lua::Property sScrollSensitivity;
|
||||
static lua::Property sLockSwitch;
|
||||
|
||||
static lua::Property sDatabaseUpdating;
|
||||
|
||||
+21
-67
@@ -12,9 +12,14 @@
|
||||
|
||||
#include "bluetooth_types.hpp"
|
||||
#include "db_events.hpp"
|
||||
#include "device_factory.hpp"
|
||||
#include "display_init.hpp"
|
||||
#include "feedback_haptics.hpp"
|
||||
#include "freertos/portmacro.h"
|
||||
#include "freertos/projdefs.h"
|
||||
#include "input_device.hpp"
|
||||
#include "input_touch_wheel.hpp"
|
||||
#include "input_volume_buttons.hpp"
|
||||
#include "lua.h"
|
||||
#include "lua.hpp"
|
||||
|
||||
@@ -62,7 +67,9 @@ namespace ui {
|
||||
std::unique_ptr<UiTask> UiState::sTask;
|
||||
std::shared_ptr<system_fsm::ServiceLocator> UiState::sServices;
|
||||
std::unique_ptr<drivers::Display> UiState::sDisplay;
|
||||
|
||||
std::shared_ptr<input::LvglInputDriver> UiState::sInput;
|
||||
std::unique_ptr<input::DeviceFactory> UiState::sDeviceFactory;
|
||||
|
||||
std::stack<std::shared_ptr<Screen>> UiState::sScreens;
|
||||
std::shared_ptr<Screen> UiState::sCurrentScreen;
|
||||
@@ -233,59 +240,6 @@ lua::Property UiState::sDisplayBrightness{
|
||||
return true;
|
||||
}};
|
||||
|
||||
lua::Property UiState::sControlsScheme{0, [](const lua::LuaValue& val) {
|
||||
/*
|
||||
if (!std::holds_alternative<int>(val))
|
||||
{ return false;
|
||||
}
|
||||
drivers::NvsStorage::InputModes mode;
|
||||
switch (std::get<int>(val)) {
|
||||
case 0:
|
||||
mode =
|
||||
drivers::NvsStorage::InputModes::kButtonsOnly;
|
||||
break;
|
||||
case 1:
|
||||
mode =
|
||||
drivers::NvsStorage::InputModes::kButtonsWithWheel;
|
||||
break;
|
||||
case 2:
|
||||
mode =
|
||||
drivers::NvsStorage::InputModes::kDirectionalWheel;
|
||||
break;
|
||||
case 3:
|
||||
mode =
|
||||
drivers::NvsStorage::InputModes::kRotatingWheel;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
sServices->nvs().PrimaryInput(mode);
|
||||
sInput->mode(mode);
|
||||
*/
|
||||
return true;
|
||||
}};
|
||||
|
||||
lua::Property UiState::sScrollSensitivity{0, [](const lua::LuaValue& val) {
|
||||
/*
|
||||
std::optional<int> sensitivity = 0;
|
||||
std::visit(
|
||||
[&](auto&& v) {
|
||||
using T =
|
||||
std::decay_t<decltype(v)>; if
|
||||
constexpr (std::is_same_v<T, int>) {
|
||||
sensitivity = v;
|
||||
}
|
||||
},
|
||||
val);
|
||||
if (!sensitivity) {
|
||||
return false;
|
||||
}
|
||||
sInput->scroll_sensitivity(*sensitivity);
|
||||
sServices->nvs().ScrollSensitivity(*sensitivity);
|
||||
*/
|
||||
return true;
|
||||
}};
|
||||
|
||||
lua::Property UiState::sLockSwitch{false};
|
||||
|
||||
lua::Property UiState::sDatabaseUpdating{false};
|
||||
@@ -374,13 +328,6 @@ void UiState::react(const system_fsm::SamdUsbStatusChanged& ev) {
|
||||
drivers::Samd::UsbStatus::kAttachedBusy);
|
||||
}
|
||||
|
||||
void UiState::react(const internal::ControlSchemeChanged&) {
|
||||
if (!sInput) {
|
||||
return;
|
||||
}
|
||||
// sInput->mode(sServices->nvs().PrimaryInput());
|
||||
}
|
||||
|
||||
void UiState::react(const database::event::UpdateStarted&) {
|
||||
sDatabaseUpdating.Update(true);
|
||||
}
|
||||
@@ -484,7 +431,10 @@ void Splash::react(const system_fsm::BootComplete& ev) {
|
||||
sDisplayBrightness.Update(brightness);
|
||||
sDisplay->SetBrightness(brightness);
|
||||
|
||||
sInput = std::make_shared<input::LvglInputDriver>(sServices);
|
||||
sDeviceFactory = std::make_unique<input::DeviceFactory>(sServices);
|
||||
sInput = std::make_shared<input::LvglInputDriver>(sServices->nvs(),
|
||||
*sDeviceFactory);
|
||||
|
||||
sTask->input(sInput);
|
||||
}
|
||||
|
||||
@@ -542,12 +492,16 @@ void Lua::entry() {
|
||||
{"brightness", &sDisplayBrightness},
|
||||
});
|
||||
|
||||
registry.AddPropertyModule("controls",
|
||||
{
|
||||
{"scheme", &sControlsScheme},
|
||||
{"scroll_sensitivity", &sScrollSensitivity},
|
||||
{"lock_switch", &sLockSwitch},
|
||||
});
|
||||
registry.AddPropertyModule("controls", {
|
||||
{"scheme", &sInput->mode()},
|
||||
{"lock_switch", &sLockSwitch},
|
||||
});
|
||||
|
||||
if (sDeviceFactory->touch_wheel()) {
|
||||
registry.AddPropertyModule(
|
||||
"controls", {{"scroll_sensitivity",
|
||||
&sDeviceFactory->touch_wheel()->sensitivity()}});
|
||||
}
|
||||
|
||||
registry.AddPropertyModule(
|
||||
"backstack",
|
||||
|
||||
Reference in New Issue
Block a user