diff --git a/Makefile.common b/Makefile.common index 8c82929a6c..f1eed13ace 100644 --- a/Makefile.common +++ b/Makefile.common @@ -554,6 +554,7 @@ endif ifeq ($(HAVE_EMSCRIPTEN), 1) OBJ += frontend/drivers/platform_emscripten.o \ input/drivers/rwebinput_input.o \ + input/drivers_joypad/rwebpad_joypad.o \ audio/drivers/rwebaudio.o \ camera/drivers/rwebcam.o endif diff --git a/Makefile.emscripten b/Makefile.emscripten index 3613fbe7a2..0334d2edf3 100644 --- a/Makefile.emscripten +++ b/Makefile.emscripten @@ -18,7 +18,7 @@ HAVE_RPNG = 1 HAVE_EMSCRIPTEN = 1 HAVE_RGUI = 1 HAVE_SDL = 0 -HAVE_SDL2 = 1 +HAVE_SDL2 = 0 HAVE_ZLIB = 1 WANT_ZLIB = 1 HAVE_STATIC_VIDEO_FILTERS = 1 @@ -32,17 +32,12 @@ ifneq ($(NATIVE_ZLIB),) WANT_ZLIB = 0 endif -ifeq ($(HAVE_SDL2), 1) -DEFINES += -DHAVE_SDL2 -endif - #if you compile with SDL2 flag add this Emscripten flag "-s USE_SDL=2" to LDFLAGS: -LIBS := -s USE_SDL=2 -s USE_ZLIB=1 -LDFLAGS := -L. --no-heap-copy -s USE_ZLIB=1 -s TOTAL_MEMORY=$(MEMORY) \ +LIBS := -s USE_ZLIB=1 +LDFLAGS := -L. --no-heap-copy -s USE_ZLIB=1 -s TOTAL_MEMORY=$(MEMORY) -s NO_EXIT_RUNTIME=0 \ -s EXPORTED_FUNCTIONS="['_main', '_malloc', '_cmd_savefiles', '_cmd_save_state', '_cmd_load_state', '_cmd_take_screenshot']" \ --js-library emscripten/library_rwebaudio.js \ - --js-library emscripten/library_rwebinput.js \ --js-library emscripten/library_rwebcam.js ifneq ($(PTHREAD), 0) LDFLAGS += -s USE_PTHREADS=$(PTHREAD) -s PTHREAD_POOL_SIZE=2 @@ -50,7 +45,12 @@ endif ifeq ($(ASYNC), 1) LDFLAGS += -s ASYNCIFY=$(ASYNC) -endif +endif + +ifeq ($(HAVE_SDL2), 1) + LIBS += -s USE_SDL=2 + DEFINES += -DHAVE_SDL2 +endif include Makefile.common diff --git a/config.def.h b/config.def.h index cc15759785..5d0509a835 100644 --- a/config.def.h +++ b/config.def.h @@ -69,7 +69,7 @@ static const bool def_playlist_entry_rename = true; static const unsigned int def_user_language = 0; -#if (defined(_WIN32) && !defined(_XBOX)) || (defined(__linux) && !defined(ANDROID) && !defined(HAVE_LAKKA)) || (defined(__MACH__) && !defined(IOS)) +#if (defined(_WIN32) && !defined(_XBOX)) || (defined(__linux) && !defined(ANDROID) && !defined(HAVE_LAKKA)) || (defined(__MACH__) && !defined(IOS)) || defined(EMSCRIPTEN) static const bool def_mouse_enable = true; #else static const bool def_mouse_enable = false; diff --git a/emscripten/library_rwebinput.js b/emscripten/library_rwebinput.js deleted file mode 100644 index 5d818f7805..0000000000 --- a/emscripten/library_rwebinput.js +++ /dev/null @@ -1,143 +0,0 @@ -//"use strict"; - -var LibraryRWebInput = { - $RI__deps: ['$Browser'], - $RI: { - temp: null, - contexts: [], - stateX: 0, - stateY: 0, - currentX: 0, - currentY: 0, - - canvasEventHandler: function(event) { - switch (event.type) { - case 'mouseup': - case 'mousedown': - var value; - var offset; - if (event.button === 0) offset = 40; - else if (event.button === 2) offset = 41; - else break; - if (event.type === 'mouseup') value = 0; - else value = 1; - for (var i = 0; i < RI.contexts.length; i++) { - {{{ makeSetValue('RI.contexts[i].state', 'offset', 'value', 'i8') }}}; - } - break; - } - }, - - eventHandler: function(event) { - var i; - switch (event.type) { - case 'mousemove': - var x = 0; - var y = 0; - - var newX = event['clientX'] - Module.canvas.offsetLeft; - var newY = event['clientY'] - Module.canvas.offsetTop; - - if (newX < 0) { - newX = 0; - x = -Module.canvas.offsetWidth; - } else if (newX > Module.canvas.offsetWidth) { - newX = Module.canvas.offsetWidth; - x = Module.canvas.offsetWidth; - } else { - x = newX - RI.currentX; - } - - if (newY < 0) { - newY = 0; - y = -Module.canvas.offsetHeight; - } else if (newY > Module.canvas.offsetHeight) { - newY = Module.canvas.offsetHeight; - y = Module.canvas.offsetHeight; - } else { - y = newY - RI.currentY; - } - - RI.currentX = newX; - RI.currentY = newY; - - for (i = 0; i < RI.contexts.length; i++) { - {{{ makeSetValue('RI.contexts[i].state', '32', 'x', 'i32') }}}; - {{{ makeSetValue('RI.contexts[i].state', '36', 'y', 'i32') }}}; - } - - break; - - case 'keyup': - case 'keydown': - var key = event.keyCode; - var offset = key >> 3; - var bit = 1 << (key & 7); - if (offset >= 32) throw 'key code error! bad code: ' + key; - for (i = 0; i < RI.contexts.length; i++) { - var value = {{{ makeGetValue('RI.contexts[i].state', 'offset', 'i8') }}}; - if (event.type === 'keyup') value &= ~bit; - else value |= bit; - {{{ makeSetValue('RI.contexts[i].state', 'offset', 'value', 'i8') }}}; - } - event.preventDefault(); - break; - - case 'blur': - case 'visibilitychange': - for (i = 0; i < RI.contexts.length; i++) { - _memset(RI.contexts[i].state, 0, 42); - } - break; - } - } - }, - - RWebInputInit: function() { - if (RI.contexts.length === 0) { - document.addEventListener('keyup', RI.eventHandler, false); - document.addEventListener('keydown', RI.eventHandler, false); - document.addEventListener('mousemove', RI.eventHandler, false); - Module.canvas.addEventListener('mouseup', RI.canvasEventHandler, false); - Module.canvas.addEventListener('mousedown', RI.canvasEventHandler, false); - document.addEventListener('blur', RI.eventHandler, false); - document.addEventListener('onvisbilitychange', RI.eventHandler, false); - } - if (RI.temp === null) RI.temp = _malloc(42); - - var s = _malloc(42); - _memset(s, 0, 42); - RI.contexts.push({ - state: s - }); - return RI.contexts.length; - }, - - RWebInputPoll: function(context) { - context -= 1; - var state = RI.contexts[context].state; - _memcpy(RI.temp, state, 42); - // reset mouse movements - {{{ makeSetValue('RI.contexts[context].state', '32', '0', 'i32') }}}; - {{{ makeSetValue('RI.contexts[context].state', '36', '0', 'i32') }}}; - return RI.temp; - }, - - RWebInputDestroy: function (context) { - if (context === RI.contexts.length) { - RI.contexts.pop(); - if (RI.contexts.length === 0) { - document.removeEventListener('keyup', RI.eventHandler, false); - document.removeEventListener('keydown', RI.eventHandler, false); - document.removeEventListener('mousemove', RI.eventHandler, false); - Module.canvas.removeEventListener('mouseup', RI.canvasEventHandler, false); - Module.canvas.removeEventListener('mousedown', RI.canvasEventHandler, false); - document.removeEventListener('blur', RI.eventHandler, false); - document.removeEventListener('onvisbilitychange', RI.eventHandler, false); - } - } - } -}; - -autoAddDeps(LibraryRWebInput, '$RI'); -mergeInto(LibraryManager.library, LibraryRWebInput); diff --git a/frontend/drivers/platform_emscripten.c b/frontend/drivers/platform_emscripten.c index b9a5c5b017..838f3e365e 100644 --- a/frontend/drivers/platform_emscripten.c +++ b/frontend/drivers/platform_emscripten.c @@ -16,6 +16,7 @@ */ #include +#include #include #include @@ -56,7 +57,7 @@ static void emscripten_mainloop(void) return; main_exit(NULL); - exit(0); + emscripten_force_exit(0); } void cmd_savefiles(void) @@ -169,7 +170,8 @@ int main(int argc, char *argv[]) { settings_t *settings = config_get_ptr(); - emscripten_set_canvas_size(800, 600); + emscripten_set_canvas_element_size("#canvas", 800, 600); + emscripten_set_element_css_size("#canvas", 800.0, 600.0); rarch_main(argc, argv, NULL); emscripten_set_main_loop(emscripten_mainloop, settings->bools.video_vsync ? 0 : INT_MAX, 1); diff --git a/gfx/drivers_context/emscriptenegl_ctx.c b/gfx/drivers_context/emscriptenegl_ctx.c index 76d4bddc00..dbc59c81e2 100644 --- a/gfx/drivers_context/emscriptenegl_ctx.c +++ b/gfx/drivers_context/emscriptenegl_ctx.c @@ -20,6 +20,7 @@ #include #include +#include #ifdef HAVE_CONFIG_H #include "../../config.h" @@ -44,6 +45,9 @@ typedef struct unsigned fb_height; } emscripten_ctx_data_t; +static int emscripten_initial_width; +static int emscripten_initial_height; + static void gfx_ctx_emscripten_swap_interval(void *data, unsigned interval) { (void)data; @@ -51,23 +55,82 @@ static void gfx_ctx_emscripten_swap_interval(void *data, unsigned interval) (void)interval; } +static void gfx_ctx_emscripten_get_canvas_size(int *width, int *height) +{ + EMSCRIPTEN_RESULT r; + EmscriptenFullscreenChangeEvent fullscreen_status; + bool is_fullscreen = false; + + r = emscripten_get_fullscreen_status(&fullscreen_status); + + if (r == EMSCRIPTEN_RESULT_SUCCESS) + { + if (fullscreen_status.isFullscreen) + { + is_fullscreen = true; + *width = fullscreen_status.screenWidth; + *height = fullscreen_status.screenHeight; + } + } + + if (!is_fullscreen) + { + r = emscripten_get_canvas_element_size("#canvas", width, height); + + if (r != EMSCRIPTEN_RESULT_SUCCESS) + { + RARCH_ERR("[EMSCRIPTEN/EGL]: Could not get screen dimensions: %d\n", + r); + *width = 800; + *height = 600; + } + } +} + static void gfx_ctx_emscripten_check_window(void *data, bool *quit, bool *resize, unsigned *width, unsigned *height, bool is_shutdown) { + EMSCRIPTEN_RESULT r; int input_width; int input_height; - int is_fullscreen; emscripten_ctx_data_t *emscripten = (emscripten_ctx_data_t*)data; - (void)data; + gfx_ctx_emscripten_get_canvas_size(&input_width, &input_height); + + if (input_width == 0 || input_height == 0) + { + input_width = emscripten_initial_width; + input_height = emscripten_initial_height; + emscripten->fb_width = emscripten->fb_height = 0; + } - emscripten_get_canvas_size(&input_width, &input_height, &is_fullscreen); *width = (unsigned)input_width; *height = (unsigned)input_height; *resize = false; - if (*width != emscripten->fb_width || *height != emscripten->fb_height) + if (input_width != emscripten->fb_width || + input_height != emscripten->fb_height) + { + r = emscripten_set_canvas_element_size("#canvas", + input_width, input_height); + + if (r != EMSCRIPTEN_RESULT_SUCCESS) + { + RARCH_ERR("[EMSCRIPTEN/EGL]: error resizing canvas: %d\n", r); + } + + /* fix Module.requestFullscreen messing with the canvas size */ + r = emscripten_set_element_css_size("#canvas", + (double)input_width, (double)input_height); + + if (r != EMSCRIPTEN_RESULT_SUCCESS) + { + RARCH_ERR("[EMSCRIPTEN/EGL]: error resizing canvas css: %d\n", r); + } + *resize = true; + emscripten_reinit_video = true; + } emscripten->fb_width = (unsigned)input_width; emscripten->fb_height = (unsigned)input_height; @@ -98,7 +161,8 @@ static void gfx_ctx_emscripten_destroy(void *data) free(data); } -static void *gfx_ctx_emscripten_init(video_frame_info_t *video_info, void *video_driver) +static void *gfx_ctx_emscripten_init(video_frame_info_t *video_info, + void *video_driver) { #ifdef HAVE_EGL unsigned width, height; @@ -128,6 +192,10 @@ static void *gfx_ctx_emscripten_init(video_frame_info_t *video_info, void *video (void)video_driver; + if (emscripten_initial_width == 0 || emscripten_initial_height == 0) + emscripten_get_canvas_element_size("#canvas", + &emscripten_initial_width, &emscripten_initial_height); + #ifdef HAVE_EGL if (g_egl_inited) { @@ -135,8 +203,8 @@ static void *gfx_ctx_emscripten_init(video_frame_info_t *video_info, void *video return (void*)"emscripten"; } - if (!egl_init_context(&emscripten->egl, EGL_NONE, EGL_DEFAULT_DISPLAY, - &major, &minor, &n, attribute_list)) + if (!egl_init_context(&emscripten->egl, EGL_NONE, + (void *)EGL_DEFAULT_DISPLAY, &major, &minor, &n, attribute_list)) { egl_report_error(); goto error; @@ -202,20 +270,10 @@ static void gfx_ctx_emscripten_input_driver(void *data, const char *name, const input_driver_t **input, void **input_data) { - void *rwebinput = NULL; + void *rwebinput = input_rwebinput.init(name); - *input = NULL; - *input_data = NULL; - -#ifndef HAVE_SDL2 - rwebinput = input_rwebinput.init(); - - if (!rwebinput) - return; - - *input = &input_rwebinput; + *input = rwebinput ? &input_rwebinput : NULL; *input_data = rwebinput; -#endif } static bool gfx_ctx_emscripten_has_focus(void *data) diff --git a/griffin/griffin.c b/griffin/griffin.c index 5bba239c5b..53d450061b 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -548,6 +548,7 @@ INPUT #include "../input/drivers_joypad/qnx_joypad.c" #elif defined(EMSCRIPTEN) #include "../input/drivers/rwebinput_input.c" +#include "../input/drivers_joypad/rwebpad_joypad.c" #elif defined(DJGPP) #include "../input/drivers/dos_input.c" #include "../input/drivers_joypad/dos_joypad.c" diff --git a/input/drivers/rwebinput_input.c b/input/drivers/rwebinput_input.c index a795a3ff2a..8cdb425b5a 100644 --- a/input/drivers/rwebinput_input.c +++ b/input/drivers/rwebinput_input.c @@ -1,5 +1,5 @@ /* RetroArch - A frontend for libretro. - * Copyright (C) 2010-2015 - Michael Lelli + * Copyright (C) 2010-2018 - Michael Lelli * Copyright (C) 2011-2017 - Daniel De Matteis * * RetroArch is free software: you can redistribute it and/or modify it under the terms @@ -19,107 +19,487 @@ #include #include +#include +#include +#include +#include + +#include #include "../input_driver.h" #include "../input_keymaps.h" #include "../../tasks/tasks_internal.h" #include "../../configuration.h" +#include "../../verbosity.h" -typedef struct rwebinput_state +/* https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button */ +#define RWEBINPUT_MOUSE_BTNL 0 +#define RWEBINPUT_MOUSE_BTNM 1 +#define RWEBINPUT_MOUSE_BTNR 2 +#define RWEBINPUT_MOUSE_BTN4 3 +#define RWEBINPUT_MOUSE_BTN5 4 + +typedef struct rwebinput_key_to_code_map_entry { - uint8_t keys[32]; - int mouse_x; - int mouse_y; - char mouse_l; - char mouse_r; - bool blocked; -} rwebinput_state_t; + const char *key; + enum retro_key rk; +} rwebinput_key_to_code_map_entry_t; -int RWebInputInit(void); -rwebinput_state_t *RWebInputPoll(int context); -void RWebInputDestroy(int context); +typedef struct rwebinput_keyboard_event +{ + int type; + EmscriptenKeyboardEvent event; +} rwebinput_keyboard_event_t; + +typedef struct rwebinput_keyboard_event_queue +{ + size_t count; + size_t max_size; + rwebinput_keyboard_event_t *events; +} rwebinput_keyboard_event_queue_t; + +typedef struct rwebinput_mouse_states +{ + signed x; + signed y; + signed delta_x; + signed delta_y; + uint8_t buttons; + double scroll_x; + double scroll_y; +} rwebinput_mouse_state_t; typedef struct rwebinput_input { - rwebinput_state_t state; - int context; + bool keys[RETROK_LAST]; + rwebinput_mouse_state_t mouse; + const input_device_driver_t *joypad; + bool blocked; } rwebinput_input_t; +/* KeyboardEvent.keyCode has been deprecated for a while and doesn't have + * separate left/right modifer codes, so we have to map string labels from + * KeyboardEvent.code to retro keys */ +static const rwebinput_key_to_code_map_entry_t rwebinput_key_to_code_map[] = +{ + { "KeyA", RETROK_a }, + { "KeyB", RETROK_b }, + { "KeyC", RETROK_c }, + { "KeyD", RETROK_d }, + { "KeyE", RETROK_e }, + { "KeyF", RETROK_f }, + { "KeyG", RETROK_g }, + { "KeyH", RETROK_h }, + { "KeyI", RETROK_i }, + { "KeyJ", RETROK_j }, + { "KeyK", RETROK_k }, + { "KeyL", RETROK_l }, + { "KeyM", RETROK_m }, + { "KeyN", RETROK_n }, + { "KeyO", RETROK_o }, + { "KeyP", RETROK_p }, + { "KeyQ", RETROK_q }, + { "KeyR", RETROK_r }, + { "KeyS", RETROK_s }, + { "KeyT", RETROK_t }, + { "KeyU", RETROK_u }, + { "KeyV", RETROK_v }, + { "KeyW", RETROK_w }, + { "KeyX", RETROK_x }, + { "KeyY", RETROK_y }, + { "KeyZ", RETROK_z }, + { "ArrowLeft", RETROK_LEFT }, + { "ArrowRight", RETROK_RIGHT }, + { "ArrowUp", RETROK_UP }, + { "ArrowDown", RETROK_DOWN }, + { "Enter", RETROK_RETURN }, + { "NumpadEnter", RETROK_KP_ENTER }, + { "Tab", RETROK_TAB }, + { "Insert", RETROK_INSERT }, + { "Delete", RETROK_DELETE }, + { "End", RETROK_END }, + { "Home", RETROK_HOME }, + { "ShiftRight", RETROK_RSHIFT }, + { "ShiftLeft", RETROK_LSHIFT }, + { "ControlLeft", RETROK_LCTRL }, + { "AltLeft", RETROK_LALT }, + { "Space", RETROK_SPACE }, + { "Escape", RETROK_ESCAPE }, + { "NumpadAdd", RETROK_KP_PLUS }, + { "NumpadSubtract", RETROK_KP_MINUS }, + { "F1", RETROK_F1 }, + { "F2", RETROK_F2 }, + { "F3", RETROK_F3 }, + { "F4", RETROK_F4 }, + { "F5", RETROK_F5 }, + { "F6", RETROK_F6 }, + { "F7", RETROK_F7 }, + { "F8", RETROK_F8 }, + { "F9", RETROK_F9 }, + { "F10", RETROK_F10 }, + { "F11", RETROK_F11 }, + { "F12", RETROK_F12 }, + { "Digit0", RETROK_0 }, + { "Digit1", RETROK_1 }, + { "Digit2", RETROK_2 }, + { "Digit3", RETROK_3 }, + { "Digit4", RETROK_4 }, + { "Digit5", RETROK_5 }, + { "Digit6", RETROK_6 }, + { "Digit7", RETROK_7 }, + { "Digit8", RETROK_8 }, + { "Digit9", RETROK_9 }, + { "PageUp", RETROK_PAGEUP }, + { "PageDown", RETROK_PAGEDOWN }, + { "Numpad0", RETROK_KP0 }, + { "Numpad1", RETROK_KP1 }, + { "Numpad2", RETROK_KP2 }, + { "Numpad3", RETROK_KP3 }, + { "Numpad4", RETROK_KP4 }, + { "Numpad5", RETROK_KP5 }, + { "Numpad6", RETROK_KP6 }, + { "Numpad7", RETROK_KP7 }, + { "Numpad8", RETROK_KP8 }, + { "Numpad9", RETROK_KP9 }, + { "Period", RETROK_PERIOD }, + { "CapsLock", RETROK_CAPSLOCK }, + { "NumLock", RETROK_NUMLOCK }, + { "Backspace", RETROK_BACKSPACE }, + { "NumpadMultiply", RETROK_KP_MULTIPLY }, + { "NumpadDivide", RETROK_KP_DIVIDE }, + { "PrintScreen", RETROK_PRINT }, + { "ScrollLock", RETROK_SCROLLOCK }, + { "Backquote", RETROK_BACKQUOTE }, + { "Pause", RETROK_PAUSE }, + { "Quote", RETROK_QUOTE }, + { "Comma", RETROK_COMMA }, + { "Minus", RETROK_MINUS }, + { "Slash", RETROK_SLASH }, + { "Semicolon", RETROK_SEMICOLON }, + { "Equal", RETROK_EQUALS }, + { "BracketLeft", RETROK_LEFTBRACKET }, + { "Backslash", RETROK_BACKSLASH }, + { "BracketRight", RETROK_RIGHTBRACKET }, + { "NumpadDecimal", RETROK_KP_PERIOD }, + { "NumpadEqual", RETROK_KP_EQUALS }, + { "ControlRight", RETROK_RCTRL }, + { "AltRight", RETROK_RALT }, + { "F13", RETROK_F13 }, + { "F14", RETROK_F14 }, + { "F15", RETROK_F15 }, + { "MetaRight", RETROK_RMETA }, + { "MetaLeft", RETROK_LMETA }, + { "Help", RETROK_HELP }, + { "ContextMenu", RETROK_MENU }, + { "Power", RETROK_POWER }, +}; + +static bool g_rwebinput_initialized; +static rwebinput_keyboard_event_queue_t *g_rwebinput_keyboard; +static rwebinput_mouse_state_t *g_rwebinput_mouse; + +/* to make the string labels for codes from JavaScript work, we convert them + * to CRC32 hashes for the LUT */ +static void rwebinput_generate_lut(void) +{ + int i; + struct rarch_key_map *key_map; + + retro_assert(ARRAY_SIZE(rarch_key_map_rwebinput) == + ARRAY_SIZE(rwebinput_key_to_code_map) + 1); + + for (i = 0; i < ARRAY_SIZE(rwebinput_key_to_code_map); i++) + { + int j; + uint32_t crc; + const rwebinput_key_to_code_map_entry_t *key_to_code = + &rwebinput_key_to_code_map[i]; + key_map = &rarch_key_map_rwebinput[i]; + crc = encoding_crc32(0, (const uint8_t *)key_to_code->key, + strlen(key_to_code->key)); + + /* sanity check: make sure there's no collisions */ + for (j = 0; j < i; j++) + { + retro_assert(rarch_key_map_rwebinput[j].sym != crc); + } + + key_map->rk = key_to_code->rk; + key_map->sym = crc; + } + + /* set terminating entry */ + key_map = &rarch_key_map_rwebinput[ARRAY_SIZE(rarch_key_map_rwebinput) - 1]; + key_map->rk = RETROK_UNKNOWN; + key_map->sym = 0; +} + +static EM_BOOL rwebinput_keyboard_cb(int event_type, + const EmscriptenKeyboardEvent *key_event, void *user_data) +{ + if (event_type == EMSCRIPTEN_EVENT_KEYPRESS) + return EM_TRUE; + + if (g_rwebinput_keyboard->count >= g_rwebinput_keyboard->max_size) + { + size_t new_max = MAX(1, g_rwebinput_keyboard->max_size << 1); + g_rwebinput_keyboard->events = realloc(g_rwebinput_keyboard->events, + new_max * sizeof(g_rwebinput_keyboard->events[0])); + retro_assert(g_rwebinput_keyboard->events != NULL); + g_rwebinput_keyboard->max_size = new_max; + } + + g_rwebinput_keyboard->events[g_rwebinput_keyboard->count].type = event_type; + memcpy(&g_rwebinput_keyboard->events[g_rwebinput_keyboard->count].event, + key_event, sizeof(*key_event)); + g_rwebinput_keyboard->count++; + + return EM_TRUE; +} + +static EM_BOOL rwebinput_mouse_cb(int event_type, + const EmscriptenMouseEvent *mouse_event, void *user_data) +{ + (void)user_data; + + uint8_t mask = 1 << mouse_event->button; + + g_rwebinput_mouse->x = mouse_event->canvasX; + g_rwebinput_mouse->y = mouse_event->canvasY; + g_rwebinput_mouse->delta_x += mouse_event->movementX; + g_rwebinput_mouse->delta_y += mouse_event->movementY; + + if (event_type == EMSCRIPTEN_EVENT_MOUSEDOWN) + { + g_rwebinput_mouse->buttons |= mask; + } + else if (event_type == EMSCRIPTEN_EVENT_MOUSEUP) + { + g_rwebinput_mouse->buttons &= ~mask; + } + + return EM_TRUE; +} + +static EM_BOOL rwebinput_wheel_cb(int event_type, + const EmscriptenWheelEvent *wheel_event, void *user_data) +{ + (void)event_type; + (void)user_data; + + g_rwebinput_mouse->scroll_x += wheel_event->deltaX; + g_rwebinput_mouse->scroll_y += wheel_event->deltaY; + + return EM_TRUE; +} + static void *rwebinput_input_init(const char *joypad_driver) { - rwebinput_input_t *rwebinput = (rwebinput_input_t*)calloc(1, sizeof(*rwebinput)); - if (!rwebinput) + rwebinput_input_t *rwebinput = + (rwebinput_input_t*)calloc(1, sizeof(*rwebinput)); + g_rwebinput_keyboard = (rwebinput_keyboard_event_queue_t*) + calloc(1, sizeof(rwebinput_keyboard_event_queue_t)); + g_rwebinput_mouse = (rwebinput_mouse_state_t*) + calloc(1, sizeof(rwebinput_mouse_state_t)); + + if (!rwebinput || !g_rwebinput_keyboard || !g_rwebinput_mouse) goto error; - rwebinput->context = RWebInputInit(); - if (!rwebinput->context) - goto error; + if (!g_rwebinput_initialized) + { + EMSCRIPTEN_RESULT r; + + g_rwebinput_initialized = true; + rwebinput_generate_lut(); + + /* emscripten currently doesn't have an API to remove handlers, so make + * once and reuse it */ + r = emscripten_set_keydown_callback("#document", NULL, false, + rwebinput_keyboard_cb); + if (r != EMSCRIPTEN_RESULT_SUCCESS) + { + RARCH_ERR( + "[EMSCRIPTEN/INPUT] failed to create keydown callback: %d\n", r); + } + + r = emscripten_set_keyup_callback("#document", NULL, false, + rwebinput_keyboard_cb); + if (r != EMSCRIPTEN_RESULT_SUCCESS) + { + RARCH_ERR( + "[EMSCRIPTEN/INPUT] failed to create keydown callback: %d\n", r); + } + + r = emscripten_set_keypress_callback("#document", NULL, false, + rwebinput_keyboard_cb); + if (r != EMSCRIPTEN_RESULT_SUCCESS) + { + RARCH_ERR( + "[EMSCRIPTEN/INPUT] failed to create keypress callback: %d\n", r); + } + + r = emscripten_set_mousedown_callback("#canvas", NULL, false, + rwebinput_mouse_cb); + if (r != EMSCRIPTEN_RESULT_SUCCESS) + { + RARCH_ERR( + "[EMSCRIPTEN/INPUT] failed to create mousedown callback: %d\n", r); + } + + r = emscripten_set_mouseup_callback("#canvas", NULL, false, + rwebinput_mouse_cb); + if (r != EMSCRIPTEN_RESULT_SUCCESS) + { + RARCH_ERR( + "[EMSCRIPTEN/INPUT] failed to create mouseup callback: %d\n", r); + } + + r = emscripten_set_mousemove_callback("#canvas", NULL, false, + rwebinput_mouse_cb); + if (r != EMSCRIPTEN_RESULT_SUCCESS) + { + RARCH_ERR( + "[EMSCRIPTEN/INPUT] failed to create mousemove callback: %d\n", r); + } + + r = emscripten_set_wheel_callback("#document", NULL, false, + rwebinput_wheel_cb); + if (r != EMSCRIPTEN_RESULT_SUCCESS) + { + RARCH_ERR( + "[EMSCRIPTEN/INPUT] failed to create wheel callback: %d\n", r); + } + } input_keymaps_init_keyboard_lut(rarch_key_map_rwebinput); + rwebinput->joypad = input_joypad_init_driver(joypad_driver, rwebinput); + return rwebinput; error: - if (rwebinput) - free(rwebinput); + free(g_rwebinput_keyboard); + free(g_rwebinput_mouse); + free(rwebinput); return NULL; } -static bool rwebinput_key_pressed_internal(void *data, int key) +static bool rwebinput_key_pressed(rwebinput_input_t *rwebinput, int key) { - unsigned sym; - bool ret; - rwebinput_input_t *rwebinput = (rwebinput_input_t*)data; - if (key >= RETROK_LAST) return false; - sym = rarch_keysym_lut[(enum retro_key)key]; - ret = rwebinput->state.keys[sym >> 3] & (1 << (sym & 7)); - - return ret; + return rwebinput->keys[key]; } -static bool rwebinput_is_pressed(rwebinput_input_t *rwebinput, - const struct retro_keybind *binds, unsigned id) +static int16_t rwebinput_pointer_device_state(rwebinput_mouse_state_t *mouse, + unsigned id, bool screen) { - if (id < RARCH_BIND_LIST_END) + struct video_viewport vp; + bool inside = false; + int16_t res_x = 0; + int16_t res_y = 0; + int16_t res_screen_x = 0; + int16_t res_screen_y = 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->x, + &res_x, &res_y, &res_screen_x, &res_screen_y))) + return 0; + + if (screen) { - const struct retro_keybind *bind = &binds[id]; - int key = binds[id].key; - return bind->valid && (key < RETROK_LAST) - && rwebinput_key_pressed_internal(rwebinput, key); + res_x = res_screen_x; + res_y = res_screen_y; } - return false; -} + inside = (res_x >= -0x7fff) && (res_y >= -0x7fff); -static bool rwebinput_key_pressed(void *data, int key) -{ - rwebinput_input_t *rwebinput = (rwebinput_input_t*)data; - return rwebinput_is_pressed(rwebinput, input_config_binds[0], key); -} + if (!inside) + return 0; -static int16_t rwebinput_mouse_state(rwebinput_input_t *rwebinput, unsigned id) -{ switch (id) { - case RETRO_DEVICE_ID_MOUSE_X: - return (int16_t) rwebinput->state.mouse_x; - case RETRO_DEVICE_ID_MOUSE_Y: - return (int16_t) rwebinput->state.mouse_y; - case RETRO_DEVICE_ID_MOUSE_LEFT: - return rwebinput->state.mouse_l; - case RETRO_DEVICE_ID_MOUSE_RIGHT: - return rwebinput->state.mouse_r; + case RETRO_DEVICE_ID_POINTER_X: + return res_x; + case RETRO_DEVICE_ID_POINTER_Y: + return res_y; + case RETRO_DEVICE_ID_POINTER_PRESSED: + return !!(mouse->buttons & (1 << RWEBINPUT_MOUSE_BTNL)); } return 0; } +static int16_t rwebinput_mouse_state(rwebinput_mouse_state_t *mouse, + unsigned id, bool screen) +{ + switch (id) + { + case RETRO_DEVICE_ID_MOUSE_X: + return (int16_t)(screen ? mouse->x : mouse->delta_x); + case RETRO_DEVICE_ID_MOUSE_Y: + return (int16_t)(screen ? mouse->y : mouse->delta_y); + case RETRO_DEVICE_ID_MOUSE_LEFT: + return !!(mouse->buttons & (1 << RWEBINPUT_MOUSE_BTNL)); + case RETRO_DEVICE_ID_MOUSE_RIGHT: + return !!(mouse->buttons & (1 << RWEBINPUT_MOUSE_BTNR)); + case RETRO_DEVICE_ID_MOUSE_MIDDLE: + return !!(mouse->buttons & (1 << RWEBINPUT_MOUSE_BTNM)); + case RETRO_DEVICE_ID_MOUSE_BUTTON_4: + return !!(mouse->buttons & (1 << RWEBINPUT_MOUSE_BTN4)); + case RETRO_DEVICE_ID_MOUSE_BUTTON_5: + return !!(mouse->buttons & (1 << RWEBINPUT_MOUSE_BTN5)); + case RETRO_DEVICE_ID_MOUSE_WHEELUP: + return mouse->scroll_y < 0.0; + case RETRO_DEVICE_ID_MOUSE_WHEELDOWN: + return mouse->scroll_y > 0.0; + case RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELUP: + return mouse->scroll_x < 0.0; + case RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN: + return mouse->scroll_x > 0.0; + } + + return 0; +} + +static bool rwebinput_is_pressed(rwebinput_input_t *rwebinput, + rarch_joypad_info_t joypad_info, const struct retro_keybind *binds, + unsigned port, unsigned id) +{ + if (id < RARCH_BIND_LIST_END) + { + const struct retro_keybind *bind = &binds[id]; + int key = bind->key; + + if (!rwebinput->blocked && (bind->key < RETROK_LAST) && + rwebinput_key_pressed(rwebinput, key)) + return true; + + if (bind->valid) + { + if (port == 0 && !!rwebinput_mouse_state(&rwebinput->mouse, + bind->mbutton, false)) + return true; + if (input_joypad_pressed(rwebinput->joypad, joypad_info, port, binds, + id)) + return true; + } + } + + return false; +} + static int16_t rwebinput_analog_pressed(rwebinput_input_t *rwebinput, - const struct retro_keybind *binds, unsigned idx, unsigned id) + rarch_joypad_info_t joypad_info, const struct retro_keybind *binds, + unsigned idx, unsigned id) { int16_t pressed_minus = 0, pressed_plus = 0; unsigned id_minus = 0; @@ -127,9 +507,9 @@ static int16_t rwebinput_analog_pressed(rwebinput_input_t *rwebinput, input_conv_analog_id_to_bind_id(idx, id, &id_minus, &id_plus); - if (rwebinput_is_pressed(rwebinput, binds, id_minus)) + if (rwebinput_is_pressed(rwebinput, joypad_info, binds, idx, id_minus)) pressed_minus = -0x7fff; - if (rwebinput_is_pressed(rwebinput, binds, id_plus)) + if (rwebinput_is_pressed(rwebinput, joypad_info, binds, idx, id_plus)) pressed_plus = 0x7fff; return pressed_plus + pressed_minus; @@ -140,18 +520,33 @@ static int16_t rwebinput_input_state(void *data, const struct retro_keybind **binds, unsigned port, unsigned device, unsigned idx, unsigned id) { + int16_t ret; rwebinput_input_t *rwebinput = (rwebinput_input_t*)data; switch (device) { case RETRO_DEVICE_JOYPAD: - return rwebinput_is_pressed(rwebinput, binds[port], id); + if (id < RARCH_BIND_LIST_END) + return rwebinput_is_pressed(rwebinput, joypad_info, binds[port], + port, id); + break; case RETRO_DEVICE_ANALOG: - return rwebinput_analog_pressed(rwebinput, binds[port], idx, id); + ret = rwebinput_analog_pressed(rwebinput, joypad_info, binds[port], + idx, id); + if (!ret && binds[port]) + ret = input_joypad_analog(rwebinput->joypad, joypad_info, port, + idx, id, binds[port]); + return ret; case RETRO_DEVICE_KEYBOARD: return rwebinput_key_pressed(rwebinput, id); case RETRO_DEVICE_MOUSE: - return rwebinput_mouse_state(rwebinput, id); + return rwebinput_mouse_state(&rwebinput->mouse, id, false); + case RARCH_DEVICE_MOUSE_SCREEN: + return rwebinput_mouse_state(&rwebinput->mouse, id, true); + case RETRO_DEVICE_POINTER: + return rwebinput_pointer_device_state(&rwebinput->mouse, id, false); + case RARCH_DEVICE_POINTER_SCREEN: + return rwebinput_pointer_device_state(&rwebinput->mouse, id, true); } return 0; @@ -161,47 +556,88 @@ static void rwebinput_input_free(void *data) { rwebinput_input_t *rwebinput = (rwebinput_input_t*)data; - if (!rwebinput) - return; - - RWebInputDestroy(rwebinput->context); - + if (g_rwebinput_keyboard) + free(g_rwebinput_keyboard->events); + free(g_rwebinput_keyboard); + g_rwebinput_keyboard = NULL; + free(g_rwebinput_mouse); + g_rwebinput_mouse = NULL; free(rwebinput); } +static void rwebinput_process_keyboard_events(rwebinput_input_t *rwebinput, + rwebinput_keyboard_event_t *event) +{ + uint32_t crc; + uint32_t keycode; + unsigned translated_keycode; + uint32_t character = 0; + uint16_t mod = 0; + const EmscriptenKeyboardEvent *key_event = &event->event; + bool keydown = event->type == EMSCRIPTEN_EVENT_KEYDOWN; + + /* a printable key: populate character field */ + if (utf8len(key_event->key) == 1) + { + const char *key_ptr = &key_event->key[0]; + character = utf8_walk(&key_ptr); + } + + if (key_event->ctrlKey) + mod |= RETROKMOD_CTRL; + if (key_event->altKey) + mod |= RETROKMOD_ALT; + if (key_event->shiftKey) + mod |= RETROKMOD_SHIFT; + if (key_event->metaKey) + mod |= RETROKMOD_META; + + keycode = encoding_crc32(0, (const uint8_t *)key_event->code, + strnlen(key_event->code, sizeof(key_event->code))); + translated_keycode = input_keymaps_translate_keysym_to_rk(keycode); + + if (translated_keycode == RETROK_BACKSPACE) + character = '\b'; + else if (translated_keycode == RETROK_RETURN || + translated_keycode == RETROK_KP_ENTER) + character = '\n'; + else if (translated_keycode == RETROK_TAB) + character = '\t'; + + input_keyboard_event(keydown, translated_keycode, character, mod, + RETRO_DEVICE_KEYBOARD); + + if (translated_keycode < RETROK_LAST) + { + rwebinput->keys[translated_keycode] = keydown; + } +} static void rwebinput_input_poll(void *data) { - unsigned i; + size_t i; rwebinput_input_t *rwebinput = (rwebinput_input_t*)data; - rwebinput_state_t *state = RWebInputPoll(rwebinput->context); - /* Get new keys. */ - for (i = 0; i < 32; i++) - { - unsigned k; - uint8_t diff; + for (i = 0; i < g_rwebinput_keyboard->count; i++) + rwebinput_process_keyboard_events(rwebinput, + &g_rwebinput_keyboard->events[i]); + g_rwebinput_keyboard->count = 0; - if (state->keys[i] == rwebinput->state.keys[i]) - continue; + memcpy(&rwebinput->mouse, g_rwebinput_mouse, sizeof(*g_rwebinput_mouse)); + g_rwebinput_mouse->delta_x = g_rwebinput_mouse->delta_y = 0; + g_rwebinput_mouse->scroll_x = g_rwebinput_mouse->scroll_y = 0.0; - diff = state->keys[i] ^ rwebinput->state.keys[i]; - - for (k = 0; diff; diff >>= 1, k++) - { - if (diff & 1) - input_keyboard_event((state->keys[i] & (1 << k)) != 0, - input_keymaps_translate_keysym_to_rk(i * 8 + k), 0, 0, - RETRO_DEVICE_KEYBOARD); - } - } - - memcpy(&rwebinput->state, state, sizeof(rwebinput->state)); + if (rwebinput->joypad) + rwebinput->joypad->poll(); } static void rwebinput_grab_mouse(void *data, bool state) { (void)data; - (void)state; + + if (state) + emscripten_request_pointerlock("#canvas", EM_TRUE); + else + emscripten_exit_pointerlock(); } static bool rwebinput_set_rumble(void *data, unsigned port, @@ -215,6 +651,14 @@ static bool rwebinput_set_rumble(void *data, unsigned port, return false; } +static const input_device_driver_t *rwebinput_get_joypad_driver(void *data) +{ + rwebinput_input_t *rwebinput = (rwebinput_input_t*)data; + if (!rwebinput) + return NULL; + return rwebinput->joypad; +} + static uint64_t rwebinput_get_capabilities(void *data) { uint64_t caps = 0; @@ -223,6 +667,7 @@ static uint64_t rwebinput_get_capabilities(void *data) caps |= (1 << RETRO_DEVICE_ANALOG); caps |= (1 << RETRO_DEVICE_KEYBOARD); caps |= (1 << RETRO_DEVICE_MOUSE); + caps |= (1 << RETRO_DEVICE_POINTER); return caps; } @@ -232,7 +677,7 @@ static bool rwebinput_keyboard_mapping_is_blocked(void *data) rwebinput_input_t *rwebinput = (rwebinput_input_t*)data; if (!rwebinput) return false; - return rwebinput->state.blocked; + return rwebinput->blocked; } static void rwebinput_keyboard_mapping_set_block(void *data, bool value) @@ -240,7 +685,7 @@ static void rwebinput_keyboard_mapping_set_block(void *data, bool value) rwebinput_input_t *rwebinput = (rwebinput_input_t*)data; if (!rwebinput) return; - rwebinput->state.blocked = value; + rwebinput->blocked = value; } input_driver_t input_rwebinput = { @@ -255,7 +700,7 @@ input_driver_t input_rwebinput = { rwebinput_grab_mouse, NULL, rwebinput_set_rumble, - NULL, + rwebinput_get_joypad_driver, NULL, rwebinput_keyboard_mapping_is_blocked, rwebinput_keyboard_mapping_set_block, diff --git a/input/drivers_joypad/rwebpad_joypad.c b/input/drivers_joypad/rwebpad_joypad.c new file mode 100644 index 0000000000..f061996e6e --- /dev/null +++ b/input/drivers_joypad/rwebpad_joypad.c @@ -0,0 +1,219 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2018 - Michael Lelli + * + * 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 + +#include +#include +#include + +#include "../input_driver.h" + +#include "../../tasks/tasks_internal.h" +#include "../../verbosity.h" + +#define CLAMPDOUBLE(x) MIN(1.0, MAX(-1.0, (x))) + +static bool g_rwebpad_initialized; + +static EM_BOOL rwebpad_gamepad_cb(int event_type, + const EmscriptenGamepadEvent *gamepad_event, void *user_data) +{ + unsigned vid = 0; + unsigned pid = 0; + + (void)event_type; + (void)gamepad_event; + (void)user_data; + + if (strncmp(gamepad_event->mapping, "standard", + sizeof(gamepad_event->mapping)) == 0) + { + /* give a dummy vid/pid for automapping */ + vid = 1; + pid = 1; + } + + + if (event_type == EMSCRIPTEN_EVENT_GAMEPADCONNECTED) + { + if(!input_autoconfigure_connect( + gamepad_event->id, /* name */ + NULL, /* display name */ + rwebpad_joypad.ident, /* driver */ + gamepad_event->index, /* idx */ + vid, /* vid */ + pid)) /* pid */ + input_config_set_device_name(gamepad_event->index, + gamepad_event->id); + } + else if (event_type == EMSCRIPTEN_EVENT_GAMEPADDISCONNECTED) + { + input_autoconfigure_disconnect(gamepad_event->index, + rwebpad_joypad.ident); + } + + return EM_TRUE; +} + +static bool rwebpad_joypad_init(void *data) +{ + int supported = emscripten_get_num_gamepads(); + (void)data; + + if (supported == EMSCRIPTEN_RESULT_NOT_SUPPORTED) + return false; + + if (!g_rwebpad_initialized) + { + EMSCRIPTEN_RESULT r; + g_rwebpad_initialized = true; + + /* callbacks needs to be registered for gamepads to connect */ + r = emscripten_set_gamepadconnected_callback(NULL, false, + rwebpad_gamepad_cb); + if (r != EMSCRIPTEN_RESULT_SUCCESS) + { + RARCH_ERR( + "[EMSCRIPTEN/PAD] failed to create connect callback: %d\n", r); + } + + r = emscripten_set_gamepaddisconnected_callback(NULL, false, + rwebpad_gamepad_cb); + if (r != EMSCRIPTEN_RESULT_SUCCESS) + { + RARCH_ERR( + "[EMSCRIPTEN/PAD] failed to create disconnect callback: %d\n", r); + } + } + + return true; +} + +static const char *rwebpad_joypad_name(unsigned pad) +{ + static EmscriptenGamepadEvent gamepad_state; + EMSCRIPTEN_RESULT r; + + r = emscripten_get_gamepad_status(pad, &gamepad_state); + + if (r == EMSCRIPTEN_RESULT_SUCCESS) + return gamepad_state.id; + else + return ""; +} + +static bool rwebpad_joypad_button(unsigned port_num, uint16_t joykey) +{ + EmscriptenGamepadEvent gamepad_state; + EMSCRIPTEN_RESULT r; + + r = emscripten_get_gamepad_status(port_num, &gamepad_state); + + if (r == EMSCRIPTEN_RESULT_SUCCESS) + if (joykey < gamepad_state.numButtons) + return gamepad_state.digitalButton[joykey]; + + return false; +} + +static void rwebpad_joypad_get_buttons(unsigned port_num, retro_bits_t *state) +{ + EmscriptenGamepadEvent gamepad_state; + EMSCRIPTEN_RESULT r; + + r = emscripten_get_gamepad_status(port_num, &gamepad_state); + + if (r == EMSCRIPTEN_RESULT_SUCCESS) + { + int i; + + for (i = 0; i < gamepad_state.numButtons; i++) + { + if (gamepad_state.digitalButton[i]) + BIT256_SET_PTR(state, i); + } + } + else + BIT256_CLEAR_ALL_PTR(state); +} + +static int16_t rwebpad_joypad_axis(unsigned port_num, uint32_t joyaxis) +{ + EmscriptenGamepadEvent gamepad_state; + EMSCRIPTEN_RESULT r; + int16_t val = 0; + + if (joyaxis == 0xFFFFFFFF) + return 0; + + r = emscripten_get_gamepad_status(port_num, &gamepad_state); + + if (r == EMSCRIPTEN_RESULT_SUCCESS) + { + if (AXIS_NEG_GET(joyaxis) < gamepad_state.numAxes) + { + val = CLAMPDOUBLE(gamepad_state.axis[AXIS_NEG_GET(joyaxis)]) * 0x7FFF; + if (val > 0) + val = 0; + } + else if (AXIS_POS_GET(joyaxis) < gamepad_state.numAxes) + { + val = CLAMPDOUBLE(gamepad_state.axis[AXIS_POS_GET(joyaxis)]) * 0x7FFF; + if (val < 0) + val = 0; + } + } + + return val; +} + +static void rwebpad_joypad_poll(void) +{ + /* this call makes emscripten poll gamepad state */ + (void)emscripten_get_num_gamepads(); +} + +static bool rwebpad_joypad_query_pad(unsigned pad) +{ + EmscriptenGamepadEvent gamepad_state; + EMSCRIPTEN_RESULT r; + + r = emscripten_get_gamepad_status(pad, &gamepad_state); + + if (r == EMSCRIPTEN_RESULT_SUCCESS) + return gamepad_state.connected == EM_TRUE; + + return false; +} + + +static void rwebpad_joypad_destroy(void) +{ +} + +input_device_driver_t rwebpad_joypad = { + rwebpad_joypad_init, + rwebpad_joypad_query_pad, + rwebpad_joypad_destroy, + rwebpad_joypad_button, + rwebpad_joypad_get_buttons, + rwebpad_joypad_axis, + rwebpad_joypad_poll, + NULL, + rwebpad_joypad_name, + "rwebpad", +}; diff --git a/input/input_autodetect_builtin.c b/input/input_autodetect_builtin.c index a00c90123b..b524f6555e 100644 --- a/input/input_autodetect_builtin.c +++ b/input/input_autodetect_builtin.c @@ -37,7 +37,8 @@ #define DECL_AXIS(axis, bind) "input_" #axis "_axis = " #bind "\n" #define DECL_AXIS_EX(axis, bind, name) "input_" #axis "_axis = " #bind "\ninput_" #axis "_axis_label = \"" name "\"\n" #define DECL_MENU(btn) "input_menu_toggle_btn = " #btn "\n" -#define DECL_AUTOCONF_DEVICE(device, driver, binds) "input_device = \"" device "\" \ninput_driver = \"" driver "\" \n" binds +#define DECL_AUTOCONF_DEVICE(device, driver, binds) "input_device = \"" device "\"\ninput_driver = \"" driver "\"\n" binds +#define DECL_AUTOCONF_PID(pid, vid, driver, binds) "input_product_id = " #pid "\ninput_vendor_id = " #vid "\ninput_driver = \"" driver "\"\n" binds /* TODO/FIXME - Missing L2/R2 */ @@ -528,6 +529,32 @@ DECL_AXIS(r_x_minus, -2) \ DECL_AXIS(r_y_plus, +3) \ DECL_AXIS(r_y_minus, -3) +#define EMSCRIPTEN_DEFAULT_BINDS \ +DECL_BTN(a, 1) \ +DECL_BTN(b, 0) \ +DECL_BTN(x, 3) \ +DECL_BTN(y, 2) \ +DECL_BTN(start, 9) \ +DECL_BTN(select, 8) \ +DECL_BTN(up, 12) \ +DECL_BTN(down, 13) \ +DECL_BTN(left, 14) \ +DECL_BTN(right, 15) \ +DECL_BTN(l, 4) \ +DECL_BTN(r, 5) \ +DECL_BTN(l2, 6) \ +DECL_BTN(r2, 7) \ +DECL_BTN(l3, 10) \ +DECL_BTN(r3, 11) \ +DECL_AXIS(l_x_plus, +0) \ +DECL_AXIS(l_x_minus, -0) \ +DECL_AXIS(l_y_plus, +1) \ +DECL_AXIS(l_y_minus, -1) \ +DECL_AXIS(r_x_plus, +2) \ +DECL_AXIS(r_x_minus, -2) \ +DECL_AXIS(r_y_plus, +3) \ +DECL_AXIS(r_y_minus, -3) + const char* const input_builtin_autoconfs[] = { #if defined(_WIN32) && defined(_XBOX) @@ -588,6 +615,9 @@ const char* const input_builtin_autoconfs[] = #endif #ifdef __SWITCH__ DECL_AUTOCONF_DEVICE("Switch Controller", "switch", SWITCH_DEFAULT_BINDS), +#endif +#ifdef EMSCRIPTEN + DECL_AUTOCONF_PID(1, 1, "rwebpad", EMSCRIPTEN_DEFAULT_BINDS), #endif NULL }; diff --git a/input/input_driver.c b/input/input_driver.c index 01fff706e3..73ea18aefe 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -181,6 +181,9 @@ static input_device_driver_t *joypad_drivers[] = { * to be selectable in the UI. */ #if defined(HAVE_HID) && !defined(WIIU) &hid_joypad, +#endif +#ifdef EMSCRIPTEN + &rwebpad_joypad, #endif &null_joypad, NULL, @@ -804,7 +807,7 @@ void state_tracker_update_input(uint16_t *input1, uint16_t *input2) static INLINE bool input_keys_pressed_iterate(unsigned i, retro_bits_t* p_new_state) { - if ((i >= RARCH_FIRST_META_KEY) && + if ((i >= RARCH_FIRST_META_KEY) && BIT64_GET(lifecycle_state, i) ) return true; diff --git a/input/input_driver.h b/input/input_driver.h index 01782d0252..b3c53ab8d2 100644 --- a/input/input_driver.h +++ b/input/input_driver.h @@ -814,6 +814,7 @@ extern input_device_driver_t qnx_joypad; extern input_device_driver_t null_joypad; extern input_device_driver_t mfi_joypad; extern input_device_driver_t dos_joypad; +extern input_device_driver_t rwebpad_joypad; extern input_driver_t input_android; extern input_driver_t input_sdl; diff --git a/input/input_keymaps.c b/input/input_keymaps.c index ef4345e8c3..8060d92b80 100644 --- a/input/input_keymaps.c +++ b/input/input_keymaps.c @@ -579,109 +579,8 @@ const struct rarch_key_map rarch_key_map_dinput[] = { #endif #ifdef EMSCRIPTEN -const struct rarch_key_map rarch_key_map_rwebinput[] = { - { 37, RETROK_LEFT }, - { 39, RETROK_RIGHT }, - { 38, RETROK_UP }, - { 40, RETROK_DOWN }, - { 13, RETROK_RETURN }, - { 9, RETROK_TAB }, - { 45, RETROK_INSERT }, - { 46, RETROK_DELETE }, - { 16, RETROK_RSHIFT }, - { 16, RETROK_LSHIFT }, - { 17, RETROK_LCTRL }, - { 35, RETROK_END }, - { 36, RETROK_HOME }, - { 34, RETROK_PAGEDOWN }, - { 33, RETROK_PAGEUP }, - { 18, RETROK_LALT }, - { 32, RETROK_SPACE }, - { 27, RETROK_ESCAPE }, - { 8, RETROK_BACKSPACE }, - { 13, RETROK_KP_ENTER }, - { 107, RETROK_KP_PLUS }, - { 109, RETROK_KP_MINUS }, - { 106, RETROK_KP_MULTIPLY }, - { 111, RETROK_KP_DIVIDE }, - { 192, RETROK_BACKQUOTE }, - { 19, RETROK_PAUSE }, - { 96, RETROK_KP0 }, - { 97, RETROK_KP1 }, - { 98, RETROK_KP2 }, - { 99, RETROK_KP3 }, - { 100, RETROK_KP4 }, - { 101, RETROK_KP5 }, - { 102, RETROK_KP6 }, - { 103, RETROK_KP7 }, - { 104, RETROK_KP8 }, - { 105, RETROK_KP9 }, - { 48, RETROK_0 }, - { 49, RETROK_1 }, - { 50, RETROK_2 }, - { 51, RETROK_3 }, - { 52, RETROK_4 }, - { 53, RETROK_5 }, - { 54, RETROK_6 }, - { 55, RETROK_7 }, - { 56, RETROK_8 }, - { 57, RETROK_9 }, - { 112, RETROK_F1 }, - { 113, RETROK_F2 }, - { 114, RETROK_F3 }, - { 115, RETROK_F4 }, - { 116, RETROK_F5 }, - { 117, RETROK_F6 }, - { 118, RETROK_F7 }, - { 119, RETROK_F8 }, - { 120, RETROK_F9 }, - { 121, RETROK_F10 }, - { 122, RETROK_F11 }, - { 123, RETROK_F12 }, - { 65, RETROK_a }, - { 66, RETROK_b }, - { 67, RETROK_c }, - { 68, RETROK_d }, - { 69, RETROK_e }, - { 70, RETROK_f }, - { 71, RETROK_g }, - { 72, RETROK_h }, - { 73, RETROK_i }, - { 74, RETROK_j }, - { 75, RETROK_k }, - { 76, RETROK_l }, - { 77, RETROK_m }, - { 78, RETROK_n }, - { 79, RETROK_o }, - { 80, RETROK_p }, - { 81, RETROK_q }, - { 82, RETROK_r }, - { 83, RETROK_s }, - { 84, RETROK_t }, - { 85, RETROK_u }, - { 86, RETROK_v }, - { 87, RETROK_w }, - { 88, RETROK_x }, - { 89, RETROK_y }, - { 90, RETROK_z }, - { 222, RETROK_QUOTE }, - { 188, RETROK_COMMA }, - { 173, RETROK_MINUS }, - { 191, RETROK_SLASH }, - { 59, RETROK_SEMICOLON }, - { 61, RETROK_EQUALS }, - { 219, RETROK_LEFTBRACKET }, - { 220, RETROK_BACKSLASH }, - { 221, RETROK_RIGHTBRACKET }, - { 188, RETROK_KP_PERIOD }, - { 17, RETROK_RCTRL }, - { 18, RETROK_RALT }, - { 190, RETROK_PERIOD }, - { 145, RETROK_SCROLLOCK }, - { 20, RETROK_CAPSLOCK }, - { 144, RETROK_NUMLOCK }, - { 0, RETROK_UNKNOWN }, -}; +/* this is generated at runtime, so it isn't constant */ +struct rarch_key_map rarch_key_map_rwebinput[RARCH_KEY_MAP_RWEBINPUT_SIZE]; #endif #ifdef WIIU diff --git a/input/input_keymaps.h b/input/input_keymaps.h index 11757f2f68..d682af4e0e 100644 --- a/input/input_keymaps.h +++ b/input/input_keymaps.h @@ -46,13 +46,18 @@ struct apple_key_name_map_entry extern const struct apple_key_name_map_entry apple_key_name_map[]; #endif +#define RARCH_KEY_MAP_RWEBINPUT_SIZE 111 + extern const struct input_key_map input_config_key_map[]; extern const struct rarch_key_map rarch_key_map_x11[]; extern const struct rarch_key_map rarch_key_map_sdl[]; extern const struct rarch_key_map rarch_key_map_sdl2[]; extern const struct rarch_key_map rarch_key_map_dinput[]; -extern const struct rarch_key_map rarch_key_map_rwebinput[]; + + /* is generated at runtime so can't be const */ +extern struct rarch_key_map rarch_key_map_rwebinput[RARCH_KEY_MAP_RWEBINPUT_SIZE]; + extern const struct rarch_key_map rarch_key_map_linux[]; extern const struct rarch_key_map rarch_key_map_apple_hid[]; extern const struct rarch_key_map rarch_key_map_android[]; @@ -96,4 +101,3 @@ extern enum retro_key rarch_keysym_lut[RETROK_LAST]; RETRO_END_DECLS #endif - diff --git a/pkg/emscripten/embed/index.html b/pkg/emscripten/embed/index.html index f7f01cdabd..ac11c536b8 100644 --- a/pkg/emscripten/embed/index.html +++ b/pkg/emscripten/embed/index.html @@ -10,11 +10,11 @@ - + - +