diff --git a/src/input.cpp b/src/input.cpp index 7c9b867f..6f66f02c 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -722,7 +722,6 @@ namespace input { } platf::gamepad_arrival_t arrival { - packet->controllerNumber, packet->type, util::endian::little(packet->capabilities), util::endian::little(packet->supportedButtonFlags), @@ -734,7 +733,7 @@ namespace input { } // Allocate a new gamepad - if (platf::alloc_gamepad(platf_input, id, arrival, input->feedback_queue)) { + if (platf::alloc_gamepad(platf_input, { id, packet->controllerNumber }, arrival, input->feedback_queue)) { free_id(gamepadMask, id); return; } @@ -765,7 +764,7 @@ namespace input { } platf::gamepad_touch_t touch { - packet->controllerNumber, + { gamepad.id, packet->controllerNumber }, packet->eventType, util::endian::little(packet->pointerId), from_netfloat(packet->x), @@ -799,7 +798,7 @@ namespace input { } platf::gamepad_motion_t motion { - packet->controllerNumber, + { gamepad.id, packet->controllerNumber }, packet->motionType, from_netfloat(packet->x), from_netfloat(packet->y), @@ -832,7 +831,7 @@ namespace input { } platf::gamepad_battery_t battery { - packet->controllerNumber, + { gamepad.id, packet->controllerNumber }, packet->batteryState, packet->batteryPercentage }; @@ -862,7 +861,7 @@ namespace input { return; } - if (platf::alloc_gamepad(platf_input, id, {}, input->feedback_queue)) { + if (platf::alloc_gamepad(platf_input, { id, (uint8_t) packet->controllerNumber }, {}, input->feedback_queue)) { free_id(gamepadMask, id); return; } diff --git a/src/platform/common.h b/src/platform/common.h index eb1b45fd..1173afd5 100644 --- a/src/platform/common.h +++ b/src/platform/common.h @@ -236,15 +236,25 @@ namespace platf { std::int16_t rsY; }; + struct gamepad_id_t { + // The global index is used when looking up gamepads in the platform's + // gamepad array. It identifies gamepads uniquely among all clients. + int globalIndex; + + // The client-relative index is the controller number as reported by the + // client. It must be used when communicating back to the client via + // the input feedback queue. + std::uint8_t clientRelativeIndex; + }; + struct gamepad_arrival_t { - std::uint8_t gamepadNumber; std::uint8_t type; std::uint16_t capabilities; std::uint32_t supportedButtons; }; struct gamepad_touch_t { - std::uint8_t gamepadNumber; + gamepad_id_t id; std::uint8_t eventType; std::uint32_t pointerId; float x; @@ -253,7 +263,7 @@ namespace platf { }; struct gamepad_motion_t { - std::uint8_t gamepadNumber; + gamepad_id_t id; std::uint8_t motionType; // Accel: m/s^2 @@ -264,7 +274,7 @@ namespace platf { }; struct gamepad_battery_t { - std::uint8_t gamepadNumber; + gamepad_id_t id; std::uint8_t state; std::uint8_t percentage; }; @@ -581,13 +591,13 @@ namespace platf { /** * @brief Creates a new virtual gamepad. * @param input The input context. - * @param nr The assigned controller number. + * @param id The gamepad ID. * @param metadata Controller metadata from client (empty if none provided). * @param feedback_queue The queue for posting messages back to the client. * @return 0 on success. */ int - alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue); + alloc_gamepad(input_t &input, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue); void free_gamepad(input_t &input, int nr); diff --git a/src/platform/linux/input.cpp b/src/platform/linux/input.cpp index c427013f..a14a63c9 100644 --- a/src/platform/linux/input.cpp +++ b/src/platform/linux/input.cpp @@ -452,7 +452,7 @@ namespace platf { public: KITTY_DEFAULT_CONSTR_MOVE(effect_t) - effect_t(int gamepadnr, uinput_t::pointer dev, feedback_queue_t &&q): + effect_t(std::uint8_t gamepadnr, uinput_t::pointer dev, feedback_queue_t &&q): gamepadnr { gamepadnr }, dev { dev }, rumble_queue { std::move(q) }, gain { 0xFFFF }, id_to_data {} {} class data_t { @@ -628,8 +628,8 @@ namespace platf { BOOST_LOG(debug) << "Removed rumble effect id ["sv << id << ']'; } - // Used as ID for rumble notifications - int gamepadnr; + // Client-relative gamepad index for rumble notifications + std::uint8_t gamepadnr; // Used as ID for adding/removinf devices from evdev notifications uinput_t::pointer dev; @@ -772,14 +772,14 @@ namespace platf { /** * @brief Creates a new virtual gamepad. - * @param nr The assigned controller number. + * @param id The gamepad ID. * @param metadata Controller metadata from client (empty if none provided). * @param feedback_queue The queue for posting messages back to the client. * @return 0 on success. */ int - alloc_gamepad(int nr, const gamepad_arrival_t &metadata, feedback_queue_t &&feedback_queue) { - TUPLE_2D_REF(input, gamepad_state, gamepads[nr]); + alloc_gamepad(const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t &&feedback_queue) { + TUPLE_2D_REF(input, gamepad_state, gamepads[id.globalIndex]); int err = libevdev_uinput_create_from_device(gamepad_dev.get(), LIBEVDEV_UINPUT_OPEN_MANAGED, &input); @@ -791,7 +791,7 @@ namespace platf { } std::stringstream ss; - ss << "sunshine_gamepad_"sv << nr; + ss << "sunshine_gamepad_"sv << id.globalIndex; auto gamepad_path = platf::appdata() / ss.str(); if (std::filesystem::is_symlink(gamepad_path)) { @@ -801,7 +801,7 @@ namespace platf { auto dev_node = libevdev_uinput_get_devnode(input.get()); rumble_ctx->rumble_queue_queue.raise( - nr, + id.clientRelativeIndex, input.get(), std::move(feedback_queue), pollfd_t { @@ -1490,14 +1490,14 @@ namespace platf { /** * @brief Creates a new virtual gamepad. * @param input The input context. - * @param nr The assigned controller number. + * @param id The gamepad ID. * @param metadata Controller metadata from client (empty if none provided). * @param feedback_queue The queue for posting messages back to the client. * @return 0 on success. */ int - alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) { - return ((input_raw_t *) input.get())->alloc_gamepad(nr, metadata, std::move(feedback_queue)); + alloc_gamepad(input_t &input, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) { + return ((input_raw_t *) input.get())->alloc_gamepad(id, metadata, std::move(feedback_queue)); } void diff --git a/src/platform/macos/input.cpp b/src/platform/macos/input.cpp index 17073479..ddb45761 100644 --- a/src/platform/macos/input.cpp +++ b/src/platform/macos/input.cpp @@ -291,13 +291,13 @@ const KeyCodeMap kKeyCodesMap[] = { /** * @brief Creates a new virtual gamepad. * @param input The input context. - * @param nr The assigned controller number. + * @param id The gamepad ID. * @param metadata Controller metadata from client (empty if none provided). * @param feedback_queue The queue for posting messages back to the client. * @return 0 on success. */ int - alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) { + alloc_gamepad(input_t &input, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_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 633ad573..f6485512 100644 --- a/src/platform/windows/input.cpp +++ b/src/platform/windows/input.cpp @@ -61,6 +61,8 @@ namespace platf { std::map pointer_id_map; uint8_t available_pointers; + uint8_t client_relative_index; + gamepad_feedback_msg_t last_rumble; gamepad_feedback_msg_t last_rgb_led; }; @@ -193,16 +195,18 @@ namespace platf { /** * @brief Attaches a new gamepad. - * @param nr The gamepad index. + * @param id The gamepad ID. * @param feedback_queue The queue for posting messages back to the client. * @param gp_type The type of gamepad. * @return 0 on success. */ int - alloc_gamepad_internal(int nr, feedback_queue_t &feedback_queue, VIGEM_TARGET_TYPE gp_type) { - auto &gamepad = gamepads[nr]; + alloc_gamepad_internal(const gamepad_id_t &id, feedback_queue_t &feedback_queue, VIGEM_TARGET_TYPE gp_type) { + auto &gamepad = gamepads[id.globalIndex]; assert(!gamepad.gp); + gamepad.client_relative_index = id.clientRelativeIndex; + if (gp_type == Xbox360Wired) { gamepad.gp.reset(vigem_target_x360_alloc()); XUSB_REPORT_INIT(&gamepad.report.x360); @@ -218,8 +222,8 @@ namespace platf { ds4_update_motion(gamepad, LI_MOTION_TYPE_GYRO, 0.0f, 0.0f, 0.0f); // Request motion events from the client at 100 Hz - feedback_queue->raise(gamepad_feedback_msg_t::make_motion_event_state(nr, LI_MOTION_TYPE_ACCEL, 100)); - feedback_queue->raise(gamepad_feedback_msg_t::make_motion_event_state(nr, LI_MOTION_TYPE_GYRO, 100)); + feedback_queue->raise(gamepad_feedback_msg_t::make_motion_event_state(gamepad.client_relative_index, LI_MOTION_TYPE_ACCEL, 100)); + feedback_queue->raise(gamepad_feedback_msg_t::make_motion_event_state(gamepad.client_relative_index, LI_MOTION_TYPE_GYRO, 100)); // We support pointer index 0 and 1 gamepad.available_pointers = 0x3; @@ -285,7 +289,9 @@ namespace platf { // Don't resend duplicate rumble data if (normalizedSmallMotor != gamepad.last_rumble.data.rumble.highfreq || normalizedLargeMotor != gamepad.last_rumble.data.rumble.lowfreq) { - gamepad_feedback_msg_t msg = gamepad_feedback_msg_t::make_rumble(x, normalizedLargeMotor, normalizedSmallMotor); + // We have to use the client-relative index when communicating back to the client + gamepad_feedback_msg_t msg = gamepad_feedback_msg_t::make_rumble( + gamepad.client_relative_index, normalizedLargeMotor, normalizedSmallMotor); gamepad.feedback_queue->raise(msg); gamepad.last_rumble = msg; } @@ -308,8 +314,11 @@ namespace platf { if (gamepad.gp.get() == target) { // Don't resend duplicate RGB data - if (r != gamepad.last_rgb_led.data.rgb_led.r || g != gamepad.last_rgb_led.data.rgb_led.g || b != gamepad.last_rgb_led.data.rgb_led.b) { - gamepad_feedback_msg_t msg = gamepad_feedback_msg_t::make_rgb_led(x, r, g, b); + if (r != gamepad.last_rgb_led.data.rgb_led.r || + g != gamepad.last_rgb_led.data.rgb_led.g || + b != gamepad.last_rgb_led.data.rgb_led.b) { + // We have to use the client-relative index when communicating back to the client + gamepad_feedback_msg_t msg = gamepad_feedback_msg_t::make_rgb_led(gamepad.client_relative_index, r, g, b); gamepad.feedback_queue->raise(msg); gamepad.last_rgb_led = msg; } @@ -603,13 +612,13 @@ namespace platf { /** * @brief Creates a new virtual gamepad. * @param input The input context. - * @param nr The assigned controller number. + * @param id The gamepad ID. * @param metadata Controller metadata from client (empty if none provided). * @param feedback_queue The queue for posting messages back to the client. * @return 0 on success. */ int - alloc_gamepad(input_t &input, int nr, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) { + alloc_gamepad(input_t &input, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) { auto raw = (input_raw_t *) input.get(); if (!raw->vigem) { @@ -619,35 +628,35 @@ namespace platf { VIGEM_TARGET_TYPE selectedGamepadType; if (config::input.gamepad == "x360"sv) { - BOOST_LOG(info) << "Gamepad " << nr << " will be Xbox 360 controller (manual selection)"sv; + BOOST_LOG(info) << "Gamepad " << id.globalIndex << " 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; + BOOST_LOG(info) << "Gamepad " << id.globalIndex << " 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; + BOOST_LOG(info) << "Gamepad " << id.globalIndex << " 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; + BOOST_LOG(info) << "Gamepad " << id.globalIndex << " 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; + BOOST_LOG(info) << "Gamepad " << id.globalIndex << " 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; + BOOST_LOG(info) << "Gamepad " << id.globalIndex << " 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; + BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be Xbox 360 controller (default)"sv; selectedGamepadType = Xbox360Wired; } - return raw->vigem->alloc_gamepad_internal(nr, feedback_queue, selectedGamepadType); + return raw->vigem->alloc_gamepad_internal(id, feedback_queue, selectedGamepadType); } void @@ -873,7 +882,7 @@ namespace platf { return; } - auto &gamepad = vigem->gamepads[touch.gamepadNumber]; + auto &gamepad = vigem->gamepads[touch.id.globalIndex]; if (!gamepad.gp) { return; } @@ -973,7 +982,7 @@ namespace platf { return; } - auto &gamepad = vigem->gamepads[motion.gamepadNumber]; + auto &gamepad = vigem->gamepads[motion.id.globalIndex]; if (!gamepad.gp) { return; } @@ -1005,7 +1014,7 @@ namespace platf { return; } - auto &gamepad = vigem->gamepads[battery.gamepadNumber]; + auto &gamepad = vigem->gamepads[battery.id.globalIndex]; if (!gamepad.gp) { return; }