Refactor gamepad device creation

This commit is contained in:
loki 2020-01-25 20:46:14 +01:00
parent 8b7c833cd4
commit 1d9ccb211e
4 changed files with 156 additions and 129 deletions

View File

@ -23,7 +23,7 @@ struct gamepad_t {
// When emulating the HOME button, we may need to artificially release the back button. // When emulating the HOME button, we may need to artificially release the back button.
// Afterwards, the gamepad state on sunshine won't match the state on Moonlight // Afterwards, the gamepad state on sunshine won't match the state on Moonlight.
// To prevent Sunshine from sending erronious input data to the active application, // To prevent Sunshine from sending erronious input data to the active application,
// Sunshine forces the button to be in a specific state until the gamepad state matches that of // Sunshine forces the button to be in a specific state until the gamepad state matches that of
// Moonlight once more. // Moonlight once more.

View File

@ -89,7 +89,7 @@ void move_mouse(input_t &input, int deltaX, int deltaY);
void button_mouse(input_t &input, int button, bool release); 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 controller, const gamepad_state_t &gamepad_state); void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state);
} }
#endif //SUNSHINE_COMMON_H #endif //SUNSHINE_COMMON_H

View File

@ -30,6 +30,78 @@ using uinput_t = util::safe_ptr<libevdev_uinput, libevdev_uinput_destroy>;
using keyboard_t = util::safe_ptr_v2<Display, int, XCloseDisplay>; using keyboard_t = util::safe_ptr_v2<Display, int, XCloseDisplay>;
struct input_raw_t { struct input_raw_t {
public:
void clear_mouse() {
std::filesystem::path mouse_path { "sunshine_mouse"sv };
if(std::filesystem::is_symlink(mouse_path)) {
std::filesystem::remove(mouse_path);
}
mouse_input.reset();
}
void clear_gamepad(int nr) {
std::stringstream ss;
ss << "sunshine_gamepad_"sv << nr;
std::filesystem::path gamepad_path { ss.str() };
if(std::filesystem::is_symlink(gamepad_path)) {
std::filesystem::remove(gamepad_path);
}
gamepads[nr].first.reset();
}
int create_mouse() {
libevdev_uinput *buf;
int err = libevdev_uinput_create_from_device(mouse_dev.get(), LIBEVDEV_UINPUT_OPEN_MANAGED, &buf);
mouse_input.reset(buf);
if(err) {
BOOST_LOG(error) << "Could not create Sunshine Mouse: "sv << strerror(-err);
return -1;
}
std::filesystem::create_symlink(libevdev_uinput_get_devnode(mouse_input.get()), "sunshine_mouse"sv);
return 0;
}
int create_gamepad(int nr) {
TUPLE_2D_REF(input, gamepad_state, gamepads[nr]);
libevdev_uinput *buf;
int err = libevdev_uinput_create_from_device(gamepad_dev.get(), LIBEVDEV_UINPUT_OPEN_MANAGED, &buf);
input.reset(buf);
gamepad_state = gamepad_state_t {};
if(err) {
BOOST_LOG(error) << "Could not create Sunshine Gamepad: "sv << strerror(-err);
return -1;
}
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);
return 0;
}
void clear() {
clear_mouse();
for(int x = 0; x < gamepads.size(); ++x) {
clear_gamepad(x);
}
}
~input_raw_t() {
clear();
}
evdev_t gamepad_dev; evdev_t gamepad_dev;
std::vector<std::pair<uinput_t, gamepad_state_t>> gamepads; std::vector<std::pair<uinput_t, gamepad_state_t>> gamepads;
@ -221,14 +293,8 @@ void keyboard(input_t &input, uint16_t modcode, bool release) {
XFlush(keyboard.get()); XFlush(keyboard.get());
} }
void gamepad(input_t &input, int controller, const gamepad_state_t &gamepad_state) { void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) {
auto &gamepads = ((input_raw_t*)input.get())->gamepads; TUPLE_2D_REF(uinput, gamepad_state_old, ((input_raw_t*)input.get())->gamepads[nr]);
if(controller < 0 || controller >= gamepads.size()) {
BOOST_LOG(warning) << "Controller number out of range: ["sv << controller << " > "sv << gamepads.size() << ']';
return;
}
TUPLE_2D_REF(uinput, gamepad_state_old, gamepads[controller]);
auto bf = gamepad_state.buttonFlags ^ gamepad_state_old.buttonFlags; auto bf = gamepad_state.buttonFlags ^ gamepad_state_old.buttonFlags;
@ -289,59 +355,50 @@ void gamepad(input_t &input, int controller, const gamepad_state_t &gamepad_stat
libevdev_uinput_write_event(uinput.get(), EV_SYN, SYN_REPORT, 0); libevdev_uinput_write_event(uinput.get(), EV_SYN, SYN_REPORT, 0);
} }
int mouse(input_raw_t &gp) { evdev_t mouse() {
gp.mouse_dev.reset(libevdev_new()); evdev_t dev { libevdev_new() };
libevdev_set_uniq(gp.mouse_dev.get(), "Sunshine Gamepad"); libevdev_set_uniq(dev.get(), "Sunshine Mouse");
libevdev_set_id_product(gp.mouse_dev.get(), 0x4038); libevdev_set_id_product(dev.get(), 0x4038);
libevdev_set_id_vendor(gp.mouse_dev.get(), 0x46D); libevdev_set_id_vendor(dev.get(), 0x46D);
libevdev_set_id_bustype(gp.mouse_dev.get(), 0x3); libevdev_set_id_bustype(dev.get(), 0x3);
libevdev_set_id_version(gp.mouse_dev.get(), 0x111); libevdev_set_id_version(dev.get(), 0x111);
libevdev_set_name(gp.mouse_dev.get(), "Logitech Wireless Mouse PID:4038"); libevdev_set_name(dev.get(), "Logitech Wireless Mouse PID:4038");
libevdev_enable_event_type(gp.mouse_dev.get(), EV_KEY); libevdev_enable_event_type(dev.get(), EV_KEY);
libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, BTN_LEFT, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, BTN_LEFT, nullptr);
libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, BTN_RIGHT, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, BTN_RIGHT, nullptr);
libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, BTN_MIDDLE, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, BTN_MIDDLE, nullptr);
libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, BTN_SIDE, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, BTN_SIDE, nullptr);
libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, BTN_EXTRA, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, BTN_EXTRA, nullptr);
libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, BTN_FORWARD, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, BTN_FORWARD, nullptr);
libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, BTN_BACK, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, BTN_BACK, nullptr);
libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, BTN_TASK, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, BTN_TASK, nullptr);
libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, 280, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, 280, nullptr);
libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, 281, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, 281, nullptr);
libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, 282, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, 282, nullptr);
libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, 283, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, 283, nullptr);
libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, 284, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, 284, nullptr);
libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, 285, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, 285, nullptr);
libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, 286, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, 286, nullptr);
libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, 287, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, 287, nullptr);
libevdev_enable_event_type(gp.mouse_dev.get(), EV_REL); libevdev_enable_event_type(dev.get(), EV_REL);
libevdev_enable_event_code(gp.mouse_dev.get(), EV_REL, REL_X, nullptr); libevdev_enable_event_code(dev.get(), EV_REL, REL_X, nullptr);
libevdev_enable_event_code(gp.mouse_dev.get(), EV_REL, REL_Y, nullptr); libevdev_enable_event_code(dev.get(), EV_REL, REL_Y, nullptr);
libevdev_enable_event_code(gp.mouse_dev.get(), EV_REL, REL_WHEEL, nullptr); libevdev_enable_event_code(dev.get(), EV_REL, REL_WHEEL, nullptr);
libevdev_enable_event_code(gp.mouse_dev.get(), EV_REL, REL_WHEEL_HI_RES, nullptr); libevdev_enable_event_code(dev.get(), EV_REL, REL_WHEEL_HI_RES, nullptr);
libevdev_enable_event_code(gp.mouse_dev.get(), EV_REL, REL_HWHEEL, nullptr); libevdev_enable_event_code(dev.get(), EV_REL, REL_HWHEEL, nullptr);
libevdev_enable_event_code(gp.mouse_dev.get(), EV_REL, REL_HWHEEL_HI_RES, nullptr); libevdev_enable_event_code(dev.get(), EV_REL, REL_HWHEEL_HI_RES, nullptr);
libevdev_enable_event_type(gp.mouse_dev.get(), EV_MSC); libevdev_enable_event_type(dev.get(), EV_MSC);
libevdev_enable_event_code(gp.mouse_dev.get(), EV_MSC, MSC_SCAN, nullptr); libevdev_enable_event_code(dev.get(), EV_MSC, MSC_SCAN, nullptr);
libevdev_uinput *buf; return dev;
int err = libevdev_uinput_create_from_device(gp.mouse_dev.get(), LIBEVDEV_UINPUT_OPEN_MANAGED, &buf);
gp.mouse_input.reset(buf);
if(err) {
BOOST_LOG(error) << "Could not create Sunshine Mouse: "sv << strerror(-err);
return -1;
}
return 0;
} }
int gamepad(input_raw_t &gp) { evdev_t x360() {
gp.gamepad_dev.reset(libevdev_new()); evdev_t dev { libevdev_new() };
input_absinfo stick { input_absinfo stick {
0, 0,
@ -367,57 +424,44 @@ int gamepad(input_raw_t &gp) {
0 0
}; };
libevdev_set_uniq(gp.gamepad_dev.get(), "Sunshine Gamepad"); libevdev_set_uniq(dev.get(), "Sunshine Gamepad");
libevdev_set_id_product(gp.gamepad_dev.get(), 0x28E); libevdev_set_id_product(dev.get(), 0x28E);
libevdev_set_id_vendor(gp.gamepad_dev.get(), 0x45E); libevdev_set_id_vendor(dev.get(), 0x45E);
libevdev_set_id_bustype(gp.gamepad_dev.get(), 0x3); libevdev_set_id_bustype(dev.get(), 0x3);
libevdev_set_id_version(gp.gamepad_dev.get(), 0x110); libevdev_set_id_version(dev.get(), 0x110);
libevdev_set_name(gp.gamepad_dev.get(), "Microsoft X-Box 360 pad"); libevdev_set_name(dev.get(), "Microsoft X-Box 360 pad");
libevdev_enable_event_type(gp.gamepad_dev.get(), EV_KEY); libevdev_enable_event_type(dev.get(), EV_KEY);
libevdev_enable_event_code(gp.gamepad_dev.get(), EV_KEY, BTN_WEST, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, BTN_WEST, nullptr);
libevdev_enable_event_code(gp.gamepad_dev.get(), EV_KEY, BTN_EAST, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, BTN_EAST, nullptr);
libevdev_enable_event_code(gp.gamepad_dev.get(), EV_KEY, BTN_NORTH, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, BTN_NORTH, nullptr);
libevdev_enable_event_code(gp.gamepad_dev.get(), EV_KEY, BTN_SOUTH, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, BTN_SOUTH, nullptr);
libevdev_enable_event_code(gp.gamepad_dev.get(), EV_KEY, BTN_THUMBL, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, BTN_THUMBL, nullptr);
libevdev_enable_event_code(gp.gamepad_dev.get(), EV_KEY, BTN_THUMBR, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, BTN_THUMBR, nullptr);
libevdev_enable_event_code(gp.gamepad_dev.get(), EV_KEY, BTN_TR, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, BTN_TR, nullptr);
libevdev_enable_event_code(gp.gamepad_dev.get(), EV_KEY, BTN_TL, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, BTN_TL, nullptr);
libevdev_enable_event_code(gp.gamepad_dev.get(), EV_KEY, BTN_SELECT, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, BTN_SELECT, nullptr);
libevdev_enable_event_code(gp.gamepad_dev.get(), EV_KEY, BTN_MODE, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, BTN_MODE, nullptr);
libevdev_enable_event_code(gp.gamepad_dev.get(), EV_KEY, BTN_START, nullptr); libevdev_enable_event_code(dev.get(), EV_KEY, BTN_START, nullptr);
libevdev_enable_event_type(gp.gamepad_dev.get(), EV_ABS); libevdev_enable_event_type(dev.get(), EV_ABS);
libevdev_enable_event_code(gp.gamepad_dev.get(), EV_ABS, ABS_HAT0Y, &dpad); libevdev_enable_event_code(dev.get(), EV_ABS, ABS_HAT0Y, &dpad);
libevdev_enable_event_code(gp.gamepad_dev.get(), EV_ABS, ABS_HAT0X, &dpad); libevdev_enable_event_code(dev.get(), EV_ABS, ABS_HAT0X, &dpad);
libevdev_enable_event_code(gp.gamepad_dev.get(), EV_ABS, ABS_Z, &trigger); libevdev_enable_event_code(dev.get(), EV_ABS, ABS_Z, &trigger);
libevdev_enable_event_code(gp.gamepad_dev.get(), EV_ABS, ABS_RZ, &trigger); libevdev_enable_event_code(dev.get(), EV_ABS, ABS_RZ, &trigger);
libevdev_enable_event_code(gp.gamepad_dev.get(), EV_ABS, ABS_X, &stick); libevdev_enable_event_code(dev.get(), EV_ABS, ABS_X, &stick);
libevdev_enable_event_code(gp.gamepad_dev.get(), EV_ABS, ABS_RX, &stick); libevdev_enable_event_code(dev.get(), EV_ABS, ABS_RX, &stick);
libevdev_enable_event_code(gp.gamepad_dev.get(), EV_ABS, ABS_Y, &stick); libevdev_enable_event_code(dev.get(), EV_ABS, ABS_Y, &stick);
libevdev_enable_event_code(gp.gamepad_dev.get(), EV_ABS, ABS_RY, &stick); libevdev_enable_event_code(dev.get(), EV_ABS, ABS_RY, &stick);
gp.gamepads.resize(MAX_GAMEPADS); return dev;
for(auto &[uinput, _] : gp.gamepads) {
libevdev_uinput *buf {};
int err = libevdev_uinput_create_from_device(gp.gamepad_dev.get(), LIBEVDEV_UINPUT_OPEN_MANAGED, &buf);
uinput.reset(buf);
if(err) {
BOOST_LOG(error) << "Could not create Sunshine Gamepad: "sv << strerror(-err);
return -1;
}
}
return 0;
} }
input_t input() { 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
@ -427,34 +471,21 @@ input_t input() {
std::abort(); std::abort();
} }
if(gamepad(gp)) { // Ensure starting from clean slate
log_flush(); gp.clear();
std::abort(); gp.mouse_dev = mouse();
} gp.gamepad_dev = x360();
if(mouse(gp)) {
log_flush();
std::abort();
}
std::filesystem::path mouse_path { "sunshine_mouse" };
if(std::filesystem::is_symlink(mouse_path)) {
std::filesystem::remove(mouse_path);
}
std::filesystem::create_symlink(libevdev_uinput_get_devnode(gp.mouse_input.get()), mouse_path);
for(int x = 0; x < gp.gamepads.size(); ++x) { for(int x = 0; x < gp.gamepads.size(); ++x) {
std::stringstream ss; if(gp.create_gamepad(x)) {
log_flush();
ss << "sunshine_gamepad_"sv << x; std::abort();
std::filesystem::path gamepad_path { ss.str() };
if(std::filesystem::is_symlink(gamepad_path)) {
std::filesystem::remove(gamepad_path);
} }
}
std::filesystem::create_symlink(libevdev_uinput_get_devnode(gp.gamepads[x].first.get()), gamepad_path); if(gp.create_mouse()) {
log_flush();
std::abort();
} }
return result; return result;

View File

@ -35,6 +35,7 @@ public:
return -1; return -1;
} }
x360s.resize(MAX_GAMEPADS);
for(auto &x360 : x360s) { for(auto &x360 : x360s) {
x360.reset(vigem_target_x360_alloc()); x360.reset(vigem_target_x360_alloc());
@ -262,7 +263,7 @@ void keyboard(input_t &input, uint16_t modcode, bool release) {
} }
} }
void gamepad(input_t &input, int controller, 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) {
return; return;
@ -270,13 +271,8 @@ void gamepad(input_t &input, int controller, const gamepad_state_t &gamepad_stat
auto vigem = (vigem_t*)input.get(); auto vigem = (vigem_t*)input.get();
if(controller < 0 && controller >= vigem->x360s.size()) {
BOOST_LOG(warning) << "Controller number out of range: ["sv << controller << " > "sv << vigem->x360s.size() << ']';
return;
}
auto &xusb = *(PXUSB_REPORT)&gamepad_state; auto &xusb = *(PXUSB_REPORT)&gamepad_state;
auto &x360 = vigem->x360s[controller]; auto &x360 = vigem->x360s[nr];
auto status = vigem_target_x360_update(vigem->client.get(), x360.get(), xusb); auto status = vigem_target_x360_update(vigem->client.get(), x360.get(), xusb);
if(!VIGEM_SUCCESS(status)) { if(!VIGEM_SUCCESS(status)) {