#include #include #include #include #include #include #include #include #include #include "common.h" #include "sunshine/utility.h" namespace platf { using namespace std::literals; using evdev_t = util::safe_ptr; using uinput_t = util::safe_ptr; struct input_raw_t { evdev_t gamepad_dev; uinput_t gamepad_input; evdev_t mouse_dev; uinput_t mouse_input; display_t display; }; //TODO: Use libevdev for keyboard and mouse, then any mention of X11 can be removed from linux_evdev.cpp struct display_attr_t { display_attr_t() : display { XOpenDisplay(nullptr) }, window { DefaultRootWindow(display) }, attr {} { XGetWindowAttributes(display, window, &attr); } ~display_attr_t() { XCloseDisplay(display); } Display *display; Window window; XWindowAttributes attr; }; void move_mouse(input_t &input, int deltaX, int deltaY) { auto mouse = ((input_raw_t*)input.get())->mouse_input.get(); if(deltaX) { libevdev_uinput_write_event(mouse, EV_REL, REL_X, deltaX); } if(deltaY) { libevdev_uinput_write_event(mouse, EV_REL, REL_Y, deltaY); } libevdev_uinput_write_event(mouse, EV_SYN, SYN_REPORT, 0); } void button_mouse(input_t &input, int button, bool release) { int btn_type; int scan; if(button == 1) { btn_type = BTN_LEFT; scan = 90001; } else if(button == 2) { btn_type = BTN_MIDDLE; scan = 90003; } else { btn_type = BTN_RIGHT; scan = 90002; } auto mouse = ((input_raw_t*)input.get())->mouse_input.get(); libevdev_uinput_write_event(mouse, EV_MSC, MSC_SCAN, scan); libevdev_uinput_write_event(mouse, EV_KEY, btn_type, release ? 0 : 1); libevdev_uinput_write_event(mouse, EV_SYN, SYN_REPORT, 0); } void scroll(input_t &input, int high_res_distance) { int distance = high_res_distance / 120; auto mouse = ((input_raw_t*)input.get())->mouse_input.get(); libevdev_uinput_write_event(mouse, EV_REL, REL_WHEEL, distance); libevdev_uinput_write_event(mouse, EV_REL, REL_WHEEL_HI_RES, high_res_distance); libevdev_uinput_write_event(mouse, EV_SYN, SYN_REPORT, 0); } 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(input_t &input, uint16_t modcode, bool release) { auto &disp = *((display_attr_t *) ((input_raw_t*)input.get())->display.get()); KeyCode kc = XKeysymToKeycode(disp.display, keysym(modcode)); if(!kc) { return; } XTestFakeKeyEvent(disp.display, kc, !release, 0); XSync(disp.display, 0); XFlush(disp.display); } namespace gp { // up pressed == -1, down pressed == 1, else 0 void dpad_y(input_t &input, int button_state) { auto &gp = *(input_raw_t*)input.get(); libevdev_uinput_write_event(gp.gamepad_input.get(), EV_ABS, ABS_HAT0Y, button_state); } // left pressed == -1, right pressed == 1, else 0 void dpad_x(input_t &input, int button_state) { auto &gp = *(input_raw_t*)input.get(); libevdev_uinput_write_event(gp.gamepad_input.get(), EV_ABS, ABS_HAT0X, button_state); } void start(input_t &input, int button_down) { auto &gp = *(input_raw_t*)input.get(); libevdev_uinput_write_event(gp.gamepad_input.get(), EV_KEY, BTN_START, button_down); } void back(input_t &input, int button_down) { auto &gp = *(input_raw_t*)input.get(); libevdev_uinput_write_event(gp.gamepad_input.get(), EV_KEY, BTN_SELECT, button_down); } void left_stick(input_t &input, int button_down) { auto &gp = *(input_raw_t*)input.get(); libevdev_uinput_write_event(gp.gamepad_input.get(), EV_KEY, BTN_THUMBL, button_down); } void right_stick(input_t &input, int button_down) { auto &gp = *(input_raw_t*)input.get(); libevdev_uinput_write_event(gp.gamepad_input.get(), EV_KEY, BTN_THUMBR, button_down); } void left_button(input_t &input, int button_down) { auto &gp = *(input_raw_t*)input.get(); libevdev_uinput_write_event(gp.gamepad_input.get(), EV_KEY, BTN_TL, button_down); } void right_button(input_t &input, int button_down) { auto &gp = *(input_raw_t*)input.get(); libevdev_uinput_write_event(gp.gamepad_input.get(), EV_KEY, BTN_TR, button_down); } void home(input_t &input, int button_down) { auto &gp = *(input_raw_t*)input.get(); libevdev_uinput_write_event(gp.gamepad_input.get(), EV_KEY, BTN_MODE, button_down); } void a(input_t &input, int button_down) { auto &gp = *(input_raw_t*)input.get(); libevdev_uinput_write_event(gp.gamepad_input.get(), EV_KEY, BTN_SOUTH, button_down); } void b(input_t &input, int button_down) { auto &gp = *(input_raw_t*)input.get(); libevdev_uinput_write_event(gp.gamepad_input.get(), EV_KEY, BTN_EAST, button_down); } void x(input_t &input, int button_down) { auto &gp = *(input_raw_t*)input.get(); libevdev_uinput_write_event(gp.gamepad_input.get(), EV_KEY, BTN_NORTH, button_down); } void y(input_t &input, int button_down) { auto &gp = *(input_raw_t*)input.get(); libevdev_uinput_write_event(gp.gamepad_input.get(), EV_KEY, BTN_WEST, button_down); } void left_trigger(input_t &input, std::uint8_t abs_z) { auto &gp = *(input_raw_t*)input.get(); libevdev_uinput_write_event(gp.gamepad_input.get(), EV_ABS, ABS_Z, abs_z); } void right_trigger(input_t &input, std::uint8_t abs_z) { auto &gp = *(input_raw_t*)input.get(); libevdev_uinput_write_event(gp.gamepad_input.get(), EV_ABS, ABS_RZ, abs_z); } void left_stick_x(input_t &input, std::int16_t x) { auto &gp = *(input_raw_t*)input.get(); libevdev_uinput_write_event(gp.gamepad_input.get(), EV_ABS, ABS_X, x); } void left_stick_y(input_t &input, std::int16_t y) { auto &gp = *(input_raw_t*)input.get(); libevdev_uinput_write_event(gp.gamepad_input.get(), EV_ABS, ABS_Y, -y); } void right_stick_x(input_t &input, std::int16_t x) { auto &gp = *(input_raw_t*)input.get(); libevdev_uinput_write_event(gp.gamepad_input.get(), EV_ABS, ABS_RX, x); } void right_stick_y(input_t &input, std::int16_t y) { auto &gp = *(input_raw_t*)input.get(); libevdev_uinput_write_event(gp.gamepad_input.get(), EV_ABS, ABS_RY, -y); } void sync(input_t &input) { auto &gp = *(input_raw_t*)input.get(); libevdev_uinput_write_event(gp.gamepad_input.get(), EV_SYN, SYN_REPORT, 0); } } int mouse(input_raw_t &gp) { gp.mouse_dev.reset(libevdev_new()); libevdev_set_uniq(gp.mouse_dev.get(), "Sunshine Gamepad"); libevdev_set_id_product(gp.mouse_dev.get(), 0x4038); libevdev_set_id_vendor(gp.mouse_dev.get(), 0x46D); libevdev_set_id_bustype(gp.mouse_dev.get(), 0x3); libevdev_set_id_version(gp.mouse_dev.get(), 0x111); libevdev_set_name(gp.mouse_dev.get(), "Logitech Wireless Mouse PID:4038"); libevdev_enable_event_type(gp.mouse_dev.get(), EV_KEY); libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, BTN_LEFT, nullptr); libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, BTN_RIGHT, nullptr); libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, BTN_MIDDLE, nullptr); libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, BTN_SIDE, nullptr); libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, BTN_EXTRA, nullptr); libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, BTN_FORWARD, nullptr); libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, BTN_BACK, nullptr); libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, BTN_TASK, nullptr); libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, 280, nullptr); libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, 281, nullptr); libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, 282, nullptr); libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, 283, nullptr); libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, 284, nullptr); libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, 285, nullptr); libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, 286, nullptr); libevdev_enable_event_code(gp.mouse_dev.get(), EV_KEY, 287, nullptr); libevdev_enable_event_type(gp.mouse_dev.get(), EV_REL); libevdev_enable_event_code(gp.mouse_dev.get(), EV_REL, REL_X, nullptr); libevdev_enable_event_code(gp.mouse_dev.get(), EV_REL, REL_Y, nullptr); libevdev_enable_event_code(gp.mouse_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(gp.mouse_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_type(gp.mouse_dev.get(), EV_MSC); libevdev_enable_event_code(gp.mouse_dev.get(), EV_MSC, MSC_SCAN, nullptr); libevdev_uinput *buf; int err = libevdev_uinput_create_from_device(gp.mouse_dev.get(), LIBEVDEV_UINPUT_OPEN_MANAGED, &buf); gp.mouse_input.reset(buf); if(err) { std::cout << "Could not create Sunshine Mouse: "sv << strerror(-err) << std::endl; return -1; } return 0; } int gamepad(input_raw_t &gp) { gp.gamepad_dev.reset(libevdev_new()); input_absinfo stick { 0, -32768, 32767, 16, 128, 0 }; input_absinfo trigger { 0, 0, 255, 0, 0, 0 }; input_absinfo dpad { 0, -1, 1, 0, 0, 0 }; libevdev_set_uniq(gp.gamepad_dev.get(), "Sunshine Gamepad"); libevdev_set_id_product(gp.gamepad_dev.get(), 0x28E); libevdev_set_id_vendor(gp.gamepad_dev.get(), 0x45E); libevdev_set_id_bustype(gp.gamepad_dev.get(), 0x3); libevdev_set_id_version(gp.gamepad_dev.get(), 0x110); libevdev_set_name(gp.gamepad_dev.get(), "Microsoft X-Box 360 pad"); libevdev_enable_event_type(gp.gamepad_dev.get(), EV_KEY); libevdev_enable_event_code(gp.gamepad_dev.get(), EV_KEY, BTN_WEST, nullptr); libevdev_enable_event_code(gp.gamepad_dev.get(), EV_KEY, BTN_EAST, nullptr); libevdev_enable_event_code(gp.gamepad_dev.get(), EV_KEY, BTN_NORTH, nullptr); libevdev_enable_event_code(gp.gamepad_dev.get(), EV_KEY, BTN_SOUTH, nullptr); libevdev_enable_event_code(gp.gamepad_dev.get(), EV_KEY, BTN_THUMBL, nullptr); libevdev_enable_event_code(gp.gamepad_dev.get(), EV_KEY, BTN_THUMBR, nullptr); libevdev_enable_event_code(gp.gamepad_dev.get(), EV_KEY, BTN_TR, nullptr); libevdev_enable_event_code(gp.gamepad_dev.get(), EV_KEY, BTN_TL, nullptr); libevdev_enable_event_code(gp.gamepad_dev.get(), EV_KEY, BTN_SELECT, nullptr); libevdev_enable_event_code(gp.gamepad_dev.get(), EV_KEY, BTN_MODE, nullptr); libevdev_enable_event_code(gp.gamepad_dev.get(), EV_KEY, BTN_START, nullptr); libevdev_enable_event_type(gp.gamepad_dev.get(), EV_ABS); libevdev_enable_event_code(gp.gamepad_dev.get(), EV_ABS, ABS_HAT0Y, &dpad); libevdev_enable_event_code(gp.gamepad_dev.get(), EV_ABS, ABS_HAT0X, &dpad); libevdev_enable_event_code(gp.gamepad_dev.get(), EV_ABS, ABS_Z, &trigger); libevdev_enable_event_code(gp.gamepad_dev.get(), EV_ABS, ABS_RZ, &trigger); libevdev_enable_event_code(gp.gamepad_dev.get(), EV_ABS, ABS_X, &stick); libevdev_enable_event_code(gp.gamepad_dev.get(), EV_ABS, ABS_RX, &stick); libevdev_enable_event_code(gp.gamepad_dev.get(), EV_ABS, ABS_Y, &stick); libevdev_enable_event_code(gp.gamepad_dev.get(), EV_ABS, ABS_RY, &stick); libevdev_uinput *buf; int err = libevdev_uinput_create_from_device(gp.gamepad_dev.get(), LIBEVDEV_UINPUT_OPEN_MANAGED, &buf); gp.gamepad_input.reset(buf); if(err) { std::cout << "Could not create Sunshine Gamepad: "sv << strerror(-err) << std::endl; return -1; } return 0; } input_t input() { input_t result { new input_raw_t() }; auto &gp = *(input_raw_t*)result.get(); if(gamepad(gp)) { return nullptr; } if(mouse(gp)) { return nullptr; } std::filesystem::path mouse_path { "sunshine_mouse" }; std::filesystem::path gamepad_path { "sunshine_gamepad" }; if(std::filesystem::exists(mouse_path)) { std::filesystem::remove(mouse_path); } if(std::filesystem::exists(gamepad_path)) { std::filesystem::remove(gamepad_path); } std::filesystem::create_symlink(libevdev_uinput_get_devnode(gp.mouse_input.get()), mouse_path); std::filesystem::create_symlink(libevdev_uinput_get_devnode(gp.gamepad_input.get()), gamepad_path); gp.display = display(); return result; } void freeInput(void *p) { auto *input = (input_raw_t*)p; delete input; } }