mirror of
https://github.com/LizardByte/Sunshine.git
synced 2025-03-12 13:13:21 +00:00
feat(input/linux): add support for more virtual input devices (#2606)
Co-authored-by: ABeltramo <beltramo.ale@gmail.com> Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com>
This commit is contained in:
parent
5f1a17f77d
commit
509576d616
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -14,6 +14,10 @@
|
||||
path = third-party/googletest
|
||||
url = https://github.com/google/googletest/
|
||||
branch = v1.14.x
|
||||
[submodule "third-party/inputtino"]
|
||||
path = third-party/inputtino
|
||||
url = https://github.com/games-on-whales/inputtino.git
|
||||
branch = stable
|
||||
[submodule "third-party/moonlight-common-c"]
|
||||
path = third-party/moonlight-common-c
|
||||
url = https://github.com/moonlight-stream/moonlight-common-c.git
|
||||
|
@ -222,6 +222,26 @@ if(${SUNSHINE_ENABLE_TRAY} AND ${SUNSHINE_TRAY} EQUAL 0 AND SUNSHINE_REQUIRE_TRA
|
||||
message(FATAL_ERROR "Tray icon is required")
|
||||
endif()
|
||||
|
||||
if(${SUNSHINE_USE_LEGACY_INPUT}) # TODO: Remove this legacy option after the next stable release
|
||||
list(APPEND PLATFORM_TARGET_FILES "${CMAKE_SOURCE_DIR}/src/platform/linux/input/legacy_input.cpp")
|
||||
else()
|
||||
# These need to be set before adding the inputtino subdirectory in order for them to be picked up
|
||||
set(LIBEVDEV_CUSTOM_INCLUDE_DIR "${EVDEV_INCLUDE_DIR}")
|
||||
set(LIBEVDEV_CUSTOM_LIBRARY "${EVDEV_LIBRARY}")
|
||||
|
||||
add_subdirectory("${CMAKE_SOURCE_DIR}/third-party/inputtino")
|
||||
list(APPEND SUNSHINE_EXTERNAL_LIBRARIES inputtino::libinputtino)
|
||||
file(GLOB_RECURSE INPUTTINO_SOURCES
|
||||
${CMAKE_SOURCE_DIR}/src/platform/linux/input/inputtino*.h
|
||||
${CMAKE_SOURCE_DIR}/src/platform/linux/input/inputtino*.cpp)
|
||||
list(APPEND PLATFORM_TARGET_FILES ${INPUTTINO_SOURCES})
|
||||
|
||||
# build libevdev before the libinputtino target
|
||||
if(EXTERNAL_PROJECT_LIBEVDEV_USED)
|
||||
add_dependencies(libinputtino libevdev)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
list(APPEND PLATFORM_TARGET_FILES
|
||||
"${CMAKE_SOURCE_DIR}/src/platform/linux/publish.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/platform/linux/graphics.h"
|
||||
@ -229,7 +249,6 @@ list(APPEND PLATFORM_TARGET_FILES
|
||||
"${CMAKE_SOURCE_DIR}/src/platform/linux/misc.h"
|
||||
"${CMAKE_SOURCE_DIR}/src/platform/linux/misc.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/platform/linux/audio.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/src/platform/linux/input.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/third-party/glad/src/egl.c"
|
||||
"${CMAKE_SOURCE_DIR}/third-party/glad/src/gl.c"
|
||||
"${CMAKE_SOURCE_DIR}/third-party/glad/include/EGL/eglplatform.h"
|
||||
|
@ -37,6 +37,9 @@ else()
|
||||
endif()
|
||||
|
||||
if(EVDEV_INCLUDE_DIR AND EVDEV_LIBRARY)
|
||||
message(STATUS "Found libevdev library: ${EVDEV_LIBRARY}")
|
||||
message(STATUS "Found libevdev include directory: ${EVDEV_INCLUDE_DIR}")
|
||||
|
||||
include_directories(SYSTEM ${EVDEV_INCLUDE_DIR})
|
||||
list(APPEND PLATFORM_LIBRARIES ${EVDEV_LIBRARY})
|
||||
else()
|
||||
|
@ -19,7 +19,7 @@ macro(find_package) # cmake-lint: disable=C0103
|
||||
string(TOLOWER "${ARGV0}" ARGV0_LOWER)
|
||||
if(
|
||||
(("${ARGV0_LOWER}" STREQUAL "boost") AND DEFINED FETCH_CONTENT_BOOST_USED) OR
|
||||
(("${ARGV0_LOWER}" STREQUAL "libevdev") AND DEFINED FETCH_CONTENT_LIBEVDEV_USED)
|
||||
(("${ARGV0_LOWER}" STREQUAL "libevdev") AND DEFINED EXTERNAL_PROJECT_LIBEVDEV_USED)
|
||||
)
|
||||
# Do nothing, as the package has already been fetched
|
||||
else()
|
||||
@ -27,17 +27,3 @@ macro(find_package) # cmake-lint: disable=C0103
|
||||
_find_package(${ARGV})
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
# override pkg_check_modules function
|
||||
macro(pkg_check_modules) # cmake-lint: disable=C0103
|
||||
string(TOLOWER "${ARGV0}" ARGV0_LOWER)
|
||||
if(
|
||||
(("${ARGV0_LOWER}" STREQUAL "boost") AND DEFINED FETCH_CONTENT_BOOST_USED) OR
|
||||
(("${ARGV0_LOWER}" STREQUAL "libevdev") AND DEFINED FETCH_CONTENT_LIBEVDEV_USED)
|
||||
)
|
||||
# Do nothing, as the package has already been fetched
|
||||
else()
|
||||
# Call the original pkg_check_modules function
|
||||
_pkg_check_modules(${ARGV})
|
||||
endif()
|
||||
endmacro()
|
||||
|
@ -54,4 +54,6 @@ elseif(UNIX) # Linux
|
||||
"Enable building wayland specific code." ON)
|
||||
option(SUNSHINE_ENABLE_X11
|
||||
"Enable X11 grab if available." ON)
|
||||
option(SUNSHINE_USE_LEGACY_INPUT # TODO: Remove this legacy option after the next stable release
|
||||
"Use the legacy virtual input implementation." OFF)
|
||||
endif()
|
||||
|
@ -209,12 +209,13 @@ Install
|
||||
The `deb`, `rpm`, `zst`, `Flatpak` and `AppImage` packages should handle these steps automatically.
|
||||
Third party packages may not.
|
||||
|
||||
Sunshine needs access to `uinput` to create mouse and gamepad events.
|
||||
Sunshine needs access to `uinput` to create mouse and gamepad virtual devices and (optionally) to `uhid`
|
||||
in order to emulate a PS5 DualSense joypad with Gyro, Acceleration and Touchpad support.
|
||||
|
||||
#. Create and reload `udev` rules for uinput.
|
||||
#. Create and reload `udev` rules for `uinput` and `uhid`.
|
||||
.. code-block:: bash
|
||||
|
||||
echo 'KERNEL=="uinput", SUBSYSTEM=="misc", OPTIONS+="static_node=uinput", TAG+="uaccess"' | \
|
||||
echo 'KERNEL=="uinput", SUBSYSTEM=="misc", OPTIONS+="static_node=uinput", TAG+="uaccess"\nKERNEL=="uhid", TAG+="uaccess"' | \
|
||||
sudo tee /etc/udev/rules.d/60-sunshine.rules
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger
|
||||
|
@ -434,8 +434,8 @@ namespace config {
|
||||
std::chrono::duration<double> { 1 / 24.9 }, // key_repeat_period
|
||||
|
||||
{
|
||||
platf::supported_gamepads().front().data(),
|
||||
platf::supported_gamepads().front().size(),
|
||||
platf::supported_gamepads(nullptr).front().name.data(),
|
||||
platf::supported_gamepads(nullptr).front().name.size(),
|
||||
}, // Default gamepad
|
||||
true, // back as touchpad click enabled (manual DS4 only)
|
||||
true, // client gamepads with motion events are emulated as DS4
|
||||
@ -938,6 +938,17 @@ namespace config {
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<std::string_view> &
|
||||
get_supported_gamepad_options() {
|
||||
const auto options = platf::supported_gamepads(nullptr);
|
||||
static std::vector<std::string_view> opts {};
|
||||
opts.reserve(options.size());
|
||||
for (auto &opt : options) {
|
||||
opts.emplace_back(opt.name);
|
||||
}
|
||||
return opts;
|
||||
}
|
||||
|
||||
void
|
||||
apply_config(std::unordered_map<std::string, std::string> &&vars) {
|
||||
if (!fs::exists(stream.file_apps.c_str())) {
|
||||
@ -1086,7 +1097,7 @@ namespace config {
|
||||
input.key_repeat_delay = std::chrono::milliseconds { to };
|
||||
}
|
||||
|
||||
string_restricted_f(vars, "gamepad"s, input.gamepad, platf::supported_gamepads());
|
||||
string_restricted_f(vars, "gamepad"s, input.gamepad, get_supported_gamepad_options());
|
||||
bool_f(vars, "ds4_back_as_touchpad_click", input.ds4_back_as_touchpad_click);
|
||||
bool_f(vars, "motion_as_ds4", input.motion_as_ds4);
|
||||
bool_f(vars, "touchpad_as_ds4", input.touchpad_as_ds4);
|
||||
|
@ -118,7 +118,7 @@ namespace input {
|
||||
|
||||
void
|
||||
free_gamepad(platf::input_t &platf_input, int id) {
|
||||
platf::gamepad(platf_input, id, platf::gamepad_state_t {});
|
||||
platf::gamepad_update(platf_input, id, platf::gamepad_state_t {});
|
||||
platf::free_gamepad(platf_input, id);
|
||||
|
||||
free_id(gamepadMask, id);
|
||||
@ -711,28 +711,28 @@ namespace input {
|
||||
if (!release) {
|
||||
// Press any synthetic modifiers required for this key
|
||||
if (synthetic_modifiers & MODIFIER_SHIFT) {
|
||||
platf::keyboard(platf_input, VKEY_SHIFT, false, flags);
|
||||
platf::keyboard_update(platf_input, VKEY_SHIFT, false, flags);
|
||||
}
|
||||
if (synthetic_modifiers & MODIFIER_CTRL) {
|
||||
platf::keyboard(platf_input, VKEY_CONTROL, false, flags);
|
||||
platf::keyboard_update(platf_input, VKEY_CONTROL, false, flags);
|
||||
}
|
||||
if (synthetic_modifiers & MODIFIER_ALT) {
|
||||
platf::keyboard(platf_input, VKEY_MENU, false, flags);
|
||||
platf::keyboard_update(platf_input, VKEY_MENU, false, flags);
|
||||
}
|
||||
}
|
||||
|
||||
platf::keyboard(platf_input, map_keycode(key_code), release, flags);
|
||||
platf::keyboard_update(platf_input, map_keycode(key_code), release, flags);
|
||||
|
||||
if (!release) {
|
||||
// Raise any synthetic modifier keys we pressed
|
||||
if (synthetic_modifiers & MODIFIER_SHIFT) {
|
||||
platf::keyboard(platf_input, VKEY_SHIFT, true, flags);
|
||||
platf::keyboard_update(platf_input, VKEY_SHIFT, true, flags);
|
||||
}
|
||||
if (synthetic_modifiers & MODIFIER_CTRL) {
|
||||
platf::keyboard(platf_input, VKEY_CONTROL, true, flags);
|
||||
platf::keyboard_update(platf_input, VKEY_CONTROL, true, flags);
|
||||
}
|
||||
if (synthetic_modifiers & MODIFIER_ALT) {
|
||||
platf::keyboard(platf_input, VKEY_MENU, true, flags);
|
||||
platf::keyboard_update(platf_input, VKEY_MENU, true, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -963,7 +963,7 @@ namespace input {
|
||||
contact_area.second,
|
||||
};
|
||||
|
||||
platf::touch(input->client_context.get(), abs_port, touch);
|
||||
platf::touch_update(input->client_context.get(), abs_port, touch);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1022,7 +1022,7 @@ namespace input {
|
||||
contact_area.second,
|
||||
};
|
||||
|
||||
platf::pen(input->client_context.get(), abs_port, pen);
|
||||
platf::pen_update(input->client_context.get(), abs_port, pen);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1211,18 +1211,18 @@ namespace input {
|
||||
// Force the back button up
|
||||
gamepad.back_button_state = button_state_e::UP;
|
||||
state.buttonFlags &= ~platf::BACK;
|
||||
platf::gamepad(platf_input, gamepad.id, state);
|
||||
platf::gamepad_update(platf_input, gamepad.id, state);
|
||||
|
||||
// Press Home button
|
||||
state.buttonFlags |= platf::HOME;
|
||||
platf::gamepad(platf_input, gamepad.id, state);
|
||||
platf::gamepad_update(platf_input, gamepad.id, state);
|
||||
|
||||
// Sleep for a short time to allow the input to be detected
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
// Release Home button
|
||||
state.buttonFlags &= ~platf::HOME;
|
||||
platf::gamepad(platf_input, gamepad.id, state);
|
||||
platf::gamepad_update(platf_input, gamepad.id, state);
|
||||
|
||||
gamepad.back_timeout_id = nullptr;
|
||||
};
|
||||
@ -1236,7 +1236,7 @@ namespace input {
|
||||
}
|
||||
}
|
||||
|
||||
platf::gamepad(platf_input, gamepad.id, gamepad_state);
|
||||
platf::gamepad_update(platf_input, gamepad.id, gamepad_state);
|
||||
|
||||
gamepad.gamepad_state = gamepad_state;
|
||||
}
|
||||
@ -1665,7 +1665,7 @@ namespace input {
|
||||
// already released
|
||||
continue;
|
||||
}
|
||||
platf::keyboard(platf_input, vk_from_kpid(kp.first) & 0x00FF, true, flags_from_kpid(kp.first));
|
||||
platf::keyboard_update(platf_input, vk_from_kpid(kp.first) & 0x00FF, true, flags_from_kpid(kp.first));
|
||||
key_press[kp.first] = false;
|
||||
}
|
||||
});
|
||||
@ -1685,6 +1685,18 @@ namespace input {
|
||||
return std::make_unique<deinit_t>();
|
||||
}
|
||||
|
||||
bool
|
||||
probe_gamepads() {
|
||||
auto input = static_cast<platf::input_t *>(platf_input.get());
|
||||
const auto gamepads = platf::supported_gamepads(input);
|
||||
for (auto &gamepad : gamepads) {
|
||||
if (gamepad.is_enabled && gamepad.name != "auto") {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<input_t>
|
||||
alloc(safe::mail_t mail) {
|
||||
auto input = std::make_shared<input_t>(
|
||||
|
@ -22,6 +22,9 @@ namespace input {
|
||||
[[nodiscard]] std::unique_ptr<platf::deinit_t>
|
||||
init();
|
||||
|
||||
bool
|
||||
probe_gamepads();
|
||||
|
||||
std::shared_ptr<input_t>
|
||||
alloc(safe::mail_t mail);
|
||||
|
||||
|
@ -272,6 +272,11 @@ main(int argc, char *argv[]) {
|
||||
|
||||
reed_solomon_init();
|
||||
auto input_deinit_guard = input::init();
|
||||
|
||||
if (input::probe_gamepads()) {
|
||||
BOOST_LOG(warning) << "No gamepad input is available"sv;
|
||||
}
|
||||
|
||||
if (video::probe_encoders()) {
|
||||
BOOST_LOG(error) << "Video failed to find working encoder"sv;
|
||||
}
|
||||
|
@ -80,6 +80,12 @@ namespace platf {
|
||||
constexpr std::uint32_t TOUCHPAD_BUTTON = 0x100000;
|
||||
constexpr std::uint32_t MISC_BUTTON = 0x200000;
|
||||
|
||||
struct supported_gamepad_t {
|
||||
std::string name;
|
||||
bool is_enabled;
|
||||
std::string reason_disabled;
|
||||
};
|
||||
|
||||
enum class gamepad_feedback_e {
|
||||
rumble,
|
||||
rumble_triggers,
|
||||
@ -695,9 +701,9 @@ namespace platf {
|
||||
void
|
||||
hscroll(input_t &input, int distance);
|
||||
void
|
||||
keyboard(input_t &input, uint16_t modcode, bool release, uint8_t flags);
|
||||
keyboard_update(input_t &input, uint16_t modcode, bool release, uint8_t flags);
|
||||
void
|
||||
gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state);
|
||||
gamepad_update(input_t &input, int nr, const gamepad_state_t &gamepad_state);
|
||||
void
|
||||
unicode(input_t &input, char *utf8, int size);
|
||||
|
||||
@ -718,7 +724,7 @@ namespace platf {
|
||||
* @param touch The touch event.
|
||||
*/
|
||||
void
|
||||
touch(client_input_t *input, const touch_port_t &touch_port, const touch_input_t &touch);
|
||||
touch_update(client_input_t *input, const touch_port_t &touch_port, const touch_input_t &touch);
|
||||
|
||||
/**
|
||||
* @brief Sends a pen event to the OS.
|
||||
@ -727,7 +733,7 @@ namespace platf {
|
||||
* @param pen The pen event.
|
||||
*/
|
||||
void
|
||||
pen(client_input_t *input, const touch_port_t &touch_port, const pen_input_t &pen);
|
||||
pen_update(client_input_t *input, const touch_port_t &touch_port, const pen_input_t &pen);
|
||||
|
||||
/**
|
||||
* @brief Sends a gamepad touch event to the OS.
|
||||
@ -784,6 +790,10 @@ namespace platf {
|
||||
[[nodiscard]] std::unique_ptr<deinit_t>
|
||||
init();
|
||||
|
||||
std::vector<std::string_view> &
|
||||
supported_gamepads();
|
||||
/**
|
||||
* @brief Gets the supported gamepads for this platform backend.
|
||||
* @return Vector of gamepad options and status.
|
||||
*/
|
||||
std::vector<supported_gamepad_t> &
|
||||
supported_gamepads(input_t *input);
|
||||
} // namespace platf
|
||||
|
149
src/platform/linux/input/inputtino.cpp
Normal file
149
src/platform/linux/input/inputtino.cpp
Normal file
@ -0,0 +1,149 @@
|
||||
#include <inputtino/input.hpp>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
#include "src/config.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/utility.h"
|
||||
|
||||
#include "inputtino_common.h"
|
||||
#include "inputtino_gamepad.h"
|
||||
#include "inputtino_keyboard.h"
|
||||
#include "inputtino_mouse.h"
|
||||
#include "inputtino_pen.h"
|
||||
#include "inputtino_touch.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf {
|
||||
|
||||
input_t
|
||||
input() {
|
||||
return { new input_raw_t() };
|
||||
}
|
||||
|
||||
std::unique_ptr<client_input_t>
|
||||
allocate_client_input_context(input_t &input) {
|
||||
return std::make_unique<client_input_raw_t>(input);
|
||||
}
|
||||
|
||||
void
|
||||
freeInput(void *p) {
|
||||
auto *input = (input_raw_t *) p;
|
||||
delete input;
|
||||
}
|
||||
|
||||
void
|
||||
move_mouse(input_t &input, int deltaX, int deltaY) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
platf::mouse::move(raw, deltaX, deltaY);
|
||||
}
|
||||
|
||||
void
|
||||
abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
platf::mouse::move_abs(raw, touch_port, x, y);
|
||||
}
|
||||
|
||||
void
|
||||
button_mouse(input_t &input, int button, bool release) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
platf::mouse::button(raw, button, release);
|
||||
}
|
||||
|
||||
void
|
||||
scroll(input_t &input, int high_res_distance) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
platf::mouse::scroll(raw, high_res_distance);
|
||||
}
|
||||
|
||||
void
|
||||
hscroll(input_t &input, int high_res_distance) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
platf::mouse::hscroll(raw, high_res_distance);
|
||||
}
|
||||
|
||||
void
|
||||
keyboard_update(input_t &input, uint16_t modcode, bool release, uint8_t flags) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
platf::keyboard::update(raw, modcode, release, flags);
|
||||
}
|
||||
|
||||
void
|
||||
unicode(input_t &input, char *utf8, int size) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
platf::keyboard::unicode(raw, utf8, size);
|
||||
}
|
||||
|
||||
void
|
||||
touch_update(client_input_t *input, const touch_port_t &touch_port, const touch_input_t &touch) {
|
||||
auto raw = (client_input_raw_t *) input;
|
||||
platf::touch::update(raw, touch_port, touch);
|
||||
}
|
||||
|
||||
void
|
||||
pen_update(client_input_t *input, const touch_port_t &touch_port, const pen_input_t &pen) {
|
||||
auto raw = (client_input_raw_t *) input;
|
||||
platf::pen::update(raw, touch_port, pen);
|
||||
}
|
||||
|
||||
int
|
||||
alloc_gamepad(input_t &input, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
return platf::gamepad::alloc(raw, id, metadata, feedback_queue);
|
||||
}
|
||||
|
||||
void
|
||||
free_gamepad(input_t &input, int nr) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
platf::gamepad::free(raw, nr);
|
||||
}
|
||||
|
||||
void
|
||||
gamepad_update(input_t &input, int nr, const gamepad_state_t &gamepad_state) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
platf::gamepad::update(raw, nr, gamepad_state);
|
||||
}
|
||||
|
||||
void
|
||||
gamepad_touch(input_t &input, const gamepad_touch_t &touch) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
platf::gamepad::touch(raw, touch);
|
||||
}
|
||||
|
||||
void
|
||||
gamepad_motion(input_t &input, const gamepad_motion_t &motion) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
platf::gamepad::motion(raw, motion);
|
||||
}
|
||||
|
||||
void
|
||||
gamepad_battery(input_t &input, const gamepad_battery_t &battery) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
platf::gamepad::battery(raw, battery);
|
||||
}
|
||||
|
||||
platform_caps::caps_t
|
||||
get_capabilities() {
|
||||
platform_caps::caps_t caps = 0;
|
||||
// TODO: if has_uinput
|
||||
caps |= platform_caps::pen_touch;
|
||||
|
||||
// We support controller touchpad input only when emulating the PS5 controller
|
||||
if (config::input.gamepad == "ds5"sv || config::input.gamepad == "auto"sv) {
|
||||
caps |= platform_caps::controller_touch;
|
||||
}
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
util::point_t
|
||||
get_mouse_loc(input_t &input) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
return platf::mouse::get_location(raw);
|
||||
}
|
||||
|
||||
std::vector<supported_gamepad_t> &
|
||||
supported_gamepads(input_t *input) {
|
||||
return platf::gamepad::supported_gamepads(input);
|
||||
}
|
||||
} // namespace platf
|
97
src/platform/linux/input/inputtino_common.h
Normal file
97
src/platform/linux/input/inputtino_common.h
Normal file
@ -0,0 +1,97 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/locale.hpp>
|
||||
#include <inputtino/input.hpp>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
#include "src/config.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/utility.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf {
|
||||
|
||||
using joypads_t = std::variant<inputtino::XboxOneJoypad, inputtino::SwitchJoypad, inputtino::PS5Joypad>;
|
||||
|
||||
struct joypad_state {
|
||||
std::unique_ptr<joypads_t> joypad;
|
||||
gamepad_feedback_msg_t last_rumble;
|
||||
gamepad_feedback_msg_t last_rgb_led;
|
||||
};
|
||||
|
||||
struct input_raw_t {
|
||||
input_raw_t():
|
||||
mouse(inputtino::Mouse::create({
|
||||
.name = "Mouse passthrough",
|
||||
.vendor_id = 0xBEEF,
|
||||
.product_id = 0xDEAD,
|
||||
.version = 0x111,
|
||||
})),
|
||||
keyboard(inputtino::Keyboard::create({
|
||||
.name = "Keyboard passthrough",
|
||||
.vendor_id = 0xBEEF,
|
||||
.product_id = 0xDEAD,
|
||||
.version = 0x111,
|
||||
})),
|
||||
gamepads(MAX_GAMEPADS) {
|
||||
if (!mouse) {
|
||||
BOOST_LOG(warning) << "Unable to create virtual mouse: " << mouse.getErrorMessage();
|
||||
}
|
||||
if (!keyboard) {
|
||||
BOOST_LOG(warning) << "Unable to create virtual keyboard: " << keyboard.getErrorMessage();
|
||||
}
|
||||
}
|
||||
|
||||
~input_raw_t() = default;
|
||||
|
||||
// All devices are wrapped in Result because it might be that we aren't able to create them (ex: udev permission denied)
|
||||
inputtino::Result<inputtino::Mouse> mouse;
|
||||
inputtino::Result<inputtino::Keyboard> keyboard;
|
||||
|
||||
/**
|
||||
* A list of gamepads that are currently connected.
|
||||
* The pointer is shared because that state will be shared with background threads that deal with rumble and LED
|
||||
*/
|
||||
std::vector<std::shared_ptr<joypad_state>> gamepads;
|
||||
};
|
||||
|
||||
struct client_input_raw_t: public client_input_t {
|
||||
client_input_raw_t(input_t &input):
|
||||
touch(inputtino::TouchScreen::create({
|
||||
.name = "Touch passthrough",
|
||||
.vendor_id = 0xBEEF,
|
||||
.product_id = 0xDEAD,
|
||||
.version = 0x111,
|
||||
})),
|
||||
pen(inputtino::PenTablet::create({
|
||||
.name = "Pen passthrough",
|
||||
.vendor_id = 0xBEEF,
|
||||
.product_id = 0xDEAD,
|
||||
.version = 0x111,
|
||||
})) {
|
||||
global = (input_raw_t *) input.get();
|
||||
if (!touch) {
|
||||
BOOST_LOG(warning) << "Unable to create virtual touch screen: " << touch.getErrorMessage();
|
||||
}
|
||||
if (!pen) {
|
||||
BOOST_LOG(warning) << "Unable to create virtual pen tablet: " << pen.getErrorMessage();
|
||||
}
|
||||
}
|
||||
|
||||
input_raw_t *global;
|
||||
|
||||
// Device state and handles for pen and touch input must be stored in the per-client
|
||||
// input context, because each connected client may be sending their own independent
|
||||
// pen/touch events. To maintain separation, we expose separate pen and touch devices
|
||||
// for each client.
|
||||
inputtino::Result<inputtino::TouchScreen> touch;
|
||||
inputtino::Result<inputtino::PenTablet> pen;
|
||||
};
|
||||
|
||||
inline float
|
||||
deg2rad(float degree) {
|
||||
return degree * (M_PI / 180.f);
|
||||
}
|
||||
} // namespace platf
|
297
src/platform/linux/input/inputtino_gamepad.cpp
Normal file
297
src/platform/linux/input/inputtino_gamepad.cpp
Normal file
@ -0,0 +1,297 @@
|
||||
#include <boost/locale.hpp>
|
||||
#include <inputtino/input.hpp>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
#include "src/config.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/utility.h"
|
||||
|
||||
#include "inputtino_common.h"
|
||||
#include "inputtino_gamepad.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf::gamepad {
|
||||
|
||||
enum GamepadStatus {
|
||||
UHID_NOT_AVAILABLE = 0,
|
||||
UINPUT_NOT_AVAILABLE,
|
||||
XINPUT_NOT_AVAILABLE,
|
||||
GAMEPAD_STATUS // Helper to indicate the number of status
|
||||
};
|
||||
|
||||
auto
|
||||
create_xbox_one() {
|
||||
return inputtino::XboxOneJoypad::create({ .name = "Sunshine X-Box One (virtual) pad",
|
||||
// https://github.com/torvalds/linux/blob/master/drivers/input/joystick/xpad.c#L147
|
||||
.vendor_id = 0x045E,
|
||||
.product_id = 0x02EA,
|
||||
.version = 0x0408 });
|
||||
}
|
||||
|
||||
auto
|
||||
create_switch() {
|
||||
return inputtino::SwitchJoypad::create({ .name = "Sunshine Nintendo (virtual) pad",
|
||||
// https://github.com/torvalds/linux/blob/master/drivers/hid/hid-ids.h#L981
|
||||
.vendor_id = 0x057e,
|
||||
.product_id = 0x2009,
|
||||
.version = 0x8111 });
|
||||
}
|
||||
|
||||
auto
|
||||
create_ds5() {
|
||||
return inputtino::PS5Joypad::create({ .name = "Sunshine DualSense (virtual) pad",
|
||||
.vendor_id = 0x054C,
|
||||
.product_id = 0x0CE6,
|
||||
.version = 0x8111 });
|
||||
}
|
||||
|
||||
int
|
||||
alloc(input_raw_t *raw, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) {
|
||||
ControllerType selectedGamepadType;
|
||||
|
||||
if (config::input.gamepad == "xone"sv) {
|
||||
BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be Xbox One controller (manual selection)"sv;
|
||||
selectedGamepadType = XboxOneWired;
|
||||
}
|
||||
else if (config::input.gamepad == "ds5"sv) {
|
||||
BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be DualSense 5 controller (manual selection)"sv;
|
||||
selectedGamepadType = DualSenseWired;
|
||||
}
|
||||
else if (config::input.gamepad == "switch"sv) {
|
||||
BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be Nintendo Pro controller (manual selection)"sv;
|
||||
selectedGamepadType = SwitchProWired;
|
||||
}
|
||||
else if (metadata.type == LI_CTYPE_XBOX) {
|
||||
BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be Xbox One controller (auto-selected by client-reported type)"sv;
|
||||
selectedGamepadType = XboxOneWired;
|
||||
}
|
||||
else if (metadata.type == LI_CTYPE_PS) {
|
||||
BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be DualShock 5 controller (auto-selected by client-reported type)"sv;
|
||||
selectedGamepadType = DualSenseWired;
|
||||
}
|
||||
else if (metadata.type == LI_CTYPE_NINTENDO) {
|
||||
BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be Nintendo Pro controller (auto-selected by client-reported type)"sv;
|
||||
selectedGamepadType = SwitchProWired;
|
||||
}
|
||||
else if (config::input.motion_as_ds4 && (metadata.capabilities & (LI_CCAP_ACCEL | LI_CCAP_GYRO))) {
|
||||
BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be DualShock 5 controller (auto-selected by motion sensor presence)"sv;
|
||||
selectedGamepadType = DualSenseWired;
|
||||
}
|
||||
else if (config::input.touchpad_as_ds4 && (metadata.capabilities & LI_CCAP_TOUCHPAD)) {
|
||||
BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be DualShock 5 controller (auto-selected by touchpad presence)"sv;
|
||||
selectedGamepadType = DualSenseWired;
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be Xbox One controller (default)"sv;
|
||||
selectedGamepadType = XboxOneWired;
|
||||
}
|
||||
|
||||
if (selectedGamepadType == XboxOneWired || selectedGamepadType == SwitchProWired) {
|
||||
if (metadata.capabilities & (LI_CCAP_ACCEL | LI_CCAP_GYRO)) {
|
||||
BOOST_LOG(warning) << "Gamepad " << id.globalIndex << " has motion sensors, but they are not usable when emulating a joypad different from DS5"sv;
|
||||
}
|
||||
if (metadata.capabilities & LI_CCAP_TOUCHPAD) {
|
||||
BOOST_LOG(warning) << "Gamepad " << id.globalIndex << " has a touchpad, but it is not usable when emulating a joypad different from DS5"sv;
|
||||
}
|
||||
if (metadata.capabilities & LI_CCAP_RGB_LED) {
|
||||
BOOST_LOG(warning) << "Gamepad " << id.globalIndex << " has an RGB LED, but it is not usable when emulating a joypad different from DS5"sv;
|
||||
}
|
||||
}
|
||||
else if (selectedGamepadType == DualSenseWired) {
|
||||
if (!(metadata.capabilities & (LI_CCAP_ACCEL | LI_CCAP_GYRO))) {
|
||||
BOOST_LOG(warning) << "Gamepad " << id.globalIndex << " is emulating a DualShock 5 controller, but the client gamepad doesn't have motion sensors active"sv;
|
||||
}
|
||||
if (!(metadata.capabilities & LI_CCAP_TOUCHPAD)) {
|
||||
BOOST_LOG(warning) << "Gamepad " << id.globalIndex << " is emulating a DualShock 5 controller, but the client gamepad doesn't have a touchpad"sv;
|
||||
}
|
||||
}
|
||||
|
||||
auto gamepad = std::make_shared<joypad_state>(joypad_state {});
|
||||
auto on_rumble_fn = [feedback_queue, idx = id.clientRelativeIndex, gamepad](int low_freq, int high_freq) {
|
||||
// Don't resend duplicate rumble data
|
||||
if (gamepad->last_rumble.type == platf::gamepad_feedback_e::rumble && gamepad->last_rumble.data.rumble.lowfreq == low_freq && gamepad->last_rumble.data.rumble.highfreq == high_freq) {
|
||||
return;
|
||||
}
|
||||
|
||||
gamepad_feedback_msg_t msg = gamepad_feedback_msg_t::make_rumble(idx, low_freq, high_freq);
|
||||
feedback_queue->raise(msg);
|
||||
gamepad->last_rumble = msg;
|
||||
};
|
||||
|
||||
switch (selectedGamepadType) {
|
||||
case XboxOneWired: {
|
||||
auto xOne = create_xbox_one();
|
||||
if (xOne) {
|
||||
(*xOne).set_on_rumble(on_rumble_fn);
|
||||
gamepad->joypad = std::make_unique<joypads_t>(std::move(*xOne));
|
||||
raw->gamepads[id.globalIndex] = std::move(gamepad);
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(warning) << "Unable to create virtual Xbox One controller: " << xOne.getErrorMessage();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
case SwitchProWired: {
|
||||
auto switchPro = create_switch();
|
||||
if (switchPro) {
|
||||
(*switchPro).set_on_rumble(on_rumble_fn);
|
||||
gamepad->joypad = std::make_unique<joypads_t>(std::move(*switchPro));
|
||||
raw->gamepads[id.globalIndex] = std::move(gamepad);
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(warning) << "Unable to create virtual Switch Pro controller: " << switchPro.getErrorMessage();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
case DualSenseWired: {
|
||||
auto ds5 = create_ds5();
|
||||
if (ds5) {
|
||||
(*ds5).set_on_rumble(on_rumble_fn);
|
||||
(*ds5).set_on_led([feedback_queue, idx = id.clientRelativeIndex, gamepad](int r, int g, int b) {
|
||||
// Don't resend duplicate LED data
|
||||
if (gamepad->last_rgb_led.type == platf::gamepad_feedback_e::set_rgb_led && gamepad->last_rgb_led.data.rgb_led.r == r && gamepad->last_rgb_led.data.rgb_led.g == g && gamepad->last_rgb_led.data.rgb_led.b == b) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto msg = gamepad_feedback_msg_t::make_rgb_led(idx, r, g, b);
|
||||
feedback_queue->raise(msg);
|
||||
gamepad->last_rgb_led = msg;
|
||||
});
|
||||
|
||||
// Activate the motion sensors
|
||||
feedback_queue->raise(gamepad_feedback_msg_t::make_motion_event_state(id.clientRelativeIndex, LI_MOTION_TYPE_ACCEL, 100));
|
||||
feedback_queue->raise(gamepad_feedback_msg_t::make_motion_event_state(id.clientRelativeIndex, LI_MOTION_TYPE_GYRO, 100));
|
||||
|
||||
gamepad->joypad = std::make_unique<joypads_t>(std::move(*ds5));
|
||||
raw->gamepads[id.globalIndex] = std::move(gamepad);
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(warning) << "Unable to create virtual DualShock 5 controller: " << ds5.getErrorMessage();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
free(input_raw_t *raw, int nr) {
|
||||
// This will call the destructor which in turn will stop the background threads for rumble and LED (and ultimately remove the joypad device)
|
||||
raw->gamepads[nr]->joypad.reset();
|
||||
raw->gamepads[nr].reset();
|
||||
}
|
||||
|
||||
void
|
||||
update(input_raw_t *raw, int nr, const gamepad_state_t &gamepad_state) {
|
||||
auto gamepad = raw->gamepads[nr];
|
||||
if (!gamepad) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::visit([gamepad_state](inputtino::Joypad &gc) {
|
||||
gc.set_pressed_buttons(gamepad_state.buttonFlags);
|
||||
gc.set_stick(inputtino::Joypad::LS, gamepad_state.lsX, gamepad_state.lsY);
|
||||
gc.set_stick(inputtino::Joypad::RS, gamepad_state.rsX, gamepad_state.rsY);
|
||||
gc.set_triggers(gamepad_state.lt, gamepad_state.rt);
|
||||
},
|
||||
*gamepad->joypad);
|
||||
}
|
||||
|
||||
void
|
||||
touch(input_raw_t *raw, const gamepad_touch_t &touch) {
|
||||
auto gamepad = raw->gamepads[touch.id.globalIndex];
|
||||
if (!gamepad) {
|
||||
return;
|
||||
}
|
||||
// Only the PS5 controller supports touch input
|
||||
if (std::holds_alternative<inputtino::PS5Joypad>(*gamepad->joypad)) {
|
||||
if (touch.pressure > 0.5) {
|
||||
std::get<inputtino::PS5Joypad>(*gamepad->joypad).place_finger(touch.pointerId, touch.x * inputtino::PS5Joypad::touchpad_width, touch.y * inputtino::PS5Joypad::touchpad_height);
|
||||
}
|
||||
else {
|
||||
std::get<inputtino::PS5Joypad>(*gamepad->joypad).release_finger(touch.pointerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
motion(input_raw_t *raw, const gamepad_motion_t &motion) {
|
||||
auto gamepad = raw->gamepads[motion.id.globalIndex];
|
||||
if (!gamepad) {
|
||||
return;
|
||||
}
|
||||
// Only the PS5 controller supports motion
|
||||
if (std::holds_alternative<inputtino::PS5Joypad>(*gamepad->joypad)) {
|
||||
switch (motion.motionType) {
|
||||
case LI_MOTION_TYPE_ACCEL:
|
||||
std::get<inputtino::PS5Joypad>(*gamepad->joypad).set_motion(inputtino::PS5Joypad::ACCELERATION, motion.x, motion.y, motion.z);
|
||||
break;
|
||||
case LI_MOTION_TYPE_GYRO:
|
||||
std::get<inputtino::PS5Joypad>(*gamepad->joypad).set_motion(inputtino::PS5Joypad::GYROSCOPE, deg2rad(motion.x), deg2rad(motion.y), deg2rad(motion.z));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
battery(input_raw_t *raw, const gamepad_battery_t &battery) {
|
||||
auto gamepad = raw->gamepads[battery.id.globalIndex];
|
||||
if (!gamepad) {
|
||||
return;
|
||||
}
|
||||
// Only the PS5 controller supports motion
|
||||
if (std::holds_alternative<inputtino::PS5Joypad>(*gamepad->joypad)) {
|
||||
inputtino::PS5Joypad::BATTERY_STATE state = inputtino::PS5Joypad::CHARGHING_ERROR;
|
||||
switch (battery.state) {
|
||||
case LI_BATTERY_STATE_CHARGING:
|
||||
state = inputtino::PS5Joypad::BATTERY_CHARGHING;
|
||||
break;
|
||||
case LI_BATTERY_STATE_DISCHARGING:
|
||||
state = inputtino::PS5Joypad::BATTERY_DISCHARGING;
|
||||
break;
|
||||
case LI_BATTERY_STATE_FULL:
|
||||
state = inputtino::PS5Joypad::BATTERY_FULL;
|
||||
break;
|
||||
}
|
||||
std::get<inputtino::PS5Joypad>(*gamepad->joypad).set_battery(state, battery.percentage);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<supported_gamepad_t> &
|
||||
supported_gamepads(input_t *input) {
|
||||
if (!input) {
|
||||
static std::vector gps {
|
||||
supported_gamepad_t { "auto", true, "" },
|
||||
supported_gamepad_t { "xone", false, "" },
|
||||
supported_gamepad_t { "ds5", false, "" },
|
||||
supported_gamepad_t { "switch", false, "" },
|
||||
};
|
||||
|
||||
return gps;
|
||||
}
|
||||
|
||||
auto ds5 = create_ds5();
|
||||
auto switchPro = create_switch();
|
||||
auto xOne = create_xbox_one();
|
||||
|
||||
static std::vector gps {
|
||||
supported_gamepad_t { "auto", true, "" },
|
||||
supported_gamepad_t { "xone", static_cast<bool>(xOne), !xOne ? xOne.getErrorMessage() : "" },
|
||||
supported_gamepad_t { "ds5", static_cast<bool>(ds5), !ds5 ? ds5.getErrorMessage() : "" },
|
||||
supported_gamepad_t { "switch", static_cast<bool>(switchPro), !switchPro ? switchPro.getErrorMessage() : "" },
|
||||
};
|
||||
|
||||
for (auto &[name, is_enabled, reason_disabled] : gps) {
|
||||
if (!is_enabled) {
|
||||
BOOST_LOG(warning) << "Gamepad " << name << " is disabled due to " << reason_disabled;
|
||||
}
|
||||
}
|
||||
|
||||
return gps;
|
||||
}
|
||||
} // namespace platf::gamepad
|
41
src/platform/linux/input/inputtino_gamepad.h
Normal file
41
src/platform/linux/input/inputtino_gamepad.h
Normal file
@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/locale.hpp>
|
||||
#include <inputtino/input.hpp>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
#include "src/platform/common.h"
|
||||
|
||||
#include "inputtino_common.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf::gamepad {
|
||||
|
||||
enum ControllerType {
|
||||
XboxOneWired,
|
||||
DualSenseWired,
|
||||
SwitchProWired
|
||||
};
|
||||
|
||||
int
|
||||
alloc(input_raw_t *raw, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue);
|
||||
|
||||
void
|
||||
free(input_raw_t *raw, int nr);
|
||||
|
||||
void
|
||||
update(input_raw_t *raw, int nr, const gamepad_state_t &gamepad_state);
|
||||
|
||||
void
|
||||
touch(input_raw_t *raw, const gamepad_touch_t &touch);
|
||||
|
||||
void
|
||||
motion(input_raw_t *raw, const gamepad_motion_t &motion);
|
||||
|
||||
void
|
||||
battery(input_raw_t *raw, const gamepad_battery_t &battery);
|
||||
|
||||
std::vector<supported_gamepad_t> &
|
||||
supported_gamepads(input_t *input);
|
||||
} // namespace platf::gamepad
|
117
src/platform/linux/input/inputtino_keyboard.cpp
Normal file
117
src/platform/linux/input/inputtino_keyboard.cpp
Normal file
@ -0,0 +1,117 @@
|
||||
#include <boost/locale.hpp>
|
||||
#include <inputtino/input.hpp>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
#include "src/config.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/utility.h"
|
||||
|
||||
#include "inputtino_common.h"
|
||||
#include "inputtino_keyboard.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf::keyboard {
|
||||
|
||||
/**
|
||||
* Takes an UTF-32 encoded string and returns a hex string representation of the bytes (uppercase)
|
||||
*
|
||||
* ex: ['👱'] = "1F471" // see UTF encoding at https://www.compart.com/en/unicode/U+1F471
|
||||
*
|
||||
* adapted from: https://stackoverflow.com/a/7639754
|
||||
*/
|
||||
std::string
|
||||
to_hex(const std::basic_string<char32_t> &str) {
|
||||
std::stringstream ss;
|
||||
ss << std::hex << std::setfill('0');
|
||||
for (const auto &ch : str) {
|
||||
ss << static_cast<uint32_t>(ch);
|
||||
}
|
||||
|
||||
std::string hex_unicode(ss.str());
|
||||
std::ranges::transform(hex_unicode, hex_unicode.begin(), ::toupper);
|
||||
return hex_unicode;
|
||||
}
|
||||
|
||||
/**
|
||||
* A map of linux scan code -> Moonlight keyboard code
|
||||
*/
|
||||
static const std::map<short, short> key_mappings = {
|
||||
{ KEY_BACKSPACE, 0x08 }, { KEY_TAB, 0x09 }, { KEY_ENTER, 0x0D }, { KEY_LEFTSHIFT, 0x10 },
|
||||
{ KEY_LEFTCTRL, 0x11 }, { KEY_CAPSLOCK, 0x14 }, { KEY_ESC, 0x1B }, { KEY_SPACE, 0x20 },
|
||||
{ KEY_PAGEUP, 0x21 }, { KEY_PAGEDOWN, 0x22 }, { KEY_END, 0x23 }, { KEY_HOME, 0x24 },
|
||||
{ KEY_LEFT, 0x25 }, { KEY_UP, 0x26 }, { KEY_RIGHT, 0x27 }, { KEY_DOWN, 0x28 },
|
||||
{ KEY_SYSRQ, 0x2C }, { KEY_INSERT, 0x2D }, { KEY_DELETE, 0x2E }, { KEY_0, 0x30 },
|
||||
{ KEY_1, 0x31 }, { KEY_2, 0x32 }, { KEY_3, 0x33 }, { KEY_4, 0x34 },
|
||||
{ KEY_5, 0x35 }, { KEY_6, 0x36 }, { KEY_7, 0x37 }, { KEY_8, 0x38 },
|
||||
{ KEY_9, 0x39 }, { KEY_A, 0x41 }, { KEY_B, 0x42 }, { KEY_C, 0x43 },
|
||||
{ KEY_D, 0x44 }, { KEY_E, 0x45 }, { KEY_F, 0x46 }, { KEY_G, 0x47 },
|
||||
{ KEY_H, 0x48 }, { KEY_I, 0x49 }, { KEY_J, 0x4A }, { KEY_K, 0x4B },
|
||||
{ KEY_L, 0x4C }, { KEY_M, 0x4D }, { KEY_N, 0x4E }, { KEY_O, 0x4F },
|
||||
{ KEY_P, 0x50 }, { KEY_Q, 0x51 }, { KEY_R, 0x52 }, { KEY_S, 0x53 },
|
||||
{ KEY_T, 0x54 }, { KEY_U, 0x55 }, { KEY_V, 0x56 }, { KEY_W, 0x57 },
|
||||
{ KEY_X, 0x58 }, { KEY_Y, 0x59 }, { KEY_Z, 0x5A }, { KEY_LEFTMETA, 0x5B },
|
||||
{ KEY_RIGHTMETA, 0x5C }, { KEY_KP0, 0x60 }, { KEY_KP1, 0x61 }, { KEY_KP2, 0x62 },
|
||||
{ KEY_KP3, 0x63 }, { KEY_KP4, 0x64 }, { KEY_KP5, 0x65 }, { KEY_KP6, 0x66 },
|
||||
{ KEY_KP7, 0x67 }, { KEY_KP8, 0x68 }, { KEY_KP9, 0x69 }, { KEY_KPASTERISK, 0x6A },
|
||||
{ KEY_KPPLUS, 0x6B }, { KEY_KPMINUS, 0x6D }, { KEY_KPDOT, 0x6E }, { KEY_KPSLASH, 0x6F },
|
||||
{ KEY_F1, 0x70 }, { KEY_F2, 0x71 }, { KEY_F3, 0x72 }, { KEY_F4, 0x73 },
|
||||
{ KEY_F5, 0x74 }, { KEY_F6, 0x75 }, { KEY_F7, 0x76 }, { KEY_F8, 0x77 },
|
||||
{ KEY_F9, 0x78 }, { KEY_F10, 0x79 }, { KEY_F11, 0x7A }, { KEY_F12, 0x7B },
|
||||
{ KEY_NUMLOCK, 0x90 }, { KEY_SCROLLLOCK, 0x91 }, { KEY_LEFTSHIFT, 0xA0 }, { KEY_RIGHTSHIFT, 0xA1 },
|
||||
{ KEY_LEFTCTRL, 0xA2 }, { KEY_RIGHTCTRL, 0xA3 }, { KEY_LEFTALT, 0xA4 }, { KEY_RIGHTALT, 0xA5 },
|
||||
{ KEY_SEMICOLON, 0xBA }, { KEY_EQUAL, 0xBB }, { KEY_COMMA, 0xBC }, { KEY_MINUS, 0xBD },
|
||||
{ KEY_DOT, 0xBE }, { KEY_SLASH, 0xBF }, { KEY_GRAVE, 0xC0 }, { KEY_LEFTBRACE, 0xDB },
|
||||
{ KEY_BACKSLASH, 0xDC }, { KEY_RIGHTBRACE, 0xDD }, { KEY_APOSTROPHE, 0xDE }, { KEY_102ND, 0xE2 }
|
||||
};
|
||||
|
||||
void
|
||||
update(input_raw_t *raw, uint16_t modcode, bool release, uint8_t flags) {
|
||||
if (raw->keyboard) {
|
||||
if (release) {
|
||||
(*raw->keyboard).release(modcode);
|
||||
}
|
||||
else {
|
||||
(*raw->keyboard).press(modcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
unicode(input_raw_t *raw, char *utf8, int size) {
|
||||
if (raw->keyboard) {
|
||||
/* Reading input text as UTF-8 */
|
||||
auto utf8_str = boost::locale::conv::to_utf<wchar_t>(utf8, utf8 + size, "UTF-8");
|
||||
/* Converting to UTF-32 */
|
||||
auto utf32_str = boost::locale::conv::utf_to_utf<char32_t>(utf8_str);
|
||||
/* To HEX string */
|
||||
auto hex_unicode = to_hex(utf32_str);
|
||||
BOOST_LOG(debug) << "Unicode, typing U+"sv << hex_unicode;
|
||||
|
||||
/* pressing <CTRL> + <SHIFT> + U */
|
||||
(*raw->keyboard).press(0xA2); // LEFTCTRL
|
||||
(*raw->keyboard).press(0xA0); // LEFTSHIFT
|
||||
(*raw->keyboard).press(0x55); // U
|
||||
(*raw->keyboard).release(0x55); // U
|
||||
|
||||
/* input each HEX character */
|
||||
for (auto &ch : hex_unicode) {
|
||||
auto key_str = "KEY_"s + ch;
|
||||
auto keycode = libevdev_event_code_from_name(EV_KEY, key_str.c_str());
|
||||
auto wincode = key_mappings.find(keycode);
|
||||
if (keycode == -1 || wincode == key_mappings.end()) {
|
||||
BOOST_LOG(warning) << "Unicode, unable to find keycode for: "sv << ch;
|
||||
}
|
||||
else {
|
||||
(*raw->keyboard).press(wincode->second);
|
||||
(*raw->keyboard).release(wincode->second);
|
||||
}
|
||||
}
|
||||
|
||||
/* releasing <SHIFT> and <CTRL> */
|
||||
(*raw->keyboard).release(0xA0); // LEFTSHIFT
|
||||
(*raw->keyboard).release(0xA2); // LEFTCTRL
|
||||
}
|
||||
}
|
||||
} // namespace platf::keyboard
|
17
src/platform/linux/input/inputtino_keyboard.h
Normal file
17
src/platform/linux/input/inputtino_keyboard.h
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/locale.hpp>
|
||||
#include <inputtino/input.hpp>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
#include "inputtino_common.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf::keyboard {
|
||||
void
|
||||
update(input_raw_t *raw, uint16_t modcode, bool release, uint8_t flags);
|
||||
|
||||
void
|
||||
unicode(input_raw_t *raw, char *utf8, int size);
|
||||
} // namespace platf::keyboard
|
88
src/platform/linux/input/inputtino_mouse.cpp
Normal file
88
src/platform/linux/input/inputtino_mouse.cpp
Normal file
@ -0,0 +1,88 @@
|
||||
#include <boost/locale.hpp>
|
||||
#include <inputtino/input.hpp>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
#include "src/config.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/utility.h"
|
||||
|
||||
#include "inputtino_common.h"
|
||||
#include "inputtino_mouse.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf::mouse {
|
||||
|
||||
void
|
||||
move(input_raw_t *raw, int deltaX, int deltaY) {
|
||||
if (raw->mouse) {
|
||||
(*raw->mouse).move(deltaX, deltaY);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
move_abs(input_raw_t *raw, const touch_port_t &touch_port, float x, float y) {
|
||||
if (raw->mouse) {
|
||||
(*raw->mouse).move_abs(x, y, touch_port.width, touch_port.height);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
button(input_raw_t *raw, int button, bool release) {
|
||||
if (raw->mouse) {
|
||||
inputtino::Mouse::MOUSE_BUTTON btn_type;
|
||||
switch (button) {
|
||||
case BUTTON_LEFT:
|
||||
btn_type = inputtino::Mouse::LEFT;
|
||||
break;
|
||||
case BUTTON_MIDDLE:
|
||||
btn_type = inputtino::Mouse::MIDDLE;
|
||||
break;
|
||||
case BUTTON_RIGHT:
|
||||
btn_type = inputtino::Mouse::RIGHT;
|
||||
break;
|
||||
case BUTTON_X1:
|
||||
btn_type = inputtino::Mouse::SIDE;
|
||||
break;
|
||||
case BUTTON_X2:
|
||||
btn_type = inputtino::Mouse::EXTRA;
|
||||
break;
|
||||
default:
|
||||
BOOST_LOG(warning) << "Unknown mouse button: " << button;
|
||||
return;
|
||||
}
|
||||
if (release) {
|
||||
(*raw->mouse).release(btn_type);
|
||||
}
|
||||
else {
|
||||
(*raw->mouse).press(btn_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
scroll(input_raw_t *raw, int high_res_distance) {
|
||||
if (raw->mouse) {
|
||||
(*raw->mouse).vertical_scroll(high_res_distance);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
hscroll(input_raw_t *raw, int high_res_distance) {
|
||||
if (raw->mouse) {
|
||||
(*raw->mouse).horizontal_scroll(high_res_distance);
|
||||
}
|
||||
}
|
||||
|
||||
util::point_t
|
||||
get_location(input_raw_t *raw) {
|
||||
if (raw->mouse) {
|
||||
// TODO: decide what to do after https://github.com/games-on-whales/inputtino/issues/6 is resolved.
|
||||
// TODO: auto x = (*raw->mouse).get_absolute_x();
|
||||
// TODO: auto y = (*raw->mouse).get_absolute_y();
|
||||
return { 0, 0 };
|
||||
}
|
||||
return { 0, 0 };
|
||||
}
|
||||
} // namespace platf::mouse
|
31
src/platform/linux/input/inputtino_mouse.h
Normal file
31
src/platform/linux/input/inputtino_mouse.h
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/locale.hpp>
|
||||
#include <inputtino/input.hpp>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
#include "src/platform/common.h"
|
||||
|
||||
#include "inputtino_common.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf::mouse {
|
||||
void
|
||||
move(input_raw_t *raw, int deltaX, int deltaY);
|
||||
|
||||
void
|
||||
move_abs(input_raw_t *raw, const touch_port_t &touch_port, float x, float y);
|
||||
|
||||
void
|
||||
button(input_raw_t *raw, int button, bool release);
|
||||
|
||||
void
|
||||
scroll(input_raw_t *raw, int high_res_distance);
|
||||
|
||||
void
|
||||
hscroll(input_raw_t *raw, int high_res_distance);
|
||||
|
||||
util::point_t
|
||||
get_location(input_raw_t *raw);
|
||||
} // namespace platf::mouse
|
69
src/platform/linux/input/inputtino_pen.cpp
Normal file
69
src/platform/linux/input/inputtino_pen.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
#include <boost/locale.hpp>
|
||||
#include <inputtino/input.hpp>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
#include "src/config.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/utility.h"
|
||||
|
||||
#include "inputtino_common.h"
|
||||
#include "inputtino_pen.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf::pen {
|
||||
void
|
||||
update(client_input_raw_t *raw, const touch_port_t &touch_port, const pen_input_t &pen) {
|
||||
if (raw->pen) {
|
||||
// First set the buttons
|
||||
(*raw->pen).set_btn(inputtino::PenTablet::PRIMARY, pen.penButtons & LI_PEN_BUTTON_PRIMARY);
|
||||
(*raw->pen).set_btn(inputtino::PenTablet::SECONDARY, pen.penButtons & LI_PEN_BUTTON_SECONDARY);
|
||||
(*raw->pen).set_btn(inputtino::PenTablet::TERTIARY, pen.penButtons & LI_PEN_BUTTON_TERTIARY);
|
||||
|
||||
// Set the tool
|
||||
inputtino::PenTablet::TOOL_TYPE tool;
|
||||
switch (pen.toolType) {
|
||||
case LI_TOOL_TYPE_PEN:
|
||||
tool = inputtino::PenTablet::PEN;
|
||||
break;
|
||||
case LI_TOOL_TYPE_ERASER:
|
||||
tool = inputtino::PenTablet::ERASER;
|
||||
break;
|
||||
default:
|
||||
tool = inputtino::PenTablet::SAME_AS_BEFORE;
|
||||
break;
|
||||
}
|
||||
|
||||
// Normalize rotation value to 0-359 degree range
|
||||
auto rotation = pen.rotation;
|
||||
if (rotation != LI_ROT_UNKNOWN) {
|
||||
rotation %= 360;
|
||||
}
|
||||
|
||||
// Here we receive:
|
||||
// - Rotation: degrees from vertical in Y dimension (parallel to screen, 0..360)
|
||||
// - Tilt: degrees from vertical in Z dimension (perpendicular to screen, 0..90)
|
||||
float tilt_x = 0;
|
||||
float tilt_y = 0;
|
||||
// Convert polar coordinates into Y tilt angles
|
||||
if (pen.tilt != LI_TILT_UNKNOWN && rotation != LI_ROT_UNKNOWN) {
|
||||
auto rotation_rads = deg2rad(rotation);
|
||||
auto tilt_rads = deg2rad(pen.tilt);
|
||||
auto r = std::sin(tilt_rads);
|
||||
auto z = std::cos(tilt_rads);
|
||||
|
||||
tilt_x = std::atan2(std::sin(-rotation_rads) * r, z) * 180.f / M_PI;
|
||||
tilt_y = std::atan2(std::cos(-rotation_rads) * r, z) * 180.f / M_PI;
|
||||
}
|
||||
|
||||
(*raw->pen).place_tool(tool,
|
||||
pen.x,
|
||||
pen.y,
|
||||
pen.eventType == LI_TOUCH_EVENT_DOWN ? pen.pressureOrDistance : -1,
|
||||
pen.eventType == LI_TOUCH_EVENT_HOVER ? pen.pressureOrDistance : -1,
|
||||
tilt_x,
|
||||
tilt_y);
|
||||
}
|
||||
}
|
||||
} // namespace platf::pen
|
16
src/platform/linux/input/inputtino_pen.h
Normal file
16
src/platform/linux/input/inputtino_pen.h
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/locale.hpp>
|
||||
#include <inputtino/input.hpp>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
#include "src/platform/common.h"
|
||||
|
||||
#include "inputtino_common.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf::pen {
|
||||
void
|
||||
update(client_input_raw_t *raw, const touch_port_t &touch_port, const pen_input_t &pen);
|
||||
}
|
51
src/platform/linux/input/inputtino_touch.cpp
Normal file
51
src/platform/linux/input/inputtino_touch.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
#include <boost/locale.hpp>
|
||||
#include <inputtino/input.hpp>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
#include "src/config.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/utility.h"
|
||||
|
||||
#include "inputtino_common.h"
|
||||
#include "inputtino_touch.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf::touch {
|
||||
void
|
||||
update(client_input_raw_t *raw, const touch_port_t &touch_port, const touch_input_t &touch) {
|
||||
if (raw->touch) {
|
||||
switch (touch.eventType) {
|
||||
case LI_TOUCH_EVENT_HOVER:
|
||||
case LI_TOUCH_EVENT_DOWN:
|
||||
case LI_TOUCH_EVENT_MOVE: {
|
||||
// Convert our 0..360 range to -90..90 relative to Y axis
|
||||
int adjusted_angle = touch.rotation;
|
||||
|
||||
if (adjusted_angle > 90 && adjusted_angle < 270) {
|
||||
// Lower hemisphere
|
||||
adjusted_angle = 180 - adjusted_angle;
|
||||
}
|
||||
|
||||
// Wrap the value if it's out of range
|
||||
if (adjusted_angle > 90) {
|
||||
adjusted_angle -= 360;
|
||||
}
|
||||
else if (adjusted_angle < -90) {
|
||||
adjusted_angle += 360;
|
||||
}
|
||||
(*raw->touch).place_finger(touch.pointerId, touch.x, touch.y, touch.pressureOrDistance, adjusted_angle);
|
||||
break;
|
||||
}
|
||||
case LI_TOUCH_EVENT_CANCEL:
|
||||
case LI_TOUCH_EVENT_UP:
|
||||
case LI_TOUCH_EVENT_HOVER_LEAVE: {
|
||||
(*raw->touch).release_finger(touch.pointerId);
|
||||
break;
|
||||
}
|
||||
// TODO: LI_TOUCH_EVENT_CANCEL_ALL
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace platf::touch
|
16
src/platform/linux/input/inputtino_touch.h
Normal file
16
src/platform/linux/input/inputtino_touch.h
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/locale.hpp>
|
||||
#include <inputtino/input.hpp>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
#include "src/platform/common.h"
|
||||
|
||||
#include "inputtino_common.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf::touch {
|
||||
void
|
||||
update(client_input_raw_t *raw, const touch_port_t &touch_port, const touch_input_t &touch);
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
/**
|
||||
* @file src/platform/linux/input.cpp
|
||||
* @brief todo
|
||||
* @file src/platform/linux/input/legacy_input.cpp
|
||||
* @brief Implementation of input handling, prior to migration to inputtino
|
||||
* @todo Remove this file after the next stable release
|
||||
*/
|
||||
#include <fcntl.h>
|
||||
#include <linux/uinput.h>
|
||||
@ -32,7 +33,7 @@ extern "C" {
|
||||
|
||||
#include "src/platform/common.h"
|
||||
|
||||
#include "misc.h"
|
||||
#include "src/platform/linux/misc.h"
|
||||
|
||||
// Support older versions
|
||||
#ifndef REL_HWHEEL_HI_RES
|
||||
@ -1502,7 +1503,7 @@ namespace platf {
|
||||
* ```
|
||||
*/
|
||||
void
|
||||
keyboard(input_t &input, uint16_t modcode, bool release, uint8_t flags) {
|
||||
keyboard_update(input_t &input, uint16_t modcode, bool release, uint8_t flags) {
|
||||
auto keyboard = ((input_raw_t *) input.get())->keyboard_input.get();
|
||||
if (!keyboard) {
|
||||
x_keyboard(input, modcode, release, flags);
|
||||
@ -1617,7 +1618,7 @@ namespace platf {
|
||||
}
|
||||
|
||||
void
|
||||
gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) {
|
||||
gamepad_update(input_t &input, int nr, const gamepad_state_t &gamepad_state) {
|
||||
TUPLE_2D_REF(uinput, gamepad_state_old, ((input_raw_t *) input.get())->gamepads[nr]);
|
||||
|
||||
auto bf = gamepad_state.buttonFlags ^ gamepad_state_old.buttonFlags;
|
||||
@ -1764,7 +1765,7 @@ namespace platf {
|
||||
* @param touch The touch event.
|
||||
*/
|
||||
void
|
||||
touch(client_input_t *input, const touch_port_t &touch_port, const touch_input_t &touch) {
|
||||
touch_update(client_input_t *input, const touch_port_t &touch_port, const touch_input_t &touch) {
|
||||
auto raw = (client_input_raw_t *) input;
|
||||
|
||||
if (!raw->touch_input) {
|
||||
@ -1949,7 +1950,7 @@ namespace platf {
|
||||
* @param pen The pen event.
|
||||
*/
|
||||
void
|
||||
pen(client_input_t *input, const touch_port_t &touch_port, const pen_input_t &pen) {
|
||||
pen_update(client_input_t *input, const touch_port_t &touch_port, const pen_input_t &pen) {
|
||||
auto raw = (client_input_raw_t *) input;
|
||||
|
||||
if (!raw->pen_input) {
|
||||
@ -2594,9 +2595,11 @@ namespace platf {
|
||||
delete input;
|
||||
}
|
||||
|
||||
std::vector<std::string_view> &
|
||||
supported_gamepads() {
|
||||
static std::vector<std::string_view> gamepads { "x360"sv };
|
||||
std::vector<supported_gamepad_t> &
|
||||
supported_gamepads(input_t *input) {
|
||||
static std::vector gamepads {
|
||||
supported_gamepad_t { "x360", true, "" },
|
||||
};
|
||||
|
||||
return gamepads;
|
||||
}
|
@ -243,7 +243,7 @@ const KeyCodeMap kKeyCodesMap[] = {
|
||||
}
|
||||
|
||||
void
|
||||
keyboard(input_t &input, uint16_t modcode, bool release, uint8_t flags) {
|
||||
keyboard_update(input_t &input, uint16_t modcode, bool release, uint8_t flags) {
|
||||
auto key = keysym(modcode);
|
||||
|
||||
BOOST_LOG(debug) << "got keycode: 0x"sv << std::hex << modcode << ", translated to: 0x" << std::hex << key << ", release:" << release;
|
||||
@ -317,7 +317,7 @@ const KeyCodeMap kKeyCodesMap[] = {
|
||||
}
|
||||
|
||||
void
|
||||
gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) {
|
||||
gamepad_update(input_t &input, int nr, const gamepad_state_t &gamepad_state) {
|
||||
BOOST_LOG(info) << "gamepad: Gamepad not yet implemented for MacOS."sv;
|
||||
}
|
||||
|
||||
@ -492,7 +492,7 @@ const KeyCodeMap kKeyCodesMap[] = {
|
||||
* @param touch The touch event.
|
||||
*/
|
||||
void
|
||||
touch(client_input_t *input, const touch_port_t &touch_port, const touch_input_t &touch) {
|
||||
touch_update(client_input_t *input, const touch_port_t &touch_port, const touch_input_t &touch) {
|
||||
// Unimplemented feature - platform_caps::pen_touch
|
||||
}
|
||||
|
||||
@ -503,7 +503,7 @@ const KeyCodeMap kKeyCodesMap[] = {
|
||||
* @param pen The pen event.
|
||||
*/
|
||||
void
|
||||
pen(client_input_t *input, const touch_port_t &touch_port, const pen_input_t &pen) {
|
||||
pen_update(client_input_t *input, const touch_port_t &touch_port, const pen_input_t &pen) {
|
||||
// Unimplemented feature - platform_caps::pen_touch
|
||||
}
|
||||
|
||||
@ -596,9 +596,11 @@ const KeyCodeMap kKeyCodesMap[] = {
|
||||
delete input;
|
||||
}
|
||||
|
||||
std::vector<std::string_view> &
|
||||
supported_gamepads() {
|
||||
static std::vector<std::string_view> gamepads { ""sv };
|
||||
std::vector<supported_gamepad_t> &
|
||||
supported_gamepads(input_t *input) {
|
||||
static std::vector gamepads {
|
||||
supported_gamepad_t { "", false, "gamepads.macos_not_implemented" }
|
||||
};
|
||||
|
||||
return gamepads;
|
||||
}
|
||||
|
@ -612,7 +612,7 @@ namespace platf {
|
||||
}
|
||||
|
||||
void
|
||||
keyboard(input_t &input, uint16_t modcode, bool release, uint8_t flags) {
|
||||
keyboard_update(input_t &input, uint16_t modcode, bool release, uint8_t flags) {
|
||||
INPUT i {};
|
||||
i.type = INPUT_KEYBOARD;
|
||||
auto &ki = i.ki;
|
||||
@ -919,7 +919,7 @@ namespace platf {
|
||||
* @param touch The touch event.
|
||||
*/
|
||||
void
|
||||
touch(client_input_t *input, const touch_port_t &touch_port, const touch_input_t &touch) {
|
||||
touch_update(client_input_t *input, const touch_port_t &touch_port, const touch_input_t &touch) {
|
||||
auto raw = (client_input_raw_t *) input;
|
||||
|
||||
// Bail if we're not running on an OS that supports virtual touch input
|
||||
@ -1050,7 +1050,7 @@ namespace platf {
|
||||
* @param pen The pen event.
|
||||
*/
|
||||
void
|
||||
pen(client_input_t *input, const touch_port_t &touch_port, const pen_input_t &pen) {
|
||||
pen_update(client_input_t *input, const touch_port_t &touch_port, const pen_input_t &pen) {
|
||||
auto raw = (client_input_raw_t *) input;
|
||||
|
||||
// Bail if we're not running on an OS that supports virtual pen input
|
||||
@ -1482,7 +1482,7 @@ namespace platf {
|
||||
* @param gamepad_state The gamepad button/axis state sent from the client.
|
||||
*/
|
||||
void
|
||||
gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) {
|
||||
gamepad_update(input_t &input, int nr, const gamepad_state_t &gamepad_state) {
|
||||
auto vigem = ((input_raw_t *) input.get())->vigem;
|
||||
|
||||
// If there is no gamepad support
|
||||
@ -1734,17 +1734,32 @@ namespace platf {
|
||||
delete input;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the supported gamepads for this platform backend.
|
||||
* @return Vector of gamepad type strings.
|
||||
*/
|
||||
std::vector<std::string_view> &
|
||||
supported_gamepads() {
|
||||
std::vector<supported_gamepad_t> &
|
||||
supported_gamepads(input_t *input) {
|
||||
bool enabled;
|
||||
if (input) {
|
||||
auto vigem = ((input_raw_t *) input)->vigem;
|
||||
enabled = vigem != nullptr;
|
||||
}
|
||||
else {
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
auto reason = enabled ? "" : "gamepads.vigem-not-available";
|
||||
|
||||
// ds4 == ps4
|
||||
static std::vector<std::string_view> gps {
|
||||
"auto"sv, "x360"sv, "ds4"sv, "ps4"sv
|
||||
static std::vector gps {
|
||||
supported_gamepad_t { "auto", true, reason },
|
||||
supported_gamepad_t { "x360", enabled, reason },
|
||||
supported_gamepad_t { "ds4", enabled, reason }
|
||||
};
|
||||
|
||||
for (auto &[name, is_enabled, reason_disabled] : gps) {
|
||||
if (!is_enabled) {
|
||||
BOOST_LOG(warning) << "Gamepad " << name << " is disabled due to " << reason_disabled;
|
||||
}
|
||||
}
|
||||
|
||||
return gps;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import PlatformLayout from '../../PlatformLayout.vue'
|
||||
|
||||
const props = defineProps([
|
||||
'platform',
|
||||
@ -22,15 +23,27 @@ const config = ref(props.config)
|
||||
</div>
|
||||
|
||||
<!-- Emulated Gamepad Type -->
|
||||
<div class="mb-3" v-if="config.controller === 'enabled' && platform === 'windows'">
|
||||
<div class="mb-3" v-if="config.controller === 'enabled' && platform !== 'macos'">
|
||||
<label for="gamepad" class="form-label">{{ $t('config.gamepad') }}</label>
|
||||
<select id="gamepad" class="form-select" v-model="config.gamepad">
|
||||
<option value="auto">{{ $t('_common.auto') }}</option>
|
||||
|
||||
<PlatformLayout :platform="platform">
|
||||
<template #linux>
|
||||
<option value="ds5">{{ $t("config.gamepad_ds5") }}</option>
|
||||
<option value="switch">{{ $t("config.gamepad_switch") }}</option>
|
||||
<option value="xone">{{ $t("config.gamepad_xone") }}</option>
|
||||
</template>
|
||||
|
||||
<template #windows>
|
||||
<option value="ds4">{{ $t('config.gamepad_ds4') }}</option>
|
||||
<option value="x360">{{ $t('config.gamepad_x360') }}</option>
|
||||
</template>
|
||||
</PlatformLayout>
|
||||
</select>
|
||||
<div class="form-text">{{ $t('config.gamepad_desc') }}</div>
|
||||
</div>
|
||||
|
||||
<div class="accordion" v-if="config.gamepad === 'ds4'">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
|
@ -169,8 +169,11 @@
|
||||
"gamepad_auto": "Automatic selection options",
|
||||
"gamepad_desc": "Choose which type of gamepad to emulate on the host",
|
||||
"gamepad_ds4": "DS4 (PS4)",
|
||||
"gamepad_ds5": "DS5 (PS5)",
|
||||
"gamepad_switch": "Nintendo Pro (Switch)",
|
||||
"gamepad_manual": "Manual DS4 options",
|
||||
"gamepad_x360": "X360 (Xbox 360)",
|
||||
"gamepad_xone": "XOne (Xbox One)",
|
||||
"global_prep_cmd": "Command Preparations",
|
||||
"global_prep_cmd_desc": "Configure a list of commands to be executed before or after running any application. If any of the specified preparation commands fail, the application launch process will be aborted.",
|
||||
"hevc_mode": "HEVC Support",
|
||||
|
@ -1 +1,2 @@
|
||||
KERNEL=="uinput", SUBSYSTEM=="misc", OPTIONS+="static_node=uinput", TAG+="uaccess"
|
||||
KERNEL=="uhid", TAG+="uaccess"
|
||||
|
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @file tests/test_mouse.cpp
|
||||
* @file tests/unit/test_mouse.cpp
|
||||
* @brief Test src/input.*.
|
||||
*/
|
||||
#include <src/input.h>
|
||||
@ -18,6 +18,9 @@ protected:
|
||||
// the alternative `platf::abs_mouse` method seem to work better during tests,
|
||||
// but I'm not sure about real work
|
||||
GTEST_SKIP_("MouseTest:: skipped for now. TODO Windows");
|
||||
#elif __linux__
|
||||
// TODO: Inputtino waiting https://github.com/games-on-whales/inputtino/issues/6 is resolved.
|
||||
GTEST_SKIP_("MouseTest:: skipped for now. TODO Inputtino");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
1
third-party/inputtino
vendored
Submodule
1
third-party/inputtino
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 2739465690c7bbd1a27cb4e285ff08f486a208e3
|
Loading…
x
Reference in New Issue
Block a user