parent
ed82063af5
commit
33919e9e3f
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
Loading…
Reference in new issue