From cadeff397cb06a5c1b6535119dc346e8801887aa Mon Sep 17 00:00:00 2001 From: Vithorio Polten Date: Mon, 27 May 2024 14:32:39 -0300 Subject: [PATCH] refactor: move server files, split input types chore: fix weird imports chore: fix sys tray import error chore: fix clang code style refactor: mouse, keyboard and touch now on their own namespaces fix: make platf_input a singleton refactor: better isolation lint: remove import and space lint: import order docs: refactor docs too lint: new line at the end build: glob linking input/server build: full src glob chore: rebase fixes chore: revert doc changes docs(src): apply new patterns from #2763 chore: fixes from code review --- cmake/compile_definitions/common.cmake | 59 +- cmake/targets/common.cmake | 4 +- src/audio.h | 1 + src/config.cpp | 4 +- src/entry_handler.cpp | 6 +- src/input.cpp | 1705 --------------------- src/input.h | 54 - src/input/common.cpp | 54 + src/input/common.h | 82 + src/input/gamepad.cpp | 421 +++++ src/input/gamepad.h | 162 ++ src/input/init.h | 15 + src/input/keyboard.cpp | 283 ++++ src/input/keyboard.h | 63 + src/input/mouse.cpp | 294 ++++ src/input/mouse.h | 155 ++ src/input/pen.cpp | 134 ++ src/input/pen.h | 43 + src/input/platform_input.cpp | 12 + src/input/platform_input.h | 27 + src/input/processor.cpp | 294 ++++ src/input/processor.h | 90 ++ src/input/touch.cpp | 132 ++ src/input/touch.h | 54 + src/main.cpp | 8 +- src/platform/linux/input/legacy_input.cpp | 2 +- src/platform/linux/publish.cpp | 4 +- src/platform/macos/input.cpp | 3 +- src/platform/macos/publish.cpp | 5 +- src/platform/windows/publish.cpp | 4 +- src/process.cpp | 2 +- src/process.h | 2 +- src/{ => server}/confighttp.cpp | 31 +- src/{ => server}/confighttp.h | 4 +- src/{ => server}/crypto.cpp | 25 +- src/{ => server}/crypto.h | 4 +- src/{ => server}/httpcommon.cpp | 27 +- src/{ => server}/httpcommon.h | 5 +- src/{ => server}/network.cpp | 10 +- src/{ => server}/network.h | 4 +- src/{ => server}/nvhttp.cpp | 32 +- src/{ => server}/nvhttp.h | 4 +- src/{ => server}/rtsp.cpp | 20 +- src/{ => server}/rtsp.h | 6 +- src/{ => server}/upnp.cpp | 22 +- src/{ => server}/upnp.h | 4 +- src/stream.cpp | 4 +- src/stream.h | 2 +- src/system_tray.cpp | 2 +- src/video.cpp | 2 +- src/video.h | 2 +- tests/unit/test_httpcommon.cpp | 2 +- tests/unit/test_mouse.cpp | 2 +- 53 files changed, 2455 insertions(+), 1937 deletions(-) delete mode 100644 src/input.cpp delete mode 100644 src/input.h create mode 100644 src/input/common.cpp create mode 100644 src/input/common.h create mode 100644 src/input/gamepad.cpp create mode 100644 src/input/gamepad.h create mode 100644 src/input/init.h create mode 100644 src/input/keyboard.cpp create mode 100644 src/input/keyboard.h create mode 100644 src/input/mouse.cpp create mode 100644 src/input/mouse.h create mode 100644 src/input/pen.cpp create mode 100644 src/input/pen.h create mode 100644 src/input/platform_input.cpp create mode 100644 src/input/platform_input.h create mode 100644 src/input/processor.cpp create mode 100644 src/input/processor.h create mode 100644 src/input/touch.cpp create mode 100644 src/input/touch.h rename src/{ => server}/confighttp.cpp (98%) rename src/{ => server}/confighttp.h (92%) rename src/{ => server}/crypto.cpp (97%) rename src/{ => server}/crypto.h (99%) rename src/{ => server}/httpcommon.cpp (95%) rename src/{ => server}/httpcommon.h (90%) rename src/{ => server}/network.cpp (97%) rename src/{ => server}/network.h (98%) rename src/{ => server}/nvhttp.cpp (98%) rename src/{ => server}/nvhttp.h (97%) rename src/{ => server}/rtsp.cpp (99%) rename src/{ => server}/rtsp.h (92%) rename src/{ => server}/upnp.cpp (97%) rename src/{ => server}/upnp.h (94%) diff --git a/cmake/compile_definitions/common.cmake b/cmake/compile_definitions/common.cmake index a2260595c9a..630d5adc7a4 100644 --- a/cmake/compile_definitions/common.cmake +++ b/cmake/compile_definitions/common.cmake @@ -55,66 +55,17 @@ list(APPEND PLATFORM_TARGET_FILES ${NVENC_SOURCES}) configure_file("${CMAKE_SOURCE_DIR}/src/version.h.in" version.h @ONLY) include_directories("${CMAKE_CURRENT_BINARY_DIR}") # required for importing version.h +file(GLOB_RECURSE COMMON_SOURCES + ${CMAKE_SOURCE_DIR}/src/**) +list(FILTER COMMON_SOURCES EXCLUDE REGEX "${CMAKE_SOURCE_DIR}/src/platform/.*") + set(SUNSHINE_TARGET_FILES "${CMAKE_SOURCE_DIR}/third-party/moonlight-common-c/src/Input.h" "${CMAKE_SOURCE_DIR}/third-party/moonlight-common-c/src/Rtsp.h" "${CMAKE_SOURCE_DIR}/third-party/moonlight-common-c/src/RtspParser.c" "${CMAKE_SOURCE_DIR}/third-party/moonlight-common-c/src/Video.h" "${CMAKE_SOURCE_DIR}/third-party/tray/src/tray.h" - "${CMAKE_SOURCE_DIR}/src/upnp.cpp" - "${CMAKE_SOURCE_DIR}/src/upnp.h" - "${CMAKE_SOURCE_DIR}/src/cbs.cpp" - "${CMAKE_SOURCE_DIR}/src/utility.h" - "${CMAKE_SOURCE_DIR}/src/uuid.h" - "${CMAKE_SOURCE_DIR}/src/config.h" - "${CMAKE_SOURCE_DIR}/src/config.cpp" - "${CMAKE_SOURCE_DIR}/src/entry_handler.cpp" - "${CMAKE_SOURCE_DIR}/src/entry_handler.h" - "${CMAKE_SOURCE_DIR}/src/file_handler.cpp" - "${CMAKE_SOURCE_DIR}/src/file_handler.h" - "${CMAKE_SOURCE_DIR}/src/globals.cpp" - "${CMAKE_SOURCE_DIR}/src/globals.h" - "${CMAKE_SOURCE_DIR}/src/logging.cpp" - "${CMAKE_SOURCE_DIR}/src/logging.h" - "${CMAKE_SOURCE_DIR}/src/main.cpp" - "${CMAKE_SOURCE_DIR}/src/main.h" - "${CMAKE_SOURCE_DIR}/src/crypto.cpp" - "${CMAKE_SOURCE_DIR}/src/crypto.h" - "${CMAKE_SOURCE_DIR}/src/nvhttp.cpp" - "${CMAKE_SOURCE_DIR}/src/nvhttp.h" - "${CMAKE_SOURCE_DIR}/src/httpcommon.cpp" - "${CMAKE_SOURCE_DIR}/src/httpcommon.h" - "${CMAKE_SOURCE_DIR}/src/confighttp.cpp" - "${CMAKE_SOURCE_DIR}/src/confighttp.h" - "${CMAKE_SOURCE_DIR}/src/rtsp.cpp" - "${CMAKE_SOURCE_DIR}/src/rtsp.h" - "${CMAKE_SOURCE_DIR}/src/stream.cpp" - "${CMAKE_SOURCE_DIR}/src/stream.h" - "${CMAKE_SOURCE_DIR}/src/video.cpp" - "${CMAKE_SOURCE_DIR}/src/video.h" - "${CMAKE_SOURCE_DIR}/src/video_colorspace.cpp" - "${CMAKE_SOURCE_DIR}/src/video_colorspace.h" - "${CMAKE_SOURCE_DIR}/src/input.cpp" - "${CMAKE_SOURCE_DIR}/src/input.h" - "${CMAKE_SOURCE_DIR}/src/audio.cpp" - "${CMAKE_SOURCE_DIR}/src/audio.h" - "${CMAKE_SOURCE_DIR}/src/platform/common.h" - "${CMAKE_SOURCE_DIR}/src/process.cpp" - "${CMAKE_SOURCE_DIR}/src/process.h" - "${CMAKE_SOURCE_DIR}/src/network.cpp" - "${CMAKE_SOURCE_DIR}/src/network.h" - "${CMAKE_SOURCE_DIR}/src/move_by_copy.h" - "${CMAKE_SOURCE_DIR}/src/system_tray.cpp" - "${CMAKE_SOURCE_DIR}/src/system_tray.h" - "${CMAKE_SOURCE_DIR}/src/task_pool.h" - "${CMAKE_SOURCE_DIR}/src/thread_pool.h" - "${CMAKE_SOURCE_DIR}/src/thread_safe.h" - "${CMAKE_SOURCE_DIR}/src/sync.h" - "${CMAKE_SOURCE_DIR}/src/round_robin.h" - "${CMAKE_SOURCE_DIR}/src/stat_trackers.h" - "${CMAKE_SOURCE_DIR}/src/stat_trackers.cpp" - "${CMAKE_SOURCE_DIR}/src/rswrapper.h" - "${CMAKE_SOURCE_DIR}/src/rswrapper.c" + ${COMMON_SOURCES} ${PLATFORM_TARGET_FILES}) if(NOT SUNSHINE_ASSETS_DIR_DEF) diff --git a/cmake/targets/common.cmake b/cmake/targets/common.cmake index b086bdd73a4..a2fba0316f1 100644 --- a/cmake/targets/common.cmake +++ b/cmake/targets/common.cmake @@ -80,7 +80,7 @@ else() endif() # src/upnp -set_source_files_properties("${CMAKE_SOURCE_DIR}/src/upnp.cpp" +set_source_files_properties("${CMAKE_SOURCE_DIR}/src/server/upnp.cpp" DIRECTORY "${CMAKE_SOURCE_DIR}" "${TEST_DIR}" PROPERTIES COMPILE_FLAGS -Wno-pedantic) @@ -106,7 +106,7 @@ set_source_files_properties("${CMAKE_SOURCE_DIR}/third-party/ViGEmClient/src/ViG string(TOUPPER "x${CMAKE_BUILD_TYPE}" BUILD_TYPE) if("${BUILD_TYPE}" STREQUAL "XDEBUG") if(WIN32) - set_source_files_properties("${CMAKE_SOURCE_DIR}/src/nvhttp.cpp" + set_source_files_properties("${CMAKE_SOURCE_DIR}/src/server/nvhttp.cpp" DIRECTORY "${CMAKE_SOURCE_DIR}" "${CMAKE_SOURCE_DIR}/tests" PROPERTIES COMPILE_FLAGS -O2) endif() diff --git a/src/audio.h b/src/audio.h index 6d04d242b4c..572a62d7151 100644 --- a/src/audio.h +++ b/src/audio.h @@ -6,6 +6,7 @@ #include "thread_safe.h" #include "utility.h" + namespace audio { enum stream_config_e : int { STEREO, ///< Stereo diff --git a/src/config.cpp b/src/config.cpp index e8accb15a57..95b24da313b 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -20,8 +20,8 @@ #include "entry_handler.h" #include "file_handler.h" #include "logging.h" -#include "nvhttp.h" -#include "rtsp.h" +#include "server/nvhttp.h" +#include "server/rtsp.h" #include "utility.h" #include "platform/common.h" diff --git a/src/entry_handler.cpp b/src/entry_handler.cpp index fb1ece2ca39..dcd89409139 100644 --- a/src/entry_handler.cpp +++ b/src/entry_handler.cpp @@ -9,13 +9,13 @@ // local includes #include "config.h" -#include "confighttp.h" #include "entry_handler.h" #include "globals.h" -#include "httpcommon.h" #include "logging.h" -#include "network.h" #include "platform/common.h" +#include "server/confighttp.h" +#include "server/httpcommon.h" +#include "server/network.h" #include "version.h" extern "C" { diff --git a/src/input.cpp b/src/input.cpp deleted file mode 100644 index bf275467d87..00000000000 --- a/src/input.cpp +++ /dev/null @@ -1,1705 +0,0 @@ -/** - * @file src/input.cpp - * @brief Definitions for gamepad, keyboard, and mouse input handling. - */ -// define uint32_t for -#include -extern "C" { -#include -#include -} - -#include -#include -#include -#include -#include -#include - -#include "config.h" -#include "globals.h" -#include "input.h" -#include "logging.h" -#include "platform/common.h" -#include "thread_pool.h" -#include "utility.h" - -#include - -// Win32 WHEEL_DELTA constant -#ifndef WHEEL_DELTA - #define WHEEL_DELTA 120 -#endif - -using namespace std::literals; -namespace input { - - constexpr auto MAX_GAMEPADS = std::min((std::size_t) platf::MAX_GAMEPADS, sizeof(std::int16_t) * 8); -#define DISABLE_LEFT_BUTTON_DELAY ((thread_pool_util::ThreadPool::task_id_t) 0x01) -#define ENABLE_LEFT_BUTTON_DELAY nullptr - - constexpr auto VKEY_SHIFT = 0x10; - constexpr auto VKEY_LSHIFT = 0xA0; - constexpr auto VKEY_RSHIFT = 0xA1; - constexpr auto VKEY_CONTROL = 0x11; - constexpr auto VKEY_LCONTROL = 0xA2; - constexpr auto VKEY_RCONTROL = 0xA3; - constexpr auto VKEY_MENU = 0x12; - constexpr auto VKEY_LMENU = 0xA4; - constexpr auto VKEY_RMENU = 0xA5; - - enum class button_state_e { - NONE, ///< No button state - DOWN, ///< Button is down - UP ///< Button is up - }; - - template - int - alloc_id(std::bitset &gamepad_mask) { - for (int x = 0; x < gamepad_mask.size(); ++x) { - if (!gamepad_mask[x]) { - gamepad_mask[x] = true; - return x; - } - } - - return -1; - } - - template - void - free_id(std::bitset &gamepad_mask, int id) { - gamepad_mask[id] = false; - } - - typedef uint32_t key_press_id_t; - key_press_id_t - make_kpid(uint16_t vk, uint8_t flags) { - return (key_press_id_t) vk << 8 | flags; - } - uint16_t - vk_from_kpid(key_press_id_t kpid) { - return kpid >> 8; - } - uint8_t - flags_from_kpid(key_press_id_t kpid) { - return kpid & 0xFF; - } - - /** - * @brief Convert a little-endian netfloat to a native endianness float. - * @param f Netfloat value. - * @return The native endianness float value. - */ - float - from_netfloat(netfloat f) { - return boost::endian::endian_load(f); - } - - /** - * @brief Convert a little-endian netfloat to a native endianness float and clamps it. - * @param f Netfloat value. - * @param min The minimium value for clamping. - * @param max The maximum value for clamping. - * @return Clamped native endianess float value. - */ - float - from_clamped_netfloat(netfloat f, float min, float max) { - return std::clamp(from_netfloat(f), min, max); - } - - static task_pool_util::TaskPool::task_id_t key_press_repeat_id {}; - static std::unordered_map key_press {}; - static std::array mouse_press {}; - - static platf::input_t platf_input; - static std::bitset gamepadMask {}; - - void - free_gamepad(platf::input_t &platf_input, int id) { - platf::gamepad_update(platf_input, id, platf::gamepad_state_t {}); - platf::free_gamepad(platf_input, id); - - free_id(gamepadMask, id); - } - struct gamepad_t { - gamepad_t(): - gamepad_state {}, back_timeout_id {}, id { -1 }, back_button_state { button_state_e::NONE } {} - ~gamepad_t() { - if (id >= 0) { - task_pool.push([id = this->id]() { - free_gamepad(platf_input, id); - }); - } - } - - platf::gamepad_state_t gamepad_state; - - thread_pool_util::ThreadPool::task_id_t back_timeout_id; - - int id; - - // When emulating the HOME button, we may need to artificially release the back button. - // Afterwards, the gamepad state on sunshine won't match the state on Moonlight. - // To prevent Sunshine from sending erroneous input data to the active application, - // Sunshine forces the button to be in a specific state until the gamepad state matches that of - // Moonlight once more. - button_state_e back_button_state; - }; - - struct input_t { - enum shortkey_e { - CTRL = 0x1, ///< Control key - ALT = 0x2, ///< Alt key - SHIFT = 0x4, ///< Shift key - SHORTCUT = CTRL | ALT | SHIFT ///< Shortcut combination - }; - - input_t( - safe::mail_raw_t::event_t touch_port_event, - platf::feedback_queue_t feedback_queue): - shortcutFlags {}, - gamepads(MAX_GAMEPADS), - client_context { platf::allocate_client_input_context(platf_input) }, - touch_port_event { std::move(touch_port_event) }, - feedback_queue { std::move(feedback_queue) }, - mouse_left_button_timeout {}, - touch_port { { 0, 0, 0, 0 }, 0, 0, 1.0f }, - accumulated_vscroll_delta {}, - accumulated_hscroll_delta {} {} - - // Keep track of alt+ctrl+shift key combo - int shortcutFlags; - - std::vector gamepads; - std::unique_ptr client_context; - - safe::mail_raw_t::event_t touch_port_event; - platf::feedback_queue_t feedback_queue; - - std::list> input_queue; - std::mutex input_queue_lock; - - thread_pool_util::ThreadPool::task_id_t mouse_left_button_timeout; - - input::touch_port_t touch_port; - - int32_t accumulated_vscroll_delta; - int32_t accumulated_hscroll_delta; - }; - - /** - * @brief Apply shortcut based on VKEY - * @param keyCode The VKEY code - * @return 0 if no shortcut applied, > 0 if shortcut applied. - */ - inline int - apply_shortcut(short keyCode) { - constexpr auto VK_F1 = 0x70; - constexpr auto VK_F13 = 0x7C; - - BOOST_LOG(debug) << "Apply Shortcut: 0x"sv << util::hex((std::uint8_t) keyCode).to_string_view(); - - if (keyCode >= VK_F1 && keyCode <= VK_F13) { - mail::man->event(mail::switch_display)->raise(keyCode - VK_F1); - return 1; - } - - switch (keyCode) { - case 0x4E /* VKEY_N */: - display_cursor = !display_cursor; - return 1; - } - - return 0; - } - - void - print(PNV_REL_MOUSE_MOVE_PACKET packet) { - BOOST_LOG(debug) - << "--begin relative mouse move packet--"sv << std::endl - << "deltaX ["sv << util::endian::big(packet->deltaX) << ']' << std::endl - << "deltaY ["sv << util::endian::big(packet->deltaY) << ']' << std::endl - << "--end relative mouse move packet--"sv; - } - - void - print(PNV_ABS_MOUSE_MOVE_PACKET packet) { - BOOST_LOG(debug) - << "--begin absolute mouse move packet--"sv << std::endl - << "x ["sv << util::endian::big(packet->x) << ']' << std::endl - << "y ["sv << util::endian::big(packet->y) << ']' << std::endl - << "width ["sv << util::endian::big(packet->width) << ']' << std::endl - << "height ["sv << util::endian::big(packet->height) << ']' << std::endl - << "--end absolute mouse move packet--"sv; - } - - void - print(PNV_MOUSE_BUTTON_PACKET packet) { - BOOST_LOG(debug) - << "--begin mouse button packet--"sv << std::endl - << "action ["sv << util::hex(packet->header.magic).to_string_view() << ']' << std::endl - << "button ["sv << util::hex(packet->button).to_string_view() << ']' << std::endl - << "--end mouse button packet--"sv; - } - - void - print(PNV_SCROLL_PACKET packet) { - BOOST_LOG(debug) - << "--begin mouse scroll packet--"sv << std::endl - << "scrollAmt1 ["sv << util::endian::big(packet->scrollAmt1) << ']' << std::endl - << "--end mouse scroll packet--"sv; - } - - void - print(PSS_HSCROLL_PACKET packet) { - BOOST_LOG(debug) - << "--begin mouse hscroll packet--"sv << std::endl - << "scrollAmount ["sv << util::endian::big(packet->scrollAmount) << ']' << std::endl - << "--end mouse hscroll packet--"sv; - } - - void - print(PNV_KEYBOARD_PACKET packet) { - BOOST_LOG(debug) - << "--begin keyboard packet--"sv << std::endl - << "keyAction ["sv << util::hex(packet->header.magic).to_string_view() << ']' << std::endl - << "keyCode ["sv << util::hex(packet->keyCode).to_string_view() << ']' << std::endl - << "modifiers ["sv << util::hex(packet->modifiers).to_string_view() << ']' << std::endl - << "flags ["sv << util::hex(packet->flags).to_string_view() << ']' << std::endl - << "--end keyboard packet--"sv; - } - - void - print(PNV_UNICODE_PACKET packet) { - std::string text(packet->text, util::endian::big(packet->header.size) - sizeof(packet->header.magic)); - BOOST_LOG(debug) - << "--begin unicode packet--"sv << std::endl - << "text ["sv << text << ']' << std::endl - << "--end unicode packet--"sv; - } - - void - print(PNV_MULTI_CONTROLLER_PACKET packet) { - // Moonlight spams controller packet even when not necessary - BOOST_LOG(verbose) - << "--begin controller packet--"sv << std::endl - << "controllerNumber ["sv << packet->controllerNumber << ']' << std::endl - << "activeGamepadMask ["sv << util::hex(packet->activeGamepadMask).to_string_view() << ']' << std::endl - << "buttonFlags ["sv << util::hex((uint32_t) packet->buttonFlags | (packet->buttonFlags2 << 16)).to_string_view() << ']' << std::endl - << "leftTrigger ["sv << util::hex(packet->leftTrigger).to_string_view() << ']' << std::endl - << "rightTrigger ["sv << util::hex(packet->rightTrigger).to_string_view() << ']' << std::endl - << "leftStickX ["sv << packet->leftStickX << ']' << std::endl - << "leftStickY ["sv << packet->leftStickY << ']' << std::endl - << "rightStickX ["sv << packet->rightStickX << ']' << std::endl - << "rightStickY ["sv << packet->rightStickY << ']' << std::endl - << "--end controller packet--"sv; - } - - /** - * @brief Prints a touch packet. - * @param packet The touch packet. - */ - void - print(PSS_TOUCH_PACKET packet) { - BOOST_LOG(debug) - << "--begin touch packet--"sv << std::endl - << "eventType ["sv << util::hex(packet->eventType).to_string_view() << ']' << std::endl - << "pointerId ["sv << util::hex(packet->pointerId).to_string_view() << ']' << std::endl - << "x ["sv << from_netfloat(packet->x) << ']' << std::endl - << "y ["sv << from_netfloat(packet->y) << ']' << std::endl - << "pressureOrDistance ["sv << from_netfloat(packet->pressureOrDistance) << ']' << std::endl - << "contactAreaMajor ["sv << from_netfloat(packet->contactAreaMajor) << ']' << std::endl - << "contactAreaMinor ["sv << from_netfloat(packet->contactAreaMinor) << ']' << std::endl - << "rotation ["sv << (uint32_t) packet->rotation << ']' << std::endl - << "--end touch packet--"sv; - } - - /** - * @brief Prints a pen packet. - * @param packet The pen packet. - */ - void - print(PSS_PEN_PACKET packet) { - BOOST_LOG(debug) - << "--begin pen packet--"sv << std::endl - << "eventType ["sv << util::hex(packet->eventType).to_string_view() << ']' << std::endl - << "toolType ["sv << util::hex(packet->toolType).to_string_view() << ']' << std::endl - << "penButtons ["sv << util::hex(packet->penButtons).to_string_view() << ']' << std::endl - << "x ["sv << from_netfloat(packet->x) << ']' << std::endl - << "y ["sv << from_netfloat(packet->y) << ']' << std::endl - << "pressureOrDistance ["sv << from_netfloat(packet->pressureOrDistance) << ']' << std::endl - << "contactAreaMajor ["sv << from_netfloat(packet->contactAreaMajor) << ']' << std::endl - << "contactAreaMinor ["sv << from_netfloat(packet->contactAreaMinor) << ']' << std::endl - << "rotation ["sv << (uint32_t) packet->rotation << ']' << std::endl - << "tilt ["sv << (uint32_t) packet->tilt << ']' << std::endl - << "--end pen packet--"sv; - } - - /** - * @brief Prints a controller arrival packet. - * @param packet The controller arrival packet. - */ - void - print(PSS_CONTROLLER_ARRIVAL_PACKET packet) { - BOOST_LOG(debug) - << "--begin controller arrival packet--"sv << std::endl - << "controllerNumber ["sv << (uint32_t) packet->controllerNumber << ']' << std::endl - << "type ["sv << util::hex(packet->type).to_string_view() << ']' << std::endl - << "capabilities ["sv << util::hex(packet->capabilities).to_string_view() << ']' << std::endl - << "supportedButtonFlags ["sv << util::hex(packet->supportedButtonFlags).to_string_view() << ']' << std::endl - << "--end controller arrival packet--"sv; - } - - /** - * @brief Prints a controller touch packet. - * @param packet The controller touch packet. - */ - void - print(PSS_CONTROLLER_TOUCH_PACKET packet) { - BOOST_LOG(debug) - << "--begin controller touch packet--"sv << std::endl - << "controllerNumber ["sv << (uint32_t) packet->controllerNumber << ']' << std::endl - << "eventType ["sv << util::hex(packet->eventType).to_string_view() << ']' << std::endl - << "pointerId ["sv << util::hex(packet->pointerId).to_string_view() << ']' << std::endl - << "x ["sv << from_netfloat(packet->x) << ']' << std::endl - << "y ["sv << from_netfloat(packet->y) << ']' << std::endl - << "pressure ["sv << from_netfloat(packet->pressure) << ']' << std::endl - << "--end controller touch packet--"sv; - } - - /** - * @brief Prints a controller motion packet. - * @param packet The controller motion packet. - */ - void - print(PSS_CONTROLLER_MOTION_PACKET packet) { - BOOST_LOG(verbose) - << "--begin controller motion packet--"sv << std::endl - << "controllerNumber ["sv << util::hex(packet->controllerNumber).to_string_view() << ']' << std::endl - << "motionType ["sv << util::hex(packet->motionType).to_string_view() << ']' << std::endl - << "x ["sv << from_netfloat(packet->x) << ']' << std::endl - << "y ["sv << from_netfloat(packet->y) << ']' << std::endl - << "z ["sv << from_netfloat(packet->z) << ']' << std::endl - << "--end controller motion packet--"sv; - } - - /** - * @brief Prints a controller battery packet. - * @param packet The controller battery packet. - */ - void - print(PSS_CONTROLLER_BATTERY_PACKET packet) { - BOOST_LOG(verbose) - << "--begin controller battery packet--"sv << std::endl - << "controllerNumber ["sv << util::hex(packet->controllerNumber).to_string_view() << ']' << std::endl - << "batteryState ["sv << util::hex(packet->batteryState).to_string_view() << ']' << std::endl - << "batteryPercentage ["sv << util::hex(packet->batteryPercentage).to_string_view() << ']' << std::endl - << "--end controller battery packet--"sv; - } - - void - print(void *payload) { - auto header = (PNV_INPUT_HEADER) payload; - - switch (util::endian::little(header->magic)) { - case MOUSE_MOVE_REL_MAGIC_GEN5: - print((PNV_REL_MOUSE_MOVE_PACKET) payload); - break; - case MOUSE_MOVE_ABS_MAGIC: - print((PNV_ABS_MOUSE_MOVE_PACKET) payload); - break; - case MOUSE_BUTTON_DOWN_EVENT_MAGIC_GEN5: - case MOUSE_BUTTON_UP_EVENT_MAGIC_GEN5: - print((PNV_MOUSE_BUTTON_PACKET) payload); - break; - case SCROLL_MAGIC_GEN5: - print((PNV_SCROLL_PACKET) payload); - break; - case SS_HSCROLL_MAGIC: - print((PSS_HSCROLL_PACKET) payload); - break; - case KEY_DOWN_EVENT_MAGIC: - case KEY_UP_EVENT_MAGIC: - print((PNV_KEYBOARD_PACKET) payload); - break; - case UTF8_TEXT_EVENT_MAGIC: - print((PNV_UNICODE_PACKET) payload); - break; - case MULTI_CONTROLLER_MAGIC_GEN5: - print((PNV_MULTI_CONTROLLER_PACKET) payload); - break; - case SS_TOUCH_MAGIC: - print((PSS_TOUCH_PACKET) payload); - break; - case SS_PEN_MAGIC: - print((PSS_PEN_PACKET) payload); - break; - case SS_CONTROLLER_ARRIVAL_MAGIC: - print((PSS_CONTROLLER_ARRIVAL_PACKET) payload); - break; - case SS_CONTROLLER_TOUCH_MAGIC: - print((PSS_CONTROLLER_TOUCH_PACKET) payload); - break; - case SS_CONTROLLER_MOTION_MAGIC: - print((PSS_CONTROLLER_MOTION_PACKET) payload); - break; - case SS_CONTROLLER_BATTERY_MAGIC: - print((PSS_CONTROLLER_BATTERY_PACKET) payload); - break; - } - } - - void - passthrough(std::shared_ptr &input, PNV_REL_MOUSE_MOVE_PACKET packet) { - if (!config::input.mouse) { - return; - } - - input->mouse_left_button_timeout = DISABLE_LEFT_BUTTON_DELAY; - platf::move_mouse(platf_input, util::endian::big(packet->deltaX), util::endian::big(packet->deltaY)); - } - - /** - * @brief Converts client coordinates on the specified surface into screen coordinates. - * @param input The input context. - * @param val The cartesian coordinate pair to convert. - * @param size The size of the client's surface containing the value. - * @return The host-relative coordinate pair if a touchport is available. - */ - std::optional> - client_to_touchport(std::shared_ptr &input, const std::pair &val, const std::pair &size) { - auto &touch_port_event = input->touch_port_event; - auto &touch_port = input->touch_port; - if (touch_port_event->peek()) { - touch_port = *touch_port_event->pop(); - } - if (!touch_port) { - BOOST_LOG(verbose) << "Ignoring early absolute input without a touch port"sv; - return std::nullopt; - } - - auto scalarX = touch_port.width / size.first; - auto scalarY = touch_port.height / size.second; - - float x = std::clamp(val.first, 0.0f, size.first) * scalarX; - float y = std::clamp(val.second, 0.0f, size.second) * scalarY; - - auto offsetX = touch_port.client_offsetX; - auto offsetY = touch_port.client_offsetY; - - x = std::clamp(x, offsetX, (size.first * scalarX) - offsetX); - y = std::clamp(y, offsetY, (size.second * scalarY) - offsetY); - - return std::pair { (x - offsetX) * touch_port.scalar_inv, (y - offsetY) * touch_port.scalar_inv }; - } - - /** - * @brief Multiply a polar coordinate pair by a cartesian scaling factor. - * @param r The radial coordinate. - * @param angle The angular coordinate (radians). - * @param scalar The scalar cartesian coordinate pair. - * @return The scaled radial coordinate. - */ - float - multiply_polar_by_cartesian_scalar(float r, float angle, const std::pair &scalar) { - // Convert polar to cartesian coordinates - float x = r * std::cos(angle); - float y = r * std::sin(angle); - - // Scale the values - x *= scalar.first; - y *= scalar.second; - - // Convert the result back to a polar radial coordinate - return std::sqrt(std::pow(x, 2) + std::pow(y, 2)); - } - - std::pair - scale_client_contact_area(const std::pair &val, uint16_t rotation, const std::pair &scalar) { - // If the rotation is unknown, we'll just scale both axes equally by using - // a 45-degree angle for our scaling calculations - float angle = rotation == LI_ROT_UNKNOWN ? (M_PI / 4) : (rotation * (M_PI / 180)); - - // If we have a major but not a minor axis, treat the touch as circular - float major = val.first; - float minor = val.second != 0.0f ? val.second : val.first; - - // The minor axis is perpendicular to major axis so the angle must be rotated by 90 degrees - return { multiply_polar_by_cartesian_scalar(major, angle, scalar), multiply_polar_by_cartesian_scalar(minor, angle + (M_PI / 2), scalar) }; - } - - void - passthrough(std::shared_ptr &input, PNV_ABS_MOUSE_MOVE_PACKET packet) { - if (!config::input.mouse) { - return; - } - - if (input->mouse_left_button_timeout == DISABLE_LEFT_BUTTON_DELAY) { - input->mouse_left_button_timeout = ENABLE_LEFT_BUTTON_DELAY; - } - - float x = util::endian::big(packet->x); - float y = util::endian::big(packet->y); - - // Prevent divide by zero - // Don't expect it to happen, but just in case - if (!packet->width || !packet->height) { - BOOST_LOG(warning) << "Moonlight passed invalid dimensions"sv; - - return; - } - - auto width = (float) util::endian::big(packet->width); - auto height = (float) util::endian::big(packet->height); - - auto tpcoords = client_to_touchport(input, { x, y }, { width, height }); - if (!tpcoords) { - return; - } - - auto &touch_port = input->touch_port; - platf::touch_port_t abs_port { - touch_port.offset_x, touch_port.offset_y, - touch_port.env_width, touch_port.env_height - }; - - platf::abs_mouse(platf_input, abs_port, tpcoords->first, tpcoords->second); - } - - void - passthrough(std::shared_ptr &input, PNV_MOUSE_BUTTON_PACKET packet) { - if (!config::input.mouse) { - return; - } - - auto release = util::endian::little(packet->header.magic) == MOUSE_BUTTON_UP_EVENT_MAGIC_GEN5; - auto button = util::endian::big(packet->button); - if (button > 0 && button < mouse_press.size()) { - if (mouse_press[button] != release) { - // button state is already what we want - return; - } - - mouse_press[button] = !release; - } - /** - * When Moonlight sends mouse input through absolute coordinates, - * it's possible that BUTTON_RIGHT is pressed down immediately after releasing BUTTON_LEFT. - * As a result, Sunshine will left-click on hyperlinks in the browser before right-clicking - * - * This can be solved by delaying BUTTON_LEFT, however, any delay on input is undesirable during gaming - * As a compromise, Sunshine will only put delays on BUTTON_LEFT when - * absolute mouse coordinates have been sent. - * - * Try to make sure BUTTON_RIGHT gets called before BUTTON_LEFT is released. - * - * input->mouse_left_button_timeout can only be nullptr - * when the last mouse coordinates were absolute - */ - if (button == BUTTON_LEFT && release && !input->mouse_left_button_timeout) { - auto f = [=]() { - auto left_released = mouse_press[BUTTON_LEFT]; - if (left_released) { - // Already released left button - return; - } - platf::button_mouse(platf_input, BUTTON_LEFT, release); - - mouse_press[BUTTON_LEFT] = false; - input->mouse_left_button_timeout = nullptr; - }; - - input->mouse_left_button_timeout = task_pool.pushDelayed(std::move(f), 10ms).task_id; - - return; - } - if ( - button == BUTTON_RIGHT && !release && - input->mouse_left_button_timeout > DISABLE_LEFT_BUTTON_DELAY) { - platf::button_mouse(platf_input, BUTTON_RIGHT, false); - platf::button_mouse(platf_input, BUTTON_RIGHT, true); - - mouse_press[BUTTON_RIGHT] = false; - - return; - } - - platf::button_mouse(platf_input, button, release); - } - - short - map_keycode(short keycode) { - auto it = config::input.keybindings.find(keycode); - if (it != std::end(config::input.keybindings)) { - return it->second; - } - - return keycode; - } - - /** - * @brief Update flags for keyboard shortcut combo's - */ - inline void - update_shortcutFlags(int *flags, short keyCode, bool release) { - switch (keyCode) { - case VKEY_SHIFT: - case VKEY_LSHIFT: - case VKEY_RSHIFT: - if (release) { - *flags &= ~input_t::SHIFT; - } - else { - *flags |= input_t::SHIFT; - } - break; - case VKEY_CONTROL: - case VKEY_LCONTROL: - case VKEY_RCONTROL: - if (release) { - *flags &= ~input_t::CTRL; - } - else { - *flags |= input_t::CTRL; - } - break; - case VKEY_MENU: - case VKEY_LMENU: - case VKEY_RMENU: - if (release) { - *flags &= ~input_t::ALT; - } - else { - *flags |= input_t::ALT; - } - break; - } - } - - bool - is_modifier(uint16_t keyCode) { - switch (keyCode) { - case VKEY_SHIFT: - case VKEY_LSHIFT: - case VKEY_RSHIFT: - case VKEY_CONTROL: - case VKEY_LCONTROL: - case VKEY_RCONTROL: - case VKEY_MENU: - case VKEY_LMENU: - case VKEY_RMENU: - return true; - default: - return false; - } - } - - void - send_key_and_modifiers(uint16_t key_code, bool release, uint8_t flags, uint8_t synthetic_modifiers) { - if (!release) { - // Press any synthetic modifiers required for this key - if (synthetic_modifiers & MODIFIER_SHIFT) { - platf::keyboard_update(platf_input, VKEY_SHIFT, false, flags); - } - if (synthetic_modifiers & MODIFIER_CTRL) { - platf::keyboard_update(platf_input, VKEY_CONTROL, false, flags); - } - if (synthetic_modifiers & MODIFIER_ALT) { - platf::keyboard_update(platf_input, VKEY_MENU, false, 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_update(platf_input, VKEY_SHIFT, true, flags); - } - if (synthetic_modifiers & MODIFIER_CTRL) { - platf::keyboard_update(platf_input, VKEY_CONTROL, true, flags); - } - if (synthetic_modifiers & MODIFIER_ALT) { - platf::keyboard_update(platf_input, VKEY_MENU, true, flags); - } - } - } - - void - repeat_key(uint16_t key_code, uint8_t flags, uint8_t synthetic_modifiers) { - // If key no longer pressed, stop repeating - if (!key_press[make_kpid(key_code, flags)]) { - key_press_repeat_id = nullptr; - return; - } - - send_key_and_modifiers(key_code, false, flags, synthetic_modifiers); - - key_press_repeat_id = task_pool.pushDelayed(repeat_key, config::input.key_repeat_period, key_code, flags, synthetic_modifiers).task_id; - } - - void - passthrough(std::shared_ptr &input, PNV_KEYBOARD_PACKET packet) { - if (!config::input.keyboard) { - return; - } - - auto release = util::endian::little(packet->header.magic) == KEY_UP_EVENT_MAGIC; - auto keyCode = packet->keyCode & 0x00FF; - - // Set synthetic modifier flags if the keyboard packet is requesting modifier - // keys that are not current pressed. - uint8_t synthetic_modifiers = 0; - if (!release && !is_modifier(keyCode)) { - if (!(input->shortcutFlags & input_t::SHIFT) && (packet->modifiers & MODIFIER_SHIFT)) { - synthetic_modifiers |= MODIFIER_SHIFT; - } - if (!(input->shortcutFlags & input_t::CTRL) && (packet->modifiers & MODIFIER_CTRL)) { - synthetic_modifiers |= MODIFIER_CTRL; - } - if (!(input->shortcutFlags & input_t::ALT) && (packet->modifiers & MODIFIER_ALT)) { - synthetic_modifiers |= MODIFIER_ALT; - } - } - - auto &pressed = key_press[make_kpid(keyCode, packet->flags)]; - if (!pressed) { - if (!release) { - // A new key has been pressed down, we need to check for key combo's - // If a key-combo has been pressed down, don't pass it through - if (input->shortcutFlags == input_t::SHORTCUT && apply_shortcut(keyCode) > 0) { - return; - } - - if (key_press_repeat_id) { - task_pool.cancel(key_press_repeat_id); - } - - if (config::input.key_repeat_delay.count() > 0) { - key_press_repeat_id = task_pool.pushDelayed(repeat_key, config::input.key_repeat_delay, keyCode, packet->flags, synthetic_modifiers).task_id; - } - } - else { - // Already released - return; - } - } - else if (!release) { - // Already pressed down key - return; - } - - pressed = !release; - - send_key_and_modifiers(keyCode, release, packet->flags, synthetic_modifiers); - - update_shortcutFlags(&input->shortcutFlags, map_keycode(keyCode), release); - } - - /** - * @brief Called to pass a vertical scroll message the platform backend. - * @param input The input context pointer. - * @param packet The scroll packet. - */ - void - passthrough(std::shared_ptr &input, PNV_SCROLL_PACKET packet) { - if (!config::input.mouse) { - return; - } - - if (config::input.high_resolution_scrolling) { - platf::scroll(platf_input, util::endian::big(packet->scrollAmt1)); - } - else { - input->accumulated_vscroll_delta += util::endian::big(packet->scrollAmt1); - auto full_ticks = input->accumulated_vscroll_delta / WHEEL_DELTA; - if (full_ticks) { - // Send any full ticks that have accumulated and store the rest - platf::scroll(platf_input, full_ticks * WHEEL_DELTA); - input->accumulated_vscroll_delta -= full_ticks * WHEEL_DELTA; - } - } - } - - /** - * @brief Called to pass a horizontal scroll message the platform backend. - * @param input The input context pointer. - * @param packet The scroll packet. - */ - void - passthrough(std::shared_ptr &input, PSS_HSCROLL_PACKET packet) { - if (!config::input.mouse) { - return; - } - - if (config::input.high_resolution_scrolling) { - platf::hscroll(platf_input, util::endian::big(packet->scrollAmount)); - } - else { - input->accumulated_hscroll_delta += util::endian::big(packet->scrollAmount); - auto full_ticks = input->accumulated_hscroll_delta / WHEEL_DELTA; - if (full_ticks) { - // Send any full ticks that have accumulated and store the rest - platf::hscroll(platf_input, full_ticks * WHEEL_DELTA); - input->accumulated_hscroll_delta -= full_ticks * WHEEL_DELTA; - } - } - } - - void - passthrough(PNV_UNICODE_PACKET packet) { - if (!config::input.keyboard) { - return; - } - - auto size = util::endian::big(packet->header.size) - sizeof(packet->header.magic); - platf::unicode(platf_input, packet->text, size); - } - - /** - * @brief Called to pass a controller arrival message to the platform backend. - * @param input The input context pointer. - * @param packet The controller arrival packet. - */ - void - passthrough(std::shared_ptr &input, PSS_CONTROLLER_ARRIVAL_PACKET packet) { - if (!config::input.controller) { - return; - } - - if (packet->controllerNumber < 0 || packet->controllerNumber >= input->gamepads.size()) { - BOOST_LOG(warning) << "ControllerNumber out of range ["sv << packet->controllerNumber << ']'; - return; - } - - if (input->gamepads[packet->controllerNumber].id >= 0) { - BOOST_LOG(warning) << "ControllerNumber already allocated ["sv << packet->controllerNumber << ']'; - return; - } - - platf::gamepad_arrival_t arrival { - packet->type, - util::endian::little(packet->capabilities), - util::endian::little(packet->supportedButtonFlags), - }; - - auto id = alloc_id(gamepadMask); - if (id < 0) { - return; - } - - // Allocate a new gamepad - if (platf::alloc_gamepad(platf_input, { id, packet->controllerNumber }, arrival, input->feedback_queue)) { - free_id(gamepadMask, id); - return; - } - - input->gamepads[packet->controllerNumber].id = id; - } - - /** - * @brief Called to pass a touch message to the platform backend. - * @param input The input context pointer. - * @param packet The touch packet. - */ - void - passthrough(std::shared_ptr &input, PSS_TOUCH_PACKET packet) { - if (!config::input.mouse) { - return; - } - - // Convert the client normalized coordinates to touchport coordinates - auto coords = client_to_touchport(input, - { from_clamped_netfloat(packet->x, 0.0f, 1.0f) * 65535.f, - from_clamped_netfloat(packet->y, 0.0f, 1.0f) * 65535.f }, - { 65535.f, 65535.f }); - if (!coords) { - return; - } - - auto &touch_port = input->touch_port; - platf::touch_port_t abs_port { - touch_port.offset_x, touch_port.offset_y, - touch_port.env_width, touch_port.env_height - }; - - // Renormalize the coordinates - coords->first /= abs_port.width; - coords->second /= abs_port.height; - - // Normalize rotation value to 0-359 degree range - auto rotation = util::endian::little(packet->rotation); - if (rotation != LI_ROT_UNKNOWN) { - rotation %= 360; - } - - // Normalize the contact area based on the touchport - auto contact_area = scale_client_contact_area( - { from_clamped_netfloat(packet->contactAreaMajor, 0.0f, 1.0f) * 65535.f, - from_clamped_netfloat(packet->contactAreaMinor, 0.0f, 1.0f) * 65535.f }, - rotation, - { abs_port.width / 65535.f, abs_port.height / 65535.f }); - - platf::touch_input_t touch { - packet->eventType, - rotation, - util::endian::little(packet->pointerId), - coords->first, - coords->second, - from_clamped_netfloat(packet->pressureOrDistance, 0.0f, 1.0f), - contact_area.first, - contact_area.second, - }; - - platf::touch_update(input->client_context.get(), abs_port, touch); - } - - /** - * @brief Called to pass a pen message to the platform backend. - * @param input The input context pointer. - * @param packet The pen packet. - */ - void - passthrough(std::shared_ptr &input, PSS_PEN_PACKET packet) { - if (!config::input.mouse) { - return; - } - - // Convert the client normalized coordinates to touchport coordinates - auto coords = client_to_touchport(input, - { from_clamped_netfloat(packet->x, 0.0f, 1.0f) * 65535.f, - from_clamped_netfloat(packet->y, 0.0f, 1.0f) * 65535.f }, - { 65535.f, 65535.f }); - if (!coords) { - return; - } - - auto &touch_port = input->touch_port; - platf::touch_port_t abs_port { - touch_port.offset_x, touch_port.offset_y, - touch_port.env_width, touch_port.env_height - }; - - // Renormalize the coordinates - coords->first /= abs_port.width; - coords->second /= abs_port.height; - - // Normalize rotation value to 0-359 degree range - auto rotation = util::endian::little(packet->rotation); - if (rotation != LI_ROT_UNKNOWN) { - rotation %= 360; - } - - // Normalize the contact area based on the touchport - auto contact_area = scale_client_contact_area( - { from_clamped_netfloat(packet->contactAreaMajor, 0.0f, 1.0f) * 65535.f, - from_clamped_netfloat(packet->contactAreaMinor, 0.0f, 1.0f) * 65535.f }, - rotation, - { abs_port.width / 65535.f, abs_port.height / 65535.f }); - - platf::pen_input_t pen { - packet->eventType, - packet->toolType, - packet->penButtons, - packet->tilt, - rotation, - coords->first, - coords->second, - from_clamped_netfloat(packet->pressureOrDistance, 0.0f, 1.0f), - contact_area.first, - contact_area.second, - }; - - platf::pen_update(input->client_context.get(), abs_port, pen); - } - - /** - * @brief Called to pass a controller touch message to the platform backend. - * @param input The input context pointer. - * @param packet The controller touch packet. - */ - void - passthrough(std::shared_ptr &input, PSS_CONTROLLER_TOUCH_PACKET packet) { - if (!config::input.controller) { - return; - } - - if (packet->controllerNumber < 0 || packet->controllerNumber >= input->gamepads.size()) { - BOOST_LOG(warning) << "ControllerNumber out of range ["sv << packet->controllerNumber << ']'; - return; - } - - auto &gamepad = input->gamepads[packet->controllerNumber]; - if (gamepad.id < 0) { - BOOST_LOG(warning) << "ControllerNumber ["sv << packet->controllerNumber << "] not allocated"sv; - return; - } - - platf::gamepad_touch_t touch { - { gamepad.id, packet->controllerNumber }, - packet->eventType, - util::endian::little(packet->pointerId), - from_clamped_netfloat(packet->x, 0.0f, 1.0f), - from_clamped_netfloat(packet->y, 0.0f, 1.0f), - from_clamped_netfloat(packet->pressure, 0.0f, 1.0f), - }; - - platf::gamepad_touch(platf_input, touch); - } - - /** - * @brief Called to pass a controller motion message to the platform backend. - * @param input The input context pointer. - * @param packet The controller motion packet. - */ - void - passthrough(std::shared_ptr &input, PSS_CONTROLLER_MOTION_PACKET packet) { - if (!config::input.controller) { - return; - } - - if (packet->controllerNumber < 0 || packet->controllerNumber >= input->gamepads.size()) { - BOOST_LOG(warning) << "ControllerNumber out of range ["sv << packet->controllerNumber << ']'; - return; - } - - auto &gamepad = input->gamepads[packet->controllerNumber]; - if (gamepad.id < 0) { - BOOST_LOG(warning) << "ControllerNumber ["sv << packet->controllerNumber << "] not allocated"sv; - return; - } - - platf::gamepad_motion_t motion { - { gamepad.id, packet->controllerNumber }, - packet->motionType, - from_netfloat(packet->x), - from_netfloat(packet->y), - from_netfloat(packet->z), - }; - - platf::gamepad_motion(platf_input, motion); - } - - /** - * @brief Called to pass a controller battery message to the platform backend. - * @param input The input context pointer. - * @param packet The controller battery packet. - */ - void - passthrough(std::shared_ptr &input, PSS_CONTROLLER_BATTERY_PACKET packet) { - if (!config::input.controller) { - return; - } - - if (packet->controllerNumber < 0 || packet->controllerNumber >= input->gamepads.size()) { - BOOST_LOG(warning) << "ControllerNumber out of range ["sv << packet->controllerNumber << ']'; - return; - } - - auto &gamepad = input->gamepads[packet->controllerNumber]; - if (gamepad.id < 0) { - BOOST_LOG(warning) << "ControllerNumber ["sv << packet->controllerNumber << "] not allocated"sv; - return; - } - - platf::gamepad_battery_t battery { - { gamepad.id, packet->controllerNumber }, - packet->batteryState, - packet->batteryPercentage - }; - - platf::gamepad_battery(platf_input, battery); - } - - void - passthrough(std::shared_ptr &input, PNV_MULTI_CONTROLLER_PACKET packet) { - if (!config::input.controller) { - return; - } - - if (packet->controllerNumber < 0 || packet->controllerNumber >= input->gamepads.size()) { - BOOST_LOG(warning) << "ControllerNumber out of range ["sv << packet->controllerNumber << ']'; - - return; - } - - auto &gamepad = input->gamepads[packet->controllerNumber]; - - // If this is an event for a new gamepad, create the gamepad now. Ideally, the client would - // send a controller arrival instead of this but it's still supported for legacy clients. - if ((packet->activeGamepadMask & (1 << packet->controllerNumber)) && gamepad.id < 0) { - auto id = alloc_id(gamepadMask); - if (id < 0) { - return; - } - - if (platf::alloc_gamepad(platf_input, { id, (uint8_t) packet->controllerNumber }, {}, input->feedback_queue)) { - free_id(gamepadMask, id); - return; - } - - gamepad.id = id; - } - else if (!(packet->activeGamepadMask & (1 << packet->controllerNumber)) && gamepad.id >= 0) { - // If this is the final event for a gamepad being removed, free the gamepad and return. - free_gamepad(platf_input, gamepad.id); - gamepad.id = -1; - return; - } - - // If this gamepad has not been initialized, ignore it. - // This could happen when platf::alloc_gamepad fails - if (gamepad.id < 0) { - BOOST_LOG(warning) << "ControllerNumber ["sv << packet->controllerNumber << "] not allocated"sv; - return; - } - - std::uint16_t bf = packet->buttonFlags; - std::uint32_t bf2 = packet->buttonFlags2; - platf::gamepad_state_t gamepad_state { - bf | (bf2 << 16), - packet->leftTrigger, - packet->rightTrigger, - packet->leftStickX, - packet->leftStickY, - packet->rightStickX, - packet->rightStickY - }; - - auto bf_new = gamepad_state.buttonFlags; - switch (gamepad.back_button_state) { - case button_state_e::UP: - if (!(platf::BACK & bf_new)) { - gamepad.back_button_state = button_state_e::NONE; - } - gamepad_state.buttonFlags &= ~platf::BACK; - break; - case button_state_e::DOWN: - if (platf::BACK & bf_new) { - gamepad.back_button_state = button_state_e::NONE; - } - gamepad_state.buttonFlags |= platf::BACK; - break; - case button_state_e::NONE: - break; - } - - bf = gamepad_state.buttonFlags ^ gamepad.gamepad_state.buttonFlags; - bf_new = gamepad_state.buttonFlags; - - if (platf::BACK & bf) { - if (platf::BACK & bf_new) { - // Don't emulate home button if timeout < 0 - if (config::input.back_button_timeout >= 0ms) { - auto f = [input, controller = packet->controllerNumber]() { - auto &gamepad = input->gamepads[controller]; - - auto &state = gamepad.gamepad_state; - - // Force the back button up - gamepad.back_button_state = button_state_e::UP; - state.buttonFlags &= ~platf::BACK; - platf::gamepad_update(platf_input, gamepad.id, state); - - // Press Home button - state.buttonFlags |= platf::HOME; - 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_update(platf_input, gamepad.id, state); - - gamepad.back_timeout_id = nullptr; - }; - - gamepad.back_timeout_id = task_pool.pushDelayed(std::move(f), config::input.back_button_timeout).task_id; - } - } - else if (gamepad.back_timeout_id) { - task_pool.cancel(gamepad.back_timeout_id); - gamepad.back_timeout_id = nullptr; - } - } - - platf::gamepad_update(platf_input, gamepad.id, gamepad_state); - - gamepad.gamepad_state = gamepad_state; - } - - enum class batch_result_e { - batched, ///< This entry was batched with the source entry - not_batchable, ///< Not eligible to batch but continue attempts to batch - terminate_batch, ///< Stop trying to batch with this entry - }; - - /** - * @brief Batch two relative mouse messages. - * @param dest The original packet to batch into. - * @param src A later packet to attempt to batch. - * @return The status of the batching operation. - */ - batch_result_e - batch(PNV_REL_MOUSE_MOVE_PACKET dest, PNV_REL_MOUSE_MOVE_PACKET src) { - short deltaX, deltaY; - - // Batching is safe as long as the result doesn't overflow a 16-bit integer - if (!__builtin_add_overflow(util::endian::big(dest->deltaX), util::endian::big(src->deltaX), &deltaX)) { - return batch_result_e::terminate_batch; - } - if (!__builtin_add_overflow(util::endian::big(dest->deltaY), util::endian::big(src->deltaY), &deltaY)) { - return batch_result_e::terminate_batch; - } - - // Take the sum of deltas - dest->deltaX = util::endian::big(deltaX); - dest->deltaY = util::endian::big(deltaY); - return batch_result_e::batched; - } - - /** - * @brief Batch two absolute mouse messages. - * @param dest The original packet to batch into. - * @param src A later packet to attempt to batch. - * @return The status of the batching operation. - */ - batch_result_e - batch(PNV_ABS_MOUSE_MOVE_PACKET dest, PNV_ABS_MOUSE_MOVE_PACKET src) { - // Batching must only happen if the reference width and height don't change - if (dest->width != src->width || dest->height != src->height) { - return batch_result_e::terminate_batch; - } - - // Take the latest absolute position - *dest = *src; - return batch_result_e::batched; - } - - /** - * @brief Batch two vertical scroll messages. - * @param dest The original packet to batch into. - * @param src A later packet to attempt to batch. - * @return The status of the batching operation. - */ - batch_result_e - batch(PNV_SCROLL_PACKET dest, PNV_SCROLL_PACKET src) { - short scrollAmt; - - // Batching is safe as long as the result doesn't overflow a 16-bit integer - if (!__builtin_add_overflow(util::endian::big(dest->scrollAmt1), util::endian::big(src->scrollAmt1), &scrollAmt)) { - return batch_result_e::terminate_batch; - } - - // Take the sum of delta - dest->scrollAmt1 = util::endian::big(scrollAmt); - dest->scrollAmt2 = util::endian::big(scrollAmt); - return batch_result_e::batched; - } - - /** - * @brief Batch two horizontal scroll messages. - * @param dest The original packet to batch into. - * @param src A later packet to attempt to batch. - * @return The status of the batching operation. - */ - batch_result_e - batch(PSS_HSCROLL_PACKET dest, PSS_HSCROLL_PACKET src) { - short scrollAmt; - - // Batching is safe as long as the result doesn't overflow a 16-bit integer - if (!__builtin_add_overflow(util::endian::big(dest->scrollAmount), util::endian::big(src->scrollAmount), &scrollAmt)) { - return batch_result_e::terminate_batch; - } - - // Take the sum of delta - dest->scrollAmount = util::endian::big(scrollAmt); - return batch_result_e::batched; - } - - /** - * @brief Batch two controller state messages. - * @param dest The original packet to batch into. - * @param src A later packet to attempt to batch. - * @return The status of the batching operation. - */ - batch_result_e - batch(PNV_MULTI_CONTROLLER_PACKET dest, PNV_MULTI_CONTROLLER_PACKET src) { - // Do not allow batching if the active controllers change - if (dest->activeGamepadMask != src->activeGamepadMask) { - return batch_result_e::terminate_batch; - } - - // We can only batch entries for the same controller, but allow batching attempts to continue - // in case we have more packets for this controller later in the queue. - if (dest->controllerNumber != src->controllerNumber) { - return batch_result_e::not_batchable; - } - - // Do not allow batching if the button state changes on this controller - if (dest->buttonFlags != src->buttonFlags || dest->buttonFlags2 != src->buttonFlags2) { - return batch_result_e::terminate_batch; - } - - // Take the latest state - *dest = *src; - return batch_result_e::batched; - } - - /** - * @brief Batch two touch messages. - * @param dest The original packet to batch into. - * @param src A later packet to attempt to batch. - * @return The status of the batching operation. - */ - batch_result_e - batch(PSS_TOUCH_PACKET dest, PSS_TOUCH_PACKET src) { - // Only batch hover or move events - if (dest->eventType != LI_TOUCH_EVENT_MOVE && - dest->eventType != LI_TOUCH_EVENT_HOVER) { - return batch_result_e::terminate_batch; - } - - // Don't batch beyond state changing events - if (src->eventType != LI_TOUCH_EVENT_MOVE && - src->eventType != LI_TOUCH_EVENT_HOVER) { - return batch_result_e::terminate_batch; - } - - // Batched events must be the same pointer ID - if (dest->pointerId != src->pointerId) { - return batch_result_e::not_batchable; - } - - // The pointer must be in the same state - if (dest->eventType != src->eventType) { - return batch_result_e::terminate_batch; - } - - // Take the latest state - *dest = *src; - return batch_result_e::batched; - } - - /** - * @brief Batch two pen messages. - * @param dest The original packet to batch into. - * @param src A later packet to attempt to batch. - * @return The status of the batching operation. - */ - batch_result_e - batch(PSS_PEN_PACKET dest, PSS_PEN_PACKET src) { - // Only batch hover or move events - if (dest->eventType != LI_TOUCH_EVENT_MOVE && - dest->eventType != LI_TOUCH_EVENT_HOVER) { - return batch_result_e::terminate_batch; - } - - // Batched events must be the same type - if (dest->eventType != src->eventType) { - return batch_result_e::terminate_batch; - } - - // Do not allow batching if the button state changes - if (dest->penButtons != src->penButtons) { - return batch_result_e::terminate_batch; - } - - // Do not batch beyond tool changes - if (dest->toolType != src->toolType) { - return batch_result_e::terminate_batch; - } - - // Take the latest state - *dest = *src; - return batch_result_e::batched; - } - - /** - * @brief Batch two controller touch messages. - * @param dest The original packet to batch into. - * @param src A later packet to attempt to batch. - * @return The status of the batching operation. - */ - batch_result_e - batch(PSS_CONTROLLER_TOUCH_PACKET dest, PSS_CONTROLLER_TOUCH_PACKET src) { - // Only batch hover or move events - if (dest->eventType != LI_TOUCH_EVENT_MOVE && - dest->eventType != LI_TOUCH_EVENT_HOVER) { - return batch_result_e::terminate_batch; - } - - // We can only batch entries for the same controller, but allow batching attempts to continue - // in case we have more packets for this controller later in the queue. - if (dest->controllerNumber != src->controllerNumber) { - return batch_result_e::not_batchable; - } - - // Don't batch beyond state changing events - if (src->eventType != LI_TOUCH_EVENT_MOVE && - src->eventType != LI_TOUCH_EVENT_HOVER) { - return batch_result_e::terminate_batch; - } - - // Batched events must be the same pointer ID - if (dest->pointerId != src->pointerId) { - return batch_result_e::not_batchable; - } - - // The pointer must be in the same state - if (dest->eventType != src->eventType) { - return batch_result_e::terminate_batch; - } - - // Take the latest state - *dest = *src; - return batch_result_e::batched; - } - - /** - * @brief Batch two controller motion messages. - * @param dest The original packet to batch into. - * @param src A later packet to attempt to batch. - * @return The status of the batching operation. - */ - batch_result_e - batch(PSS_CONTROLLER_MOTION_PACKET dest, PSS_CONTROLLER_MOTION_PACKET src) { - // We can only batch entries for the same controller, but allow batching attempts to continue - // in case we have more packets for this controller later in the queue. - if (dest->controllerNumber != src->controllerNumber) { - return batch_result_e::not_batchable; - } - - // Batched events must be the same sensor - if (dest->motionType != src->motionType) { - return batch_result_e::not_batchable; - } - - // Take the latest state - *dest = *src; - return batch_result_e::batched; - } - - /** - * @brief Batch two input messages. - * @param dest The original packet to batch into. - * @param src A later packet to attempt to batch. - * @return The status of the batching operation. - */ - batch_result_e - batch(PNV_INPUT_HEADER dest, PNV_INPUT_HEADER src) { - // We can only batch if the packet types are the same - if (dest->magic != src->magic) { - return batch_result_e::terminate_batch; - } - - // We can only batch certain message types - switch (util::endian::little(dest->magic)) { - case MOUSE_MOVE_REL_MAGIC_GEN5: - return batch((PNV_REL_MOUSE_MOVE_PACKET) dest, (PNV_REL_MOUSE_MOVE_PACKET) src); - case MOUSE_MOVE_ABS_MAGIC: - return batch((PNV_ABS_MOUSE_MOVE_PACKET) dest, (PNV_ABS_MOUSE_MOVE_PACKET) src); - case SCROLL_MAGIC_GEN5: - return batch((PNV_SCROLL_PACKET) dest, (PNV_SCROLL_PACKET) src); - case SS_HSCROLL_MAGIC: - return batch((PSS_HSCROLL_PACKET) dest, (PSS_HSCROLL_PACKET) src); - case MULTI_CONTROLLER_MAGIC_GEN5: - return batch((PNV_MULTI_CONTROLLER_PACKET) dest, (PNV_MULTI_CONTROLLER_PACKET) src); - case SS_TOUCH_MAGIC: - return batch((PSS_TOUCH_PACKET) dest, (PSS_TOUCH_PACKET) src); - case SS_PEN_MAGIC: - return batch((PSS_PEN_PACKET) dest, (PSS_PEN_PACKET) src); - case SS_CONTROLLER_TOUCH_MAGIC: - return batch((PSS_CONTROLLER_TOUCH_PACKET) dest, (PSS_CONTROLLER_TOUCH_PACKET) src); - case SS_CONTROLLER_MOTION_MAGIC: - return batch((PSS_CONTROLLER_MOTION_PACKET) dest, (PSS_CONTROLLER_MOTION_PACKET) src); - default: - // Not a batchable message type - return batch_result_e::terminate_batch; - } - } - - /** - * @brief Called on a thread pool thread to process an input message. - * @param input The input context pointer. - */ - void - passthrough_next_message(std::shared_ptr input) { - // 'entry' backs the 'payload' pointer, so they must remain in scope together - std::vector entry; - PNV_INPUT_HEADER payload; - - // Lock the input queue while batching, but release it before sending - // the input to the OS. This avoids potentially lengthy lock contention - // in the control stream thread while input is being processed by the OS. - { - std::lock_guard lg(input->input_queue_lock); - - // If all entries have already been processed, nothing to do - if (input->input_queue.empty()) { - return; - } - - // Pop off the first entry, which we will send - entry = input->input_queue.front(); - payload = (PNV_INPUT_HEADER) entry.data(); - input->input_queue.pop_front(); - - // Try to batch with remaining items on the queue - auto i = input->input_queue.begin(); - while (i != input->input_queue.end()) { - auto batchable_entry = *i; - auto batchable_payload = (PNV_INPUT_HEADER) batchable_entry.data(); - - auto batch_result = batch(payload, batchable_payload); - if (batch_result == batch_result_e::terminate_batch) { - // Stop batching - break; - } - else if (batch_result == batch_result_e::batched) { - // Erase this entry since it was batched - i = input->input_queue.erase(i); - } - else { - // We couldn't batch this entry, but try to batch later entries. - i++; - } - } - } - - // Print the final input packet - input::print((void *) payload); - - // Send the batched input to the OS - switch (util::endian::little(payload->magic)) { - case MOUSE_MOVE_REL_MAGIC_GEN5: - passthrough(input, (PNV_REL_MOUSE_MOVE_PACKET) payload); - break; - case MOUSE_MOVE_ABS_MAGIC: - passthrough(input, (PNV_ABS_MOUSE_MOVE_PACKET) payload); - break; - case MOUSE_BUTTON_DOWN_EVENT_MAGIC_GEN5: - case MOUSE_BUTTON_UP_EVENT_MAGIC_GEN5: - passthrough(input, (PNV_MOUSE_BUTTON_PACKET) payload); - break; - case SCROLL_MAGIC_GEN5: - passthrough(input, (PNV_SCROLL_PACKET) payload); - break; - case SS_HSCROLL_MAGIC: - passthrough(input, (PSS_HSCROLL_PACKET) payload); - break; - case KEY_DOWN_EVENT_MAGIC: - case KEY_UP_EVENT_MAGIC: - passthrough(input, (PNV_KEYBOARD_PACKET) payload); - break; - case UTF8_TEXT_EVENT_MAGIC: - passthrough((PNV_UNICODE_PACKET) payload); - break; - case MULTI_CONTROLLER_MAGIC_GEN5: - passthrough(input, (PNV_MULTI_CONTROLLER_PACKET) payload); - break; - case SS_TOUCH_MAGIC: - passthrough(input, (PSS_TOUCH_PACKET) payload); - break; - case SS_PEN_MAGIC: - passthrough(input, (PSS_PEN_PACKET) payload); - break; - case SS_CONTROLLER_ARRIVAL_MAGIC: - passthrough(input, (PSS_CONTROLLER_ARRIVAL_PACKET) payload); - break; - case SS_CONTROLLER_TOUCH_MAGIC: - passthrough(input, (PSS_CONTROLLER_TOUCH_PACKET) payload); - break; - case SS_CONTROLLER_MOTION_MAGIC: - passthrough(input, (PSS_CONTROLLER_MOTION_PACKET) payload); - break; - case SS_CONTROLLER_BATTERY_MAGIC: - passthrough(input, (PSS_CONTROLLER_BATTERY_PACKET) payload); - break; - } - } - - /** - * @brief Called on the control stream thread to queue an input message. - * @param input The input context pointer. - * @param input_data The input message. - */ - void - passthrough(std::shared_ptr &input, std::vector &&input_data) { - { - std::lock_guard lg(input->input_queue_lock); - input->input_queue.push_back(std::move(input_data)); - } - task_pool.push(passthrough_next_message, input); - } - - void - reset(std::shared_ptr &input) { - task_pool.cancel(key_press_repeat_id); - task_pool.cancel(input->mouse_left_button_timeout); - - // Ensure input is synchronous, by using the task_pool - task_pool.push([]() { - for (int x = 0; x < mouse_press.size(); ++x) { - if (mouse_press[x]) { - platf::button_mouse(platf_input, x, true); - mouse_press[x] = false; - } - } - - for (auto &kp : key_press) { - if (!kp.second) { - // already released - continue; - } - platf::keyboard_update(platf_input, vk_from_kpid(kp.first) & 0x00FF, true, flags_from_kpid(kp.first)); - key_press[kp.first] = false; - } - }); - } - - class deinit_t: public platf::deinit_t { - public: - ~deinit_t() override { - platf_input.reset(); - } - }; - - [[nodiscard]] std::unique_ptr - init() { - platf_input = platf::input(); - - return std::make_unique(); - } - - bool - probe_gamepads() { - auto input = static_cast(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 - alloc(safe::mail_t mail) { - auto input = std::make_shared( - mail->event(mail::touch_port), - mail->queue(mail::gamepad_feedback)); - - // Workaround to ensure new frames will be captured when a client connects - task_pool.pushDelayed([]() { - platf::move_mouse(platf_input, 1, 1); - platf::move_mouse(platf_input, -1, -1); - }, - 100ms); - - return input; - } -} // namespace input diff --git a/src/input.h b/src/input.h deleted file mode 100644 index a20c9c305b3..00000000000 --- a/src/input.h +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @file src/input.h - * @brief Declarations for gamepad, keyboard, and mouse input handling. - */ -#pragma once - -#include - -#include "platform/common.h" -#include "thread_safe.h" - -namespace input { - struct input_t; - - void - print(void *input); - void - reset(std::shared_ptr &input); - void - passthrough(std::shared_ptr &input, std::vector &&input_data); - - [[nodiscard]] std::unique_ptr - init(); - - bool - probe_gamepads(); - - std::shared_ptr - alloc(safe::mail_t mail); - - struct touch_port_t: public platf::touch_port_t { - int env_width, env_height; - - // Offset x and y coordinates of the client - float client_offsetX, client_offsetY; - - float scalar_inv; - - explicit - operator bool() const { - return width != 0 && height != 0 && env_width != 0 && env_height != 0; - } - }; - - /** - * @brief Scale the ellipse axes according to the provided size. - * @param val The major and minor axis pair. - * @param rotation The rotation value from the touch/pen event. - * @param scalar The scalar cartesian coordinate pair. - * @return The major and minor axis pair. - */ - std::pair - scale_client_contact_area(const std::pair &val, uint16_t rotation, const std::pair &scalar); -} // namespace input diff --git a/src/input/common.cpp b/src/input/common.cpp new file mode 100644 index 00000000000..261b486fb90 --- /dev/null +++ b/src/input/common.cpp @@ -0,0 +1,54 @@ +/** + * @file src/input/common.cpp + * @brief Definitions for common input. + */ +#include "src/input/common.h" +#include "src/input/gamepad.h" +#include "src/input/init.h" + +#include "src/thread_pool.h" + +#include + +using namespace std::literals; + +namespace input { + + float + from_netfloat(netfloat f) { + return boost::endian::endian_load(f); + } + + float + from_clamped_netfloat(netfloat f, float min, float max) { + return std::clamp(from_netfloat(f), min, max); + } + + float + multiply_polar_by_cartesian_scalar(float r, float angle, const std::pair &scalar) { + // Convert polar to cartesian coordinates + float x = r * std::cos(angle); + float y = r * std::sin(angle); + + // Scale the values + x *= scalar.first; + y *= scalar.second; + + // Convert the result back to a polar radial coordinate + return std::sqrt(std::pow(x, 2) + std::pow(y, 2)); + } + + std::pair + scale_client_contact_area(const std::pair &val, uint16_t rotation, const std::pair &scalar) { + // If the rotation is unknown, we'll just scale both axes equally by using + // a 45-degree angle for our scaling calculations + float angle = rotation == LI_ROT_UNKNOWN ? (M_PI / 4) : (rotation * (M_PI / 180)); + + // If we have a major but not a minor axis, treat the touch as circular + float major = val.first; + float minor = val.second != 0.0f ? val.second : val.first; + + // The minor axis is perpendicular to major axis so the angle must be rotated by 90 degrees + return { multiply_polar_by_cartesian_scalar(major, angle, scalar), multiply_polar_by_cartesian_scalar(minor, angle + (M_PI / 2), scalar) }; + } +} // namespace input diff --git a/src/input/common.h b/src/input/common.h new file mode 100644 index 00000000000..4d4895f6630 --- /dev/null +++ b/src/input/common.h @@ -0,0 +1,82 @@ +/** + * @file src/input/common.h + * @brief Declarations for common input. + */ +#pragma once + +#include +extern "C" { +#include +#include +} + +#include + +#include "src/globals.h" +#include "src/platform/common.h" +#include "src/thread_pool.h" + +#include "src/input/gamepad.h" + +namespace input { + + struct gamepad_orchestrator { + gamepad_orchestrator(): + gamepads(gamepad::MAX_GAMEPADS) {}; + + std::vector gamepads; + }; + + struct touch_port_t: public platf::touch_port_t { + int env_width, env_height; + + // Offset x and y coordinates of the client + float client_offsetX, client_offsetY; + + float scalar_inv; + + explicit + operator bool() const { + return width != 0 && height != 0 && env_width != 0 && env_height != 0; + } + }; + + /** + * @brief Convert a little-endian netfloat to a native endianness float and clamps it. + * @param f Netfloat value. + * @param min The minimium value for clamping. + * @param max The maximum value for clamping. + * @return Clamped native endianess float value. + */ + float + from_clamped_netfloat(netfloat f, float min, float max); + + /** + * @brief Convert a little-endian netfloat to a native endianness float. + * @param f Netfloat value. + * @return The native endianness float value. + */ + float + from_netfloat(netfloat f); + + /** + * @brief Multiply a polar coordinate pair by a cartesian scaling factor. + * @param r The radial coordinate. + * @param angle The angular coordinate (radians). + * @param scalar The scalar cartesian coordinate pair. + * @return The scaled radial coordinate. + */ + float + multiply_polar_by_cartesian_scalar(float r, float angle, const std::pair &scalar); + + /** + * @brief Scale the ellipse axes according to the provided size. + * @param val The major and minor axis pair. + * @param rotation The rotation value from the touch/pen event. + * @param scalar The scalar cartesian coordinate pair. + * @return The major and minor axis pair. + */ + std::pair + scale_client_contact_area(const std::pair &val, uint16_t rotation, const std::pair &scalar); + +} // namespace input diff --git a/src/input/gamepad.cpp b/src/input/gamepad.cpp new file mode 100644 index 00000000000..875fd5eaa78 --- /dev/null +++ b/src/input/gamepad.cpp @@ -0,0 +1,421 @@ +/** + * @file src/input/gamepad.cpp + * @brief Definitions for common gamepad input. + */ +#include "src/input/gamepad.h" +#include "src/input/common.h" +#include "src/input/init.h" +#include "src/input/platform_input.h" +#include "src/input/processor.h" + +#include "src/globals.h" +#include "src/thread_pool.h" + +using namespace std::literals; + +namespace input::gamepad { + + template + int + alloc_id(std::bitset &gamepad_mask) { + for (int x = 0; x < gamepad_mask.size(); ++x) { + if (!gamepad_mask[x]) { + gamepad_mask[x] = true; + return x; + } + } + + return -1; + } + + template + void + free_id(std::bitset &gamepad_mask, int id) { + gamepad_mask[id] = false; + } + + void + free_gamepad(platf::input_t &platf_input, int id) { + platf::gamepad_update(platf_input, id, platf::gamepad_state_t {}); + platf::free_gamepad(platf_input, id); + + free_id(gamepadMask, id); + } + + void + print(PNV_MULTI_CONTROLLER_PACKET packet) { + // Moonlight spams controller packet even when not necessary + BOOST_LOG(verbose) + << "--begin controller packet--"sv << std::endl + << "controllerNumber ["sv << packet->controllerNumber << ']' << std::endl + << "activeGamepadMask ["sv << util::hex(packet->activeGamepadMask).to_string_view() << ']' << std::endl + << "buttonFlags ["sv << util::hex((uint32_t) packet->buttonFlags | (packet->buttonFlags2 << 16)).to_string_view() << ']' << std::endl + << "leftTrigger ["sv << util::hex(packet->leftTrigger).to_string_view() << ']' << std::endl + << "rightTrigger ["sv << util::hex(packet->rightTrigger).to_string_view() << ']' << std::endl + << "leftStickX ["sv << packet->leftStickX << ']' << std::endl + << "leftStickY ["sv << packet->leftStickY << ']' << std::endl + << "rightStickX ["sv << packet->rightStickX << ']' << std::endl + << "rightStickY ["sv << packet->rightStickY << ']' << std::endl + << "--end controller packet--"sv; + } + + void + print(PSS_CONTROLLER_ARRIVAL_PACKET packet) { + BOOST_LOG(debug) + << "--begin controller arrival packet--"sv << std::endl + << "controllerNumber ["sv << (uint32_t) packet->controllerNumber << ']' << std::endl + << "type ["sv << util::hex(packet->type).to_string_view() << ']' << std::endl + << "capabilities ["sv << util::hex(packet->capabilities).to_string_view() << ']' << std::endl + << "supportedButtonFlags ["sv << util::hex(packet->supportedButtonFlags).to_string_view() << ']' << std::endl + << "--end controller arrival packet--"sv; + } + + void + print(PSS_CONTROLLER_TOUCH_PACKET packet) { + BOOST_LOG(debug) + << "--begin controller touch packet--"sv << std::endl + << "controllerNumber ["sv << (uint32_t) packet->controllerNumber << ']' << std::endl + << "eventType ["sv << util::hex(packet->eventType).to_string_view() << ']' << std::endl + << "pointerId ["sv << util::hex(packet->pointerId).to_string_view() << ']' << std::endl + << "x ["sv << from_netfloat(packet->x) << ']' << std::endl + << "y ["sv << from_netfloat(packet->y) << ']' << std::endl + << "pressure ["sv << from_netfloat(packet->pressure) << ']' << std::endl + << "--end controller touch packet--"sv; + } + + void + print(PSS_CONTROLLER_MOTION_PACKET packet) { + BOOST_LOG(verbose) + << "--begin controller motion packet--"sv << std::endl + << "controllerNumber ["sv << util::hex(packet->controllerNumber).to_string_view() << ']' << std::endl + << "motionType ["sv << util::hex(packet->motionType).to_string_view() << ']' << std::endl + << "x ["sv << from_netfloat(packet->x) << ']' << std::endl + << "y ["sv << from_netfloat(packet->y) << ']' << std::endl + << "z ["sv << from_netfloat(packet->z) << ']' << std::endl + << "--end controller motion packet--"sv; + } + + void + print(PSS_CONTROLLER_BATTERY_PACKET packet) { + BOOST_LOG(verbose) + << "--begin controller battery packet--"sv << std::endl + << "controllerNumber ["sv << util::hex(packet->controllerNumber).to_string_view() << ']' << std::endl + << "batteryState ["sv << util::hex(packet->batteryState).to_string_view() << ']' << std::endl + << "batteryPercentage ["sv << util::hex(packet->batteryPercentage).to_string_view() << ']' << std::endl + << "--end controller battery packet--"sv; + } + + void + passthrough(std::shared_ptr &input, PSS_CONTROLLER_ARRIVAL_PACKET packet) { + if (!config::input.controller) { + return; + } + + if (packet->controllerNumber < 0 || packet->controllerNumber >= input->gamepads->size()) { + BOOST_LOG(warning) << "ControllerNumber out of range ["sv << packet->controllerNumber << ']'; + return; + } + + if ((*input->gamepads)[packet->controllerNumber].id >= 0) { + BOOST_LOG(warning) << "ControllerNumber already allocated ["sv << packet->controllerNumber << ']'; + return; + } + + platf::gamepad_arrival_t arrival { + packet->type, + util::endian::little(packet->capabilities), + util::endian::little(packet->supportedButtonFlags), + }; + + auto id = alloc_id(gamepadMask); + if (id < 0) { + return; + } + + // Allocate a new gamepad + if (platf::alloc_gamepad(PlatformInput::getInstance(), { id, packet->controllerNumber }, arrival, input->feedback_queue)) { + free_id(gamepadMask, id); + return; + } + + (*input->gamepads)[packet->controllerNumber].id = id; + } + + void + passthrough(std::shared_ptr &input, PSS_CONTROLLER_TOUCH_PACKET packet) { + if (!config::input.controller) { + return; + } + + if (packet->controllerNumber < 0 || packet->controllerNumber >= input->gamepads->size()) { + BOOST_LOG(warning) << "ControllerNumber out of range ["sv << packet->controllerNumber << ']'; + return; + } + + auto &gamepad = (*input->gamepads)[packet->controllerNumber]; + if (gamepad.id < 0) { + BOOST_LOG(warning) << "ControllerNumber ["sv << packet->controllerNumber << "] not allocated"sv; + return; + } + + platf::gamepad_touch_t touch { + { gamepad.id, packet->controllerNumber }, + packet->eventType, + util::endian::little(packet->pointerId), + from_clamped_netfloat(packet->x, 0.0f, 1.0f), + from_clamped_netfloat(packet->y, 0.0f, 1.0f), + from_clamped_netfloat(packet->pressure, 0.0f, 1.0f), + }; + + platf::gamepad_touch(PlatformInput::getInstance(), touch); + } + + void + passthrough(std::shared_ptr &input, PSS_CONTROLLER_MOTION_PACKET packet) { + if (!config::input.controller) { + return; + } + + if (packet->controllerNumber < 0 || packet->controllerNumber >= input->gamepads->size()) { + BOOST_LOG(warning) << "ControllerNumber out of range ["sv << packet->controllerNumber << ']'; + return; + } + + auto &gamepad = (*input->gamepads)[packet->controllerNumber]; + if (gamepad.id < 0) { + BOOST_LOG(warning) << "ControllerNumber ["sv << packet->controllerNumber << "] not allocated"sv; + return; + } + + platf::gamepad_motion_t motion { + { gamepad.id, packet->controllerNumber }, + packet->motionType, + from_netfloat(packet->x), + from_netfloat(packet->y), + from_netfloat(packet->z), + }; + + platf::gamepad_motion(PlatformInput::getInstance(), motion); + } + + void + passthrough(std::shared_ptr &input, PNV_MULTI_CONTROLLER_PACKET packet) { + if (!config::input.controller) { + return; + } + + if (packet->controllerNumber < 0 || packet->controllerNumber >= input->gamepads->size()) { + BOOST_LOG(warning) << "ControllerNumber out of range ["sv << packet->controllerNumber << ']'; + + return; + } + + auto &gamepad = (*input->gamepads)[packet->controllerNumber]; + + // If this is an event for a new gamepad, create the gamepad now. Ideally, the client would + // send a controller arrival instead of this but it's still supported for legacy clients. + if ((packet->activeGamepadMask & (1 << packet->controllerNumber)) && gamepad.id < 0) { + auto id = alloc_id(gamepadMask); + if (id < 0) { + return; + } + + if (platf::alloc_gamepad(PlatformInput::getInstance(), { id, (uint8_t) packet->controllerNumber }, {}, input->feedback_queue)) { + free_id(gamepadMask, id); + return; + } + + gamepad.id = id; + } + else if (!(packet->activeGamepadMask & (1 << packet->controllerNumber)) && gamepad.id >= 0) { + // If this is the final event for a gamepad being removed, free the gamepad and return. + free_gamepad(PlatformInput::getInstance(), gamepad.id); + gamepad.id = -1; + return; + } + + // If this gamepad has not been initialized, ignore it. + // This could happen when platf::alloc_gamepad fails + if (gamepad.id < 0) { + BOOST_LOG(warning) << "ControllerNumber ["sv << packet->controllerNumber << "] not allocated"sv; + return; + } + + std::uint16_t bf = packet->buttonFlags; + std::uint32_t bf2 = packet->buttonFlags2; + platf::gamepad_state_t gamepad_state { + bf | (bf2 << 16), + packet->leftTrigger, + packet->rightTrigger, + packet->leftStickX, + packet->leftStickY, + packet->rightStickX, + packet->rightStickY + }; + + auto bf_new = gamepad_state.buttonFlags; + switch (gamepad.back_button_state) { + case button_state_e::UP: + if (!(platf::BACK & bf_new)) { + gamepad.back_button_state = button_state_e::NONE; + } + gamepad_state.buttonFlags &= ~platf::BACK; + break; + case button_state_e::DOWN: + if (platf::BACK & bf_new) { + gamepad.back_button_state = button_state_e::NONE; + } + gamepad_state.buttonFlags |= platf::BACK; + break; + case button_state_e::NONE: + break; + } + + bf = gamepad_state.buttonFlags ^ gamepad.gamepad_state.buttonFlags; + bf_new = gamepad_state.buttonFlags; + + if (platf::BACK & bf) { + if (platf::BACK & bf_new) { + // Don't emulate home button if timeout < 0 + if (config::input.back_button_timeout >= 0ms) { + auto f = [input, controller = packet->controllerNumber]() { + auto &gamepad = (*input->gamepads)[controller]; + + auto &state = gamepad.gamepad_state; + + // Force the back button up + gamepad.back_button_state = button_state_e::UP; + state.buttonFlags &= ~platf::BACK; + platf::gamepad_update(PlatformInput::getInstance(), gamepad.id, state); + + // Press Home button + state.buttonFlags |= platf::HOME; + platf::gamepad_update(PlatformInput::getInstance(), 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_update(PlatformInput::getInstance(), gamepad.id, state); + + gamepad.back_timeout_id = nullptr; + }; + + gamepad.back_timeout_id = task_pool.pushDelayed(std::move(f), config::input.back_button_timeout).task_id; + } + } + else if (gamepad.back_timeout_id) { + task_pool.cancel(gamepad.back_timeout_id); + gamepad.back_timeout_id = nullptr; + } + } + + platf::gamepad_update(PlatformInput::getInstance(), gamepad.id, gamepad_state); + + gamepad.gamepad_state = gamepad_state; + } + + void + passthrough(std::shared_ptr &input, PSS_CONTROLLER_BATTERY_PACKET packet) { + if (!config::input.controller) { + return; + } + + if (packet->controllerNumber < 0 || packet->controllerNumber >= input->gamepads->size()) { + BOOST_LOG(warning) << "ControllerNumber out of range ["sv << packet->controllerNumber << ']'; + return; + } + + auto &gamepad = (*input->gamepads)[packet->controllerNumber]; + if (gamepad.id < 0) { + BOOST_LOG(warning) << "ControllerNumber ["sv << packet->controllerNumber << "] not allocated"sv; + return; + } + + platf::gamepad_battery_t battery { + { gamepad.id, packet->controllerNumber }, + packet->batteryState, + packet->batteryPercentage + }; + + platf::gamepad_battery(PlatformInput::getInstance(), battery); + } + + batch_result_e + batch(PSS_CONTROLLER_TOUCH_PACKET dest, PSS_CONTROLLER_TOUCH_PACKET src) { + // Only batch hover or move events + if (dest->eventType != LI_TOUCH_EVENT_MOVE && + dest->eventType != LI_TOUCH_EVENT_HOVER) { + return batch_result_e::terminate_batch; + } + + // We can only batch entries for the same controller, but allow batching attempts to continue + // in case we have more packets for this controller later in the queue. + if (dest->controllerNumber != src->controllerNumber) { + return batch_result_e::not_batchable; + } + + // Don't batch beyond state changing events + if (src->eventType != LI_TOUCH_EVENT_MOVE && + src->eventType != LI_TOUCH_EVENT_HOVER) { + return batch_result_e::terminate_batch; + } + + // Batched events must be the same pointer ID + if (dest->pointerId != src->pointerId) { + return batch_result_e::not_batchable; + } + + // The pointer must be in the same state + if (dest->eventType != src->eventType) { + return batch_result_e::terminate_batch; + } + + // Take the latest state + *dest = *src; + return batch_result_e::batched; + } + + batch_result_e + batch(PNV_MULTI_CONTROLLER_PACKET dest, PNV_MULTI_CONTROLLER_PACKET src) { + // Do not allow batching if the active controllers change + if (dest->activeGamepadMask != src->activeGamepadMask) { + return batch_result_e::terminate_batch; + } + + // We can only batch entries for the same controller, but allow batching attempts to continue + // in case we have more packets for this controller later in the queue. + if (dest->controllerNumber != src->controllerNumber) { + return batch_result_e::not_batchable; + } + + // Do not allow batching if the button state changes on this controller + if (dest->buttonFlags != src->buttonFlags || dest->buttonFlags2 != src->buttonFlags2) { + return batch_result_e::terminate_batch; + } + + // Take the latest state + *dest = *src; + return batch_result_e::batched; + } + + batch_result_e + batch(PSS_CONTROLLER_MOTION_PACKET dest, PSS_CONTROLLER_MOTION_PACKET src) { + // We can only batch entries for the same controller, but allow batching attempts to continue + // in case we have more packets for this controller later in the queue. + if (dest->controllerNumber != src->controllerNumber) { + return batch_result_e::not_batchable; + } + + // Batched events must be the same sensor + if (dest->motionType != src->motionType) { + return batch_result_e::not_batchable; + } + + // Take the latest state + *dest = *src; + return batch_result_e::batched; + } + +} // namespace input::gamepad diff --git a/src/input/gamepad.h b/src/input/gamepad.h new file mode 100644 index 00000000000..1a6afacb698 --- /dev/null +++ b/src/input/gamepad.h @@ -0,0 +1,162 @@ +/** + * @file src/input/gamepad.h + * @brief Declarations for common gamepad input. + */ +#pragma once + +#include "platform_input.h" + +#include +extern "C" { +#include +#include +} + +#include + +#include "src/globals.h" +#include "src/platform/common.h" +#include "src/thread_pool.h" + +#include "src/input/init.h" + +namespace input::gamepad { + static std::bitset gamepadMask = {}; + + constexpr auto MAX_GAMEPADS = std::min(static_cast(platf::MAX_GAMEPADS), sizeof(std::int16_t) * 8); + + enum class button_state_e { + NONE, ///< No button state + DOWN, ///< Button is down + UP ///< Button is up + }; + + template + int + alloc_id(std::bitset &gamepad_mask); + + template + void + free_id(std::bitset &gamepad_mask, int id); + + void + free_gamepad(platf::input_t &platf_input, int id); + + struct gamepad_t { + gamepad_t(): + gamepad_state {}, back_timeout_id {}, id { -1 }, back_button_state { button_state_e::NONE } {} + ~gamepad_t() { + if (id >= 0) { + task_pool.push([id = this->id]() { + free_gamepad(PlatformInput::getInstance(), id); + }); + } + } + + platf::gamepad_state_t gamepad_state; + + thread_pool_util::ThreadPool::task_id_t back_timeout_id; + + int id; + + // When emulating the HOME button, we may need to artificially release the back button. + // Afterwards, the gamepad state on sunshine won't match the state on Moonlight. + // To prevent Sunshine from sending erroneous input data to the active application, + // Sunshine forces the button to be in a specific state until the gamepad state matches that of + // Moonlight once more. + button_state_e back_button_state; + }; + + void + print(PNV_MULTI_CONTROLLER_PACKET packet); + + /** + * @brief Prints a controller arrival packet. + * @param packet The controller arrival packet. + */ + void + print(PSS_CONTROLLER_ARRIVAL_PACKET packet); + + /** + * @brief Prints a controller touch packet. + * @param packet The controller touch packet. + */ + void + print(PSS_CONTROLLER_TOUCH_PACKET packet); + + /** + * @brief Prints a controller motion packet. + * @param packet The controller motion packet. + */ + void + print(PSS_CONTROLLER_MOTION_PACKET packet); + + /** + * @brief Prints a controller battery packet. + * @param packet The controller battery packet. + */ + void + print(PSS_CONTROLLER_BATTERY_PACKET packet); + + /** + * @brief Called to pass a controller arrival message to the platform backend. + * @param input The input context pointer. + * @param packet The controller arrival packet. + */ + void + passthrough(std::shared_ptr &input, PSS_CONTROLLER_ARRIVAL_PACKET packet); + + /** + * @brief Called to pass a controller touch message to the platform backend. + * @param input The input context pointer. + * @param packet The controller touch packet. + */ + void + passthrough(std::shared_ptr &input, PSS_CONTROLLER_TOUCH_PACKET packet); + + /** + * @brief Called to pass a controller motion message to the platform backend. + * @param input The input context pointer. + * @param packet The controller motion packet. + */ + void + passthrough(std::shared_ptr &input, PSS_CONTROLLER_MOTION_PACKET packet); + + void + passthrough(std::shared_ptr &input, PNV_MULTI_CONTROLLER_PACKET packet); + + /** + * @brief Called to pass a controller battery message to the platform backend. + * @param input The input context pointer. + * @param packet The controller battery packet. + */ + void + passthrough(std::shared_ptr &input, PSS_CONTROLLER_BATTERY_PACKET packet); + + /** + * @brief Batch two controller touch messages. + * @param dest The original packet to batch into. + * @param src A later packet to attempt to batch. + * @return The status of the batching operation. + */ + batch_result_e + batch(PSS_CONTROLLER_TOUCH_PACKET dest, PSS_CONTROLLER_TOUCH_PACKET src); + + /** + * @brief Batch two controller state messages. + * @param dest The original packet to batch into. + * @param src A later packet to attempt to batch. + * @return The status of the batching operation. + */ + batch_result_e + batch(PNV_MULTI_CONTROLLER_PACKET dest, PNV_MULTI_CONTROLLER_PACKET src); + + /** + * @brief Batch two controller motion messages. + * @param dest The original packet to batch into. + * @param src A later packet to attempt to batch. + * @return The status of the batching operation. + */ + batch_result_e + batch(PSS_CONTROLLER_MOTION_PACKET dest, PSS_CONTROLLER_MOTION_PACKET src); +} // namespace input::gamepad diff --git a/src/input/init.h b/src/input/init.h new file mode 100644 index 00000000000..5a0a40ddb19 --- /dev/null +++ b/src/input/init.h @@ -0,0 +1,15 @@ +/** + * @file src/input/init.h + * @brief Declarations for common input initialization. + */ +#pragma once + +namespace input { + struct input_t; + + enum class batch_result_e { + batched, ///< Batched with the source entry + not_batchable, ///< Not eligible to batch but continue attempts to batch + terminate_batch, ///< Stop trying to batch + }; +} // namespace input diff --git a/src/input/keyboard.cpp b/src/input/keyboard.cpp new file mode 100644 index 00000000000..26c223735cc --- /dev/null +++ b/src/input/keyboard.cpp @@ -0,0 +1,283 @@ +/** + * @file src/input/keyboard.cpp + * @brief Definitions for common keyboard input. + */ +#include "src/input/keyboard.h" +#include "src/input/init.h" +#include "src/input/platform_input.h" +#include "src/input/processor.h" + +namespace input::keyboard { + + constexpr auto VKEY_SHIFT = 0x10; + constexpr auto VKEY_LSHIFT = 0xA0; + constexpr auto VKEY_RSHIFT = 0xA1; + constexpr auto VKEY_CONTROL = 0x11; + constexpr auto VKEY_LCONTROL = 0xA2; + constexpr auto VKEY_RCONTROL = 0xA3; + constexpr auto VKEY_MENU = 0x12; + constexpr auto VKEY_LMENU = 0xA4; + constexpr auto VKEY_RMENU = 0xA5; + + static task_pool_util::TaskPool::task_id_t key_press_repeat_id {}; + static std::unordered_map key_press {}; + + namespace { + key_press_id_t + make_kpid(uint16_t vk, uint8_t flags) { + return (key_press_id_t) vk << 8 | flags; + } + + uint16_t + vk_from_kpid(key_press_id_t kpid) { + return kpid >> 8; + } + + uint8_t + flags_from_kpid(key_press_id_t kpid) { + return kpid & 0xFF; + } + + /** + * @brief Apply shortcut based on VKEY. + * @param keyCode The VKEY code. + * @return 0 if no shortcut applied, > 0 if shortcut applied. + */ + int + apply_shortcut(short keyCode) { + constexpr auto VK_F1 = 0x70; + constexpr auto VK_F13 = 0x7C; + + BOOST_LOG(debug) << "Apply Shortcut: 0x"sv << util::hex((std::uint8_t) keyCode).to_string_view(); + + if (keyCode >= VK_F1 && keyCode <= VK_F13) { + mail::man->event(mail::switch_display)->raise(keyCode - VK_F1); + return 1; + } + + switch (keyCode) { + case 0x4E /* VKEY_N */: + display_cursor = !display_cursor; + return 1; + } + + return 0; + } + + short + map_keycode(short keycode) { + auto it = config::input.keybindings.find(keycode); + if (it != std::end(config::input.keybindings)) { + return it->second; + } + + return keycode; + } + + /** + * @brief Update flags for keyboard shortcut combos + */ + void + update_shortcutFlags(int *flags, short keyCode, bool release) { + switch (keyCode) { + case VKEY_SHIFT: + case VKEY_LSHIFT: + case VKEY_RSHIFT: + if (release) { + *flags &= ~input_t::SHIFT; + } + else { + *flags |= input_t::SHIFT; + } + break; + case VKEY_CONTROL: + case VKEY_LCONTROL: + case VKEY_RCONTROL: + if (release) { + *flags &= ~input_t::CTRL; + } + else { + *flags |= input_t::CTRL; + } + break; + case VKEY_MENU: + case VKEY_LMENU: + case VKEY_RMENU: + if (release) { + *flags &= ~input_t::ALT; + } + else { + *flags |= input_t::ALT; + } + break; + } + } + + bool + is_modifier(uint16_t keyCode) { + switch (keyCode) { + case VKEY_SHIFT: + case VKEY_LSHIFT: + case VKEY_RSHIFT: + case VKEY_CONTROL: + case VKEY_LCONTROL: + case VKEY_RCONTROL: + case VKEY_MENU: + case VKEY_LMENU: + case VKEY_RMENU: + return true; + default: + return false; + } + } + + void + send_key_and_modifiers(uint16_t key_code, bool release, uint8_t flags, uint8_t synthetic_modifiers) { + if (!release) { + // Press any synthetic modifiers required for this key + if (synthetic_modifiers & MODIFIER_SHIFT) { + platf::keyboard_update(PlatformInput::getInstance(), VKEY_SHIFT, false, flags); + } + if (synthetic_modifiers & MODIFIER_CTRL) { + platf::keyboard_update(PlatformInput::getInstance(), VKEY_CONTROL, false, flags); + } + if (synthetic_modifiers & MODIFIER_ALT) { + platf::keyboard_update(PlatformInput::getInstance(), VKEY_MENU, false, flags); + } + } + + platf::keyboard_update(PlatformInput::getInstance(), map_keycode(key_code), release, flags); + + if (!release) { + // Raise any synthetic modifier keys we pressed + if (synthetic_modifiers & MODIFIER_SHIFT) { + platf::keyboard_update(PlatformInput::getInstance(), VKEY_SHIFT, true, flags); + } + if (synthetic_modifiers & MODIFIER_CTRL) { + platf::keyboard_update(PlatformInput::getInstance(), VKEY_CONTROL, true, flags); + } + if (synthetic_modifiers & MODIFIER_ALT) { + platf::keyboard_update(PlatformInput::getInstance(), VKEY_MENU, true, flags); + } + } + } + + void + repeat_key(uint16_t key_code, uint8_t flags, uint8_t synthetic_modifiers) { + // If key no longer pressed, stop repeating + if (!key_press[make_kpid(key_code, flags)]) { + key_press_repeat_id = nullptr; + return; + } + + send_key_and_modifiers(key_code, false, flags, synthetic_modifiers); + + key_press_repeat_id = task_pool.pushDelayed(repeat_key, config::input.key_repeat_period, key_code, flags, synthetic_modifiers).task_id; + } + } // namespace + + void + print(PNV_KEYBOARD_PACKET packet) { + BOOST_LOG(debug) + << "--begin keyboard packet--"sv << std::endl + << "keyAction ["sv << util::hex(packet->header.magic).to_string_view() << ']' << std::endl + << "keyCode ["sv << util::hex(packet->keyCode).to_string_view() << ']' << std::endl + << "modifiers ["sv << util::hex(packet->modifiers).to_string_view() << ']' << std::endl + << "flags ["sv << util::hex(packet->flags).to_string_view() << ']' << std::endl + << "--end keyboard packet--"sv; + } + + void + print(PNV_UNICODE_PACKET packet) { + std::string text(packet->text, util::endian::big(packet->header.size) - sizeof(packet->header.magic)); + BOOST_LOG(debug) + << "--begin unicode packet--"sv << std::endl + << "text ["sv << text << ']' << std::endl + << "--end unicode packet--"sv; + } + + void + passthrough(std::shared_ptr &input, PNV_KEYBOARD_PACKET packet) { + if (!config::input.keyboard) { + return; + } + + auto release = util::endian::little(packet->header.magic) == KEY_UP_EVENT_MAGIC; + auto keyCode = packet->keyCode & 0x00FF; + + // Set synthetic modifier flags if the keyboard packet is requesting modifier + // keys that are not current pressed. + uint8_t synthetic_modifiers = 0; + if (!release && !is_modifier(keyCode)) { + if (!(input->shortcutFlags & input_t::SHIFT) && (packet->modifiers & MODIFIER_SHIFT)) { + synthetic_modifiers |= MODIFIER_SHIFT; + } + if (!(input->shortcutFlags & input_t::CTRL) && (packet->modifiers & MODIFIER_CTRL)) { + synthetic_modifiers |= MODIFIER_CTRL; + } + if (!(input->shortcutFlags & input_t::ALT) && (packet->modifiers & MODIFIER_ALT)) { + synthetic_modifiers |= MODIFIER_ALT; + } + } + + auto &pressed = key_press[make_kpid(keyCode, packet->flags)]; + if (!pressed) { + if (!release) { + // A new key has been pressed down, we need to check for key combo's + // If a key-combo has been pressed down, don't pass it through + if (input->shortcutFlags == input_t::SHORTCUT && apply_shortcut(keyCode) > 0) { + return; + } + + if (key_press_repeat_id) { + task_pool.cancel(key_press_repeat_id); + } + + if (config::input.key_repeat_delay.count() > 0) { + key_press_repeat_id = task_pool.pushDelayed(repeat_key, config::input.key_repeat_delay, keyCode, packet->flags, synthetic_modifiers).task_id; + } + } + else { + // Already released + return; + } + } + else if (!release) { + // Already pressed down key + return; + } + + pressed = !release; + + send_key_and_modifiers(keyCode, release, packet->flags, synthetic_modifiers); + + update_shortcutFlags(&input->shortcutFlags, map_keycode(keyCode), release); + } + + void + passthrough(PNV_UNICODE_PACKET packet) { + if (!config::input.keyboard) { + return; + } + + auto size = util::endian::big(packet->header.size) - sizeof(packet->header.magic); + platf::unicode(PlatformInput::getInstance(), packet->text, size); + } + + void + reset(platf::input_t &platf_input) { + for (auto &kp : key_press) { + if (!kp.second) { + // already released + continue; + } + platf::keyboard_update(platf_input, vk_from_kpid(kp.first) & 0x00FF, true, flags_from_kpid(kp.first)); + key_press[kp.first] = false; + } + } + + void + cancel() { + task_pool.cancel(key_press_repeat_id); + } +} // namespace input::keyboard diff --git a/src/input/keyboard.h b/src/input/keyboard.h new file mode 100644 index 00000000000..a79dd54ba05 --- /dev/null +++ b/src/input/keyboard.h @@ -0,0 +1,63 @@ +/** + * @file src/input/keyboard.h + * @brief Declarations for common keyboard input. + */ +#pragma once + +#include +extern "C" { +#include +#include +} + +#include + +#include "src/input/common.h" +#include "src/platform/common.h" +#include "src/thread_safe.h" + +namespace input::keyboard { + typedef uint32_t key_press_id_t; + + /** + * @brief Prints a keyboard event packet. + * @param packet The keyboard event packet. + */ + void + print(PNV_KEYBOARD_PACKET packet); + + /** + * @brief Prints a unicode text packet. + * @param packet The unicode text packet. + */ + void + print(PNV_UNICODE_PACKET packet); + + /** + * @brief Called to pass a keyboard event to the platform backend. + * @param input The input context pointer. + * @param packet The keyboard event packet. + */ + void + passthrough(std::shared_ptr &input, PNV_KEYBOARD_PACKET packet); + + /** + * @brief Called to pass a unicode text message to the platform backend. + * @param packet The unicode text packet. + */ + void + passthrough(PNV_UNICODE_PACKET packet); + + /** + * @brief Resets the overall state of a keyboard in the platform backend. + * @param platf_input The input context reference. + */ + void + reset(platf::input_t &platf_input); + + /** + * @brief Cancel pending key presses in the platform backend task pool. + */ + void + cancel(); +} // namespace input::keyboard diff --git a/src/input/mouse.cpp b/src/input/mouse.cpp new file mode 100644 index 00000000000..5ce9d429356 --- /dev/null +++ b/src/input/mouse.cpp @@ -0,0 +1,294 @@ +/** + * @file src/input/mouse.cpp + * @brief Definitions for common mouse input. + */ +#include "mouse.h" + +#include "src/input/init.h" +#include "src/input/platform_input.h" +#include "src/input/processor.h" +#include "src/input/touch.h" + +namespace input::mouse { + +#define DISABLE_LEFT_BUTTON_DELAY ((thread_pool_util::ThreadPool::task_id_t) 0x01) +#define ENABLE_LEFT_BUTTON_DELAY nullptr + + static std::array mouse_press {}; + + void + print(PNV_REL_MOUSE_MOVE_PACKET packet) { + BOOST_LOG(debug) + << "--begin relative mouse move packet--"sv << std::endl + << "deltaX ["sv << util::endian::big(packet->deltaX) << ']' << std::endl + << "deltaY ["sv << util::endian::big(packet->deltaY) << ']' << std::endl + << "--end relative mouse move packet--"sv; + } + + void + print(PNV_ABS_MOUSE_MOVE_PACKET packet) { + BOOST_LOG(debug) + << "--begin absolute mouse move packet--"sv << std::endl + << "x ["sv << util::endian::big(packet->x) << ']' << std::endl + << "y ["sv << util::endian::big(packet->y) << ']' << std::endl + << "width ["sv << util::endian::big(packet->width) << ']' << std::endl + << "height ["sv << util::endian::big(packet->height) << ']' << std::endl + << "--end absolute mouse move packet--"sv; + } + + void + print(PNV_MOUSE_BUTTON_PACKET packet) { + BOOST_LOG(debug) + << "--begin mouse button packet--"sv << std::endl + << "action ["sv << util::hex(packet->header.magic).to_string_view() << ']' << std::endl + << "button ["sv << util::hex(packet->button).to_string_view() << ']' << std::endl + << "--end mouse button packet--"sv; + } + + void + print(PNV_SCROLL_PACKET packet) { + BOOST_LOG(debug) + << "--begin mouse scroll packet--"sv << std::endl + << "scrollAmt1 ["sv << util::endian::big(packet->scrollAmt1) << ']' << std::endl + << "--end mouse scroll packet--"sv; + } + + void + print(PSS_HSCROLL_PACKET packet) { + BOOST_LOG(debug) + << "--begin mouse hscroll packet--"sv << std::endl + << "scrollAmount ["sv << util::endian::big(packet->scrollAmount) << ']' << std::endl + << "--end mouse hscroll packet--"sv; + } + + void + passthrough(const std::shared_ptr &input, PNV_REL_MOUSE_MOVE_PACKET packet) { + if (!config::input.mouse) { + return; + } + + input->mouse_left_button_timeout = DISABLE_LEFT_BUTTON_DELAY; + platf::move_mouse(PlatformInput::getInstance(), util::endian::big(packet->deltaX), util::endian::big(packet->deltaY)); + } + + void + passthrough(std::shared_ptr &input, PNV_ABS_MOUSE_MOVE_PACKET packet) { + if (!config::input.mouse) { + return; + } + + if (input->mouse_left_button_timeout == DISABLE_LEFT_BUTTON_DELAY) { + input->mouse_left_button_timeout = ENABLE_LEFT_BUTTON_DELAY; + } + + float x = util::endian::big(packet->x); + float y = util::endian::big(packet->y); + + // Prevent divide by zero + // Don't expect it to happen, but just in case + if (!packet->width || !packet->height) { + BOOST_LOG(warning) << "Moonlight passed invalid dimensions"sv; + + return; + } + + auto width = (float) util::endian::big(packet->width); + auto height = (float) util::endian::big(packet->height); + + auto tpcoords = touch::client_to_touchport(input, { x, y }, { width, height }); + if (!tpcoords) { + return; + } + + auto &touch_port = input->touch_port; + platf::touch_port_t abs_port { + touch_port.offset_x, touch_port.offset_y, + touch_port.env_width, touch_port.env_height + }; + + platf::abs_mouse(PlatformInput::getInstance(), abs_port, tpcoords->first, tpcoords->second); + } + + void + passthrough(std::shared_ptr &input, PNV_MOUSE_BUTTON_PACKET packet) { + if (!config::input.mouse) { + return; + } + + auto release = util::endian::little(packet->header.magic) == MOUSE_BUTTON_UP_EVENT_MAGIC_GEN5; + auto button = util::endian::big(packet->button); + if (button > 0 && button < mouse_press.size()) { + if (mouse_press[button] != release) { + // button state is already what we want + return; + } + + mouse_press[button] = !release; + } + + /** + * When Moonlight sends mouse input through absolute coordinates, + * it's possible that BUTTON_RIGHT is pressed down immediately after releasing BUTTON_LEFT. + * As a result, Sunshine will left-click on hyperlinks in the browser before right-clicking + * + * This can be solved by delaying BUTTON_LEFT, however, any delay on input is undesirable during gaming + * As a compromise, Sunshine will only put delays on BUTTON_LEFT when + * absolute mouse coordinates have been sent. + * + * Try to make sure BUTTON_RIGHT gets called before BUTTON_LEFT is released. + * + * input->mouse_left_button_timeout can only be nullptr + * when the last mouse coordinates were absolute + */ + if (button == BUTTON_LEFT && release && !input->mouse_left_button_timeout) { + auto f = [=]() { + auto left_released = mouse_press[BUTTON_LEFT]; + if (left_released) { + // Already released left button + return; + } + platf::button_mouse(PlatformInput::getInstance(), BUTTON_LEFT, release); + + mouse_press[BUTTON_LEFT] = false; + input->mouse_left_button_timeout = nullptr; + }; + + input->mouse_left_button_timeout = task_pool.pushDelayed(std::move(f), 10ms).task_id; + + return; + } + if ( + button == BUTTON_RIGHT && !release && + input->mouse_left_button_timeout > DISABLE_LEFT_BUTTON_DELAY) { + platf::button_mouse(PlatformInput::getInstance(), BUTTON_RIGHT, false); + platf::button_mouse(PlatformInput::getInstance(), BUTTON_RIGHT, true); + + mouse_press[BUTTON_RIGHT] = false; + + return; + } + + platf::button_mouse(PlatformInput::getInstance(), button, release); + } + + void + passthrough(std::shared_ptr &input, PNV_SCROLL_PACKET packet) { + if (!config::input.mouse) { + return; + } + + if (config::input.high_resolution_scrolling) { + platf::scroll(PlatformInput::getInstance(), util::endian::big(packet->scrollAmt1)); + } + else { + input->accumulated_vscroll_delta += util::endian::big(packet->scrollAmt1); + if (auto full_ticks = input->accumulated_vscroll_delta / WHEEL_DELTA) { + // Send any full ticks that have accumulated and store the rest + platf::scroll(PlatformInput::getInstance(), full_ticks * WHEEL_DELTA); + input->accumulated_vscroll_delta -= full_ticks * WHEEL_DELTA; + } + } + } + + void + passthrough(std::shared_ptr &input, PSS_HSCROLL_PACKET packet) { + if (!config::input.mouse) { + return; + } + + if (config::input.high_resolution_scrolling) { + platf::hscroll(PlatformInput::getInstance(), util::endian::big(packet->scrollAmount)); + } + else { + input->accumulated_hscroll_delta += util::endian::big(packet->scrollAmount); + if (auto full_ticks = input->accumulated_hscroll_delta / WHEEL_DELTA) { + // Send any full ticks that have accumulated and store the rest + platf::hscroll(PlatformInput::getInstance(), full_ticks * WHEEL_DELTA); + input->accumulated_hscroll_delta -= full_ticks * WHEEL_DELTA; + } + } + } + + batch_result_e + batch(PNV_REL_MOUSE_MOVE_PACKET dest, PNV_REL_MOUSE_MOVE_PACKET src) { + short deltaX, deltaY; + + // Batching is safe as long as the result doesn't overflow a 16-bit integer + if (!__builtin_add_overflow(util::endian::big(dest->deltaX), util::endian::big(src->deltaX), &deltaX)) { + return batch_result_e::terminate_batch; + } + if (!__builtin_add_overflow(util::endian::big(dest->deltaY), util::endian::big(src->deltaY), &deltaY)) { + return batch_result_e::terminate_batch; + } + + // Take the sum of deltas + dest->deltaX = util::endian::big(deltaX); + dest->deltaY = util::endian::big(deltaY); + return batch_result_e::batched; + } + + batch_result_e + batch(PNV_ABS_MOUSE_MOVE_PACKET dest, PNV_ABS_MOUSE_MOVE_PACKET src) { + // Batching must only happen if the reference width and height don't change + if (dest->width != src->width || dest->height != src->height) { + return batch_result_e::terminate_batch; + } + + // Take the latest absolute position + *dest = *src; + return batch_result_e::batched; + } + + batch_result_e + batch(PNV_SCROLL_PACKET dest, PNV_SCROLL_PACKET src) { + short scrollAmt; + + // Batching is safe as long as the result doesn't overflow a 16-bit integer + if (!__builtin_add_overflow(util::endian::big(dest->scrollAmt1), util::endian::big(src->scrollAmt1), &scrollAmt)) { + return batch_result_e::terminate_batch; + } + + // Take the sum of delta + dest->scrollAmt1 = util::endian::big(scrollAmt); + dest->scrollAmt2 = util::endian::big(scrollAmt); + return batch_result_e::batched; + } + + batch_result_e + batch(PSS_HSCROLL_PACKET dest, PSS_HSCROLL_PACKET src) { + short scrollAmt; + + // Batching is safe as long as the result doesn't overflow a 16-bit integer + if (!__builtin_add_overflow(util::endian::big(dest->scrollAmount), util::endian::big(src->scrollAmount), &scrollAmt)) { + return batch_result_e::terminate_batch; + } + + // Take the sum of delta + dest->scrollAmount = util::endian::big(scrollAmt); + return batch_result_e::batched; + } + + void + reset(platf::input_t &platf_input) { + for (int x = 0; x < mouse_press.size(); ++x) { + if (mouse_press[x]) { + platf::button_mouse(platf_input, x, true); + mouse_press[x] = false; + } + } + } + + void + force_frame_move(platf::input_t &platf_input) { + task_pool.pushDelayed([&platf_input]() { + platf::move_mouse(platf_input, 1, 1); + platf::move_mouse(platf_input, -1, -1); + }, + 100ms); + } + + void + cancel(const std::shared_ptr &input) { + task_pool.cancel(input->mouse_left_button_timeout); + } +} // namespace input::mouse diff --git a/src/input/mouse.h b/src/input/mouse.h new file mode 100644 index 00000000000..500dbd37528 --- /dev/null +++ b/src/input/mouse.h @@ -0,0 +1,155 @@ +/** + * @file src/input/mouse.h + * @brief Declarations for common mouse input. + */ +#pragma once + +#include +extern "C" { +#include +#include +} + +#include + +#include "src/input/common.h" +#include "src/platform/common.h" +#include "src/thread_safe.h" + +namespace input::mouse { +// Win32 WHEEL_DELTA constant +#ifndef WHEEL_DELTA + #define WHEEL_DELTA 120 +#endif + + /** + * @brief Prints a relative mouse movement event (x, y) packet. + * @param packet The move packet. + */ + void + print(PNV_REL_MOUSE_MOVE_PACKET packet); + + /** + * @brief Prints an absolute mouse movement event (x, y) packet. + * @param packet The move packet. + */ + void + print(PNV_ABS_MOUSE_MOVE_PACKET packet); + + /** + * @brief Prints a mouse button event packet. + * @param packet The button packet. + */ + void + print(PNV_MOUSE_BUTTON_PACKET packet); + + /** + * @brief Prints a vertical mouse scroll event packet. + * @param packet The scroll packet. + */ + void + print(PNV_SCROLL_PACKET packet); + + /** + * @brief Prints a horizontal mouse scroll event packet. + * @param packet The scroll packet. + */ + void + print(PSS_HSCROLL_PACKET packet); + + /** + * @brief Called to pass a relative mouse movement event (dx, dy) to the platform backend. + * @param input The input context pointer. + * @param packet The move event packet. + */ + void + passthrough(const std::shared_ptr &input, PNV_REL_MOUSE_MOVE_PACKET packet); + + /** + * @brief Called to pass an absolute mouse movement event (x, y) to the platform backend. + * @param input The input context pointer. + * @param packet The move event packet. + */ + void + passthrough(std::shared_ptr &input, PNV_ABS_MOUSE_MOVE_PACKET packet); + + /** + * @brief Called to pass a button event message to the platform backend. + * @param input The input context pointer. + * @param packet The button event packet. + */ + void + passthrough(std::shared_ptr &input, PNV_MOUSE_BUTTON_PACKET packet); + + /** + * @brief Called to pass a vertical scroll message the platform backend. + * @param input The input context pointer. + * @param packet The scroll packet. + */ + void + passthrough(std::shared_ptr &input, PNV_SCROLL_PACKET packet); + + /** + * @brief Called to pass a horizontal scroll message the platform backend. + * @param input The input context pointer. + * @param packet The scroll packet. + */ + void + passthrough(std::shared_ptr &input, PSS_HSCROLL_PACKET packet); + + /** + * @brief Batch two relative mouse messages. + * @param dest The original packet to batch into. + * @param src A later packet to attempt to batch. + * @return The status of the batching operation. + */ + batch_result_e + batch(PNV_REL_MOUSE_MOVE_PACKET dest, PNV_REL_MOUSE_MOVE_PACKET src); + + /** + * @brief Batch two absolute mouse messages. + * @param dest The original packet to batch into. + * @param src A later packet to attempt to batch. + * @return The status of the batching operation. + */ + batch_result_e + batch(PNV_ABS_MOUSE_MOVE_PACKET dest, PNV_ABS_MOUSE_MOVE_PACKET src); + + /** + * @brief Batch two vertical scroll messages. + * @param dest The original packet to batch into. + * @param src A later packet to attempt to batch. + * @return The status of the batching operation. + */ + batch_result_e + batch(PNV_SCROLL_PACKET dest, PNV_SCROLL_PACKET src); + + /** + * @brief Batch two horizontal scroll messages. + * @param dest The original packet to batch into. + * @param src A later packet to attempt to batch. + * @return The status of the batching operation. + */ + batch_result_e + batch(PSS_HSCROLL_PACKET dest, PSS_HSCROLL_PACKET src); + + /** + * @brief Resets the overall state of a mouse in the platform backend. + * @param platf_input The input context reference. + */ + void + reset(platf::input_t &platf_input); + + /** + * @brief Move the mouse slightly to force a video frame render + * @param platf_input + */ + void + force_frame_move(platf::input_t &platf_input); + + /** + * @brief Cancel pending mouse events in the platform backend task pool. + */ + void + cancel(const std::shared_ptr &input); +} // namespace input::mouse diff --git a/src/input/pen.cpp b/src/input/pen.cpp new file mode 100644 index 00000000000..e5b15fbe51b --- /dev/null +++ b/src/input/pen.cpp @@ -0,0 +1,134 @@ +/** + * @file src/input/pen.cpp + * @brief Definitions for common pen input. + */ +#include +extern "C" { +#include +#include +} + +#include +#include +#include +#include +#include +#include + +#include "src/config.h" +#include "src/globals.h" +#include "src/logging.h" +#include "src/platform/common.h" +#include "src/thread_pool.h" +#include "src/utility.h" + +#include "src/input/common.h" +#include "src/input/init.h" +#include "src/input/pen.h" +#include "src/input/processor.h" +#include "src/input/touch.h" +#include + +using namespace std::literals; + +namespace input::pen { + void + print(PSS_PEN_PACKET packet) { + BOOST_LOG(debug) + << "--begin pen packet--"sv << std::endl + << "eventType ["sv << util::hex(packet->eventType).to_string_view() << ']' << std::endl + << "toolType ["sv << util::hex(packet->toolType).to_string_view() << ']' << std::endl + << "penButtons ["sv << util::hex(packet->penButtons).to_string_view() << ']' << std::endl + << "x ["sv << from_netfloat(packet->x) << ']' << std::endl + << "y ["sv << from_netfloat(packet->y) << ']' << std::endl + << "pressureOrDistance ["sv << from_netfloat(packet->pressureOrDistance) << ']' << std::endl + << "contactAreaMajor ["sv << from_netfloat(packet->contactAreaMajor) << ']' << std::endl + << "contactAreaMinor ["sv << from_netfloat(packet->contactAreaMinor) << ']' << std::endl + << "rotation ["sv << (uint32_t) packet->rotation << ']' << std::endl + << "tilt ["sv << (uint32_t) packet->tilt << ']' << std::endl + << "--end pen packet--"sv; + } + + void + passthrough(std::shared_ptr &input, PSS_PEN_PACKET packet) { + if (!config::input.mouse) { + return; + } + + // Convert the client normalized coordinates to touchport coordinates + auto coords = touch::client_to_touchport(input, + { from_clamped_netfloat(packet->x, 0.0f, 1.0f) * 65535.f, + from_clamped_netfloat(packet->y, 0.0f, 1.0f) * 65535.f }, + { 65535.f, 65535.f }); + if (!coords) { + return; + } + + auto &touch_port = input->touch_port; + platf::touch_port_t abs_port { + touch_port.offset_x, touch_port.offset_y, + touch_port.env_width, touch_port.env_height + }; + + // Renormalize the coordinates + coords->first /= abs_port.width; + coords->second /= abs_port.height; + + // Normalize rotation value to 0-359 degree range + auto rotation = util::endian::little(packet->rotation); + if (rotation != LI_ROT_UNKNOWN) { + rotation %= 360; + } + + // Normalize the contact area based on the touchport + auto contact_area = scale_client_contact_area( + { from_clamped_netfloat(packet->contactAreaMajor, 0.0f, 1.0f) * 65535.f, + from_clamped_netfloat(packet->contactAreaMinor, 0.0f, 1.0f) * 65535.f }, + rotation, + { abs_port.width / 65535.f, abs_port.height / 65535.f }); + + platf::pen_input_t pen { + packet->eventType, + packet->toolType, + packet->penButtons, + packet->tilt, + rotation, + coords->first, + coords->second, + from_clamped_netfloat(packet->pressureOrDistance, 0.0f, 1.0f), + contact_area.first, + contact_area.second, + }; + + platf::pen_update(input->client_context.get(), abs_port, pen); + } + + batch_result_e + batch(PSS_PEN_PACKET dest, PSS_PEN_PACKET src) { + // Only batch hover or move events + if (dest->eventType != LI_TOUCH_EVENT_MOVE && + dest->eventType != LI_TOUCH_EVENT_HOVER) { + return batch_result_e::terminate_batch; + } + + // Batched events must be the same type + if (dest->eventType != src->eventType) { + return batch_result_e::terminate_batch; + } + + // Do not allow batching if the button state changes + if (dest->penButtons != src->penButtons) { + return batch_result_e::terminate_batch; + } + + // Do not batch beyond tool changes + if (dest->toolType != src->toolType) { + return batch_result_e::terminate_batch; + } + + // Take the latest state + *dest = *src; + return batch_result_e::batched; + } + +} // namespace input::pen diff --git a/src/input/pen.h b/src/input/pen.h new file mode 100644 index 00000000000..5bc9928d4d2 --- /dev/null +++ b/src/input/pen.h @@ -0,0 +1,43 @@ +/** + * @file src/input/pen.h + * @brief Declarations for common pen input. + */ +#pragma once + +#include +extern "C" { +#include +#include +} + +#include + +#include "src/input/common.h" +#include "src/platform/common.h" +#include "src/thread_safe.h" + +namespace input::pen { + /** + * @brief Prints a pen packet. + * @param packet The pen packet. + */ + void + print(PSS_PEN_PACKET packet); + + /** + * @brief Called to pass a pen message to the platform backend. + * @param input The input context pointer. + * @param packet The pen packet. + */ + void + passthrough(std::shared_ptr &input, PSS_PEN_PACKET packet); + + /** + * @brief Batch two pen messages. + * @param dest The original packet to batch into. + * @param src A later packet to attempt to batch. + * @return The status of the batching operation. + */ + batch_result_e + batch(PSS_PEN_PACKET dest, PSS_PEN_PACKET src); +} // namespace input::pen diff --git a/src/input/platform_input.cpp b/src/input/platform_input.cpp new file mode 100644 index 00000000000..5a679817706 --- /dev/null +++ b/src/input/platform_input.cpp @@ -0,0 +1,12 @@ +/** + * @file src/input/platform_input.cpp + * @brief Definitions for common platform input. + */ +#include "src/input/platform_input.h" + +platf::input_t PlatformInput::_instance; + +platf::input_t & +PlatformInput::getInstance() { + return _instance; +} diff --git a/src/input/platform_input.h b/src/input/platform_input.h new file mode 100644 index 00000000000..317d43769ff --- /dev/null +++ b/src/input/platform_input.h @@ -0,0 +1,27 @@ +/** + * @file src/input/platform_input.h + * @brief Declarations for common platform input singleton. + */ +#pragma once + +#include "src/platform/common.h" + +/** + * Singleton class to hold a reference to the platform input. + * + * We used to have a single global instance of the platform input, declared in the input namespace, + * but this caused issues with static initialization, while using it from many namespaces, + * so we moved it to it's own singleton class. + * + * @todo Improve the whole input layer so we can easily initialize via a factory, DI, whatever and remove this. + */ +class PlatformInput { +public: + static platf::input_t _instance; + + static platf::input_t & + getInstance(); + +private: + PlatformInput() = default; +}; diff --git a/src/input/processor.cpp b/src/input/processor.cpp new file mode 100644 index 00000000000..72475487f59 --- /dev/null +++ b/src/input/processor.cpp @@ -0,0 +1,294 @@ +/** + * @file src/input/processor.cpp + * @brief Definitions for common processor input. + */ +// define uint32_t for + +#include "platform_input.h" + +#include +extern "C" { +#include +#include +} + +#include +#include +#include +#include +#include +#include + +#include "src/input/common.h" +#include "src/input/gamepad.h" +#include "src/input/init.h" +#include "src/input/keyboard.h" +#include "src/input/mouse.h" +#include "src/input/pen.h" +#include "src/input/processor.h" +#include "src/input/touch.h" + +#include "src/config.h" +#include "src/globals.h" +#include "src/logging.h" +#include "src/platform/common.h" +#include "src/thread_pool.h" +#include "src/utility.h" + +#include + +using namespace std::literals; + +namespace input { + + namespace { + /** + * @brief Batch two input messages. + * @param dest The original packet to batch into. + * @param src A later packet to attempt to batch. + * @return The status of the batching operation. + */ + batch_result_e + batch(PNV_INPUT_HEADER dest, PNV_INPUT_HEADER src) { + // We can only batch if the packet types are the same + if (dest->magic != src->magic) { + return batch_result_e::terminate_batch; + } + + // We can only batch certain message types + switch (util::endian::little(dest->magic)) { + case MOUSE_MOVE_REL_MAGIC_GEN5: + return mouse::batch((PNV_REL_MOUSE_MOVE_PACKET) dest, (PNV_REL_MOUSE_MOVE_PACKET) src); + case MOUSE_MOVE_ABS_MAGIC: + return mouse::batch((PNV_ABS_MOUSE_MOVE_PACKET) dest, (PNV_ABS_MOUSE_MOVE_PACKET) src); + case SCROLL_MAGIC_GEN5: + return mouse::batch((PNV_SCROLL_PACKET) dest, (PNV_SCROLL_PACKET) src); + case SS_HSCROLL_MAGIC: + return mouse::batch((PSS_HSCROLL_PACKET) dest, (PSS_HSCROLL_PACKET) src); + case MULTI_CONTROLLER_MAGIC_GEN5: + return gamepad::batch((PNV_MULTI_CONTROLLER_PACKET) dest, (PNV_MULTI_CONTROLLER_PACKET) src); + case SS_TOUCH_MAGIC: + return touch::batch((PSS_TOUCH_PACKET) dest, (PSS_TOUCH_PACKET) src); + case SS_PEN_MAGIC: + return pen::batch((PSS_PEN_PACKET) dest, (PSS_PEN_PACKET) src); + case SS_CONTROLLER_TOUCH_MAGIC: + return gamepad::batch((PSS_CONTROLLER_TOUCH_PACKET) dest, (PSS_CONTROLLER_TOUCH_PACKET) src); + case SS_CONTROLLER_MOTION_MAGIC: + return gamepad::batch((PSS_CONTROLLER_MOTION_PACKET) dest, (PSS_CONTROLLER_MOTION_PACKET) src); + default: + // Not a batchable message type + return batch_result_e::terminate_batch; + } + } + + /** + * @brief Called on a thread pool thread to process an input message. + * @param input The input context pointer. + */ + void + passthrough_next_message(std::shared_ptr input) { + // 'entry' backs the 'payload' pointer, so they must remain in scope together + std::vector entry; + PNV_INPUT_HEADER payload; + + // Lock the input queue while batching, but release it before sending + // the input to the OS. This avoids potentially lengthy lock contention + // in the control stream thread while input is being processed by the OS. + { + std::lock_guard lg(input->input_queue_lock); + + // If all entries have already been processed, nothing to do + if (input->input_queue.empty()) { + return; + } + + // Pop off the first entry, which we will send + entry = input->input_queue.front(); + payload = (PNV_INPUT_HEADER) entry.data(); + input->input_queue.pop_front(); + + // Try to batch with remaining items on the queue + auto i = input->input_queue.begin(); + while (i != input->input_queue.end()) { + auto batchable_entry = *i; + auto batchable_payload = (PNV_INPUT_HEADER) batchable_entry.data(); + + auto batch_result = batch(payload, batchable_payload); + if (batch_result == batch_result_e::terminate_batch) { + // Stop batching + break; + } + else if (batch_result == batch_result_e::batched) { + // Erase this entry since it was batched + i = input->input_queue.erase(i); + } + else { + // We couldn't batch this entry, but try to batch later entries. + i++; + } + } + } + + // Print the final input packet + input::print((void *) payload); + + // Send the batched input to the OS + switch (util::endian::little(payload->magic)) { + case MOUSE_MOVE_REL_MAGIC_GEN5: + mouse::passthrough(input, (PNV_REL_MOUSE_MOVE_PACKET) payload); + break; + case MOUSE_MOVE_ABS_MAGIC: + mouse::passthrough(input, (PNV_ABS_MOUSE_MOVE_PACKET) payload); + break; + case MOUSE_BUTTON_DOWN_EVENT_MAGIC_GEN5: + case MOUSE_BUTTON_UP_EVENT_MAGIC_GEN5: + mouse::passthrough(input, (PNV_MOUSE_BUTTON_PACKET) payload); + break; + case SCROLL_MAGIC_GEN5: + mouse::passthrough(input, (PNV_SCROLL_PACKET) payload); + break; + case SS_HSCROLL_MAGIC: + mouse::passthrough(input, (PSS_HSCROLL_PACKET) payload); + break; + case KEY_DOWN_EVENT_MAGIC: + case KEY_UP_EVENT_MAGIC: + keyboard::passthrough(input, (PNV_KEYBOARD_PACKET) payload); + break; + case UTF8_TEXT_EVENT_MAGIC: + keyboard::passthrough((PNV_UNICODE_PACKET) payload); + break; + case MULTI_CONTROLLER_MAGIC_GEN5: + gamepad::passthrough(input, (PNV_MULTI_CONTROLLER_PACKET) payload); + break; + case SS_TOUCH_MAGIC: + touch::passthrough(input, (PSS_TOUCH_PACKET) payload); + break; + case SS_PEN_MAGIC: + pen::passthrough(input, (PSS_PEN_PACKET) payload); + break; + case SS_CONTROLLER_ARRIVAL_MAGIC: + gamepad::passthrough(input, (PSS_CONTROLLER_ARRIVAL_PACKET) payload); + break; + case SS_CONTROLLER_TOUCH_MAGIC: + gamepad::passthrough(input, (PSS_CONTROLLER_TOUCH_PACKET) payload); + break; + case SS_CONTROLLER_MOTION_MAGIC: + gamepad::passthrough(input, (PSS_CONTROLLER_MOTION_PACKET) payload); + break; + case SS_CONTROLLER_BATTERY_MAGIC: + gamepad::passthrough(input, (PSS_CONTROLLER_BATTERY_PACKET) payload); + break; + } + } + } // namespace + + void + print(void *payload) { + const auto header = static_cast(payload); + + switch (util::endian::little(header->magic)) { + case MOUSE_MOVE_REL_MAGIC_GEN5: + mouse::print(static_cast(payload)); + break; + case MOUSE_MOVE_ABS_MAGIC: + mouse::print(static_cast(payload)); + break; + case MOUSE_BUTTON_DOWN_EVENT_MAGIC_GEN5: + case MOUSE_BUTTON_UP_EVENT_MAGIC_GEN5: + mouse::print(static_cast(payload)); + break; + case SCROLL_MAGIC_GEN5: + mouse::print(static_cast(payload)); + break; + case SS_HSCROLL_MAGIC: + mouse::print(static_cast(payload)); + break; + case KEY_DOWN_EVENT_MAGIC: + case KEY_UP_EVENT_MAGIC: + keyboard::print(static_cast(payload)); + break; + case UTF8_TEXT_EVENT_MAGIC: + keyboard::print(static_cast(payload)); + break; + case MULTI_CONTROLLER_MAGIC_GEN5: + gamepad::print(static_cast(payload)); + break; + case SS_TOUCH_MAGIC: + touch::print(static_cast(payload)); + break; + case SS_PEN_MAGIC: + pen::print(static_cast(payload)); + break; + case SS_CONTROLLER_ARRIVAL_MAGIC: + gamepad::print(static_cast(payload)); + break; + case SS_CONTROLLER_TOUCH_MAGIC: + gamepad::print(static_cast(payload)); + break; + case SS_CONTROLLER_MOTION_MAGIC: + gamepad::print(static_cast(payload)); + break; + case SS_CONTROLLER_BATTERY_MAGIC: + gamepad::print(static_cast(payload)); + break; + } + } + + void + passthrough(std::shared_ptr &input, std::vector &&input_data) { + { + std::lock_guard lg(input->input_queue_lock); + input->input_queue.push_back(std::move(input_data)); + } + task_pool.push(passthrough_next_message, input); + } + + void + reset(std::shared_ptr &input) { + keyboard::cancel(); + mouse::cancel(input); + + // Ensure input is synchronous, by using the task_pool + task_pool.push([]() { + mouse::reset(PlatformInput::getInstance()); + keyboard::reset(PlatformInput::getInstance()); + }); + } + + class deinit_t: public platf::deinit_t { + public: + ~deinit_t() override { + PlatformInput::getInstance().reset(); + } + }; + + [[nodiscard]] std::unique_ptr + init() { + PlatformInput::_instance = platf::input(); + + return std::make_unique(); + } + + bool + probe_gamepads() { + platf::input_t &input = PlatformInput::getInstance(); + const auto gamepads = platf::supported_gamepads(reinterpret_cast(&input)); + for (auto &gamepad : gamepads) { + if (gamepad.is_enabled && gamepad.name != "auto") { + return false; + } + } + return true; + } + + std::shared_ptr + alloc(safe::mail_t mail) { + auto input = std::make_shared( + mail->event(mail::touch_port), + mail->queue(mail::gamepad_feedback)); + + // Workaround to ensure new frames will be captured when a client connects + mouse::force_frame_move(PlatformInput::getInstance()); + return input; + } +} // namespace input diff --git a/src/input/processor.h b/src/input/processor.h new file mode 100644 index 00000000000..a13d0cfc7a7 --- /dev/null +++ b/src/input/processor.h @@ -0,0 +1,90 @@ +/** + * @file src/input/processor.h + * @brief Declarations for common processor input. + */ +#pragma once + +#include + +#include "src/input/common.h" +#include "src/input/init.h" +#include "src/platform/common.h" +#include "src/thread_safe.h" + +namespace input { + + /** + * @brief Retrieves the packet from the payload input and prints its contents. + * @param payload The payload context pointer. + */ + void + print(void *payload); + + /** + * @brief Resets all inputs overall state in the platform backend. + * @param input The input context pointer. + */ + void + reset(std::shared_ptr &input); + + /** + * @brief Called on the control stream thread to queue an input message. + * @param input The input context pointer. + * @param input_data The input message. + */ + void + passthrough(std::shared_ptr &input, std::vector &&input_data); + + [[nodiscard]] std::unique_ptr + init(); + + std::shared_ptr + alloc(safe::mail_t mail); + + bool + probe_gamepads(); + + struct input_t { + enum shortkey_e { + CTRL = 0x1, ///< Control/Command key + ALT = 0x2, ///< Alt key + SHIFT = 0x4, ///< Shift key + SHORTCUT = CTRL | ALT | SHIFT ///< Shortcut combo + }; + + input_t( + safe::mail_raw_t::event_t touch_port_event, + platf::feedback_queue_t feedback_queue): + shortcutFlags {}, + gamepads_orchestrator(std::make_shared().get()), + client_context { platf::allocate_client_input_context(PlatformInput::getInstance()) }, + touch_port_event { std::move(touch_port_event) }, + feedback_queue { std::move(feedback_queue) }, + mouse_left_button_timeout {}, + touch_port { { 0, 0, 0, 0 }, 0, 0, 1.0f }, + accumulated_vscroll_delta {}, + accumulated_hscroll_delta {} {} + + // Keep track of alt+ctrl+shift key combo + int shortcutFlags; + + gamepad_orchestrator *gamepads_orchestrator; + std::vector *gamepads = &gamepads_orchestrator->gamepads; + + std::unique_ptr client_context; + + safe::mail_raw_t::event_t touch_port_event; + platf::feedback_queue_t feedback_queue; + + std::list> input_queue; + std::mutex input_queue_lock; + + thread_pool_util::ThreadPool::task_id_t mouse_left_button_timeout; + + input::touch_port_t touch_port; + + int32_t accumulated_vscroll_delta; + int32_t accumulated_hscroll_delta; + }; + +} // namespace input diff --git a/src/input/touch.cpp b/src/input/touch.cpp new file mode 100644 index 00000000000..a399319ffc1 --- /dev/null +++ b/src/input/touch.cpp @@ -0,0 +1,132 @@ +/** + * @file src/input/touch.cpp + * @brief Definitions for common touch input. + */ +#include "src/input/touch.h" +#include "src/input/init.h" +#include "src/input/processor.h" + +namespace input::touch { + void + print(PSS_TOUCH_PACKET packet) { + BOOST_LOG(debug) + << "--begin touch packet--"sv << std::endl + << "eventType ["sv << util::hex(packet->eventType).to_string_view() << ']' << std::endl + << "pointerId ["sv << util::hex(packet->pointerId).to_string_view() << ']' << std::endl + << "x ["sv << from_netfloat(packet->x) << ']' << std::endl + << "y ["sv << from_netfloat(packet->y) << ']' << std::endl + << "pressureOrDistance ["sv << from_netfloat(packet->pressureOrDistance) << ']' << std::endl + << "contactAreaMajor ["sv << from_netfloat(packet->contactAreaMajor) << ']' << std::endl + << "contactAreaMinor ["sv << from_netfloat(packet->contactAreaMinor) << ']' << std::endl + << "rotation ["sv << (uint32_t) packet->rotation << ']' << std::endl + << "--end touch packet--"sv; + } + + std::optional> + client_to_touchport(std::shared_ptr &input, const std::pair &val, const std::pair &size) { + auto &touch_port_event = input->touch_port_event; + auto &touch_port = input->touch_port; + if (touch_port_event->peek()) { + touch_port = *touch_port_event->pop(); + } + if (!touch_port) { + BOOST_LOG(verbose) << "Ignoring early absolute input without a touch port"sv; + return std::nullopt; + } + + auto scalarX = touch_port.width / size.first; + auto scalarY = touch_port.height / size.second; + + float x = std::clamp(val.first, 0.0f, size.first) * scalarX; + float y = std::clamp(val.second, 0.0f, size.second) * scalarY; + + auto offsetX = touch_port.client_offsetX; + auto offsetY = touch_port.client_offsetY; + + x = std::clamp(x, offsetX, (size.first * scalarX) - offsetX); + y = std::clamp(y, offsetY, (size.second * scalarY) - offsetY); + + return std::pair { (x - offsetX) * touch_port.scalar_inv, (y - offsetY) * touch_port.scalar_inv }; + } + + void + passthrough(std::shared_ptr &input, PSS_TOUCH_PACKET packet) { + if (!config::input.mouse) { + return; + } + + // Convert the client normalized coordinates to touchport coordinates + auto coords = touch::client_to_touchport(input, + { from_clamped_netfloat(packet->x, 0.0f, 1.0f) * 65535.f, + from_clamped_netfloat(packet->y, 0.0f, 1.0f) * 65535.f }, + { 65535.f, 65535.f }); + if (!coords) { + return; + } + + auto &touch_port = input->touch_port; + platf::touch_port_t abs_port { + touch_port.offset_x, touch_port.offset_y, + touch_port.env_width, touch_port.env_height + }; + + // Renormalize the coordinates + coords->first /= abs_port.width; + coords->second /= abs_port.height; + + // Normalize rotation value to 0-359 degree range + auto rotation = util::endian::little(packet->rotation); + if (rotation != LI_ROT_UNKNOWN) { + rotation %= 360; + } + + // Normalize the contact area based on the touchport + auto contact_area = scale_client_contact_area( + { from_clamped_netfloat(packet->contactAreaMajor, 0.0f, 1.0f) * 65535.f, + from_clamped_netfloat(packet->contactAreaMinor, 0.0f, 1.0f) * 65535.f }, + rotation, + { abs_port.width / 65535.f, abs_port.height / 65535.f }); + + platf::touch_input_t touch { + packet->eventType, + rotation, + util::endian::little(packet->pointerId), + coords->first, + coords->second, + from_clamped_netfloat(packet->pressureOrDistance, 0.0f, 1.0f), + contact_area.first, + contact_area.second, + }; + + platf::touch_update(input->client_context.get(), abs_port, touch); + } + + batch_result_e + batch(PSS_TOUCH_PACKET dest, PSS_TOUCH_PACKET src) { + // Only batch hover or move events + if (dest->eventType != LI_TOUCH_EVENT_MOVE && + dest->eventType != LI_TOUCH_EVENT_HOVER) { + return batch_result_e::terminate_batch; + } + + // Don't batch beyond state changing events + if (src->eventType != LI_TOUCH_EVENT_MOVE && + src->eventType != LI_TOUCH_EVENT_HOVER) { + return batch_result_e::terminate_batch; + } + + // Batched events must be the same pointer ID + if (dest->pointerId != src->pointerId) { + return batch_result_e::not_batchable; + } + + // The pointer must be in the same state + if (dest->eventType != src->eventType) { + return batch_result_e::terminate_batch; + } + + // Take the latest state + *dest = *src; + return batch_result_e::batched; + } +} // namespace input::touch diff --git a/src/input/touch.h b/src/input/touch.h new file mode 100644 index 00000000000..3d3c5375ce6 --- /dev/null +++ b/src/input/touch.h @@ -0,0 +1,54 @@ +/** + * @file src/input/touch.h + * @brief Declarations for common touch input. + */ +#pragma once + +#include +extern "C" { +#include +#include +} + +#include + +#include "src/input/common.h" +#include "src/platform/common.h" +#include "src/thread_pool.h" + +namespace input::touch { + + /** + * @brief Prints a touch packet. + * @param packet The touch packet. + */ + void + print(PSS_TOUCH_PACKET packet); + + /** + * @brief Convert client coordinates on the specified surface into screen coordinates. + * @param input The input context. + * @param val The cartesian coordinate pair to convert. + * @param size The size of the client's surface containing the value. + * @return The host-relative coordinate pair if a touchport is available. + */ + std::optional> + client_to_touchport(std::shared_ptr &input, const std::pair &val, const std::pair &size); + + /** + * @brief Called to pass a touch message to the platform backend. + * @param input The input context pointer. + * @param packet The touch packet. + */ + void + passthrough(std::shared_ptr &input, PSS_TOUCH_PACKET packet); + + /** + * @brief Batch two touch messages. + * @param dest The original packet to batch into. + * @param src A later packet to attempt to batch. + * @return The status of the batching operation. + */ + batch_result_e + batch(PSS_TOUCH_PACKET dest, PSS_TOUCH_PACKET src); +} // namespace input::touch diff --git a/src/main.cpp b/src/main.cpp index 1a1d5ef24ef..249bb942bdb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,16 +9,16 @@ #include // local includes -#include "confighttp.h" #include "entry_handler.h" #include "globals.h" -#include "httpcommon.h" #include "logging.h" #include "main.h" -#include "nvhttp.h" #include "process.h" +#include "server/confighttp.h" +#include "server/httpcommon.h" +#include "server/nvhttp.h" +#include "server/upnp.h" #include "system_tray.h" -#include "upnp.h" #include "version.h" #include "video.h" diff --git a/src/platform/linux/input/legacy_input.cpp b/src/platform/linux/input/legacy_input.cpp index 3d894d47390..85290e482d7 100644 --- a/src/platform/linux/input/legacy_input.cpp +++ b/src/platform/linux/input/legacy_input.cpp @@ -26,7 +26,7 @@ extern "C" { #include #include "src/config.h" -#include "src/input.h" +#include "src/input/processor.h" #include "src/logging.h" #include "src/platform/common.h" #include "src/utility.h" diff --git a/src/platform/linux/publish.cpp b/src/platform/linux/publish.cpp index 29641411e50..fc3bdfa2080 100644 --- a/src/platform/linux/publish.cpp +++ b/src/platform/linux/publish.cpp @@ -7,9 +7,9 @@ #include "misc.h" #include "src/logging.h" -#include "src/network.h" -#include "src/nvhttp.h" #include "src/platform/common.h" +#include "src/server/network.h" +#include "src/server/nvhttp.h" #include "src/utility.h" using namespace std::literals; diff --git a/src/platform/macos/input.cpp b/src/platform/macos/input.cpp index 83c668eb20d..bc3b7e4daac 100644 --- a/src/platform/macos/input.cpp +++ b/src/platform/macos/input.cpp @@ -2,7 +2,8 @@ * @file src/platform/macos/input.cpp * @brief Definitions for macOS input handling. */ -#include "src/input.h" +#include "src/input/common.h" +#include "src/input/processor.h" #import #include diff --git a/src/platform/macos/publish.cpp b/src/platform/macos/publish.cpp index ec4f1f452dd..eee0307d089 100644 --- a/src/platform/macos/publish.cpp +++ b/src/platform/macos/publish.cpp @@ -6,9 +6,10 @@ #include #include "src/logging.h" -#include "src/network.h" -#include "src/nvhttp.h" #include "src/platform/common.h" +#include "src/server/network.h" +#include "src/server/nvhttp.h" +#include "src/utility.h" using namespace std::literals; diff --git a/src/platform/windows/publish.cpp b/src/platform/windows/publish.cpp index fe3352e2309..d247e49247b 100644 --- a/src/platform/windows/publish.cpp +++ b/src/platform/windows/publish.cpp @@ -14,9 +14,9 @@ #include "misc.h" #include "src/config.h" #include "src/logging.h" -#include "src/network.h" -#include "src/nvhttp.h" #include "src/platform/common.h" +#include "src/server/network.h" +#include "src/server/nvhttp.h" #include "src/thread_safe.h" #define _FN(x, ret, args) \ diff --git a/src/process.cpp b/src/process.cpp index 6ab6b31757b..61df32f0087 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -22,9 +22,9 @@ #include #include "config.h" -#include "crypto.h" #include "logging.h" #include "platform/common.h" +#include "server/crypto.h" #include "system_tray.h" #include "utility.h" diff --git a/src/process.h b/src/process.h index 29abaa88acc..0c764c1c85a 100644 --- a/src/process.h +++ b/src/process.h @@ -15,7 +15,7 @@ #include "config.h" #include "platform/common.h" -#include "rtsp.h" +#include "server/rtsp.h" #include "utility.h" namespace proc { diff --git a/src/confighttp.cpp b/src/server/confighttp.cpp similarity index 98% rename from src/confighttp.cpp rename to src/server/confighttp.cpp index 6d80220612c..c1fcfc63550 100644 --- a/src/confighttp.cpp +++ b/src/server/confighttp.cpp @@ -1,13 +1,11 @@ /** - * @file src/confighttp.cpp + * @file src/server/confighttp.cpp * @brief Definitions for the Web UI Config HTTP server. * * @todo Authentication, better handling of routes common to nvhttp, cleanup */ #define BOOST_BIND_GLOBAL_PLACEHOLDERS -#include "process.h" - #include #include @@ -25,19 +23,20 @@ #include #include -#include "config.h" -#include "confighttp.h" -#include "crypto.h" -#include "file_handler.h" -#include "globals.h" -#include "httpcommon.h" -#include "logging.h" -#include "network.h" -#include "nvhttp.h" -#include "platform/common.h" -#include "rtsp.h" -#include "utility.h" -#include "uuid.h" +#include "src/config.h" +#include "src/file_handler.h" +#include "src/globals.h" +#include "src/logging.h" +#include "src/platform/common.h" +#include "src/process.h" +#include "src/server/confighttp.h" +#include "src/server/crypto.h" +#include "src/server/httpcommon.h" +#include "src/server/network.h" +#include "src/server/nvhttp.h" +#include "src/server/rtsp.h" +#include "src/utility.h" +#include "src/uuid.h" #include "version.h" using namespace std::literals; diff --git a/src/confighttp.h b/src/server/confighttp.h similarity index 92% rename from src/confighttp.h rename to src/server/confighttp.h index db202cb665b..faf54e30fac 100644 --- a/src/confighttp.h +++ b/src/server/confighttp.h @@ -1,5 +1,5 @@ /** - * @file src/confighttp.h + * @file src/server/confighttp.h * @brief Declarations for the Web UI Config HTTP server. */ #pragma once @@ -7,7 +7,7 @@ #include #include -#include "thread_safe.h" +#include "src/thread_safe.h" #define WEB_DIR SUNSHINE_ASSETS_DIR "/web/" diff --git a/src/crypto.cpp b/src/server/crypto.cpp similarity index 97% rename from src/crypto.cpp rename to src/server/crypto.cpp index b16ab08ab37..619c952ab7a 100644 --- a/src/crypto.cpp +++ b/src/server/crypto.cpp @@ -1,15 +1,22 @@ /** - * @file src/crypto.cpp + * @file src/server/crypto.cpp * @brief Definitions for cryptography functions. */ -#include "crypto.h" #include +#include "src/server/crypto.h" + namespace crypto { using asn1_string_t = util::safe_ptr; - cert_chain_t::cert_chain_t(): - _certs {}, _cert_ctx { X509_STORE_CTX_new() } {} + /** + * @memberof crypto + */ + cert_chain_t:: + cert_chain_t(): + _certs {}, + _cert_ctx { X509_STORE_CTX_new() } {} + void cert_chain_t::add(x509_t &&cert) { x509_store_t x509_store { X509_STORE_new() }; @@ -17,6 +24,7 @@ namespace crypto { X509_STORE_add_cert(x509_store.get(), cert.get()); _certs.emplace_back(std::make_pair(std::move(cert), std::move(x509_store))); } + void cert_chain_t::clear() { _certs.clear(); @@ -309,13 +317,16 @@ namespace crypto { return update_outlen + final_outlen; } - ecb_t::ecb_t(const aes_t &key, bool padding): + ecb_t:: + ecb_t(const aes_t &key, bool padding): cipher_t { EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_new(), key, padding } {} - cbc_t::cbc_t(const aes_t &key, bool padding): + cbc_t:: + cbc_t(const aes_t &key, bool padding): cipher_t { nullptr, nullptr, key, padding } {} - gcm_t::gcm_t(const crypto::aes_t &key, bool padding): + gcm_t:: + gcm_t(const crypto::aes_t &key, bool padding): cipher_t { nullptr, nullptr, key, padding } {} } // namespace cipher diff --git a/src/crypto.h b/src/server/crypto.h similarity index 99% rename from src/crypto.h rename to src/server/crypto.h index 20651ecb1ae..2afcd138843 100644 --- a/src/crypto.h +++ b/src/server/crypto.h @@ -1,5 +1,5 @@ /** - * @file src/crypto.h + * @file src/server/crypto.h * @brief Declarations for cryptography functions. */ #pragma once @@ -10,7 +10,7 @@ #include #include -#include "utility.h" +#include "src/utility.h" namespace crypto { struct creds_t { diff --git a/src/httpcommon.cpp b/src/server/httpcommon.cpp similarity index 95% rename from src/httpcommon.cpp rename to src/server/httpcommon.cpp index 355ab6e9a92..e70a69501e3 100644 --- a/src/httpcommon.cpp +++ b/src/server/httpcommon.cpp @@ -1,11 +1,9 @@ /** - * @file src/httpcommon.cpp + * @file src/server/httpcommon.cpp * @brief Definitions for common HTTP. */ #define BOOST_BIND_GLOBAL_PLACEHOLDERS -#include "process.h" - #include #include @@ -20,17 +18,18 @@ #include #include -#include "config.h" -#include "crypto.h" -#include "file_handler.h" -#include "httpcommon.h" -#include "logging.h" -#include "network.h" -#include "nvhttp.h" -#include "platform/common.h" -#include "rtsp.h" -#include "utility.h" -#include "uuid.h" +#include "src/config.h" +#include "src/file_handler.h" +#include "src/logging.h" +#include "src/platform/common.h" +#include "src/process.h" +#include "src/server/crypto.h" +#include "src/server/httpcommon.h" +#include "src/server/network.h" +#include "src/server/nvhttp.h" +#include "src/server/rtsp.h" +#include "src/utility.h" +#include "src/uuid.h" namespace http { using namespace std::literals; diff --git a/src/httpcommon.h b/src/server/httpcommon.h similarity index 90% rename from src/httpcommon.h rename to src/server/httpcommon.h index 324ada03d23..a666fdb6fcb 100644 --- a/src/httpcommon.h +++ b/src/server/httpcommon.h @@ -1,11 +1,10 @@ /** - * @file src/httpcommon.h + * @file src/server/httpcommon.h * @brief Declarations for common HTTP. */ #pragma once -#include "network.h" -#include "thread_safe.h" +#include "src/server/network.h" namespace http { diff --git a/src/network.cpp b/src/server/network.cpp similarity index 97% rename from src/network.cpp rename to src/server/network.cpp index b7b18de88b0..d2e31740bc2 100644 --- a/src/network.cpp +++ b/src/server/network.cpp @@ -1,11 +1,11 @@ /** - * @file src/network.cpp + * @file src/server/network.cpp * @brief Definitions for networking related functions. */ -#include "network.h" -#include "config.h" -#include "logging.h" -#include "utility.h" +#include "src/server/network.h" +#include "src/config.h" +#include "src/logging.h" +#include "src/utility.h" #include #include diff --git a/src/network.h b/src/server/network.h similarity index 98% rename from src/network.h rename to src/server/network.h index ffc0b2d2ff4..01dd4b66b42 100644 --- a/src/network.h +++ b/src/server/network.h @@ -1,5 +1,5 @@ /** - * @file src/network.h + * @file src/server/network.h * @brief Declarations for networking related functions. */ #pragma once @@ -11,7 +11,7 @@ #include -#include "utility.h" +#include "src/utility.h" namespace net { void diff --git a/src/nvhttp.cpp b/src/server/nvhttp.cpp similarity index 98% rename from src/nvhttp.cpp rename to src/server/nvhttp.cpp index 085402ffaea..397003ec0dd 100644 --- a/src/nvhttp.cpp +++ b/src/server/nvhttp.cpp @@ -1,5 +1,5 @@ /** - * @file src/nvhttp.cpp + * @file src/server/nvhttp.cpp * @brief Definitions for the nvhttp (GameStream) server. */ // macros @@ -20,21 +20,21 @@ #include // local includes -#include "config.h" -#include "crypto.h" -#include "file_handler.h" -#include "globals.h" -#include "httpcommon.h" -#include "logging.h" -#include "network.h" -#include "nvhttp.h" -#include "platform/common.h" -#include "process.h" -#include "rtsp.h" -#include "system_tray.h" -#include "utility.h" -#include "uuid.h" -#include "video.h" +#include "src/config.h" +#include "src/file_handler.h" +#include "src/globals.h" +#include "src/logging.h" +#include "src/platform/common.h" +#include "src/process.h" +#include "src/server/crypto.h" +#include "src/server/httpcommon.h" +#include "src/server/network.h" +#include "src/server/nvhttp.h" +#include "src/server/rtsp.h" +#include "src/system_tray.h" +#include "src/utility.h" +#include "src/uuid.h" +#include "src/video.h" using namespace std::literals; namespace nvhttp { diff --git a/src/nvhttp.h b/src/server/nvhttp.h similarity index 97% rename from src/nvhttp.h rename to src/server/nvhttp.h index 1f8726c35e0..84b3c4b79b9 100644 --- a/src/nvhttp.h +++ b/src/server/nvhttp.h @@ -1,5 +1,5 @@ /** - * @file src/nvhttp.h + * @file src/server/nvhttp.h * @brief Declarations for the nvhttp (GameStream) server. */ // macros @@ -12,7 +12,7 @@ #include // local includes -#include "thread_safe.h" +#include "src/thread_safe.h" /** * @brief Contains all the functions and variables related to the nvhttp (GameStream) server. diff --git a/src/rtsp.cpp b/src/server/rtsp.cpp similarity index 99% rename from src/rtsp.cpp rename to src/server/rtsp.cpp index 3528c258115..b36b5a737c9 100644 --- a/src/rtsp.cpp +++ b/src/server/rtsp.cpp @@ -1,5 +1,5 @@ /** - * @file src/rtsp.cpp + * @file src/server/rtsp.cpp * @brief Definitions for RTSP streaming. */ #define BOOST_BIND_GLOBAL_PLACEHOLDERS @@ -16,15 +16,15 @@ extern "C" { #include #include -#include "config.h" -#include "globals.h" -#include "input.h" -#include "logging.h" -#include "network.h" -#include "rtsp.h" -#include "stream.h" -#include "sync.h" -#include "video.h" +#include "src/config.h" +#include "src/globals.h" +#include "src/input/processor.h" +#include "src/logging.h" +#include "src/server/network.h" +#include "src/server/rtsp.h" +#include "src/stream.h" +#include "src/sync.h" +#include "src/video.h" #include diff --git a/src/rtsp.h b/src/server/rtsp.h similarity index 92% rename from src/rtsp.h rename to src/server/rtsp.h index 910fc427eb4..2620b467585 100644 --- a/src/rtsp.h +++ b/src/server/rtsp.h @@ -1,13 +1,13 @@ /** - * @file src/rtsp.h + * @file src/server/rtsp.h * @brief Declarations for RTSP streaming. */ #pragma once #include -#include "crypto.h" -#include "thread_safe.h" +#include "src/server/crypto.h" +#include "src/thread_safe.h" namespace rtsp_stream { constexpr auto RTSP_SETUP_PORT = 21; diff --git a/src/upnp.cpp b/src/server/upnp.cpp similarity index 97% rename from src/upnp.cpp rename to src/server/upnp.cpp index 747e604a332..b094f03bd24 100644 --- a/src/upnp.cpp +++ b/src/server/upnp.cpp @@ -1,20 +1,20 @@ /** - * @file src/upnp.cpp + * @file src/server/upnp.cpp * @brief Definitions for UPnP port mapping. */ #include #include -#include "config.h" -#include "confighttp.h" -#include "globals.h" -#include "logging.h" -#include "network.h" -#include "nvhttp.h" -#include "rtsp.h" -#include "stream.h" -#include "upnp.h" -#include "utility.h" +#include "src/config.h" +#include "src/globals.h" +#include "src/logging.h" +#include "src/server/confighttp.h" +#include "src/server/network.h" +#include "src/server/nvhttp.h" +#include "src/server/rtsp.h" +#include "src/server/upnp.h" +#include "src/stream.h" +#include "src/utility.h" using namespace std::literals; diff --git a/src/upnp.h b/src/server/upnp.h similarity index 94% rename from src/upnp.h rename to src/server/upnp.h index 6f32f2e9d1e..d85f1ff0b8a 100644 --- a/src/upnp.h +++ b/src/server/upnp.h @@ -1,12 +1,12 @@ /** - * @file src/upnp.h + * @file src/server/upnp.h * @brief Declarations for UPnP port mapping. */ #pragma once #include -#include "platform/common.h" +#include "src/platform/common.h" /** * @brief UPnP port mapping. diff --git a/src/stream.cpp b/src/stream.cpp index 0f6b946347f..98d25fbe5f2 100644 --- a/src/stream.cpp +++ b/src/stream.cpp @@ -21,9 +21,9 @@ extern "C" { #include "config.h" #include "globals.h" -#include "input.h" +#include "input/processor.h" #include "logging.h" -#include "network.h" +#include "server/network.h" #include "stream.h" #include "sync.h" #include "system_tray.h" diff --git a/src/stream.h b/src/stream.h index 95d1e2d4e88..3e713147fb5 100644 --- a/src/stream.h +++ b/src/stream.h @@ -8,7 +8,7 @@ #include #include "audio.h" -#include "crypto.h" +#include "server/rtsp.h" #include "video.h" namespace stream { diff --git a/src/system_tray.cpp b/src/system_tray.cpp index e949ff57ac1..bbdb99b62cf 100644 --- a/src/system_tray.cpp +++ b/src/system_tray.cpp @@ -36,10 +36,10 @@ #include // local includes - #include "confighttp.h" #include "logging.h" #include "platform/common.h" #include "process.h" + #include "server/confighttp.h" #include "src/entry_handler.h" #include "version.h" diff --git a/src/video.cpp b/src/video.cpp index 908b7fa932c..ff49cfb32ae 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -19,7 +19,7 @@ extern "C" { #include "cbs.h" #include "config.h" #include "globals.h" -#include "input.h" +#include "input/processor.h" #include "logging.h" #include "nvenc/nvenc_base.h" #include "platform/common.h" diff --git a/src/video.h b/src/video.h index b46517c8897..390d41fca2d 100644 --- a/src/video.h +++ b/src/video.h @@ -4,7 +4,7 @@ */ #pragma once -#include "input.h" +#include "input/processor.h" #include "platform/common.h" #include "thread_safe.h" #include "video_colorspace.h" diff --git a/tests/unit/test_httpcommon.cpp b/tests/unit/test_httpcommon.cpp index c979304d215..5eff224b652 100644 --- a/tests/unit/test_httpcommon.cpp +++ b/tests/unit/test_httpcommon.cpp @@ -2,7 +2,7 @@ * @file tests/unit/test_httpcommon.cpp * @brief Test src/httpcommon.*. */ -#include +#include #include diff --git a/tests/unit/test_mouse.cpp b/tests/unit/test_mouse.cpp index d97cb9c1ec6..1c3b3a146cb 100644 --- a/tests/unit/test_mouse.cpp +++ b/tests/unit/test_mouse.cpp @@ -2,7 +2,7 @@ * @file tests/unit/test_mouse.cpp * @brief Test src/input.*. */ -#include +#include #include #include