Dynamically plug and unplug emulated gamepads

This commit is contained in:
loki 2020-01-31 20:57:34 +01:00
parent 3a6c18279e
commit b10c971374
7 changed files with 90 additions and 38 deletions

View File

@ -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) {
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_t> &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_t> 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_t> &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 } {}
}

View File

@ -37,6 +37,7 @@ struct input_t {
platf::input_t input;
std::uint16_t active_gamepad_state;
std::vector<gamepad_t> gamepads;
};

View File

@ -82,7 +82,7 @@ using input_t = util::safe_ptr<void, freeInput>;
std::string get_mac_address(const std::string_view &address);
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();
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

View File

@ -326,7 +326,7 @@ std::unique_ptr<display_t> shm_display() {
return shm;
}
std::shared_ptr<display_t> display() {
std::unique_ptr<display_t> display() {
auto shm_disp = shm_display();
if(!shm_disp) {

View File

@ -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();

View File

@ -2,8 +2,8 @@
#include <sstream>
#include <iomanip>
#include <Ws2tcpip.h>
#include <Winsock2.h>
#include <ws2tcpip.h>
#include <winsock2.h>
#include <windows.h>
#include <winuser.h>
#include <iphlpapi.h>
@ -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) {

View File

@ -717,7 +717,7 @@ const char *format_str[] = {
}
namespace platf {
std::shared_ptr<display_t> display() {
std::unique_ptr<display_t> display() {
auto disp = std::make_unique<dxgi::display_t>();
if (disp->init()) {