mirror of
https://github.com/LizardByte/Sunshine.git
synced 2025-03-28 19:20:26 +00:00
Dynamically plug and unplug emulated gamepads
This commit is contained in:
parent
3a6c18279e
commit
b10c971374
@ -125,9 +125,28 @@ void passthrough(platf::input_t &input, PNV_SCROLL_PACKET packet) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void passthrough(std::shared_ptr<input_t> &input, PNV_MULTI_CONTROLLER_PACKET packet) {
|
void passthrough(std::shared_ptr<input_t> &input, PNV_MULTI_CONTROLLER_PACKET packet) {
|
||||||
if(packet->controllerNumber < 0 || packet->controllerNumber >= input->gamepads.size()) {
|
auto xorGamepadMask = input->active_gamepad_state ^ packet->activeGamepadMask;
|
||||||
BOOST_LOG(warning) << "ControllerNumber out of range ["sv << packet->controllerNumber << ']';
|
|
||||||
|
|
||||||
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,9 +154,7 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_MULTI_CONTROLLER_PACKET pa
|
|||||||
|
|
||||||
display_cursor = false;
|
display_cursor = false;
|
||||||
|
|
||||||
std::uint16_t bf;
|
std::uint16_t bf = packet->buttonFlags;
|
||||||
std::memcpy(&bf, &packet->buttonFlags, sizeof(std::uint16_t));
|
|
||||||
|
|
||||||
platf::gamepad_state_t gamepad_state{
|
platf::gamepad_state_t gamepad_state{
|
||||||
bf,
|
bf,
|
||||||
packet->leftTrigger,
|
packet->leftTrigger,
|
||||||
@ -254,15 +271,7 @@ void reset_helper(std::shared_ptr<input_t> input) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NV_MULTI_CONTROLLER_PACKET fake_packet;
|
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;
|
|
||||||
|
|
||||||
passthrough(input, &fake_packet);
|
passthrough(input, &fake_packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,6 +283,6 @@ void reset(std::shared_ptr<input_t> &input) {
|
|||||||
task_pool.push(reset_helper, 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 } {}
|
gamepad_t::gamepad_t() : gamepad_state {}, back_timeout_id {}, back_button_state { button_state_e::NONE } {}
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@ struct input_t {
|
|||||||
|
|
||||||
platf::input_t input;
|
platf::input_t input;
|
||||||
|
|
||||||
|
std::uint16_t active_gamepad_state;
|
||||||
std::vector<gamepad_t> gamepads;
|
std::vector<gamepad_t> gamepads;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ using input_t = util::safe_ptr<void, freeInput>;
|
|||||||
std::string get_mac_address(const std::string_view &address);
|
std::string get_mac_address(const std::string_view &address);
|
||||||
|
|
||||||
std::unique_ptr<mic_t> microphone(std::uint32_t sample_rate);
|
std::unique_ptr<mic_t> microphone(std::uint32_t sample_rate);
|
||||||
std::shared_ptr<display_t> display();
|
std::unique_ptr<display_t> display();
|
||||||
|
|
||||||
input_t input();
|
input_t input();
|
||||||
void move_mouse(input_t &input, int deltaX, int deltaY);
|
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 scroll(input_t &input, int distance);
|
||||||
void keyboard(input_t &input, uint16_t modcode, bool release);
|
void keyboard(input_t &input, uint16_t modcode, bool release);
|
||||||
void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state);
|
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
|
#endif //SUNSHINE_COMMON_H
|
||||||
|
@ -326,7 +326,7 @@ std::unique_ptr<display_t> shm_display() {
|
|||||||
return shm;
|
return shm;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<display_t> display() {
|
std::unique_ptr<display_t> display() {
|
||||||
auto shm_disp = shm_display();
|
auto shm_disp = shm_display();
|
||||||
|
|
||||||
if(!shm_disp) {
|
if(!shm_disp) {
|
||||||
|
@ -51,7 +51,7 @@ public:
|
|||||||
std::filesystem::remove(gamepad_path);
|
std::filesystem::remove(gamepad_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
gamepads[nr].first.reset();
|
gamepads[nr] = std::make_pair(uinput_t{}, gamepad_state_t {});
|
||||||
}
|
}
|
||||||
|
|
||||||
int create_mouse() {
|
int create_mouse() {
|
||||||
@ -69,7 +69,7 @@ public:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int create_gamepad(int nr) {
|
int alloc_gamepad(int nr) {
|
||||||
TUPLE_2D_REF(input, gamepad_state, gamepads[nr]);
|
TUPLE_2D_REF(input, gamepad_state, gamepads[nr]);
|
||||||
|
|
||||||
libevdev_uinput *buf;
|
libevdev_uinput *buf;
|
||||||
@ -86,8 +86,12 @@ public:
|
|||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "sunshine_gamepad_"sv << nr;
|
ss << "sunshine_gamepad_"sv << nr;
|
||||||
std::filesystem::path gamepad_path { ss.str() };
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,6 +297,14 @@ void keyboard(input_t &input, uint16_t modcode, bool release) {
|
|||||||
XFlush(keyboard.get());
|
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) {
|
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]);
|
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() };
|
input_t result { new input_raw_t() };
|
||||||
auto &gp = *(input_raw_t*)result.get();
|
auto &gp = *(input_raw_t*)result.get();
|
||||||
|
|
||||||
gp.gamepads.resize(MAX_GAMEPADS);
|
|
||||||
gp.keyboard.reset(XOpenDisplay(nullptr));
|
gp.keyboard.reset(XOpenDisplay(nullptr));
|
||||||
|
|
||||||
// If we do not have a keyboard, gamepad or mouse, no input is possible and we should abort
|
// 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();
|
std::abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gp.gamepads.resize(MAX_GAMEPADS);
|
||||||
|
|
||||||
// Ensure starting from clean slate
|
// Ensure starting from clean slate
|
||||||
gp.clear();
|
gp.clear();
|
||||||
gp.mouse_dev = mouse();
|
gp.mouse_dev = mouse();
|
||||||
gp.gamepad_dev = x360();
|
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()) {
|
if(gp.create_mouse()) {
|
||||||
log_flush();
|
log_flush();
|
||||||
std::abort();
|
std::abort();
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
|
||||||
#include <Ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
#include <Winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <winuser.h>
|
#include <winuser.h>
|
||||||
#include <iphlpapi.h>
|
#include <iphlpapi.h>
|
||||||
@ -36,20 +36,38 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
x360s.resize(MAX_GAMEPADS);
|
x360s.resize(MAX_GAMEPADS);
|
||||||
for(auto &x360 : x360s) {
|
|
||||||
x360.reset(vigem_target_x360_alloc());
|
|
||||||
|
|
||||||
status = vigem_target_add(client.get(), x360.get());
|
return 0;
|
||||||
if(!VIGEM_SUCCESS(status)) {
|
}
|
||||||
BOOST_LOG(error) << "Couldn't add Gamepad to ViGEm connection ["sv << util::hex(status).to_string_view() << ']';
|
|
||||||
|
|
||||||
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;
|
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() {
|
~vigem_t() {
|
||||||
if(client) {
|
if(client) {
|
||||||
for(auto &x360 : x360s) {
|
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) {
|
void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) {
|
||||||
// If there is no gamepad support
|
// If there is no gamepad support
|
||||||
if(!input) {
|
if(!input) {
|
||||||
|
@ -717,7 +717,7 @@ const char *format_str[] = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace platf {
|
namespace platf {
|
||||||
std::shared_ptr<display_t> display() {
|
std::unique_ptr<display_t> display() {
|
||||||
auto disp = std::make_unique<dxgi::display_t>();
|
auto disp = std::make_unique<dxgi::display_t>();
|
||||||
|
|
||||||
if (disp->init()) {
|
if (disp->init()) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user