/* 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 . */ #include #include "../input_driver.h" #include "../input_keymaps.h" #include "../../configuration.h" #include "../../gfx/video_driver.h" #include "../../verbosity.h" typedef struct { uint8_t keys[256]; } winraw_keyboard_t; typedef struct { HANDLE hnd; LONG x, y, dlt_x, dlt_y; LONG whl_u, whl_d; bool btn_l, btn_m, btn_r, btn_b4, btn_b5; } winraw_mouse_t; typedef struct { bool kbd_mapp_block; bool mouse_grab; winraw_keyboard_t keyboard; HWND window; winraw_mouse_t *mice; const input_device_driver_t *joypad; } winraw_input_t; static winraw_keyboard_t *g_keyboard = NULL; static winraw_mouse_t *g_mice = NULL; static unsigned g_mouse_cnt = 0; static bool g_mouse_xy_mapping_ready = false; static double g_view_abs_ratio_x = 0.0; static double g_view_abs_ratio_y = 0.0; static HWND winraw_create_window(WNDPROC wnd_proc) { HWND wnd; WNDCLASSA wc = {0}; wc.hInstance = GetModuleHandleA(NULL); if (!wc.hInstance) { RARCH_ERR("[WINRAW]: GetModuleHandleA failed with error %lu.\n", GetLastError()); return NULL; } wc.lpfnWndProc = wnd_proc; wc.lpszClassName = "winraw-input"; if (!RegisterClassA(&wc) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) { RARCH_ERR("[WINRAW]: RegisterClassA failed with error %lu.\n", GetLastError()); return NULL; } wnd = CreateWindowExA(0, wc.lpszClassName, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); if (!wnd) { RARCH_ERR("[WINRAW]: CreateWindowExA failed with error %lu.\n", GetLastError()); goto error; } return wnd; error: UnregisterClassA(wc.lpszClassName, NULL); return NULL; } static void winraw_destroy_window(HWND wnd) { BOOL r; if (!wnd) return; r = DestroyWindow(wnd); if (!r) { RARCH_WARN("[WINRAW]: DestroyWindow failed with error %lu.\n", GetLastError()); } r = UnregisterClassA("winraw-input", NULL); if (!r) { RARCH_WARN("[WINRAW]: UnregisterClassA failed with error %lu.\n", GetLastError()); } } 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) { RARCH_ERR("[WINRAW]: RegisterRawInputDevices failed with error %lu.\n", GetLastError()); return false; } return true; } static void winraw_log_mice_info(winraw_mouse_t *mice, unsigned mouse_cnt) { UINT r; unsigned i; char name[256]; UINT name_size = sizeof(name); for (i = 0; i < mouse_cnt; ++i) { r = GetRawInputDeviceInfoA(mice[i].hnd, RIDI_DEVICENAME, name, &name_size); if (r == (UINT)-1 || r == 0) name[0] = '\0'; RARCH_LOG("[WINRAW]: Mouse #%u %s.\n", i, name); } } static bool winraw_init_devices(winraw_mouse_t **mice, unsigned *mouse_cnt) { UINT i; POINT crs_pos; winraw_mouse_t *mice_r = NULL; unsigned mouse_cnt_r = 0; RAWINPUTDEVICELIST *devs = NULL; UINT dev_cnt = 0; UINT r = GetRawInputDeviceList( NULL, &dev_cnt, sizeof(RAWINPUTDEVICELIST)); if (r == (UINT)-1) { RARCH_ERR("[WINRAW]: GetRawInputDeviceList failed with error %lu.\n", GetLastError()); goto error; } devs = (RAWINPUTDEVICELIST*)malloc(dev_cnt * sizeof(RAWINPUTDEVICELIST)); if (!devs) goto error; dev_cnt = GetRawInputDeviceList(devs, &dev_cnt, sizeof(RAWINPUTDEVICELIST)); if (dev_cnt == (UINT)-1) { RARCH_ERR("[WINRAW]: GetRawInputDeviceList failed with error %lu.\n", GetLastError()); goto error; } for (i = 0; i < dev_cnt; ++i) mouse_cnt_r += devs[i].dwType == RIM_TYPEMOUSE ? 1 : 0; if (mouse_cnt_r) { mice_r = (winraw_mouse_t*)calloc(1, mouse_cnt_r * sizeof(winraw_mouse_t)); if (!mice_r) goto error; if (!GetCursorPos(&crs_pos)) goto error; for (i = 0; i < mouse_cnt_r; ++i) { mice_r[i].x = crs_pos.x; mice_r[i].y = crs_pos.y; } } /* count is already checked, so this is safe */ for (i = mouse_cnt_r = 0; i < dev_cnt; ++i) { if (devs[i].dwType == RIM_TYPEMOUSE) mice_r[mouse_cnt_r++].hnd = devs[i].hDevice; } winraw_log_mice_info(mice_r, mouse_cnt_r); *mice = mice_r; *mouse_cnt = mouse_cnt_r; return true; error: free(devs); free(mice_r); *mice = NULL; *mouse_cnt = 0; return false; } 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) { RARCH_ERR("[WINRAW]: RegisterRawInputDevice failed with error %lu.\n", GetLastError()); return false; } return true; } static int16_t winraw_lightgun_aiming_state(winraw_input_t *wr, unsigned port, unsigned id) { const int edge_detect = 32700; struct video_viewport vp; bool inside = false; unsigned i; settings_t *settings = config_get_ptr(); winraw_mouse_t *mouse = NULL; int16_t res_x = 0; int16_t res_y = 0; int16_t res_screen_x = 0; int16_t res_screen_y = 0; if (port >= MAX_USERS) return 0; for (i = 0; i < g_mouse_cnt; ++i) { if (i == settings->uints.input_mouse_index[port]) { mouse = &wr->mice[i]; break; } } if (!mouse) return 0; vp.x = 0; vp.y = 0; vp.width = 0; vp.height = 0; vp.full_width = 0; vp.full_height = 0; if ( !( video_driver_translate_coord_viewport_wrap( &vp, mouse->x, mouse->y, &res_x, &res_y, &res_screen_x, &res_screen_y ) ) ) { return 0; } inside = (res_x >= -edge_detect) && (res_y >= -edge_detect) && (res_x <= edge_detect) && (res_y <= edge_detect); switch ( id ) { case RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X: return inside ? res_x : 0; case RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y: return inside ? res_y : 0; case RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN: return !inside; default: break; } return 0; } static int16_t winraw_mouse_state(winraw_input_t *wr, unsigned port, bool abs, unsigned id) { unsigned i; settings_t *settings = config_get_ptr(); winraw_mouse_t *mouse = NULL; if (port >= MAX_USERS) return 0; for (i = 0; i < g_mouse_cnt; ++i) { if (i == settings->uints.input_mouse_index[port]) { mouse = &wr->mice[i]; break; } } if (!mouse) return 0; switch (id) { case RETRO_DEVICE_ID_MOUSE_X: return abs ? mouse->x : mouse->dlt_x; case RETRO_DEVICE_ID_MOUSE_Y: return abs ? mouse->y : mouse->dlt_y; case RETRO_DEVICE_ID_MOUSE_LEFT: return mouse->btn_l ? 1 : 0; case RETRO_DEVICE_ID_MOUSE_RIGHT: return mouse->btn_r ? 1 : 0; case RETRO_DEVICE_ID_MOUSE_WHEELUP: return mouse->whl_u ? 1 : 0; case RETRO_DEVICE_ID_MOUSE_WHEELDOWN: return mouse->whl_d ? 1 : 0; case RETRO_DEVICE_ID_MOUSE_MIDDLE: return mouse->btn_m ? 1 : 0; case RETRO_DEVICE_ID_MOUSE_BUTTON_4: return mouse->btn_b4 ? 1 : 0; case RETRO_DEVICE_ID_MOUSE_BUTTON_5: return mouse->btn_b5 ? 1 : 0; } return 0; } static bool winraw_keyboard_pressed(winraw_input_t *wr, unsigned key) { unsigned k = rarch_keysym_lut[(enum retro_key)key]; return wr->keyboard.keys[k]; } static bool winraw_mbutton_pressed(winraw_input_t *wr, unsigned port, unsigned key) { unsigned i; winraw_mouse_t *mouse = NULL; settings_t *settings = config_get_ptr(); if (port >= MAX_USERS) return false; for (i = 0; i < g_mouse_cnt; ++i) { if (i == settings->uints.input_mouse_index[port]) { mouse = &wr->mice[i]; break; } } if (!mouse) return false; switch ( key ) { case RETRO_DEVICE_ID_MOUSE_LEFT: return mouse->btn_l; case RETRO_DEVICE_ID_MOUSE_RIGHT: return mouse->btn_r; case RETRO_DEVICE_ID_MOUSE_MIDDLE: return mouse->btn_m; case RETRO_DEVICE_ID_MOUSE_BUTTON_4: return mouse->btn_b4; case RETRO_DEVICE_ID_MOUSE_BUTTON_5: return mouse->btn_b5; case RETRO_DEVICE_ID_MOUSE_WHEELUP: return mouse->whl_u; case RETRO_DEVICE_ID_MOUSE_WHEELDOWN: return mouse->whl_d; } return false; } static bool winraw_is_pressed(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 && (bind->key < RETROK_LAST) && winraw_keyboard_pressed(wr, bind->key)) return true; if (binds && binds[id].valid) { if (winraw_mbutton_pressed(wr, port, bind->mbutton)) return true; if (input_joypad_pressed(wr->joypad, joypad_info, port, binds, id)) return true; } return false; } static void winraw_init_mouse_xy_mapping() { struct video_viewport viewport; int center_x; int center_y; unsigned i; if (video_driver_get_viewport_info(&viewport)) { center_x = viewport.x + viewport.width / 2; center_y = viewport.y + viewport.height / 2; for (i = 0; i < g_mouse_cnt; ++i) { g_mice[i].x = center_x; g_mice[i].y = center_y; } g_view_abs_ratio_x = (double)viewport.full_width / 65535.0; g_view_abs_ratio_y = (double)viewport.full_height / 65535.0; g_mouse_xy_mapping_ready = true; } } static int16_t winraw_deprecated_lightgun_state(winraw_input_t *wr, unsigned port, unsigned id) { unsigned i; settings_t *settings = config_get_ptr(); winraw_mouse_t *mouse = NULL; if (port >= MAX_USERS) return 0; for (i = 0; i < g_mouse_cnt; ++i) { if (i == settings->uints.input_mouse_index[port]) { mouse = &wr->mice[i]; break; } } if (!mouse) return 0; switch (id) { case RETRO_DEVICE_ID_LIGHTGUN_X: return mouse->dlt_x; case RETRO_DEVICE_ID_LIGHTGUN_Y: return mouse->dlt_y; } return 0; } static void winraw_update_mouse_state(winraw_mouse_t *mouse, RAWMOUSE *state) { POINT crs_pos; if (state->usFlags & MOUSE_MOVE_ABSOLUTE) { if (g_mouse_xy_mapping_ready) { state->lLastX = (LONG)(g_view_abs_ratio_x * state->lLastX); state->lLastY = (LONG)(g_view_abs_ratio_y * state->lLastY); InterlockedExchangeAdd(&mouse->dlt_x, state->lLastX - mouse->x); InterlockedExchangeAdd(&mouse->dlt_y, state->lLastY - mouse->y); mouse->x = state->lLastX; mouse->y = state->lLastY; } else winraw_init_mouse_xy_mapping(); } else if (state->lLastX || state->lLastY) { InterlockedExchangeAdd(&mouse->dlt_x, state->lLastX); InterlockedExchangeAdd(&mouse->dlt_y, state->lLastY); if (!GetCursorPos(&crs_pos)) { RARCH_WARN("[WINRAW]: GetCursorPos failed with error %lu.\n", GetLastError()); } else if (!ScreenToClient((HWND)video_driver_window_get(), &crs_pos)) { RARCH_WARN("[WINRAW]: ScreenToClient failed with error %lu.\n", GetLastError()); } else { mouse->x = crs_pos.x; mouse->y = crs_pos.y; } } if (state->usButtonFlags & RI_MOUSE_LEFT_BUTTON_DOWN) mouse->btn_l = true; else if (state->usButtonFlags & RI_MOUSE_LEFT_BUTTON_UP) mouse->btn_l = false; if (state->usButtonFlags & RI_MOUSE_MIDDLE_BUTTON_DOWN) mouse->btn_m = true; else if (state->usButtonFlags & RI_MOUSE_MIDDLE_BUTTON_UP) mouse->btn_m = false; if (state->usButtonFlags & RI_MOUSE_RIGHT_BUTTON_DOWN) mouse->btn_r = true; else if (state->usButtonFlags & RI_MOUSE_RIGHT_BUTTON_UP) mouse->btn_r = false; if (state->usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) mouse->btn_b4 = true; else if (state->usButtonFlags & RI_MOUSE_BUTTON_4_UP) mouse->btn_b4 = false; if (state->usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) mouse->btn_b5 = true; else if (state->usButtonFlags & RI_MOUSE_BUTTON_5_UP) mouse->btn_b5 = false; if (state->usButtonFlags & RI_MOUSE_WHEEL) { if ((SHORT)state->usButtonData > 0) InterlockedExchange(&mouse->whl_u, 1); else if ((SHORT)state->usButtonData < 0) InterlockedExchange(&mouse->whl_d, 1); } } static LRESULT CALLBACK winraw_callback(HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar) { static uint8_t data[1024]; UINT r; unsigned i; RAWINPUT *ri = (RAWINPUT*)data; UINT size = sizeof(data); if (msg != WM_INPUT) return DefWindowProcA(wnd, msg, wpar, lpar); /* app is in the background */ if (GET_RAWINPUT_CODE_WPARAM(wpar) != RIM_INPUT) goto end; r = GetRawInputData((HRAWINPUT)lpar, RID_INPUT, data, &size, sizeof(RAWINPUTHEADER)); if (r == (UINT)-1) { RARCH_WARN("[WINRAW]: GetRawInputData failed with error %lu.\n", GetLastError()); 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) { for (i = 0; i < g_mouse_cnt; ++i) { if (g_mice[i].hnd == ri->header.hDevice) { winraw_update_mouse_state(&g_mice[i], &ri->data.mouse); break; } } } end: DefWindowProcA(wnd, msg, wpar, lpar); return 0; } static void *winraw_init(const char *joypad_driver) { bool r; winraw_input_t *wr = (winraw_input_t *) calloc(1, sizeof(winraw_input_t)); g_keyboard = (winraw_keyboard_t*) calloc(1, sizeof(winraw_keyboard_t)); if (!wr || !g_keyboard) goto error; RARCH_LOG("[WINRAW]: Initializing input driver... \n"); input_keymaps_init_keyboard_lut(rarch_key_map_winraw); wr->window = winraw_create_window(winraw_callback); if (!wr->window) goto error; r = winraw_init_devices(&g_mice, &g_mouse_cnt); if (!r) goto error; if (!g_mouse_cnt) { RARCH_LOG("[WINRAW]: Mouse unavailable.\n"); } else { wr->mice = (winraw_mouse_t*) malloc(g_mouse_cnt * sizeof(winraw_mouse_t)); if (!wr->mice) goto error; memcpy(wr->mice, g_mice, g_mouse_cnt * sizeof(winraw_mouse_t)); } r = winraw_set_keyboard_input(wr->window); if (!r) goto error; r = winraw_set_mouse_input(wr->window, false); if (!r) goto error; wr->joypad = input_joypad_init_driver(joypad_driver, wr); 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_keyboard); free(g_mice); if (wr) free(wr->mice); free(wr); return NULL; } static void winraw_poll(void *d) { unsigned i; winraw_input_t *wr = (winraw_input_t*)d; memcpy(&wr->keyboard, g_keyboard, sizeof(winraw_keyboard_t)); /* following keys are not handled by windows raw input api */ wr->keyboard.keys[VK_LCONTROL] = GetAsyncKeyState(VK_LCONTROL) >> 1 ? 1 : 0; wr->keyboard.keys[VK_RCONTROL] = GetAsyncKeyState(VK_RCONTROL) >> 1 ? 1 : 0; wr->keyboard.keys[VK_LMENU] = GetAsyncKeyState(VK_LMENU) >> 1 ? 1 : 0; wr->keyboard.keys[VK_RMENU] = GetAsyncKeyState(VK_RMENU) >> 1 ? 1 : 0; wr->keyboard.keys[VK_LSHIFT] = GetAsyncKeyState(VK_LSHIFT) >> 1 ? 1 : 0; wr->keyboard.keys[VK_RSHIFT] = GetAsyncKeyState(VK_RSHIFT) >> 1 ? 1 : 0; for (i = 0; i < g_mouse_cnt; ++i) { wr->mice[i].x = g_mice[i].x; wr->mice[i].y = g_mice[i].y; wr->mice[i].dlt_x = InterlockedExchange(&g_mice[i].dlt_x, 0); wr->mice[i].dlt_y = InterlockedExchange(&g_mice[i].dlt_y, 0); wr->mice[i].whl_u = InterlockedExchange(&g_mice[i].whl_u, 0); wr->mice[i].whl_d = InterlockedExchange(&g_mice[i].whl_d, 0); wr->mice[i].btn_l = g_mice[i].btn_l; wr->mice[i].btn_m = g_mice[i].btn_m; wr->mice[i].btn_r = g_mice[i].btn_r; wr->mice[i].btn_b4 = g_mice[i].btn_b4; wr->mice[i].btn_b5 = g_mice[i].btn_b5; } 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_JOYPAD: if (id < RARCH_BIND_LIST_END) return winraw_is_pressed(wr, joypad_info, binds[port], port, id); break; case RETRO_DEVICE_KEYBOARD: return (id < RETROK_LAST) && winraw_keyboard_pressed(wr, id); case RETRO_DEVICE_MOUSE: return winraw_mouse_state(wr, port, false, id); case RARCH_DEVICE_MOUSE_SCREEN: return winraw_mouse_state(wr, port, true, id); case RETRO_DEVICE_ANALOG: if (binds[port]) return input_joypad_analog(wr->joypad, joypad_info, port, index, id, binds[port]); break; case RETRO_DEVICE_LIGHTGUN: switch ( id ) { /*aiming*/ case RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X: case RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y: case RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN: return winraw_lightgun_aiming_state( wr, port, id ); /*buttons*/ case RETRO_DEVICE_ID_LIGHTGUN_TRIGGER: return winraw_is_pressed(wr, joypad_info, binds[port], port, RARCH_LIGHTGUN_TRIGGER); case RETRO_DEVICE_ID_LIGHTGUN_RELOAD: return winraw_is_pressed(wr, joypad_info, binds[port], port, RARCH_LIGHTGUN_RELOAD); case RETRO_DEVICE_ID_LIGHTGUN_AUX_A: return winraw_is_pressed(wr, joypad_info, binds[port], port, RARCH_LIGHTGUN_AUX_A); case RETRO_DEVICE_ID_LIGHTGUN_AUX_B: return winraw_is_pressed(wr, joypad_info, binds[port], port, RARCH_LIGHTGUN_AUX_B); case RETRO_DEVICE_ID_LIGHTGUN_AUX_C: return winraw_is_pressed(wr, joypad_info, binds[port], port, RARCH_LIGHTGUN_AUX_C); case RETRO_DEVICE_ID_LIGHTGUN_START: return winraw_is_pressed(wr, joypad_info, binds[port], port, RARCH_LIGHTGUN_START); case RETRO_DEVICE_ID_LIGHTGUN_SELECT: return winraw_is_pressed(wr, joypad_info, binds[port], port, RARCH_LIGHTGUN_SELECT); case RETRO_DEVICE_ID_LIGHTGUN_DPAD_UP: return winraw_is_pressed(wr, joypad_info, binds[port], port, RARCH_LIGHTGUN_DPAD_UP); case RETRO_DEVICE_ID_LIGHTGUN_DPAD_DOWN: return winraw_is_pressed(wr, joypad_info, binds[port], port, RARCH_LIGHTGUN_DPAD_DOWN); case RETRO_DEVICE_ID_LIGHTGUN_DPAD_LEFT: return winraw_is_pressed(wr, joypad_info, binds[port], port, RARCH_LIGHTGUN_DPAD_LEFT); case RETRO_DEVICE_ID_LIGHTGUN_DPAD_RIGHT: return winraw_is_pressed(wr, joypad_info, binds[port], port, RARCH_LIGHTGUN_DPAD_RIGHT); /*deprecated*/ case RETRO_DEVICE_ID_LIGHTGUN_X: case RETRO_DEVICE_ID_LIGHTGUN_Y: return winraw_deprecated_lightgun_state(wr, port, id); case RETRO_DEVICE_ID_LIGHTGUN_PAUSE: return winraw_is_pressed(wr, joypad_info, binds[port], port, RARCH_LIGHTGUN_START); } break; } return 0; } static void winraw_free(void *d) { winraw_input_t *wr = (winraw_input_t*)d; if (wr->joypad) wr->joypad->destroy(); winraw_set_mouse_input(NULL, false); winraw_set_keyboard_input(NULL); winraw_destroy_window(wr->window); free(g_mice); free(g_keyboard); free(wr->mice); free(wr); g_mouse_xy_mapping_ready = false; } static uint64_t winraw_get_capabilities(void *u) { return (1 << RETRO_DEVICE_KEYBOARD) | (1 << RETRO_DEVICE_MOUSE) | (1 << RETRO_DEVICE_JOYPAD) | (1 << RETRO_DEVICE_ANALOG) | (1 << RETRO_DEVICE_LIGHTGUN); } static void winraw_grab_mouse(void *d, bool grab) { bool r = false; winraw_input_t *wr = (winraw_input_t*)d; if (grab == wr->mouse_grab) return; r = winraw_set_mouse_input(wr->window, grab); if (!r) 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_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, };