From 1bc945e3dcc37a5cd0b8c080ace621e1de90448d Mon Sep 17 00:00:00 2001 From: Zoran Vuckovic Date: Thu, 18 May 2017 18:48:41 +0200 Subject: [PATCH] Add Windows raw input driver --- Makefile.common | 4 + input/drivers/winraw_input.c | 489 +++++++++++++++++++++++++++++++++++ input/input_driver.c | 3 + input/input_driver.h | 1 + input/input_keymaps.c | 103 ++++++++ input/input_keymaps.h | 1 + 6 files changed, 601 insertions(+) create mode 100644 input/drivers/winraw_input.c diff --git a/Makefile.common b/Makefile.common index 1b09409b67..068ef85c09 100644 --- a/Makefile.common +++ b/Makefile.common @@ -756,6 +756,10 @@ ifeq ($(HAVE_PARPORT), 1) OBJ += input/drivers_joypad/parport_joypad.o endif +ifneq ($(findstring Win32,$(OS)),) + OBJ += input/drivers/winraw_input.o +endif + # Companion UI ifneq ($(findstring Win32,$(OS)),) diff --git a/input/drivers/winraw_input.c b/input/drivers/winraw_input.c new file mode 100644 index 0000000000..a86dbb103a --- /dev/null +++ b/input/drivers/winraw_input.c @@ -0,0 +1,489 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2011-2017 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#define WIN32_LEAN_AND_MEAN +#include + +#include "../input_driver.h" +#include "../input_keymaps.h" +#include "../video_driver.h" +#include "../../verbosity.h" + +#define WINRAW_LOG(msg)\ + RARCH_LOG("[WINRAW]: "msg"\n") +#define WINRAW_ERR(err)\ + RARCH_ERR("[WINRAW]: "err"\n") + +#define WINRAW_SYS_WRN(fun)\ + RARCH_WARN("[WINRAW]: "fun" failed with error %lu.\n", GetLastError()) +#define WINRAW_SYS_ERR(fun)\ + RARCH_ERR("[WINRAW]: "fun" failed with error %lu.\n", GetLastError()) + +typedef struct +{ + uint8_t keys[256]; +} winraw_keyboard_t; + +typedef struct +{ + LONG x, y, dlt_x, dlt_y; + LONG whl_u, whl_d; + bool btn_l, btn_m, btn_r; +} winraw_mouse_t; + +typedef struct +{ + winraw_keyboard_t keyboard; + winraw_mouse_t mouse; + const input_device_driver_t *joypad; + HWND window; + bool kbd_mapp_block; + bool mouse_grab; +} winraw_input_t; + +static winraw_keyboard_t *g_keyboard; +static winraw_mouse_t *g_mouse; + +HWND winraw_create_window(const char *cls_name, WNDPROC wnd_proc) +{ + WNDCLASSA wc = {0}; + HWND wnd; + BOOL r; + + wc.hInstance = GetModuleHandleA(NULL); + if (!wc.hInstance) + { + WINRAW_SYS_ERR("GetModuleHandleA"); + return NULL; + } + + wc.lpfnWndProc = wnd_proc; + wc.lpszClassName = cls_name; + if (!RegisterClassA(&wc) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) + { + WINRAW_SYS_ERR("RegisterClassA"); + return NULL; + } + + wnd = CreateWindowExA(0, cls_name, NULL, 0, 0, 0, 0, 0, + HWND_MESSAGE, NULL, NULL, NULL); + if (!wnd) + { + WINRAW_SYS_ERR("CreateWindowExA"); + goto error; + } + + return wnd; + +error: + UnregisterClassA(cls_name, NULL); + return NULL; +} + +void winraw_destroy_window(HWND wnd) +{ + WINDOWINFO wi = { sizeof(WINDOWINFO), .atomWindowType = 0 }; + void *cls = 0; + BOOL r; + + if (!wnd) + return; + + r = GetWindowInfo(wnd, &wi); + if (!r) + WINRAW_SYS_WRN("GetWindowInfo"); + r = DestroyWindow(wnd); + if (!r) + WINRAW_SYS_WRN("DestroyWindow"); + if (wi.atomWindowType) + { + cls += wi.atomWindowType; + r = UnregisterClassA((LPCSTR)cls, NULL); + if (!r) + WINRAW_SYS_WRN("UnregisterClassA"); + } +} + +static bool winraw_set_keyboard_input(HWND window) +{ + RAWINPUTDEVICE rid; + BOOL r; + + rid.dwFlags = window ? 0 : RIDEV_REMOVE; + rid.hwndTarget = window; + rid.usUsagePage = 0x01; /* generic desktop */ + rid.usUsage = 0x06; /* keyboard */ + + r = RegisterRawInputDevices(&rid, 1, sizeof(RAWINPUTDEVICE)); + if (!r) + { + WINRAW_SYS_ERR("RegisterRawInputDevices"); + return false; + } + + return true; +} + +static bool winraw_set_mouse_input(HWND window, bool grab) +{ + RAWINPUTDEVICE rid; + BOOL r; + + if (window) + rid.dwFlags = grab ? RIDEV_CAPTUREMOUSE : 0; + else + rid.dwFlags = RIDEV_REMOVE; + + rid.hwndTarget = window; + rid.usUsagePage = 0x01; /* generic desktop */ + rid.usUsage = 0x02; /* mouse */ + + r = RegisterRawInputDevices(&rid, 1, sizeof(RAWINPUTDEVICE)); + if (!r) + { + WINRAW_SYS_ERR("RegisterRawInputDevices"); + return false; + } + + return true; +} + +static int16_t winraw_keyboard_state(winraw_input_t *wr, unsigned id) +{ + unsigned key; + + if (id >= RETROK_LAST) + return 0; + + key = input_keymaps_translate_rk_to_keysym((enum retro_key)id); + return wr->keyboard.keys[key]; +} + +static int16_t winraw_mouse_state(winraw_input_t *wr, bool abs, unsigned id) +{ + switch (id) + { + case RETRO_DEVICE_ID_MOUSE_X: + return abs ? wr->mouse.x : wr->mouse.dlt_x; + case RETRO_DEVICE_ID_MOUSE_Y: + return abs ? wr->mouse.y : wr->mouse.dlt_y; + case RETRO_DEVICE_ID_MOUSE_LEFT: + return wr->mouse.btn_l ? 1 : 0; + case RETRO_DEVICE_ID_MOUSE_RIGHT: + return wr->mouse.btn_r ? 1 : 0; + case RETRO_DEVICE_ID_MOUSE_WHEELUP: + return wr->mouse.whl_u ? 1 : 0; + case RETRO_DEVICE_ID_MOUSE_WHEELDOWN: + return wr->mouse.whl_d ? 1 : 0; + case RETRO_DEVICE_ID_MOUSE_MIDDLE: + return wr->mouse.btn_m ? 1 : 0; + } + + return 0; +} + +static int16_t winraw_joypad_state(winraw_input_t *wr, + rarch_joypad_info_t joypad_info, + const struct retro_keybind *binds, + unsigned port, unsigned id) +{ + const struct retro_keybind *bind = &binds[id]; + + if (!wr->kbd_mapp_block && winraw_keyboard_state(wr, bind->key)) + return 1; + + return input_joypad_pressed(wr->joypad, joypad_info, port, binds, id); +} + +static LRESULT CALLBACK winraw_callback(HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar) +{ + static uint8_t data[1024]; + RAWINPUT *ri = (RAWINPUT*)data; + UINT size = sizeof(data); + POINT crs_pos; + UINT r; + + if (msg != WM_INPUT) + return DefWindowProcA(wnd, msg, wpar, lpar); + + if (GET_RAWINPUT_CODE_WPARAM(wpar) != RIM_INPUT) /* app is in the background */ + goto end; + + r = GetRawInputData((HRAWINPUT)lpar, RID_INPUT, data, &size, sizeof(RAWINPUTHEADER)); + if (r == (UINT)-1) + { + WINRAW_SYS_WRN("GetRawInputData"); + goto end; + } + + if (ri->header.dwType == RIM_TYPEKEYBOARD) + { + if (ri->data.keyboard.Message == WM_KEYDOWN) + g_keyboard->keys[ri->data.keyboard.VKey] = 1; + else if (ri->data.keyboard.Message == WM_KEYUP) + g_keyboard->keys[ri->data.keyboard.VKey] = 0; + } + else if (ri->header.dwType == RIM_TYPEMOUSE) + { + if (ri->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) + { + g_mouse->x = ri->data.mouse.lLastX; + g_mouse->y = ri->data.mouse.lLastY; + } + else if (ri->data.mouse.lLastX || ri->data.mouse.lLastY) + { + InterlockedExchangeAdd(&g_mouse->dlt_x, ri->data.mouse.lLastX); + InterlockedExchangeAdd(&g_mouse->dlt_y, ri->data.mouse.lLastY); + + if (!GetCursorPos(&crs_pos)) + WINRAW_SYS_WRN("GetCursorPos"); + else if (!ScreenToClient((HWND)video_driver_window_get(), &crs_pos)) + WINRAW_SYS_WRN("ScreenToClient"); + else + { + g_mouse->x = crs_pos.x; + g_mouse->y = crs_pos.y; + } + } + + if (ri->data.mouse.usButtonFlags & RI_MOUSE_LEFT_BUTTON_DOWN) + g_mouse->btn_l = true; + else if (ri->data.mouse.usButtonFlags & RI_MOUSE_LEFT_BUTTON_UP) + g_mouse->btn_l = false; + + if (ri->data.mouse.usButtonFlags & RI_MOUSE_MIDDLE_BUTTON_DOWN) + g_mouse->btn_m = true; + else if (ri->data.mouse.usButtonFlags & RI_MOUSE_MIDDLE_BUTTON_UP) + g_mouse->btn_m = false; + + if (ri->data.mouse.usButtonFlags & RI_MOUSE_RIGHT_BUTTON_DOWN) + g_mouse->btn_r = true; + else if (ri->data.mouse.usButtonFlags & RI_MOUSE_RIGHT_BUTTON_UP) + g_mouse->btn_r = false; + + if (ri->data.mouse.usButtonFlags & RI_MOUSE_WHEEL) + { + if ((SHORT)ri->data.mouse.usButtonData > 0) + InterlockedExchange(&g_mouse->whl_u, 1); + else if ((SHORT)ri->data.mouse.usButtonData < 0) + InterlockedExchange(&g_mouse->whl_d, 1); + } + } + +end: + DefWindowProcA(wnd, msg, wpar, lpar); + return 0; +} + +static void *winraw_init(const char *joypad_driver) +{ + winraw_input_t *wr; + bool r; + + WINRAW_LOG("Initializing input driver ..."); + + wr = (winraw_input_t *)calloc(1, sizeof(winraw_input_t)); + g_keyboard = (winraw_keyboard_t*)calloc(1, sizeof(winraw_keyboard_t)); + g_mouse = (winraw_mouse_t*)calloc(1, sizeof(winraw_mouse_t)); + if (!wr || !g_keyboard || !g_mouse) + { + WINRAW_ERR("calloc failed."); + goto error; + } + + input_keymaps_init_keyboard_lut(rarch_key_map_winraw); + + wr->window = winraw_create_window("winraw-input", winraw_callback); + if (!wr->window) + { + WINRAW_ERR("winraw_create_window failed."); + goto error; + } + + r = winraw_set_keyboard_input(wr->window); + if (!r) + { + WINRAW_ERR("winraw_set_keyboard_input failed."); + goto error; + } + + r = winraw_set_mouse_input(wr->window, false); + if (!r) + { + WINRAW_ERR("winraw_set_mouse_input failed."); + goto error; + } + + wr->joypad = input_joypad_init_driver(joypad_driver, wr); + + WINRAW_LOG("Input driver initialized."); + return wr; + +error: + if (wr && wr->window) + { + winraw_set_mouse_input(NULL, false); + winraw_set_keyboard_input(NULL); + winraw_destroy_window(wr->window); + } + free(g_mouse); + free(g_keyboard); + free(wr); + WINRAW_ERR("Input driver initialization failed."); + return NULL; +} + +static void winraw_poll(void *d) +{ + winraw_input_t *wr = (winraw_input_t*)d; + + memcpy(&wr->keyboard, g_keyboard, sizeof(winraw_keyboard_t)); + + wr->mouse.x = g_mouse->x; + wr->mouse.y = g_mouse->y; + wr->mouse.dlt_x = InterlockedExchange(&g_mouse->dlt_x, 0); + wr->mouse.dlt_y = InterlockedExchange(&g_mouse->dlt_y, 0); + wr->mouse.whl_u = InterlockedExchange(&g_mouse->whl_u, 0); + wr->mouse.whl_d = InterlockedExchange(&g_mouse->whl_d, 0); + wr->mouse.btn_l = g_mouse->btn_l; + wr->mouse.btn_m = g_mouse->btn_m; + wr->mouse.btn_r = g_mouse->btn_r; + + if (wr->joypad) + wr->joypad->poll(); +} + +static int16_t winraw_input_state(void *d, + rarch_joypad_info_t joypad_info, + const struct retro_keybind **binds, + unsigned port, unsigned device, unsigned index, unsigned id) +{ + winraw_input_t *wr = (winraw_input_t*)d; + + switch (device) + { + case RETRO_DEVICE_KEYBOARD: + return winraw_keyboard_state(wr, id); + case RETRO_DEVICE_MOUSE: + return winraw_mouse_state(wr, false, id); + case RARCH_DEVICE_MOUSE_SCREEN: + return winraw_mouse_state(wr, true, id); + case RETRO_DEVICE_JOYPAD: + return winraw_joypad_state(wr, joypad_info, binds[port], port, id); + case RETRO_DEVICE_ANALOG: + if (binds[port]) + return input_joypad_analog(wr->joypad, joypad_info, + port, index, id, binds[port]); + } + + return 0; +} + +static bool winraw_meta_key_pressed(void *u1, int u2) +{ + return false; +} + +static void winraw_free(void *d) +{ + winraw_input_t *wr = (winraw_input_t*)d; + + WINRAW_LOG("Deinitializing input driver ..."); + + if (wr->joypad) + wr->joypad->destroy(); + winraw_set_mouse_input(NULL, false); + winraw_set_keyboard_input(NULL); + winraw_destroy_window(wr->window); + free(g_mouse); + free(g_keyboard); + free(wr); + + WINRAW_LOG("Input driver deinitialized."); +} + +static uint64_t winraw_get_capabilities(void *u) +{ + return RETRO_DEVICE_KEYBOARD | + RETRO_DEVICE_MOUSE | + RETRO_DEVICE_JOYPAD | + RETRO_DEVICE_ANALOG; +} + +static void winraw_grab_mouse(void *d, bool grab) +{ + winraw_input_t *wr = (winraw_input_t*)d; + bool r; + + if (grab == wr->mouse_grab) + return; + + r = winraw_set_mouse_input(wr->window, grab); + if (!r) + { + WINRAW_ERR("Mouse grab failed."); + return; + } + + wr->mouse_grab = grab; +} + +static bool winraw_set_rumble(void *d, unsigned port, + enum retro_rumble_effect effect, uint16_t strength) +{ + winraw_input_t *wr = (winraw_input_t*)d; + + return input_joypad_set_rumble(wr->joypad, port, effect, strength); +} + +static const input_device_driver_t *winraw_get_joypad_driver(void *d) +{ + winraw_input_t *wr = (winraw_input_t*)d; + + return wr->joypad; +} + +static bool winraw_keyboard_mapping_is_blocked(void *d) +{ + winraw_input_t *wr = (winraw_input_t*)d; + + return wr->kbd_mapp_block; +} + +static void winraw_keyboard_mapping_set_block(void *d, bool block) +{ + winraw_input_t *wr = (winraw_input_t*)d; + + wr->kbd_mapp_block = block; +} + +input_driver_t input_winraw = { + winraw_init, + winraw_poll, + winraw_input_state, + winraw_meta_key_pressed, + winraw_free, + NULL, + NULL, + winraw_get_capabilities, + "raw", + winraw_grab_mouse, + NULL, + winraw_set_rumble, + winraw_get_joypad_driver, + NULL, + winraw_keyboard_mapping_is_blocked, + winraw_keyboard_mapping_set_block, +}; diff --git a/input/input_driver.c b/input/input_driver.c index a868d244c7..7378d92627 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -94,6 +94,9 @@ static const input_driver_t *input_drivers[] = { #endif #ifdef DJGPP &input_dos, +#endif +#ifdef _WIN32 + &input_winraw, #endif &input_null, NULL, diff --git a/input/input_driver.h b/input/input_driver.h index b824a24518..d847fe2e06 100644 --- a/input/input_driver.h +++ b/input/input_driver.h @@ -366,6 +366,7 @@ extern input_driver_t input_cocoa; extern input_driver_t input_qnx; extern input_driver_t input_rwebinput; extern input_driver_t input_dos; +extern input_driver_t input_winraw; extern input_driver_t input_null; RETRO_END_DECLS diff --git a/input/input_keymaps.c b/input/input_keymaps.c index 9bc933bf55..3f43db63d5 100644 --- a/input/input_keymaps.c +++ b/input/input_keymaps.c @@ -1515,6 +1515,109 @@ const struct rarch_key_map rarch_key_map_dos[] = { }; #endif +#ifdef _WIN32 +const struct rarch_key_map rarch_key_map_winraw[] = { + { VK_BACK, RETROK_BACKSPACE }, + { VK_TAB, RETROK_TAB }, + { VK_CLEAR, RETROK_CLEAR }, + { VK_RETURN, RETROK_RETURN }, + { VK_PAUSE, RETROK_PAUSE }, + { VK_ESCAPE, RETROK_ESCAPE }, + { VK_MODECHANGE, RETROK_MODE }, + { VK_SPACE, RETROK_SPACE }, + { VK_PRIOR, RETROK_PAGEUP }, + { VK_NEXT, RETROK_PAGEDOWN }, + { VK_END, RETROK_END }, + { VK_HOME, RETROK_HOME }, + { VK_LEFT, RETROK_LEFT }, + { VK_UP, RETROK_UP }, + { VK_RIGHT, RETROK_RIGHT }, + { VK_DOWN, RETROK_DOWN }, + { VK_PRINT, RETROK_PRINT }, + { VK_INSERT, RETROK_INSERT }, + { VK_DELETE, RETROK_DELETE }, + { VK_HELP, RETROK_HELP }, + { 0x30, RETROK_0 }, + { 0x31, RETROK_1 }, + { 0x32, RETROK_2 }, + { 0x33, RETROK_3 }, + { 0x34, RETROK_4 }, + { 0x35, RETROK_5 }, + { 0x36, RETROK_6 }, + { 0x37, RETROK_7 }, + { 0x38, RETROK_8 }, + { 0x39, RETROK_9 }, + { 0x41, RETROK_a }, + { 0x42, RETROK_b }, + { 0x43, RETROK_c }, + { 0x44, RETROK_d }, + { 0x45, RETROK_e }, + { 0x46, RETROK_f }, + { 0x47, RETROK_g }, + { 0x48, RETROK_h }, + { 0x49, RETROK_i }, + { 0x4A, RETROK_j }, + { 0x4B, RETROK_k }, + { 0x4C, RETROK_l }, + { 0x4D, RETROK_m }, + { 0x4E, RETROK_n }, + { 0x4F, RETROK_o }, + { 0x50, RETROK_p }, + { 0x51, RETROK_q }, + { 0x52, RETROK_r }, + { 0x53, RETROK_s }, + { 0x54, RETROK_t }, + { 0x55, RETROK_u }, + { 0x56, RETROK_v }, + { 0x57, RETROK_w }, + { 0x58, RETROK_x }, + { 0x59, RETROK_y }, + { 0x5A, RETROK_z }, + { VK_LWIN, RETROK_LSUPER }, + { VK_RWIN, RETROK_RSUPER }, + { VK_APPS, RETROK_MENU }, + { VK_NUMPAD0, RETROK_KP0 }, + { VK_NUMPAD1, RETROK_KP1 }, + { VK_NUMPAD2, RETROK_KP2 }, + { VK_NUMPAD3, RETROK_KP3 }, + { VK_NUMPAD4, RETROK_KP4 }, + { VK_NUMPAD5, RETROK_KP5 }, + { VK_NUMPAD6, RETROK_KP6 }, + { VK_NUMPAD7, RETROK_KP7 }, + { VK_NUMPAD8, RETROK_KP8 }, + { VK_NUMPAD9, RETROK_KP9 }, + { VK_MULTIPLY, RETROK_KP_MULTIPLY }, + { VK_ADD, RETROK_KP_PLUS }, + { VK_SUBTRACT, RETROK_KP_MINUS }, + { VK_DECIMAL, RETROK_KP_PERIOD }, + { VK_DIVIDE, RETROK_KP_DIVIDE }, + { VK_F1, RETROK_F1 }, + { VK_F2, RETROK_F2 }, + { VK_F3, RETROK_F3 }, + { VK_F4, RETROK_F4 }, + { VK_F5, RETROK_F5 }, + { VK_F6, RETROK_F6 }, + { VK_F7, RETROK_F7 }, + { VK_F8, RETROK_F8 }, + { VK_F9, RETROK_F9 }, + { VK_F10, RETROK_F10 }, + { VK_F11, RETROK_F11 }, + { VK_F12, RETROK_F12 }, + { VK_F13, RETROK_F13 }, + { VK_F14, RETROK_F14 }, + { VK_F15, RETROK_F15 }, + { VK_NUMLOCK, RETROK_NUMLOCK }, + { VK_SCROLL, RETROK_SCROLLOCK }, + { VK_LSHIFT, RETROK_LSHIFT }, + { VK_RSHIFT, RETROK_RSHIFT }, + { VK_LCONTROL, RETROK_LCTRL }, + { VK_RCONTROL, RETROK_RCTRL }, + { VK_LMENU, RETROK_LALT }, + { VK_RMENU, RETROK_RALT }, + { 0, RETROK_UNKNOWN } +}; +#endif + static enum retro_key rarch_keysym_lut[RETROK_LAST]; /** diff --git a/input/input_keymaps.h b/input/input_keymaps.h index df3cb48790..001727a760 100644 --- a/input/input_keymaps.h +++ b/input/input_keymaps.h @@ -59,6 +59,7 @@ extern const struct rarch_key_map rarch_key_map_android[]; extern const struct rarch_key_map rarch_key_map_qnx[]; extern const struct rarch_key_map rarch_key_map_dos[]; extern const struct rarch_key_map rarch_key_map_wiiu[]; +extern const struct rarch_key_map rarch_key_map_winraw[]; /** * input_keymaps_init_keyboard_lut: