diff --git a/docs/source/about/advanced_usage.rst b/docs/source/about/advanced_usage.rst index 088d9045c2..88e000594c 100644 --- a/docs/source/about/advanced_usage.rst +++ b/docs/source/about/advanced_usage.rst @@ -140,17 +140,18 @@ gamepad ===== =========== Value Description ===== =========== - x360 xbox 360 controller - ds4 dualshock controller (PS4) + auto Selected based on information from client + x360 Xbox 360 controller + ds4 DualShock 4 controller (PS4) ===== =========== **Default** - ``x360`` + ``auto`` **Example** .. code-block:: text - gamepad = x360 + gamepad = auto back_button_timeout ^^^^^^^^^^^^^^^^^^^ diff --git a/src/input.cpp b/src/input.cpp index 3de6c327dc..f12ff28e28 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -264,6 +264,21 @@ namespace input { << "--end controller 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; + } + void print(void *payload) { auto header = (PNV_INPUT_HEADER) payload; @@ -295,6 +310,9 @@ namespace input { case MULTI_CONTROLLER_MAGIC_GEN5: print((PNV_MULTI_CONTROLLER_PACKET) payload); break; + case SS_CONTROLLER_ARRIVAL_MAGIC: + print((PSS_CONTROLLER_ARRIVAL_PACKET) payload); + break; } } @@ -643,7 +661,7 @@ namespace input { return -1; } - if (platf::alloc_gamepad(platf_input, id, rumble_queue)) { + if (platf::alloc_gamepad(platf_input, id, {}, rumble_queue)) { free_id(gamepadMask, id); // allocating a gamepad failed: solution: ignore gamepads // The implementations of platf::alloc_gamepad already has logging @@ -658,6 +676,46 @@ namespace input { return 0; } + /** + * @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 >= gamepadMask.size()) { + // Invalid controller number + return; + } + + if (gamepadMask[packet->controllerNumber]) { + // There's already a gamepad in this slot + return; + } + + platf::gamepad_arrival_t arrival { + packet->controllerNumber, + packet->type, + util::endian::little(packet->capabilities), + util::endian::little(packet->supportedButtonFlags), + }; + + gamepadMask[packet->controllerNumber] = true; + input->active_gamepad_state |= (1 << packet->controllerNumber); + + // Allocate a new gamepad + if (platf::alloc_gamepad(platf_input, packet->controllerNumber, arrival, input->rumble_queue)) { + free_id(gamepadMask, packet->controllerNumber); + return; + } + + input->gamepads[packet->controllerNumber].id = packet->controllerNumber; + } + void passthrough(std::shared_ptr &input, PNV_MULTI_CONTROLLER_PACKET packet) { if (!config::input.controller) { @@ -1135,6 +1193,9 @@ namespace input { case MULTI_CONTROLLER_MAGIC_GEN5: passthrough(input, (PNV_MULTI_CONTROLLER_PACKET) payload); break; + case SS_CONTROLLER_ARRIVAL_MAGIC: + passthrough(input, (PSS_CONTROLLER_ARRIVAL_PACKET) payload); + break; } } diff --git a/src/platform/common.h b/src/platform/common.h index d9af3b1864..b291c707ab 100644 --- a/src/platform/common.h +++ b/src/platform/common.h @@ -170,6 +170,13 @@ namespace platf { std::int16_t rsY; }; + struct gamepad_arrival_t { + std::uint8_t gamepadNumber; + std::uint8_t type; + std::uint16_t capabilities; + std::uint32_t supportedButtons; + }; + class deinit_t { public: virtual ~deinit_t() = default; @@ -455,8 +462,16 @@ namespace platf { void unicode(input_t &input, char *utf8, int size); + /** + * @brief Creates a new virtual gamepad. + * @param input The input context. + * @param nr The assigned controller number. + * @param metadata Controller metadata from client (empty if none provided). + * @param rumble_queue The queue for posting rumble messages to the client. + * @return 0 on success. + */ int - alloc_gamepad(input_t &input, int nr, rumble_queue_t rumble_queue); + alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, rumble_queue_t rumble_queue); void free_gamepad(input_t &input, int nr); diff --git a/src/platform/linux/input.cpp b/src/platform/linux/input.cpp index 85980ef37b..0e96d1a6ca 100644 --- a/src/platform/linux/input.cpp +++ b/src/platform/linux/input.cpp @@ -770,8 +770,15 @@ namespace platf { return 0; } + /** + * @brief Creates a new virtual gamepad. + * @param nr The assigned controller number. + * @param metadata Controller metadata from client (empty if none provided). + * @param rumble_queue The queue for posting rumble messages to the client. + * @return 0 on success. + */ int - alloc_gamepad(int nr, rumble_queue_t &&rumble_queue) { + alloc_gamepad(int nr, const gamepad_arrival_t &metadata, rumble_queue_t &&rumble_queue) { TUPLE_2D_REF(input, gamepad_state, gamepads[nr]); int err = libevdev_uinput_create_from_device(gamepad_dev.get(), LIBEVDEV_UINPUT_OPEN_MANAGED, &input); @@ -1480,9 +1487,17 @@ namespace platf { keyboard_ev(kb, KEY_LEFTCTRL, 0); } + /** + * @brief Creates a new virtual gamepad. + * @param input The input context. + * @param nr The assigned controller number. + * @param metadata Controller metadata from client (empty if none provided). + * @param rumble_queue The queue for posting rumble messages to the client. + * @return 0 on success. + */ int - alloc_gamepad(input_t &input, int nr, rumble_queue_t rumble_queue) { - return ((input_raw_t *) input.get())->alloc_gamepad(nr, std::move(rumble_queue)); + alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, rumble_queue_t rumble_queue) { + return ((input_raw_t *) input.get())->alloc_gamepad(nr, metadata, std::move(rumble_queue)); } void diff --git a/src/platform/macos/input.cpp b/src/platform/macos/input.cpp index 78079e3f38..c5b20e58d8 100644 --- a/src/platform/macos/input.cpp +++ b/src/platform/macos/input.cpp @@ -288,8 +288,16 @@ const KeyCodeMap kKeyCodesMap[] = { BOOST_LOG(info) << "unicode: Unicode input not yet implemented for MacOS."sv; } + /** + * @brief Creates a new virtual gamepad. + * @param input The input context. + * @param nr The assigned controller number. + * @param metadata Controller metadata from client (empty if none provided). + * @param rumble_queue The queue for posting rumble messages to the client. + * @return 0 on success. + */ int - alloc_gamepad(input_t &input, int nr, rumble_queue_t rumble_queue) { + alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, rumble_queue_t rumble_queue) { BOOST_LOG(info) << "alloc_gamepad: Gamepad not yet implemented for MacOS."sv; return -1; } diff --git a/src/platform/windows/input.cpp b/src/platform/windows/input.cpp index 737fb9e3fb..f26c6c9e1c 100644 --- a/src/platform/windows/input.cpp +++ b/src/platform/windows/input.cpp @@ -26,15 +26,6 @@ namespace platf { using client_t = util::safe_ptr<_VIGEM_CLIENT_T, vigem_free>; using target_t = util::safe_ptr<_VIGEM_TARGET_T, vigem_target_free>; - static VIGEM_TARGET_TYPE - map(const std::string_view &gp) { - if (gp == "x360"sv) { - return Xbox360Wired; - } - - return DualShock4Wired; - } - void CALLBACK x360_notify( client_t::pointer client, @@ -423,15 +414,54 @@ namespace platf { } } + /** + * @brief Creates a new virtual gamepad. + * @param input The input context. + * @param nr The assigned controller number. + * @param metadata Controller metadata from client (empty if none provided). + * @param rumble_queue The queue for posting rumble messages to the client. + * @return 0 on success. + */ int - alloc_gamepad(input_t &input, int nr, rumble_queue_t rumble_queue) { + alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, rumble_queue_t rumble_queue) { auto raw = (input_raw_t *) input.get(); if (!raw->vigem) { return 0; } - return raw->vigem->alloc_gamepad_interal(nr, rumble_queue, map(config::input.gamepad)); + VIGEM_TARGET_TYPE selectedGamepadType; + + if (config::input.gamepad == "x360"sv) { + BOOST_LOG(info) << "Gamepad " << nr << " will be Xbox 360 controller (manual selection)"sv; + selectedGamepadType = Xbox360Wired; + } + else if (config::input.gamepad == "ps4"sv || config::input.gamepad == "ds4"sv) { + BOOST_LOG(info) << "Gamepad " << nr << " will be DualShock 4 controller (manual selection)"sv; + selectedGamepadType = DualShock4Wired; + } + else if (metadata.type == LI_CTYPE_PS) { + BOOST_LOG(info) << "Gamepad " << nr << " will be DualShock 4 controller (auto-selected by client-reported type)"sv; + selectedGamepadType = DualShock4Wired; + } + else if (metadata.type == LI_CTYPE_XBOX) { + BOOST_LOG(info) << "Gamepad " << nr << " will be Xbox 360 controller (auto-selected by client-reported type)"sv; + selectedGamepadType = Xbox360Wired; + } + else if (metadata.capabilities & (LI_CCAP_ACCEL | LI_CCAP_GYRO)) { + BOOST_LOG(info) << "Gamepad " << nr << " will be DualShock 4 controller (auto-selected by motion sensor presence)"sv; + selectedGamepadType = DualShock4Wired; + } + else if (metadata.capabilities & LI_CCAP_TOUCHPAD) { + BOOST_LOG(info) << "Gamepad " << nr << " will be DualShock 4 controller (auto-selected by touchpad presence)"sv; + selectedGamepadType = DualShock4Wired; + } + else { + BOOST_LOG(info) << "Gamepad " << nr << " will be Xbox 360 controller (default)"sv; + selectedGamepadType = Xbox360Wired; + } + + return raw->vigem->alloc_gamepad_interal(nr, rumble_queue, selectedGamepadType); } void @@ -591,11 +621,15 @@ namespace platf { delete input; } + /** + * @brief Gets the supported gamepads for this platform backend. + * @return Vector of gamepad type strings. + */ std::vector & supported_gamepads() { // ds4 == ps4 static std::vector gps { - "x360"sv, "ds4"sv, "ps4"sv + "auto"sv, "x360"sv, "ds4"sv, "ps4"sv }; return gps; diff --git a/src_assets/common/assets/web/config.html b/src_assets/common/assets/web/config.html index 4433d46353..1258c35e33 100644 --- a/src_assets/common/assets/web/config.html +++ b/src_assets/common/assets/web/config.html @@ -94,10 +94,11 @@

Configuration

-
Choose which type of gamepad to Emulate on the host
+
Choose which type of gamepad to emulate on the host
@@ -1029,7 +1030,7 @@

Configuration

"dwmflush": "enabled", "encoder": "", "fps": "[10,30,60,90,120]", - "gamepad": "x360", + "gamepad": "auto", "hevc_mode": 0, "key_rightalt_to_key_win": "disabled", "keyboard": "enabled",