From a09dda3a9da71a1f00e07dd1c9e1efbb1b381988 Mon Sep 17 00:00:00 2001 From: ToadKing Date: Tue, 10 Sep 2013 20:21:48 -0400 Subject: [PATCH] [EMSCRIPTEN] add custom input driver, removes SDL dependency + adds mouse support --- Makefile.emscripten | 18 +-- emscripten/RWebAudio.c => audio/rwebaudio.c | 2 +- config.def.h | 3 + driver.c | 5 +- driver.h | 1 + emscripten/RWebInput.h | 29 ++++ emscripten/library_rwebinput.js | 115 +++++++++++++++ gfx/context/emscriptenegl_ctx.c | 12 +- input/input_common.c | 90 ++++++++++++ input/input_common.h | 1 + input/rwebinput_input.c | 149 ++++++++++++++++++++ settings.c | 2 + 12 files changed, 403 insertions(+), 24 deletions(-) rename emscripten/RWebAudio.c => audio/rwebaudio.c (98%) create mode 100644 emscripten/RWebInput.h create mode 100644 emscripten/library_rwebinput.js create mode 100644 input/rwebinput_input.c diff --git a/Makefile.emscripten b/Makefile.emscripten index 559638ab75..c1e913419c 100644 --- a/Makefile.emscripten +++ b/Makefile.emscripten @@ -15,6 +15,7 @@ OBJ = frontend/frontend_emscripten.o \ movie.o \ gfx/gfx_common.o \ input/input_common.o \ + input/rwebinput_input.o \ core_options.o \ patch.o \ compat/compat.o \ @@ -22,7 +23,7 @@ OBJ = frontend/frontend_emscripten.o \ screenshot.o \ cheats.o \ audio/utils.o \ - emscripten/RWebAudio.o \ + audio/rwebaudio.o \ input/overlay.o \ fifo_buffer.o \ gfx/scaler/scaler.o \ @@ -41,9 +42,7 @@ OBJ = frontend/frontend_emscripten.o \ HAVE_OPENGL = 1 HAVE_RGUI = 1 -HAVE_SDL = 1 -HAVE_SDL_IMAGE = 1 -HAVE_FREETYPE = 1 +HAVE_SDL = 0 HAVE_ZLIB = 1 HAVE_FBO = 1 WANT_MINIZ = 1 @@ -59,11 +58,7 @@ libretro = libretro_emscripten.bc LIBS = -lm DEFINES = -DHAVE_SCREENSHOTS -DHAVE_NULLAUDIO -DHAVE_BSV_MOVIE -DPACKAGE_VERSION=\"0.9.9.3\" -LDFLAGS = -L. -s TOTAL_MEMORY=$(MEMORY) --js-library emscripten/library_rwebaudio.js - -ifeq ($(SCALER_NO_SIMD), 1) - DEFINES += -DSCALER_NO_SIMD -endif +LDFLAGS = -L. -s TOTAL_MEMORY=$(MEMORY) --js-library emscripten/library_rwebaudio.js --js-library emscripten/library_rwebinput.js ifeq ($(PERF_TEST), 1) DEFINES += -DPERF_TEST @@ -80,11 +75,6 @@ ifeq ($(HAVE_SDL), 1) DEFINES += -ISDL -DHAVE_SDL endif -ifeq ($(HAVE_THREADS), 1) - OBJ += autosave.o thread.o gfx/thread_wrapper.o - DEFINES += -DHAVE_THREADS -endif - ifeq ($(HAVE_OPENGL), 1) OBJ += gfx/gl.o gfx/math/matrix.o gfx/fonts/gl_font.o gfx/fonts/gl_raster_font.o gfx/gfx_context.o gfx/context/emscriptenegl_ctx.o gfx/shader_glsl.o gfx/glsym/rglgen.o gfx/glsym/glsym_es2.o DEFINES += -DHAVE_OPENGL -DHAVE_OPENGLES -DHAVE_OPENGLES2 -DHAVE_EGL -DHAVE_OVERLAY -DHAVE_GLSL diff --git a/emscripten/RWebAudio.c b/audio/rwebaudio.c similarity index 98% rename from emscripten/RWebAudio.c rename to audio/rwebaudio.c index 1040da32d7..7139a88de4 100644 --- a/emscripten/RWebAudio.c +++ b/audio/rwebaudio.c @@ -16,7 +16,7 @@ #include "../driver.h" #include "../general.h" -#include "RWebAudio.h" +#include "../emscripten/RWebAudio.h" static void ra_free(void *data) { diff --git a/config.def.h b/config.def.h index 32d263a623..9e6903f4e4 100644 --- a/config.def.h +++ b/config.def.h @@ -78,6 +78,7 @@ enum INPUT_LINUXRAW, INPUT_APPLE, INPUT_QNX, + INPUT_RWEBINPUT, INPUT_NULL }; @@ -155,6 +156,8 @@ enum #define INPUT_DEFAULT_DRIVER INPUT_ANDROID #elif defined(_WIN32) #define INPUT_DEFAULT_DRIVER INPUT_DINPUT +#elif defined(EMSCRIPTEN) +#define INPUT_DEFAULT_DRIVER INPUT_RWEBINPUT #elif defined(HAVE_SDL) #define INPUT_DEFAULT_DRIVER INPUT_SDL #elif defined(__CELLOS_LV2__) diff --git a/driver.c b/driver.c index 51e8e6a577..cc5ea7f253 100644 --- a/driver.c +++ b/driver.c @@ -63,7 +63,7 @@ static const audio_driver_t *audio_drivers[] = { #ifdef HAVE_JACK &audio_jack, #endif -#if defined(HAVE_SDL) && !defined(EMSCRIPTEN) +#ifdef HAVE_SDL &audio_sdl, #endif #ifdef HAVE_XAUDIO @@ -168,6 +168,9 @@ static const input_driver_t *input_drivers[] = { #ifdef __BLACKBERRY_QNX__ &input_qnx, #endif +#ifdef EMSCRIPTEN + &input_rwebinput, +#endif #ifdef HAVE_NULLINPUT &input_null, #endif diff --git a/driver.h b/driver.h index 91dd004005..8bf5cc4f6b 100644 --- a/driver.h +++ b/driver.h @@ -537,6 +537,7 @@ extern const input_driver_t input_xinput; extern const input_driver_t input_linuxraw; extern const input_driver_t input_apple; extern const input_driver_t input_qnx; +extern const input_driver_t input_rwebinput; extern const input_driver_t input_null; #include "driver_funcs.h" diff --git a/emscripten/RWebInput.h b/emscripten/RWebInput.h new file mode 100644 index 0000000000..103cc7a0cd --- /dev/null +++ b/emscripten/RWebInput.h @@ -0,0 +1,29 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2013 - 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 + +typedef struct rwebinput_state +{ + char keys[32]; + int mouse_x; + int mouse_y; + char mouse_l; + char mouse_r; +} rwebinput_state_t; + +int RWebInputInit(void); +rwebinput_state_t *RWebInputPoll(int context); +void RWebInputDestroy(int context); diff --git a/emscripten/library_rwebinput.js b/emscripten/library_rwebinput.js new file mode 100644 index 0000000000..3ca2fd9cd1 --- /dev/null +++ b/emscripten/library_rwebinput.js @@ -0,0 +1,115 @@ +//"use strict"; + +var LibraryRWebInput = { + $RI__deps: ['$Browser'], + $RI: { + temp: null, + contexts: [], + + eventHandler: function(event) { + var i; + switch (event.type) { + case 'mousemove': + var x = event['movementX'] || event['mozMovementX'] || event['webkitMovementX']; + var y = event['movementY'] || event['mozMovementY'] || event['webkitMovementY']; + for (i = 0; i < RI.contexts.length; i++) { + var oldX = {{{ makeGetValue('RI.contexts[i].state', '32', 'i32') }}}; + var oldY = {{{ makeGetValue('RI.contexts[i].state', '36', 'i32') }}}; + x += oldX; + y += oldY; + {{{ makeSetValue('RI.contexts[i].state', '32', 'x', 'i32') }}}; + {{{ makeSetValue('RI.contexts[i].state', '36', 'y', 'i32') }}}; + } + break; + case 'mouseup': + case 'mousedown': + var l, r; + + if (event.buttons & 1) l = 1; + else l = 0; + + if (event.buttons & 2) r = 1; + else r = 0; + + for (i = 0; i < RI.contexts.length; i++) { + {{{ makeSetValue('RI.contexts[i].state', '40', 'l', 'i8') }}}; + {{{ makeSetValue('RI.contexts[i].state', '41', 'r', 'i8') }}}; + } + break; + case 'click': + e.preventDefault(); + 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(latency) { + if (RI.contexts.length === 0) { + document.addEventListener('keyup', RI.eventHandler, false); + document.addEventListener('keydown', RI.eventHandler, false); + document.addEventListener('mousemove', RI.eventHandler, false); + document.addEventListener('mouseup', RI.eventHandler, false); + document.addEventListener('mousedown', RI.eventHandler, false); + document.addEventListener('click', RI.eventHandler, 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); + document.removeEventListener('mouseup', RI.eventHandler, false); + document.removeEventListener('mousedown', RI.eventHandler, false); + document.removeEventListener('click', RI.eventHandler, false); + document.removeEventListener('blur', RI.eventHandler, false); + document.removeEventListener('onvisbilitychange', RI.eventHandler, false); + } + } + } +}; + +autoAddDeps(LibraryRWebInput, '$RI'); +mergeInto(LibraryManager.library, LibraryRWebInput); diff --git a/gfx/context/emscriptenegl_ctx.c b/gfx/context/emscriptenegl_ctx.c index 363152c358..f9b89a42ca 100644 --- a/gfx/context/emscriptenegl_ctx.c +++ b/gfx/context/emscriptenegl_ctx.c @@ -31,7 +31,6 @@ #include #include #include -#include static EGLContext g_egl_ctx; static EGLSurface g_egl_surf; @@ -220,15 +219,12 @@ static void gfx_ctx_input_driver(const input_driver_t **input, void **input_data { *input = NULL; - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE) != 0) - return; + void *rwebinput = input_rwebinput.init(); - void *sdlinput = input_sdl.init(); - - if (sdlinput) + if (rwebinput) { - *input = &input_sdl; - *input_data = sdlinput; + *input = &input_rwebinput; + *input_data = rwebinput; } } diff --git a/input/input_common.c b/input/input_common.c index e58eede808..9993a99f13 100644 --- a/input/input_common.c +++ b/input/input_common.c @@ -513,6 +513,96 @@ 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 }, + { 0, RETROK_UNKNOWN }, +}; +#endif + static enum retro_key rarch_keysym_lut[RETROK_LAST]; void input_init_keyboard_lut(const struct rarch_key_map *map) diff --git a/input/input_common.h b/input/input_common.h index 2ce912e98a..64b6447062 100644 --- a/input/input_common.h +++ b/input/input_common.h @@ -105,6 +105,7 @@ struct rarch_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_dinput[]; +extern const struct rarch_key_map rarch_key_map_rwebinput[]; void input_init_keyboard_lut(const struct rarch_key_map *map); enum retro_key input_translate_keysym_to_rk(unsigned sym); diff --git a/input/rwebinput_input.c b/input/rwebinput_input.c new file mode 100644 index 0000000000..1a65eec7d3 --- /dev/null +++ b/input/rwebinput_input.c @@ -0,0 +1,149 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2013 - 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 "input_common.h" + +#include "../driver.h" + +#include "../boolean.h" +#include "../general.h" + +#include "../emscripten/RWebInput.h" + +static bool uninited = false; + +typedef struct rwebinput_input +{ + rwebinput_state_t state; + int context; +} rwebinput_input_t; + +static void *rwebinput_input_init(void) +{ + rwebinput_input_t *rwebinput = (rwebinput_input_t*)calloc(1, sizeof(*rwebinput)); + if (!rwebinput) + return NULL; + + rwebinput->context = RWebInputInit(); + if (!rwebinput->context) + { + free(rwebinput); + return NULL; + } + + input_init_keyboard_lut(rarch_key_map_rwebinput); + + return rwebinput; +} + +static bool rwebinput_key_pressed(rwebinput_input_t *rwebinput, int key) +{ + if (key >= RETROK_LAST) + return false; + + unsigned sym = input_translate_rk_to_keysym((enum retro_key)key); + bool ret = rwebinput->state.keys[sym >> 3] & (1 << (sym & 7)); + return ret; +} + +static bool rwebinput_is_pressed(rwebinput_input_t *rwebinput, const struct retro_keybind *binds, unsigned id) +{ + if (id < RARCH_BIND_LIST_END) + { + const struct retro_keybind *bind = &binds[id]; + return bind->valid && rwebinput_key_pressed(rwebinput, binds[id].key); + } + else + return false; +} + +static bool rwebinput_bind_button_pressed(void *data, int key) +{ + rwebinput_input_t *rwebinput = (rwebinput_input_t*)data; + return rwebinput_is_pressed(rwebinput, g_settings.input.binds[0], key); +} + +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; + default: + return 0; + } +} + +static int16_t rwebinput_input_state(void *data, const struct retro_keybind **binds, unsigned port, unsigned device, unsigned index, unsigned id) +{ + rwebinput_input_t *rwebinput = (rwebinput_input_t*)data; + + switch (device) + { + case RETRO_DEVICE_JOYPAD: + return rwebinput_is_pressed(rwebinput, binds[port], id); + + case RETRO_DEVICE_KEYBOARD: + return rwebinput_key_pressed(rwebinput, id); + + case RETRO_DEVICE_MOUSE: + return rwebinput_mouse_state(rwebinput, id); + + default: + return 0; + } +} + +static void rwebinput_input_free(void *data) +{ + rwebinput_input_t *rwebinput = (rwebinput_input_t*)data; + uninited = true; + + RWebInputDestroy(rwebinput->context); + + free(data); +} + +static void rwebinput_input_poll(void *data) +{ + rwebinput_input_t *rwebinput = (rwebinput_input_t*)data; + + rwebinput_state_t *state = RWebInputPoll(rwebinput->context); + memcpy(&rwebinput->state, state, sizeof(rwebinput->state)); +} + +static void rwebinput_grab_mouse(void *data, bool state) +{ + (void)data; + (void)state; +} + +const input_driver_t input_rwebinput = { + rwebinput_input_init, + rwebinput_input_poll, + rwebinput_input_state, + rwebinput_bind_button_pressed, + rwebinput_input_free, + NULL, + "rwebinput", + rwebinput_grab_mouse, +}; + diff --git a/settings.c b/settings.c index 682774941a..4e4331bc63 100644 --- a/settings.c +++ b/settings.c @@ -139,6 +139,8 @@ const char *config_get_default_input(void) return "apple_input"; case INPUT_QNX: return "qnx_input"; + case INPUT_RWEBINPUT: + return "rwebinput"; case INPUT_NULL: return "null"; default: