diff --git a/sunshine/input.cpp b/sunshine/input.cpp index a159da08..d71384d1 100644 --- a/sunshine/input.cpp +++ b/sunshine/input.cpp @@ -125,9 +125,28 @@ void passthrough(platf::input_t &input, PNV_SCROLL_PACKET packet) { } void passthrough(std::shared_ptr &input, PNV_MULTI_CONTROLLER_PACKET packet) { - if(packet->controllerNumber < 0 || packet->controllerNumber >= input->gamepads.size()) { - BOOST_LOG(warning) << "ControllerNumber out of range ["sv << packet->controllerNumber << ']'; + auto xorGamepadMask = input->active_gamepad_state ^ packet->activeGamepadMask; + for(int x = 0; x < platf::MAX_GAMEPADS; ++x) { + if((xorGamepadMask >> x) & 1) { + if((input->active_gamepad_state >> x) & 1) { + platf::gamepad(input->input, x, platf::gamepad_state_t {}); + platf::free_gamepad(input->input, x); + } + else if(platf::alloc_gamepad(input->input, x)) { + //TODO: abort stream session + } + } + } + input->active_gamepad_state = packet->activeGamepadMask; + + if(packet->controllerNumber < 0 || packet->controllerNumber >= input->gamepads.size()) { + BOOST_LOG(error) << "ControllerNumber out of range ["sv << packet->controllerNumber << ']'; + + return; + } + + if(!((input->active_gamepad_state >> packet->controllerNumber) & 1)) { return; } @@ -135,9 +154,7 @@ void passthrough(std::shared_ptr &input, PNV_MULTI_CONTROLLER_PACKET pa display_cursor = false; - std::uint16_t bf; - std::memcpy(&bf, &packet->buttonFlags, sizeof(std::uint16_t)); - + std::uint16_t bf = packet->buttonFlags; platf::gamepad_state_t gamepad_state{ bf, packet->leftTrigger, @@ -254,15 +271,7 @@ void reset_helper(std::shared_ptr input) { } } - NV_MULTI_CONTROLLER_PACKET fake_packet; - fake_packet.buttonFlags = 0; - fake_packet.leftStickX = 0; - fake_packet.leftStickY = 0; - fake_packet.rightStickX = 0; - fake_packet.rightStickY = 0; - fake_packet.leftTrigger = 0; - fake_packet.rightTrigger = 0; - + NV_MULTI_CONTROLLER_PACKET fake_packet {}; passthrough(input, &fake_packet); } @@ -274,6 +283,6 @@ void reset(std::shared_ptr &input) { task_pool.push(reset_helper, input); } -input_t::input_t() : mouse_press {}, input { platf::input() }, gamepads(platf::MAX_GAMEPADS) {} +input_t::input_t() : mouse_press {}, input { platf::input() }, active_gamepad_state {}, gamepads (platf::MAX_GAMEPADS) {} gamepad_t::gamepad_t() : gamepad_state {}, back_timeout_id {}, back_button_state { button_state_e::NONE } {} } diff --git a/sunshine/input.h b/sunshine/input.h index da242d47..f07f925e 100644 --- a/sunshine/input.h +++ b/sunshine/input.h @@ -37,6 +37,7 @@ struct input_t { platf::input_t input; + std::uint16_t active_gamepad_state; std::vector gamepads; }; diff --git a/sunshine/platform/common.h b/sunshine/platform/common.h index f8ea9aed..8d3d04f4 100644 --- a/sunshine/platform/common.h +++ b/sunshine/platform/common.h @@ -82,7 +82,7 @@ using input_t = util::safe_ptr; std::string get_mac_address(const std::string_view &address); std::unique_ptr microphone(std::uint32_t sample_rate); -std::shared_ptr display(); +std::unique_ptr display(); input_t input(); void move_mouse(input_t &input, int deltaX, int deltaY); @@ -90,6 +90,9 @@ void button_mouse(input_t &input, int button, bool release); void scroll(input_t &input, int distance); void keyboard(input_t &input, uint16_t modcode, bool release); void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state); + +int alloc_gamepad(input_t &input, int nr); +void free_gamepad(input_t &input, int nr); } #endif //SUNSHINE_COMMON_H diff --git a/sunshine/platform/linux.cpp b/sunshine/platform/linux.cpp index f4cdc52f..7190ff43 100644 --- a/sunshine/platform/linux.cpp +++ b/sunshine/platform/linux.cpp @@ -326,7 +326,7 @@ std::unique_ptr shm_display() { return shm; } -std::shared_ptr display() { +std::unique_ptr display() { auto shm_disp = shm_display(); if(!shm_disp) { diff --git a/sunshine/platform/linux_evdev.cpp b/sunshine/platform/linux_evdev.cpp index 95d5e4e6..38958ffc 100644 --- a/sunshine/platform/linux_evdev.cpp +++ b/sunshine/platform/linux_evdev.cpp @@ -51,7 +51,7 @@ public: std::filesystem::remove(gamepad_path); } - gamepads[nr].first.reset(); + gamepads[nr] = std::make_pair(uinput_t{}, gamepad_state_t {}); } int create_mouse() { @@ -69,7 +69,7 @@ public: return 0; } - int create_gamepad(int nr) { + int alloc_gamepad(int nr) { TUPLE_2D_REF(input, gamepad_state, gamepads[nr]); libevdev_uinput *buf; @@ -86,8 +86,12 @@ public: std::stringstream ss; ss << "sunshine_gamepad_"sv << nr; std::filesystem::path gamepad_path { ss.str() }; - std::filesystem::create_symlink(libevdev_uinput_get_devnode(input.get()), gamepad_path); + if(std::filesystem::is_symlink(gamepad_path)) { + std::filesystem::remove(gamepad_path); + } + + std::filesystem::create_symlink(libevdev_uinput_get_devnode(input.get()), gamepad_path); return 0; } @@ -293,6 +297,14 @@ void keyboard(input_t &input, uint16_t modcode, bool release) { XFlush(keyboard.get()); } +int alloc_gamepad(input_t &input, int nr) { + return ((input_raw_t*)input.get())->alloc_gamepad(nr); +} + +void free_gamepad(input_t &input, int nr) { + ((input_raw_t*)input.get())->clear_gamepad(nr); +} + void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) { TUPLE_2D_REF(uinput, gamepad_state_old, ((input_raw_t*)input.get())->gamepads[nr]); @@ -461,7 +473,6 @@ input_t input() { input_t result { new input_raw_t() }; auto &gp = *(input_raw_t*)result.get(); - gp.gamepads.resize(MAX_GAMEPADS); gp.keyboard.reset(XOpenDisplay(nullptr)); // If we do not have a keyboard, gamepad or mouse, no input is possible and we should abort @@ -471,18 +482,13 @@ input_t input() { std::abort(); } + gp.gamepads.resize(MAX_GAMEPADS); + // Ensure starting from clean slate gp.clear(); gp.mouse_dev = mouse(); gp.gamepad_dev = x360(); - for(int x = 0; x < gp.gamepads.size(); ++x) { - if(gp.create_gamepad(x)) { - log_flush(); - std::abort(); - } - } - if(gp.create_mouse()) { log_flush(); std::abort(); diff --git a/sunshine/platform/windows.cpp b/sunshine/platform/windows.cpp index d46267e6..3be53c07 100755 --- a/sunshine/platform/windows.cpp +++ b/sunshine/platform/windows.cpp @@ -2,8 +2,8 @@ #include #include -#include -#include +#include +#include #include #include #include @@ -36,20 +36,38 @@ public: } x360s.resize(MAX_GAMEPADS); - for(auto &x360 : x360s) { - x360.reset(vigem_target_x360_alloc()); - status = vigem_target_add(client.get(), x360.get()); - if(!VIGEM_SUCCESS(status)) { - BOOST_LOG(error) << "Couldn't add Gamepad to ViGEm connection ["sv << util::hex(status).to_string_view() << ']'; + return 0; + } - return -1; - } + int alloc_x360(int nr) { + auto &x360 = x360s[nr]; + assert(!x360); + + x360.reset(vigem_target_x360_alloc()); + auto status = vigem_target_add(client.get(), x360.get()); + if(!VIGEM_SUCCESS(status)) { + BOOST_LOG(error) << "Couldn't add Gamepad to ViGEm connection ["sv << util::hex(status).to_string_view() << ']'; + + return -1; } return 0; } + void free_target(int nr) { + auto &x360 = x360s[nr]; + + if(x360 && vigem_target_is_attached(x360.get())) { + auto status = vigem_target_remove(client.get(), x360.get()); + if(!VIGEM_SUCCESS(status)) { + BOOST_LOG(warning) << "Couldn't detach gamepad from ViGEm ["sv << util::hex(status).to_string_view() << ']'; + } + } + + x360.reset(); + } + ~vigem_t() { if(client) { for(auto &x360 : x360s) { @@ -263,6 +281,21 @@ void keyboard(input_t &input, uint16_t modcode, bool release) { } } +int alloc_gamepad(input_t &input, int nr) { + if(!input) { + return 0; + } + + return ((vigem_t*)input.get())->alloc_x360(nr); +} + +void free_gamepad(input_t &input, int nr) { + if(!input) { + return; + } + + ((vigem_t*)input.get())->free_target(nr); +} void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) { // If there is no gamepad support if(!input) { diff --git a/sunshine/platform/windows_dxgi.cpp b/sunshine/platform/windows_dxgi.cpp index 9f8269a4..c5f6539d 100644 --- a/sunshine/platform/windows_dxgi.cpp +++ b/sunshine/platform/windows_dxgi.cpp @@ -717,7 +717,7 @@ const char *format_str[] = { } namespace platf { -std::shared_ptr display() { +std::unique_ptr display() { auto disp = std::make_unique(); if (disp->init()) {