Emulate an xbox 360 controller

This commit is contained in:
loki 2019-12-06 16:33:13 +01:00
parent 3ee5b75c46
commit d8f1cf3cd0
5 changed files with 56 additions and 195 deletions

View File

@ -6,6 +6,7 @@ extern "C" {
#include <moonlight-common-c/src/Input.h>
}
#include <cstring>
#include <iostream>
#include "input.h"
@ -95,48 +96,69 @@ void print(void *input) {
}
}
void passthrough(platf::display_t::element_type *display, PNV_MOUSE_MOVE_PACKET packet) {
platf::move_mouse(display, util::endian::big(packet->deltaX), util::endian::big(packet->deltaY));
void passthrough(platf::input_t &input, PNV_MOUSE_MOVE_PACKET packet) {
platf::move_mouse(input, util::endian::big(packet->deltaX), util::endian::big(packet->deltaY));
}
void passthrough(platf::display_t::element_type *display, PNV_MOUSE_BUTTON_PACKET packet) {
void passthrough(platf::input_t &input, PNV_MOUSE_BUTTON_PACKET packet) {
auto constexpr BUTTON_RELEASED = 0x09;
platf::button_mouse(display, util::endian::big(packet->button), packet->action == BUTTON_RELEASED);
platf::button_mouse(input, util::endian::big(packet->button), packet->action == BUTTON_RELEASED);
}
void passthrough(platf::display_t::element_type *display, PNV_KEYBOARD_PACKET packet) {
void passthrough(platf::input_t &input, PNV_KEYBOARD_PACKET packet) {
auto constexpr BUTTON_RELEASED = 0x04;
platf::keyboard(display, packet->keyCode & 0x00FF, packet->keyAction == BUTTON_RELEASED);
platf::keyboard(input, packet->keyCode & 0x00FF, packet->keyAction == BUTTON_RELEASED);
}
void passthrough(platf::display_t::element_type *display, PNV_SCROLL_PACKET packet) {
platf::scroll(display, util::endian::big(packet->scrollAmt1));
void passthrough(platf::input_t &input, PNV_SCROLL_PACKET packet) {
platf::scroll(input, util::endian::big(packet->scrollAmt1));
}
void passthrough(platf::display_t::element_type *display, void *input) {
int input_type = util::endian::big(*(int*)input);
void passthrough(platf::input_t &input, PNV_MULTI_CONTROLLER_PACKET packet) {
std::uint16_t bf;
static_assert(sizeof(bf) == sizeof(packet->buttonFlags), "Can't memcpy :(");
std::memcpy(&bf, &packet->buttonFlags, sizeof(std::uint16_t));
platf::gamepad_state_t gamepad_state {
bf,
packet->leftTrigger,
packet->rightTrigger,
packet->leftStickX,
packet->leftStickY,
packet->rightStickX,
packet->rightStickY
};
platf::gamepad(input, gamepad_state);
}
void passthrough(platf::input_t &input, void *payload) {
int input_type = util::endian::big(*(int*)payload);
switch(input_type) {
case PACKET_TYPE_MOUSE_MOVE:
passthrough(display, (PNV_MOUSE_MOVE_PACKET)input);
passthrough(input, (PNV_MOUSE_MOVE_PACKET)payload);
break;
case PACKET_TYPE_MOUSE_BUTTON:
passthrough(display, (PNV_MOUSE_BUTTON_PACKET)input);
passthrough(input, (PNV_MOUSE_BUTTON_PACKET)payload);
break;
case PACKET_TYPE_SCROLL_OR_KEYBOARD:
{
char *tmp_input = (char*)input + 4;
char *tmp_input = (char*)payload + 4;
if(tmp_input[0] == 0x0A) {
passthrough(display, (PNV_SCROLL_PACKET)input);
passthrough(input, (PNV_SCROLL_PACKET)payload);
}
else {
passthrough(display, (PNV_KEYBOARD_PACKET)input);
passthrough(input, (PNV_KEYBOARD_PACKET)payload);
}
break;
}
case PACKET_TYPE_MULTI_CONTROLLER:
passthrough(input, (PNV_MULTI_CONTROLLER_PACKET)payload);
break;
}
}
}

View File

@ -10,7 +10,7 @@
namespace input {
void print(void *input);
void passthrough(platf::display_t::element_type *display, void *input);
void passthrough(platf::input_t &, void *input);
}
#endif //SUNSHINE_INPUT_H

View File

@ -14,42 +14,43 @@ void freeDisplay(void*);
void freeImage(void*);
void freeAudio(void*);
void freeMic(void*);
void freeGamePad(void*);
void freeInput(void*);
using display_t = util::safe_ptr<void, freeDisplay>;
using img_t = util::safe_ptr<void, freeImage>;
using mic_t = util::safe_ptr<void, freeMic>;
using audio_t = util::safe_ptr<void, freeAudio>;
using gamepad_t = util::safe_ptr<void, freeGamePad>;
using input_t = util::safe_ptr<void, freeInput>;
struct gamepad_state_t {
std::uint16_t buttonFlags;
std::uint8_t lt;
std::uint8_t rt;
std::uint16_t lsX;
std::uint16_t lsY;
std::uint16_t rsX;
std::uint16_t rsY;
std::int16_t lsX;
std::int16_t lsY;
std::int16_t rsX;
std::int16_t rsY;
};
std::string get_local_ip();
display_t display();
img_t snapshot(display_t &display);
mic_t microphone();
audio_t audio(mic_t &mic, std::uint32_t sample_size);
gamepad_t gamepad();
display_t display();
img_t snapshot(display_t &display);
int32_t img_width(img_t &);
int32_t img_height(img_t &);
uint8_t *img_data(img_t &);
int16_t *audio_data(audio_t &);
void move_mouse(display_t::element_type *display, int deltaX, int deltaY);
void button_mouse(display_t::element_type *display, int button, bool release);
void scroll(display_t::element_type *display, int distance);
void keyboard(display_t::element_type *display, uint16_t modcode, bool release);
void gamepad_event(gamepad_t &gamepad, const gamepad_state_t &gamepad_state);
input_t input();
void move_mouse(input_t &input, int deltaX, int deltaY);
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, const gamepad_state_t &gamepad_state);
}
#endif //SUNSHINE_COMMON_H

View File

@ -13,7 +13,6 @@
#include <X11/Xutil.h>
#include <X11/XKBlib.h>
#include <X11/extensions/Xfixes.h>
#include <X11/extensions/XTest.h>
#include <pulse/simple.h>
#include <pulse/error.h>
@ -202,167 +201,6 @@ std::int16_t *audio_data(audio_t &audio) {
return (int16_t*)audio.get();
}
void move_mouse(display_t::element_type *display, int deltaX, int deltaY) {
auto &disp = *((display_attr_t*)display);
XWarpPointer(disp.display, None, None, 0, 0, 0, 0, deltaX, deltaY);
XFlush(disp.display);
}
void button_mouse(display_t::element_type *display, int button, bool release) {
auto &disp = *((display_attr_t *) display);
XTestFakeButtonEvent(disp.display, button, !release, CurrentTime);
XFlush(disp.display);
}
void scroll(display_t::element_type *display, int distance) {
auto &disp = *((display_attr_t *) display);
int button = distance > 0 ? 4 : 5;
distance = std::abs(distance / 120);
while(distance > 0) {
--distance;
XTestFakeButtonEvent(disp.display, button, True, CurrentTime);
XTestFakeButtonEvent(disp.display, button, False, CurrentTime);
XSync(disp.display, 0);
}
XFlush(disp.display);
}
uint16_t keysym(uint16_t modcode) {
constexpr auto VK_NUMPAD = 0x60;
constexpr auto VK_F1 = 0x70;
if(modcode >= VK_NUMPAD && modcode < VK_NUMPAD + 10) {
return XK_KP_0 + (modcode - VK_NUMPAD);
}
if(modcode >= VK_F1 && modcode < VK_F1 + 13) {
return XK_F1 + (modcode - VK_F1);
}
switch(modcode) {
case 0x08:
return XK_BackSpace;
case 0x09:
return XK_Tab;
case 0x0D:
return XK_Return;
case 0x13:
return XK_Pause;
case 0x14:
return XK_Caps_Lock;
case 0x1B:
return XK_Escape;
case 0x21:
return XK_Page_Up;
case 0x22:
return XK_Page_Down;
case 0x23:
return XK_End;
case 0x24:
return XK_Home;
case 0x25:
return XK_Left;
case 0x26:
return XK_Up;
case 0x27:
return XK_Right;
case 0x28:
return XK_Down;
case 0x29:
return XK_Select;
case 0x2B:
return XK_Execute;
case 0x2C:
return XK_Print; //FIXME: is this correct? (printscreen)
case 0x2D:
return XK_Insert;
case 0x2E:
return XK_Delete;
case 0x2F:
return XK_Help;
case 0x6A:
return XK_KP_Multiply;
case 0x6B:
return XK_KP_Add;
case 0x6C:
return XK_KP_Decimal; //FIXME: is this correct? (Comma)
case 0x6D:
return XK_KP_Subtract;
case 0x6E:
return XK_KP_Separator; //FIXME: is this correct? (Period)
case 0x6F:
return XK_KP_Divide;
case 0x90:
return XK_Num_Lock; //FIXME: is this correct: (NumlockClear)
case 0x91:
return XK_Scroll_Lock;
case 0xA0:
return XK_Shift_L;
case 0xA1:
return XK_Shift_R;
case 0xA2:
return XK_Control_L;
case 0xA3:
return XK_Control_R;
case 0xA4:
return XK_Alt_L;
case 0xA5: /* return XK_Alt_R; */
return XK_Super_L;
case 0xBA:
return XK_semicolon;
case 0xBB:
return XK_equal;
case 0xBC:
return XK_comma;
case 0xBD:
return XK_minus;
case 0xBE:
return XK_period;
case 0xBF:
return XK_slash;
case 0xC0:
return XK_grave;
case 0xDB:
return XK_bracketleft;
case 0xDC:
return XK_backslash;
case 0xDD:
return XK_bracketright;
case 0xDE:
return XK_apostrophe;
case 0x01: //FIXME: Moonlight doesn't support Super key
return XK_Super_L;
case 0x02:
return XK_Super_R;
}
return modcode;
}
void keyboard(display_t::element_type *display, uint16_t modcode, bool release) {
auto &disp = *((display_attr_t *) display);
KeyCode kc = XKeysymToKeycode(disp.display, keysym(modcode));
if(!kc) {
return;
}
XTestFakeKeyEvent(disp.display, kc, !release, 0);
XSync(disp.display, 0);
XFlush(disp.display);
}
void freeDisplay(void*p) {
delete (display_attr_t*)p;
}

View File

@ -376,7 +376,7 @@ void server_t::map(uint16_t type, std::function<void(const std::string_view &)>
void controlThread() {
server_t server { CONTROL_PORT };
std::shared_ptr display = platf::display();
auto input = platf::input();
server.map(packetTypes[IDX_START_A], [](const std::string_view &payload) {
session.pingTimeout = std::chrono::steady_clock::now() + config::stream.ping_timeout;
@ -420,7 +420,7 @@ void controlThread() {
std::cout << "lastFrame [" << lastFrame << ']' << std::endl;
});
server.map(packetTypes[IDX_INPUT_DATA], [display](const std::string_view &payload) mutable {
server.map(packetTypes[IDX_INPUT_DATA], [&input](const std::string_view &payload) mutable {
session.pingTimeout = std::chrono::steady_clock::now() + config::stream.ping_timeout;
std::cout << "type [IDX_INPUT_DATA]"sv << std::endl;
@ -444,7 +444,7 @@ void controlThread() {
}
input::print(plaintext.data());
input::passthrough(display.get(), plaintext.data());
input::passthrough(input, plaintext.data());
});
while(session.client_state > 0) {