Skip to content

Commit

Permalink
Added mouse wheel support on Linux
Browse files Browse the repository at this point in the history
  • Loading branch information
houmain committed May 7, 2024
1 parent 14eecf1 commit 52ba39f
Show file tree
Hide file tree
Showing 10 changed files with 46 additions and 31 deletions.
2 changes: 1 addition & 1 deletion src/config/ParseKeySequence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ void ParseKeySequence::add_timeout_event(uint16_t timeout, bool is_not, bool can
if (!m_sequence.empty() &&
m_sequence.back().key == Key::timeout &&
m_sequence.back().state == state)
m_sequence.back().timeout = sum_timeouts(m_sequence.back().timeout, timeout);
m_sequence.back().value = sum_timeouts(m_sequence.back().value, timeout);
else
m_sequence.emplace_back(Key::timeout, state, timeout);
}
Expand Down
14 changes: 7 additions & 7 deletions src/runtime/KeyEvent.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,23 @@ enum class KeyState : uint16_t {
};

struct KeyEvent {
static const int timeout_bits = 12;
static const int value_bits = 12;

Key key;
KeyState state : 4;
uint16_t timeout : timeout_bits;
uint16_t value : value_bits;

KeyEvent()
: key(), state(), timeout() {
: key(), state(), value() {
}
KeyEvent(Key key, KeyState state)
: key(key), state(state), timeout() {
: key(key), state(state), value() {
}
KeyEvent(Key key, KeyState state, uint16_t timeout)
: key(key), state(state), timeout(timeout) {
KeyEvent(Key key, KeyState state, uint16_t value)
: key(key), state(state), value(value) {
}
bool operator==(const KeyEvent& b) const {
return (key == b.key && state == b.state && timeout == b.timeout);
return (key == b.key && state == b.state && value == b.value);
}
bool operator!=(const KeyEvent& b) const {
return !(*this == b);
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/MatchKeySequence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace {

// not commutative, first parameter needs to be input sequence
bool timeout_unifiable(const KeyEvent& se, const KeyEvent& ee) {
const auto time_reached = (se.timeout >= ee.timeout);
const auto time_reached = (se.value >= ee.value);
const auto is_not = (is_not_timeout(se.state) || is_not_timeout(ee.state));
return (is_not ? !time_reached : time_reached);
}
Expand Down
6 changes: 4 additions & 2 deletions src/runtime/Stage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ namespace {

bool has_mouse_mappings(const KeySequence& sequence) {
return std::any_of(begin(sequence), end(sequence),
[](const KeyEvent& event) { return is_mouse_button(event.key); });
[](const KeyEvent& event) {
return is_mouse_button(event.key) || is_mouse_wheel(event.key);
});
}

bool has_mouse_mappings(const std::vector<Stage::Context>& contexts) {
Expand Down Expand Up @@ -392,7 +394,7 @@ void Stage::apply_input(const KeyEvent event, int device_index) {
// suppress short timeout after not-timeout was exceeded
if (m_current_timeout && is_not_timeout(m_current_timeout->state)) {
if (event.key == Key::timeout) {
if (m_current_timeout->timeout == event.timeout) {
if (m_current_timeout->value == event.value) {
m_current_timeout->not_exceeded = true;
}
else if (m_current_timeout->not_exceeded) {
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/Timeout.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ template<typename R, typename P>
uint16_t duration_to_timeout(const std::chrono::duration<R, P>& duration) {
const auto milliseconds =
std::chrono::duration_cast<std::chrono::milliseconds>(duration);
const auto max = (1 << KeyEvent::timeout_bits) - 1;
const auto max = (1 << KeyEvent::value_bits) - 1;
return static_cast<uint16_t>(std::clamp(
static_cast<int>(milliseconds.count()),
0, max));
Expand All @@ -26,7 +26,7 @@ KeyEvent make_input_timeout_event(const std::chrono::duration<R, P>& duration) {
}

inline uint16_t sum_timeouts(uint16_t a, uint16_t b) {
const auto max = (1 << KeyEvent::timeout_bits) - 1;
const auto max = (1 << KeyEvent::value_bits) - 1;
return static_cast<uint16_t>(std::min(
static_cast<int>(a) + static_cast<int>(b), max));
}
Expand Down
6 changes: 3 additions & 3 deletions src/server/ServerState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ bool ServerState::translate_input(KeyEvent input, int device_index) {

// automatically insert mouse wheel Down before Up
if (is_mouse_wheel(input.key) && input.state == KeyState::Up)
translate_input({ input.key, KeyState::Down }, device_index);
translate_input({ input.key, KeyState::Down, input.value }, device_index);

if (input.key != Key::timeout)
m_last_key_event = input;
Expand All @@ -221,7 +221,7 @@ bool ServerState::translate_input(KeyEvent input, int device_index) {
if (!output.empty() && output.back().key == Key::timeout) {
const auto& request = output.back();
schedule_timeout(
timeout_to_milliseconds(request.timeout),
timeout_to_milliseconds(request.value),
cancel_timeout_on_up(request.state));
output.pop_back();
}
Expand Down Expand Up @@ -283,7 +283,7 @@ bool ServerState::flush_send_buffer() {
}

if (event.key == Key::timeout) {
schedule_flush(timeout_to_milliseconds(event.timeout));
schedule_flush(timeout_to_milliseconds(event.value));
++i;
break;
}
Expand Down
21 changes: 14 additions & 7 deletions src/server/unix/GrabbedDevicesLinux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -410,11 +410,18 @@ const std::vector<DeviceDesc>& GrabbedDevices::grabbed_device_descs() const {
}

std::optional<KeyEvent> to_key_event(const GrabbedDevices::Event& event) {
if (event.type != EV_KEY)
return { };

return KeyEvent{
static_cast<Key>(event.code),
(event.value == 0 ? KeyState::Up : KeyState::Down),
};
if (event.type == EV_KEY)
return KeyEvent{
static_cast<Key>(event.code),
(event.value == 0 ? KeyState::Up : KeyState::Down),
};

if (event.type == EV_REL && event.code == REL_WHEEL_HI_RES)
return KeyEvent{
(event.value < 0 ? Key::WheelDown : Key::WheelUp),
KeyState::Up,
static_cast<uint16_t>(event.value)
};

return { };
}
18 changes: 12 additions & 6 deletions src/server/unix/VirtualDeviceLinux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,8 @@ namespace {
::ioctl(fd, UI_SET_RELBIT, REL_X);
::ioctl(fd, UI_SET_RELBIT, REL_Y);
::ioctl(fd, UI_SET_RELBIT, REL_Z);
::ioctl(fd, UI_SET_RELBIT, REL_WHEEL);
::ioctl(fd, UI_SET_RELBIT, REL_HWHEEL);
#if defined(REL_WHEEL_HI_RES)
::ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES);
::ioctl(fd, UI_SET_RELBIT, REL_HWHEEL_HI_RES);
#endif

// add absolute axes which are commonly found on keyboards
::ioctl(fd, UI_SET_EVBIT, EV_ABS);
Expand Down Expand Up @@ -142,8 +138,18 @@ class VirtualDeviceImpl {
}

bool send_key_event(const KeyEvent& event) {
return send_event(EV_KEY, *event.key, get_key_event_value(event)) &&
send_event(EV_SYN, SYN_REPORT, 0);
if (is_mouse_wheel(event.key)) {
const auto value = (event.value ?
static_cast<int16_t>(event.value) :
event.key == Key::WheelDown ? -120 : 120);
if (!send_event(EV_REL, REL_WHEEL_HI_RES, value))
return false;
}
else {
if (!send_event(EV_KEY, *event.key, get_key_event_value(event)))
return false;
}
return send_event(EV_SYN, SYN_REPORT, 0);
}
};

Expand Down
2 changes: 1 addition & 1 deletion src/server/verbose_debug_io.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ void verbose_debug_io(const KeyEvent& input,
const auto format = [](const KeyEvent& e) {
if (e.key == Key::timeout)
return (e.state == KeyState::Not ? "!" : "") +
std::to_string(timeout_to_milliseconds(e.timeout).count()) + "ms";
std::to_string(timeout_to_milliseconds(e.value).count()) + "ms";

const auto key_name = [](Key key) {
if (const auto name = get_key_name(key))
Expand Down
2 changes: 1 addition & 1 deletion src/test/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ std::ostream& operator<<(std::ostream& os, const KeyEvent& event) {
os << "Action" << (*event.key - *Key::first_action);
}
else if (event.key == Key::timeout) {
os << timeout_to_milliseconds(event.timeout).count() << "ms";
os << timeout_to_milliseconds(event.value).count() << "ms";
}
else if (auto name = get_key_name(event.key)) {
os << name;
Expand Down

0 comments on commit 52ba39f

Please sign in to comment.