From ef0d8e6173dbeaf7e3f1323c87ea7982624503d9 Mon Sep 17 00:00:00 2001 From: Andreas Bergmeier Date: Mon, 13 Jan 2014 19:56:32 +0100 Subject: [PATCH 01/85] Add support for Ninja and Eclipse+Ninja testing in test_cmake. --- tests/test_other.py | 69 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 7 deletions(-) diff --git a/tests/test_other.py b/tests/test_other.py index f91b4683d1a82..c6147f6372494 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -284,9 +284,33 @@ def test_cmake(self): if WINDOWS: generators = ['MinGW Makefiles', 'NMake Makefiles'] else: - generators = ['Unix Makefiles'] - - make_commands = { 'MinGW Makefiles': ['mingw32-make'], 'NMake Makefiles': ['nmake', '/NOLOGO'], 'Unix Makefiles': ['make'] } + generators = ['Unix Makefiles', 'Ninja', 'Eclipse CDT4 - Ninja'] + + def nmake_detect_error(configuration): + if Building.which(configuration['build'][0]): + return None + else: + return 'Skipping NMake test for CMake support, since nmake was not found in PATH. Run this test in Visual Studio command prompt to easily access nmake.' + + def check_makefile(configuration, dirname): + assert os.path.exists(dirname + '/Makefile'), 'CMake call did not produce a Makefile!' + + configurations = { 'MinGW Makefiles' : { 'prebuild': check_makefile, + 'build' : ['mingw32-make'], + + }, + 'NMake Makefiles' : { 'detect' : nmake_detect_error, + 'prebuild': check_makefile, + 'build' : ['nmake', '/NOLOGO'], + }, + 'Unix Makefiles' : { 'prebuild': check_makefile, + 'build' : ['make'], + }, + 'Ninja' : { 'build' : ['ninja'], + }, + 'Eclipse CDT4 - Ninja': { 'build' : ['ninja'], + } + } if os.name == 'nt': emconfigure = path_from_root('emconfigure.bat') @@ -294,11 +318,37 @@ def test_cmake(self): emconfigure = path_from_root('emconfigure') for generator in generators: - if generator == 'NMake Makefiles' and not Building.which('nmake'): - print >> sys.stderr, 'Skipping NMake test for CMake support, since nmake was not found in PATH. Run this test in Visual Studio command prompt to easily access nmake.' + conf = configurations[generator] + + make = conf['build'] + + try: + detector = conf['detect'] + except KeyError: + detector = None + + if detector: + error = detector(conf) + elif len(make) == 1 and not Building.which(make[0]): + # Use simple test if applicable + error = 'Skipping %s test for CMake support, since it could not be detected.' % generator + else: + error = None + + if error: + print >> sys.stderr, error continue - make = make_commands[generator] + try: + prebuild = conf['prebuild'] + except KeyError: + prebuild = None + + try: + postbuild = conf['postbuild'] + except KeyError: + postbuild = None + cmake_cases = ['target_js', 'target_html'] cmake_outputs = ['test_cmake.js', 'hello_world_gles.html'] for i in range(0, 2): @@ -331,7 +381,9 @@ def test_cmake(self): logging.error('Failed command: ' + ' '.join(cmd)) logging.error('Result:\n' + ret[1]) raise Exception('cmake call failed!') - assert os.path.exists(tempdirname + '/Makefile'), 'CMake call did not produce a Makefile!' + + if prebuild: + prebuild(configuration, tempdirname) # Build cmd = make + (['VERBOSE=1'] if verbose_level >= 3 else []) @@ -344,6 +396,9 @@ def test_cmake(self): raise Exception('make failed!') assert os.path.exists(tempdirname + '/' + cmake_outputs[i]), 'Building a cmake-generated Makefile failed to produce an output file %s!' % tempdirname + '/' + cmake_outputs[i] + if postbuild: + postbuild(configuration, tempdirname) + # Run through node, if CMake produced a .js file. if cmake_outputs[i].endswith('.js'): ret = Popen(listify(NODE_JS) + [tempdirname + '/' + cmake_outputs[i]], stdout=PIPE).communicate()[0] From 726bbf32e89b8578df540af21194dac4d019eb67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Mon, 9 Dec 2013 15:29:01 +0200 Subject: [PATCH 02/85] Fix typo in GL library abort function case. --- src/library_gl.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/library_gl.js b/src/library_gl.js index f6978c04e973c..64f13701dde15 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -2585,7 +2585,7 @@ var LibraryGL = { return "float"; } - return Abort_NoSupport("Unsupported combiner op: 0x" + op.toString(16)); + return Abort_noSupport("Unsupported combiner op: 0x" + op.toString(16)); } function getCurTexUnit() { @@ -2912,7 +2912,7 @@ var LibraryGL = { } } - return Abort_NoSupport("Unsupported TexEnv mode: 0x" + this.mode.toString(16)); + return Abort_noSupport("Unsupported TexEnv mode: 0x" + this.mode.toString(16)); } CTexEnv.prototype.genCombinerLines = function CTexEnv_getCombinerLines(isColor, outputVar, From 3ee72465de5bdebce7584f65586228ab5b32c01e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Mon, 9 Dec 2013 15:34:00 +0200 Subject: [PATCH 03/85] Fix (harmless) out-of-bounds access on vertex attribute indices in GL emulation init when GL_FFP_ONLY is set. --- src/library_gl.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/library_gl.js b/src/library_gl.js index 64f13701dde15..b96ba0f21534d 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -3643,7 +3643,8 @@ var LibraryGL = { GLctx.bindAttribLocation(this.program, GLImmediate.VERTEX, 'a_position'); GLctx.bindAttribLocation(this.program, GLImmediate.COLOR, 'a_color'); GLctx.bindAttribLocation(this.program, GLImmediate.NORMAL, 'a_normal'); - for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) { + var maxVertexAttribs = GLctx.getParameter(GLctx.MAX_VERTEX_ATTRIBS); + for (var i = 0; i < GLImmediate.MAX_TEXTURES && GLImmediate.TEXTURE0 + i < maxVertexAttribs; i++) { GLctx.bindAttribLocation(this.program, GLImmediate.TEXTURE0 + i, 'a_texCoord'+i); GLctx.bindAttribLocation(this.program, GLImmediate.TEXTURE0 + i, aTexCoordPrefix+i); } From 03a3c426265d1cf8c11073e35e75be911004b57d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Mon, 9 Dec 2013 19:08:43 +0200 Subject: [PATCH 04/85] Implement glGetTexEnviv and glGetTexEnvfv in GL emulation mode. --- src/library_gl.js | 112 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/src/library_gl.js b/src/library_gl.js index b96ba0f21534d..ba0216cbb37c4 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -3329,6 +3329,107 @@ var LibraryGL = { Module.printErr('WARNING: Unhandled `pname` in call to `glTexEnvfv`.'); } }, + + hook_getTexEnviv: function(target, pname, param) { + if (target != GL_TEXTURE_ENV) + return; + + var env = getCurTexUnit().env; + switch (pname) { + case GL_TEXTURE_ENV_MODE: + {{{ makeSetValue('param', '0', 'env.mode', 'i32') }}}; + return; + + case GL_TEXTURE_ENV_COLOR: + {{{ makeSetValue('param', '0', 'Math.max(Math.min(env.envColor[0]*255, 255, -255))', 'i32') }}}; + {{{ makeSetValue('param', '1', 'Math.max(Math.min(env.envColor[1]*255, 255, -255))', 'i32') }}}; + {{{ makeSetValue('param', '2', 'Math.max(Math.min(env.envColor[2]*255, 255, -255))', 'i32') }}}; + {{{ makeSetValue('param', '3', 'Math.max(Math.min(env.envColor[3]*255, 255, -255))', 'i32') }}}; + return; + + case GL_COMBINE_RGB: + {{{ makeSetValue('param', '0', 'env.colorCombiner', 'i32') }}}; + return; + + case GL_COMBINE_ALPHA: + {{{ makeSetValue('param', '0', 'env.alphaCombiner', 'i32') }}}; + return; + + case GL_SRC0_RGB: + {{{ makeSetValue('param', '0', 'env.colorSrc[0]', 'i32') }}}; + return; + + case GL_SRC1_RGB: + {{{ makeSetValue('param', '0', 'env.colorSrc[1]', 'i32') }}}; + return; + + case GL_SRC2_RGB: + {{{ makeSetValue('param', '0', 'env.colorSrc[2]', 'i32') }}}; + return; + + case GL_SRC0_ALPHA: + {{{ makeSetValue('param', '0', 'env.alphaSrc[0]', 'i32') }}}; + return; + + case GL_SRC1_ALPHA: + {{{ makeSetValue('param', '0', 'env.alphaSrc[1]', 'i32') }}}; + return; + + case GL_SRC2_ALPHA: + {{{ makeSetValue('param', '0', 'env.alphaSrc[2]', 'i32') }}}; + return; + + case GL_OPERAND0_RGB: + {{{ makeSetValue('param', '0', 'env.colorOp[0]', 'i32') }}}; + return; + + case GL_OPERAND1_RGB: + {{{ makeSetValue('param', '0', 'env.colorOp[1]', 'i32') }}}; + return; + + case GL_OPERAND2_RGB: + {{{ makeSetValue('param', '0', 'env.colorOp[2]', 'i32') }}}; + return; + + case GL_OPERAND0_ALPHA: + {{{ makeSetValue('param', '0', 'env.alphaOp[0]', 'i32') }}}; + return; + + case GL_OPERAND1_ALPHA: + {{{ makeSetValue('param', '0', 'env.alphaOp[1]', 'i32') }}}; + return; + + case GL_OPERAND2_ALPHA: + {{{ makeSetValue('param', '0', 'env.alphaOp[2]', 'i32') }}}; + return; + + case GL_RGB_SCALE: + {{{ makeSetValue('param', '0', 'env.colorScale', 'i32') }}}; + return; + + case GL_ALPHA_SCALE: + {{{ makeSetValue('param', '0', 'env.alphaScale', 'i32') }}}; + return; + + default: + Module.printErr('WARNING: Unhandled `pname` in call to `glGetTexEnvi`.'); + } + }, + + hook_getTexEnvfv: function(target, pname, param) { + if (target != GL_TEXTURE_ENV) + return; + + var env = getCurTexUnit().env; + switch (pname) { + case GL_TEXTURE_ENV_COLOR: + {{{ makeSetValue('param', '0', 'env.envColor[0]', 'float') }}}; + {{{ makeSetValue('param', '1', 'env.envColor[1]', 'float') }}}; + {{{ makeSetValue('param', '2', 'env.envColor[2]', 'float') }}}; + {{{ makeSetValue('param', '3', 'env.envColor[3]', 'float') }}}; + return; + } + } }; }, @@ -4003,6 +4104,14 @@ var LibraryGL = { //glTexEnvfv(target, pname, param); }; + _glGetTexEnviv = function _glGetTexEnviv(target, pname, param) { + GL.immediate.TexEnvJIT.hook_getTexEnviv(target, pname, param); + }; + + _glGetTexEnvfv = function _glGetTexEnvfv(target, pname, param) { + GL.immediate.TexEnvJIT.hook_getTexEnvfv(target, pname, param); + }; + var glGetIntegerv = _glGetIntegerv; _glGetIntegerv = function _glGetIntegerv(pname, params) { switch (pname) { @@ -4787,6 +4896,9 @@ var LibraryGL = { glTexEnvf: function() { Runtime.warnOnce('glTexEnvf: TODO') }, glTexEnvfv: function() { Runtime.warnOnce('glTexEnvfv: TODO') }, + glGetTexEnviv: function(target, pname, param) { throw 'GL emulation not initialized!'; }, + glGetTexEnvfv: function(target, pname, param) { throw 'GL emulation not initialized!'; }, + glTexImage1D: function() { throw 'glTexImage1D: TODO' }, glTexCoord3f: function() { throw 'glTexCoord3f: TODO' }, glGetTexLevelParameteriv: function() { throw 'glGetTexLevelParameteriv: TODO' }, From 3d05d222cbbb0ab6b8be12c5ea86fdf49d0d691a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Thu, 12 Dec 2013 18:39:44 +0200 Subject: [PATCH 05/85] Fix typo in case of abort_noSupport in library_gl.js. --- src/library_gl.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/library_gl.js b/src/library_gl.js index ba0216cbb37c4..9dceb058da1a1 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -2585,7 +2585,7 @@ var LibraryGL = { return "float"; } - return Abort_noSupport("Unsupported combiner op: 0x" + op.toString(16)); + return abort_noSupport("Unsupported combiner op: 0x" + op.toString(16)); } function getCurTexUnit() { @@ -2912,7 +2912,7 @@ var LibraryGL = { } } - return Abort_noSupport("Unsupported TexEnv mode: 0x" + this.mode.toString(16)); + return abort_noSupport("Unsupported TexEnv mode: 0x" + this.mode.toString(16)); } CTexEnv.prototype.genCombinerLines = function CTexEnv_getCombinerLines(isColor, outputVar, From 8c93579ff8761d06d691aec0d73124956a2d6452 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Thu, 12 Dec 2013 19:03:49 +0200 Subject: [PATCH 06/85] Add test for glGetTexEnv implementation. Fix output of GL_TEXTURE_ENV_COLOR color data from glGetTexEnvfv. --- src/library_gl.js | 6 ++-- tests/glgettexenv.c | 71 +++++++++++++++++++++++++++++++++++++++++++ tests/test_browser.py | 3 ++ 3 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 tests/glgettexenv.c diff --git a/src/library_gl.js b/src/library_gl.js index 9dceb058da1a1..ffc395b4eb739 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -3424,9 +3424,9 @@ var LibraryGL = { switch (pname) { case GL_TEXTURE_ENV_COLOR: {{{ makeSetValue('param', '0', 'env.envColor[0]', 'float') }}}; - {{{ makeSetValue('param', '1', 'env.envColor[1]', 'float') }}}; - {{{ makeSetValue('param', '2', 'env.envColor[2]', 'float') }}}; - {{{ makeSetValue('param', '3', 'env.envColor[3]', 'float') }}}; + {{{ makeSetValue('param', '4', 'env.envColor[1]', 'float') }}}; + {{{ makeSetValue('param', '8', 'env.envColor[2]', 'float') }}}; + {{{ makeSetValue('param', '12', 'env.envColor[3]', 'float') }}}; return; } } diff --git a/tests/glgettexenv.c b/tests/glgettexenv.c new file mode 100644 index 0000000000000..a051a69048646 --- /dev/null +++ b/tests/glgettexenv.c @@ -0,0 +1,71 @@ +/* +THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION +AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN. + +THE ORIGINAL AUTHOR IS KYLE FOLEY. + +THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY +OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF +MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, +ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE +RESULTING FROM THE USE, MODIFICATION, OR +REDISTRIBUTION OF THIS SOFTWARE. +*/ + +#if !EMSCRIPTEN +#define USE_GLEW 1 +#endif + +#if USE_GLEW +#include "GL/glew.h" +#endif + +#include "SDL/SDL.h" +#if !USE_GLEW +#include "SDL/SDL_opengl.h" +#endif + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + SDL_Surface *screen; + if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) { + printf("Unable to initialize SDL: %s\n", SDL_GetError()); + return 1; + } + + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); + screen = SDL_SetVideoMode( 640, 480, 24, SDL_OPENGL ); + if ( !screen ) { + printf("Unable to set video mode: %s\n", SDL_GetError()); + return 1; + } + + GLint value = 0; + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND); + glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &value); + assert(value == GL_BLEND); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &value); + assert(value == GL_MODULATE); + + GLfloat colora[4] = { 0.2f, 0.3f, 0.4f, 0.5f }; + GLfloat colorb[4] = {}; + glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, colora); + glGetTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, colorb); + printf("%f %f %f %f\n", colorb[0], colorb[1], colorb[2], colorb[3]); + assert(colora[0] == colorb[0]); + assert(colora[1] == colorb[1]); + assert(colora[2] == colorb[2]); + assert(colora[3] == colorb[3]); + SDL_Quit(); + +#ifdef REPORT_RESULT + int result = 1; + REPORT_RESULT(); +#endif + return 0; +} diff --git a/tests/test_browser.py b/tests/test_browser.py index c2eaabb6c3a29..27dffff216941 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -1543,6 +1543,9 @@ def test_cubegeom_pre2_vao2(self): def test_cube_explosion(self): self.btest('cube_explosion.c', reference='cube_explosion.png', args=['-s', 'LEGACY_GL_EMULATION=1']) + def test_glgettexenv(self): + self.btest('glgettexenv.c', args=['-s', 'LEGACY_GL_EMULATION=1'], expected=['1']) + def test_sdl_canvas_blank(self): self.btest('sdl_canvas_blank.c', reference='sdl_canvas_blank.png') From 3cd9b678c63f728ec2f4647dcd1fa4e5a5103194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Tue, 14 Jan 2014 12:44:46 +0200 Subject: [PATCH 07/85] Fix GLImmediate signature after rebase. --- src/library_gl.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/library_gl.js b/src/library_gl.js index ffc395b4eb739..a4da4220e8c26 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -4105,11 +4105,11 @@ var LibraryGL = { }; _glGetTexEnviv = function _glGetTexEnviv(target, pname, param) { - GL.immediate.TexEnvJIT.hook_getTexEnviv(target, pname, param); + GLImmediate.TexEnvJIT.hook_getTexEnviv(target, pname, param); }; _glGetTexEnvfv = function _glGetTexEnvfv(target, pname, param) { - GL.immediate.TexEnvJIT.hook_getTexEnvfv(target, pname, param); + GLImmediate.TexEnvJIT.hook_getTexEnvfv(target, pname, param); }; var glGetIntegerv = _glGetIntegerv; From facbcfb04ddaaf5053e928ef792cf7258da5c110 Mon Sep 17 00:00:00 2001 From: Andreas Bergmeier Date: Tue, 14 Jan 2014 13:18:06 +0100 Subject: [PATCH 08/85] Replace printing to stderr with logging warning. --- tests/test_other.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_other.py b/tests/test_other.py index c6147f6372494..6d7995ee8b4e6 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -336,7 +336,7 @@ def check_makefile(configuration, dirname): error = None if error: - print >> sys.stderr, error + logging.warning(error) continue try: From f989aa0abb94592d7b757a1d58cd4f9e4df51d8e Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Thu, 16 Jan 2014 20:59:16 +0700 Subject: [PATCH 09/85] Remove __01*64_ aliases that don't appear to be necessary. These were added when Python support was fixed up, but don't appear to be needed or used anywhere. --- src/library.js | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/library.js b/src/library.js index 69569601bc432..fc12075332212 100644 --- a/src/library.js +++ b/src/library.js @@ -149,8 +149,6 @@ LibraryManager.library = { } return {{{ makeGetValue(0, '_readdir.result', 'i8*') }}}; }, - __01readdir64_: 'readdir', - // TODO: Check if we need to link any other aliases. // ========================================================================== // utime.h @@ -398,11 +396,6 @@ LibraryManager.library = { stat64: 'stat', fstat64: 'fstat', lstat64: 'lstat', - __01fstat64_: 'fstat', - __01stat64_: 'stat', - __01lstat64_: 'lstat', - - // TODO: Check if other aliases are needed. // ========================================================================== // sys/statvfs.h @@ -433,8 +426,6 @@ LibraryManager.library = { // http://pubs.opengroup.org/onlinepubs/009604499/functions/statvfs.html return _statvfs(0, buf); }, - __01statvfs64_: 'statvfs', - __01fstatvfs64_: 'fstatvfs', // ========================================================================== // fcntl.h @@ -1568,11 +1559,6 @@ LibraryManager.library = { open64: 'open', lseek64: 'lseek', ftruncate64: 'ftruncate', - __01open64_: 'open', - __01lseek64_: 'lseek', - __01truncate64_: 'truncate', - __01ftruncate64_: 'ftruncate', - // TODO: Check if any other aliases are needed. // ========================================================================== // stdio.h @@ -2898,11 +2884,6 @@ LibraryManager.library = { #endif fopen64: 'fopen', - __01fopen64_: 'fopen', - __01freopen64_: 'freopen', - __01fseeko64_: 'fseek', - __01ftello64_: 'ftell', - __01tmpfile64_: 'tmpfile', __isoc99_fscanf: 'fscanf', // TODO: Check if any other aliases are needed. _IO_getc: 'getc', @@ -2950,7 +2931,6 @@ LibraryManager.library = { _mmap.mappings[ptr] = { malloc: ptr, num: num, allocated: allocated }; return ptr; }, - __01mmap64_: 'mmap', munmap: function(start, num) { if (!_mmap.mappings) _mmap.mappings = {}; @@ -6871,7 +6851,6 @@ LibraryManager.library = { // int setrlimit(int resource, const struct rlimit *rlp) return 0; }, - __01getrlimit64_: 'getrlimit', // TODO: Implement for real. We just do time used, and no useful data getrusage: function(resource, rlp) { From 3c4f29d187ce68f6e0b21a59eb8399a7c6e6eb16 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 16 Jan 2014 21:15:02 -0800 Subject: [PATCH 10/85] make asm2g use SAFE_HEAP in fastcomp --- tests/test_core.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_core.py b/tests/test_core.py index a884ea7560c90..5aab48f5a36e4 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -6422,7 +6422,10 @@ def setUp(self): asm1 = make_run("asm1", compiler=CLANG, emcc_args=["-O1"]) asm2 = make_run("asm2", compiler=CLANG, emcc_args=["-O2"]) asm2f = make_run("asm2f", compiler=CLANG, emcc_args=["-O2", "-s", "PRECISE_F32=1"]) -asm2g = make_run("asm2g", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "ASSERTIONS=1", "--memory-init-file", "1", "-s", "CHECK_HEAP_ALIGN=1"]) +if os.environ.get('EMCC_FAST_COMPILER') == '1': + asm2g = make_run("asm2g", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "ASSERTIONS=1", "--memory-init-file", "1", "-s", "SAFE_HEAP=1"]) +else: + asm2g = make_run("asm2g", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "ASSERTIONS=1", "--memory-init-file", "1", "-s", "CHECK_HEAP_ALIGN=1"]) asm2x86 = make_run("asm2x86", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "CHECK_HEAP_ALIGN=1"], env={"EMCC_LLVM_TARGET": "i386-pc-linux-gnu"}) # Make custom runs with various options From 6c0e61a08545ce1bb7eb7676d7d7f3d022253f23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Thu, 16 Jan 2014 15:49:23 +0200 Subject: [PATCH 11/85] Fix GL_FFP_ONLY path to enable/disable vertex attribute arrays as a response to glEnable/DisableClientState instead of adjusting that during rendering. --- src/library_gl.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/library_gl.js b/src/library_gl.js index 61ca8957dbd24..dd7aeafb2488d 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -3872,11 +3872,9 @@ var LibraryGL = { #if GL_FFP_ONLY if (!GL.currArrayBuffer) { GLctx.vertexAttribPointer(GLImmediate.VERTEX, posAttr.size, posAttr.type, false, GLImmediate.stride, posAttr.offset); - GL.enableVertexAttribArray(GLImmediate.VERTEX); if (this.hasNormal) { var normalAttr = clientAttributes[GLImmediate.NORMAL]; GLctx.vertexAttribPointer(GLImmediate.NORMAL, normalAttr.size, normalAttr.type, true, GLImmediate.stride, normalAttr.offset); - GL.enableVertexAttribArray(GLImmediate.NORMAL); } } #else @@ -3899,11 +3897,9 @@ var LibraryGL = { var texAttr = clientAttributes[attribLoc]; if (texAttr.size) { GLctx.vertexAttribPointer(attribLoc, texAttr.size, texAttr.type, false, GLImmediate.stride, texAttr.offset); - GL.enableVertexAttribArray(attribLoc); } else { // These two might be dangerous, but let's try them. GLctx.vertexAttrib4f(attribLoc, 0, 0, 0, 1); - GL.disableVertexAttribArray(attribLoc); } } #else @@ -3938,21 +3934,18 @@ var LibraryGL = { #if GL_FFP_ONLY if (!GL.currArrayBuffer) { GLctx.vertexAttribPointer(GLImmediate.COLOR, colorAttr.size, colorAttr.type, true, GLImmediate.stride, colorAttr.offset); - GL.enableVertexAttribArray(GLImmediate.COLOR); } #else GLctx.vertexAttribPointer(this.colorLocation, colorAttr.size, colorAttr.type, true, GLImmediate.stride, colorAttr.offset); GLctx.enableVertexAttribArray(this.colorLocation); #endif - } else if (this.hasColor) { -#if GL_FFP_ONLY - GL.disableVertexAttribArray(GLImmediate.COLOR); - GLctx.vertexAttrib4fv(GLImmediate.COLOR, GLImmediate.clientColor); -#else + } +#if !GL_FFP_ONLY + else if (this.hasColor) { GLctx.disableVertexAttribArray(this.colorLocation); GLctx.vertexAttrib4fv(this.colorLocation, GLImmediate.clientColor); -#endif } +#endif if (this.hasFog) { if (this.fogColorLocation) GLctx.uniform4fv(this.fogColorLocation, GLEmulation.fogColor); if (this.fogEndLocation) GLctx.uniform1f(this.fogEndLocation, GLEmulation.fogEnd); @@ -4461,6 +4454,9 @@ var LibraryGL = { GLImmediate.clientColor[1] = g; GLImmediate.clientColor[2] = b; GLImmediate.clientColor[3] = a; +#if GL_FFP_ONLY + GLctx.vertexAttrib4fv(GLImmediate.COLOR, GLImmediate.clientColor); +#endif } }, glColor4d: 'glColor4f', @@ -4608,6 +4604,10 @@ var LibraryGL = { GLImmediate.enabledClientAttributes[attrib] = true; GLImmediate.totalEnabledClientAttributes++; GLImmediate.currentRenderer = null; // Will need to change current renderer, since the set of active vertex pointers changed. +#if GL_FFP_ONLY + // In GL_FFP_ONLY mode, attributes are bound to the same index in each FFP emulation shader, so we can immediately apply the change here. + GL.enableVertexAttribArray(attrib); +#endif if (GLEmulation.currentVao) GLEmulation.currentVao.enabledClientStates[cap] = 1; GLImmediate.modifiedClientAttributes = true; } @@ -4624,6 +4624,10 @@ var LibraryGL = { GLImmediate.enabledClientAttributes[attrib] = false; GLImmediate.totalEnabledClientAttributes--; GLImmediate.currentRenderer = null; // Will need to change current renderer, since the set of active vertex pointers changed. +#if GL_FFP_ONLY + // In GL_FFP_ONLY mode, attributes are bound to the same index in each FFP emulation shader, so we can immediately apply the change here. + GL.disableVertexAttribArray(attrib); +#endif if (GLEmulation.currentVao) delete GLEmulation.currentVao.enabledClientStates[cap]; GLImmediate.modifiedClientAttributes = true; } @@ -4635,7 +4639,6 @@ var LibraryGL = { #if GL_FFP_ONLY if (GL.currArrayBuffer) { GLctx.vertexAttribPointer(GLImmediate.VERTEX, size, type, false, stride, pointer); - GL.enableVertexAttribArray(GLImmediate.VERTEX); } #endif }, @@ -4645,7 +4648,6 @@ var LibraryGL = { if (GL.currArrayBuffer) { var loc = GLImmediate.TEXTURE0 + GLImmediate.clientActiveTexture; GLctx.vertexAttribPointer(loc, size, type, false, stride, pointer); - GL.enableVertexAttribArray(loc); } #endif }, @@ -4654,7 +4656,6 @@ var LibraryGL = { #if GL_FFP_ONLY if (GL.currArrayBuffer) { GLctx.vertexAttribPointer(GLImmediate.NORMAL, size, type, true, stride, pointer); - GL.enableVertexAttribArray(GLImmediate.NORMAL); } #endif }, @@ -4663,7 +4664,6 @@ var LibraryGL = { #if GL_FFP_ONLY if (GL.currArrayBuffer) { GLctx.vertexAttribPointer(GLImmediate.COLOR, size, type, true, stride, pointer); - GL.enableVertexAttribArray(GLImmediate.COLOR); } #endif }, From e138af189b10854ba16cbc99843a171c59c6d331 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 16 Jan 2014 17:20:47 -0800 Subject: [PATCH 12/85] remove current memory initialization chunking via .concat(..) --- emcc | 4 +--- src/parseTools.js | 23 ++--------------------- tools/shared.py | 2 +- 3 files changed, 4 insertions(+), 25 deletions(-) diff --git a/emcc b/emcc index c295a76e19647..4ad0706a2844c 100755 --- a/emcc +++ b/emcc @@ -1913,9 +1913,7 @@ try: shared.try_delete(memfile) def repl(m): # handle chunking of the memory initializer - s = re.sub('[\[\]\n\(\)\. ]', '', m.groups(0)[0]) - s = s.replace('concat', ',') - if s[-1] == ',': s = s[:-1] + s = m.groups(0)[0][1:-1] open(memfile, 'wb').write(''.join(map(lambda x: chr(int(x or '0')), s.split(',')))) if DEBUG: # Copy into temp dir as well, so can be run there too diff --git a/src/parseTools.js b/src/parseTools.js index 036ccfc1a2e5f..a564f2e35132d 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -1789,31 +1789,12 @@ function makePointer(slab, pos, allocator, type, ptr, finalMemoryInitialization) types = 'i8'; } - // JS engines sometimes say array initializers are too large. Work around that by chunking and calling concat to combine at runtime - var chunkSize = JS_CHUNK_SIZE; - function chunkify(array) { - // break very large slabs into parts - var ret = ''; - var index = 0; - while (index < array.length) { - ret = (ret ? ret + '.concat(' : '') + '[' + array.slice(index, index + chunkSize).map(JSON.stringify) + ']' + (ret ? ')\n' : ''); - index += chunkSize; - } - return ret; - } - if (typeof slab == 'object' && slab.length > chunkSize) { - slab = chunkify(slab); - } if (typeof types == 'object') { while (types.length < slab.length) types.push(0); } - if (typeof types != 'string' && types.length > chunkSize) { - types = chunkify(types); - } else { - types = JSON.stringify(types); - } + types = JSON.stringify(types); if (typeof slab == 'object') slab = '[' + slab.join(',') + ']'; - return 'allocate(' + slab + ', ' + types + (allocator ? ', ' + allocator : '') + (allocator == 'ALLOC_NONE' ? ', ' + ptr : '') + ')'; + return 'allocate(' + slab + ', ' + types + (allocator ? ', ' + allocator : '') + (allocator == 'ALLOC_NONE' ? ', ' + ptr : '') + ');'; } function makeGetSlabs(ptr, type, allowMultiple, unsigned) { diff --git a/tools/shared.py b/tools/shared.py index bb50350df7b5b..781a7ee7a5c3c 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -1568,7 +1568,7 @@ def fix(m): chunkify = cache.chunkify class JS: - memory_initializer_pattern = '/\* memory initializer \*/ allocate\(([\d,\.concat\(\)\[\]\\n ]+)"i8", ALLOC_NONE, ([\dRuntime\.GLOBAL_BASEH+]+)\)' + memory_initializer_pattern = '/\* memory initializer \*/ allocate\((\[[\d, ]+\]), "i8", ALLOC_NONE, ([\d+Runtime\.GLOBAL_BASEH]+)\);' no_memory_initializer_pattern = '/\* no memory initializer \*/' memory_staticbump_pattern = 'STATICTOP = STATIC_BASE \+ (\d+);' From 620ff9a94d98e34d4f394a683e6035596d6ac3ac Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 16 Jan 2014 18:36:08 -0800 Subject: [PATCH 13/85] optimize memory initializer if it is large --- emcc | 12 ++++++++- tools/shared.py | 71 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/emcc b/emcc index 4ad0706a2844c..51ad3cf26f7dd 100755 --- a/emcc +++ b/emcc @@ -1913,7 +1913,7 @@ try: shared.try_delete(memfile) def repl(m): # handle chunking of the memory initializer - s = m.groups(0)[0][1:-1] + s = m.groups(0)[0] open(memfile, 'wb').write(''.join(map(lambda x: chr(int(x or '0')), s.split(',')))) if DEBUG: # Copy into temp dir as well, so can be run there too @@ -1924,6 +1924,7 @@ try: src = re.sub(shared.JS.memory_initializer_pattern, repl, open(final).read(), count=1) open(final + '.mem.js', 'w').write(src) final += '.mem.js' + src = None js_transform_tempfiles[-1] = final # simple text substitution preserves comment line number mappings if DEBUG: if os.path.exists(memfile): @@ -1931,6 +1932,15 @@ try: logging.debug('wrote memory initialization to %s' % memfile) else: logging.debug('did not see memory initialization') + elif shared.Settings.USE_TYPED_ARRAYS == 2 and not shared.Settings.MAIN_MODULE and not shared.Settings.SIDE_MODULE: + # not writing a binary init, but we can at least optimize them by splitting them up + src = open(final).read() + src = shared.JS.optimize_initializer(src) + if src is not None: + logging.debug('optimizing memory initialization') + open(final + '.mem.js', 'w').write(src) + final += '.mem.js' + src = None # It is useful to run several js optimizer passes together, to save on unneeded unparsing/reparsing js_optimizer_queue = [] diff --git a/tools/shared.py b/tools/shared.py index 781a7ee7a5c3c..e05a5f7a7fb21 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -1568,7 +1568,7 @@ def fix(m): chunkify = cache.chunkify class JS: - memory_initializer_pattern = '/\* memory initializer \*/ allocate\((\[[\d, ]+\]), "i8", ALLOC_NONE, ([\d+Runtime\.GLOBAL_BASEH]+)\);' + memory_initializer_pattern = '/\* memory initializer \*/ allocate\(\[([\d, ]+)\], "i8", ALLOC_NONE, ([\d+Runtime\.GLOBAL_BASEH]+)\);' no_memory_initializer_pattern = '/\* no memory initializer \*/' memory_staticbump_pattern = 'STATICTOP = STATIC_BASE \+ (\d+);' @@ -1652,6 +1652,75 @@ def align(x, by): while x % by != 0: x += 1 return x + INITIALIZER_CHUNK_SIZE = 10240 + + @staticmethod + def collect_initializers(src): + ret = [] + max_offset = -1 + for init in re.finditer(JS.memory_initializer_pattern, src): + contents = init.group(1).split(',') + offset = sum([int(x) if x[0] != 'R' else 0 for x in init.group(2).split('+')]) + ret.append((offset, contents)) + assert offset > max_offset + max_offset = offset + return ret + + @staticmethod + def split_initializer(contents): + # given a memory initializer (see memory_initializer_pattern), split it up into multiple initializers to avoid long runs of zeros or a single overly-large allocator + ret = [] + l = len(contents) + maxx = JS.INITIALIZER_CHUNK_SIZE + i = 0 + start = 0 + while 1: + if i - start >= maxx or (i > start and i == l): + #print >> sys.stderr, 'new', start, i-start + ret.append((start, contents[start:i])) + start = i + if i == l: break + if contents[i] != '0': + i += 1 + else: + # look for a sequence of zeros + j = i + 1 + while j < l and contents[j] == '0': j += 1 + if j-i > maxx/10 or j-start >= maxx: + #print >> sys.stderr, 'skip', start, i-start, j-start + ret.append((start, contents[start:i])) # skip over the zeros starting at i and ending at j + start = j + i = j + return ret + + @staticmethod + def replace_initializers(src, inits): + class State: + first = True + def rep(m): + if not State.first: return '' + # write out all the new initializers in place of the first old one + State.first = False + def gen_init(init): + offset, contents = init + return '/* memory initializer */ allocate([%s], "i8", ALLOC_NONE, Runtime.GLOBAL_BASE%s);' % ( + ','.join(contents), + '' if offset == 0 else ('+%d' % offset) + ) + return '\n'.join(map(gen_init, inits)) + return re.sub(JS.memory_initializer_pattern, rep, src) + + @staticmethod + def optimize_initializer(src): + inits = JS.collect_initializers(src) + if len(inits) == 0: return None + assert len(inits) == 1 + init = inits[0] + offset, contents = init + assert offset == 0 # offset 0, singleton + if len(contents) <= JS.INITIALIZER_CHUNK_SIZE: return None + return JS.replace_initializers(src, JS.split_initializer(contents)) + # Compression of code and data for smaller downloads class Compression: on = False From d19b49ff81788b3414a3b90fe96b5e5887449c3b Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Jan 2014 10:03:13 -0800 Subject: [PATCH 14/85] disable test_fnmatch in s_x_x --- tests/test_core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_core.py b/tests/test_core.py index 5aab48f5a36e4..dcc85343f040d 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -3706,6 +3706,7 @@ def test_strstr(self): self.do_run_from_file(src, output) def test_fnmatch(self): + if self.emcc_args is None: return self.skip('requires linking in libc++') test_path = path_from_root('tests', 'core', 'fnmatch') src, output = (test_path + s for s in ('.c', '.out')) self.do_run_from_file(src, output) From d0342ae398c47077b5dd20f1f6ba20110b27ec9e Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Jan 2014 10:19:47 -0800 Subject: [PATCH 15/85] use double for float arguments to safe heap --- tools/js-optimizer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index fa59dbec632bd..7640f43a6a132 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -3951,7 +3951,7 @@ function safeHeap(ast) { return ['call', ['name', 'SAFE_HEAP_STORE'], [ptr, makeAsmCoercion(value, ASM_INT), ['num', 4], ['num', '0']]]; } case 'HEAPF32': { - return ['call', ['name', 'SAFE_HEAP_STORE'], [ptr, makeAsmCoercion(value, ASM_FLOAT), ['num', 4], ['num', '1']]]; + return ['call', ['name', 'SAFE_HEAP_STORE'], [ptr, makeAsmCoercion(value, ASM_DOUBLE), ['num', 4], ['num', '1']]]; } case 'HEAPF64': { return ['call', ['name', 'SAFE_HEAP_STORE'], [ptr, makeAsmCoercion(value, ASM_DOUBLE), ['num', 8], ['num', '1']]]; @@ -3975,7 +3975,7 @@ function safeHeap(ast) { return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 4], ['num', '0']]], ASM_INT); } case 'HEAPF32': { - return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 4], ['num', '1']]], ASM_FLOAT); + return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 4], ['num', '1']]], ASM_DOUBLE); } case 'HEAPF64': { return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 8], ['num', '1']]], ASM_DOUBLE); From 799c3a3daefbef0797e0c7ef978955af42251863 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Jan 2014 10:29:30 -0800 Subject: [PATCH 16/85] add missing ; --- src/library.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/library.js b/src/library.js index 82aa5362c4772..0799422d9bdec 100644 --- a/src/library.js +++ b/src/library.js @@ -3307,7 +3307,7 @@ LibraryManager.library = { poolPtr = allocate(TOTAL_ENV_SIZE, 'i8', ALLOC_STATIC); envPtr = allocate(MAX_ENV_VALUES * {{{ Runtime.QUANTUM_SIZE }}}, 'i8*', ALLOC_STATIC); - {{{ makeSetValue('envPtr', '0', 'poolPtr', 'i8*') }}} + {{{ makeSetValue('envPtr', '0', 'poolPtr', 'i8*') }}}; {{{ makeSetValue(makeGlobalUse('_environ'), 0, 'envPtr', 'i8*') }}}; } else { envPtr = {{{ makeGetValue(makeGlobalUse('_environ'), '0', 'i8**') }}}; From d7cdf4581f3d98442fc704afeb95dace9e208fd0 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Jan 2014 10:43:51 -0800 Subject: [PATCH 17/85] add missing ;s --- src/library.js | 282 ++++++++++++++++++++++----------------------- src/library_sdl.js | 40 +++---- 2 files changed, 161 insertions(+), 161 deletions(-) diff --git a/src/library.js b/src/library.js index 0799422d9bdec..a7289fb8d45e3 100644 --- a/src/library.js +++ b/src/library.js @@ -100,7 +100,7 @@ LibraryManager.library = { return FS.handleFSError(e); } if (stream.position < 0 || stream.position >= entries.length) { - {{{ makeSetValue('result', '0', '0', 'i8*') }}} + {{{ makeSetValue('result', '0', '0', 'i8*') }}}; return 0; } var id; @@ -118,15 +118,15 @@ LibraryManager.library = { FS.isLink(child.mode) ? 10 : // DT_LNK, symbolic link. 8; // DT_REG, regular file. } - {{{ makeSetValue('entry', C_STRUCTS.dirent.d_ino, 'id', 'i32') }}} - {{{ makeSetValue('entry', C_STRUCTS.dirent.d_off, 'offset', 'i32') }}} - {{{ makeSetValue('entry', C_STRUCTS.dirent.d_reclen, 'name.length + 1', 'i32') }}} + {{{ makeSetValue('entry', C_STRUCTS.dirent.d_ino, 'id', 'i32') }}}; + {{{ makeSetValue('entry', C_STRUCTS.dirent.d_off, 'offset', 'i32') }}}; + {{{ makeSetValue('entry', C_STRUCTS.dirent.d_reclen, 'name.length + 1', 'i32') }}}; for (var i = 0; i < name.length; i++) { - {{{ makeSetValue('entry + ' + C_STRUCTS.dirent.d_name, 'i', 'name.charCodeAt(i)', 'i8') }}} + {{{ makeSetValue('entry + ' + C_STRUCTS.dirent.d_name, 'i', 'name.charCodeAt(i)', 'i8') }}}; } - {{{ makeSetValue('entry + ' + C_STRUCTS.dirent.d_name, 'i', '0', 'i8') }}} - {{{ makeSetValue('entry', C_STRUCTS.dirent.d_type, 'type', 'i8') }}} - {{{ makeSetValue('result', '0', 'entry', 'i8*') }}} + {{{ makeSetValue('entry + ' + C_STRUCTS.dirent.d_name, 'i', '0', 'i8') }}}; + {{{ makeSetValue('entry', C_STRUCTS.dirent.d_type, 'type', 'i8') }}}; + {{{ makeSetValue('result', '0', 'entry', 'i8*') }}}; stream.position++; return 0; }, @@ -205,13 +205,13 @@ LibraryManager.library = { var length = i; if (allSlashes) { // All slashes result in a single slash. - {{{ makeSetValue('path', '1', '0', 'i8') }}} + {{{ makeSetValue('path', '1', '0', 'i8') }}}; return [path, -1]; } else { // Strip trailing slashes. while (slashPositions.length && slashPositions[slashPositions.length - 1] == length - 1) { - {{{ makeSetValue('path', 'slashPositions.pop(i)', '0', 'i8') }}} + {{{ makeSetValue('path', 'slashPositions.pop(i)', '0', 'i8') }}}; length--; } return [path, slashPositions.pop()]; @@ -232,9 +232,9 @@ LibraryManager.library = { // http://pubs.opengroup.org/onlinepubs/007908799/xsh/dirname.html var result = ___libgenSplitName(path); if (result[1] == 0) { - {{{ makeSetValue('result[0]', 1, '0', 'i8') }}} + {{{ makeSetValue('result[0]', 1, '0', 'i8') }}}; } else if (result[1] !== -1) { - {{{ makeSetValue('result[0]', 'result[1]', '0', 'i8') }}} + {{{ makeSetValue('result[0]', 'result[1]', '0', 'i8') }}}; } return result[0]; }, @@ -255,22 +255,22 @@ LibraryManager.library = { {{{ makeSetValue('buf', C_STRUCTS.stat.st_dev, 'stat.dev', 'i32') }}}; {{{ makeSetValue('buf', C_STRUCTS.stat.__st_dev_padding, '0', 'i32') }}}; {{{ makeSetValue('buf', C_STRUCTS.stat.__st_ino_truncated, 'stat.ino', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.stat.st_mode, 'stat.mode', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_nlink, 'stat.nlink', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_uid, 'stat.uid', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_gid, 'stat.gid', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_rdev, 'stat.rdev', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.stat.st_mode, 'stat.mode', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_nlink, 'stat.nlink', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_uid, 'stat.uid', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_gid, 'stat.gid', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_rdev, 'stat.rdev', 'i32') }}}; {{{ makeSetValue('buf', C_STRUCTS.stat.__st_rdev_padding, '0', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.stat.st_size, 'stat.size', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_blksize, '4096', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_blocks, 'stat.blocks', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_atim.tv_sec, 'Math.floor(stat.atime.getTime() / 1000)', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_atim.tv_nsec, '0', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_mtim.tv_sec, 'Math.floor(stat.mtime.getTime() / 1000)', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_mtim.tv_nsec, '0', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_ctim.tv_sec, 'Math.floor(stat.ctime.getTime() / 1000)', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_ctim.tv_nsec, '0', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.stat.st_ino, 'stat.ino', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.stat.st_size, 'stat.size', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_blksize, '4096', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_blocks, 'stat.blocks', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_atim.tv_sec, 'Math.floor(stat.atime.getTime() / 1000)', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_atim.tv_nsec, '0', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_mtim.tv_sec, 'Math.floor(stat.mtime.getTime() / 1000)', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_mtim.tv_nsec, '0', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_ctim.tv_sec, 'Math.floor(stat.ctime.getTime() / 1000)', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_ctim.tv_nsec, '0', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.stat.st_ino, 'stat.ino', 'i32') }}}; return 0; } catch (e) { FS.handleFSError(e); @@ -407,17 +407,17 @@ LibraryManager.library = { // int statvfs(const char *restrict path, struct statvfs *restrict buf); // NOTE: None of the constants here are true. We're just returning safe and // sane values. - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_bsize, '4096', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_frsize, '4096', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_blocks, '1000000', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_bfree, '500000', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_bavail, '500000', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_files, 'FS.nextInode', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_ffree, '1000000', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_favail, '1000000', 'i32') }}} - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_fsid, '42', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_bsize, '4096', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_frsize, '4096', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_blocks, '1000000', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_bfree, '500000', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_bavail, '500000', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_files, 'FS.nextInode', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_ffree, '1000000', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_favail, '1000000', 'i32') }}}; + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_fsid, '42', 'i32') }}}; {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_flag, '2', 'i32') }}} // ST_NOSUID - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_namemax, '255', 'i32') }}} + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_namemax, '255', 'i32') }}}; return 0; }, fstatvfs__deps: ['statvfs'], @@ -506,7 +506,7 @@ LibraryManager.library = { var arg = {{{ makeGetValue('varargs', 0, 'i32') }}}; var offset = {{{ C_STRUCTS.flock.l_type }}}; // We're always unlocked. - {{{ makeSetValue('arg', 'offset', cDefine('F_UNLCK'), 'i16') }}} + {{{ makeSetValue('arg', 'offset', cDefine('F_UNLCK'), 'i16') }}}; return 0; case {{{ cDefine('F_SETLK') }}}: case {{{ cDefine('F_SETLKW') }}}: @@ -585,7 +585,7 @@ LibraryManager.library = { } mask &= events | {{{ cDefine('POLLERR') }}} | {{{ cDefine('POLLHUP') }}}; if (mask) nonzero++; - {{{ makeSetValue('pollfd', C_STRUCTS.pollfd.revents, 'mask', 'i16') }}} + {{{ makeSetValue('pollfd', C_STRUCTS.pollfd.revents, 'mask', 'i16') }}}; } return nonzero; }, @@ -1165,7 +1165,7 @@ LibraryManager.library = { } else { var length = Math.min(len, value.length); for (var i = 0; i < length; i++) { - {{{ makeSetValue('buf', 'i', 'value.charCodeAt(i)', 'i8') }}} + {{{ makeSetValue('buf', 'i', 'value.charCodeAt(i)', 'i8') }}}; } if (len > length) {{{ makeSetValue('buf', 'i++', '0', 'i8') }}} return i; @@ -1214,9 +1214,9 @@ LibraryManager.library = { // int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid); // http://linux.die.net/man/2/getresuid // We have just one process/group/user, all with ID 0. - {{{ makeSetValue('ruid', '0', '0', 'i32') }}} - {{{ makeSetValue('euid', '0', '0', 'i32') }}} - {{{ makeSetValue('suid', '0', '0', 'i32') }}} + {{{ makeSetValue('ruid', '0', '0', 'i32') }}}; + {{{ makeSetValue('euid', '0', '0', 'i32') }}}; + {{{ makeSetValue('suid', '0', '0', 'i32') }}}; return 0; }, getresgid: 'getresuid', @@ -1228,7 +1228,7 @@ LibraryManager.library = { ___setErrNo(ERRNO_CODES.EINVAL); return -1; } else { - {{{ makeSetValue('grouplist', '0', '0', 'i32') }}} + {{{ makeSetValue('grouplist', '0', '0', 'i32') }}}; return 1; } }, @@ -1261,10 +1261,10 @@ LibraryManager.library = { } var length = Math.min(namelen, host.length); for (var i = 0; i < length; i++) { - {{{ makeSetValue('name', 'i', 'host.charCodeAt(i)', 'i8') }}} + {{{ makeSetValue('name', 'i', 'host.charCodeAt(i)', 'i8') }}}; } if (namelen > length) { - {{{ makeSetValue('name', 'i', '0', 'i8') }}} + {{{ makeSetValue('name', 'i', '0', 'i8') }}}; return 0; } else { ___setErrNo(ERRNO_CODES.ENAMETOOLONG); @@ -1381,8 +1381,8 @@ LibraryManager.library = { for (var i = 0; i < nbytes; i += 2) { var first = {{{ makeGetValue('src', 'i', 'i8') }}}; var second = {{{ makeGetValue('src', 'i + 1', 'i8') }}}; - {{{ makeSetValue('dest', 'i', 'second', 'i8') }}} - {{{ makeSetValue('dest', 'i + 1', 'first', 'i8') }}} + {{{ makeSetValue('dest', 'i', 'second', 'i8') }}}; + {{{ makeSetValue('dest', 'i + 1', 'first', 'i8') }}}; } }, tcgetpgrp: function(fildes) { @@ -1790,7 +1790,7 @@ LibraryManager.library = { break; case 'X': case 'x': - {{{ makeSetValue('argPtr', 0, 'parseInt(text, 16)', 'i32') }}} + {{{ makeSetValue('argPtr', 0, 'parseInt(text, 16)', 'i32') }}}; break; case 'F': case 'f': @@ -1801,15 +1801,15 @@ LibraryManager.library = { case 'E': // fallthrough intended if (long_) { - {{{ makeSetValue('argPtr', 0, 'parseFloat(text)', 'double') }}} + {{{ makeSetValue('argPtr', 0, 'parseFloat(text)', 'double') }}}; } else { - {{{ makeSetValue('argPtr', 0, 'parseFloat(text)', 'float') }}} + {{{ makeSetValue('argPtr', 0, 'parseFloat(text)', 'float') }}}; } break; case 's': var array = intArrayFromString(text); for (var j = 0; j < array.length; j++) { - {{{ makeSetValue('argPtr', 'j', 'array[j]', 'i8') }}} + {{{ makeSetValue('argPtr', 'j', 'array[j]', 'i8') }}}; } break; } @@ -2245,7 +2245,7 @@ LibraryManager.library = { case 'n': { // Write the length written so far to the next parameter. var ptr = getNextArg('i32*'); - {{{ makeSetValue('ptr', '0', 'ret.length', 'i32') }}} + {{{ makeSetValue('ptr', '0', 'ret.length', 'i32') }}}; break; } case '%': { @@ -2372,9 +2372,9 @@ LibraryManager.library = { ___setErrNo(ERRNO_CODES.ESPIPE); return -1; } - {{{ makeSetValue('pos', '0', 'stream.position', 'i32') }}} + {{{ makeSetValue('pos', '0', 'stream.position', 'i32') }}}; var state = (stream.eof ? 1 : 0) + (stream.error ? 2 : 0); - {{{ makeSetValue('pos', Runtime.getNativeTypeSize('i32'), 'state', 'i32') }}} + {{{ makeSetValue('pos', Runtime.getNativeTypeSize('i32'), 'state', 'i32') }}}; return 0; }, fgets__deps: ['fgetc'], @@ -2391,9 +2391,9 @@ LibraryManager.library = { if (streamObj.error || (streamObj.eof && i == 0)) return 0; else if (streamObj.eof) break; } - {{{ makeSetValue('s', 'i', 'byte_', 'i8') }}} + {{{ makeSetValue('s', 'i', 'byte_', 'i8') }}}; } - {{{ makeSetValue('s', 'i', '0', 'i8') }}} + {{{ makeSetValue('s', 'i', '0', 'i8') }}}; return s; }, gets__deps: ['fgets'], @@ -2457,7 +2457,7 @@ LibraryManager.library = { // int fputc(int c, FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fputc.html var chr = unSign(c & 0xFF); - {{{ makeSetValue('_fputc.ret', '0', 'chr', 'i8') }}} + {{{ makeSetValue('_fputc.ret', '0', 'chr', 'i8') }}}; var ret = _write(stream, _fputc.ret, 1); if (ret == -1) { var streamObj = FS.getStream(stream); @@ -2511,7 +2511,7 @@ LibraryManager.library = { return 0; } while (streamObj.ungotten.length && bytesToRead > 0) { - {{{ makeSetValue('ptr++', '0', 'streamObj.ungotten.pop()', 'i8') }}} + {{{ makeSetValue('ptr++', '0', 'streamObj.ungotten.pop()', 'i8') }}}; bytesToRead--; bytesRead++; } @@ -3127,7 +3127,7 @@ LibraryManager.library = { // Set end pointer. if (endptr) { - {{{ makeSetValue('endptr', 0, 'str', '*') }}} + {{{ makeSetValue('endptr', 0, 'str', '*') }}}; } // Unsign if needed. @@ -3213,7 +3213,7 @@ LibraryManager.library = { // Set end pointer. if (endptr) { - {{{ makeSetValue('endptr', 0, 'str', '*') }}} + {{{ makeSetValue('endptr', 0, 'str', '*') }}}; } try { @@ -3431,7 +3431,7 @@ LibraryManager.library = { var limit = Math.min(nelem, 3); var doubleSize = {{{ Runtime.getNativeTypeSize('double') }}}; for (var i = 0; i < limit; i++) { - {{{ makeSetValue('loadavg', 'i * doubleSize', '0.1', 'double') }}} + {{{ makeSetValue('loadavg', 'i * doubleSize', '0.1', 'double') }}}; } return limit; }, @@ -3460,9 +3460,9 @@ LibraryManager.library = { } else { var size = Math.min(4095, absolute.path.length); // PATH_MAX - 1. for (var i = 0; i < size; i++) { - {{{ makeSetValue('resolved_name', 'i', 'absolute.path.charCodeAt(i)', 'i8') }}} + {{{ makeSetValue('resolved_name', 'i', 'absolute.path.charCodeAt(i)', 'i8') }}}; } - {{{ makeSetValue('resolved_name', 'size', '0', 'i8') }}} + {{{ makeSetValue('resolved_name', 'size', '0', 'i8') }}}; return resolved_name; } }, @@ -3670,7 +3670,7 @@ LibraryManager.library = { var padding = 0, curr = 0, i = 0; while ((i|0) < (num|0)) { curr = padding ? 0 : {{{ makeGetValueAsm('psrc', 'i', 'i8') }}}; - {{{ makeSetValue('pdest', 'i', 'curr', 'i8') }}} + {{{ makeSetValue('pdest', 'i', 'curr', 'i8') }}}; padding = padding ? 1 : ({{{ makeGetValueAsm('psrc', 'i', 'i8') }}} == 0); i = (i+1)|0; } @@ -3723,7 +3723,7 @@ LibraryManager.library = { if ({{{ makeGetValue('pdest', 'len+i', 'i8') }}} == 0) break; i ++; if (i == num) { - {{{ makeSetValue('pdest', 'len+i', 0, 'i8') }}} + {{{ makeSetValue('pdest', 'len+i', 0, 'i8') }}}; break; } } @@ -4091,7 +4091,7 @@ LibraryManager.library = { var i16size = {{{ Runtime.getNativeTypeSize('i16') }}}; var arr = _malloc(values.length * i16size); for (var i = 0; i < values.length; i++) { - {{{ makeSetValue('arr', 'i * i16size', 'values[i]', 'i16') }}} + {{{ makeSetValue('arr', 'i * i16size', 'values[i]', 'i16') }}}; } me.ret = allocate([arr + 128 * i16size], 'i16*', ALLOC_NORMAL); } @@ -4119,7 +4119,7 @@ LibraryManager.library = { var i32size = {{{ Runtime.getNativeTypeSize('i32') }}}; var arr = _malloc(values.length * i32size); for (var i = 0; i < values.length; i++) { - {{{ makeSetValue('arr', 'i * i32size', 'values[i]', 'i32') }}} + {{{ makeSetValue('arr', 'i * i32size', 'values[i]', 'i32') }}}; } me.ret = allocate([arr + 128 * i32size], 'i32*', ALLOC_NORMAL); } @@ -4146,7 +4146,7 @@ LibraryManager.library = { var i32size = {{{ Runtime.getNativeTypeSize('i32') }}}; var arr = _malloc(values.length * i32size); for (var i = 0; i < values.length; i++) { - {{{ makeSetValue('arr', 'i * i32size', 'values[i]', 'i32') }}} + {{{ makeSetValue('arr', 'i * i32size', 'values[i]', 'i32') }}}; } me.ret = allocate([arr + 128 * i32size], 'i32*', ALLOC_NORMAL); } @@ -4348,9 +4348,9 @@ LibraryManager.library = { #if EXCEPTION_DEBUG Module.printErr('Compiled code throwing an exception, ' + [ptr,type,destructor] + ', at ' + stackTrace()); #endif - {{{ makeSetValue('_llvm_eh_exception.buf', '0', 'ptr', 'void*') }}} - {{{ makeSetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, 'type', 'void*') }}} - {{{ makeSetValue('_llvm_eh_exception.buf', 2 * QUANTUM_SIZE, 'destructor', 'void*') }}} + {{{ makeSetValue('_llvm_eh_exception.buf', '0', 'ptr', 'void*') }}}; + {{{ makeSetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, 'type', 'void*') }}}; + {{{ makeSetValue('_llvm_eh_exception.buf', 2 * QUANTUM_SIZE, 'destructor', 'void*') }}}; if (!("uncaught_exception" in __ZSt18uncaught_exceptionv)) { __ZSt18uncaught_exceptionv.uncaught_exception = 1; } else { @@ -4396,18 +4396,18 @@ LibraryManager.library = { __THREW__ = 0; #endif // Clear type. - {{{ makeSetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, '0', 'void*') }}} + {{{ makeSetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, '0', 'void*') }}}; // Call destructor if one is registered then clear it. var ptr = {{{ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') }}}; var destructor = {{{ makeGetValue('_llvm_eh_exception.buf', 2 * QUANTUM_SIZE, 'void*') }}}; if (destructor) { Runtime.dynCall('vi', destructor, [ptr]); - {{{ makeSetValue('_llvm_eh_exception.buf', 2 * QUANTUM_SIZE, '0', 'i32') }}} + {{{ makeSetValue('_llvm_eh_exception.buf', 2 * QUANTUM_SIZE, '0', 'i32') }}}; } // Free ptr if it isn't null. if (ptr) { ___cxa_free_exception(ptr); - {{{ makeSetValue('_llvm_eh_exception.buf', '0', '0', 'void*') }}} + {{{ makeSetValue('_llvm_eh_exception.buf', '0', '0', 'void*') }}}; } }, __cxa_get_exception_ptr__deps: ['llvm_eh_exception'], @@ -4818,11 +4818,11 @@ LibraryManager.library = { cbrtl: 'cbrt', modf: function(x, intpart) { - {{{ makeSetValue('intpart', 0, 'Math.floor(x)', 'double') }}} + {{{ makeSetValue('intpart', 0, 'Math.floor(x)', 'double') }}}; return x - {{{ makeGetValue('intpart', 0, 'double') }}}; }, modff: function(x, intpart) { - {{{ makeSetValue('intpart', 0, 'Math.floor(x)', 'float') }}} + {{{ makeSetValue('intpart', 0, 'Math.floor(x)', 'float') }}}; return x - {{{ makeGetValue('intpart', 0, 'float') }}}; }, frexp: function(x, exp_addr) { @@ -4838,7 +4838,7 @@ LibraryManager.library = { if (exp_ === raw_exp) exp_ += 1; sig = sign*x/Math.pow(2, exp_); } - {{{ makeSetValue('exp_addr', 0, 'exp_', 'i32') }}} + {{{ makeSetValue('exp_addr', 0, 'exp_', 'i32') }}}; return sig; }, frexpf: 'frexp', @@ -5346,7 +5346,7 @@ LibraryManager.library = { time: function(ptr) { var ret = Math.floor(Date.now()/1000); if (ptr) { - {{{ makeSetValue('ptr', 0, 'ret', 'i32') }}} + {{{ makeSetValue('ptr', 0, 'ret', 'i32') }}}; } return ret; }, @@ -5373,9 +5373,9 @@ LibraryManager.library = { {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_min, 'i32') }}}, {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'i32') }}}, 0).getTime() / 1000; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'new Date(timestamp).getDay()', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'new Date(timestamp).getDay()', 'i32') }}}; var yday = Math.round((timestamp - (new Date(year, 0, 1)).getTime()) / (1000 * 60 * 60 * 24)); - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}}; return timestamp; }, timelocal: 'mktime', @@ -5388,15 +5388,15 @@ LibraryManager.library = { gmtime_r__deps: ['__tm_timezone'], gmtime_r: function(time, tmPtr) { var date = new Date({{{ makeGetValue('time', 0, 'i32') }}}*1000); - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'date.getUTCSeconds()', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_min, 'date.getUTCMinutes()', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'date.getUTCHours()', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'date.getUTCDate()', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'date.getUTCMonth()', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_year, 'date.getUTCFullYear()-1900', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getUTCDay()', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_gmtoff, '0', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_isdst, '0', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'date.getUTCSeconds()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_min, 'date.getUTCMinutes()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'date.getUTCHours()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'date.getUTCDate()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'date.getUTCMonth()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_year, 'date.getUTCFullYear()-1900', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getUTCDay()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_gmtoff, '0', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_isdst, '0', 'i32') }}}; var start = new Date(date); // define date using UTC, start from Jan 01 00:00:00 UTC start.setUTCDate(1); start.setUTCMonth(0); @@ -5405,8 +5405,8 @@ LibraryManager.library = { start.setUTCSeconds(0); start.setUTCMilliseconds(0); var yday = Math.floor((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)); - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_zone, '___tm_timezone', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_zone, '___tm_timezone', 'i32') }}}; return tmPtr; }, @@ -5429,23 +5429,23 @@ LibraryManager.library = { localtime_r: function(time, tmPtr) { _tzset(); var date = new Date({{{ makeGetValue('time', 0, 'i32') }}}*1000); - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'date.getSeconds()', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_min, 'date.getMinutes()', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'date.getHours()', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'date.getDate()', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'date.getMonth()', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_year, 'date.getFullYear()-1900', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getDay()', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'date.getSeconds()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_min, 'date.getMinutes()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'date.getHours()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'date.getDate()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'date.getMonth()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_year, 'date.getFullYear()-1900', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getDay()', 'i32') }}}; var start = new Date(date.getFullYear(), 0, 1); var yday = Math.floor((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)); - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}} - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_gmtoff, 'start.getTimezoneOffset() * 60', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_gmtoff, 'start.getTimezoneOffset() * 60', 'i32') }}}; var dst = Number(start.getTimezoneOffset() != date.getTimezoneOffset()); - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_isdst, 'dst', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_isdst, 'dst', 'i32') }}}; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_zone, '___tm_timezone', 'i32') }}} + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_zone, '___tm_timezone', 'i32') }}}; return tmPtr; }, @@ -5463,9 +5463,9 @@ LibraryManager.library = { var timePart = formatted.match(/\d{2}:\d{2}:\d{2}/)[0]; formatted = datePart + timePart + ' ' + date.getFullYear() + '\n'; formatted.split('').forEach(function(chr, index) { - {{{ makeSetValue('buf', 'index', 'chr.charCodeAt(0)', 'i8') }}} + {{{ makeSetValue('buf', 'index', 'chr.charCodeAt(0)', 'i8') }}}; }); - {{{ makeSetValue('buf', '25', '0', 'i8') }}} + {{{ makeSetValue('buf', '25', '0', 'i8') }}}; return buf; }, @@ -5495,18 +5495,18 @@ LibraryManager.library = { if (_tzset.called) return; _tzset.called = true; - {{{ makeSetValue(makeGlobalUse('_timezone'), '0', '-(new Date()).getTimezoneOffset() * 60', 'i32') }}} + {{{ makeSetValue(makeGlobalUse('_timezone'), '0', '-(new Date()).getTimezoneOffset() * 60', 'i32') }}}; var winter = new Date(2000, 0, 1); var summer = new Date(2000, 6, 1); - {{{ makeSetValue(makeGlobalUse('_daylight'), '0', 'Number(winter.getTimezoneOffset() != summer.getTimezoneOffset())', 'i32') }}} + {{{ makeSetValue(makeGlobalUse('_daylight'), '0', 'Number(winter.getTimezoneOffset() != summer.getTimezoneOffset())', 'i32') }}}; var winterName = 'GMT'; // XXX do not rely on browser timezone info, it is very unpredictable | winter.toString().match(/\(([A-Z]+)\)/)[1]; var summerName = 'GMT'; // XXX do not rely on browser timezone info, it is very unpredictable | summer.toString().match(/\(([A-Z]+)\)/)[1]; var winterNamePtr = allocate(intArrayFromString(winterName), 'i8', ALLOC_NORMAL); var summerNamePtr = allocate(intArrayFromString(summerName), 'i8', ALLOC_NORMAL); - {{{ makeSetValue(makeGlobalUse('_tzname'), '0', 'winterNamePtr', 'i32') }}} - {{{ makeSetValue(makeGlobalUse('_tzname'), Runtime.QUANTUM_SIZE, 'summerNamePtr', 'i32') }}} + {{{ makeSetValue(makeGlobalUse('_tzname'), '0', 'winterNamePtr', 'i32') }}}; + {{{ makeSetValue(makeGlobalUse('_tzname'), Runtime.QUANTUM_SIZE, 'summerNamePtr', 'i32') }}}; }, stime__deps: ['$ERRNO_CODES', '__setErrNo'], @@ -6076,15 +6076,15 @@ LibraryManager.library = { */ var fullDate = new Date(date.year, date.month, date.day, date.hour, date.min, date.sec, 0); - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_sec, 'fullDate.getSeconds()', 'i32') }}} - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_min, 'fullDate.getMinutes()', 'i32') }}} - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_hour, 'fullDate.getHours()', 'i32') }}} - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_mday, 'fullDate.getDate()', 'i32') }}} - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_mon, 'fullDate.getMonth()', 'i32') }}} - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_year, 'fullDate.getFullYear()-1900', 'i32') }}} - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_wday, 'fullDate.getDay()', 'i32') }}} - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_yday, '__arraySum(__isLeapYear(fullDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, fullDate.getMonth()-1)+fullDate.getDate()-1', 'i32') }}} - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_isdst, '0', 'i32') }}} + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_sec, 'fullDate.getSeconds()', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_min, 'fullDate.getMinutes()', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_hour, 'fullDate.getHours()', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_mday, 'fullDate.getDate()', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_mon, 'fullDate.getMonth()', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_year, 'fullDate.getFullYear()-1900', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_wday, 'fullDate.getDay()', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_yday, '__arraySum(__isLeapYear(fullDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, fullDate.getMonth()-1)+fullDate.getDate()-1', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_isdst, '0', 'i32') }}}; // we need to convert the matched sequence into an integer array to take care of UTF-8 characters > 0x7F // TODO: not sure that intArrayFromString handles all unicode characters correctly @@ -6115,8 +6115,8 @@ LibraryManager.library = { var seconds = {{{ makeGetValue('rqtp', C_STRUCTS.timespec.tv_sec, 'i32') }}}; var nanoseconds = {{{ makeGetValue('rqtp', C_STRUCTS.timespec.tv_nsec, 'i32') }}}; if (rmtp !== 0) { - {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_sec, '0', 'i32') }}} - {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_nsec, '0', 'i32') }}} + {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_sec, '0', 'i32') }}}; + {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_nsec, '0', 'i32') }}}; } return _usleep((seconds * 1e6) + (nanoseconds / 1000)); }, @@ -6147,7 +6147,7 @@ LibraryManager.library = { } else { nsec = _emscripten_get_now_res(); } - {{{ makeSetValue('res', C_STRUCTS.timespec.tv_sec, '1', 'i32') }}} + {{{ makeSetValue('res', C_STRUCTS.timespec.tv_sec, '1', 'i32') }}}; {{{ makeSetValue('res', C_STRUCTS.timespec.tv_nsec, 'nsec', 'i32') }}} // resolution is milliseconds return 0; }, @@ -6828,7 +6828,7 @@ LibraryManager.library = { __setErrNo__postset: '___errno_state = Runtime.staticAlloc(4); {{{ makeSetValue("___errno_state", 0, 0, "i32") }}};', __setErrNo: function(value) { // For convenient setting and returning of errno. - {{{ makeSetValue('___errno_state', '0', 'value', 'i32') }}} + {{{ makeSetValue('___errno_state', '0', 'value', 'i32') }}}; return value; }, __errno_location__deps: ['__setErrNo'], @@ -6856,10 +6856,10 @@ LibraryManager.library = { // TODO: Implement for real. We just do time used, and no useful data getrusage: function(resource, rlp) { // int getrusage(int resource, struct rusage *rlp); - {{{ makeSetValue('rlp', C_STRUCTS.rusage.ru_utime.tv_sec, '1', 'i32') }}} - {{{ makeSetValue('rlp', C_STRUCTS.rusage.ru_utime.tv_usec, '2', 'i32') }}} - {{{ makeSetValue('rlp', C_STRUCTS.rusage.ru_stime.tv_sec, '3', 'i32') }}} - {{{ makeSetValue('rlp', C_STRUCTS.rusage.ru_stime.tv_usec, '4', 'i32') }}} + {{{ makeSetValue('rlp', C_STRUCTS.rusage.ru_utime.tv_sec, '1', 'i32') }}}; + {{{ makeSetValue('rlp', C_STRUCTS.rusage.ru_utime.tv_usec, '2', 'i32') }}}; + {{{ makeSetValue('rlp', C_STRUCTS.rusage.ru_stime.tv_sec, '3', 'i32') }}}; + {{{ makeSetValue('rlp', C_STRUCTS.rusage.ru_stime.tv_usec, '4', 'i32') }}}; return 0; }, @@ -6923,8 +6923,8 @@ LibraryManager.library = { void **restrict stackaddr, size_t *restrict stacksize); */ /*FIXME: assumes that there is only one thread, and that attr is the current thread*/ - {{{ makeSetValue('stackaddr', '0', 'STACK_BASE', 'i8*') }}} - {{{ makeSetValue('stacksize', '0', 'TOTAL_STACK', 'i32') }}} + {{{ makeSetValue('stackaddr', '0', 'STACK_BASE', 'i8*') }}}; + {{{ makeSetValue('stacksize', '0', 'TOTAL_STACK', 'i32') }}}; return 0; }, @@ -6942,7 +6942,7 @@ LibraryManager.library = { if (key == 0) { return ERRNO_CODES.EINVAL; } - {{{ makeSetValue('key', '0', 'PTHREAD_SPECIFIC_NEXT_KEY', 'i32*') }}} + {{{ makeSetValue('key', '0', 'PTHREAD_SPECIFIC_NEXT_KEY', 'i32*') }}}; // values start at 0 PTHREAD_SPECIFIC[PTHREAD_SPECIFIC_NEXT_KEY] = 0; PTHREAD_SPECIFIC_NEXT_KEY++; @@ -7000,7 +7000,7 @@ LibraryManager.library = { posix_memalign__deps: ['memalign'], posix_memalign: function(memptr, alignment, size) { var ptr = _memalign(alignment, size); - {{{ makeSetValue('memptr', '0', 'ptr', 'i8*') }}} + {{{ makeSetValue('memptr', '0', 'ptr', 'i8*') }}}; return 0; }, @@ -7042,7 +7042,7 @@ LibraryManager.library = { if (addr === null) { return 0; } - {{{ makeSetValue('inp', '0', 'addr', 'i32') }}} + {{{ makeSetValue('inp', '0', 'addr', 'i32') }}}; return 1; }, @@ -7201,7 +7201,7 @@ LibraryManager.library = { if (ret === null) { return 0; } - {{{ makeSetValue('dst', '0', 'ret', 'i32') }}} + {{{ makeSetValue('dst', '0', 'ret', 'i32') }}}; return 1; }, _inet_pton6_raw__deps: ['htons'], @@ -7406,18 +7406,18 @@ LibraryManager.library = { var ret = _malloc({{{ C_STRUCTS.hostent.__size__ }}}); // XXX possibly leaked, as are others here var nameBuf = _malloc(name.length+1); writeStringToMemory(name, nameBuf); - {{{ makeSetValue('ret', C_STRUCTS.hostent.h_name, 'nameBuf', 'i8*') }}} + {{{ makeSetValue('ret', C_STRUCTS.hostent.h_name, 'nameBuf', 'i8*') }}}; var aliasesBuf = _malloc(4); - {{{ makeSetValue('aliasesBuf', '0', '0', 'i8*') }}} - {{{ makeSetValue('ret', C_STRUCTS.hostent.h_aliases, 'aliasesBuf', 'i8**') }}} + {{{ makeSetValue('aliasesBuf', '0', '0', 'i8*') }}}; + {{{ makeSetValue('ret', C_STRUCTS.hostent.h_aliases, 'aliasesBuf', 'i8**') }}}; var afinet = {{{ cDefine('AF_INET') }}}; - {{{ makeSetValue('ret', C_STRUCTS.hostent.h_addrtype, 'afinet', 'i32') }}} - {{{ makeSetValue('ret', C_STRUCTS.hostent.h_length, '4', 'i32') }}} + {{{ makeSetValue('ret', C_STRUCTS.hostent.h_addrtype, 'afinet', 'i32') }}}; + {{{ makeSetValue('ret', C_STRUCTS.hostent.h_length, '4', 'i32') }}}; var addrListBuf = _malloc(12); - {{{ makeSetValue('addrListBuf', '0', 'addrListBuf+8', 'i32*') }}} - {{{ makeSetValue('addrListBuf', '4', '0', 'i32*') }}} - {{{ makeSetValue('addrListBuf', '8', '__inet_pton4_raw(DNS.lookup_name(name))', 'i32') }}} - {{{ makeSetValue('ret', C_STRUCTS.hostent.h_addr_list, 'addrListBuf', 'i8**') }}} + {{{ makeSetValue('addrListBuf', '0', 'addrListBuf+8', 'i32*') }}}; + {{{ makeSetValue('addrListBuf', '4', '0', 'i32*') }}}; + {{{ makeSetValue('addrListBuf', '8', '__inet_pton4_raw(DNS.lookup_name(name))', 'i32') }}}; + {{{ makeSetValue('ret', C_STRUCTS.hostent.h_addr_list, 'addrListBuf', 'i8**') }}}; return ret; }, diff --git a/src/library_sdl.js b/src/library_sdl.js index fc38dd1ca48ca..5a1112891d2bb 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -232,17 +232,17 @@ var LibrarySDL = { {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pixels, 'buffer', 'void*') }}} // SDL_Surface.pixels {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.clip_rect, '0', 'i32*') }}} // SDL_Surface.offset - {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.refcount, '1', 'i32') }}} + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.refcount, '1', 'i32') }}}; {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.format, cDefine('SDL_PIXELFORMAT_RGBA8888'), 'i32') }}} // SDL_PIXELFORMAT_RGBA8888 {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.palette, '0', 'i32') }}} // TODO - {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.BitsPerPixel, 'bpp * 8', 'i8') }}} - {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.BytesPerPixel, 'bpp', 'i8') }}} + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.BitsPerPixel, 'bpp * 8', 'i8') }}}; + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.BytesPerPixel, 'bpp', 'i8') }}}; - {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.Rmask, 'rmask || 0x000000ff', 'i32') }}} - {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.Gmask, 'gmask || 0x0000ff00', 'i32') }}} - {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.Bmask, 'bmask || 0x00ff0000', 'i32') }}} - {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.Amask, 'amask || 0xff000000', 'i32') }}} + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.Rmask, 'rmask || 0x000000ff', 'i32') }}}; + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.Gmask, 'gmask || 0x0000ff00', 'i32') }}}; + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.Bmask, 'bmask || 0x00ff0000', 'i32') }}}; + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.Amask, 'amask || 0xff000000', 'i32') }}}; // Decide if we want to use WebGL or not var useWebGL = (flags & 0x04000000) != 0; // SDL_OPENGL @@ -592,19 +592,19 @@ var LibrarySDL = { scan = SDL.scanCodes[key] || key; } - {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}} - {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.state, 'down ? 1 : 0', 'i8') }}} + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.state, 'down ? 1 : 0', 'i8') }}}; {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.repeat, '0', 'i8') }}} // TODO - {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.scancode, 'scan', 'i32') }}} - {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.sym, 'key', 'i32') }}} - {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.mod, 'SDL.modState', 'i16') }}} + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.scancode, 'scan', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.sym, 'key', 'i32') }}}; + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.mod, 'SDL.modState', 'i16') }}}; // some non-character keys (e.g. backspace and tab) won't have keypressCharCode set, fill in with the keyCode. - {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.unicode, 'event.keypressCharCode || key', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.unicode, 'event.keypressCharCode || key', 'i32') }}}; break; } case 'keypress': { - {{{ makeSetValue('ptr', C_STRUCTS.SDL_TextInputEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.SDL_TextInputEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; // Not filling in windowID for now var cStr = intArrayFromString(String.fromCharCode(event.charCode)); for (var i = 0; i < cStr.length; ++i) { @@ -819,9 +819,9 @@ var LibrarySDL = { SDL_Linked_Version: function() { if (SDL.version === null) { SDL.version = _malloc({{{ C_STRUCTS.SDL_version.__size__ }}}); - {{{ makeSetValue('SDL.version + ' + C_STRUCTS.SDL_version.major, '0', '1', 'i8') }}} - {{{ makeSetValue('SDL.version + ' + C_STRUCTS.SDL_version.minor, '0', '3', 'i8') }}} - {{{ makeSetValue('SDL.version + ' + C_STRUCTS.SDL_version.patch, '0', '0', 'i8') }}} + {{{ makeSetValue('SDL.version + ' + C_STRUCTS.SDL_version.major, '0', '1', 'i8') }}}; + {{{ makeSetValue('SDL.version + ' + C_STRUCTS.SDL_version.minor, '0', '3', 'i8') }}}; + {{{ makeSetValue('SDL.version + ' + C_STRUCTS.SDL_version.patch, '0', '0', 'i8') }}}; } return SDL.version; }, @@ -881,9 +881,9 @@ var LibrarySDL = { var ret = _malloc(5*Runtime.QUANTUM_SIZE); {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*0', '0', '0', 'i32') }}} // TODO {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*1', '0', '0', 'i32') }}} // TODO - {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*2', '0', '0', 'void*') }}} - {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*3', '0', 'Module["canvas"].width', 'i32') }}} - {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*4', '0', 'Module["canvas"].height', 'i32') }}} + {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*2', '0', '0', 'void*') }}}; + {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*3', '0', 'Module["canvas"].width', 'i32') }}}; + {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*4', '0', 'Module["canvas"].height', 'i32') }}}; return ret; }, From c0ea7656d75302c75657188860ed16247ca56708 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Jan 2014 14:31:18 -0800 Subject: [PATCH 18/85] clean up emcc timing logging --- emcc | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/emcc b/emcc index 51ad3cf26f7dd..5271ca57709aa 100755 --- a/emcc +++ b/emcc @@ -781,6 +781,14 @@ def filename_type_ending(filename): suffix = filename_type_suffix(filename) return '' if not suffix else ('.' + suffix) +# Log out times for emcc stages +log_time_last = time.time() +def log_time(name): + global log_time_last + now = time.time() + logging.debug('emcc step "%s" took %.2f seconds' % (name, now - log_time_last)) + log_time_last = now + try: call = CXX if use_cxx else CC @@ -1297,6 +1305,8 @@ try: temp_files = [] + log_time('parse arguments and setup') + # First, generate LLVM bitcode. For each input file, we get base.o with bitcode for input_file in input_files: file_ending = filename_type_ending(input_file) @@ -1336,6 +1346,8 @@ try: logging.error(input_file + ': Unknown file suffix when compiling to LLVM bitcode!') sys.exit(1) + log_time('bitcodeize inputs') + if not LEAVE_INPUTS_RAW: assert len(temp_files) == len(input_files) @@ -1382,6 +1394,8 @@ try: shared.Building.link(temp_files, specified_target) exit(0) + log_time('bitcodeize inputs') + ## Continue on to create JavaScript logging.debug('will generate JavaScript') @@ -1755,15 +1769,14 @@ try: libfile = shared.Cache.get(name, create) extra_files_to_link.append(libfile) + log_time('calculate system libraries') + # First, combine the bitcode files if there are several. We must also link if we have a singleton .a if len(input_files) + len(extra_files_to_link) > 1 or \ (not LEAVE_INPUTS_RAW and not (suffix(temp_files[0]) in BITCODE_ENDINGS or suffix(temp_files[0]) in DYNAMICLIB_ENDINGS) and shared.Building.is_ar(temp_files[0])): linker_inputs = temp_files + extra_files_to_link logging.debug('linking: ' + str(linker_inputs)) - t0 = time.time() shared.Building.link(linker_inputs, in_temp(target_basename + '.bc'), force_archive_contents = len(filter(lambda temp: not temp.endswith(STATICLIB_ENDINGS), temp_files)) == 0) - t1 = time.time() - logging.debug(' linking took %.2f seconds' % (t1 - t0)) final = in_temp(target_basename + '.bc') else: if not LEAVE_INPUTS_RAW: @@ -1773,19 +1786,16 @@ try: final = in_temp(input_files[0]) shutil.copyfile(input_files[0], final) + log_time('link') + if DEBUG: logging.debug('saving intermediate processing steps to %s' % shared.EMSCRIPTEN_TEMP_DIR) intermediate_counter = 0 - intermediate_time = None def save_intermediate(name=None, suffix='js'): - global intermediate_counter, intermediate_time + global intermediate_counter shutil.copyfile(final, os.path.join(shared.EMSCRIPTEN_TEMP_DIR, 'emcc-%d%s.%s' % (intermediate_counter, '' if name is None else '-' + name, suffix))) intermediate_counter += 1 - now = time.time() - if intermediate_time: - logging.debug(' step took %.2f seconds' % (now - intermediate_time)) - intermediate_time = now if not LEAVE_INPUTS_RAW: save_intermediate('basebc', 'bc') @@ -1846,6 +1856,8 @@ try: final += '.adsimp.bc' if DEBUG: save_intermediate('adsimp', 'bc') + log_time('post-link') + # Emscripten logging.debug('LLVM => JS') extra_args = [] if not js_libraries else ['--libraries', ','.join(map(os.path.abspath, js_libraries))] @@ -1853,6 +1865,8 @@ try: final = shared.Building.emscripten(final, append_ext=False, extra_args=extra_args) if DEBUG: save_intermediate('original') + log_time('emscript (llvm=>js)') + # Embed and preload files if len(preload_files) + len(embed_files) > 0: logging.debug('setting up files') @@ -1942,6 +1956,8 @@ try: final += '.mem.js' src = None + log_time('source transforms') + # It is useful to run several js optimizer passes together, to save on unneeded unparsing/reparsing js_optimizer_queue = [] js_optimizer_extra_info = {} @@ -2022,6 +2038,8 @@ try: flush_js_optimizer_queue() + log_time('js opts') + # Remove some trivial whitespace # TODO: do not run when compress has already been done on all parts of the code src = open(final).read() src = re.sub(r'\n+[ \n]*\n+', '\n', src) @@ -2128,6 +2146,8 @@ try: # copy final JS to output shutil.move(final, target) + log_time('final emitting') + if DEBUG: logging.debug('total time: %.2f seconds' % (time.time() - start_time)) finally: From 27a17f57f868a057ad7846cf3dceea6bc8d0f77f Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Jan 2014 15:10:12 -0800 Subject: [PATCH 19/85] don't remove whitespace in emcc, not worth the time --- emcc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/emcc b/emcc index 5271ca57709aa..7333b83279e99 100755 --- a/emcc +++ b/emcc @@ -2041,9 +2041,9 @@ try: log_time('js opts') # Remove some trivial whitespace # TODO: do not run when compress has already been done on all parts of the code - src = open(final).read() - src = re.sub(r'\n+[ \n]*\n+', '\n', src) - open(final, 'w').write(src) + #src = open(final).read() + #src = re.sub(r'\n+[ \n]*\n+', '\n', src) + #open(final, 'w').write(src) def generate_source_map(map_file_base_name, offset=0): jsrun.run_js(shared.path_from_root('tools', 'source-maps', 'sourcemapper.js'), From af52dc5060efa8121130be7976ac919deceab358 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Jan 2014 15:35:51 -0800 Subject: [PATCH 20/85] remove stale comment --- emscripten.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emscripten.py b/emscripten.py index aeace63d22573..725f573f9413b 100755 --- a/emscripten.py +++ b/emscripten.py @@ -725,7 +725,7 @@ def emscript_fast(infile, settings, outfile, libraries=[], compiler_engine=None, outfile: The file where the output is written. """ - assert(settings['ASM_JS']) # TODO: apply ASM_JS even in -O0 for fastcomp + assert(settings['ASM_JS']) # Overview: # * Run LLVM backend to emit JS. JS includes function bodies, memory initializer, From dd7771e94e0e80c4232c39140a0263a18e0bfa86 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Jan 2014 16:12:09 -0800 Subject: [PATCH 21/85] handle unsigned reads in safe heap --- src/parseTools.js | 2 +- src/preamble.js | 9 ++++++--- tools/js-optimizer.js | 25 +++++++++++++++++-------- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/parseTools.js b/src/parseTools.js index a564f2e35132d..1c70a018de9f6 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -1328,7 +1328,7 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa if (printType !== 'null' && printType[0] !== '#') printType = '"' + safeQuote(printType) + '"'; if (printType[0] === '#') printType = printType.substr(1); if (ASM_JS) { - if (!ignore) return asmCoercion('SAFE_HEAP_LOAD(' + asmCoercion(offset, 'i32') + ', ' + Runtime.getNativeTypeSize(type) + ', ' + ((type in Runtime.FLOAT_TYPES)|0) + ')', type); + if (!ignore) return asmCoercion('SAFE_HEAP_LOAD(' + asmCoercion(offset, 'i32') + ', ' + Runtime.getNativeTypeSize(type) + ', ' + ((type in Runtime.FLOAT_TYPES)|0) + ', ' + (!!unsigned+0) + ')', type); // else fall through } else { return asmCoercion('SAFE_HEAP_LOAD(' + offset + ', ' + (ASM_JS ? 0 : printType) + ', ' + (!!unsigned+0) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')', type); diff --git a/src/preamble.js b/src/preamble.js index d70ef4b14309c..d415b87e16461 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -189,13 +189,16 @@ function SAFE_HEAP_STORE(dest, value, bytes, isFloat) { setValue(dest, value, getSafeHeapType(bytes, isFloat), 1); } -function SAFE_HEAP_LOAD(dest, bytes, isFloat) { +function SAFE_HEAP_LOAD(dest, bytes, isFloat, unsigned) { #if SAFE_HEAP_LOG - Module.print('SAFE_HEAP load: ' + [dest, bytes, isFloat]); + Module.print('SAFE_HEAP load: ' + [dest, bytes, isFloat, unsigned]); #endif assert(dest > 0, 'segmentation fault'); assert(dest % bytes === 0); - return getValue(dest, getSafeHeapType(bytes, isFloat), 1); + var type = getSafeHeapType(bytes, isFloat); + var ret = getValue(dest, type, 1); + if (unsigned) ret = unSign(ret, parseInt(type.substr(1)), 1); + return ret; } #endif diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 7640f43a6a132..6d65f3e7011e9 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -3965,20 +3965,29 @@ function safeHeap(ast) { var ptr = fixPtr(node[2], heap); // SAFE_HEAP_LOAD(ptr, bytes, isFloat) switch (heap) { - case 'HEAP8': case 'HEAPU8': { - return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 1], ['num', '0']]], ASM_INT); + case 'HEAP8': { + return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 1], ['num', '0'], ['num', '0']]], ASM_INT); } - case 'HEAP16': case 'HEAPU16': { - return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 2], ['num', '0']]], ASM_INT); + case 'HEAPU8': { + return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 1], ['num', '0'], ['num', '1']]], ASM_INT); } - case 'HEAP32': case 'HEAPU32': { - return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 4], ['num', '0']]], ASM_INT); + case 'HEAP16': { + return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 2], ['num', '0'], ['num', '0']]], ASM_INT); + } + case 'HEAPU16': { + return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 2], ['num', '0'], ['num', '1']]], ASM_INT); + } + case 'HEAP32': { + return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 4], ['num', '0'], ['num', '0']]], ASM_INT); + } + case 'HEAPU32': { + return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 4], ['num', '0'], ['num', '1']]], ASM_INT); } case 'HEAPF32': { - return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 4], ['num', '1']]], ASM_DOUBLE); + return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 4], ['num', '1'], ['num', '0']]], ASM_DOUBLE); } case 'HEAPF64': { - return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 8], ['num', '1']]], ASM_DOUBLE); + return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 8], ['num', '1'], ['num', '0']]], ASM_DOUBLE); } default: throw 'bad heap ' + heap; } From f5cd0acf0df47b2613e461b155412e7e4a6fbac5 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Jan 2014 16:17:46 -0800 Subject: [PATCH 22/85] add missing ; --- src/library.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/library.js b/src/library.js index a7289fb8d45e3..d2adbf24c1fab 100644 --- a/src/library.js +++ b/src/library.js @@ -416,7 +416,7 @@ LibraryManager.library = { {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_ffree, '1000000', 'i32') }}}; {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_favail, '1000000', 'i32') }}}; {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_fsid, '42', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_flag, '2', 'i32') }}} // ST_NOSUID + {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_flag, '2', 'i32') }}}; // ST_NOSUID {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_namemax, '255', 'i32') }}}; return 0; }, From a0e815f0a7b99018bef7be0d3ed34fdde2be744b Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Jan 2014 16:30:13 -0800 Subject: [PATCH 23/85] make debug checks in asm2g more lenient, so they pass on fastcomp --- tests/test_core.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_core.py b/tests/test_core.py index dcc85343f040d..09a5d56d2a9a8 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5009,7 +5009,9 @@ def do_test(): def clean(text): text = text.replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('{\n}', '{}') return '\n'.join(sorted(text.split('\n'))) - self.assertIdentical(clean(open('release.js').read()), clean(open('debug%d.js' % debug).read())) # EMCC_DEBUG=1 mode must not generate different code! + sizes = len(open('release.js').read()), len(open('debug%d.js' % debug).read()) + print >> sys.stderr, debug, 'sizes', sizes + assert abs(sizes[0] - sizes[1]) < 0.0001*sizes[0] # we can't check on identical output, compilation is not 100% deterministic (order of switch elements, etc.), but size should be ~identical print >> sys.stderr, 'debug check %d passed too' % debug try: From b50632c760a09b4916273f8fdc2682b4e2be14ac Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Jan 2014 17:15:15 -0800 Subject: [PATCH 24/85] fix flakey lua benchmark --- tests/test_benchmark.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_benchmark.py b/tests/test_benchmark.py index 21a4717824403..729512f3240aa 100644 --- a/tests/test_benchmark.py +++ b/tests/test_benchmark.py @@ -472,7 +472,7 @@ def test_zzz_java_nbody(self): # tests xmlvm compiled java, including bitcasts o def lua(self, benchmark, expected, output_parser=None, args_processor=None): shutil.copyfile(path_from_root('tests', 'lua', benchmark + '.lua'), benchmark + '.lua') def lib_builder(name, native, env_init): - ret = self.get_library('lua', [os.path.join('src', 'lua'), os.path.join('src', 'liblua.a')], make=['make', 'generic'], configure=None, native=native, cache_name_extra=name, env_init=env_init) + ret = self.get_library('lua_native' if native else 'lua', [os.path.join('src', 'lua'), os.path.join('src', 'liblua.a')], make=['make', 'generic'], configure=None, native=native, cache_name_extra=name, env_init=env_init) if native: return ret shutil.copyfile(ret[0], ret[0] + '.bc') ret[0] += '.bc' From 918bbd41b3a0bb76948c879f6c6725d8c55d6a85 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Jan 2014 19:47:46 -0800 Subject: [PATCH 25/85] fix test_source_map --- tests/test_core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_core.py b/tests/test_core.py index 09a5d56d2a9a8..dbd2cd7a89cc5 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -6058,6 +6058,7 @@ def build_and_check(): # optimizer can deal with both types. out_file = re.sub(' *//@.*$', '', out_file, flags=re.MULTILINE) def clean(code): + code = re.sub(r'\n+[ \n]*\n+', '\n', code) code = code.replace('{\n}', '{}') return '\n'.join(sorted(code.split('\n'))) self.assertIdentical(clean(no_maps_file), clean(out_file)) From d963b0fb6103311cefdb23ce2230ea7af7b6b2a9 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 17 Jan 2014 19:54:16 -0800 Subject: [PATCH 26/85] fix asm1.test_cubescript --- tests/test_core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_core.py b/tests/test_core.py index dbd2cd7a89cc5..f5d18e451a65d 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -4628,6 +4628,7 @@ def test_cubescript(self): assert 'asm1' in test_modes if self.run_name == 'asm1': generated = open('src.cpp.o.js').read() + generated = re.sub(r'\n+[ \n]*\n+', '\n', generated) main = generated[generated.find('function runPostSets'):] main = main[:main.find('\n}')] assert main.count('\n') == 7, 'must not emit too many postSets: %d' % main.count('\n') From f138d08629386110156ce8426ce57f7388c4cae0 Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Sat, 18 Jan 2014 13:42:42 +0700 Subject: [PATCH 27/85] Remove unused aliases. --- src/library.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/library.js b/src/library.js index d2adbf24c1fab..c58b4e67d19b7 100644 --- a/src/library.js +++ b/src/library.js @@ -225,7 +225,6 @@ LibraryManager.library = { var result = ___libgenSplitName(path); return result[0] + result[1] + 1; }, - __xpg_basename: 'basename', dirname__deps: ['__libgenSplitName'], dirname: function(path) { // char *dirname(char *path); @@ -2883,17 +2882,6 @@ LibraryManager.library = { }, #endif - fopen64: 'fopen', - __isoc99_fscanf: 'fscanf', - // TODO: Check if any other aliases are needed. - _IO_getc: 'getc', - _IO_putc: 'putc', - _ZNSo3putEc: 'putchar', - _ZNSo5flushEv__deps: ['fflush', 'stdout'], - _ZNSo5flushEv: function() { - _fflush({{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}}); - }, - // ========================================================================== // sys/mman.h // ========================================================================== From 7b47897f853989c10921745d1556d998d78ec295 Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Sat, 18 Jan 2014 13:43:39 +0700 Subject: [PATCH 28/85] catched -> caught --- tests/core/test_multiexception.in | 6 +++--- tests/test_core.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/core/test_multiexception.in b/tests/core/test_multiexception.in index 46acbbf3b9e88..5453d11cdbc24 100644 --- a/tests/core/test_multiexception.in +++ b/tests/core/test_multiexception.in @@ -22,9 +22,9 @@ void setjmp_func(jmp_state* s, int level) { s->jmp = c_jmp; setjmp_func(s, level + 1); } - catch (int catched_eid) { - printf("caught %d\n", catched_eid); - if (catched_eid == c_jmp) { + catch (int caught_eid) { + printf("caught %d\n", caught_eid); + if (caught_eid == c_jmp) { printf("setjmp exception execution path, level: %d, prev_jmp: %d\n", level, prev_jmp); if (prev_jmp != -1) { diff --git a/tests/test_core.py b/tests/test_core.py index f5d18e451a65d..cda5b7593af7b 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1269,7 +1269,7 @@ class MyException } catch (MyException & e) { - std::cout << "Catched..."; + std::cout << "Caught..."; } try @@ -1278,7 +1278,7 @@ class MyException } catch (MyException e) { - std::cout << "Catched..."; + std::cout << "Caught..."; } return 0; @@ -1288,7 +1288,7 @@ class MyException Settings.DISABLE_EXCEPTION_CATCHING = 0 if '-O2' in self.emcc_args: self.emcc_args.pop() ; self.emcc_args.pop() # disable closure to work around a closure bug - self.do_run(src, 'Throw...Construct...Catched...Destruct...Throw...Construct...Copy...Catched...Destruct...Destruct...') + self.do_run(src, 'Throw...Construct...Caught...Destruct...Throw...Construct...Copy...Caught...Destruct...Destruct...') def test_exception_2(self): if self.emcc_args is None: return self.skip('need emcc to add in libcxx properly') From 57c672983864f3c21f0f5461d4597b28ec3f7471 Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Sat, 18 Jan 2014 13:55:38 +0700 Subject: [PATCH 29/85] Make exception tests named more consistently. --- ...st_exception_2.in => test_exceptions_2.in} | 0 ..._exception_2.out => test_exceptions_2.out} | 0 ...iexception.in => test_exceptions_multi.in} | 0 ...xception.out => test_exceptions_multi.out} | 0 ...td_exception.in => test_exceptions_std.in} | 0 ..._exception.out => test_exceptions_std.out} | 0 .../test_exceptions_typed.in} | 0 .../test_exceptions_typed.out} | 0 ...ption.in => test_exceptions_white_list.in} | 0 ...ion.out => test_exceptions_white_list.out} | 0 tests/test_core.py | 32 ++++++++++--------- 11 files changed, 17 insertions(+), 15 deletions(-) rename tests/core/{test_exception_2.in => test_exceptions_2.in} (100%) rename tests/core/{test_exception_2.out => test_exceptions_2.out} (100%) rename tests/core/{test_multiexception.in => test_exceptions_multi.in} (100%) rename tests/core/{test_multiexception.out => test_exceptions_multi.out} (100%) rename tests/core/{test_std_exception.in => test_exceptions_std.in} (100%) rename tests/core/{test_std_exception.out => test_exceptions_std.out} (100%) rename tests/{exceptions/typed.cpp => core/test_exceptions_typed.in} (100%) rename tests/{exceptions/output.txt => core/test_exceptions_typed.out} (100%) rename tests/core/{test_white_list_exception.in => test_exceptions_white_list.in} (100%) rename tests/core/{test_white_list_exception.out => test_exceptions_white_list.out} (100%) diff --git a/tests/core/test_exception_2.in b/tests/core/test_exceptions_2.in similarity index 100% rename from tests/core/test_exception_2.in rename to tests/core/test_exceptions_2.in diff --git a/tests/core/test_exception_2.out b/tests/core/test_exceptions_2.out similarity index 100% rename from tests/core/test_exception_2.out rename to tests/core/test_exceptions_2.out diff --git a/tests/core/test_multiexception.in b/tests/core/test_exceptions_multi.in similarity index 100% rename from tests/core/test_multiexception.in rename to tests/core/test_exceptions_multi.in diff --git a/tests/core/test_multiexception.out b/tests/core/test_exceptions_multi.out similarity index 100% rename from tests/core/test_multiexception.out rename to tests/core/test_exceptions_multi.out diff --git a/tests/core/test_std_exception.in b/tests/core/test_exceptions_std.in similarity index 100% rename from tests/core/test_std_exception.in rename to tests/core/test_exceptions_std.in diff --git a/tests/core/test_std_exception.out b/tests/core/test_exceptions_std.out similarity index 100% rename from tests/core/test_std_exception.out rename to tests/core/test_exceptions_std.out diff --git a/tests/exceptions/typed.cpp b/tests/core/test_exceptions_typed.in similarity index 100% rename from tests/exceptions/typed.cpp rename to tests/core/test_exceptions_typed.in diff --git a/tests/exceptions/output.txt b/tests/core/test_exceptions_typed.out similarity index 100% rename from tests/exceptions/output.txt rename to tests/core/test_exceptions_typed.out diff --git a/tests/core/test_white_list_exception.in b/tests/core/test_exceptions_white_list.in similarity index 100% rename from tests/core/test_white_list_exception.in rename to tests/core/test_exceptions_white_list.in diff --git a/tests/core/test_white_list_exception.out b/tests/core/test_exceptions_white_list.out similarity index 100% rename from tests/core/test_white_list_exception.out rename to tests/core/test_exceptions_white_list.out diff --git a/tests/test_core.py b/tests/test_core.py index cda5b7593af7b..841800df8ffb1 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1290,24 +1290,24 @@ class MyException self.emcc_args.pop() ; self.emcc_args.pop() # disable closure to work around a closure bug self.do_run(src, 'Throw...Construct...Caught...Destruct...Throw...Construct...Copy...Caught...Destruct...Destruct...') - def test_exception_2(self): + def test_exceptions_2(self): if self.emcc_args is None: return self.skip('need emcc to add in libcxx properly') Settings.DISABLE_EXCEPTION_CATCHING = 0 - test_path = path_from_root('tests', 'core', 'test_exception_2') + test_path = path_from_root('tests', 'core', 'test_exceptions_2') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) - def test_white_list_exception(self): + def test_exceptions_white_list(self): if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp') Settings.DISABLE_EXCEPTION_CATCHING = 2 Settings.EXCEPTION_CATCHING_WHITELIST = ["__Z12somefunctionv"] Settings.INLINING_LIMIT = 50 # otherwise it is inlined and not identified - test_path = path_from_root('tests', 'core', 'test_white_list_exception') + test_path = path_from_root('tests', 'core', 'test_exceptions_white_list') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) @@ -1315,7 +1315,7 @@ def test_white_list_exception(self): Settings.DISABLE_EXCEPTION_CATCHING = 0 Settings.EXCEPTION_CATCHING_WHITELIST = [] - def test_uncaught_exception(self): + def test_exceptions_uncaught(self): if self.emcc_args is None: return self.skip('no libcxx inclusion without emcc') Settings.DISABLE_EXCEPTION_CATCHING = 0 @@ -1354,29 +1354,31 @@ def test_uncaught_exception(self): ''' self.do_run(src, 'success') - def test_typed_exceptions(self): - Settings.DISABLE_EXCEPTION_CATCHING = 0 - Settings.SAFE_HEAP = 0 # Throwing null will cause an ignorable null pointer access. - src = open(path_from_root('tests', 'exceptions', 'typed.cpp'), 'r').read() - expected = open(path_from_root('tests', 'exceptions', 'output.txt'), 'r').read() - self.do_run(src, expected) + def test_exceptions_typed(self): + Settings.DISABLE_EXCEPTION_CATCHING = 0 + Settings.SAFE_HEAP = 0 # Throwing null will cause an ignorable null pointer access. + + test_path = path_from_root('tests', 'core', 'test_exceptions_typed') + src, output = (test_path + s for s in ('.in', '.out')) + + self.do_run_from_file(src, output) - def test_multiexception(self): + def test_exceptions_multi(self): if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp') Settings.DISABLE_EXCEPTION_CATCHING = 0 - test_path = path_from_root('tests', 'core', 'test_multiexception') + test_path = path_from_root('tests', 'core', 'test_exceptions_multi') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) - def test_std_exception(self): + def test_exceptions_std(self): if self.emcc_args is None: return self.skip('requires emcc') Settings.DISABLE_EXCEPTION_CATCHING = 0 self.emcc_args += ['-s', 'SAFE_HEAP=0'] - test_path = path_from_root('tests', 'core', 'test_std_exception') + test_path = path_from_root('tests', 'core', 'test_exceptions_std') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) From ab0a6e5a6fc4a5d25d19bdfaf818fc4151e95148 Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Sat, 18 Jan 2014 14:09:58 +0700 Subject: [PATCH 30/85] No need for 64 bit aliases for these --- src/library.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/library.js b/src/library.js index c58b4e67d19b7..f59cf3b3ac888 100644 --- a/src/library.js +++ b/src/library.js @@ -392,9 +392,6 @@ LibraryManager.library = { _umask.cmask = newMask; return oldMask; }, - stat64: 'stat', - fstat64: 'fstat', - lstat64: 'lstat', // ========================================================================== // sys/statvfs.h @@ -1555,9 +1552,6 @@ LibraryManager.library = { if (bytes != 0) self.alloc(bytes); return ret; // Previous break location. }, - open64: 'open', - lseek64: 'lseek', - ftruncate64: 'ftruncate', // ========================================================================== // stdio.h @@ -2553,7 +2547,6 @@ LibraryManager.library = { return 0; }, fseeko: 'fseek', - fseeko64: 'fseek', fsetpos__deps: ['$FS', 'lseek', '__setErrNo', '$ERRNO_CODES'], fsetpos: function(stream, pos) { // int fsetpos(FILE *stream, const fpos_t *pos); @@ -2590,7 +2583,6 @@ LibraryManager.library = { } }, ftello: 'ftell', - ftello64: 'ftell', fwrite__deps: ['$FS', 'write'], fwrite: function(ptr, size, nitems, stream) { // size_t fwrite(const void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream); From 8762f317490f82ddadf8257d29f8dc2b074b2683 Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Sat, 18 Jan 2014 14:31:20 +0700 Subject: [PATCH 31/85] Implement catopen/catgets/catclose to help libcxx tests pass. --- src/library.js | 21 ++++++++++++++++++++- tests/core/test_nl_types.in | 8 ++++++++ tests/core/test_nl_types.out | 1 + tests/test_core.py | 6 ++++++ 4 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 tests/core/test_nl_types.in create mode 100644 tests/core/test_nl_types.out diff --git a/src/library.js b/src/library.js index f59cf3b3ac888..b38a74bf3d661 100644 --- a/src/library.js +++ b/src/library.js @@ -557,6 +557,25 @@ LibraryManager.library = { return 0; }, + // ========================================================================== + // nl_types.h + // ========================================================================== + + catopen: function(name, oflag) { + // nl_catd catopen (const char *name, int oflag) + return -1; + }, + + catgets: function(catd, set_id, msg_id, s) { + // char *catgets (nl_catd catd, int set_id, int msg_id, const char *s) + return s; + }, + + catclose: function(catd) { + // int catclose (nl_catd catd) + return 0; + }, + // ========================================================================== // poll.h // ========================================================================== @@ -9155,7 +9174,7 @@ function autoAddDeps(object, name) { } // Add aborting stubs for various libc stuff needed by libc++ -['pthread_cond_signal', 'pthread_equal', 'pthread_join', 'pthread_detach', 'catgets', 'catopen', 'catclose'].forEach(function(aborter) { +['pthread_cond_signal', 'pthread_equal', 'pthread_join', 'pthread_detach'].forEach(function(aborter) { LibraryManager.library[aborter] = function aborting_stub() { throw 'TODO: ' + aborter }; }); diff --git a/tests/core/test_nl_types.in b/tests/core/test_nl_types.in new file mode 100644 index 0000000000000..666920eebe4af --- /dev/null +++ b/tests/core/test_nl_types.in @@ -0,0 +1,8 @@ +#include +#include + +int main(int argc, char ** argv) { + nl_catd c = catopen("none", 0); + printf("Hello, %s.\n", catgets(c, 0, 0, "world")); + return catclose(c); +} diff --git a/tests/core/test_nl_types.out b/tests/core/test_nl_types.out new file mode 100644 index 0000000000000..f75ba05f340c5 --- /dev/null +++ b/tests/core/test_nl_types.out @@ -0,0 +1 @@ +Hello, world. diff --git a/tests/test_core.py b/tests/test_core.py index 841800df8ffb1..b1e1ffe67b174 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -4294,6 +4294,12 @@ def test_getloadavg(self): self.do_run_from_file(src, output) + def test_nl_types(self): + test_path = path_from_root('tests', 'core', 'test_nl_types') + src, output = (test_path + s for s in ('.in', '.out')) + + self.do_run_from_file(src, output) + def test_799(self): src = open(path_from_root('tests', '799.cpp'), 'r').read() self.do_run(src, '''Set PORT family: 0, port: 3979 From ac244127bf487f7d74f894412a5e54b8850c0e86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Sat, 18 Jan 2014 12:33:18 +0200 Subject: [PATCH 32/85] Fix FFP GL emulation directives. The form !GL_FFP_ONLY is not supported, use GL_FFP_ONLY == 0 instead. --- src/library_gl.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/library_gl.js b/src/library_gl.js index dd7aeafb2488d..1b6a47a4a995a 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -3547,13 +3547,13 @@ var LibraryGL = { } keyView.next((enabledAttributesKey << 2) | fogParam); -#if !GL_FFP_ONLY +#if GL_FFP_ONLY == 0 // By cur program: keyView.next(GL.currProgram); if (!GL.currProgram) { #endif GLImmediate.TexEnvJIT.traverseState(keyView); -#if !GL_FFP_ONLY +#if GL_FFP_ONLY == 0 } #endif @@ -3940,7 +3940,7 @@ var LibraryGL = { GLctx.enableVertexAttribArray(this.colorLocation); #endif } -#if !GL_FFP_ONLY +#if GL_FFP_ONLY == 0 else if (this.hasColor) { GLctx.disableVertexAttribArray(this.colorLocation); GLctx.vertexAttrib4fv(this.colorLocation, GLImmediate.clientColor); @@ -3955,7 +3955,7 @@ var LibraryGL = { }, cleanup: function cleanup() { -#if !GL_FFP_ONLY +#if GL_FFP_ONLY == 0 GLctx.disableVertexAttribArray(this.positionLocation); if (this.hasTextures) { for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) { @@ -4342,7 +4342,7 @@ var LibraryGL = { } #if GL_UNSAFE_OPTS == 0 -#if !GL_FFP_ONLY +#if GL_FFP_ONLY == 0 renderer.cleanup(); #endif #endif From 7a1c4076a28a4a7eee344dff199c43d03c996f2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Sat, 18 Jan 2014 12:52:43 +0200 Subject: [PATCH 33/85] Fix GL emulation cleanup to properly update the state it changes. --- src/library_gl.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/library_gl.js b/src/library_gl.js index 1b6a47a4a995a..781a07d9a0558 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -3972,6 +3972,7 @@ var LibraryGL = { } if (!GL.currProgram) { GLctx.useProgram(null); + GLImmediate.fixedFunctionProgram = 0; } if (!GL.currArrayBuffer) { GLctx.bindBuffer(GLctx.ARRAY_BUFFER, null); From 514455525bf4f8e4e72ccdef7d3f88b26fbc77e5 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sat, 18 Jan 2014 10:15:28 -0800 Subject: [PATCH 34/85] handle #if !X in preprocessor --- src/library_gl.js | 14 +++++++------- src/parseTools.js | 6 +++++- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/library_gl.js b/src/library_gl.js index 781a07d9a0558..bf4ccccee4664 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -2380,7 +2380,7 @@ var LibraryGL = { // GL Immediate mode // See comment in GLEmulation.init() -#if FULL_ES2 == 0 +#if !FULL_ES2 $GLImmediate__postset: 'GLImmediate.setupFuncs(); Browser.moduleContextCreatedCallbacks.push(function() { GLImmediate.init() });', #endif $GLImmediate__deps: ['$Browser', '$GL', '$GLEmulation'], @@ -3547,13 +3547,13 @@ var LibraryGL = { } keyView.next((enabledAttributesKey << 2) | fogParam); -#if GL_FFP_ONLY == 0 +#if !GL_FFP_ONLY // By cur program: keyView.next(GL.currProgram); if (!GL.currProgram) { #endif GLImmediate.TexEnvJIT.traverseState(keyView); -#if GL_FFP_ONLY == 0 +#if !GL_FFP_ONLY } #endif @@ -3940,7 +3940,7 @@ var LibraryGL = { GLctx.enableVertexAttribArray(this.colorLocation); #endif } -#if GL_FFP_ONLY == 0 +#if !GL_FFP_ONLY else if (this.hasColor) { GLctx.disableVertexAttribArray(this.colorLocation); GLctx.vertexAttrib4fv(this.colorLocation, GLImmediate.clientColor); @@ -3955,7 +3955,7 @@ var LibraryGL = { }, cleanup: function cleanup() { -#if GL_FFP_ONLY == 0 +#if !GL_FFP_ONLY GLctx.disableVertexAttribArray(this.positionLocation); if (this.hasTextures) { for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) { @@ -4342,8 +4342,8 @@ var LibraryGL = { GLctx.bindBuffer(GLctx.ELEMENT_ARRAY_BUFFER, GL.buffers[GL.currElementArrayBuffer] || null); } -#if GL_UNSAFE_OPTS == 0 -#if GL_FFP_ONLY == 0 +#if !GL_UNSAFE_OPTS +#if !GL_FFP_ONLY renderer.cleanup(); #endif #endif diff --git a/src/parseTools.js b/src/parseTools.js index 1c70a018de9f6..e09cd2e244201 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -46,7 +46,11 @@ function preprocess(text) { error('unsupported preprecessor op ' + op); } } else { - showStack.push(ident in this && this[ident] > 0); + if (ident[0] === '!') { + showStack.push(!(this[ident.substr(1)] > 0)); + } else { + showStack.push(ident in this && this[ident] > 0); + } } } else if (line[2] == 'n') { // include var included = read(line.substr(line.indexOf(' ')+1)); From fa70d238fca62dc3f7b1aefa8f0bf8c6dc337eea Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sat, 18 Jan 2014 18:26:19 -0800 Subject: [PATCH 35/85] improve EM_ASM to tolerate quotes when safe, and error when not; fixes #2032 --- src/runtime.js | 12 +++++++++++- tests/core/test_inlinejs3.in | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/runtime.js b/src/runtime.js index 1fc9e026459a3..4fcca56b25575 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -393,7 +393,17 @@ var Runtime = { for (var i = 0; i < numArgs; i++) { args.push(String.fromCharCode(36) + i); // $0, $1 etc } - return Runtime.asmConstCache[code] = eval('(function(' + args.join(',') + '){ ' + Pointer_stringify(code) + ' })'); // new Function does not allow upvars in node + code = Pointer_stringify(code); + if (code[0] === '"') { + // tolerate EM_ASM("..code..") even though EM_ASM(..code..) is correct + if (code.indexOf('"', 1) === code.length-1) { + code = code.substr(1, code.length-2); + } else { + // something invalid happened, e.g. EM_ASM("..code($0)..", input) + abort('invalid EM_ASM input |' + code + '|. Please use EM_ASM(..code..) (no quotes) or EM_ASM({ ..code($0).. }, input) (to input values)'); + } + } + return Runtime.asmConstCache[code] = eval('(function(' + args.join(',') + '){ ' + code + ' })'); // new Function does not allow upvars in node }, warnOnce: function(text) { diff --git a/tests/core/test_inlinejs3.in b/tests/core/test_inlinejs3.in index 3e1913ffa24d3..e21ed041b4dc6 100644 --- a/tests/core/test_inlinejs3.in +++ b/tests/core/test_inlinejs3.in @@ -3,7 +3,7 @@ int main(int argc, char **argv) { EM_ASM(Module.print('hello dere1')); - EM_ASM(Module.print('hello dere2');); + EM_ASM("Module.print('hello dere2');"); for (int i = 0; i < 3; i++) { EM_ASM(Module.print('hello dere3'); Module.print('hello dere' + 4);); } From 7fff4e3eaa209df9a7f967a89dca1362d1ede536 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sun, 19 Jan 2014 12:09:18 -0800 Subject: [PATCH 36/85] better error message for asm.js not supporting growable heap yet --- emcc | 4 ++++ src/compiler.js | 3 --- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/emcc b/emcc index 7333b83279e99..030e87b0658da 100755 --- a/emcc +++ b/emcc @@ -1240,6 +1240,10 @@ try: if shared.Settings.SAFE_HEAP and not js_opts: logging.warning('asm.js+SAFE_HEAP requires js opts to be run (-O1 or above by default)') + if shared.Settings.ALLOW_MEMORY_GROWTH: + logging.error('Cannot enable ALLOW_MEMORY_GROWTH with asm.js, build with -s ASM_JS=0 if you need a growable heap'); + sys.exit(1); + if shared.Settings.CORRECT_SIGNS >= 2 or shared.Settings.CORRECT_OVERFLOWS >= 2 or shared.Settings.CORRECT_ROUNDINGS >= 2: debug_level = 4 # must keep debug info to do line-by-line operations diff --git a/src/compiler.js b/src/compiler.js index e4ce1c886a4ef..17a8e83c12eb7 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -183,9 +183,6 @@ if (SAFE_HEAP) USE_BSS = 0; // must initialize heap for safe heap // Settings sanity checks assert(!(USE_TYPED_ARRAYS === 2 && QUANTUM_SIZE !== 4), 'For USE_TYPED_ARRAYS == 2, must have normal QUANTUM_SIZE of 4'); -if (ASM_JS) { - assert(!ALLOW_MEMORY_GROWTH, 'Cannot grow asm.js heap'); -} assert(!(!NAMED_GLOBALS && BUILD_AS_SHARED_LIB), 'shared libraries must have named globals'); // Output some info and warnings based on settings From 1c5d10fb5b13e5ac8927ea11de6c780ede117a16 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Sun, 19 Jan 2014 19:21:37 -0800 Subject: [PATCH 37/85] remove obsolete assertion --- src/library.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/library.js b/src/library.js index b38a74bf3d661..09cb9b423397c 100644 --- a/src/library.js +++ b/src/library.js @@ -6230,9 +6230,6 @@ LibraryManager.library = { label = label|0; table = table|0; var i = 0; -#if ASSERTIONS - if ((label|0) == 0) abort(121); -#endif setjmpId = (setjmpId+1)|0; {{{ makeSetValueAsm('env', '0', 'setjmpId', 'i32') }}}; while ((i|0) < {{{ 2*MAX_SETJMPS }}}) { From 9339aabb90aeb8999fae8e2761142d94e0877b3e Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Fri, 17 Jan 2014 14:35:14 +0700 Subject: [PATCH 38/85] Remove unused _Unwind_* functions. --- src/library.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/library.js b/src/library.js index 09cb9b423397c..c915125ce4287 100644 --- a/src/library.js +++ b/src/library.js @@ -4427,14 +4427,6 @@ LibraryManager.library = { throw exception; }, - _Unwind_Resume_or_Rethrow: function(ptr) { - {{{ makeThrow('ptr') }}}; - }, - _Unwind_RaiseException: function(ptr) { - {{{ makeThrow('ptr') }}}; - }, - _Unwind_DeleteException: function(ptr) {}, - terminate: '__cxa_call_unexpected', __gxx_personality_v0__deps: ['llvm_eh_exception', '_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch'], From dd4745db3ba58ceecebc406744d199231e5d7f7e Mon Sep 17 00:00:00 2001 From: Ryan Kelly Date: Mon, 20 Jan 2014 20:58:22 +1100 Subject: [PATCH 39/85] Fix a param-handling bug in normalizeAsm --- tools/js-optimizer.js | 1 + tools/test-js-optimizer-asm-regs-min-output.js | 5 +++++ tools/test-js-optimizer-asm-regs-min.js | 5 +++++ 3 files changed, 11 insertions(+) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 6d65f3e7011e9..d604f54643ded 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -1603,6 +1603,7 @@ function normalizeAsm(func) { node = node[1]; var name = node[2][1]; if (func[2] && func[2].indexOf(name) < 0) break; // not an assign into a parameter, but a global + if (name in data.params) break; // already done that param, must be starting function body data.params[name] = detectAsmCoercion(node[3]); stats[i] = emptyNode(); i++; diff --git a/tools/test-js-optimizer-asm-regs-min-output.js b/tools/test-js-optimizer-asm-regs-min-output.js index 3955e48a03a9b..748b8e0adbd06 100644 --- a/tools/test-js-optimizer-asm-regs-min-output.js +++ b/tools/test-js-optimizer-asm-regs-min-output.js @@ -33,4 +33,9 @@ function cl(b) { a(c); i1(b); } +function cl(b) { + b = b | 0; + b = b + 4; + a(b); +} diff --git a/tools/test-js-optimizer-asm-regs-min.js b/tools/test-js-optimizer-asm-regs-min.js index a5b9427e3ceb9..65d8d42999cc7 100644 --- a/tools/test-js-optimizer-asm-regs-min.js +++ b/tools/test-js-optimizer-asm-regs-min.js @@ -33,5 +33,10 @@ function collideLocal(i1) { aGlobal(a); // multiple collisions, a and i1 bGlobal(i1); } +function collideLocal(i1) { + i1 = i1 | 0; + i1 = i1 + 4; // statement is of similar shape to a param coercion + aGlobal(i1); +} // EMSCRIPTEN_GENERATED_FUNCTIONS // EXTRA_INFO: { "names": ["a", "b", "c", "d", "e", "f", "g", "h", "i", "i1", "cl"], "globals": { "aGlobal": "a", "bGlobal": "i1", "collideLocal": "cl" } } From 8232a25d37d77fe523757ed0cf32557df07e011a Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 20 Jan 2014 12:03:21 -0800 Subject: [PATCH 40/85] enable first setjmp test in fastcomp --- tests/test_core.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index b1e1ffe67b174..4d1cfdd3636af 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1117,12 +1117,10 @@ def test_regex(self): self.do_run_from_file(src, output) def test_longjmp(self): - if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp') - - test_path = path_from_root('tests', 'core', 'test_longjmp') - src, output = (test_path + s for s in ('.in', '.out')) - - self.do_run_from_file(src, output) + if os.environ.get('EMCC_FAST_COMPILER') == '1': Settings.DISABLE_EXCEPTION_CATCHING = 1 + test_path = path_from_root('tests', 'core', 'test_longjmp') + src, output = (test_path + s for s in ('.in', '.out')) + self.do_run_from_file(src, output) def test_longjmp2(self): if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp') From 6363dec347be9cb2e7dd775330785c8f8019eca9 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 20 Jan 2014 15:15:39 -0800 Subject: [PATCH 41/85] fix longjmp call by pointer in fastcomp, and enable test_longjmp2 --- src/library.js | 4 ++++ tests/test_core.py | 4 +--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/library.js b/src/library.js index 09cb9b423397c..c668a5aa90cfb 100644 --- a/src/library.js +++ b/src/library.js @@ -6287,6 +6287,10 @@ LibraryManager.library = { throw { longjmp: true, id: {{{ makeGetValue('env', '0', 'i32') }}}, value: value || 1 }; #endif }, + emscripten_longjmp__deps: ['longjmp'], + emscripten_longjmp: function(env, value) { + _longjmp(env, value); + }, // ========================================================================== // signal.h diff --git a/tests/test_core.py b/tests/test_core.py index 4d1cfdd3636af..3b06730843611 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1123,11 +1123,9 @@ def test_longjmp(self): self.do_run_from_file(src, output) def test_longjmp2(self): - if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp') - + if os.environ.get('EMCC_FAST_COMPILER') == '1': Settings.DISABLE_EXCEPTION_CATCHING = 1 test_path = path_from_root('tests', 'core', 'test_longjmp2') src, output = (test_path + s for s in ('.in', '.out')) - self.do_run_from_file(src, output) def test_longjmp3(self): From 12cd36c11725f30adc5a198ab8dc96da760bfd84 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 20 Jan 2014 15:18:58 -0800 Subject: [PATCH 42/85] enable more setjmp tests in fastcomp --- tests/test_core.py | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index 3b06730843611..8694961dcc5a4 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1129,38 +1129,29 @@ def test_longjmp2(self): self.do_run_from_file(src, output) def test_longjmp3(self): - if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp') - + if os.environ.get('EMCC_FAST_COMPILER') == '1': Settings.DISABLE_EXCEPTION_CATCHING = 1 test_path = path_from_root('tests', 'core', 'test_longjmp3') src, output = (test_path + s for s in ('.in', '.out')) - self.do_run_from_file(src, output) def test_longjmp4(self): - if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp') - + if os.environ.get('EMCC_FAST_COMPILER') == '1': Settings.DISABLE_EXCEPTION_CATCHING = 1 test_path = path_from_root('tests', 'core', 'test_longjmp4') src, output = (test_path + s for s in ('.in', '.out')) - self.do_run_from_file(src, output) def test_longjmp_funcptr(self): - if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp') - + if os.environ.get('EMCC_FAST_COMPILER') == '1': Settings.DISABLE_EXCEPTION_CATCHING = 1 test_path = path_from_root('tests', 'core', 'test_longjmp_funcptr') src, output = (test_path + s for s in ('.in', '.out')) - self.do_run_from_file(src, output) def test_longjmp_repeat(self): - if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp') - - Settings.MAX_SETJMPS = 1 - - test_path = path_from_root('tests', 'core', 'test_longjmp_repeat') - src, output = (test_path + s for s in ('.in', '.out')) - - self.do_run_from_file(src, output) + if os.environ.get('EMCC_FAST_COMPILER') == '1': Settings.DISABLE_EXCEPTION_CATCHING = 1 + Settings.MAX_SETJMPS = 1 + test_path = path_from_root('tests', 'core', 'test_longjmp_repeat') + src, output = (test_path + s for s in ('.in', '.out')) + self.do_run_from_file(src, output) def test_longjmp_stacked(self): if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp') From 9e6dedaa2325c5e4fb0ac41fa8902123ecd91647 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 20 Jan 2014 15:47:24 -0800 Subject: [PATCH 43/85] enable test_longjmp_stacked in fastcomp --- tests/test_core.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index 8694961dcc5a4..38eb0a27e5134 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1154,14 +1154,11 @@ def test_longjmp_repeat(self): self.do_run_from_file(src, output) def test_longjmp_stacked(self): - if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp') - + if os.environ.get('EMCC_FAST_COMPILER') == '1': Settings.DISABLE_EXCEPTION_CATCHING = 1 test_path = path_from_root('tests', 'core', 'test_longjmp_stacked') src, output = (test_path + s for s in ('.in', '.out')) - self.do_run_from_file(src, output) - def test_longjmp_exc(self): if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp') From 544a0fd1282f2e0f39644877edf753463f406bee Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 20 Jan 2014 15:49:40 -0800 Subject: [PATCH 44/85] fix up final setjmp tests for fastcomp --- tests/test_core.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index 38eb0a27e5134..efe65904cb9c9 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1160,15 +1160,13 @@ def test_longjmp_stacked(self): self.do_run_from_file(src, output) def test_longjmp_exc(self): - if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp') - + if os.environ.get('EMCC_FAST_COMPILER') == '1': Settings.DISABLE_EXCEPTION_CATCHING = 1 test_path = path_from_root('tests', 'core', 'test_longjmp_exc') src, output = (test_path + s for s in ('.in', '.out')) - self.do_run_from_file(src, output) def test_setjmp_many(self): - if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp') + if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp: make MAX_SETJMPS take effect') src = r''' #include From 8db7402c6c2e7b15fe73ae54f271be0b4e4f4373 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 20 Jan 2014 16:17:53 -0800 Subject: [PATCH 45/85] updates and fixes for test_cases --- tests/cases/longjmp_tiny_invoke.ll | 2 +- tests/cases/longjmp_tiny_phi.ll | 2 +- tests/cases/longjmp_tiny_phi2.ll | 2 +- tests/test_core.py | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/cases/longjmp_tiny_invoke.ll b/tests/cases/longjmp_tiny_invoke.ll index e1a72e001c95c..5fd35194cdc55 100644 --- a/tests/cases/longjmp_tiny_invoke.ll +++ b/tests/cases/longjmp_tiny_invoke.ll @@ -9,7 +9,7 @@ target triple = "i386-pc-linux-gnu" define i32 @main() { %retval = alloca i32, align 4 store i32 0, i32* %retval - %call = invoke i32 @setjmp(i16* getelementptr inbounds ([20 x i16]* @_ZL3buf, i32 0, i32 0)) returns_twice, !dbg !20 + %call = invoke i32 @setjmp(i16* getelementptr inbounds ([20 x i16]* @_ZL3buf, i32 0, i32 0)) returns_twice to label %allgood unwind label %awful allgood: diff --git a/tests/cases/longjmp_tiny_phi.ll b/tests/cases/longjmp_tiny_phi.ll index cced7cab90eb6..21b936dde1dd6 100644 --- a/tests/cases/longjmp_tiny_phi.ll +++ b/tests/cases/longjmp_tiny_phi.ll @@ -28,7 +28,7 @@ if.else: ; preds = %entry br label %if.end if.end: ; preds = %if.else, %if.then - %aaa = phi i32 [ -1, %if.then ], [ 0, %if.else ], [ 1, %two ], [ 2, %entry ] + %aaa = phi i32 [ -1, %if.then ], [ 0, %if.else ] %call3 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str2, i32 0, i32 0), i32 %aaa), !dbg !26 ret i32 %aaa, !dbg !28 } diff --git a/tests/cases/longjmp_tiny_phi2.ll b/tests/cases/longjmp_tiny_phi2.ll index 1d7761c3b02f8..88312fc6da06b 100644 --- a/tests/cases/longjmp_tiny_phi2.ll +++ b/tests/cases/longjmp_tiny_phi2.ll @@ -24,7 +24,7 @@ if.then: ; preds = %entry br label %if.end, !dbg !25 if.end: ; preds = %if.else, %if.then - %aaa = phi i32 [ -1, %if.then ], [ 1, %two ], [ 2, %entry ] + %aaa = phi i32 [ -1, %if.then ], [ 1, %two ] %call3 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str2, i32 0, i32 0), i32 %aaa), !dbg !26 ret i32 %aaa, !dbg !28 } diff --git a/tests/test_core.py b/tests/test_core.py index efe65904cb9c9..381d1a7e2b156 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5045,6 +5045,7 @@ def test_lifetime(self): # to process. def test_cases(self): if Building.LLVM_OPTS: return self.skip("Our code is not exactly 'normal' llvm assembly") + if os.environ.get('EMCC_FAST_COMPILER') == '1': Settings.DISABLE_EXCEPTION_CATCHING = 1 emcc_args = self.emcc_args @@ -5060,7 +5061,7 @@ def test_cases(self): 'structphiparam', 'callwithstructural_ta2', 'callwithstructural64_ta2', 'structinparam', # pnacl limitations in ExpandStructRegs '2xi40', # pnacl limitations in ExpandGetElementPtr 'legalizer_ta2', '514_ta2', # pnacl limitation in not legalizing i104, i96, etc. - 'longjmp_tiny', 'longjmp_tiny_invoke', 'longjmp_tiny_phi', 'longjmp_tiny_phi2', 'longjmp_tiny_invoke_phi', 'indirectbrphi', 'ptrtoint_blockaddr', 'quoted', # current fastcomp limitations FIXME + 'indirectbrphi', 'ptrtoint_blockaddr', 'quoted', # current fastcomp limitations FIXME 'sillyfuncast2', 'sillybitcast', 'atomicrmw_unaligned' # TODO XXX ]: continue if '_ta2' in shortname and not Settings.USE_TYPED_ARRAYS == 2: From 9d10b207a5145e6427b7c774945dd3aa25577f76 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 20 Jan 2014 17:21:40 -0800 Subject: [PATCH 46/85] enable and fix more tests for fastcomp --- tests/test_core.py | 6 ------ tests/utf32.cpp | 20 ++++++++++---------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index 381d1a7e2b156..8c4d47a8b36f9 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1281,7 +1281,6 @@ def test_exceptions_2(self): self.do_run_from_file(src, output) - def test_exceptions_white_list(self): if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp') @@ -1346,13 +1345,9 @@ def test_exceptions_typed(self): self.do_run_from_file(src, output) def test_exceptions_multi(self): - if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp') - Settings.DISABLE_EXCEPTION_CATCHING = 0 - test_path = path_from_root('tests', 'core', 'test_exceptions_multi') src, output = (test_path + s for s in ('.in', '.out')) - self.do_run_from_file(src, output) def test_exceptions_std(self): @@ -4077,7 +4072,6 @@ def test_utf(self): def test_utf32(self): if self.emcc_args is None: return self.skip('need libc for wcslen()') if not self.is_le32(): return self.skip('this test uses inline js, which requires le32') - if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp') self.do_run(open(path_from_root('tests', 'utf32.cpp')).read(), 'OK.') self.do_run(open(path_from_root('tests', 'utf32.cpp')).read(), 'OK.', args=['-fshort-wchar']) diff --git a/tests/utf32.cpp b/tests/utf32.cpp index 6b75b24462cb7..d00338a61e0c1 100644 --- a/tests/utf32.cpp +++ b/tests/utf32.cpp @@ -16,11 +16,11 @@ int main() { if (sizeof(wchar_t) == 4) { utf32 *memory = new utf32[wstr.length()+1]; - asm("var str = Module.UTF32ToString(%0);" - "Module.print(str);" - "Module.stringToUTF32(str, %1);" - : - : "r"(wstr.c_str()), "r"(memory)); + EM_ASM_INT({ + var str = Module.UTF32ToString($0); + Module.print(str); + Module.stringToUTF32(str, $1); + }, wstr.c_str(), memory); // Compare memory to confirm that the string is intact after taking a route through JS side. const utf32 *srcPtr = reinterpret_cast(wstr.c_str()); @@ -33,11 +33,11 @@ int main() { } else { // sizeof(wchar_t) == 2, and we're building with -fshort-wchar. utf16 *memory = new utf16[2*wstr.length()+1]; - asm("var str = Module.UTF16ToString(%0);" - "Module.print(str);" - "Module.stringToUTF16(str, %1);" - : - : "r"(wstr.c_str()), "r"(memory)); + EM_ASM_INT({ + var str = Module.UTF16ToString($0); + Module.print(str); + Module.stringToUTF16(str, $1); + }, wstr.c_str(), memory); // Compare memory to confirm that the string is intact after taking a route through JS side. const utf16 *srcPtr = reinterpret_cast(wstr.c_str()); From ce15abc1d356fda7a5509d449c9c18623a9b4ea3 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 20 Jan 2014 17:26:18 -0800 Subject: [PATCH 47/85] enable more tests in fastcomp --- tests/test_core.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index 8c4d47a8b36f9..f27491c75ca0c 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -4665,7 +4665,7 @@ def test_gcc_unmangler(self): def test_lua(self): if self.emcc_args is None: return self.skip('requires emcc') if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: make this work') - if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp') + if os.environ.get('EMCC_FAST_COMPILER') == '1': Settings.DISABLE_EXCEPTION_CATCHING = 1 for aggro in ([0, 1] if Settings.ASM_JS and '-O2' in self.emcc_args else [0]): print aggro @@ -4686,7 +4686,7 @@ def get_freetype(self): def test_freetype(self): if self.emcc_args is None: return self.skip('requires emcc') if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: Figure out and try to fix') - if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp') + if os.environ.get('EMCC_FAST_COMPILER') == '1': Settings.DISABLE_EXCEPTION_CATCHING = 1 assert 'asm2g' in test_modes if self.run_name == 'asm2g': @@ -4832,7 +4832,7 @@ def test(): def test_poppler(self): if self.emcc_args is None: return self.skip('very slow, we only do this in emcc runs') - if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp') + if os.environ.get('EMCC_FAST_COMPILER') == '1': Settings.DISABLE_EXCEPTION_CATCHING = 1 Settings.CORRECT_OVERFLOWS = 1 Settings.CORRECT_SIGNS = 1 From 51204c52e42c8d6b6754d50438a032c7cd8e553b Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 20 Jan 2014 17:51:47 -0800 Subject: [PATCH 48/85] enable more tests in fastcomp --- tests/test_core.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index f27491c75ca0c..7c16f55ef526c 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5997,7 +5997,6 @@ def test_debug(self): def test_source_map(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip("doesn't pass without typed arrays") - if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp') if NODE_JS not in JS_ENGINES: return self.skip('sourcemapper requires Node to run') if '-g' not in Building.COMPILER_TEST_OPTS: Building.COMPILER_TEST_OPTS.append('-g') @@ -6082,7 +6081,6 @@ def test_exception_source_map(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip("doesn't pass without typed arrays") if '-g4' not in Building.COMPILER_TEST_OPTS: Building.COMPILER_TEST_OPTS.append('-g4') if NODE_JS not in JS_ENGINES: return self.skip('sourcemapper requires Node to run') - if os.environ.get('EMCC_FAST_COMPILER') == '1': return self.skip('todo in fastcomp') src = ''' #include @@ -6107,7 +6105,7 @@ def post(filename): tools.shared.NODE_JS, [map_filename])) with open(filename) as f: lines = f.readlines() for m in mappings: - if m['originalLine'] == 5 and '__cxa_throw' in lines[m['generatedLine']]: + if m['originalLine'] == 5 and '__cxa_throw' in lines[m['generatedLine']-1]: # -1 to fix 0-start vs 1-start return assert False, 'Must label throw statements with line numbers' From e7ae95bd86e363d6c51ca00abacb6733fc0d01cb Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Mon, 20 Jan 2014 15:03:35 +0700 Subject: [PATCH 49/85] Move workaround for emscripten from include/exception to library.js. --- src/library.js | 15 ++++++++++++++- system/include/libcxx/exception | 4 ++-- tests/core/test_exceptions_std.in | 1 + tests/core/test_exceptions_std.out | 3 ++- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/library.js b/src/library.js index c915125ce4287..5e53805fc0c5d 100644 --- a/src/library.js +++ b/src/library.js @@ -4539,7 +4539,20 @@ LibraryManager.library = { } }, - _ZNSt9exceptionD2Ev: function(){}, // XXX a dependency of dlmalloc, but not actually needed if libcxx is not anyhow included + // Destructors for std::exception since we don't have them implemented in libcxx as we aren't using libcxxabi. + // These are also needed for the dlmalloc tests. + _ZNSt9exceptionD1Ev: function() {}, + _ZNSt9exceptionD2Ev: function() {}, + + _ZNKSt9exception4whatEv__deps: ['_malloc'], + _ZNKSt9exception4whatEv: function() { + if (!__ZNKSt9exception4whatEv.buffer) { + var name = "std::exception"; + __ZNKSt9exception4whatEv.buffer = _malloc(name.length + 1); + writeStringToMemory(name, __ZNKSt9exception4whatEv.buffer); + } + return __ZNKSt9exception4whatEv.buffer; + }, _ZNSt9type_infoD2Ev: function(){}, diff --git a/system/include/libcxx/exception b/system/include/libcxx/exception index ddd75bd45643c..cad802e056a5f 100644 --- a/system/include/libcxx/exception +++ b/system/include/libcxx/exception @@ -91,8 +91,8 @@ class _LIBCPP_EXCEPTION_ABI exception { public: _LIBCPP_INLINE_VISIBILITY exception() _NOEXCEPT {} - virtual ~exception() _NOEXCEPT {} // XXX EMSCRIPTEN - implement in header - virtual const char* what() const _NOEXCEPT { return "std::exception"; } // XXX EMSCRIPTEN - implement in header + virtual ~exception() _NOEXCEPT; + virtual const char* what() const _NOEXCEPT; }; class _LIBCPP_EXCEPTION_ABI bad_exception diff --git a/tests/core/test_exceptions_std.in b/tests/core/test_exceptions_std.in index 4b5905d8c9da6..3b9f874b23b03 100644 --- a/tests/core/test_exceptions_std.in +++ b/tests/core/test_exceptions_std.in @@ -7,6 +7,7 @@ int main() { throw e; } catch (std::exception e) { + printf("what? %s\n", e.what()); printf("caught std::exception\n"); } return 0; diff --git a/tests/core/test_exceptions_std.out b/tests/core/test_exceptions_std.out index c1660de4c5774..eddab21cd9185 100644 --- a/tests/core/test_exceptions_std.out +++ b/tests/core/test_exceptions_std.out @@ -1 +1,2 @@ -caught std::exception \ No newline at end of file +what? std::exception +caught std::exception From 7a8c26b65f906de5f7490311e89eb6d5bf722211 Mon Sep 17 00:00:00 2001 From: Ryan Kelly Date: Tue, 21 Jan 2014 17:43:09 +1100 Subject: [PATCH 50/85] Minify loop labels while we're minifying local names. --- tools/js-optimizer.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index d604f54643ded..98cb4c490a043 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -2870,7 +2870,6 @@ function minifyLocals(ast) { } }); - // Traverse and minify all names. // The first time we encounter a local name, we assign it a // minified name that's not currently in use. Allocating on // demand means they're processed in a predicatable order, @@ -2887,6 +2886,17 @@ function minifyLocals(ast) { } } } + + // We can also minify loop labels, using a separate namespace + // to the variable declarations. + var newLabels = {}; + var nextMinifiedLabel = 0; + function getNextMinifiedLabel() { + ensureMinifiedNames(nextMinifiedLabel); + return minifiedNames[nextMinifiedLabel++]; + } + + // Traverse and minify all names. if (fun[1] in extraInfo.globals) { fun[1] = extraInfo.globals[fun[1]]; assert(fun[1]); @@ -2917,6 +2927,15 @@ function minifyLocals(ast) { } defn[0] = newNames[name]; }); + } else if (type === 'label') { + if (!newLabels[node[1]]) { + newLabels[node[1]] = getNextMinifiedLabel(); + } + node[1] = newLabels[node[1]]; + } else if (type === 'break' || type === 'continue') { + if (node[1]) { + node[1] = newLabels[node[1]]; + } } }); From 19a38d059bd6da1402edd3dc13c8485025059fa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Tue, 21 Jan 2014 17:30:41 +0200 Subject: [PATCH 51/85] Workaround glew linkage issue, see https://github.com/kripken/emscripten/issues/2025 --- system/include/GL/glew.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/system/include/GL/glew.h b/system/include/GL/glew.h index efd90d6f15b5c..8be23b60a3293 100644 --- a/system/include/GL/glew.h +++ b/system/include/GL/glew.h @@ -827,7 +827,9 @@ GLenum glewInit (void); GLboolean glewIsSupported (const char *name); #define glewIsExtensionSupported(x) glewIsSupported(x) -GLboolean glewExperimental; +//GLboolean glewExperimental; +static GLboolean glewExperimental; // XXX Emscripten Added 'static' to work around a linkage issue. See https://github.com/kripken/emscripten/issues/2025 + GLboolean glewGetExtension (const char *name); const GLubyte * glewGetErrorString (GLenum error); const GLubyte * glewGetString (GLenum name); From 5aca98bc3c716bdff10aab915e190afe47e21379 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 21 Jan 2014 13:58:27 -0800 Subject: [PATCH 52/85] update tests to allow exceptions and setjmp together in fastcomp --- tests/cases/longjmp_tiny_invoke.ll | 4 ++++ tests/cases/longjmp_tiny_invoke_phi.ll | 4 ++++ tests/test_core.py | 12 ------------ 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/tests/cases/longjmp_tiny_invoke.ll b/tests/cases/longjmp_tiny_invoke.ll index 5fd35194cdc55..6f856d492024d 100644 --- a/tests/cases/longjmp_tiny_invoke.ll +++ b/tests/cases/longjmp_tiny_invoke.ll @@ -29,9 +29,13 @@ if.end: ; preds = %if.else, %if.then ret i32 0, !dbg !28 awful: + %Z = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) + cleanup ret i32 1 } +declare i32 @__gxx_personality_v0(...) + declare i32 @setjmp(i16*) returns_twice declare i32 @printf(i8*, ...) diff --git a/tests/cases/longjmp_tiny_invoke_phi.ll b/tests/cases/longjmp_tiny_invoke_phi.ll index 30c4333918909..0df3f924092a0 100644 --- a/tests/cases/longjmp_tiny_invoke_phi.ll +++ b/tests/cases/longjmp_tiny_invoke_phi.ll @@ -35,6 +35,8 @@ if.end: ; preds = %if.else, %if.then ret i32 0 awful: + %Z = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) + cleanup ret i32 1 } @@ -44,3 +46,5 @@ declare i32 @printf(i8*, ...) declare void @longjmp(i16*, i32) +declare i32 @__gxx_personality_v0(...) + diff --git a/tests/test_core.py b/tests/test_core.py index 7c16f55ef526c..493ab77b1e391 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1117,50 +1117,42 @@ def test_regex(self): self.do_run_from_file(src, output) def test_longjmp(self): - if os.environ.get('EMCC_FAST_COMPILER') == '1': Settings.DISABLE_EXCEPTION_CATCHING = 1 test_path = path_from_root('tests', 'core', 'test_longjmp') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) def test_longjmp2(self): - if os.environ.get('EMCC_FAST_COMPILER') == '1': Settings.DISABLE_EXCEPTION_CATCHING = 1 test_path = path_from_root('tests', 'core', 'test_longjmp2') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) def test_longjmp3(self): - if os.environ.get('EMCC_FAST_COMPILER') == '1': Settings.DISABLE_EXCEPTION_CATCHING = 1 test_path = path_from_root('tests', 'core', 'test_longjmp3') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) def test_longjmp4(self): - if os.environ.get('EMCC_FAST_COMPILER') == '1': Settings.DISABLE_EXCEPTION_CATCHING = 1 test_path = path_from_root('tests', 'core', 'test_longjmp4') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) def test_longjmp_funcptr(self): - if os.environ.get('EMCC_FAST_COMPILER') == '1': Settings.DISABLE_EXCEPTION_CATCHING = 1 test_path = path_from_root('tests', 'core', 'test_longjmp_funcptr') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) def test_longjmp_repeat(self): - if os.environ.get('EMCC_FAST_COMPILER') == '1': Settings.DISABLE_EXCEPTION_CATCHING = 1 Settings.MAX_SETJMPS = 1 test_path = path_from_root('tests', 'core', 'test_longjmp_repeat') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) def test_longjmp_stacked(self): - if os.environ.get('EMCC_FAST_COMPILER') == '1': Settings.DISABLE_EXCEPTION_CATCHING = 1 test_path = path_from_root('tests', 'core', 'test_longjmp_stacked') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) def test_longjmp_exc(self): - if os.environ.get('EMCC_FAST_COMPILER') == '1': Settings.DISABLE_EXCEPTION_CATCHING = 1 test_path = path_from_root('tests', 'core', 'test_longjmp_exc') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) @@ -4665,7 +4657,6 @@ def test_gcc_unmangler(self): def test_lua(self): if self.emcc_args is None: return self.skip('requires emcc') if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: make this work') - if os.environ.get('EMCC_FAST_COMPILER') == '1': Settings.DISABLE_EXCEPTION_CATCHING = 1 for aggro in ([0, 1] if Settings.ASM_JS and '-O2' in self.emcc_args else [0]): print aggro @@ -4686,7 +4677,6 @@ def get_freetype(self): def test_freetype(self): if self.emcc_args is None: return self.skip('requires emcc') if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: Figure out and try to fix') - if os.environ.get('EMCC_FAST_COMPILER') == '1': Settings.DISABLE_EXCEPTION_CATCHING = 1 assert 'asm2g' in test_modes if self.run_name == 'asm2g': @@ -4832,7 +4822,6 @@ def test(): def test_poppler(self): if self.emcc_args is None: return self.skip('very slow, we only do this in emcc runs') - if os.environ.get('EMCC_FAST_COMPILER') == '1': Settings.DISABLE_EXCEPTION_CATCHING = 1 Settings.CORRECT_OVERFLOWS = 1 Settings.CORRECT_SIGNS = 1 @@ -5039,7 +5028,6 @@ def test_lifetime(self): # to process. def test_cases(self): if Building.LLVM_OPTS: return self.skip("Our code is not exactly 'normal' llvm assembly") - if os.environ.get('EMCC_FAST_COMPILER') == '1': Settings.DISABLE_EXCEPTION_CATCHING = 1 emcc_args = self.emcc_args From 4757fe25341d47bd8be2330c9609be452240dc16 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 21 Jan 2014 14:37:07 -0800 Subject: [PATCH 53/85] deprecate -O3 in preparation for using it for heavier js opts --- emcc | 23 ++++------------------- tools/shared.py | 6 ------ 2 files changed, 4 insertions(+), 25 deletions(-) diff --git a/emcc b/emcc index 030e87b0658da..5a76409597fdc 100755 --- a/emcc +++ b/emcc @@ -146,7 +146,7 @@ Options that are modified or new in %s include: This is the recommended setting when you want a reasonably optimized build that is generated as - quickly as possible (it is much faster than -O2). + quickly as possible (it builds much faster than -O2). (Note: for details on the affects of different opt levels, see apply_opt_level() in @@ -158,20 +158,8 @@ Options that are modified or new in %s include: time in return for the smallest and fastest output. - -O3 As -O2, plus dangerous optimizations that may - break the generated code! This adds - - -s FORCE_ALIGNED_MEMORY=1 - -s DOUBLE_MODE=0 - -s PRECISE_I64_MATH=0 - --closure 1 - --llvm-lto 3 - - This is not recommended at all. A better idea - is to try each of these separately on top of - -O2 to see what works. See the wiki and - src/settings.js (for the -s options) for more - information. + For tips on optimizing your code, see + https://github.com/kripken/emscripten/wiki/Optimizing-Code -s OPTION=VALUE JavaScript code generation option passed into the emscripten compiler. For the @@ -267,8 +255,7 @@ Options that are modified or new in %s include: code size and may in some cases increase runtime speed (although the opposite can also occur). Note that it takes time to run, and - may require some changes to the code. This - is run by default in -O3. + may require some changes to the code. In asm.js mode, closure will only be used on the 'shell' code around the compiled code (the @@ -1037,9 +1024,7 @@ try: if js_opts is None: js_opts = opt_level >= 2 if llvm_opts is None: llvm_opts = LLVM_OPT_LEVEL[opt_level] - if llvm_lto is None and opt_level >= 3: llvm_lto = 3 if opt_level == 0: debug_level = 4 - if closure is None and opt_level == 3: closure = True if llvm_lto is None and bind: logging.debug('running lto for embind') # XXX this is a workaround for a pointer issue diff --git a/tools/shared.py b/tools/shared.py index e05a5f7a7fb21..e28a66c3d2e1e 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -809,12 +809,6 @@ def apply_opt_level(self, opt_level, noisy=False): self.attrs['DISABLE_EXCEPTION_CATCHING'] = 1 self.attrs['RELOOP'] = 1 self.attrs['ALIASING_FUNCTION_POINTERS'] = 1 - if opt_level >= 3: - # Aside from these, -O3 also runs closure compiler and llvm lto - self.attrs['FORCE_ALIGNED_MEMORY'] = 1 - self.attrs['DOUBLE_MODE'] = 0 - self.attrs['PRECISE_I64_MATH'] = 0 - if noisy: logging.warning('Applying some potentially unsafe optimizations! (Use -O2 if this fails.)') def __getattr__(self, attr): if attr in self.attrs: From e1f18ae55d269799f35759fe6ebe8bd09da41646 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 21 Jan 2014 16:07:44 -0800 Subject: [PATCH 54/85] document what -O3 will be --- emcc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/emcc b/emcc index 5a76409597fdc..a5d202309ce6e 100755 --- a/emcc +++ b/emcc @@ -158,6 +158,9 @@ Options that are modified or new in %s include: time in return for the smallest and fastest output. + -O3 As -O2, plus additional optimizations that can + take a significant amount of compilation time. + For tips on optimizing your code, see https://github.com/kripken/emscripten/wiki/Optimizing-Code From 8a0a84373d188f9bcf60ca0121706305d4530dd3 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 21 Jan 2014 17:43:40 -0800 Subject: [PATCH 55/85] update other.test_emcc --- tests/test_other.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/test_other.py b/tests/test_other.py index f69eced2715d0..53128391fc6fc 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -134,13 +134,13 @@ def test_emcc(self): (['-o', 'something.js', '-O2'], 2, None, 0, 1), (['-o', 'something.js', '-O2', '-g'], 2, None, 0, 0), (['-o', 'something.js', '-Os'], 2, None, 0, 1), - (['-o', 'something.js', '-O3', '-s', 'ASM_JS=0'], 3, None, 1, 1), + (['-o', 'something.js', '-O3', '-s', 'ASM_JS=0'], 3, None, 0, 1), # and, test compiling to bitcode first (['-o', 'something.bc'], 0, [], 0, 0), (['-o', 'something.bc', '-O0'], 0, [], 0, 0), (['-o', 'something.bc', '-O1'], 1, ['-O1'], 0, 0), (['-o', 'something.bc', '-O2'], 2, ['-O2'], 0, 0), - (['-o', 'something.bc', '-O3'], 3, ['-O3', '-s', 'ASM_JS=0'], 1, 0), + (['-o', 'something.bc', '-O3'], 3, ['-O3', '-s', 'ASM_JS=0'], 0, 0), (['-O1', '-o', 'something.bc'], 1, [], 0, 0), ]: print params, opt_level, bc_params, closure, has_malloc @@ -157,7 +157,6 @@ def test_emcc(self): print '....', bc_args output = Popen(bc_args, stdout=PIPE, stderr=PIPE).communicate() assert os.path.exists('something.js'), output[1] - assert ('Applying some potentially unsafe optimizations!' in output[1]) == (opt_level >= 3), 'unsafe warning should appear in opt >= 3' self.assertContained('hello, world!', run_js('something.js')) # Verify optimization level etc. in the generated code @@ -196,7 +195,6 @@ def test_emcc(self): (['-O2', '-g3'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'registerize is cancelled by -g3'), #(['-O2', '-g4'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'same as -g3 for now'), (['-s', 'INLINING_LIMIT=0'], lambda generated: 'function _dump' in generated, 'no inlining without opts'), - (['-O3', '-s', 'INLINING_LIMIT=0', '--closure', '0'], lambda generated: 'function _dump' not in generated, 'lto/inlining'), (['-Os', '--llvm-lto', '1', '-s', 'ASM_JS=0', '-g2'], lambda generated: 'function _dump' in generated, '-Os disables inlining'), (['-s', 'USE_TYPED_ARRAYS=0'], lambda generated: 'new Int32Array' not in generated, 'disable typed arrays'), (['-s', 'USE_TYPED_ARRAYS=1'], lambda generated: 'IHEAPU = ' in generated, 'typed arrays 1 selected'), From 68d6af077f116819bddb5797d9f8c8bd37998bff Mon Sep 17 00:00:00 2001 From: Ryan Kelly Date: Mon, 16 Dec 2013 01:47:46 +1100 Subject: [PATCH 56/85] Registerize based on full liveness analysis. This does a much-more-expensive but much-more-thorough registerization pass based a live-variable analysis for each function. azakai: perform this on -O3 and above --- emcc | 5 +- tools/js-optimizer.js | 1061 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1065 insertions(+), 1 deletion(-) diff --git a/emcc b/emcc index a5d202309ce6e..77a168e93e5cb 100755 --- a/emcc +++ b/emcc @@ -2017,7 +2017,10 @@ try: js_optimizer_extra_info['sizeToOutline'] = shared.Settings.OUTLINING_LIMIT if (not closure or shared.Settings.ASM_JS) and shared.Settings.RELOOP and debug_level < 3: - js_optimizer_queue += ['registerize'] + if shared.Settings.ASM_JS and opt_level >= 3: + js_optimizer_queue += ['registerizeHarder'] + else: + js_optimizer_queue += ['registerize'] if opt_level > 0: if debug_level < 2 and shared.Settings.ASM_JS: js_optimizer_queue += ['minifyNames'] diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 98cb4c490a043..39456d409fe2b 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -2066,6 +2066,1066 @@ function registerize(ast) { }); } + +// Assign variables to 'registers', coalescing them onto a smaller number of shared +// variables. +// +// This does the same job as 'registerize' above, but burns a lot more cycles trying +// to reduce the total number of register variables. Key points about the operation: +// +// * we decompose the AST into a flow graph and perform a full liveness +// analysis, to determine which variables are live at each point. +// +// * variables that are live concurrently are assigned to different registers. +// +// * variables that are linked via 'x=y' style statements are assigned the same +// register if possible, so that the redundant assignment can be removed. +// (e.g. assignments used to pass state around through loops). +// +// * any code that cannot be reached through the flow-graph is removed. +// (e.g. redundant break statements like 'break L123; break;'). +// +// * any assignments that we can prove are not subsequently used are removed. +// (e.g. unnecessary assignments to the 'label' variable). +// +function registerizeHarder(ast) { + assert(asm); + + traverseGeneratedFunctions(ast, function(fun) { + + var asmData = normalizeAsm(fun); + + var localVars = asmData.vars; + for (var name in asmData.params) { + localVars[name] = asmData.params[name]; + } + + // Utilities for allocating register variables. + // We need distinct register pools for each type of variable. + + var allRegsByType = [{}, {}, {}]; + var regPrefixByType = ['i', 'd', 'f']; + var nextReg = 1; + + function createReg(forName) { + // Create a new register of type suitable for the given variable name. + var allRegs = allRegsByType[localVars[forName]]; + reg = nextReg++; + allRegs[reg] = regPrefixByType[localVars[forName]] + reg; + return reg; + } + + // Traverse the tree in execution order and synthesize a basic flow-graph. + // It's convenient to build a kind of "dual" graph where the nodes identify + // the junctions between blocks at which control-flow may branch, and each + // basic block is an edge connecting two such junctions. + // For each junction we store: + // * set of blocks that originate at the junction + // * set of blocks that terminate at the junction + // For each block we store: + // * a single entry junction + // * a single exit junction + // * any preconditions satisfied at entry to the block + // * a 'use' and 'kill' set of names for the block + // * full sequence of 'name' and 'assign' nodes in the block + // * whether each such node appears as part of a larger expression + // (and therefore cannot be safely eliminated) + + var junctions = []; + var blocks = []; + var curEntryJunction = null; + var nextBasicBlock = null; + var isInExpr = 0; + var activeLabels = [{}]; + var nextLoopLabel = null; + + var ENTRY_JUNCTION = 0; + var EXIT_JUNCTION = 1; + var ENTRY_BLOCK = 0; + + function addJunction() { + // Create a new junction, without inserting it into the graph. + // This is useful for e.g. pre-allocating an exit node. + var id = junctions.length; + junctions[id] = {id: id, inblocks: {}, outblocks: {}}; + return id; + } + + function markJunction(id) { + // Mark current traversal location as a junction. + // This makes a new basic block exiting at this position. + if (id === undefined || id === null) { + id = addJunction(); + } + joinJunction(id); + return id; + } + + function setJunction(id) { + // Set the next entry junction to the given id. + // This can be used to enter at a previously-declared point. + assert(nextBasicBlock.nodes.length === 0, 'refusing to abandon an in-progress basic block') + curEntryJunction = id; + } + + function joinJunction(id) { + // Complete the pending basic block by exiting at this position. + // This can be used to exit at a previously-declared point. + if (curEntryJunction !== null) { + nextBasicBlock.id = blocks.length; + nextBasicBlock.entry = curEntryJunction; + nextBasicBlock.exit = id; + junctions[curEntryJunction].outblocks[nextBasicBlock.id] = 1; + junctions[id].inblocks[nextBasicBlock.id] = 1; + blocks.push(nextBasicBlock); + } + nextBasicBlock = { id: null, entry: null, exit: null, pre: {}, nodes: [], isexpr: [], use: {}, kill: {} }; + curEntryJunction = id; + return id; + } + + function pushActiveLabels(onContinue, onBreak) { + // Push the target junctions for continuing/breaking a loop. + // This should be called before traversing into a loop. + var newLabels = copy(activeLabels[activeLabels.length-1]); + newLabels[null] = [onContinue, onBreak]; + if (nextLoopLabel) { + newLabels[nextLoopLabel] = [onContinue, onBreak]; + nextLoopLabel = null; + } + activeLabels.push(newLabels); + } + + function popActiveLabels() { + // Pop the target junctions for continuing/breaking a loop. + // This should be called after traversing into a loop. + activeLabels.pop(); + } + + function markNonLocalJump(type, label) { + // Complete a block via 'return', 'break' or 'continue'. + // This joins the targetted junction and then sets the current junction to null. + // Any code traversed before we get back an existing junction is dead code. + if (type === 'return') { + joinJunction(EXIT_JUNCTION); + } else { + label = label ? label : null; + var targets = activeLabels[activeLabels.length-1][label]; + assert(targets, 'jump to unknown label'); + if (type === 'continue') { + joinJunction(targets[0]); + } else if (type === 'break') { + joinJunction(targets[1]); + } else { + assert(false, 'unknown jump node type'); + } + } + setJunction(null); + } + + function addUseNode(node) { + // Mark a use of the given name node in the current basic block. + assert(node[0] === 'name', 'not a use node'); + var name = node[1]; + if (name in localVars) { + nextBasicBlock.nodes.push(node); + nextBasicBlock.isexpr.push(isInExpr); + if (!nextBasicBlock.kill[name]) { + nextBasicBlock.use[name] = 1; + } + } + } + + function addKillNode(node) { + // Mark an assignment to the given name node in the current basic block. + assert(node[0] === 'assign', 'not a kill node'); + assert(node[1] === true, 'not a kill node'); + assert(node[2][0] === 'name', 'not a kill node'); + var name = node[2][1]; + if (name in localVars) { + nextBasicBlock.nodes.push(node); + nextBasicBlock.isexpr.push(isInExpr); + nextBasicBlock.kill[name] = 1; + } + } + + function lookThroughCasts(node) { + // Look through value-preserving casts, like "x | 0" => "x" + if (node[0] === 'binary' && node[1] === '|') { + if (node[3][0] === 'num' && node[3][1] === 0) { + return lookThroughCasts(node[2]); + } + } + return node; + } + + function addPreCondTrue(node) { + // Add pre-conditions implied by truth of the + // given node to the current basic block. + assert(nextBasicBlock.nodes.length === 0, 'cant add preconditions to an in-progress basic block') + if (node[0] === 'binary' && node[1] === '==') { + var lhs = lookThroughCasts(node[2]); + var rhs = lookThroughCasts(node[3]); + if (lhs[0] === 'name' && rhs[0] === 'num') { + nextBasicBlock.pre[lhs[1]] = ['==', rhs[1]]; + } + } + } + + function addPreCondFalse(node) { + // Add pre-conditions implied by falsehood of the + // given node to the current basic block. + assert(nextBasicBlock.nodes.length === 0, 'cant add preconditions to an in-progress basic block') + if (node[0] === 'binary' && node[1] === '==') { + var lhs = lookThroughCasts(node[2]); + var rhs = lookThroughCasts(node[3]); + if (lhs[0] === 'name' && rhs[0] === 'num') { + nextBasicBlock.pre[lhs[1]] = ['!=', rhs[1]]; + } + } + } + + function isTrueNode(node) { + // Check if the given node is statically truthy. + return (node[0] === 'num' && node[1] != 0); + } + + function isFalseNode(node) { + // Check if the given node is statically falsy. + return (node[0] === 'num' && node[1] == 0); + } + + function morphNode(node, newNode) { + // In-place morph a node into some other type of node. + var i = 0; + while (i < node.length && i < newNode.length) { + node[i] = newNode[i]; + i++; + } + while (i < newNode.length) { + node.push(newNode[i]); + i++; + } + if (i < node.length) { + node.splice(i); + } + } + + function buildFlowGraph(node) { + // Recursive function to build up the flow-graph. + // It walks the tree in execution order, calling the above state-management + // functions at appropriate points in the traversal. + var type = node[0]; + + // Any code traversed without an active entry junction must be dead, + // as the resulting block could never be entered. Let's remove it. + if (curEntryJunction === null && junctions.length > 0) { + morphNode(node, ['block', []]); + return; + } + + // Traverse each node type according to its particular control-flow semantics. + switch (type) { + case 'defun': + var jEntry = markJunction(); + assert(jEntry === ENTRY_JUNCTION); + var jExit = addJunction(); + assert(jExit === EXIT_JUNCTION); + for (var i = 0; i < node[3].length; i++) { + buildFlowGraph(node[3][i]); + } + joinJunction(jExit); + break; + case 'if': + isInExpr++; + buildFlowGraph(node[1]); + isInExpr--; + var jEnter = markJunction(); + addPreCondTrue(node[1]); + if (node[2]) { + buildFlowGraph(node[2]); + } + var jExit = markJunction(); + setJunction(jEnter); + addPreCondFalse(node[1]); + if (node[3]) { + buildFlowGraph(node[3]); + } + joinJunction(jExit); + break; + case 'conditional': + isInExpr++; + buildFlowGraph(node[1]); + var jEnter = markJunction(); + addPreCondTrue(node[1]); + if (node[2]) { + buildFlowGraph(node[2]); + } + var jExit = markJunction(); + setJunction(jEnter); + addPreCondFalse(node[1]); + if (node[3]) { + buildFlowGraph(node[3]); + } + joinJunction(jExit); + isInExpr--; + break; + case 'while': + // Special-case "while (1) {}" to use fewer junctions, + // since emscripten generates a lot of these. + if (isTrueNode(node[1])) { + var jLoop = markJunction(); + var jExit = addJunction(); + pushActiveLabels(jLoop, jExit); + buildFlowGraph(node[2]); + popActiveLabels(); + joinJunction(jLoop); + setJunction(jExit); + } else { + var jCond = markJunction(); + var jLoop = addJunction(); + var jExit = addJunction(); + isInExpr++; + buildFlowGraph(node[1]); + isInExpr--; + joinJunction(jLoop); + pushActiveLabels(jCond, jExit); + addPreCondTrue(node[1]); + buildFlowGraph(node[2]); + popActiveLabels(); + joinJunction(jCond); + // An empty basic-block linking condition exit to loop exit. + setJunction(jLoop); + joinJunction(jExit); + } + break; + case 'do': + // Special-case "do {} while (1)" and "do {} while (0)" to use + // fewer junctions, since emscripten generates a lot of these. + if (isFalseNode(node[1])) { + var jExit = addJunction(); + pushActiveLabels(jExit, jExit); + buildFlowGraph(node[2]); + popActiveLabels(); + joinJunction(jExit); + } else if (isTrueNode(node[1])) { + var jLoop = markJunction(); + var jExit = addJunction(); + pushActiveLabels(jLoop, jExit); + buildFlowGraph(node[2]); + popActiveLabels(); + joinJunction(jLoop); + setJunction(jExit); + } else { + var jLoop = markJunction(); + var jCond = addJunction(); + var jCondExit = addJunction(); + var jExit = addJunction(); + pushActiveLabels(jCond, jExit); + buildFlowGraph(node[2]); + popActiveLabels(); + joinJunction(jCond); + isInExpr++; + buildFlowGraph(node[1]); + isInExpr--; + joinJunction(jCondExit); + joinJunction(jLoop); + setJunction(jCondExit); + joinJunction(jExit) + } + break; + case 'for': + var jTest = addJunction(); + var jBody = addJunction(); + var jStep = addJunction(); + var jExit = addJunction(); + buildFlowGraph(node[1]); + joinJunction(jTest); + isInExpr++; + buildFlowGraph(node[2]); + isInExpr--; + joinJunction(jBody); + pushActiveLabels(jStep, jExit); + buildFlowGraph(node[4]); + popActiveLabels(); + joinJunction(jStep); + buildFlowGraph(node[3]); + joinJunction(jTest); + setJunction(jBody); + joinJunction(jExit); + break; + case 'label': + assert(node[2][0] in BREAK_CAPTURERS, 'label on non-loop, non-switch statement') + nextLoopLabel = node[1]; + buildFlowGraph(node[2]); + break; + case 'switch': + // This is horrific. It need to capture the default flow-through + // logic of sequential case bodies, as well as the theoretical + // sequential evaluation of each case clause. + // TODO: simplify based on asmjs switch-statement restrictions. + isInExpr++; + buildFlowGraph(node[1]); + isInExpr--; + var jCheckExit = markJunction(); + var jExit = addJunction(); + pushActiveLabels(null, jExit); + // Process all cases as a sequential chain of checks. + // Process all case bodies as one big flow-through statement. + // They might break themselves out of it but this implements the + // default fall-through case logic. + var hasDefault = false; + var jPrevCaseExit = jCheckExit; + var jPrevBodyExit = jCheckExit; + for (var i=0; i=0 ; j--) { + var node = block.nodes[j]; + if (node[0] === 'name') { + var name = node[1]; + live[name] = 1; + use[name] = j; + if (lastUseLoc[name] === undefined) { + lastUseLoc[name] = j; + firstDeadLoc[name] = j; + } + } else { + var name = node[2][1]; + // We only keep assignments if they will be subsequently used. + if (name in live) { + kill[name] = 1; + delete use[name]; + delete live[name]; + firstDeadLoc[name] = j; + if (lastUseLoc[name] === undefined) { + lastUseLoc[name] = j; + } + if (lastKillLoc[name] === undefined) { + lastKillLoc[name] = j; + } + // If it's an "x=y" and "y" is not live, then we can create a + // flow-through link from "y" to "x". If not then there's no + // flow-through link for "x". + var oldLink = link[name]; + if (oldLink) { + delete link[name]; + if (node[3][0] === 'name') { + if (node[3][1] in localVars) { + link[node[3][1]] = oldLink; + } + } + } + } else { + // The result of this assignment is never used, so delete it. + // We may need to keep the RHS for its value or its side-effects. + function removeUnusedNodes(j, n) { + for (var name in lastUseLoc) { + lastUseLoc[name] -= n; + } + for (var name in lastKillLoc) { + lastKillLoc[name] -= n; + } + for (var name in firstDeadLoc) { + firstDeadLoc[name] -= n; + } + block.nodes.splice(j, n); + block.isexpr.splice(j, n); + } + if (block.isexpr[j] || hasSideEffects(node[3])) { + morphNode(node, node[3]); + removeUnusedNodes(j, 1); + } else { + var numUsesInExpr = 0; + traverse(node[3], function(node, type) { + if (type === 'name' && node[1] in localVars) { + numUsesInExpr++; + } + }); + morphNode(node, ['block', []]); + j = j - numUsesInExpr; + removeUnusedNodes(j, 1 + numUsesInExpr); + } + } + } + } + block.use = use; + block.kill = kill; + block.link = link; + block.lastUseLoc = lastUseLoc; + block.firstDeadLoc = firstDeadLoc; + block.lastKillLoc = lastKillLoc; + } + + var jWorklistMap = { EXIT_JUNCTION: 1 }; + var jWorklist = [EXIT_JUNCTION]; + var bWorklistMap = {}; + var bWorklist = []; + + // Be sure to visit every junction at least once. + // This avoids missing some vars because we disconnected them + // when processing the labelled jumps. + for (var i = junctions.length - 1; i >= EXIT_JUNCTION; i--) { + jWorklistMap[i] = 1; + jWorklist.push(i); + } + + while (jWorklist.length > 0) { + // Iterate on just the junctions until we get stable live sets. + // The first run of this loop will grow the live sets to their maximal size. + // Subsequent runs will shrink them based on eliminated in-block uses. + while (jWorklist.length > 0) { + var junc = junctions[jWorklist.pop()]; + delete jWorklistMap[junc.id]; + var oldLive = junc.live || null; + analyzeJunction(junc); + if (!sortedJsonCompare(oldLive, junc.live)) { + // Live set changed, updated predecessor blocks and junctions. + for (var b in junc.inblocks) { + if (!(b in bWorklistMap)) { + bWorklistMap[b] = 1; + bWorklist.push(b); + } + var jPred = blocks[b].entry; + if (!(jPred in jWorklistMap)) { + jWorklistMap[jPred] = 1; + jWorklist.push(jPred); + } + } + } + } + // Now update the blocks based on the calculated live sets. + while (bWorklist.length > 0) { + var block = blocks[bWorklist.pop()]; + delete bWorklistMap[block.id]; + var oldUse = block.use; + analyzeBlock(block); + if (!sortedJsonCompare(oldUse, block.use)) { + // The use set changed, re-process the entry junction. + if (!(block.entry in jWorklistMap)) { + jWorklistMap[block.entry] = 1; + jWorklist.push(block.entry); + } + } + } + } + + // Insist that all function parameters are alive at function entry. + // This ensures they will be assigned independent registers, even + // if they happen to be unused. + + for (var name in asmData.params) { + junctions[ENTRY_JUNCTION].live[name] = 1; + } + + // For variables that are live at one or more junctions, we assign them + // a consistent register for the entire scope of the function. Find pairs + // of variable that cannot use the same register (the "conflicts") as well + // as pairs of variables that we'd like to have share the same register + // (the "links"). + + var junctionVariables = {}; + + function initializeJunctionVariable(name) { + junctionVariables[name] = { conf: {}, link: {}, excl: {}, reg: null }; + } + + for (var i = 0; i < junctions.length; i++) { + var junc = junctions[i]; + for (var name in junc.live) { + if (!junctionVariables[name]) initializeJunctionVariable(name); + // It conflicts with all other names live at this junction. + for (var otherName in junc.live) { + if (otherName == name) continue; + junctionVariables[name].conf[otherName] = 1; + } + for (var b in junc.outblocks) { + // It conflits with any output vars of successor blocks, + // if they're assigned before it goes dead in that block. + block = blocks[b]; + var jSucc = junctions[block.exit]; + for (var otherName in jSucc.live) { + if (junc.live[otherName]) continue; + if (block.lastKillLoc[otherName] < block.firstDeadLoc[name]) { + if (!junctionVariables[otherName]) initializeJunctionVariable(otherName); + junctionVariables[name].conf[otherName] = 1; + junctionVariables[otherName].conf[name] = 1; + } + } + // It links with any linkages in the outgoing blocks. + var linkName = block.link[name]; + if (linkName && linkName !== name) { + if (!junctionVariables[linkName]) initializeJunctionVariable(linkName); + junctionVariables[name].link[linkName] = 1; + junctionVariables[linkName].link[name] = 1; + } + } + } + } + + // Attempt to sort the junction variables to heuristically reduce conflicts. + // Simple starting point: handle the most-conflicted variables first. + // This seems to work pretty well. + + var sortedJunctionVariables = keys(junctionVariables); + sortedJunctionVariables.sort(function(name1, name2) { + var jv1 = junctionVariables[name1]; + var jv2 = junctionVariables[name2]; + if (jv1.numConfs === undefined) { + jv1.numConfs = setSize(jv1.conf); + } + if (jv2.numConfs === undefined) { + jv2.numConfs = setSize(jv2.conf); + } + return jv2.numConfs - jv1.numConfs; + }); + + // We can now assign a register to each junction variable. + // Process them in order, trying available registers until we find + // one that works, and propagating the choice to linked/conflicted + // variables as we go. + + function tryAssignRegister(name, reg) { + // Try to assign the given register to the given variable, + // and propagate that choice throughout the graph. + // Returns true if successful, false if there was a conflict. + var jv = junctionVariables[name]; + if (jv.reg !== null) { + return jv.reg === reg; + } + if (jv.excl[reg]) { + return false; + } + jv.reg = reg; + // Exclude use of this register at all conflicting variables. + for (var confName in jv.conf) { + junctionVariables[confName].excl[reg] = 1; + } + // Try to propagate it into linked variables. + // It's not an error if we can't. + for (var linkName in jv.link) { + tryAssignRegister(linkName, reg); + } + return true; + } + + NEXTVARIABLE: + for (var i = 0; i < sortedJunctionVariables.length; i++) { + var name = sortedJunctionVariables[i]; + // It may already be assigned due to linked-variable propagation. + if (junctionVariables[name].reg !== null) { + continue NEXTVARIABLE; + } + // Try to use existing registers first. + var allRegs = allRegsByType[localVars[name]]; + for (var reg in allRegs) { + if (tryAssignRegister(name, reg)) { + continue NEXTVARIABLE; + } + } + // They're all taken, create a new one. + tryAssignRegister(name, createReg(name)); + } + + // Each basic block can now be processed in turn. + // There may be internal-use-only variables that still need a register + // assigned, but they can be treated just for this block. We know + // that all inter-block variables are in a good state thanks to + // junction variable consistency. + + for (var i = 0; i < blocks.length; i++) { + var block = blocks[i]; + if (block.nodes.length === 0) continue; + var jEnter = junctions[block.entry]; + var jExit = junctions[block.exit]; + // Calculate the full internal liveness states for this block. + var liveness = [{}]; + for (var name in jExit.live) { + liveness[0][name] = 1; + } + for (var j=block.nodes.length-1; j>=0; j--) { + var node = block.nodes[j]; + liveness.unshift(copy(liveness[0])); + if (node[0] === 'assign') { + var name = node[2][1]; + delete liveness[0][name]; + } else if (node[0] === 'name') { + var name = node[1]; + liveness[0][name] = 1; + } else { + assert(false, 'unexpected node type: ' + node[0]); + } + } + assert(liveness.length == block.nodes.length + 1); + assert(setSize(setSub(liveness[0], jEnter.live)) == 0); + // Mark the point at which each output reg gets assigned. + // Variables that live past this point must not be assigned + // to that register. + var outputAssignLoc = {}; + var outputVars = {}; + for (var name in jExit.live) { + var reg = junctionVariables[name].reg; + assert(reg !== null, 'output variable doesnt have a register'); + outputAssignLoc[reg] = block.lastKillLoc[name]; + outputVars[reg] = name; + } + // Scan through in execution order, allocating registers on demand. + // Be careful to avoid conflicts with the output registers. + // We consume free registers in last-used order, which helps to + // eliminate "x=y" assignments that are the last use of "y". + var assignedRegs = {}; + var freeRegsByType = copy(allRegsByType); + // Begin with all live vars assigned per the entry-point. + for (var name in liveness[0]) { + var reg = junctionVariables[name].reg; + assert(reg !== null, 'input variable doesnt have a register'); + assignedRegs[name] = reg; + delete freeRegsByType[localVars[name]][reg]; + } + for (var j = 0; j < freeRegsByType.length; j++) { + freeRegsByType[j] = keys(freeRegsByType[j]); + } + // Scan through the nodes in sequence, modifying each node in-place + // and freeing registers according to the calculated liveness info. + for (var j = 0; j < block.nodes.length; j++) { + var node = block.nodes[j]; + var name = node[0] === 'assign' ? node[2][1] : node[1]; + var allRegs = allRegsByType[localVars[name]]; + var freeRegs = freeRegsByType[localVars[name]]; + var reg = assignedRegs[name]; + if (node[0] === 'name') { + // A use. It should already be in a register. + assert(liveness[j][name], 'use node, but name was not alive?') + assert(reg, 'live variable did not have a reg?') + node[1] = allRegs[reg]; + } else { + // A kill. This should assign it a new register. + assert(!liveness[j][name], 'kill node, but name was alive?') + assert(!reg, 'non-live variable still had a reg?') + if (name in jExit.live && j === block.lastKillLoc[name]) { + // Assignment to an output variable, must use pre-assigned reg. + reg = junctionVariables[name].reg; + assignedRegs[name] = reg; + for (var k = freeRegs.length - 1; k >= 0; k--) { + if (freeRegs[k] === reg) { + freeRegs.splice(k, 1); + break; + } + } + } else { + // Try to use one of the existing free registers. + // It must not conflict with an output register. + for (var k = freeRegs.length - 1; k >= 0; k--) { + reg = freeRegs[k]; + // Check for conflict with output registers. + if (block.lastUseLoc[name] > outputAssignLoc[reg]) { + if (name !== outputVars[reg]) { + continue; + } + } + // Found one! + assignedRegs[name] = reg; + freeRegs.splice(k, 1); + break; + } + // If we didn't find a suitable register, create a new one. + if (!assignedRegs[name]) { + reg = createReg(name); + assignedRegs[name] = reg; + } + } + // Modify assignment to use the new name. + // If we happen to create a "x=x" type do-nothing assignment, + // we can safely morph it into a no-op. + node[2][1] = allRegs[reg]; + if (node[3][0] === 'name' && node[3][1] === node[2][1]) { + morphNode(node, ['block', []]); + } + } + // Free the reg if it's not live in the next step. + if (!liveness[j+1][name]) { + delete assignedRegs[name]; + freeRegs.push(reg); + } + } + } + + // Assign registers to function params based on entry junction + + var paramRegs = {} + if (fun[2]) { + for (var i = 0; i < fun[2].length; i++) { + var allRegs = allRegsByType[localVars[fun[2][i]]]; + fun[2][i] = allRegs[junctionVariables[fun[2][i]].reg]; + paramRegs[fun[2][i]] = 1; + } + } + + // That's it! + // Re-construct the function with appropriate variable definitions. + + var finalAsmData = { + params: {}, + vars: {}, + inlines: asmData.inlines, + }; + for (var i = 1; i < nextReg; i++) { + var reg; + for (var type=0; type Date: Tue, 21 Jan 2014 17:03:36 -0800 Subject: [PATCH 57/85] rename a variable to our conventions --- tools/js-optimizer.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 39456d409fe2b..a66fe346bbe18 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -2133,7 +2133,7 @@ function registerizeHarder(ast) { var junctions = []; var blocks = []; - var curEntryJunction = null; + var currEntryJunction = null; var nextBasicBlock = null; var isInExpr = 0; var activeLabels = [{}]; @@ -2165,22 +2165,22 @@ function registerizeHarder(ast) { // Set the next entry junction to the given id. // This can be used to enter at a previously-declared point. assert(nextBasicBlock.nodes.length === 0, 'refusing to abandon an in-progress basic block') - curEntryJunction = id; + currEntryJunction = id; } function joinJunction(id) { // Complete the pending basic block by exiting at this position. // This can be used to exit at a previously-declared point. - if (curEntryJunction !== null) { + if (currEntryJunction !== null) { nextBasicBlock.id = blocks.length; - nextBasicBlock.entry = curEntryJunction; + nextBasicBlock.entry = currEntryJunction; nextBasicBlock.exit = id; - junctions[curEntryJunction].outblocks[nextBasicBlock.id] = 1; + junctions[currEntryJunction].outblocks[nextBasicBlock.id] = 1; junctions[id].inblocks[nextBasicBlock.id] = 1; blocks.push(nextBasicBlock); } nextBasicBlock = { id: null, entry: null, exit: null, pre: {}, nodes: [], isexpr: [], use: {}, kill: {} }; - curEntryJunction = id; + currEntryJunction = id; return id; } @@ -2319,7 +2319,7 @@ function registerizeHarder(ast) { // Any code traversed without an active entry junction must be dead, // as the resulting block could never be entered. Let's remove it. - if (curEntryJunction === null && junctions.length > 0) { + if (currEntryJunction === null && junctions.length > 0) { morphNode(node, ['block', []]); return; } From c744bcec81b3c036f3a434573853baf1bd03d82d Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 21 Jan 2014 17:09:01 -0800 Subject: [PATCH 58/85] avoid nonstandard use of splice --- tools/js-optimizer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index a66fe346bbe18..aa1f4a0fe62ba 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -2306,8 +2306,8 @@ function registerizeHarder(ast) { node.push(newNode[i]); i++; } - if (i < node.length) { - node.splice(i); + if (node.length > newNode.length) { + node.length = newNode.length; } } From 55cadd9dc575f27c46a2c8d9775ada8d1d51ebc4 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 21 Jan 2014 17:23:38 -0800 Subject: [PATCH 59/85] fix typo --- tools/js-optimizer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index aa1f4a0fe62ba..5fd0f1381817b 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -2478,7 +2478,7 @@ function registerizeHarder(ast) { var jPrevCaseExit = jCheckExit; var jPrevBodyExit = jCheckExit; for (var i=0; i Date: Tue, 21 Jan 2014 17:39:43 -0800 Subject: [PATCH 60/85] assign to null to clear a local variable, avoid delete --- tools/js-optimizer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 5fd0f1381817b..b35da99d371c6 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -2670,8 +2670,8 @@ function registerizeHarder(ast) { junctions[block.exit].inblocks[block.id] = 1; } } - delete labelledBlocks; - delete labelledJumps; + labelledBlocks = null; + labelledJumps = null; // Do a backwards data-flow analysis to determine the set of live // variables at each junction, and to use this information to eliminate From 4741568741488536bfc910339a681057b1bb9c66 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 21 Jan 2014 18:00:18 -0800 Subject: [PATCH 61/85] switch benchmarks to -O3 --- tests/test_benchmark.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_benchmark.py b/tests/test_benchmark.py index 729512f3240aa..8e3dab7b543ea 100644 --- a/tests/test_benchmark.py +++ b/tests/test_benchmark.py @@ -106,7 +106,7 @@ def process(filename): final = os.path.dirname(filename) + os.path.sep + self.name+'_' + os.path.basename(filename) + '.js' try_delete(final) output = Popen([PYTHON, EMCC, filename, #'-O3', - '-O2', '-s', 'DOUBLE_MODE=0', '-s', 'PRECISE_I64_MATH=0', + '-O3', '-s', 'DOUBLE_MODE=0', '-s', 'PRECISE_I64_MATH=0', '--memory-init-file', '0', '--js-transform', 'python hardcode.py', '-s', 'TOTAL_MEMORY=128*1024*1024', #'--closure', '1', From 020fc37f70f985e090b24778fa3ba1d9f52f29b2 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 21 Jan 2014 18:24:32 -0800 Subject: [PATCH 62/85] add benchmarker option to vary native compiler flags --- tests/test_benchmark.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/test_benchmark.py b/tests/test_benchmark.py index 8e3dab7b543ea..1bf1c1d5ef898 100644 --- a/tests/test_benchmark.py +++ b/tests/test_benchmark.py @@ -51,20 +51,21 @@ def display(self, baseline=None): print class NativeBenchmarker(Benchmarker): - def __init__(self, name, cc, cxx): + def __init__(self, name, cc, cxx, args=['-O2']): self.name = name self.cc = cc self.cxx = cxx + self.args = args def build(self, parent, filename, args, shared_args, emcc_args, native_args, native_exec, lib_builder): self.parent = parent if lib_builder: native_args = native_args + lib_builder(self.name, native=True, env_init={ 'CC': self.cc, 'CXX': self.cxx }) if not native_exec: compiler = self.cxx if filename.endswith('cpp') else self.cc - process = Popen([compiler, '-O2', '-fno-math-errno', filename, '-o', filename+'.native'] + shared_args + native_args, stdout=PIPE, stderr=parent.stderr_redirect) + process = Popen([compiler, '-fno-math-errno', filename, '-o', filename+'.native'] + self.args + shared_args + native_args, stdout=PIPE, stderr=parent.stderr_redirect) output = process.communicate() if process.returncode is not 0: - print >> sys.stderr, "Building native executable with command '%s' failed with a return code %d!" % (' '.join([compiler, '-O2', filename, '-o', filename+'.native']), process.returncode) + print >> sys.stderr, "Building native executable with command failed" print "Output: " + output[0] else: shutil.copyfile(native_exec, filename + '.native') @@ -124,6 +125,7 @@ def run(self, args): benchmarkers = [ #NativeBenchmarker('clang', CLANG_CC, CLANG), NativeBenchmarker('clang-3.2', os.path.join(LLVM_3_2, 'clang'), os.path.join(LLVM_3_2, 'clang++')), + #NativeBenchmarker('clang-3.2-O3', os.path.join(LLVM_3_2, 'clang'), os.path.join(LLVM_3_2, 'clang++'), ['-O3']), #NativeBenchmarker('clang-3.3', os.path.join(LLVM_3_3, 'clang'), os.path.join(LLVM_3_3, 'clang++')), #NativeBenchmarker('clang-3.4', os.path.join(LLVM_3_4, 'clang'), os.path.join(LLVM_3_4, 'clang++')), #NativeBenchmarker('gcc', 'gcc', 'g++'), From 0e20a4f58d6b2fa97320e229b87d80cc6cacef67 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 21 Jan 2014 18:06:49 -0800 Subject: [PATCH 63/85] add asm3 test mode --- tests/runner.py | 2 +- tests/test_core.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/runner.py b/tests/runner.py index 7f0cbaedb9aa0..f59d5cb92879e 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -36,7 +36,7 @@ def path_from_root(*pathelems): # Core test runner class, shared between normal tests and benchmarks checked_sanity = False -test_modes = ['default', 'o1', 'o2', 'asm1', 'asm2', 'asm2f', 'asm2g', 'asm2x86', 's_0_0', 's_0_1'] +test_modes = ['default', 'o1', 'o2', 'asm1', 'asm2', 'asm3', 'asm2f', 'asm2g', 'asm2x86', 's_0_0', 's_0_1'] test_index = 0 class RunnerCore(unittest.TestCase): diff --git a/tests/test_core.py b/tests/test_core.py index 493ab77b1e391..311f33a006002 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -6343,7 +6343,7 @@ def setUp(self): if self.emcc_args is not None: Settings.load(self.emcc_args) Building.LLVM_OPTS = 0 - if '-O2' in self.emcc_args: + if '-O2' in self.emcc_args or '-O3' in self.emcc_args: Building.COMPILER_TEST_OPTS = [] # remove -g in -O2 tests, for more coverage #Building.COMPILER_TEST_OPTS += self.emcc_args for arg in self.emcc_args: @@ -6397,6 +6397,7 @@ def setUp(self): # asm.js asm1 = make_run("asm1", compiler=CLANG, emcc_args=["-O1"]) asm2 = make_run("asm2", compiler=CLANG, emcc_args=["-O2"]) +asm3 = make_run("asm3", compiler=CLANG, emcc_args=["-O3"]) asm2f = make_run("asm2f", compiler=CLANG, emcc_args=["-O2", "-s", "PRECISE_F32=1"]) if os.environ.get('EMCC_FAST_COMPILER') == '1': asm2g = make_run("asm2g", compiler=CLANG, emcc_args=["-O2", "-g", "-s", "ASSERTIONS=1", "--memory-init-file", "1", "-s", "SAFE_HEAP=1"]) From cc77e142465c3df7ac02dfe21560e23ab7d1df70 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 21 Jan 2014 19:20:54 -0800 Subject: [PATCH 64/85] do not registerizeHarder when outlining --- emcc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emcc b/emcc index 77a168e93e5cb..7460da619e307 100755 --- a/emcc +++ b/emcc @@ -2017,7 +2017,7 @@ try: js_optimizer_extra_info['sizeToOutline'] = shared.Settings.OUTLINING_LIMIT if (not closure or shared.Settings.ASM_JS) and shared.Settings.RELOOP and debug_level < 3: - if shared.Settings.ASM_JS and opt_level >= 3: + if shared.Settings.ASM_JS and opt_level >= 3 and shared.Settings.OUTLINING_LIMIT == 0: js_optimizer_queue += ['registerizeHarder'] else: js_optimizer_queue += ['registerize'] From e797f2af396f79acb824aaac8ac7befa7528dfa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Wed, 22 Jan 2014 12:26:21 +0200 Subject: [PATCH 65/85] Fix default vertex color to (1,1,1,1) and properly enable correct attribute streams when doing immediate mode rendering when GL_FFP_ONLY is set. --- src/library_gl.js | 11 +++++++++-- tests/s3tc.c | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/library_gl.js b/src/library_gl.js index bf4ccccee4664..edd5890e72317 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -3501,6 +3501,11 @@ var LibraryGL = { GLImmediate.enabledClientAttributes[name] = true; GLImmediate.setClientAttribute(name, size, type, 0, GLImmediate.rendererComponentPointer); GLImmediate.rendererComponentPointer += size * GL.byteSizeByType[type - GL.byteSizeByTypeRoot]; +#if GL_FFP_ONLY + // We can enable the correct attribute stream index immediately here, since the same attribute in each shader + // will be bound to this same index. + GL.enableVertexAttribArray(name); +#endif } else { GLImmediate.rendererComponents[name]++; } @@ -3760,7 +3765,7 @@ var LibraryGL = { this.texCoordLocations[i] = GLctx.getAttribLocation(this.program, aTexCoordPrefix + i); } } - + this.colorLocation = GLctx.getAttribLocation(this.program, 'a_color'); if (!useCurrProgram) { // Temporarily switch to the program so we can set our sampler uniforms early. var prevBoundProg = GLctx.getParameter(GLctx.CURRENT_PROGRAM); @@ -3772,6 +3777,9 @@ var LibraryGL = { GLctx.uniform1i(texSamplerLoc, texUnitID); } } + // The default color attribute value is not the same as the default for all other attribute streams (0,0,0,1) but (1,1,1,1), + // so explicitly set it right at start. + GLctx.vertexAttrib4fv(this.colorLocation, [1,1,1,1]); GLctx.useProgram(prevBoundProg); } @@ -3779,7 +3787,6 @@ var LibraryGL = { for (var i = 0; i < GLImmediate.MAX_TEXTURES; i++) { this.textureMatrixLocations[i] = GLctx.getUniformLocation(this.program, 'u_textureMatrix' + i); } - this.colorLocation = GLctx.getAttribLocation(this.program, 'a_color'); this.normalLocation = GLctx.getAttribLocation(this.program, 'a_normal'); this.modelViewLocation = GLctx.getUniformLocation(this.program, 'u_modelView'); diff --git a/tests/s3tc.c b/tests/s3tc.c index 5f7bee83dbf74..eb2ed3fc42399 100644 --- a/tests/s3tc.c +++ b/tests/s3tc.c @@ -133,7 +133,7 @@ int main(int argc, char *argv[]) glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); - // Render the last item using oldschool glBegin etc + // Render the last item using oldschool glBegin etc glBegin( GL_TRIANGLE_STRIP ); glTexCoord2i( 0, 0 ); glVertex3f( 100, 300, 0 ); glTexCoord2i( 1, 0 ); glVertex3f( 300, 300, 0 ); From 0b1df261d344ad53d82b8e86a17d619bc43eb3cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Wed, 22 Jan 2014 12:28:31 +0200 Subject: [PATCH 66/85] Add test for s3tc.c rendering when GL_FFP_ONLY flag is set. --- tests/s3tc.c | 2 +- tests/test_browser.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/s3tc.c b/tests/s3tc.c index eb2ed3fc42399..5f7bee83dbf74 100644 --- a/tests/s3tc.c +++ b/tests/s3tc.c @@ -133,7 +133,7 @@ int main(int argc, char *argv[]) glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); - // Render the last item using oldschool glBegin etc + // Render the last item using oldschool glBegin etc glBegin( GL_TRIANGLE_STRIP ); glTexCoord2i( 0, 0 ); glVertex3f( 100, 300, 0 ); glTexCoord2i( 1, 0 ); glVertex3f( 300, 300, 0 ); diff --git a/tests/test_browser.py b/tests/test_browser.py index eed18a3dc882d..9321f5293697a 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -1599,6 +1599,10 @@ def test_s3tc(self): shutil.copyfile(path_from_root('tests', 'screenshot.dds'), os.path.join(self.get_dir(), 'screenshot.dds')) self.btest('s3tc.c', reference='s3tc.png', args=['--preload-file', 'screenshot.dds', '-s', 'LEGACY_GL_EMULATION=1']) + def test_s3tc_ffp_only(self): + shutil.copyfile(path_from_root('tests', 'screenshot.dds'), os.path.join(self.get_dir(), 'screenshot.dds')) + self.btest('s3tc.c', reference='s3tc.png', args=['--preload-file', 'screenshot.dds', '-s', 'LEGACY_GL_EMULATION=1', '-s', 'GL_FFP_ONLY=1']) + def test_s3tc_crunch(self): shutil.copyfile(path_from_root('tests', 'ship.dds'), 'ship.dds') shutil.copyfile(path_from_root('tests', 'bloom.dds'), 'bloom.dds') From 96895e6dae8cf1314f8b4ce3fbab4e7c70ecaf4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Wed, 22 Jan 2014 18:59:37 +0200 Subject: [PATCH 67/85] Implement C-based events library for various HTML5 APIs. --- src/library_events.js | 1241 ++++++++++++++++++++++++++++ src/modules.js | 2 +- src/struct_info.json | 184 ++++- system/include/emscripten/events.h | 625 ++++++++++++++ tests/test_browser.py | 2 + tests/test_events.c | 382 +++++++++ 6 files changed, 2434 insertions(+), 2 deletions(-) create mode 100644 src/library_events.js create mode 100644 system/include/emscripten/events.h create mode 100644 tests/test_events.c diff --git a/src/library_events.js b/src/library_events.js new file mode 100644 index 0000000000000..d1a8612779fed --- /dev/null +++ b/src/library_events.js @@ -0,0 +1,1241 @@ +var LibraryJsEvents = { + $JsEvents: { + // pointers to structs malloc()ed to Emscripten HEAP for JS->C interop. + keyEvent: 0, + mouseEvent: 0, + wheelEvent: 0, + uiEvent: 0, + focusEvent: 0, + deviceOrientationEvent: 0, + deviceMotionEvent: 0, + fullscreenChangeEvent: 0, + pointerlockChangeEvent: 0, + visibilityChangeEvent: 0, + touchEvent: 0, + + // When the C runtime exits via exit(), we unregister all event handlers added by this library to be nice and clean. + // Track in this field whether we have yet registered that __ATEXIT__ handler. + removeEventListenersRegistered: false, + + registerRemoveEventListeners: function() { + if (!JsEvents.removeEventListenersRegistered) { + __ATEXIT__.push({ func: function() { + for(var i = JsEvents.eventHandlers.length-1; i >= 0; --i) { + JsEvents._removeHandler(i); + } + } }); + JsEvents.removeEventListenersRegistered = true; + } + }, + + findEventTarget: function(target) { + if (target) { + if (typeof target == "number") { + target = Pointer_stringify(target); + } + if (target == '#window') { + return window; + } else if (target == '#document') { + return document; + } else if (target == '#screen') { + return window.screen; + } else if (target == '#canvas') { + return Module['canvas']; + } + if (typeof target == 'string') { + return document.getElementById(target); + } else { + return target; + } + } else { + // The sensible target varies between events, but use window as the default + // since DOM events mostly can default to that. Specific callback registrations + // override their own defaults. + return window; + } + }, + + deferredCalls: [], + + // Queues the given function call to occur the next time we enter an event handler. + // Existing implementations of pointerlock apis have required that + // the target element is active in fullscreen mode first. Thefefore give + // fullscreen mode request a precedence of 1 and pointer lock a precedence of 2 + // and sort by that to always request fullscreen before pointer lock. + deferCall: function(targetFunction, precedence, argsList) { + function arraysHaveEqualContent(arrA, arrB) { + if (arrA.length != arrB.length) { + return false; + } + for(var i in arrA) { + if (arrA[i] != arrB[i]) { + return false; + } + } + return true; + } + // Test if the given call was already queued, and if so, don't add it again. + for(var i in JsEvents.deferredCalls) { + var call = JsEvents.deferredCalls[i]; + if (call.targetFunction == targetFunction && arraysHaveEqualContent(call.argsList, argsList)) { + return; + } + } + JsEvents.deferredCalls.push({ + targetFunction: targetFunction, + precedence: precedence, + argsList: argsList + }); + + JsEvents.deferredCalls.sort(function(x,y) { return x.precedence < y.precedence; }); + }, + + // Erases all deferred calls to the given target function from the queue list. + removeDeferredCalls: function(targetFunction) { + for(var i = 0; i < JsEvents.deferredCalls.length; ++i) { + if (JsEvents.deferredCalls[i].targetFunction == targetFunction) { + JsEvents.deferredCalls.splice(i, 1); + --i; + } + } + }, + + canPerformEventHandlerRequests: function() { + return JsEvents.inEventHandler && JsEvents.currentEventHandler.allowsDeferredCalls; + }, + + runDeferredCalls: function() { + if (!JsEvents.canPerformEventHandlerRequests()) { + return; + } + for(var i = 0; i < JsEvents.deferredCalls.length; ++i) { + var call = JsEvents.deferredCalls[i]; + JsEvents.deferredCalls.splice(i, 1); + --i; + call.targetFunction.apply(this, call.argsList); + } + }, + + // If positive, we are currently executing in a JS event handler. + inEventHandler: 0, + // If we are in an event handler, specifies the event handler object from the eventHandlers array that is currently running. + currentEventHandler: null, + + // Stores objects representing each currently registered JS event handler. + eventHandlers: [], + + _removeHandler: function(i) { + JsEvents.eventHandlers[i].target.removeEventListener(JsEvents.eventHandlers[i].eventTypeString, JsEvents.eventHandlers[i].handlerFunc, true); + JsEvents.eventHandlers.splice(i, 1); + }, + + registerOrRemoveHandler: function(eventHandler) { + var jsEventHandler = function jsEventHandler(event) { + // Increment nesting count for the event handler. + ++JsEvents.inEventHandler; + JsEvents.currentEventHandler = eventHandler; + // Process any old deferred calls the user has placed. + JsEvents.runDeferredCalls(); + // Process the actual event, calls back to user C code handler. + eventHandler.handlerFunc(event); + // Process any new deferred calls that were placed right now from this event handler. + JsEvents.runDeferredCalls(); + // Out of event handler - restore nesting count. + --JsEvents.inEventHandler; + } + + if (eventHandler.callbackfunc) { + eventHandler.target.addEventListener(eventHandler.eventTypeString, jsEventHandler, eventHandler.useCapture); + JsEvents.eventHandlers.push(eventHandler); + JsEvents.registerRemoveEventListeners(); + } else { + for(var i = 0; i < JsEvents.eventHandlers.length; ++i) { + if (JsEvents.eventHandlers[i].target == eventHandler.target + && JsEvents.eventHandlers[i].eventTypeString == eventHandler.eventTypeString) { + JsEvents._removeHandler(i--); + } + } + } + }, + + registerKeyEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JsEvents.keyEvent) { + JsEvents.keyEvent = _malloc( {{{ C_STRUCTS.emscripten_KeyboardEvent.__size__ }}} ); + } + var handlerFunc = function(event) { + var e = event || window.event; + writeStringToMemory(e.key ? e.key : "", JsEvents.keyEvent + {{{ C_STRUCTS.emscripten_KeyboardEvent.key }}} ); + writeStringToMemory(e.code ? e.code : "", JsEvents.keyEvent + {{{ C_STRUCTS.emscripten_KeyboardEvent.code }}} ); + {{{ makeSetValue('JsEvents.keyEvent', C_STRUCTS.emscripten_KeyboardEvent.location, 'e.location', 'i32') }}} + {{{ makeSetValue('JsEvents.keyEvent', C_STRUCTS.emscripten_KeyboardEvent.ctrlKey, 'e.ctrlKey', 'i32') }}} + {{{ makeSetValue('JsEvents.keyEvent', C_STRUCTS.emscripten_KeyboardEvent.shiftKey, 'e.shiftKey', 'i32') }}} + {{{ makeSetValue('JsEvents.keyEvent', C_STRUCTS.emscripten_KeyboardEvent.altKey, 'e.altKey', 'i32') }}} + {{{ makeSetValue('JsEvents.keyEvent', C_STRUCTS.emscripten_KeyboardEvent.metaKey, 'e.metaKey', 'i32') }}} + {{{ makeSetValue('JsEvents.keyEvent', C_STRUCTS.emscripten_KeyboardEvent.repeat, 'e.repeat', 'i32') }}} + writeStringToMemory(e.locale ? e.locale : "", JsEvents.keyEvent + {{{ C_STRUCTS.emscripten_KeyboardEvent.locale }}} ); + writeStringToMemory(e.char ? e.char : "", JsEvents.keyEvent + {{{ C_STRUCTS.emscripten_KeyboardEvent.charValue }}} ); + {{{ makeSetValue('JsEvents.keyEvent', C_STRUCTS.emscripten_KeyboardEvent.charCode, 'e.charCode', 'i32') }}} + {{{ makeSetValue('JsEvents.keyEvent', C_STRUCTS.emscripten_KeyboardEvent.keyCode, 'e.keyCode', 'i32') }}} + {{{ makeSetValue('JsEvents.keyEvent', C_STRUCTS.emscripten_KeyboardEvent.which, 'e.which', 'i32') }}} + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.keyEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JsEvents.findEventTarget(target), + allowsDeferredCalls: true, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JsEvents.registerOrRemoveHandler(eventHandler); + }, + + fillMouseEventData: function(eventStruct, e) { + var rect = Module['canvas'].getBoundingClientRect(); + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.timestamp, 'JsEvents.tick()', 'double') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.screenX, 'e.screenX', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.screenY, 'e.screenY', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.clientX, 'e.clientX', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.clientY, 'e.clientY', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.ctrlKey, 'e.ctrlKey', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.shiftKey, 'e.shiftKey', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.altKey, 'e.altKey', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.metaKey, 'e.metaKey', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.button, 'e.button', 'i16') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.buttons, 'e.buttons', 'i16') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.movementX, 'e.movementX', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.movementY, 'e.movementY', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.canvasX, 'e.clientX - rect.left', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.canvasY, 'e.clientY - rect.top', 'i32') }}} + }, + + registerMouseEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JsEvents.mouseEvent) { + JsEvents.mouseEvent = _malloc( {{{ C_STRUCTS.emscripten_MouseEvent.__size__ }}} ); + } + var handlerFunc = function(event) { + var e = event || window.event; + JsEvents.fillMouseEventData(JsEvents.mouseEvent, e); + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.mouseEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JsEvents.findEventTarget(target), + allowsDeferredCalls: eventTypeString != 'mousemove', // Mouse move events do not allow fullscreen/pointer lock requests to be handled in them! + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JsEvents.registerOrRemoveHandler(eventHandler); + }, + + registerWheelEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JsEvents.wheelEvent) { + JsEvents.wheelEvent = _malloc( {{{ C_STRUCTS.emscripten_WheelEvent.__size__ }}} ); + } + var handlerFunc = function(event) { + var e = event || window.event; + JsEvents.fillMouseEventData(JsEvents.wheelEvent, e); + {{{ makeSetValue('JsEvents.wheelEvent', C_STRUCTS.emscripten_WheelEvent.deltaX, 'e.deltaX', 'double') }}} + {{{ makeSetValue('JsEvents.wheelEvent', C_STRUCTS.emscripten_WheelEvent.deltaY, 'e.deltaY', 'double') }}} + {{{ makeSetValue('JsEvents.wheelEvent', C_STRUCTS.emscripten_WheelEvent.deltaZ, 'e.deltaZ', 'double') }}} + {{{ makeSetValue('JsEvents.wheelEvent', C_STRUCTS.emscripten_WheelEvent.deltaMode, 'e.deltaMode', 'i32') }}} + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.wheelEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JsEvents.findEventTarget(target), + allowsDeferredCalls: true, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JsEvents.registerOrRemoveHandler(eventHandler); + }, + + pageScrollPos: function() { + if (window.pageXOffset > 0 || window.pageYOffset > 0) { + return [window.pageXOffset, window.pageYOffset]; + } + if (document.documentElement.scrollLeft > 0 || document.documentElement.scrollTop > 0) { + return [document.documentElement.scrollLeft, document.documentElement.scrollTop]; + } + return [document.body.scrollLeft|0, document.body.scrollTop|0]; + }, + + registerUiEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JsEvents.uiEvent) { + JsEvents.uiEvent = _malloc( {{{ C_STRUCTS.emscripten_UiEvent.__size__ }}} ); + } + + if (eventTypeString == "scroll" && !target) { + target = document; // By default read scroll events on document rather than window. + } else { + target = JsEvents.findEventTarget(target); + } + + var handlerFunc = function(event) { + var e = event || window.event; + if (e.target != target) { + // Never take ui events such as scroll via a 'bubbled' route, but always from the direct element that + // was targeted. Otherwise e.g. if app logs a message in response to a page scroll, the Emscripten log + // message box could cause to scroll, generating a new (bubbled) scroll message, causing a new log print, + // causing a new scroll, etc.. + return; + } + var scrollPos = JsEvents.pageScrollPos(); + {{{ makeSetValue('JsEvents.uiEvent', C_STRUCTS.emscripten_UiEvent.detail, 'e.detail', 'i32') }}} + {{{ makeSetValue('JsEvents.uiEvent', C_STRUCTS.emscripten_UiEvent.documentBodyClientWidth, 'document.body.clientWidth', 'i32') }}} + {{{ makeSetValue('JsEvents.uiEvent', C_STRUCTS.emscripten_UiEvent.documentBodyClientHeight, 'document.body.clientHeight', 'i32') }}} + {{{ makeSetValue('JsEvents.uiEvent', C_STRUCTS.emscripten_UiEvent.windowInnerWidth, 'window.innerWidth', 'i32') }}} + {{{ makeSetValue('JsEvents.uiEvent', C_STRUCTS.emscripten_UiEvent.windowInnerHeight, 'window.innerHeight', 'i32') }}} + {{{ makeSetValue('JsEvents.uiEvent', C_STRUCTS.emscripten_UiEvent.windowOuterWidth, 'window.outerWidth', 'i32') }}} + {{{ makeSetValue('JsEvents.uiEvent', C_STRUCTS.emscripten_UiEvent.windowOuterHeight, 'window.outerHeight', 'i32') }}} + {{{ makeSetValue('JsEvents.uiEvent', C_STRUCTS.emscripten_UiEvent.scrollTop, 'scrollPos[0]', 'i32') }}} + {{{ makeSetValue('JsEvents.uiEvent', C_STRUCTS.emscripten_UiEvent.scrollLeft, 'scrollPos[1]', 'i32') }}} + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.uiEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: target, + allowsDeferredCalls: false, // Neither scroll or resize events allow running requests inside them. + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JsEvents.registerOrRemoveHandler(eventHandler); + }, + + getNodeNameForTarget: function(target) { + if (!target) { + return ''; + } + if (target == window) { + return '#window'; + } + if (target == window.screen) { + return '#screen'; + } + return (target && target.nodeName) ? target.nodeName : ''; + }, + + registerFocusEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JsEvents.focusEvent) { + JsEvents.focusEvent = _malloc( {{{ C_STRUCTS.emscripten_FocusEvent.__size__ }}} ); + } + var handlerFunc = function(event) { + var e = event || window.event; + + var nodeName = JsEvents.getNodeNameForTarget(e.target); + var id = e.target.id ? e.target.id : ''; + writeStringToMemory(nodeName, JsEvents.focusEvent + {{{ C_STRUCTS.emscripten_FocusEvent.nodeName }}} ); + writeStringToMemory(id, JsEvents.focusEvent + {{{ C_STRUCTS.emscripten_FocusEvent.id }}} ); + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.focusEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JsEvents.findEventTarget(target), + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JsEvents.registerOrRemoveHandler(eventHandler); + }, + + tick: function() { + if (window['performance'] && window['performance']['now']) { + return window['performance']['now'](); + } else { + return Date.now(); + } + }, + + registerDeviceOrientationEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JsEvents.deviceOrientationEvent) { + JsEvents.deviceOrientationEvent = _malloc( {{{ C_STRUCTS.emscripten_DeviceOrientationEvent.__size__ }}} ); + } + var handlerFunc = function(event) { + var e = event || window.event; + + {{{ makeSetValue('JsEvents.deviceOrientationEvent', C_STRUCTS.emscripten_DeviceOrientationEvent.timestamp, 'JsEvents.tick()', 'double') }}} + {{{ makeSetValue('JsEvents.deviceOrientationEvent', C_STRUCTS.emscripten_DeviceOrientationEvent.alpha, 'e.alpha', 'double') }}} + {{{ makeSetValue('JsEvents.deviceOrientationEvent', C_STRUCTS.emscripten_DeviceOrientationEvent.beta, 'e.beta', 'double') }}} + {{{ makeSetValue('JsEvents.deviceOrientationEvent', C_STRUCTS.emscripten_DeviceOrientationEvent.gamma, 'e.gamma', 'double') }}} + {{{ makeSetValue('JsEvents.deviceOrientationEvent', C_STRUCTS.emscripten_DeviceOrientationEvent.absolute, 'e.absolute', 'i32') }}} + + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.deviceOrientationEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JsEvents.findEventTarget(target), + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JsEvents.registerOrRemoveHandler(eventHandler); + }, + + registerDeviceMotionEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JsEvents.deviceMotionEvent) { + JsEvents.deviceMotionEvent = _malloc( {{{ C_STRUCTS.emscripten_DeviceMotionEvent.__size__ }}} ); + } + var handlerFunc = function(event) { + var e = event || window.event; + + {{{ makeSetValue('JsEvents.deviceOrientationEvent', C_STRUCTS.emscripten_DeviceMotionEvent.timestamp, 'JsEvents.tick()', 'double') }}} + {{{ makeSetValue('JsEvents.deviceMotionEvent', C_STRUCTS.emscripten_DeviceMotionEvent.accelerationX, 'e.acceleration.x', 'double') }}} + {{{ makeSetValue('JsEvents.deviceMotionEvent', C_STRUCTS.emscripten_DeviceMotionEvent.accelerationY, 'e.acceleration.y', 'double') }}} + {{{ makeSetValue('JsEvents.deviceMotionEvent', C_STRUCTS.emscripten_DeviceMotionEvent.accelerationZ, 'e.acceleration.z', 'double') }}} + {{{ makeSetValue('JsEvents.deviceMotionEvent', C_STRUCTS.emscripten_DeviceMotionEvent.accelerationIncludingGravityX, 'e.accelerationIncludingGravity.x', 'double') }}} + {{{ makeSetValue('JsEvents.deviceMotionEvent', C_STRUCTS.emscripten_DeviceMotionEvent.accelerationIncludingGravityY, 'e.accelerationIncludingGravity.y', 'double') }}} + {{{ makeSetValue('JsEvents.deviceMotionEvent', C_STRUCTS.emscripten_DeviceMotionEvent.accelerationIncludingGravityZ, 'e.accelerationIncludingGravity.z', 'double') }}} + {{{ makeSetValue('JsEvents.deviceMotionEvent', C_STRUCTS.emscripten_DeviceMotionEvent.rotationRateAlpha, 'e.rotationRate.alpha', 'double') }}} + {{{ makeSetValue('JsEvents.deviceMotionEvent', C_STRUCTS.emscripten_DeviceMotionEvent.rotationRateBeta, 'e.rotationRate.beta', 'double') }}} + {{{ makeSetValue('JsEvents.deviceMotionEvent', C_STRUCTS.emscripten_DeviceMotionEvent.rotationRateGamma, 'e.rotationRate.gamma', 'double') }}} + + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.deviceMotionEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JsEvents.findEventTarget(target), + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JsEvents.registerOrRemoveHandler(eventHandler); + }, + + fillOrientationChangeEventData: function(eventStruct, e) { + var orientations = ["portrait-primary", "portrait-secondary", "landscape-primary", "landscape-secondary"]; + var orientations2 = ["portrait", "portrait", "landscape", "landscape"]; + + var orientationString = window.screen.orientation || window.screen.mozOrientation; + var orientation = orientations.indexOf(orientationString); + if (orientation == -1) { + orientation = orientations2.indexOf(orientationString); + } + + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_OrientationChangeEvent.orientationIndex, '1 << orientation', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_OrientationChangeEvent.orientationAngle, 'window.orientation', 'i32') }}} + }, + + registerOrientationChangeEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JsEvents.orientationChangeEvent) { + JsEvents.orientationChangeEvent = _malloc( {{{ C_STRUCTS.emscripten_OrientationChangeEvent.__size__ }}} ); + } + + if (!target) { + target = window.screen; // Orientation events need to be captured from 'window.screen' instead of 'window' + } else { + target = JsEvents.findEventTarget(target); + } + + var handlerFunc = function(event) { + var e = event || window.event; + + JsEvents.fillOrientationChangeEventData(JsEvents.orientationChangeEvent, e); + + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.orientationChangeEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + if (eventTypeString == "orientationchange" && window.screen.mozOrientation !== undefined) { + eventTypeString = "mozorientationchange"; + } + + var eventHandler = { + target: target, + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JsEvents.registerOrRemoveHandler(eventHandler); + }, + + fillFullscreenChangeEventData: function(eventStruct, e) { + var fullscreenElement = document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement; + var isFullscreen = !!fullscreenElement; + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_FullscreenChangeEvent.isFullscreen, 'isFullscreen', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_FullscreenChangeEvent.fullscreenEnabled, 'document.fullscreenEnabled', 'i32') }}} + var nodeName = JsEvents.getNodeNameForTarget(fullscreenElement); + var id = (fullscreenElement && fullscreenElement.id) ? fullscreenElement.id : ''; + writeStringToMemory(nodeName, eventStruct + {{{ C_STRUCTS.emscripten_FullscreenChangeEvent.nodeName }}} ); + writeStringToMemory(id, eventStruct + {{{ C_STRUCTS.emscripten_FullscreenChangeEvent.id }}} ); + }, + + registerFullscreenChangeEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JsEvents.fullscreenChangeEvent) { + JsEvents.fullscreenChangeEvent = _malloc( {{{ C_STRUCTS.emscripten_FullscreenChangeEvent.__size__ }}} ); + } + + if (!target) { + target = document; // Fullscreen change events need to be captured from 'document' by default instead of 'window' + } else { + target = JsEvents.findEventTarget(target); + } + + var handlerFunc = function(event) { + var e = event || window.event; + + JsEvents.fillFullscreenChangeEventData(JsEvents.fullscreenChangeEvent, e); + + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.fullscreenChangeEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: target, + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JsEvents.registerOrRemoveHandler(eventHandler); + }, + + requestFullscreen: function(target) { + if (target.requestFullscreen) { + target.requestFullscreen(); + } else if (target.msRequestFullscreen) { + target.msRequestFullscreen(); + } else if (target.mozRequestFullScreen) { + target.mozRequestFullScreen(); + } else if (target.webkitRequestFullscreen) { + target.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); + } else { + return 2 /* Target does not support requesting fullscreen */; + } + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + fillPointerlockChangeEventData: function(eventStruct, e) { + var pointerLockElement = document.pointerLockElement || document.mozPointerLockElement || document.webkitPointerLockElement || document.msPointerLockElement; + var isPointerlocked = !!pointerLockElement; + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_PointerlockChangeEvent.isActive, 'isPointerlocked', 'i32') }}} + var nodeName = JsEvents.getNodeNameForTarget(pointerLockElement); + var id = (pointerLockElement && pointerLockElement.id) ? pointerLockElement.id : ''; + writeStringToMemory(nodeName, eventStruct + {{{ C_STRUCTS.emscripten_PointerlockChangeEvent.nodeName }}} ); + writeStringToMemory(id, eventStruct + {{{ C_STRUCTS.emscripten_PointerlockChangeEvent.id }}}); + }, + + registerPointerlockChangeEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JsEvents.pointerlockChangeEvent) { + JsEvents.pointerlockChangeEvent = _malloc( {{{ C_STRUCTS.emscripten_PointerlockChangeEvent.__size__ }}} ); + } + + if (!target) { + target = document; // Pointer lock change events need to be captured from 'document' by default instead of 'window' + } else { + target = JsEvents.findEventTarget(target); + } + + var handlerFunc = function(event) { + var e = event || window.event; + + JsEvents.fillPointerlockChangeEventData(JsEvents.pointerlockChangeEvent, e); + + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.pointerlockChangeEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: target, + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JsEvents.registerOrRemoveHandler(eventHandler); + }, + + requestPointerLock: function(target) { + if (target.requestPointerLock) { + target.requestPointerLock(); + } else if (target.mozRequestPointerLock) { + target.mozRequestPointerLock(); + } else if (target.webkitRequestPointerLock) { + target.webkitRequestPointerLock(); + } else if (target.msRequestPointerLock) { + target.msRequestPointerLock(); + } else { + return 2 /* Target does not support requesting pointer lock */; + } + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + fillVisibilityChangeEventData: function(eventStruct, e) { + var visibilityStates = [ "hidden", "visible", "prerender", "unloaded" ]; + var visibilityState = visibilityStates.indexOf(document.visibilityState); + + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_VisibilityChangeEvent.hidden, 'document.hidden', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_VisibilityChangeEvent.visibilityState, 'visibilityState', 'i32') }}} + }, + + registerVisibilityChangeEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JsEvents.visibilityChangeEvent) { + JsEvents.visibilityChangeEvent = _malloc( {{{ C_STRUCTS.emscripten_VisibilityChangeEvent.__size__ }}} ); + } + + if (!target) { + target = document; // Visibility change events need to be captured from 'document' by default instead of 'window' + } else { + target = JsEvents.findEventTarget(target); + } + + var handlerFunc = function(event) { + var e = event || window.event; + + JsEvents.fillVisibilityChangeEventData(JsEvents.visibilityChangeEvent, e); + + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.visibilityChangeEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: target, + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JsEvents.registerOrRemoveHandler(eventHandler); + }, + + registerTouchEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JsEvents.touchEvent) { + JsEvents.touchEvent = _malloc( {{{ C_STRUCTS.emscripten_TouchEvent.__size__ }}} ); + } + + var handlerFunc = function(event) { + var e = event || window.event; + + var touches = {}; + for(var i = 0; i < e.touches.length; ++i) { + var touch = e.touches[i]; + touches[touch.identifier] = touch; + } + for(var i = 0; i < e.changedTouches.length; ++i) { + var touch = e.changedTouches[i]; + touches[touch.identifier] = touch; + touch.changed = true; + } + for(var i = 0; i < e.targetTouches.length; ++i) { + var touch = e.targetTouches[i]; + touches[touch.identifier].onTarget = true; + } + + var ptr = JsEvents.touchEvent; + {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchEvent.ctrlKey, 'e.ctrlKey', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchEvent.shiftKey, 'e.shiftKey', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchEvent.altKey, 'e.altKey', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchEvent.metaKey, 'e.metaKey', 'i32') }}} + ptr += {{{ C_STRUCTS.emscripten_TouchEvent.touches }}}; // Advance to the start of the touch array. + var rect = Module['canvas'].getBoundingClientRect(); + var numTouches = 0; + for(var i in touches) { + var t = touches[i]; + {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchPoint.identifier, 't.identifier', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchPoint.screenX, 't.screenX', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchPoint.screenY, 't.screenY', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchPoint.clientX, 't.clientX', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchPoint.clientY, 't.clientY', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchPoint.pageX, 't.pageX', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchPoint.pageY, 't.pageY', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchPoint.isChanged, 't.changed', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchPoint.onTarget, 't.onTarget', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchPoint.canvasX, 't.clientX - rect.left', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchPoint.canvasY, 't.clientY - rect.top', 'i32') }}} + ptr += {{{ C_STRUCTS.emscripten_TouchPoint.__size__ }}}; + + if (++numTouches >= 32) { + break; + } + } + {{{ makeSetValue('JsEvents.touchEvent', C_STRUCTS.emscripten_TouchEvent.numTouches, 'numTouches', 'i32') }}} + + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.touchEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JsEvents.findEventTarget(target), + allowsDeferredCalls: true, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JsEvents.registerOrRemoveHandler(eventHandler); + }, + + fillGamepadEventData: function(eventStruct, e) { + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_GamepadEvent.timestamp, 'e.timestamp', 'double') }}} + for(var i = 0; i < e.axes.length; ++i) { + {{{ makeSetValue('eventStruct+i*8', C_STRUCTS.emscripten_GamepadEvent.axis, 'e.axes[i]', 'double') }}} + } + for(var i = 0; i < e.buttons.length; ++i) { + {{{ makeSetValue('eventStruct+i*8', C_STRUCTS.emscripten_GamepadEvent.analogButton, 'e.buttons[i].value', 'double') }}} + } + for(var i = 0; i < e.buttons.length; ++i) { + {{{ makeSetValue('eventStruct+i*4', C_STRUCTS.emscripten_GamepadEvent.digitalButton, 'e.buttons[i].pressed', 'i32') }}} + } + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_GamepadEvent.connected, 'e.connected', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_GamepadEvent.index, 'e.index', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_GamepadEvent.numAxes, 'e.axes.length', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_GamepadEvent.numButtons, 'e.buttons.length', 'i32') }}} + writeStringToMemory(e.id, eventStruct + {{{ C_STRUCTS.emscripten_GamepadEvent.id }}} ); + writeStringToMemory(e.mapping, eventStruct + {{{ C_STRUCTS.emscripten_GamepadEvent.mapping }}} ); + }, + + registerGamepadEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JsEvents.gamepadEvent) { + JsEvents.gamepadEvent = _malloc( {{{ C_STRUCTS.emscripten_GamepadEvent.__size__ }}} ); + } + + var handlerFunc = function(event) { + var e = event || window.event; + + JsEvents.fillGamepadEventData(JsEvents.gamepadEvent, e.gamepad); + + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.gamepadEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JsEvents.findEventTarget(target), + allowsDeferredCalls: true, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JsEvents.registerOrRemoveHandler(eventHandler); + }, + + registerBeforeUnloadEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + var handlerFunc = function(event) { + var e = event || window.event; + + var confirmationMessage = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, 0, userData]); + + confirmationMessage = Pointer_stringify(confirmationMessage); + if (confirmationMessage) { + e.preventDefault(); + e.returnValue = confirmationMessage; + return confirmationMessage; + } + }; + + var eventHandler = { + target: JsEvents.findEventTarget(target), + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JsEvents.registerOrRemoveHandler(eventHandler); + }, + + battery: function() { return navigator.battery || navigator.mozBattery || navigator.webkitBattery; }, + + fillBatteryEventData: function(eventStruct, e) { + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_BatteryEvent.chargingTime, 'e.chargingTime', 'double') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_BatteryEvent.dischargingTime, 'e.dischargingTime', 'double') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_BatteryEvent.level, 'e.level', 'double') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_BatteryEvent.charging, 'e.charging', 'i32') }}} + }, + + registerBatteryEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JsEvents.batteryEvent) { + JsEvents.batteryEvent = _malloc( {{{ C_STRUCTS.emscripten_BatteryEvent.__size__ }}} ); + } + + var handlerFunc = function(event) { + var e = event || window.event; + + JsEvents.fillBatteryEventData(JsEvents.batteryEvent, JsEvents.battery()); + + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.batteryEvent, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JsEvents.findEventTarget(target), + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JsEvents.registerOrRemoveHandler(eventHandler); + }, + + registerWebGlEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!target) { + target = Module['canvas']; + } + var handlerFunc = function(event) { + var e = event || window.event; + + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, 0, userData]); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JsEvents.findEventTarget(target), + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JsEvents.registerOrRemoveHandler(eventHandler); + }, + }, + + emscripten_set_keypress_callback: function(target, userData, useCapture, callbackfunc) { + JsEvents.registerKeyEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_KEYPRESS') }}}, "keypress"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_keydown_callback: function(target, userData, useCapture, callbackfunc) { + JsEvents.registerKeyEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_KEYDOWN') }}}, "keydown"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_keyup_callback: function(target, userData, useCapture, callbackfunc) { + JsEvents.registerKeyEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_KEYUP') }}}, "keyup"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_click_callback: function(target, userData, useCapture, callbackfunc) { + JsEvents.registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_CLICK') }}}, "click"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_mousedown_callback: function(target, userData, useCapture, callbackfunc) { + JsEvents.registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_MOUSEDOWN') }}}, "mousedown"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_mouseup_callback: function(target, userData, useCapture, callbackfunc) { + JsEvents.registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_MOUSEUP') }}}, "mouseup"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_dblclick_callback: function(target, userData, useCapture, callbackfunc) { + JsEvents.registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_DBLCLICK') }}}, "dblclick"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_mousemove_callback: function(target, userData, useCapture, callbackfunc) { + JsEvents.registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_MOUSEMOVE') }}}, "mousemove"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_get_mouse_status: function(mouseState) { + if (!JsEvents.mouseEvent) { + return {{{ cDefine('EMSCRIPTEN_RESULT_NO_DATA') }}}; + } + // HTML5 does not really have a polling API for mouse events, so implement one manually by + // returning the data from the most recently received event. This requires that user has registered + // at least some no-op function as an event handler to any of the mouse function. + HEAP32.set(HEAP32.subarray(JsEvents.mouseEvent, {{{ C_STRUCTS.emscripten_MouseEvent.__size__ }}}), mouseState); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_wheel_callback: function(target, userData, useCapture, callbackfunc) { + JsEvents.registerWheelEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_WHEEL') }}}, "wheel"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_resize_callback: function(target, userData, useCapture, callbackfunc) { + JsEvents.registerUiEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_RESIZE') }}}, "resize"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_scroll_callback: function(target, userData, useCapture, callbackfunc) { + JsEvents.registerUiEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_SCROLL') }}}, "scroll"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_blur_callback: function(target, userData, useCapture, callbackfunc) { + JsEvents.registerFocusEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_BLUR') }}}, "blur"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_focus_callback: function(target, userData, useCapture, callbackfunc) { + JsEvents.registerFocusEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FOCUS') }}}, "focus"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_focusin_callback: function(target, userData, useCapture, callbackfunc) { + JsEvents.registerFocusEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FOCUSIN') }}}, "focusin"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_focusout_callback: function(target, userData, useCapture, callbackfunc) { + JsEvents.registerFocusEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FOCUSOUT') }}}, "focusout"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_deviceorientation_callback: function(userData, useCapture, callbackfunc) { + JsEvents.registerDeviceOrientationEventCallback(window, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_DEVICEORIENTATION') }}}, "deviceorientation"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_get_deviceorientation_status: function(orientationState) { + if (!JsEvents.deviceOrientationEvent) { + return {{{ cDefine('EMSCRIPTEN_RESULT_NO_DATA') }}}; + } + // HTML5 does not really have a polling API for device orientation events, so implement one manually by + // returning the data from the most recently received event. This requires that user has registered + // at least some no-op function as an event handler. + HEAP32.set(HEAP32.subarray(JsEvents.deviceOrientationEvent, {{{ C_STRUCTS.emscripten_DeviceOrientationEvent.__size__ }}}), orientationState); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_devicemotion_callback: function(userData, useCapture, callbackfunc) { + JsEvents.registerDeviceMotionEventCallback(window, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_DEVICEMOTION') }}}, "devicemotion"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_get_devicemotion_status: function(motionState) { + if (!JsEvents.deviceMotionEvent) { + return {{{ cDefine('EMSCRIPTEN_RESULT_NO_DATA') }}}; + } + // HTML5 does not really have a polling API for device motion events, so implement one manually by + // returning the data from the most recently received event. This requires that user has registered + // at least some no-op function as an event handler. + HEAP32.set(HEAP32.subarray(JsEvents.deviceMotionEvent, {{{ C_STRUCTS.emscripten_DeviceMotionEvent.__size__ }}}), motionState); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_orientationchange_callback: function(userData, useCapture, callbackfunc) { + if (!window.screen || !window.screen.addEventListener) { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } + JsEvents.registerOrientationChangeEventCallback(window.screen, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_ORIENTATIONCHANGE') }}}, "orientationchange"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_get_orientation_status: function(orientationChangeEvent) { + JsEvents.fillOrientationChangeEventData(orientationChangeEvent); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_lock_orientation: function(allowedOrientations) { + var orientations = []; + if (allowedOrientations & 1) orientations.push("portrait-primary"); + if (allowedOrientations & 2) orientations.push("portrait-secondary"); + if (allowedOrientations & 4) orientations.push("landscape-primary"); + if (allowedOrientations & 8) orientations.push("landscape-secondary"); + if (window.screen.lockOrientation) { + window.screen.lockOrientation(orientations); + } else if (window.screen.mozLockOrientation) { + window.screen.mozLockOrientation(orientations); + } else if (window.screen.webkitLockOrientation) { + window.screen.webkitLockOrientation(orientations); + } else if (window.screen.msLockOrientation) { + window.screen.msLockOrientation(orientations); + } else { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_unlock_orientation: function() { + if (window.screen.unlockOrientation) { + window.screen.unlockOrientation(); + } else if (window.screen.mozUnlockOrientation) { + window.screen.mozUnlockOrientation(); + } else if (window.screen.webkitUnlockOrientation) { + window.screen.webkitUnlockOrientation(); + } else if (window.screen.msUnlockOrientation) { + window.screen.msUnlockOrientation(); + } else { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_fullscreenchange_callback: function(userData, useCapture, callbackfunc) { + JsEvents.registerFullscreenChangeEventCallback(userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FULLSCREENCHANGE') }}}, "fullscreenchange"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_get_fullscreen_status: function(fullscreenStatus) { + JsEvents.fillFullscreenChangeEventData(fullscreenStatus); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + // https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Using_full_screen_mode + emscripten_request_fullscreen: function(target, deferUntilInEventHandler) { + if (!target) { + target = '#canvas'; + } + target = JsEvents.findEventTarget(target); + if (!target) { + return {{{ cDefine('EMSCRIPTEN_RESULT_INVALID_TARGET') }}}; + } + + if (!target.requestFullscreen && !target.msRequestFullscreen && !target.mozRequestFullScreen && !target.webkitRequestFullscreen) { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } + + var canPerformRequests = JsEvents.canPerformEventHandlerRequests(); + + // Queue this function call if we're not currently in an event handler and the user saw it appropriate to do so. + if (!canPerformRequests) { + if (deferUntilInEventHandler) { + JsEvents.deferCall(JsEvents.requestFullscreen, 1 /* priority over pointer lock */, [target]); + return {{{ cDefine('EMSCRIPTEN_RESULT_DEFERRED') }}}; + } else { + return {{{ cDefine('EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED') }}}; + } + } + + return JsEvents.requestFullscreen(target); + }, + + emscripten_exit_fullscreen: function() { + // Make sure no queued up calls will fire after this. + JsEvents.removeDeferredCalls(JsEvents.requestFullscreen); + + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if (document.msExitFullscreen) { + document.msExitFullscreen(); + } else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen(); + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } else { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_pointerlockchange_callback: function(userData, useCapture, callbackfunc) { + JsEvents.registerPointerlockChangeEventCallback(document, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_POINTERLOCKCHANGE') }}}, "pointerlockchange"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_get_pointerlock_status: function(pointerlockStatus) { + JsEvents.fillPointerlockChangeEventData(pointerlockStatus); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_request_pointerlock: function(target, deferUntilInEventHandler) { + if (!target) { + target = '#canvas'; + } + target = JsEvents.findEventTarget(target); + if (!target) { + return {{{ cDefine('EMSCRIPTEN_RESULT_INVALID_TARGET') }}}; + } + if (!target.requestPointerLock && !target.mozRequestPointerLock && !target.webkitRequestPointerLock && !target.msRequestPointerLock) { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } + + var canPerformRequests = JsEvents.canPerformEventHandlerRequests(); + + // Queue this function call if we're not currently in an event handler and the user saw it appropriate to do so. + if (!canPerformRequests) { + if (deferUntilInEventHandler) { + JsEvents.deferCall(JsEvents.requestPointerLock, 2 /* priority below fullscreen */, [target]); + return {{{ cDefine('EMSCRIPTEN_RESULT_DEFERRED') }}}; + } else { + return {{{ cDefine('EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED') }}}; + } + } + + return JsEvents.requestPointerLock(target); + }, + + emscripten_exit_pointerlock: function() { + // Make sure no queued up calls will fire after this. + JsEvents.removeDeferredCalls(JsEvents.requestPointerLock); + + if (document.exitPointerLock) { + document.exitPointerLock(); + } else if (document.msExitPointerLock) { + document.msExitPointerLock(); + } else if (document.mozExitPointerLock) { + document.mozExitPointerLock(); + } else if (document.webkitExitPointerLock) { + document.webkitExitPointerLock(); + } else { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_vibrate: function(msecs) { + if (!navigator.vibrate) { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } + + navigator.vibrate(msecs); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_vibrate_pattern: function(msecsArray, numEntries) { + if (!navigator.vibrate) { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } + + var vibrateList = []; + for(var i = 0; i < numEntries; ++i) { + var msecs = {{{ makeGetValue('msecsArray', 'i*4', 'i32') }}} + vibrateList.push(msecs); + } + navigator.vibrate(vibrateList); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_visibilitychange_callback: function(userData, useCapture, callbackfunc) { + JsEvents.registerVisibilityChangeEventCallback(document, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_VISIBILITYCHANGE') }}}, "visibilitychange"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_get_visibility_status: function(visibilityStatus) { + JsEvents.fillVisibilityChangeEventData(visibilityStatus); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_touchstart_callback: function(target, userData, useCapture, callbackfunc) { + JsEvents.registerTouchEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_TOUCHSTART') }}}, "touchstart"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_touchend_callback: function(target, userData, useCapture, callbackfunc) { + JsEvents.registerTouchEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_TOUCHEND') }}}, "touchend"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_touchmove_callback: function(target, userData, useCapture, callbackfunc) { + JsEvents.registerTouchEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_TOUCHMOVE') }}}, "touchmove"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_touchcancel_callback: function(target, userData, useCapture, callbackfunc) { + JsEvents.registerTouchEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_TOUCHCANCEL') }}}, "touchcancel"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_gamepadconnected_callback: function(userData, useCapture, callbackfunc) { + JsEvents.registerGamepadEventCallback(window, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_GAMEPADCONNECTED') }}}, "gamepadconnected"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_gamepaddisconnected_callback: function(userData, useCapture, callbackfunc) { + JsEvents.registerGamepadEventCallback(window, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_GAMEPADDISCONNECTED') }}}, "gamepaddisconnected"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_get_num_gamepads: function() { + if (!navigator.getGamepads) { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } + return navigator.getGamepads().length; + }, + + emscripten_get_gamepad_status: function(index, gamepadState) { + if (!navigator.getGamepads) { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } + var gamepads = navigator.getGamepads(); + if (index < 0 || index >= gamepads.length) { + return {{{ cDefine('EMSCRIPTEN_RESULT_INVALID_PARAM') }}}; + } + JsEvents.fillGamepadEventData(gamepadState, gamepads[index]); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_beforeunload_callback: function(userData, callbackfunc) { + JsEvents.registerBeforeUnloadEventCallback(window, userData, true, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_BEFOREUNLOAD') }}}, "beforeunload"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_batterychargingchange_callback: function(userData, callbackfunc) { + JsEvents.registerBatteryEventCallback(JsEvents.battery(), userData, true, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_BATTERYCHARGINGCHANGE') }}}, "chargingchange"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_batterylevelchange_callback: function(userData, callbackfunc) { + JsEvents.registerBatteryEventCallback(JsEvents.battery(), userData, true, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_BATTERYLEVELCHANGE') }}}, "levelchange"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_get_battery_status: function(batteryState) { + JsEvents.fillBatteryEventData(batteryState, JsEvents.battery()); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_webglcontextlost_callback: function(target, userData, useCapture, callbackfunc) { + JsEvents.registerWebGlEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST') }}}, "webglcontextlost"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, + + emscripten_set_webglcontextrestored_callback: function(target, userData, useCapture, callbackfunc) { + JsEvents.registerWebGlEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED') }}}, "webglcontextrestored"); + return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; + }, +}; + +autoAddDeps(LibraryJsEvents, '$JsEvents'); +mergeInto(LibraryManager.library, LibraryJsEvents); diff --git a/src/modules.js b/src/modules.js index ad467ba7694a4..7b267971faec0 100644 --- a/src/modules.js +++ b/src/modules.js @@ -424,7 +424,7 @@ var LibraryManager = { load: function() { if (this.library) return; - var libraries = ['library.js', 'library_path.js', 'library_fs.js', 'library_idbfs.js', 'library_memfs.js', 'library_nodefs.js', 'library_sockfs.js', 'library_tty.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js', 'library_jansson.js', 'library_openal.js', 'library_glfw.js', 'library_uuid.js', 'library_glew.js'].concat(additionalLibraries); + var libraries = ['library.js', 'library_path.js', 'library_fs.js', 'library_idbfs.js', 'library_memfs.js', 'library_nodefs.js', 'library_sockfs.js', 'library_tty.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js', 'library_jansson.js', 'library_openal.js', 'library_glfw.js', 'library_uuid.js', 'library_glew.js', 'library_events.js'].concat(additionalLibraries); for (var i = 0; i < libraries.length; i++) { eval(processMacros(preprocess(read(libraries[i])))); } diff --git a/src/struct_info.json b/src/struct_info.json index a22851b62d087..ec44909744cdb 100644 --- a/src/struct_info.json +++ b/src/struct_info.json @@ -1073,5 +1073,187 @@ "UUID_TYPE_DCE_RANDOM" ], "structs": {} + }, + // =========================================== + // emscripten events library + // =========================================== + { + "file": "emscripten/events.h", + "defines": [ + "EMSCRIPTEN_EVENT_KEYPRESS", + "EMSCRIPTEN_EVENT_KEYDOWN", + "EMSCRIPTEN_EVENT_KEYUP", + "EMSCRIPTEN_EVENT_CLICK", + "EMSCRIPTEN_EVENT_MOUSEDOWN", + "EMSCRIPTEN_EVENT_MOUSEUP", + "EMSCRIPTEN_EVENT_DBLCLICK", + "EMSCRIPTEN_EVENT_MOUSEMOVE", + "EMSCRIPTEN_EVENT_WHEEL", + "EMSCRIPTEN_EVENT_RESIZE", + "EMSCRIPTEN_EVENT_SCROLL", + "EMSCRIPTEN_EVENT_BLUR", + "EMSCRIPTEN_EVENT_FOCUS", + "EMSCRIPTEN_EVENT_FOCUSIN", + "EMSCRIPTEN_EVENT_FOCUSOUT", + "EMSCRIPTEN_EVENT_DEVICEORIENTATION", + "EMSCRIPTEN_EVENT_DEVICEMOTION", + "EMSCRIPTEN_EVENT_ORIENTATIONCHANGE", + "EMSCRIPTEN_EVENT_FULLSCREENCHANGE", + "EMSCRIPTEN_EVENT_POINTERLOCKCHANGE", + "EMSCRIPTEN_EVENT_VISIBILITYCHANGE", + "EMSCRIPTEN_EVENT_TOUCHSTART", + "EMSCRIPTEN_EVENT_TOUCHEND", + "EMSCRIPTEN_EVENT_TOUCHMOVE", + "EMSCRIPTEN_EVENT_TOUCHCANCEL", + "EMSCRIPTEN_EVENT_GAMEPADCONNECTED", + "EMSCRIPTEN_EVENT_GAMEPADDISCONNECTED", + "EMSCRIPTEN_EVENT_BEFOREUNLOAD", + "EMSCRIPTEN_EVENT_BATTERYCHARGINGCHANGE", + "EMSCRIPTEN_EVENT_BATTERYLEVELCHANGE", + "EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST", + "EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED", + + "EMSCRIPTEN_RESULT_SUCCESS", + "EMSCRIPTEN_RESULT_DEFERRED", + "EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED", + "EMSCRIPTEN_RESULT_INVALID_TARGET", + "EMSCRIPTEN_RESULT_INVALID_PARAM", + "EMSCRIPTEN_RESULT_NOT_SUPPORTED", + "EMSCRIPTEN_RESULT_FAILED", + "EMSCRIPTEN_RESULT_NO_DATA" + ], + "structs": { + "emscripten_KeyboardEvent": [ + "key", + "code", + "location", + "ctrlKey", + "shiftKey", + "altKey", + "metaKey", + "repeat", + "locale", + "charValue", + "charCode", + "keyCode", + "which" + ], + "emscripten_MouseEvent": [ + "timestamp", + "screenX", + "screenY", + "clientX", + "clientY", + "ctrlKey", + "shiftKey", + "altKey", + "metaKey", + "button", + "buttons", + "movementX", + "movementY", + "canvasX", + "canvasY" + ], + "emscripten_WheelEvent": [ + "mouse", + "deltaX", + "deltaY", + "deltaZ", + "deltaMode" + ], + "emscripten_UiEvent": [ + "detail", + "documentBodyClientWidth", + "documentBodyClientHeight", + "windowInnerWidth", + "windowInnerHeight", + "windowOuterWidth", + "windowOuterHeight", + "scrollTop", + "scrollLeft" + ], + "emscripten_FocusEvent": [ + "nodeName", + "id" + ], + "emscripten_DeviceOrientationEvent": [ + "timestamp", + "alpha", + "beta", + "gamma", + "absolute" + ], + "emscripten_DeviceMotionEvent": [ + "timestamp", + "accelerationX", + "accelerationY", + "accelerationZ", + "accelerationIncludingGravityX", + "accelerationIncludingGravityY", + "accelerationIncludingGravityZ", + "rotationRateAlpha", + "rotationRateBeta", + "rotationRateGamma" + ], + "emscripten_OrientationChangeEvent": [ + "orientationIndex", + "orientationAngle" + ], + "emscripten_FullscreenChangeEvent": [ + "isFullscreen", + "fullscreenEnabled", + "nodeName", + "id" + ], + "emscripten_PointerlockChangeEvent": [ + "isActive", + "nodeName", + "id" + ], + "emscripten_VisibilityChangeEvent": [ + "hidden", + "visibilityState" + ], + "emscripten_TouchPoint": [ + "identifier", + "screenX", + "screenY", + "clientX", + "clientY", + "pageX", + "pageY", + "isChanged", + "onTarget", + "canvasX", + "canvasY" + ], + "emscripten_TouchEvent": [ + "numTouches", + "ctrlKey", + "shiftKey", + "altKey", + "metaKey", + "touches" + ], + "emscripten_GamepadEvent": [ + "timestamp", + "axis", + "analogButton", + "digitalButton", + "connected", + "index", + "numAxes", + "numButtons", + "id", + "mapping" + ], + "emscripten_BatteryEvent": [ + "chargingTime", + "dischargingTime", + "level", + "charging" + ] + } } -] + ] diff --git a/system/include/emscripten/events.h b/system/include/emscripten/events.h new file mode 100644 index 0000000000000..4caae3cfd5fd8 --- /dev/null +++ b/system/include/emscripten/events.h @@ -0,0 +1,625 @@ +#ifndef __emscripten_events_h__ +#define __emscripten_events_h__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* This file defines Emscripten low-level glue bindings for interfacing with the following HTML5 APIs: + * - DOM Level 3 Events: Keyboard, Mouse, Mouse Wheel, Resize, Scroll, Focus. See https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html + * - DeviceOrientation Events for gyro and accelerometer. See http://www.w3.org/TR/orientation-event/ + * - Screen Orientation Events for portrait/landscape handling. See https://dvcs.w3.org/hg/screen-orientation/raw-file/tip/Overview.html + * - Fullscreen Events for browser canvas fullscreen modes transitioning. See https://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html + * - Pointer Lock Events for relative-mode mouse motion control. See http://www.w3.org/TR/pointerlock/ + * - Vibration API for mobile device haptic vibration feedback control. See http://dev.w3.org/2009/dap/vibration/ + * - Page Visibility Events for power management control. See http://www.w3c-test.org/webperf/specs/PageVisibility/ + * - Touch Events. See http://www.w3.org/TR/touch-events/ + * - Gamepad API. See http://www.w3.org/TR/gamepad/ + * - Beforeunload event. See http://www.whatwg.org/specs/web-apps/current-work/multipage/history.html#beforeunloadevent + * - WebGL context events. See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2 + * + * Most web APIs are event-based, which means that the functionality is accessed by registering a callback function to be called when the event occurs. The + * Gamepad API is currently an exception, for which only a polling API is available. For some APIs, both an event-based and a polling-based API is exposed. + * + * Calling a callback registration function with a null pointer function causes an unregistration of that callback from the given target element. All event + * handlers are also automatically unregistered when the C exit() function is invoked during the atexit handler pass. Use either the function + * emscripten_set_main_loop() or set Module.noExitRuntime = true; to make sure that leaving main() will not immediately cause an exit() and clean up the + * event handlers. + * + * Throughout this file, the function signatures have a 'target' parameter. This parameter allows specifying the HTML Element ID to which the callback + * registration is to be applied to. This field has the following special meanings: + * - 0 or NULL: A default element is chosen automatically based on the event type, which should be reasonable most of the time. + * - "#window": The event listener is applied to the JS 'window' object. + * - "#document": The event listener is applied to the JS 'document' object. + * - "#screen": The event listener is applied to the JS 'window.screen' object. + * - "#canvas": The event listener is applied to the Emscripten default WebGL canvas element. + * - Any other string without a leading hash "#" sign: The event listener is applied to the element by the given ID on the page. + * + * The callback hook functions also take in a 'userData' parameter. This is a custom user-defineable value that will be carried through unchanged to all + * invocations of the registered event callback. Use this e.g. to pass a pointer to a C++ class or similar to enclose the C API in a clean object-oriented manner. + * + * Callback handlers that return an EM_BOOL may return nonzero to signal that the default action for that event is to be suppressed. This will call + * the .preventDefault(); member on the event. Returning zero will cause the default browser event action to be carried out. + * + * Most functions return the result using the type EMSCRIPTEN_RESULT. Nonzero and positive values denote success. Negative values + * signal failure. None of the functions fail or abort by throwing a JS or C++ exception. If a particular browser does not support the given feature, + * the value EMSCRIPTEN_RESULT_NOT_SUPPORTED will be returned at the time the callback is registered. + * + * Due to web security purposes, the pointer lock and fullscreen requests can only be invoked from inside an user-originated event handler. Such requests + * are automatically deferred until the user presses a keyboard or mouse button the next time. + */ + +#define EMSCRIPTEN_EVENT_KEYPRESS 1 +#define EMSCRIPTEN_EVENT_KEYDOWN 2 +#define EMSCRIPTEN_EVENT_KEYUP 3 +#define EMSCRIPTEN_EVENT_CLICK 4 +#define EMSCRIPTEN_EVENT_MOUSEDOWN 5 +#define EMSCRIPTEN_EVENT_MOUSEUP 6 +#define EMSCRIPTEN_EVENT_DBLCLICK 7 +#define EMSCRIPTEN_EVENT_MOUSEMOVE 8 +#define EMSCRIPTEN_EVENT_WHEEL 9 +#define EMSCRIPTEN_EVENT_RESIZE 10 +#define EMSCRIPTEN_EVENT_SCROLL 11 +#define EMSCRIPTEN_EVENT_BLUR 12 +#define EMSCRIPTEN_EVENT_FOCUS 13 +#define EMSCRIPTEN_EVENT_FOCUSIN 14 +#define EMSCRIPTEN_EVENT_FOCUSOUT 15 +#define EMSCRIPTEN_EVENT_DEVICEORIENTATION 16 +#define EMSCRIPTEN_EVENT_DEVICEMOTION 17 +#define EMSCRIPTEN_EVENT_ORIENTATIONCHANGE 18 +#define EMSCRIPTEN_EVENT_FULLSCREENCHANGE 19 +#define EMSCRIPTEN_EVENT_POINTERLOCKCHANGE 20 +#define EMSCRIPTEN_EVENT_VISIBILITYCHANGE 21 +#define EMSCRIPTEN_EVENT_TOUCHSTART 22 +#define EMSCRIPTEN_EVENT_TOUCHEND 23 +#define EMSCRIPTEN_EVENT_TOUCHMOVE 24 +#define EMSCRIPTEN_EVENT_TOUCHCANCEL 25 +#define EMSCRIPTEN_EVENT_GAMEPADCONNECTED 26 +#define EMSCRIPTEN_EVENT_GAMEPADDISCONNECTED 27 +#define EMSCRIPTEN_EVENT_BEFOREUNLOAD 28 +#define EMSCRIPTEN_EVENT_BATTERYCHARGINGCHANGE 29 +#define EMSCRIPTEN_EVENT_BATTERYLEVELCHANGE 30 +#define EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST 31 +#define EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED 32 + +#define EMSCRIPTEN_RESULT int + +// The operation succeeded +#define EMSCRIPTEN_RESULT_SUCCESS 0 + +// For web security reasons, the requested operation cannot be completed now, but was deferred for completion in the next event handler. +#define EMSCRIPTEN_RESULT_DEFERRED 1 + +// The given operation is not supported by this browser or the target element. +#define EMSCRIPTEN_RESULT_NOT_SUPPORTED -1 + +// For web security reasons, the requested operation could not be completed now, and it failed since the user requested the operation to not be deferred. +#define EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED -2 + +// The given target element for the operation is invalid. +#define EMSCRIPTEN_RESULT_INVALID_TARGET -3 + +// An invalid parameter was passed to the function. +#define EMSCRIPTEN_RESULT_INVALID_PARAM -4 + +// The operation failed due to some generic reason. +#define EMSCRIPTEN_RESULT_FAILED -5 + +// Operation failed since no data is currently available. +#define EMSCRIPTEN_RESULT_NO_DATA -6 + +#define EM_BOOL int +#define EM_UTF8 char + +#define DOM_KEY_LOCATION int +#define DOM_KEY_LOCATION_STANDARD 0x00 +#define DOM_KEY_LOCATION_LEFT 0x01 +#define DOM_KEY_LOCATION_RIGHT 0x02 +#define DOM_KEY_LOCATION_NUMPAD 0x03 + +/* + * The event structure passed in keyboard events keypress, keydown and keyup. + * https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#keys + */ +typedef struct emscripten_KeyboardEvent { + // The printed representation of the pressed key. + EM_UTF8 key[32]; + // A string that identifies the physical key being pressed. The value is not affected by the current keyboard + // layout or modifier state, so a particular key will always return the same value. + EM_UTF8 code[32]; + // Indicates the location of the key on the keyboard. One of the DOM_KEY_LOCATION_ values. + unsigned long location; + // Specifies which modifiers were active during the key event. + EM_BOOL ctrlKey; + EM_BOOL shiftKey; + EM_BOOL altKey; + EM_BOOL metaKey; + // Specifies if this keyboard event represents a repeated press. + EM_BOOL repeat; + // A locale string indicating the locale the keyboard is configured for. This may be the empty string if the + // browser or device doesn't know the keyboard's locale. + EM_UTF8 locale[32]; + // The following fields are values from previous versions of the DOM key events specifications. + // See https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent?redirectlocale=en-US&redirectslug=DOM%2FKeyboardEvent + // The character representation of the key. + EM_UTF8 charValue[32]; + // The Unicode reference number of the key; this attribute is used only by the keypress event. For keys whose char attribute + // contains multiple characters, this is the Unicode value of the first character in that attribute. + unsigned long charCode; + // A system and implementation dependent numerical code identifying the unmodified value of the pressed key. + unsigned long keyCode; + // A system and implementation dependent numeric code identifying the unmodified value of the pressed key; this is usually the same as keyCode. + unsigned long which; +} emscripten_KeyboardEvent; + +/* + * Registers a callback function for receiving browser-generated keyboard input events. + * See https://developer.mozilla.org/en/DOM/Event/UIEvent/KeyEvent + * and http://www.javascriptkit.com/jsref/eventkeyboardmouse.shtml + */ +extern EMSCRIPTEN_RESULT emscripten_set_keypress_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const emscripten_KeyboardEvent *keyEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_keydown_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const emscripten_KeyboardEvent *keyEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_keyup_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const emscripten_KeyboardEvent *keyEvent, void *userData)); + +/* + * The event structure passed in mouse events click, mousedown, mouseup, dblclick and mousemove. + * https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#interface-MouseEvent + */ +typedef struct emscripten_MouseEvent { + // A timestamp of when this data was generated by the browser. This is an absolute wallclock time in milliseconds. + double timestamp; + // The coordinate relative to the browser screen coordinate system. + long screenX; + long screenY; + // The coordinate relative to the viewport associate with the event. + long clientX; + long clientY; + // Specifies which modifiers were active during the mouse event. + EM_BOOL ctrlKey; + EM_BOOL shiftKey; + EM_BOOL altKey; + EM_BOOL metaKey; + // Which pointer device button changed state. + unsigned short button; + // A bitmask that indicates which combinations of mouse buttons were being held down at the time of the event. + unsigned short buttons; + // If pointer lock is active, these two extra fields give relative mouse movement since the last event. + long movementX; + long movementY; + // Emscripten-specific extension: These fields give the mouse coordinates mapped to the Emscripten canvas client area. + long canvasX; + long canvasY; + // Pad this struct to multiple of 8 bytes to make WheelEvent unambiguously align to 8 bytes. + long padding; +} emscripten_MouseEvent; + +/* + * Registers a callback function for receiving browser-generated mouse input events. + * See https://developer.mozilla.org/en/DOM/MouseEvent + */ +extern EMSCRIPTEN_RESULT emscripten_set_click_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const emscripten_MouseEvent *mouseEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_mousedown_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const emscripten_MouseEvent *mouseEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_mouseup_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const emscripten_MouseEvent *mouseEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_dblclick_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const emscripten_MouseEvent *mouseEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_mousemove_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const emscripten_MouseEvent *mouseEvent, void *userData)); +/* + * Returns the most recently received mouse event state. Note that for this function call to succeed, emscripten_set_xx_callback must have first + * been called with one of the mouse event types and a non-zero callback function pointer to enable the Mouse state capture. + */ +extern EMSCRIPTEN_RESULT emscripten_get_mouse_status(emscripten_MouseEvent *mouseState); + +#define DOM_DELTA_PIXEL 0x00 +#define DOM_DELTA_LINE 0x01 +#define DOM_DELTA_PAGE 0x02 + +/* + * The event structure passed in mouse wheelevent. + * https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#interface-WheelEvent + */ +typedef struct emscripten_WheelEvent { + // Specifies general mouse information related to this event. + emscripten_MouseEvent mouse; + // Measures along different axes the movement of the wheel. + double deltaX; + double deltaY; + double deltaZ; + // One of the DOM_DELTA_ values that indicates the units of measurement for the delta values. + unsigned long deltaMode; +} emscripten_WheelEvent; + +/* + * Registers a callback function for receiving browser-generated mouse wheel events. + * See http://www.w3.org/TR/DOM-Level-3-Events/#event-type-wheel + */ +extern EMSCRIPTEN_RESULT emscripten_set_wheel_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_WheelEvent *wheelEvent, void *userData)); + +/* + * The event structure passed in DOM element resize and scroll events. + * https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#interface-UIEvent + */ +typedef struct emscripten_UiEvent { + // Specifies detail information about this event. + long detail; + // The clientWidth/clientHeight of the document.body element. + int documentBodyClientWidth; + int documentBodyClientHeight; + // The innerWidth/innerHeight of the window element. + int windowInnerWidth; + int windowInnerHeight; + // The outerWidth/outerHeight of the window element. + int windowOuterWidth; + int windowOuterHeight; + // The page scroll position. + int scrollTop; + int scrollLeft; +} emscripten_UiEvent; + +/* + * Registers a callback function for receiving DOM element resize and scroll events. + * See https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-resize + */ +extern EMSCRIPTEN_RESULT emscripten_set_resize_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_UiEvent *uiEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_scroll_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_UiEvent *uiEvent, void *userData)); + +/* + * The event structure passed in DOM element blur, focus, focusin and focusout events. + * https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#interface-FocusEvent + */ +typedef struct emscripten_FocusEvent { + // The nodeName of the target HTML Element. See https://developer.mozilla.org/en-US/docs/Web/API/Node.nodeName + EM_UTF8 nodeName[128]; + // The HTML Element ID of the target element. + EM_UTF8 id[128]; +} emscripten_FocusEvent; + +/* + * Registers a callback function for receiving DOM element blur, focus, focusin and focusout events. + * See https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-blur + */ +extern EMSCRIPTEN_RESULT emscripten_set_blur_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_FocusEvent *focusEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_focus_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_FocusEvent *focusEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_focusin_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_FocusEvent *focusEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_focusout_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_FocusEvent *focusEvent, void *userData)); + +/* + * The event structure passed in the deviceorientation event. + * http://dev.w3.org/geo/api/spec-source-orientation.html#deviceorientation + */ +typedef struct emscripten_DeviceOrientationEvent { + // Absolute wallclock time in msec units of when the event occurred. + double timestamp; + // The orientation of the device in terms of the transformation from a coordinate frame fixed on the Earth to a coordinate frame fixed in the device. + double alpha; + double beta; + double gamma; + // If false, the orientation is only relative to some other bas orinetation, not to the fixed coordinate frame. + EM_BOOL absolute; +} emscripten_DeviceOrientationEvent; + +/* + * Registers a callback function for receiving the deviceorientation event. + * See http://dev.w3.org/geo/api/spec-source-orientation.html + */ +extern EMSCRIPTEN_RESULT emscripten_set_deviceorientation_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_DeviceOrientationEvent *orientationEvent, void *userData)); +/* + * Returns the most recently received deviceorientation event state. Note that for this function call to succeed, emscripten_set_deviceorientation_callback + * must have first been called with one of the mouse event types and a non-zero callback function pointer to enable the Device Orientation state capture. + */ +extern EMSCRIPTEN_RESULT emscripten_get_deviceorientation_status(emscripten_DeviceOrientationEvent *orientationState); + +/* + * The event structure passed in the devicemotion event. + * http://dev.w3.org/geo/api/spec-source-orientation.html#devicemotion + */ +typedef struct emscripten_DeviceMotionEvent { + // Absolute wallclock time in msec units of when the event occurred. + double timestamp; + // Acceleration of the device excluding gravity. + double accelerationX; + double accelerationY; + double accelerationZ; + // Acceleration of the device including gravity. + double accelerationIncludingGravityX; + double accelerationIncludingGravityY; + double accelerationIncludingGravityZ; + // The rotational delta of the device. + double rotationRateAlpha; + double rotationRateBeta; + double rotationRateGamma; +} emscripten_DeviceMotionEvent; + +/* + * Registers a callback function for receiving the devicemotion event. + * See http://dev.w3.org/geo/api/spec-source-orientation.html + */ +extern EMSCRIPTEN_RESULT emscripten_set_devicemotion_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_DeviceMotionEvent *motionEvent, void *userData)); +/* + * Returns the most recently received deviceomotion event state. Note that for this function call to succeed, emscripten_set_devicemotion_callback + * must have first been called with one of the mouse event types and a non-zero callback function pointer to enable the Device Motion state capture. + */ +extern EMSCRIPTEN_RESULT emscripten_get_devicemotion_status(emscripten_DeviceMotionEvent *motionState); + +#define EMSCRIPTEN_ORIENTATION_PORTRAIT_PRIMARY 1 +#define EMSCRIPTEN_ORIENTATION_PORTRAIT_SECONDARY 2 +#define EMSCRIPTEN_ORIENTATION_LANDSCAPE_PRIMARY 4 +#define EMSCRIPTEN_ORIENTATION_LANDSCAPE_SECONDARY 8 + +/* + * The event structure passed in the orientationchange event. + * https://dvcs.w3.org/hg/screen-orientation/raw-file/tip/Overview.html + */ +typedef struct emscripten_OrientationChangeEvent { + // One of EM_ORIENTATION_PORTRAIT_xx fields, or -1 if unknown. + int orientationIndex; + // Emscripten-specific extension: Some browsers refer to 'window.orientation', so report that as well. + // Orientation angle in degrees. 0: "default orientation", i.e. default upright orientation to hold the mobile device in. Could be either landscape or portrait. + int orientationAngle; +} emscripten_OrientationChangeEvent; + +/* + * Registers a callback function for receiving the orientationchange event. + * https://dvcs.w3.org/hg/screen-orientation/raw-file/tip/Overview.html + */ +extern EMSCRIPTEN_RESULT emscripten_set_orientationchange_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_OrientationChangeEvent *orientationChangeEvent, void *userData)); +/* + * Returns the current device orientation state. + */ +extern EMSCRIPTEN_RESULT emscripten_get_orientation_status(emscripten_OrientationChangeEvent *orientationStatus); +/* + * Locks the screen orientation to the given set of allowed orientations. + * allowedOrientations: A bitfield set of EM_ORIENTATION_xx flags. + */ +extern EMSCRIPTEN_RESULT emscripten_lock_orientation(int allowedOrientations); +/* + * Allows the screen to turn again into any orientation. + */ +extern EMSCRIPTEN_RESULT emscripten_unlock_orientation(); + +/* + * The event structure passed in the fullscreenchange event. + * https://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html + */ +typedef struct emscripten_FullscreenChangeEvent { + // Specifies whether an element on the browser page is currently fullscreen. + EM_BOOL isFullscreen; + // Specifies if the current page has the ability to display elements fullscreen. + EM_BOOL fullscreenEnabled; + // The nodeName of the target HTML Element that is in full screen mode. See https://developer.mozilla.org/en-US/docs/Web/API/Node.nodeName + EM_UTF8 nodeName[128]; + // The HTML Element ID of the target HTML element that is in full screen mode. + EM_UTF8 id[128]; +} emscripten_FullscreenChangeEvent; + +/* + * Registers a callback function for receiving the fullscreenchange event. + * https://dvcs.w3.org/hg/screen-orientation/raw-file/tip/Overview.html + */ +extern EMSCRIPTEN_RESULT emscripten_set_fullscreenchange_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_FullscreenChangeEvent *fullscreenChangeEvent, void *userData)); +/* + * Returns the current page fullscreen state. + */ +extern EMSCRIPTEN_RESULT emscripten_get_fullscreen_status(emscripten_FullscreenChangeEvent *fullscreenStatus); +/* + * Requests the given target element to transition to full screen mode. + * Note: This function can only run inside a user-generated JavaScript event handler. + * deferUntilInEventHandler: If true and you called this function outside an event callback, this request will + * be queued to be executed the next time a JS event handler runs. If false, this + * function will instead fail if not running inside a JS event handler. + */ +extern EMSCRIPTEN_RESULT emscripten_request_fullscreen(const char *target, int deferUntilInEventHandler); +/* + * Returns back to windowed browsing mode. + */ +extern EMSCRIPTEN_RESULT emscripten_exit_fullscreen(); + +/* + * The event structure passed in the pointerlockchange event. + * http://www.w3.org/TR/pointerlock/ + */ +typedef struct emscripten_PointerlockChangeEvent { + // Specifies whether an element on the browser page currently has pointer lock enabled. + EM_BOOL isActive; + // The nodeName of the target HTML Element that has the pointer lock active. See https://developer.mozilla.org/en-US/docs/Web/API/Node.nodeName + EM_UTF8 nodeName[128]; + // The HTML Element ID of the target HTML element that has the pointer lock active. + EM_UTF8 id[128]; +} emscripten_PointerlockChangeEvent; + +/* + * Registers a callback function for receiving the pointerlockchange event. + * Pointer lock hides the mouse cursor and exclusively gives the target element relative mouse movement events via the mousemove event. + * http://www.w3.org/TR/pointerlock/ + */ +extern EMSCRIPTEN_RESULT emscripten_set_pointerlockchange_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_PointerlockChangeEvent *pointerlockChangeEvent, void *userData)); +/* + * Returns the current page pointerlock state. + */ +extern EMSCRIPTEN_RESULT emscripten_get_pointerlock_status(emscripten_PointerlockChangeEvent *pointerlockStatus); +/* + * Requests the given target element to grab pointerlock. + * Note: This function can only run inside a user-generated JavaScript event handler. + * deferUntilInEventHandler: If true and you called this function outside an event callback, this request will + * be queued to be executed the next time a JS event handler runs. If false, this + * function will instead fail if not running inside a JS event handler. + */ +extern EMSCRIPTEN_RESULT emscripten_request_pointerlock(const char *target, int deferUntilInEventHandler); +/* + * Exits pointer lock state and restores the mouse cursor to be visible again. + */ +extern EMSCRIPTEN_RESULT emscripten_exit_pointerlock(); + +#define EMSCRIPTEN_VISIBILITY_HIDDEN 0 +#define EMSCRIPTEN_VISIBILITY_VISIBLE 1 +#define EMSCRIPTEN_VISIBILITY_PRERENDER 2 +#define EMSCRIPTEN_VISIBILITY_UNLOADED 3 + +/* + * The event structure passed in the visibilitychange event. + * http://www.w3c-test.org/webperf/specs/PageVisibility/ + */ +typedef struct emscripten_VisibilityChangeEvent { + // If true, the current browser page is now hidden. + EM_BOOL hidden; + // Specifies a more fine-grained state of the current page visibility status. One of the EMSCRIPTEN_VISIBILITY_ values. + int visibilityState; +} emscripten_VisibilityChangeEvent; + +/* + * Registers a callback function for receiving the visibilitychange event. + * http://www.w3c-test.org/webperf/specs/PageVisibility/ + */ +extern EMSCRIPTEN_RESULT emscripten_set_visibilitychange_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_VisibilityChangeEvent *visibilityChangeEvent, void *userData)); +/* + * Returns the current page visibility state. + */ +extern EMSCRIPTEN_RESULT emscripten_get_visibility_status(emscripten_VisibilityChangeEvent *visibilityStatus); + +/* + * Specifies the status of a single touch point on the page. + * See http://www.w3.org/TR/touch-events/#touch-interface + */ +typedef struct emscripten_TouchPoint +{ + // An identification number for each touch point. + long identifier; + // The touch coordinate relative to the whole screen origin, in pixels. + long screenX; + long screenY; + // The touch coordinate relative to the viewport, in pixels. + long clientX; + long clientY; + // The touch coordinate relative to the viewport, in pixels, and including any scroll offset. + long pageX; + long pageY; + // Specifies whether this touch point changed during this event. + EM_BOOL isChanged; + // Specifies whether this touch point is still above the original target on which it was initially pressed against. + EM_BOOL onTarget; + // The touch coordinates mapped to the Emscripten canvas client area, in pixels. + long canvasX; + long canvasY; +} emscripten_TouchPoint; + +/* + * Specifies the data of a single touch event. + * See http://www.w3.org/TR/touch-events/#touchevent-interface + */ +typedef struct emscripten_TouchEvent { + // The number of valid elements in the touches array. + int numTouches; + // Specifies which modifiers were active during the key event. + EM_BOOL ctrlKey; + EM_BOOL shiftKey; + EM_BOOL altKey; + EM_BOOL metaKey; + // An array of currently active touches, one for each finger. + emscripten_TouchPoint touches[32]; +} emscripten_TouchEvent; + +/* + * Registers a callback function for receiving the touchstart, touchend, touchmove and touchcancel events. + * http://www.w3.org/TR/touch-events/ + */ +extern EMSCRIPTEN_RESULT emscripten_set_touchstart_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_TouchEvent *touchEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_touchend_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_TouchEvent *touchEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_touchmove_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_TouchEvent *touchEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_touchcancel_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_TouchEvent *touchEvent, void *userData)); + +/* + * Represents the current snapshot state of a gamepad. + * http://www.w3.org/TR/gamepad/#gamepad-interface + */ +typedef struct emscripten_GamepadEvent { + // Absolute wallclock time in msec units of when the data was recorded. + double timestamp; + // The number of valid axes entries in the axis array. + int numAxes; + // The number of valid button entries in the analogButton and digitalButton arrays. + int numButtons; + // The analog state of the gamepad axes, in the range [-1, 1]. + double axis[64]; + // The analog state of the gamepad buttons, in the range [0, 1]. + double analogButton[64]; + // The digital state of the gamepad buttons, either 0 or 1. + EM_BOOL digitalButton[64]; + // Specifies whether this gamepad is connected to the browser page. + EM_BOOL connected; + // An ordinal associated with this gamepad, zero-based. + long index; + // An ID for the brand or style of the connected gamepad device. Typically, this will include the USB vendor and a product ID. + EM_UTF8 id[64]; + // A string that identifies the layout or control mapping of this device. + EM_UTF8 mapping[64]; +} emscripten_GamepadEvent; + +/* + * Registers a callback function for receiving the gamepadconnected and gamepaddisconnected events. + * http://www.w3.org/TR/gamepad/ + */ +extern EMSCRIPTEN_RESULT emscripten_set_gamepadconnected_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_GamepadEvent *gamepadEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_gamepaddisconnected_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_GamepadEvent *gamepadEvent, void *userData)); + +/* + * Returns the number of gamepads connected to the system or EMSCRIPTEN_RESULT_NOT_SUPPORTED if the current browser does not support gamepads. + * Note: A gamepad does not show up as connected until a button on it is pressed. + */ +extern int emscripten_get_num_gamepads(); +/* + * Returns a snapshot of the current gamepad state. + */ +extern EMSCRIPTEN_RESULT emscripten_get_gamepad_status(int index, emscripten_GamepadEvent *gamepadState); + +/* + * The event structure passed in the battery chargingchange and levelchange event. + * http://www.w3.org/TR/battery-status/#batterymanager-interface + */ +typedef struct emscripten_BatteryEvent { + double chargingTime; + double dischargingTime; + double level; + EM_BOOL charging; +} emscripten_BatteryEvent; + +/* + * Registers a callback function for receiving the battery chargingchange and levelchange events. + * http://www.w3.org/TR/battery-status/ + */ +extern EMSCRIPTEN_RESULT emscripten_set_batterychargingchange_callback(void *userData, EM_BOOL (*func)(int eventType, const emscripten_BatteryEvent *batteryEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_batterylevelchange_callback(void *userData, EM_BOOL (*func)(int eventType, const emscripten_BatteryEvent *batteryEvent, void *userData)); +/* + * Returns the current battery status. + */ +extern EMSCRIPTEN_RESULT emscripten_get_battery_status(emscripten_BatteryEvent *batteryState); + +/* + * Produces a vibration feedback for given msecs. + * http://dev.w3.org/2009/dap/vibration/ + */ +extern EMSCRIPTEN_RESULT emscripten_vibrate(int msecs); +/* + * Produces a complex vibration feedback pattern. + * msecsArray: An array of timing entries [on, off, on, off, on, off, ...] where every second one specifies a duration of vibration, and + * every other one specifies a duration of silence. + * numEntries: The number of integers in the array msecsArray. + */ +extern EMSCRIPTEN_RESULT emscripten_vibrate_pattern(int *msecsArray, int numEntries); + +/* + * Registers a callback function for receiving the page beforeunload event. + * Hook onto this event to perform process right prior to page close, and/or display a confirmation notification asking if the user really wants to leave the page. + * http://www.whatwg.org/specs/web-apps/current-work/multipage/history.html#beforeunloadevent + */ +extern EMSCRIPTEN_RESULT emscripten_set_beforeunload_callback(void *userData, const char *(*func)(int eventType, const void *reserved, void *userData)); + +/* + * Registers a callback function for the canvas webgl context webglcontextlost and webglcontextrestored events. + * See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2 + */ +extern EMSCRIPTEN_RESULT emscripten_set_webglcontextlost_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const void *reserved, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_webglcontextrestored_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const void *reserved, void *userData)); + +#ifdef __cplusplus +} // ~extern "C" +#endif + +#endif diff --git a/tests/test_browser.py b/tests/test_browser.py index 9321f5293697a..e7139f6efb3b5 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -1768,3 +1768,5 @@ def test_doublestart_bug(self): self.btest('doublestart.c', args=['--pre-js', 'pre.js', '-o', 'test.html'], expected='1') + def test_events(self): + self.btest(path_from_root('tests', 'test_events.c'), expected='0') diff --git a/tests/test_events.c b/tests/test_events.c new file mode 100644 index 0000000000000..23fa87045112d --- /dev/null +++ b/tests/test_events.c @@ -0,0 +1,382 @@ +#include +#include +#include +#include + +static inline const char *emscripten_event_type_to_string(int eventType) { + const char *events[] = { "(invalid)", "(none)", "keypress", "keydown", "keyup", "click", "mousedown", "mouseup", "dblclick", "mousemove", "wheel", "resize", + "scroll", "blur", "focus", "focusin", "focusout", "deviceorientation", "devicemotion", "orientationchange", "fullscreenchange", "pointerlockchange", + "visibilitychange", "touchstart", "touchend", "touchmove", "touchcancel", "gamepadconnected", "gamepaddisconnected", "beforeunload", + "batterychargingchange", "batterylevelchange", "webglcontextlost", "webglcontextrestored", "(invalid)" }; + ++eventType; + if (eventType < 0) eventType = 0; + if (eventType >= sizeof(events)/sizeof(events[0])) eventType = sizeof(events)/sizeof(events[0])-1; + return events[eventType]; +} + +// The event handler functions can return 1 to suppress the event and disable the default action. That calls event.preventDefault(); +// Returning 0 signals that the event was not consumed by the code, and will allow the event to pass on and bubble up normally. +EM_BOOL key_callback(int eventType, const emscripten_KeyboardEvent *e, void *userData) +{ + printf("%s, key: \"%s\", code: \"%s\", location: %lu,%s%s%s%s repeat: %d, locale: \"%s\", char: \"%s\", charCode: %lu, keyCode: %lu, which: %lu\n", + emscripten_event_type_to_string(eventType), e->key, e->code, e->location, + e->ctrlKey ? " CTRL" : "", e->shiftKey ? " SHIFT" : "", e->altKey ? " ALT" : "", e->metaKey ? " META" : "", + e->repeat, e->locale, e->charValue, e->charCode, e->keyCode, e->which); + + if (eventType == EMSCRIPTEN_EVENT_KEYPRESS && !strcmp(e->key, "f")) { + emscripten_FullscreenChangeEvent fsce; + emscripten_get_fullscreen_status(&fsce); + if (!fsce.isFullscreen) { + emscripten_request_fullscreen(0, 1); + } else { + emscripten_exit_fullscreen(); + } + } + + if (eventType == EMSCRIPTEN_EVENT_KEYPRESS && !strcmp(e->key, "p")) { + emscripten_PointerlockChangeEvent plce; + emscripten_get_pointerlock_status(&plce); + if (!plce.isActive) { + emscripten_request_pointerlock(0, 1); + } else { + emscripten_exit_pointerlock(); + } + } + + return 0; +} + +EM_BOOL mouse_callback(int eventType, const emscripten_MouseEvent *e, void *userData) +{ + printf("%s, screen: (%ld,%ld), client: (%ld,%ld),%s%s%s%s button: %hu, buttons: %hu, movement: (%ld,%ld), canvas: (%ld,%ld)\n", + emscripten_event_type_to_string(eventType), e->screenX, e->screenY, e->clientX, e->clientY, + e->ctrlKey ? " CTRL" : "", e->shiftKey ? " SHIFT" : "", e->altKey ? " ALT" : "", e->metaKey ? " META" : "", + e->button, e->buttons, e->movementX, e->movementY, e->canvasX, e->canvasY); + + return 0; +} + +EM_BOOL wheel_callback(int eventType, const emscripten_WheelEvent *e, void *userData) +{ + printf("%s, screen: (%ld,%ld), client: (%ld,%ld),%s%s%s%s button: %hu, buttons: %hu, canvas: (%ld,%ld), delta:(%g,%g,%g), deltaMode:%lu\n", + emscripten_event_type_to_string(eventType), e->mouse.screenX, e->mouse.screenY, e->mouse.clientX, e->mouse.clientY, + e->mouse.ctrlKey ? " CTRL" : "", e->mouse.shiftKey ? " SHIFT" : "", e->mouse.altKey ? " ALT" : "", e->mouse.metaKey ? " META" : "", + e->mouse.button, e->mouse.buttons, e->mouse.canvasX, e->mouse.canvasY, + (float)e->deltaX, (float)e->deltaY, (float)e->deltaZ, e->deltaMode); + + return 0; +} + +EM_BOOL uievent_callback(int eventType, const emscripten_UiEvent *e, void *userData) +{ + printf("%s, detail: %ld, document.body.client size: (%d,%d), window.inner size: (%d,%d), scrollPos: (%d, %d)\n", + emscripten_event_type_to_string(eventType), e->detail, e->documentBodyClientWidth, e->documentBodyClientHeight, + e->windowInnerWidth, e->windowInnerHeight, e->scrollTop, e->scrollLeft); + + return 0; +} + +EM_BOOL focusevent_callback(int eventType, const emscripten_FocusEvent *e, void *userData) +{ + printf("%s, nodeName: \"%s\", id: \"%s\"\n", emscripten_event_type_to_string(eventType), e->nodeName, e->id[0] == '\0' ? "(empty string)" : e->id); + + return 0; +} + +EM_BOOL deviceorientation_callback(int eventType, const emscripten_DeviceOrientationEvent *e, void *userData) +{ + printf("%s, (%g, %g, %g)\n", emscripten_event_type_to_string(eventType), e->alpha, e->beta, e->gamma); + + return 0; +} + +EM_BOOL devicemotion_callback(int eventType, const emscripten_DeviceMotionEvent *e, void *userData) +{ + printf("%s, accel: (%g, %g, %g), accelInclGravity: (%g, %g, %g), rotationRate: (%g, %g, %g)\n", + emscripten_event_type_to_string(eventType), + e->accelerationX, e->accelerationY, e->accelerationZ, + e->accelerationIncludingGravityX, e->accelerationIncludingGravityY, e->accelerationIncludingGravityZ, + e->rotationRateAlpha, e->rotationRateBeta, e->rotationRateGamma); + + return 0; +} + +EM_BOOL orientationchange_callback(int eventType, const emscripten_OrientationChangeEvent *e, void *userData) +{ + printf("%s, orientationAngle: %d, orientationIndex: %d\n", emscripten_event_type_to_string(eventType), e->orientationAngle, e->orientationIndex); + + return 0; +} + +EM_BOOL fullscreenchange_callback(int eventType, const emscripten_FullscreenChangeEvent *e, void *userData) +{ + printf("%s, isFullscreen: %d, fullscreenEnabled: %d, fs element nodeName: \"%s\", fs element id: \"%s\"\n", + emscripten_event_type_to_string(eventType), e->isFullscreen, e->fullscreenEnabled, e->nodeName, e->id); + + return 0; +} + +EM_BOOL pointerlockchange_callback(int eventType, const emscripten_PointerlockChangeEvent *e, void *userData) +{ + printf("%s, isActive: %d, pointerlock element nodeName: \"%s\", id: \"%s\"\n", + emscripten_event_type_to_string(eventType), e->isActive, e->nodeName, e->id); + + return 0; +} + +EM_BOOL visibilitychange_callback(int eventType, const emscripten_VisibilityChangeEvent *e, void *userData) +{ + printf("%s, hidden: %d, visibilityState: %d\n", emscripten_event_type_to_string(eventType), e->hidden, e->visibilityState); + + return 0; +} + +EM_BOOL touch_callback(int eventType, const emscripten_TouchEvent *e, void *userData) +{ + printf("%s, numTouches: %d %s%s%s%s\n", + emscripten_event_type_to_string(eventType), e->numTouches, + e->ctrlKey ? " CTRL" : "", e->shiftKey ? " SHIFT" : "", e->altKey ? " ALT" : "", e->metaKey ? " META" : ""); + for(int i = 0; i < e->numTouches; ++i) + { + const emscripten_TouchPoint *t = &e->touches[i]; + printf(" %ld: screen: (%ld,%ld), client: (%ld,%ld), page: (%ld,%ld), isChanged: %d, onTarget: %d, canvas: (%ld, %ld)\n", + t->identifier, t->screenX, t->screenY, t->clientX, t->clientY, t->pageX, t->pageY, t->isChanged, t->onTarget, t->canvasX, t->canvasY); + } + + return 0; +} + +EM_BOOL gamepad_callback(int eventType, const emscripten_GamepadEvent *e, void *userData) +{ + printf("%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"\n", + eventType != 0 ? emscripten_event_type_to_string(eventType) : "Gamepad state", e->timestamp, e->connected, e->index, + e->numAxes, e->numButtons, e->id, e->mapping); + + if (e->connected) + { + for(int i = 0; i < e->numAxes; ++i) + printf("Axis %d: %g\n", i, e->axis[i]); + + for(int i = 0; i < e->numButtons; ++i) + printf("Button %d: Digital: %d, Analog: %g\n", i, e->digitalButton[i], e->analogButton[i]); + } + + return 0; +} + +const char *beforeunload_callback(int eventType, const void *reserved, void *userData) +{ +#ifdef REPORT_RESULT + return ""; // For test harness, don't show a confirmation dialog to not block and keep the test runner automated. +#else + return "Do you really want to leave the page?"; +#endif +} + +void formatTime(char *str, int seconds) +{ + int h = seconds / (60*60); + seconds -= h*60*60; + int m = seconds / 60; + seconds -= m*60; + if (h > 0) + { + sprintf(str, "%dh:%02dm:%02ds", h, m, seconds); + } + else + { + sprintf(str, "%02dm:%02ds", m, seconds); + } +} + +EM_BOOL battery_callback(int eventType, const emscripten_BatteryEvent *e, void *userData) +{ + char t1[64]; + formatTime(t1, (int)e->chargingTime); + char t2[64]; + formatTime(t2, (int)e->dischargingTime); + printf("%s: chargingTime: %s, dischargingTime: %s, level: %g%%, charging: %d\n", + emscripten_event_type_to_string(eventType), t1, t2, e->level*100, e->charging); + + return 0; +} + +EM_BOOL webglcontext_callback(int eventType, const void *reserved, void *userData) +{ + printf("%s.\n", emscripten_event_type_to_string(eventType)); + + return 0; +} + +emscripten_GamepadEvent prevState[32]; +int prevNumGamepads = 0; + +void mainloop() +{ + int numGamepads = emscripten_get_num_gamepads(); + if (numGamepads != prevNumGamepads) + { + printf("Number of connected gamepads: %d\n", numGamepads); + prevNumGamepads = numGamepads; + } + + for(int i = 0; i < numGamepads && i < 32; ++i) + { + emscripten_GamepadEvent ge; + int failed = emscripten_get_gamepad_status(i, &ge); + if (!failed) + { + int g = ge.index; + for(int j = 0; j < ge.numAxes; ++j) + { + if (ge.axis[j] != prevState[g].axis[j]) + printf("Gamepad %d, axis %d: %g\n", g, j, ge.axis[j]); + } + + for(int j = 0; j < ge.numButtons; ++j) + { + if (ge.analogButton[j] != prevState[g].analogButton[j] || ge.digitalButton[j] != prevState[g].digitalButton[j]) + printf("Gamepad %d, button %d: Digital: %d, Analog: %g\n", g, j, ge.digitalButton[j], ge.analogButton[j]); + } + prevState[g] = ge; + } + } + +} + +#ifdef REPORT_RESULT +void report_result(void *arg) +{ + int result = 0; + REPORT_RESULT(); +} +#endif + +int main() +{ + emscripten_set_keypress_callback(0, 0, 1, key_callback); + emscripten_set_keydown_callback(0, 0, 1, key_callback); + emscripten_set_keyup_callback(0, 0, 1, key_callback); + + emscripten_set_click_callback(0, 0, 1, mouse_callback); + emscripten_set_mousedown_callback(0, 0, 1, mouse_callback); + emscripten_set_mouseup_callback(0, 0, 1, mouse_callback); + emscripten_set_dblclick_callback(0, 0, 1, mouse_callback); + emscripten_set_mousemove_callback(0, 0, 1, mouse_callback); + + emscripten_set_wheel_callback(0, 0, 1, wheel_callback); + + emscripten_set_resize_callback(0, 0, 1, uievent_callback); + emscripten_set_scroll_callback(0, 0, 1, uievent_callback); + + emscripten_set_blur_callback(0, 0, 1, focusevent_callback); + emscripten_set_focus_callback(0, 0, 1, focusevent_callback); + emscripten_set_focusin_callback(0, 0, 1, focusevent_callback); + emscripten_set_focusout_callback(0, 0, 1, focusevent_callback); + + emscripten_set_deviceorientation_callback(0, 1, deviceorientation_callback); + emscripten_set_devicemotion_callback(0, 1, devicemotion_callback); + + emscripten_set_orientationchange_callback(0, 1, orientationchange_callback); + + // Test the polling of orientation. + emscripten_OrientationChangeEvent oce; + emscripten_get_orientation_status(&oce); + printf("The current orientation is:\n"); + orientationchange_callback(EMSCRIPTEN_EVENT_ORIENTATIONCHANGE, &oce, 0); + + int newOrientation = (oce.orientationIndex == EMSCRIPTEN_ORIENTATION_PORTRAIT_PRIMARY + || oce.orientationIndex == EMSCRIPTEN_ORIENTATION_PORTRAIT_SECONDARY) ? EMSCRIPTEN_ORIENTATION_LANDSCAPE_PRIMARY : EMSCRIPTEN_ORIENTATION_PORTRAIT_PRIMARY; + printf("Locking orientation to state %d..\n", newOrientation); + // Test locking of orientation. + int failed = emscripten_lock_orientation(newOrientation); + + if (failed) + printf("No support for orientation lock!\n"); + + emscripten_get_orientation_status(&oce); + printf("The current orientation is after locking:\n"); + orientationchange_callback(18, &oce, 0); + + printf("Unlocking orientation..\n"); + emscripten_unlock_orientation(); + + emscripten_FullscreenChangeEvent fsce; + emscripten_get_fullscreen_status(&fsce); + printf("The current fullscreen status is:\n"); + fullscreenchange_callback(EMSCRIPTEN_EVENT_FULLSCREENCHANGE, &fsce, 0); + + emscripten_set_fullscreenchange_callback(0, 1, fullscreenchange_callback); + + // These won't do anything, since fullscreen must be requested in an event handler, + // but call these anyways to confirm that they don't crash in an exception in the test suite. + failed = emscripten_request_fullscreen(0, 1); + if (failed != 0 && failed != 3) + printf("Fullscreen request failed! (%d)\n", failed); + emscripten_exit_fullscreen(); + + emscripten_PointerlockChangeEvent plce; + emscripten_get_pointerlock_status(&plce); + printf("The current pointerlock status is:\n"); + pointerlockchange_callback(EMSCRIPTEN_EVENT_POINTERLOCKCHANGE, &plce, 0); + + emscripten_set_pointerlockchange_callback(0, 1, pointerlockchange_callback); + + // These won't do anything, since pointer lock must be requested in an event handler, + // but call these anyways to confirm that they don't crash in an exception in the test suite. + failed = emscripten_request_pointerlock(0, 1); + if (failed != 0 && failed != 3) + printf("Pointer lock request failed! (%d)\n", failed); + emscripten_exit_pointerlock(); + + int vibratePattern[] = { + 150, 500, + 300, 500, + 450 + }; + emscripten_vibrate_pattern(vibratePattern, sizeof(vibratePattern)/sizeof(vibratePattern[0])); + + emscripten_VisibilityChangeEvent vce; + emscripten_get_visibility_status(&vce); + printf("Current visibility status:\n"); + visibilitychange_callback(EMSCRIPTEN_EVENT_VISIBILITYCHANGE, &vce, 0); + + emscripten_set_visibilitychange_callback(0, 1, visibilitychange_callback); + + emscripten_set_touchstart_callback(0, 0, 1, touch_callback); + emscripten_set_touchend_callback(0, 0, 1, touch_callback); + emscripten_set_touchmove_callback(0, 0, 1, touch_callback); + emscripten_set_touchcancel_callback(0, 0, 1, touch_callback); + + emscripten_set_gamepadconnected_callback(0, 1, gamepad_callback); + emscripten_set_gamepaddisconnected_callback(0, 1, gamepad_callback); + + emscripten_set_main_loop(mainloop, 10, 0); + + emscripten_set_beforeunload_callback(0, beforeunload_callback); + + emscripten_set_batterychargingchange_callback(0, battery_callback); + emscripten_set_batterylevelchange_callback(0, battery_callback); + + emscripten_BatteryEvent bs; + failed = emscripten_get_battery_status(&bs); + if (failed) { + printf("Browser can not provide Battery Status information!\n"); + } else { + printf("Current battery status:\n"); + battery_callback(EMSCRIPTEN_EVENT_BATTERYLEVELCHANGE, &bs, 0); + } + + emscripten_set_webglcontextlost_callback(0, 0, 1, webglcontext_callback); + emscripten_set_webglcontextrestored_callback(0, 0, 1, webglcontext_callback); + + /* For the events to function, one must either call emscripten_set_main_loop or enable Module.noExitRuntime by some other means. + Otherwise the application will exit after leaving main(), and the atexit handlers will clean up all event hooks (by design). */ + EM_ASM(Module['noExitRuntime'] = true); + +#ifdef REPORT_RESULT + // Keep the page running for a moment. + emscripten_async_call(report_result, 0, 5000); +#endif + return 0; +} From d655dd640096c0de8df62eac6259766773e378d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Wed, 22 Jan 2014 19:02:10 +0200 Subject: [PATCH 68/85] Bump version after adding new entries to src/struct_info.json. --- tools/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/shared.py b/tools/shared.py index e28a66c3d2e1e..665a6d526d069 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -345,7 +345,7 @@ def find_temp_directory(): # we re-check sanity when the settings are changed) # We also re-check sanity and clear the cache when the version changes -EMSCRIPTEN_VERSION = '1.9.3' +EMSCRIPTEN_VERSION = '1.9.4' def generate_sanity(): return EMSCRIPTEN_VERSION + '|' + get_llvm_target() + '|' + LLVM_ROOT + '|' + get_clang_version() From 245a29dedd9015861386ea4fafc3697c4bfcd761 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 22 Jan 2014 12:05:14 -0800 Subject: [PATCH 69/85] avoid unresolved symbol warnings on misc funcs in fastcomp --- src/library.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/library.js b/src/library.js index f9af8a9617541..fdb0764de6e47 100644 --- a/src/library.js +++ b/src/library.js @@ -9168,6 +9168,28 @@ LibraryManager.library = { // misc shims for musl __lockfile: function() { return 1 }, __unlockfile: function(){}, + + // misc definitions to avoid unnecessary unresolved symbols from fastcomp + emscripten_prep_setjmp: true, + emscripten_check_longjmp: true, + emscripten_get_longjmp_result: true, + emscripten_setjmp: true, + emscripten_preinvoke: true, + emscripten_postinvoke: true, + emscripten_resume: true, + emscripten_landingpad: true, + getHigh32: true, + setHigh32: true, + FPtoILow: true, + FPtoIHigh: true, + BDtoILow: true, + BDtoIHigh: true, + SItoF: true, + UItoF: true, + SItoD: true, + UItoD: true, + BItoD: true, + llvm_dbg_value: true, }; function autoAddDeps(object, name) { From 8608181c811e6c48d5c449596c4809e8e38c37ac Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 22 Jan 2014 13:09:56 -0800 Subject: [PATCH 70/85] fastcomp updates and a test for fptosi|ui on floats and doubles --- src/library.js | 6 ++++-- tests/cases/fptosi.ll | 28 ++++++++++++++++++++++++++++ tests/cases/fptosi.txt | 6 ++++++ 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 tests/cases/fptosi.ll create mode 100644 tests/cases/fptosi.txt diff --git a/src/library.js b/src/library.js index fdb0764de6e47..21d69949d1efd 100644 --- a/src/library.js +++ b/src/library.js @@ -9180,8 +9180,10 @@ LibraryManager.library = { emscripten_landingpad: true, getHigh32: true, setHigh32: true, - FPtoILow: true, - FPtoIHigh: true, + FtoILow: true, + FtoIHigh: true, + DtoILow: true, + DtoIHigh: true, BDtoILow: true, BDtoIHigh: true, SItoF: true, diff --git a/tests/cases/fptosi.ll b/tests/cases/fptosi.ll new file mode 100644 index 0000000000000..71bc6af8c8fe2 --- /dev/null +++ b/tests/cases/fptosi.ll @@ -0,0 +1,28 @@ +; ModuleID = '/dev/shm/tmp/src.cpp.o' +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" + +@.str = private unnamed_addr constant [8 x i8] c"*%.3f*\0A\00", align 1 ; [#uses=1 type=[8 x i8]*] +@.str2 = private unnamed_addr constant [6 x i8] c"*%d*\0A\00", align 1 ; [#uses=1 type=[6 x i8]*] + +; [#uses=0] +define i32 @main() { +entry: + %f = fadd float 1.000, 0.500 + %d = fadd double 3.333, 0.444 + %fd = fpext float %f to double + %call1 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([8 x i8]* @.str, i32 0, i32 0), double %fd) ; [#uses=0 type=i32] + %call2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([8 x i8]* @.str, i32 0, i32 0), double %d) ; [#uses=0 type=i32] + %fs = fptosi float %f to i64 + %fu = fptoui float %f to i64 + %ds = fptosi double %d to i64 + %du = fptoui double %d to i64 + %call3 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str2, i32 0, i32 0), i64 %fs) ; [#uses=0 type=i32] + %call4 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str2, i32 0, i32 0), i64 %fu) ; [#uses=0 type=i32] + %call5 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str2, i32 0, i32 0), i64 %ds) ; [#uses=0 type=i32] + %call6 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str2, i32 0, i32 0), i64 %du) ; [#uses=0 type=i32] + ret i32 1 +} + +; [#uses=1] +declare i32 @printf(i8*, ...) diff --git a/tests/cases/fptosi.txt b/tests/cases/fptosi.txt new file mode 100644 index 0000000000000..eea925c4742ec --- /dev/null +++ b/tests/cases/fptosi.txt @@ -0,0 +1,6 @@ +*1.500* +*3.777* +*1* +*1* +*3* +*3* From 1a899000422116a9d19f0e4636771f36a8f205ad Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 22 Jan 2014 14:01:24 -0800 Subject: [PATCH 71/85] eliminate llvm_prefetch; fixes #2043 --- src/library.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/library.js b/src/library.js index 21d69949d1efd..bc577e7813037 100644 --- a/src/library.js +++ b/src/library.js @@ -4291,6 +4291,8 @@ LibraryManager.library = { abort('trap!'); }, + llvm_prefetch: function(){}, + __assert_fail: function(condition, filename, line, func) { ABORT = true; throw 'Assertion failed: ' + Pointer_stringify(condition) + ', at: ' + [filename ? Pointer_stringify(filename) : 'unknown filename', line, func ? Pointer_stringify(func) : 'unknown function'] + ' at ' + stackTrace(); From 00c0ccc15b36742bdbb472ce09b1eae7616e822f Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 22 Jan 2014 15:57:18 -0800 Subject: [PATCH 72/85] skip test_debug in asm3 --- tests/test_core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_core.py b/tests/test_core.py index 311f33a006002..d25847d74ee67 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5954,7 +5954,7 @@ def test_safe_heap(self): def test_debug(self): if '-g' not in Building.COMPILER_TEST_OPTS: Building.COMPILER_TEST_OPTS.append('-g') if self.emcc_args is not None: - if '-O1' in self.emcc_args or '-O2' in self.emcc_args: return self.skip('optimizations remove LLVM debug info') + if '-O1' in self.emcc_args or '-O2' in self.emcc_args or '-O3' in self.emcc_args: return self.skip('optimizations remove LLVM debug info') src = ''' #include From 7dfbd9ac5ec8d096cbc147d7693814d2d81568bc Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 22 Jan 2014 17:09:02 -0800 Subject: [PATCH 73/85] some headless additions --- src/headless.js | 12 ++++++++++++ src/headlessCanvas.js | 1 + 2 files changed, 13 insertions(+) diff --git a/src/headless.js b/src/headless.js index e545864118ac8..5880c087d9432 100644 --- a/src/headless.js +++ b/src/headless.js @@ -82,6 +82,16 @@ var window = { } listeners.push(func); }, + removeEventListener: function(id, func) { + var listeners = this.eventListeners[id]; + if (!listeners) return; + for (var i = 0; i < listeners.length; i++) { + if (listeners[i] === func) { + listeners.splice(i, 1); + return; + } + } + }, callEventListeners: function(id) { var listeners = this.eventListeners[id]; if (listeners) { @@ -101,6 +111,7 @@ var document = { headless: true, eventListeners: {}, addEventListener: window.addEventListener, + removeEventListener: window.removeEventListener, callEventListeners: window.callEventListeners, getElementById: function(id) { switch(id) { @@ -144,6 +155,7 @@ var document = { }, eventListeners: {}, addEventListener: document.addEventListener, + removeEventListener: document.removeEventListener, callEventListeners: document.callEventListeners, }; }; diff --git a/src/headlessCanvas.js b/src/headlessCanvas.js index 4951aed8d8e2a..6b0f9d47993ae 100644 --- a/src/headlessCanvas.js +++ b/src/headlessCanvas.js @@ -600,6 +600,7 @@ function headlessCanvas() { style: {}, eventListeners: {}, addEventListener: function(){}, + removeEventListener: function(){}, requestFullScreen: function() { document.fullscreenElement = document.getElementById('canvas'); window.setTimeout(function() { From ee3397080daddc7e07ff212da1f9ed4cb8a7f947 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 22 Jan 2014 17:09:36 -0800 Subject: [PATCH 74/85] fix some sdl typos that error on safe heap in asm mode, and add test coverage; fixes #2044 --- src/library_sdl.js | 24 ++++++++++++------------ tests/test_browser.py | 3 +++ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/library_sdl.js b/src/library_sdl.js index 5a1112891d2bb..80734d95fdf61 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -223,19 +223,19 @@ var LibrarySDL = { var is_SDL_HWPALETTE = flags & 0x00200000; var bpp = is_SDL_HWPALETTE ? 1 : 4; - {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.flags, 'flags', 'i32') }}} // SDL_Surface.flags - {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.format, 'pixelFormat', 'void*') }}} // SDL_Surface.format TODO - {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.w, 'width', 'i32') }}} // SDL_Surface.w - {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.h, 'height', 'i32') }}} // SDL_Surface.h - {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pitch, 'width * bpp', 'i32') }}} // SDL_Surface.pitch, assuming RGBA or indexed for now, + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.flags, 'flags', 'i32') }}}; // SDL_Surface.flags + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.format, 'pixelFormat', 'void*') }}};// SDL_Surface.format TODO + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.w, 'width', 'i32') }}}; // SDL_Surface.w + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.h, 'height', 'i32') }}}; // SDL_Surface.h + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pitch, 'width * bpp', 'i32') }}}; // SDL_Surface.pitch, assuming RGBA or indexed for now, // since that is what ImageData gives us in browsers - {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pixels, 'buffer', 'void*') }}} // SDL_Surface.pixels - {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.clip_rect, '0', 'i32*') }}} // SDL_Surface.offset + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pixels, 'buffer', 'void*') }}}; // SDL_Surface.pixels + {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.clip_rect, '0', 'i32*') }}}; // SDL_Surface.offset {{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.refcount, '1', 'i32') }}}; - {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.format, cDefine('SDL_PIXELFORMAT_RGBA8888'), 'i32') }}} // SDL_PIXELFORMAT_RGBA8888 - {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.palette, '0', 'i32') }}} // TODO + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.format, cDefine('SDL_PIXELFORMAT_RGBA8888'), 'i32') }}};// SDL_PIXELFORMAT_RGBA8888 + {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.palette, '0', 'i32') }}};// TODO {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.BitsPerPixel, 'bpp * 8', 'i8') }}}; {{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.BytesPerPixel, 'bpp', 'i8') }}}; @@ -594,7 +594,7 @@ var LibrarySDL = { {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.state, 'down ? 1 : 0', 'i8') }}}; - {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.repeat, '0', 'i8') }}} // TODO + {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.repeat, '0', 'i8') }}}; // TODO {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.scancode, 'scan', 'i32') }}}; {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.sym, 'key', 'i32') }}}; {{{ makeSetValue('ptr', C_STRUCTS.SDL_KeyboardEvent.keysym + C_STRUCTS.SDL_Keysym.mod, 'SDL.modState', 'i16') }}}; @@ -879,8 +879,8 @@ var LibrarySDL = { SDL_GetVideoInfo: function() { // %struct.SDL_VideoInfo = type { i32, i32, %struct.SDL_PixelFormat*, i32, i32 } - 5 fields of quantum size var ret = _malloc(5*Runtime.QUANTUM_SIZE); - {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*0', '0', '0', 'i32') }}} // TODO - {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*1', '0', '0', 'i32') }}} // TODO + {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*0', '0', '0', 'i32') }}}; // TODO + {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*1', '0', '0', 'i32') }}}; // TODO {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*2', '0', '0', 'void*') }}}; {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*3', '0', 'Module["canvas"].width', 'i32') }}}; {{{ makeSetValue('ret+Runtime.QUANTUM_SIZE*4', '0', 'Module["canvas"].height', 'i32') }}}; diff --git a/tests/test_browser.py b/tests/test_browser.py index 9321f5293697a..8c75c86144ddd 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -683,6 +683,9 @@ def test_sdl_stb_image_data(self): def test_sdl_canvas(self): self.btest('sdl_canvas.c', expected='1', args=['-s', 'LEGACY_GL_EMULATION=1']) + # some extra coverage + self.btest('sdl_canvas.c', expected='1', args=['-s', 'LEGACY_GL_EMULATION=1', '-s', '-O0', 'SAFE_HEAP=1']) + self.btest('sdl_canvas.c', expected='1', args=['-s', 'LEGACY_GL_EMULATION=1', '-s', '-O2', 'SAFE_HEAP=1']) def test_sdl_canvas_proxy(self): def post(): From a912e8271c990e59b2dd7c1d03d1adb9151f49de Mon Sep 17 00:00:00 2001 From: Ryan Kelly Date: Thu, 23 Jan 2014 00:28:14 +1100 Subject: [PATCH 75/85] Reduce memory usage when assigning registers in a block. Rather than pre-calculating liveness states and then processing nodes in execution order, we process nodes in reverse execution order and use the liveness info implicit in this traversal. Names are assigned a register when they're used for the first time, and the register is freed when we reach an assignment. --- tools/js-optimizer.js | 163 ++++++++++++++++++++++-------------------- 1 file changed, 84 insertions(+), 79 deletions(-) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index b35da99d371c6..786766817dcdd 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -2711,6 +2711,7 @@ function registerizeHarder(ast) { var link = {}; var lastUseLoc = {}; var firstDeadLoc = {}; + var firstKillLoc = {}; var lastKillLoc = {}; for (var name in live) { link[name] = name; @@ -2735,6 +2736,7 @@ function registerizeHarder(ast) { delete use[name]; delete live[name]; firstDeadLoc[name] = j; + firstKillLoc[name] = j; if (lastUseLoc[name] === undefined) { lastUseLoc[name] = j; } @@ -2760,6 +2762,9 @@ function registerizeHarder(ast) { for (var name in lastUseLoc) { lastUseLoc[name] -= n; } + for (var name in firstKillLoc) { + firstKillLoc[name] -= n; + } for (var name in lastKillLoc) { lastKillLoc[name] -= n; } @@ -2791,6 +2796,7 @@ function registerizeHarder(ast) { block.link = link; block.lastUseLoc = lastUseLoc; block.firstDeadLoc = firstDeadLoc; + block.firstKillLoc = firstKillLoc; block.lastKillLoc = lastKillLoc; } @@ -2975,47 +2981,41 @@ function registerizeHarder(ast) { if (block.nodes.length === 0) continue; var jEnter = junctions[block.entry]; var jExit = junctions[block.exit]; - // Calculate the full internal liveness states for this block. - var liveness = [{}]; - for (var name in jExit.live) { - liveness[0][name] = 1; - } - for (var j=block.nodes.length-1; j>=0; j--) { - var node = block.nodes[j]; - liveness.unshift(copy(liveness[0])); - if (node[0] === 'assign') { - var name = node[2][1]; - delete liveness[0][name]; - } else if (node[0] === 'name') { - var name = node[1]; - liveness[0][name] = 1; - } else { - assert(false, 'unexpected node type: ' + node[0]); - } - } - assert(liveness.length == block.nodes.length + 1); - assert(setSize(setSub(liveness[0], jEnter.live)) == 0); - // Mark the point at which each output reg gets assigned. - // Variables that live past this point must not be assigned + // Mark the point at which each input reg becomes dead. + // Variables alive before this point must not be assigned // to that register. - var outputAssignLoc = {}; - var outputVars = {}; + var inputVars = {} + var inputDeadLoc = {}; + var inputVarsByReg = {}; for (var name in jExit.live) { - var reg = junctionVariables[name].reg; - assert(reg !== null, 'output variable doesnt have a register'); - outputAssignLoc[reg] = block.lastKillLoc[name]; - outputVars[reg] = name; - } - // Scan through in execution order, allocating registers on demand. - // Be careful to avoid conflicts with the output registers. + if (!(name in block.kill)) { + inputVars[name] = 1; + var reg = junctionVariables[name].reg; + assert(reg !== null, 'input variable doesnt have a register'); + inputDeadLoc[reg] = block.firstDeadLoc[name]; + inputVarsByReg[reg] = name; + } + } + for (var name in block.use) { + if (!(name in inputVars)) { + inputVars[name] = 1; + var reg = junctionVariables[name].reg; + assert(reg !== null, 'input variable doesnt have a register'); + inputDeadLoc[reg] = block.firstDeadLoc[name]; + inputVarsByReg[reg] = name; + } + } + assert(setSize(setSub(inputVars, jEnter.live)) == 0); + // Scan through backwards, allocating registers on demand. + // Be careful to avoid conflicts with the input registers. // We consume free registers in last-used order, which helps to // eliminate "x=y" assignments that are the last use of "y". var assignedRegs = {}; var freeRegsByType = copy(allRegsByType); - // Begin with all live vars assigned per the entry-point. - for (var name in liveness[0]) { + // Begin with all live vars assigned per the exit junction. + for (var name in jExit.live) { var reg = junctionVariables[name].reg; - assert(reg !== null, 'input variable doesnt have a register'); + assert(reg !== null, 'output variable doesnt have a register'); assignedRegs[name] = reg; delete freeRegsByType[localVars[name]][reg]; } @@ -3023,66 +3023,71 @@ function registerizeHarder(ast) { freeRegsByType[j] = keys(freeRegsByType[j]); } // Scan through the nodes in sequence, modifying each node in-place - // and freeing registers according to the calculated liveness info. - for (var j = 0; j < block.nodes.length; j++) { + // and grabbing/freeing registers as needed. + var maybeRemoveNodes = []; + for (var j = block.nodes.length - 1; j >= 0; j--) { var node = block.nodes[j]; var name = node[0] === 'assign' ? node[2][1] : node[1]; var allRegs = allRegsByType[localVars[name]]; var freeRegs = freeRegsByType[localVars[name]]; var reg = assignedRegs[name]; if (node[0] === 'name') { - // A use. It should already be in a register. - assert(liveness[j][name], 'use node, but name was not alive?') - assert(reg, 'live variable did not have a reg?') - node[1] = allRegs[reg]; - } else { - // A kill. This should assign it a new register. - assert(!liveness[j][name], 'kill node, but name was alive?') - assert(!reg, 'non-live variable still had a reg?') - if (name in jExit.live && j === block.lastKillLoc[name]) { - // Assignment to an output variable, must use pre-assigned reg. - reg = junctionVariables[name].reg; - assignedRegs[name] = reg; - for (var k = freeRegs.length - 1; k >= 0; k--) { - if (freeRegs[k] === reg) { + // A use. Grab a register if it doesn't have one. + if (!reg) { + if (name in inputVars && j <= block.firstDeadLoc[name]) { + // Assignment to an input variable, must use pre-assigned reg. + reg = junctionVariables[name].reg; + assignedRegs[name] = reg; + for (var k = freeRegs.length - 1; k >= 0; k--) { + if (freeRegs[k] === reg) { + freeRegs.splice(k, 1); + break; + } + } + } else { + // Try to use one of the existing free registers. + // It must not conflict with an input register. + for (var k = freeRegs.length - 1; k >= 0; k--) { + reg = freeRegs[k]; + // Check for conflict with input registers. + if (block.firstKillLoc[name] <= inputDeadLoc[reg]) { + if (name !== inputVarsByReg[reg]) { + continue; + } + } + // Found one! + assignedRegs[name] = reg; freeRegs.splice(k, 1); break; } - } - } else { - // Try to use one of the existing free registers. - // It must not conflict with an output register. - for (var k = freeRegs.length - 1; k >= 0; k--) { - reg = freeRegs[k]; - // Check for conflict with output registers. - if (block.lastUseLoc[name] > outputAssignLoc[reg]) { - if (name !== outputVars[reg]) { - continue; - } + // If we didn't find a suitable register, create a new one. + if (!assignedRegs[name]) { + reg = createReg(name); + assignedRegs[name] = reg; } - // Found one! - assignedRegs[name] = reg; - freeRegs.splice(k, 1); - break; - } - // If we didn't find a suitable register, create a new one. - if (!assignedRegs[name]) { - reg = createReg(name); - assignedRegs[name] = reg; } } - // Modify assignment to use the new name. - // If we happen to create a "x=x" type do-nothing assignment, - // we can safely morph it into a no-op. + node[1] = allRegs[reg]; + } else { + // A kill. This frees the assigned register. + assert(reg, 'live variable doesnt have a reg?') node[2][1] = allRegs[reg]; - if (node[3][0] === 'name' && node[3][1] === node[2][1]) { - morphNode(node, ['block', []]); + freeRegs.push(reg); + delete assignedRegs[name]; + if (node[3][0] === 'name' && node[3][1] in localVars) { + maybeRemoveNodes.push([j, node]); } } - // Free the reg if it's not live in the next step. - if (!liveness[j+1][name]) { - delete assignedRegs[name]; - freeRegs.push(reg); + } + // If we managed to create an "x=x" assignments, remove them. + for (var j = 0; j < maybeRemoveNodes.length; j++) { + var node = maybeRemoveNodes[j][1]; + if (node[2][1] === node[3][1]) { + if (block.isexpr[maybeRemoveNodes[j][0]]) { + morphNode(node, node[2]); + } else { + morphNode(node, ['block', []]); + } } } } From 0bae95f2fc0f917b2ddf7343171c2353aa11a820 Mon Sep 17 00:00:00 2001 From: Ryan Kelly Date: Thu, 23 Jan 2014 21:59:13 +1100 Subject: [PATCH 76/85] Add dedicated testcases for registerizeHarder pass. --- tests/test_other.py | 2 + ...est-js-optimizer-asm-regs-harder-output.js | 131 +++++++++++++++ tools/test-js-optimizer-asm-regs-harder.js | 150 ++++++++++++++++++ 3 files changed, 283 insertions(+) create mode 100644 tools/test-js-optimizer-asm-regs-harder-output.js create mode 100644 tools/test-js-optimizer-asm-regs-harder.js diff --git a/tests/test_other.py b/tests/test_other.py index 53128391fc6fc..8895a911ff449 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -1773,6 +1773,8 @@ def test_js_optimizer(self): ['asm', 'eliminate']), (path_from_root('tools', 'test-js-optimizer-asm-regs.js'), open(path_from_root('tools', 'test-js-optimizer-asm-regs-output.js')).read(), ['asm', 'registerize']), + (path_from_root('tools', 'test-js-optimizer-asm-regs-harder.js'), open(path_from_root('tools', 'test-js-optimizer-asm-regs-harder-output.js')).read(), + ['asm', 'registerizeHarder']), (path_from_root('tools', 'test-js-optimizer-asm-regs-min.js'), open(path_from_root('tools', 'test-js-optimizer-asm-regs-min-output.js')).read(), ['asm', 'registerize', 'minifyLocals']), (path_from_root('tools', 'test-js-optimizer-asm-pre.js'), open(path_from_root('tools', 'test-js-optimizer-asm-pre-output.js')).read(), diff --git a/tools/test-js-optimizer-asm-regs-harder-output.js b/tools/test-js-optimizer-asm-regs-harder-output.js new file mode 100644 index 0000000000000..710018bba8890 --- /dev/null +++ b/tools/test-js-optimizer-asm-regs-harder-output.js @@ -0,0 +1,131 @@ +function asm(d1, i2) { + d1 = +d1; + i2 = i2 | 0; + i2 = d1 + d1 | 0; + d1 = d(Math_max(10, Math_min(5, f()))); + i2 = i2 + 2 | 0; + print(i2); + d1 = d1 * 5; + return d1; +} +function _doit(i3, i2, i1) { + i3 = i3 | 0; + i2 = i2 | 0; + i1 = i1 | 0; + i3 = STACKTOP; + _printf(__str | 0, (tempInt = STACKTOP, STACKTOP = STACKTOP + 8 | 0, HEAP32[(tempInt & 16777215) >> 2] = i2, HEAP32[(tempInt + 4 & 16777215) >> 2] = i1, tempInt)); + STACKTOP = i3; + return 0 | 0; +} +function stackRestore(i1) { + i1 = i1 | 0; + STACKTOP = i1; +} +function switchey(d1, i2) { + d1 = +d1; + i2 = i2 | 0; + switch (d1 | 0) { + case 0: + i2 = d1 + d1 | 0; + d1 = d(Math_max(10, Math_min(5, f()))); + i2 = i2 + 2 | 0; + print(i2); + d1 = d1 * 5; + return d1; + case 1: + return 20; + } +} +function switchey2() { + var i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0, d7 = +0, d8 = +0; + i2 = STACKTOP; + STACKTOP = STACKTOP + 8 | 0; + i6 = 1; + while (1) switch (i6 | 0) { + case 1: + i1 = i2 | 0; + __ZN6RandomC1Ev(i1); + i4 = 0; + i3 = 0; + i6 = 2; + break; + case 2: + d8 = +__ZN6Random3getEf(8, +1); + d7 = +__ZN6Random3getEf(i1, +1); + _printf(24, (tempInt = STACKTOP, STACKTOP = STACKTOP + 16 | 0, HEAPF64[CHECK_ALIGN_8(tempInt | 0) >> 3] = d8, HEAPF64[CHECK_ALIGN_8(tempInt + 8 | 0) >> 3] = d7, tempInt) | 0); + i5 = (d8 != d7 & 1) + i3 | 0; + i6 = i4 + 1 | 0; + if ((i6 | 0) < 100) { + i4 = i6; + i3 = i5; + i6 = 2; + break; + } else { + i6 = 3; + break; + } + case 3: + _printf(16, (tempInt = STACKTOP, STACKTOP = STACKTOP + 8 | 0, HEAP32[CHECK_ALIGN_4(tempInt | 0) >> 2] = i5, tempInt) | 0); + STACKTOP = i2; + return 0; + } + return 0; +} +function iffey() { + var i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, d6 = +0, d7 = +0; + i2 = STACKTOP; + STACKTOP = STACKTOP + 8 | 0; + i4 = 1; + while (1) { + if (i4 | 0) { + i1 = i2 | 0; + __ZN6RandomC1Ev(i1); + i3 = 0; + i5 = 0; + i4 = 2; + } else { + d7 = +__ZN6Random3getEf(8, +1); + d6 = +__ZN6Random3getEf(i1, +1); + _printf(24, (tempInt = STACKTOP, STACKTOP = STACKTOP + 16 | 0, HEAPF64[CHECK_ALIGN_8(tempInt | 0) >> 3] = d7, HEAPF64[CHECK_ALIGN_8(tempInt + 8 | 0) >> 3] = d6, tempInt) | 0); + i5 = (d7 != d6 & 1) + i5 | 0; + i3 = i3 + 1 | 0; + if ((i3 | 0) < 100) { + i4 = 2; + } else { + return 10; + } + } + } + return 0; +} +function labelledJump(i3) { + i3 = i3 | 0; + var i1 = 0, i2 = 0; + i2 = 2; + if (i3) { + i2 = 17; + i1 = 1; + } + if (i1 == 1) { + i3 = i2 + 1; + } else { + i3 = i2 + 1; + } + return i3; +} +function linkedVars() { + var i1 = 0, i2 = 0; + while (1) { + i2 = 9; + i1 = 5; + while (i2 > 0 || i1 > 0) { + if (i2 < i1) { + i2 = i2 - 1; + } else { + i1 = i1 - 1; + } + } + } + return i2 + i1; +} + diff --git a/tools/test-js-optimizer-asm-regs-harder.js b/tools/test-js-optimizer-asm-regs-harder.js new file mode 100644 index 0000000000000..cf44c1dd2921e --- /dev/null +++ b/tools/test-js-optimizer-asm-regs-harder.js @@ -0,0 +1,150 @@ +function asm(x, y) { + x = +x; + y = y | 0; + var int1 = 0, int2 = 0; // do not mix the types! + var double1 = +0, double2 = +0; + int1 = (x+x)|0; + double1 = d(Math_max(10, Math_min(5, f()))); + int2 = (int1+2)|0; + print(int2); + double2 = double1*5; + return double2; +} +function _doit($x, $y$0, $y$1) { + $x = $x | 0; + $y$0 = $y$0 | 0; + $y$1 = $y$1 | 0; + var __stackBase__ = 0; + __stackBase__ = STACKTOP; + _printf(__str | 0, (tempInt = STACKTOP, STACKTOP = STACKTOP + 8 | 0, HEAP32[(tempInt & 16777215) >> 2] = $y$0, HEAP32[(tempInt + 4 & 16777215) >> 2] = $y$1, tempInt)); + STACKTOP = __stackBase__; + return 0 | 0; +} +function stackRestore(top) { + top = top|0; + STACKTOP = top; +} +function switchey(x, y) { + x = +x; + y = y | 0; + var int1 = 0, int2 = 0; // do not mix the types! + var double1 = +0, double2 = +0; + switch(x|0) { + case 0: + int1 = (x+x)|0; + double1 = d(Math_max(10, Math_min(5, f()))); + int2 = (int1+2)|0; + print(int2); + double2 = double1*5; + return double2; + case 1: + return 20; + } +} +function switchey2() { + var $rng2 = 0, $count_06 = 0, $i_05 = 0, $2 = +0, $3 = +0, $count_1 = 0, $9 = 0, label = 0, __stackBase__ = 0; + __stackBase__ = STACKTOP; + STACKTOP = STACKTOP + 8 | 0; + label = 1; + while (1) switch (label | 0) { + case 1: + $rng2 = __stackBase__ | 0; + __ZN6RandomC1Ev($rng2); + $i_05 = 0; + $count_06 = 0; + label = 2; + break; + case 2: + $2 = +__ZN6Random3getEf(8, +1); + $3 = +__ZN6Random3getEf($rng2, +1); + _printf(24, (tempInt = STACKTOP, STACKTOP = STACKTOP + 16 | 0, HEAPF64[CHECK_ALIGN_8(tempInt | 0) >> 3] = $2, HEAPF64[CHECK_ALIGN_8(tempInt + 8 | 0) >> 3] = $3, tempInt) | 0); + $count_1 = ($2 != $3 & 1) + $count_06 | 0; + $9 = $i_05 + 1 | 0; + if (($9 | 0) < 100) { + $i_05 = $9; + $count_06 = $count_1; + label = 2; + break; + } else { + label = 3; + break; + } + case 3: + _printf(16, (tempInt = STACKTOP, STACKTOP = STACKTOP + 8 | 0, HEAP32[CHECK_ALIGN_4(tempInt | 0) >> 2] = $count_1, tempInt) | 0); + STACKTOP = __stackBase__; + return 0; + } + return 0; +} +function iffey() { + var $rng2 = 0, $count_06 = 0, $i_05 = 0, $2 = +0, $3 = +0, $count_1 = 0, $9 = 0, label = 0, __stackBase__ = 0; + __stackBase__ = STACKTOP; + STACKTOP = STACKTOP + 8 | 0; + label = 1; + while (1) { + if (label | 0) { + $rng2 = __stackBase__ | 0; + __ZN6RandomC1Ev($rng2); + $i_05 = 0; + $count_06 = 0; + label = 2; + } else { + $2 = +__ZN6Random3getEf(8, +1); + $3 = +__ZN6Random3getEf($rng2, +1); + _printf(24, (tempInt = STACKTOP, STACKTOP = STACKTOP + 16 | 0, HEAPF64[CHECK_ALIGN_8(tempInt | 0) >> 3] = $2, HEAPF64[CHECK_ALIGN_8(tempInt + 8 | 0) >> 3] = $3, tempInt) | 0); + $count_1 = ($2 != $3 & 1) + $count_06 | 0; + $9 = $i_05 + 1 | 0; + if (($9 | 0) < 100) { + $i_05 = $9; + $count_06 = $count_1; + label = 2; + } else { + label = 3; + return 10; + } + } + } + return 0; +} +function labelledJump(x) { + x = x | 0; + var label = 0 + // y and z don't conflict, but only if you know about labelled jumps. + var y = 0, z = 0; + y = 2; + if (x) { + z = 17; + label = 1; + } + if (label == 1) { + x = z + 1; + } else { + x = y + 1; + } + return x; +} +function linkedVars() { + var outer1 = 0, outer2 = 0; + var inner1_0 = 0, inner1_1 = 0, inner2_0 = 0, inner2_1 = 0; + while (1) { + outer1 = 9; + outer2 = 5; + while (outer1 > 0 || outer2 > 0) { + // All these copy assignment should be eliminated by var sharing. + inner1_0 = outer1; + inner2_0 = outer2; + if (inner1_0 < inner2_0) { + inner1_1 = inner1_0 - 1; + inner2_1 = inner2_0; + } else { + inner1_1 = inner1_0; + inner2_1 = inner2_0 - 1; + } + outer1 = inner1_1; + outer2 = inner2_1; + } + } + return outer1 + outer2; +} +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "_doit", "stackRestore", "switchey", "switchey2", "iffey", "labelledJump", "linkedVars'] + From d21f2ea3adcd5c4f0d6e2ffaaa61f24dbb581737 Mon Sep 17 00:00:00 2001 From: Ryan Kelly Date: Thu, 23 Jan 2014 22:18:57 +1100 Subject: [PATCH 77/85] Simplify handling of switch statements in registerizeHarder. It now assumes they're of the very restricted for that's actually produced by emscripten, which both simplified the code and uncovered opportunities for further register reduction in a testcase. --- tools/js-optimizer.js | 41 ++++++------------- ...est-js-optimizer-asm-regs-harder-output.js | 28 ++++++------- 2 files changed, 26 insertions(+), 43 deletions(-) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 786766817dcdd..abb854c6432b3 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -2460,55 +2460,40 @@ function registerizeHarder(ast) { buildFlowGraph(node[2]); break; case 'switch': - // This is horrific. It need to capture the default flow-through - // logic of sequential case bodies, as well as the theoretical - // sequential evaluation of each case clause. - // TODO: simplify based on asmjs switch-statement restrictions. + // Emscripten generates switch statements of a very limited + // form: all case clauses are numeric literals, and all + // case bodies end with a break. So it's basically equivalent + // to a multi-way 'if' statement. isInExpr++; buildFlowGraph(node[1]); isInExpr--; var jCheckExit = markJunction(); var jExit = addJunction(); pushActiveLabels(null, jExit); - // Process all cases as a sequential chain of checks. - // Process all case bodies as one big flow-through statement. - // They might break themselves out of it but this implements the - // default fall-through case logic. var hasDefault = false; - var jPrevCaseExit = jCheckExit; - var jPrevBodyExit = jCheckExit; for (var i=0; i> 3] = d8, HEAPF64[CHECK_ALIGN_8(tempInt + 8 | 0) >> 3] = d7, tempInt) | 0); - i5 = (d8 != d7 & 1) + i3 | 0; - i6 = i4 + 1 | 0; - if ((i6 | 0) < 100) { - i4 = i6; - i3 = i5; - i6 = 2; + d7 = +__ZN6Random3getEf(8, +1); + d6 = +__ZN6Random3getEf(i1, +1); + _printf(24, (tempInt = STACKTOP, STACKTOP = STACKTOP + 16 | 0, HEAPF64[CHECK_ALIGN_8(tempInt | 0) >> 3] = d7, HEAPF64[CHECK_ALIGN_8(tempInt + 8 | 0) >> 3] = d6, tempInt) | 0); + i5 = (d7 != d6 & 1) + i5 | 0; + i4 = i4 + 1 | 0; + if ((i4 | 0) < 100) { + i3 = 2; break; } else { - i6 = 3; + i3 = 3; break; } case 3: From d55c07198d9aa3352cda6e44d73dc160e9a877fe Mon Sep 17 00:00:00 2001 From: Ryan Kelly Date: Thu, 23 Jan 2014 22:30:42 +1100 Subject: [PATCH 78/85] Add TODO about more permissive label handling in registerizeHarder --- tools/js-optimizer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index abb854c6432b3..21d521fd98713 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -2597,6 +2597,7 @@ function registerizeHarder(ast) { if (labelCond && labelCond[0] === '==') { // If there are multiple blocks with the same label, all bets are off. // This seems to happen sometimes for short blocks that end with a return. + // TODO: it should be safe to merge the duplicates if they're identical. if (labelCond[1] in labelledBlocks) { labelledBlocks = {}; labelledJumps = []; From 9d8957c9209dd3f0d62519a47963b4968585ac72 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 23 Jan 2014 16:13:49 -0800 Subject: [PATCH 79/85] better error reporting on errors in js libraries; fixes #2033 --- src/modules.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/modules.js b/src/modules.js index ad467ba7694a4..d1ef4243135b8 100644 --- a/src/modules.js +++ b/src/modules.js @@ -426,7 +426,20 @@ var LibraryManager = { var libraries = ['library.js', 'library_path.js', 'library_fs.js', 'library_idbfs.js', 'library_memfs.js', 'library_nodefs.js', 'library_sockfs.js', 'library_tty.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js', 'library_jansson.js', 'library_openal.js', 'library_glfw.js', 'library_uuid.js', 'library_glew.js'].concat(additionalLibraries); for (var i = 0; i < libraries.length; i++) { - eval(processMacros(preprocess(read(libraries[i])))); + var filename = libraries[i]; + var src = read(filename); + try { + var processed = processMacros(preprocess(src)); + eval(processed); + } catch(e) { + var details = [e, e.lineNumber ? 'line number: ' + e.lineNumber : '', (e.stack || "").toString().replace('Object.', filename)]; + if (processed) { + error('failure to execute js library "' + filename + '": ' + details + '\npreprocessed source (you can run a js engine on this to get a clearer error message sometimes):\n=============\n' + processed + '\n=============\n'); + } else { + error('failure to process js library "' + filename + '": ' + details + '\noriginal source:\n=============\n' + src + '\n=============\n'); + } + throw e; + } } /* From 91de0c9e7848d370d3113592c6d22b5b22c4ef09 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 23 Jan 2014 16:23:14 -0800 Subject: [PATCH 80/85] export the runtime; fixes #2041 --- src/preamble.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/preamble.js b/src/preamble.js index d415b87e16461..9a65b092729e0 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -6,6 +6,8 @@ {{RUNTIME}} +Module['Runtime'] = Runtime; + #if ASM_JS #if RESERVED_FUNCTION_POINTERS function jsCall() { From c99a28dedf06cd1114f0b4f782b2e1e1d6c10051 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 23 Jan 2014 17:52:57 -0800 Subject: [PATCH 81/85] allow adding a custom prefix to file package downloads, so they can happen from a different url; fixes #2049 --- tests/test_browser.py | 39 +++++++++++++++++++++++++++++++++++++++ tools/file_packager.py | 8 ++++---- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/tests/test_browser.py b/tests/test_browser.py index 02bcecbda4453..2c9407dbaa1e1 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -580,6 +580,45 @@ def test_multifile(self): shutil.rmtree(os.path.join(self.get_dir(), 'subdirr')) self.run_browser('page.html', 'You should see two cool numbers', '/report_result?1') + def test_custom_file_package_url(self): + # a few files inside a directory + self.clear() + os.makedirs(os.path.join(self.get_dir(), 'subdirr')); + os.makedirs(os.path.join(self.get_dir(), 'cdn')); + open(os.path.join(self.get_dir(), 'subdirr', 'data1.txt'), 'w').write('''1214141516171819''') + # change the file package base dir to look in a "cdn". note that normally you would add this in your own custom html file etc., and not by + # modifying the existing shell in this manner + open(self.in_dir('shell.html'), 'w').write(open(path_from_root('src', 'shell.html')).read().replace('var Module = {', 'var Module = { filePackageURL: "cdn/", ')) + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(self.with_report_result(r''' + #include + #include + #include + int main() { + char buf[17]; + + FILE *f = fopen("subdirr/data1.txt", "r"); + fread(buf, 1, 16, f); + buf[16] = 0; + fclose(f); + printf("|%s|\n", buf); + int result = !strcmp("1214141516171819", buf); + + REPORT_RESULT(); + return 0; + } + ''')) + + def test(): + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--shell-file', 'shell.html', '--preload-file', 'subdirr/data1.txt', '-o', 'test.html']).communicate() + shutil.move('test.data', os.path.join('cdn', 'test.data')) + self.run_browser('test.html', '', '/report_result?1') + + test() + + # TODO: CORS, test using a full url for filePackageURL + #open(self.in_dir('shell.html'), 'w').write(open(path_from_root('src', 'shell.html')).read().replace('var Module = {', 'var Module = { filePackageURL: "http:/localhost:8888/cdn/", ')) + #test() + def test_compressed_file(self): open(os.path.join(self.get_dir(), 'datafile.txt'), 'w').write('compress this please' + (2000*'.')) open(os.path.join(self.get_dir(), 'datafile2.txt'), 'w').write('moar' + (100*'!')) diff --git a/tools/file_packager.py b/tools/file_packager.py index 8b65b219c59c4..6c60db588f7b8 100644 --- a/tools/file_packager.py +++ b/tools/file_packager.py @@ -462,7 +462,7 @@ def was_seen(name): package_uuid = uuid.uuid4(); remote_package_name = os.path.basename(Compression.compressed_name(data_target) if Compression.on else data_target) - code += r''' + ret += r''' var PACKAGE_PATH; if (typeof window === 'object') { PACKAGE_PATH = window['encodeURIComponent'](window.location.pathname.toString().substring(0, window.location.pathname.toString().lastIndexOf('/')) + '/'); @@ -471,7 +471,7 @@ def was_seen(name): PACKAGE_PATH = encodeURIComponent(location.pathname.toString().substring(0, location.pathname.toString().lastIndexOf('/')) + '/'); } var PACKAGE_NAME = '%s'; - var REMOTE_PACKAGE_NAME = '%s'; + var REMOTE_PACKAGE_NAME = (Module['filePackageURL'] || '') + '%s'; var PACKAGE_UUID = '%s'; ''' % (data_target, remote_package_name, package_uuid) @@ -666,7 +666,7 @@ def was_seen(name): # Only tricky bit is the fetch is async, but also when runWithFS is called is async, so we handle both orderings. ret += r''' var fetched = null, fetchedCallback = null; - fetchRemotePackage('%s', function(data) { + fetchRemotePackage(REMOTE_PACKAGE_NAME, function(data) { if (fetchedCallback) { fetchedCallback(data); fetchedCallback = null; @@ -674,7 +674,7 @@ def was_seen(name): fetched = data; } }, handleError); - ''' % os.path.basename(Compression.compressed_name(data_target) if Compression.on else data_target) + ''' code += r''' Module.preloadResults[PACKAGE_NAME] = {fromCache: false}; From 958492b5ef450407e2a29380a67534d876403b97 Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Thu, 23 Jan 2014 16:49:56 -0800 Subject: [PATCH 82/85] use correct bufferData method when passing a NULL pointer --- src/library_gl.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/library_gl.js b/src/library_gl.js index 3055309bb4b34..0a30292aa8e77 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -957,7 +957,11 @@ var LibraryGL = { usage = 0x88E8; // GL_DYNAMIC_DRAW break; } - GLctx.bufferData(target, HEAPU8.subarray(data, data+size), usage); + if (!data) { + GLctx.bufferData(target, size, usage); + } else { + GLctx.bufferData(target, HEAPU8.subarray(data, data+size), usage); + } }, glBufferSubData__sig: 'viiii', From 44998c302a9cd458d57802129c0cbc9d61dc10e7 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 23 Jan 2014 17:58:46 -0800 Subject: [PATCH 83/85] rename filePackageURL to filePackagePrefixURL --- tests/test_browser.py | 6 +++--- tools/file_packager.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_browser.py b/tests/test_browser.py index 2c9407dbaa1e1..3000240de475f 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -588,7 +588,7 @@ def test_custom_file_package_url(self): open(os.path.join(self.get_dir(), 'subdirr', 'data1.txt'), 'w').write('''1214141516171819''') # change the file package base dir to look in a "cdn". note that normally you would add this in your own custom html file etc., and not by # modifying the existing shell in this manner - open(self.in_dir('shell.html'), 'w').write(open(path_from_root('src', 'shell.html')).read().replace('var Module = {', 'var Module = { filePackageURL: "cdn/", ')) + open(self.in_dir('shell.html'), 'w').write(open(path_from_root('src', 'shell.html')).read().replace('var Module = {', 'var Module = { filePackagePrefixURL: "cdn/", ')) open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(self.with_report_result(r''' #include #include @@ -615,8 +615,8 @@ def test(): test() - # TODO: CORS, test using a full url for filePackageURL - #open(self.in_dir('shell.html'), 'w').write(open(path_from_root('src', 'shell.html')).read().replace('var Module = {', 'var Module = { filePackageURL: "http:/localhost:8888/cdn/", ')) + # TODO: CORS, test using a full url for filePackagePrefixURL + #open(self.in_dir('shell.html'), 'w').write(open(path_from_root('src', 'shell.html')).read().replace('var Module = {', 'var Module = { filePackagePrefixURL: "http:/localhost:8888/cdn/", ')) #test() def test_compressed_file(self): diff --git a/tools/file_packager.py b/tools/file_packager.py index 6c60db588f7b8..9699730f9ab4e 100644 --- a/tools/file_packager.py +++ b/tools/file_packager.py @@ -471,7 +471,7 @@ def was_seen(name): PACKAGE_PATH = encodeURIComponent(location.pathname.toString().substring(0, location.pathname.toString().lastIndexOf('/')) + '/'); } var PACKAGE_NAME = '%s'; - var REMOTE_PACKAGE_NAME = (Module['filePackageURL'] || '') + '%s'; + var REMOTE_PACKAGE_NAME = (Module['filePackagePrefixURL'] || '') + '%s'; var PACKAGE_UUID = '%s'; ''' % (data_target, remote_package_name, package_uuid) From 9f7690fa3de0d28ed93c88f2a80537a79bfd2847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Fri, 24 Jan 2014 11:57:18 +0200 Subject: [PATCH 84/85] Rename library_events to library_html5 and other suggested renamings. Add better support for reporting EMSCRIPTEN_RESULT_NOT_SUPPORTED for unsupported features. --- src/{library_events.js => library_html5.js} | 722 +++++++++--------- src/modules.js | 2 +- src/struct_info.json | 35 +- .../include/emscripten/{events.h => html5.h} | 149 ++-- tests/test_browser.py | 4 +- tests/test_events.c | 382 --------- tests/test_html5.c | 474 ++++++++++++ 7 files changed, 940 insertions(+), 828 deletions(-) rename src/{library_events.js => library_html5.js} (54%) rename system/include/emscripten/{events.h => html5.h} (86%) delete mode 100644 tests/test_events.c create mode 100644 tests/test_html5.c diff --git a/src/library_events.js b/src/library_html5.js similarity index 54% rename from src/library_events.js rename to src/library_html5.js index d1a8612779fed..703f9a7442806 100644 --- a/src/library_events.js +++ b/src/library_html5.js @@ -1,5 +1,5 @@ -var LibraryJsEvents = { - $JsEvents: { +var LibraryJSEvents = { + $JSEvents: { // pointers to structs malloc()ed to Emscripten HEAP for JS->C interop. keyEvent: 0, mouseEvent: 0, @@ -18,13 +18,13 @@ var LibraryJsEvents = { removeEventListenersRegistered: false, registerRemoveEventListeners: function() { - if (!JsEvents.removeEventListenersRegistered) { + if (!JSEvents.removeEventListenersRegistered) { __ATEXIT__.push({ func: function() { - for(var i = JsEvents.eventHandlers.length-1; i >= 0; --i) { - JsEvents._removeHandler(i); + for(var i = JSEvents.eventHandlers.length-1; i >= 0; --i) { + JSEvents._removeHandler(i); } } }); - JsEvents.removeEventListenersRegistered = true; + JSEvents.removeEventListenersRegistered = true; } }, @@ -33,20 +33,13 @@ var LibraryJsEvents = { if (typeof target == "number") { target = Pointer_stringify(target); } - if (target == '#window') { - return window; - } else if (target == '#document') { - return document; - } else if (target == '#screen') { - return window.screen; - } else if (target == '#canvas') { - return Module['canvas']; - } - if (typeof target == 'string') { - return document.getElementById(target); - } else { - return target; - } + if (target == '#window') return window; + else if (target == '#document') return document; + else if (target == '#screen') return window.screen; + else if (target == '#canvas') return Module['canvas']; + + if (typeof target == 'string') return document.getElementById(target); + else return target; } else { // The sensible target varies between events, but use window as the default // since DOM events mostly can default to that. Specific callback registrations @@ -64,53 +57,50 @@ var LibraryJsEvents = { // and sort by that to always request fullscreen before pointer lock. deferCall: function(targetFunction, precedence, argsList) { function arraysHaveEqualContent(arrA, arrB) { - if (arrA.length != arrB.length) { - return false; - } + if (arrA.length != arrB.length) return false; + for(var i in arrA) { - if (arrA[i] != arrB[i]) { - return false; - } + if (arrA[i] != arrB[i]) return false; } return true; } // Test if the given call was already queued, and if so, don't add it again. - for(var i in JsEvents.deferredCalls) { - var call = JsEvents.deferredCalls[i]; + for(var i in JSEvents.deferredCalls) { + var call = JSEvents.deferredCalls[i]; if (call.targetFunction == targetFunction && arraysHaveEqualContent(call.argsList, argsList)) { return; } } - JsEvents.deferredCalls.push({ + JSEvents.deferredCalls.push({ targetFunction: targetFunction, precedence: precedence, argsList: argsList }); - JsEvents.deferredCalls.sort(function(x,y) { return x.precedence < y.precedence; }); + JSEvents.deferredCalls.sort(function(x,y) { return x.precedence < y.precedence; }); }, // Erases all deferred calls to the given target function from the queue list. removeDeferredCalls: function(targetFunction) { - for(var i = 0; i < JsEvents.deferredCalls.length; ++i) { - if (JsEvents.deferredCalls[i].targetFunction == targetFunction) { - JsEvents.deferredCalls.splice(i, 1); + for(var i = 0; i < JSEvents.deferredCalls.length; ++i) { + if (JSEvents.deferredCalls[i].targetFunction == targetFunction) { + JSEvents.deferredCalls.splice(i, 1); --i; } } }, canPerformEventHandlerRequests: function() { - return JsEvents.inEventHandler && JsEvents.currentEventHandler.allowsDeferredCalls; + return JSEvents.inEventHandler && JSEvents.currentEventHandler.allowsDeferredCalls; }, runDeferredCalls: function() { - if (!JsEvents.canPerformEventHandlerRequests()) { + if (!JSEvents.canPerformEventHandlerRequests()) { return; } - for(var i = 0; i < JsEvents.deferredCalls.length; ++i) { - var call = JsEvents.deferredCalls[i]; - JsEvents.deferredCalls.splice(i, 1); + for(var i = 0; i < JSEvents.deferredCalls.length; ++i) { + var call = JSEvents.deferredCalls[i]; + JSEvents.deferredCalls.splice(i, 1); --i; call.targetFunction.apply(this, call.argsList); } @@ -125,165 +115,167 @@ var LibraryJsEvents = { eventHandlers: [], _removeHandler: function(i) { - JsEvents.eventHandlers[i].target.removeEventListener(JsEvents.eventHandlers[i].eventTypeString, JsEvents.eventHandlers[i].handlerFunc, true); - JsEvents.eventHandlers.splice(i, 1); + JSEvents.eventHandlers[i].target.removeEventListener(JSEvents.eventHandlers[i].eventTypeString, JSEvents.eventHandlers[i].handlerFunc, true); + JSEvents.eventHandlers.splice(i, 1); }, registerOrRemoveHandler: function(eventHandler) { var jsEventHandler = function jsEventHandler(event) { // Increment nesting count for the event handler. - ++JsEvents.inEventHandler; - JsEvents.currentEventHandler = eventHandler; + ++JSEvents.inEventHandler; + JSEvents.currentEventHandler = eventHandler; // Process any old deferred calls the user has placed. - JsEvents.runDeferredCalls(); + JSEvents.runDeferredCalls(); // Process the actual event, calls back to user C code handler. eventHandler.handlerFunc(event); // Process any new deferred calls that were placed right now from this event handler. - JsEvents.runDeferredCalls(); + JSEvents.runDeferredCalls(); // Out of event handler - restore nesting count. - --JsEvents.inEventHandler; + --JSEvents.inEventHandler; } if (eventHandler.callbackfunc) { eventHandler.target.addEventListener(eventHandler.eventTypeString, jsEventHandler, eventHandler.useCapture); - JsEvents.eventHandlers.push(eventHandler); - JsEvents.registerRemoveEventListeners(); + JSEvents.eventHandlers.push(eventHandler); + JSEvents.registerRemoveEventListeners(); } else { - for(var i = 0; i < JsEvents.eventHandlers.length; ++i) { - if (JsEvents.eventHandlers[i].target == eventHandler.target - && JsEvents.eventHandlers[i].eventTypeString == eventHandler.eventTypeString) { - JsEvents._removeHandler(i--); + for(var i = 0; i < JSEvents.eventHandlers.length; ++i) { + if (JSEvents.eventHandlers[i].target == eventHandler.target + && JSEvents.eventHandlers[i].eventTypeString == eventHandler.eventTypeString) { + JSEvents._removeHandler(i--); } } } }, registerKeyEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { - if (!JsEvents.keyEvent) { - JsEvents.keyEvent = _malloc( {{{ C_STRUCTS.emscripten_KeyboardEvent.__size__ }}} ); + if (!JSEvents.keyEvent) { + JSEvents.keyEvent = _malloc( {{{ C_STRUCTS.EmscriptenKeyboardEvent.__size__ }}} ); } var handlerFunc = function(event) { var e = event || window.event; - writeStringToMemory(e.key ? e.key : "", JsEvents.keyEvent + {{{ C_STRUCTS.emscripten_KeyboardEvent.key }}} ); - writeStringToMemory(e.code ? e.code : "", JsEvents.keyEvent + {{{ C_STRUCTS.emscripten_KeyboardEvent.code }}} ); - {{{ makeSetValue('JsEvents.keyEvent', C_STRUCTS.emscripten_KeyboardEvent.location, 'e.location', 'i32') }}} - {{{ makeSetValue('JsEvents.keyEvent', C_STRUCTS.emscripten_KeyboardEvent.ctrlKey, 'e.ctrlKey', 'i32') }}} - {{{ makeSetValue('JsEvents.keyEvent', C_STRUCTS.emscripten_KeyboardEvent.shiftKey, 'e.shiftKey', 'i32') }}} - {{{ makeSetValue('JsEvents.keyEvent', C_STRUCTS.emscripten_KeyboardEvent.altKey, 'e.altKey', 'i32') }}} - {{{ makeSetValue('JsEvents.keyEvent', C_STRUCTS.emscripten_KeyboardEvent.metaKey, 'e.metaKey', 'i32') }}} - {{{ makeSetValue('JsEvents.keyEvent', C_STRUCTS.emscripten_KeyboardEvent.repeat, 'e.repeat', 'i32') }}} - writeStringToMemory(e.locale ? e.locale : "", JsEvents.keyEvent + {{{ C_STRUCTS.emscripten_KeyboardEvent.locale }}} ); - writeStringToMemory(e.char ? e.char : "", JsEvents.keyEvent + {{{ C_STRUCTS.emscripten_KeyboardEvent.charValue }}} ); - {{{ makeSetValue('JsEvents.keyEvent', C_STRUCTS.emscripten_KeyboardEvent.charCode, 'e.charCode', 'i32') }}} - {{{ makeSetValue('JsEvents.keyEvent', C_STRUCTS.emscripten_KeyboardEvent.keyCode, 'e.keyCode', 'i32') }}} - {{{ makeSetValue('JsEvents.keyEvent', C_STRUCTS.emscripten_KeyboardEvent.which, 'e.which', 'i32') }}} - var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.keyEvent, userData]); + writeStringToMemory(e.key ? e.key : "", JSEvents.keyEvent + {{{ C_STRUCTS.EmscriptenKeyboardEvent.key }}} ); + writeStringToMemory(e.code ? e.code : "", JSEvents.keyEvent + {{{ C_STRUCTS.EmscriptenKeyboardEvent.code }}} ); + {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.location, 'e.location', 'i32') }}} + {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.ctrlKey, 'e.ctrlKey', 'i32') }}} + {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.shiftKey, 'e.shiftKey', 'i32') }}} + {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.altKey, 'e.altKey', 'i32') }}} + {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.metaKey, 'e.metaKey', 'i32') }}} + {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.repeat, 'e.repeat', 'i32') }}} + writeStringToMemory(e.locale ? e.locale : "", JSEvents.keyEvent + {{{ C_STRUCTS.EmscriptenKeyboardEvent.locale }}} ); + writeStringToMemory(e.char ? e.char : "", JSEvents.keyEvent + {{{ C_STRUCTS.EmscriptenKeyboardEvent.charValue }}} ); + {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.charCode, 'e.charCode', 'i32') }}} + {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.keyCode, 'e.keyCode', 'i32') }}} + {{{ makeSetValue('JSEvents.keyEvent', C_STRUCTS.EmscriptenKeyboardEvent.which, 'e.which', 'i32') }}} + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.keyEvent, userData]); if (shouldCancel) { e.preventDefault(); } }; + var isInternetExplorer = (navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') > 0); + var eventHandler = { - target: JsEvents.findEventTarget(target), - allowsDeferredCalls: true, + target: JSEvents.findEventTarget(target), + allowsDeferredCalls: isInternetExplorer ? false : true, // MSIE doesn't allow fullscreen and pointerlock requests from key handlers, others do. eventTypeString: eventTypeString, callbackfunc: callbackfunc, handlerFunc: handlerFunc, useCapture: useCapture }; - JsEvents.registerOrRemoveHandler(eventHandler); + JSEvents.registerOrRemoveHandler(eventHandler); }, fillMouseEventData: function(eventStruct, e) { var rect = Module['canvas'].getBoundingClientRect(); - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.timestamp, 'JsEvents.tick()', 'double') }}} - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.screenX, 'e.screenX', 'i32') }}} - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.screenY, 'e.screenY', 'i32') }}} - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.clientX, 'e.clientX', 'i32') }}} - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.clientY, 'e.clientY', 'i32') }}} - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.ctrlKey, 'e.ctrlKey', 'i32') }}} - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.shiftKey, 'e.shiftKey', 'i32') }}} - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.altKey, 'e.altKey', 'i32') }}} - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.metaKey, 'e.metaKey', 'i32') }}} - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.button, 'e.button', 'i16') }}} - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.buttons, 'e.buttons', 'i16') }}} - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.movementX, 'e.movementX', 'i32') }}} - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.movementY, 'e.movementY', 'i32') }}} - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.canvasX, 'e.clientX - rect.left', 'i32') }}} - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_MouseEvent.canvasY, 'e.clientY - rect.top', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.timestamp, 'JSEvents.tick()', 'double') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.screenX, 'e.screenX', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.screenY, 'e.screenY', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.clientX, 'e.clientX', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.clientY, 'e.clientY', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.ctrlKey, 'e.ctrlKey', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.shiftKey, 'e.shiftKey', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.altKey, 'e.altKey', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.metaKey, 'e.metaKey', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.button, 'e.button', 'i16') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.buttons, 'e.buttons', 'i16') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.movementX, 'e.movementX', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.movementY, 'e.movementY', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.canvasX, 'e.clientX - rect.left', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenMouseEvent.canvasY, 'e.clientY - rect.top', 'i32') }}} }, registerMouseEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { - if (!JsEvents.mouseEvent) { - JsEvents.mouseEvent = _malloc( {{{ C_STRUCTS.emscripten_MouseEvent.__size__ }}} ); + if (!JSEvents.mouseEvent) { + JSEvents.mouseEvent = _malloc( {{{ C_STRUCTS.EmscriptenMouseEvent.__size__ }}} ); } var handlerFunc = function(event) { var e = event || window.event; - JsEvents.fillMouseEventData(JsEvents.mouseEvent, e); - var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.mouseEvent, userData]); + JSEvents.fillMouseEventData(JSEvents.mouseEvent, e); + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.mouseEvent, userData]); if (shouldCancel) { e.preventDefault(); } }; var eventHandler = { - target: JsEvents.findEventTarget(target), + target: JSEvents.findEventTarget(target), allowsDeferredCalls: eventTypeString != 'mousemove', // Mouse move events do not allow fullscreen/pointer lock requests to be handled in them! eventTypeString: eventTypeString, callbackfunc: callbackfunc, handlerFunc: handlerFunc, useCapture: useCapture }; - JsEvents.registerOrRemoveHandler(eventHandler); + JSEvents.registerOrRemoveHandler(eventHandler); }, registerWheelEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { - if (!JsEvents.wheelEvent) { - JsEvents.wheelEvent = _malloc( {{{ C_STRUCTS.emscripten_WheelEvent.__size__ }}} ); + if (!JSEvents.wheelEvent) { + JSEvents.wheelEvent = _malloc( {{{ C_STRUCTS.EmscriptenWheelEvent.__size__ }}} ); } var handlerFunc = function(event) { var e = event || window.event; - JsEvents.fillMouseEventData(JsEvents.wheelEvent, e); - {{{ makeSetValue('JsEvents.wheelEvent', C_STRUCTS.emscripten_WheelEvent.deltaX, 'e.deltaX', 'double') }}} - {{{ makeSetValue('JsEvents.wheelEvent', C_STRUCTS.emscripten_WheelEvent.deltaY, 'e.deltaY', 'double') }}} - {{{ makeSetValue('JsEvents.wheelEvent', C_STRUCTS.emscripten_WheelEvent.deltaZ, 'e.deltaZ', 'double') }}} - {{{ makeSetValue('JsEvents.wheelEvent', C_STRUCTS.emscripten_WheelEvent.deltaMode, 'e.deltaMode', 'i32') }}} - var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.wheelEvent, userData]); + JSEvents.fillMouseEventData(JSEvents.wheelEvent, e); + {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaX, 'e.deltaX', 'double') }}} + {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaY, 'e.deltaY', 'double') }}} + {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaZ, 'e.deltaZ', 'double') }}} + {{{ makeSetValue('JSEvents.wheelEvent', C_STRUCTS.EmscriptenWheelEvent.deltaMode, 'e.deltaMode', 'i32') }}} + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.wheelEvent, userData]); if (shouldCancel) { e.preventDefault(); } }; var eventHandler = { - target: JsEvents.findEventTarget(target), + target: JSEvents.findEventTarget(target), allowsDeferredCalls: true, eventTypeString: eventTypeString, callbackfunc: callbackfunc, handlerFunc: handlerFunc, useCapture: useCapture }; - JsEvents.registerOrRemoveHandler(eventHandler); + JSEvents.registerOrRemoveHandler(eventHandler); }, pageScrollPos: function() { if (window.pageXOffset > 0 || window.pageYOffset > 0) { return [window.pageXOffset, window.pageYOffset]; } - if (document.documentElement.scrollLeft > 0 || document.documentElement.scrollTop > 0) { + if (typeof document.documentElement.scrollLeft !== 'undefined' || typeof document.documentElement.scrollTop !== 'undefined') { return [document.documentElement.scrollLeft, document.documentElement.scrollTop]; } return [document.body.scrollLeft|0, document.body.scrollTop|0]; }, registerUiEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { - if (!JsEvents.uiEvent) { - JsEvents.uiEvent = _malloc( {{{ C_STRUCTS.emscripten_UiEvent.__size__ }}} ); + if (!JSEvents.uiEvent) { + JSEvents.uiEvent = _malloc( {{{ C_STRUCTS.EmscriptenUiEvent.__size__ }}} ); } if (eventTypeString == "scroll" && !target) { target = document; // By default read scroll events on document rather than window. } else { - target = JsEvents.findEventTarget(target); + target = JSEvents.findEventTarget(target); } var handlerFunc = function(event) { @@ -295,17 +287,17 @@ var LibraryJsEvents = { // causing a new scroll, etc.. return; } - var scrollPos = JsEvents.pageScrollPos(); - {{{ makeSetValue('JsEvents.uiEvent', C_STRUCTS.emscripten_UiEvent.detail, 'e.detail', 'i32') }}} - {{{ makeSetValue('JsEvents.uiEvent', C_STRUCTS.emscripten_UiEvent.documentBodyClientWidth, 'document.body.clientWidth', 'i32') }}} - {{{ makeSetValue('JsEvents.uiEvent', C_STRUCTS.emscripten_UiEvent.documentBodyClientHeight, 'document.body.clientHeight', 'i32') }}} - {{{ makeSetValue('JsEvents.uiEvent', C_STRUCTS.emscripten_UiEvent.windowInnerWidth, 'window.innerWidth', 'i32') }}} - {{{ makeSetValue('JsEvents.uiEvent', C_STRUCTS.emscripten_UiEvent.windowInnerHeight, 'window.innerHeight', 'i32') }}} - {{{ makeSetValue('JsEvents.uiEvent', C_STRUCTS.emscripten_UiEvent.windowOuterWidth, 'window.outerWidth', 'i32') }}} - {{{ makeSetValue('JsEvents.uiEvent', C_STRUCTS.emscripten_UiEvent.windowOuterHeight, 'window.outerHeight', 'i32') }}} - {{{ makeSetValue('JsEvents.uiEvent', C_STRUCTS.emscripten_UiEvent.scrollTop, 'scrollPos[0]', 'i32') }}} - {{{ makeSetValue('JsEvents.uiEvent', C_STRUCTS.emscripten_UiEvent.scrollLeft, 'scrollPos[1]', 'i32') }}} - var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.uiEvent, userData]); + var scrollPos = JSEvents.pageScrollPos(); + {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.detail, 'e.detail', 'i32') }}} + {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.documentBodyClientWidth, 'document.body.clientWidth', 'i32') }}} + {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.documentBodyClientHeight, 'document.body.clientHeight', 'i32') }}} + {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.windowInnerWidth, 'window.innerWidth', 'i32') }}} + {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.windowInnerHeight, 'window.innerHeight', 'i32') }}} + {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.windowOuterWidth, 'window.outerWidth', 'i32') }}} + {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.windowOuterHeight, 'window.outerHeight', 'i32') }}} + {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.scrollTop, 'scrollPos[0]', 'i32') }}} + {{{ makeSetValue('JSEvents.uiEvent', C_STRUCTS.EmscriptenUiEvent.scrollLeft, 'scrollPos[1]', 'i32') }}} + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.uiEvent, userData]); if (shouldCancel) { e.preventDefault(); } @@ -319,154 +311,150 @@ var LibraryJsEvents = { handlerFunc: handlerFunc, useCapture: useCapture }; - JsEvents.registerOrRemoveHandler(eventHandler); + JSEvents.registerOrRemoveHandler(eventHandler); }, getNodeNameForTarget: function(target) { - if (!target) { - return ''; - } - if (target == window) { - return '#window'; - } - if (target == window.screen) { - return '#screen'; - } + if (!target) return ''; + if (target == window) return '#window'; + if (target == window.screen) return '#screen'; return (target && target.nodeName) ? target.nodeName : ''; }, registerFocusEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { - if (!JsEvents.focusEvent) { - JsEvents.focusEvent = _malloc( {{{ C_STRUCTS.emscripten_FocusEvent.__size__ }}} ); + if (!JSEvents.focusEvent) { + JSEvents.focusEvent = _malloc( {{{ C_STRUCTS.EmscriptenFocusEvent.__size__ }}} ); } var handlerFunc = function(event) { var e = event || window.event; - var nodeName = JsEvents.getNodeNameForTarget(e.target); + var nodeName = JSEvents.getNodeNameForTarget(e.target); var id = e.target.id ? e.target.id : ''; - writeStringToMemory(nodeName, JsEvents.focusEvent + {{{ C_STRUCTS.emscripten_FocusEvent.nodeName }}} ); - writeStringToMemory(id, JsEvents.focusEvent + {{{ C_STRUCTS.emscripten_FocusEvent.id }}} ); - var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.focusEvent, userData]); + writeStringToMemory(nodeName, JSEvents.focusEvent + {{{ C_STRUCTS.EmscriptenFocusEvent.nodeName }}} ); + writeStringToMemory(id, JSEvents.focusEvent + {{{ C_STRUCTS.EmscriptenFocusEvent.id }}} ); + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.focusEvent, userData]); if (shouldCancel) { e.preventDefault(); } }; var eventHandler = { - target: JsEvents.findEventTarget(target), + target: JSEvents.findEventTarget(target), allowsDeferredCalls: false, eventTypeString: eventTypeString, callbackfunc: callbackfunc, handlerFunc: handlerFunc, useCapture: useCapture }; - JsEvents.registerOrRemoveHandler(eventHandler); + JSEvents.registerOrRemoveHandler(eventHandler); }, tick: function() { - if (window['performance'] && window['performance']['now']) { - return window['performance']['now'](); - } else { - return Date.now(); - } + if (window['performance'] && window['performance']['now']) return window['performance']['now'](); + else return Date.now(); }, registerDeviceOrientationEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { - if (!JsEvents.deviceOrientationEvent) { - JsEvents.deviceOrientationEvent = _malloc( {{{ C_STRUCTS.emscripten_DeviceOrientationEvent.__size__ }}} ); + if (!JSEvents.deviceOrientationEvent) { + JSEvents.deviceOrientationEvent = _malloc( {{{ C_STRUCTS.EmscriptenDeviceOrientationEvent.__size__ }}} ); } var handlerFunc = function(event) { var e = event || window.event; - {{{ makeSetValue('JsEvents.deviceOrientationEvent', C_STRUCTS.emscripten_DeviceOrientationEvent.timestamp, 'JsEvents.tick()', 'double') }}} - {{{ makeSetValue('JsEvents.deviceOrientationEvent', C_STRUCTS.emscripten_DeviceOrientationEvent.alpha, 'e.alpha', 'double') }}} - {{{ makeSetValue('JsEvents.deviceOrientationEvent', C_STRUCTS.emscripten_DeviceOrientationEvent.beta, 'e.beta', 'double') }}} - {{{ makeSetValue('JsEvents.deviceOrientationEvent', C_STRUCTS.emscripten_DeviceOrientationEvent.gamma, 'e.gamma', 'double') }}} - {{{ makeSetValue('JsEvents.deviceOrientationEvent', C_STRUCTS.emscripten_DeviceOrientationEvent.absolute, 'e.absolute', 'i32') }}} + {{{ makeSetValue('JSEvents.deviceOrientationEvent', C_STRUCTS.EmscriptenDeviceOrientationEvent.timestamp, 'JSEvents.tick()', 'double') }}} + {{{ makeSetValue('JSEvents.deviceOrientationEvent', C_STRUCTS.EmscriptenDeviceOrientationEvent.alpha, 'e.alpha', 'double') }}} + {{{ makeSetValue('JSEvents.deviceOrientationEvent', C_STRUCTS.EmscriptenDeviceOrientationEvent.beta, 'e.beta', 'double') }}} + {{{ makeSetValue('JSEvents.deviceOrientationEvent', C_STRUCTS.EmscriptenDeviceOrientationEvent.gamma, 'e.gamma', 'double') }}} + {{{ makeSetValue('JSEvents.deviceOrientationEvent', C_STRUCTS.EmscriptenDeviceOrientationEvent.absolute, 'e.absolute', 'i32') }}} - var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.deviceOrientationEvent, userData]); + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.deviceOrientationEvent, userData]); if (shouldCancel) { e.preventDefault(); } }; var eventHandler = { - target: JsEvents.findEventTarget(target), + target: JSEvents.findEventTarget(target), allowsDeferredCalls: false, eventTypeString: eventTypeString, callbackfunc: callbackfunc, handlerFunc: handlerFunc, useCapture: useCapture }; - JsEvents.registerOrRemoveHandler(eventHandler); + JSEvents.registerOrRemoveHandler(eventHandler); }, registerDeviceMotionEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { - if (!JsEvents.deviceMotionEvent) { - JsEvents.deviceMotionEvent = _malloc( {{{ C_STRUCTS.emscripten_DeviceMotionEvent.__size__ }}} ); + if (!JSEvents.deviceMotionEvent) { + JSEvents.deviceMotionEvent = _malloc( {{{ C_STRUCTS.EmscriptenDeviceMotionEvent.__size__ }}} ); } var handlerFunc = function(event) { var e = event || window.event; - {{{ makeSetValue('JsEvents.deviceOrientationEvent', C_STRUCTS.emscripten_DeviceMotionEvent.timestamp, 'JsEvents.tick()', 'double') }}} - {{{ makeSetValue('JsEvents.deviceMotionEvent', C_STRUCTS.emscripten_DeviceMotionEvent.accelerationX, 'e.acceleration.x', 'double') }}} - {{{ makeSetValue('JsEvents.deviceMotionEvent', C_STRUCTS.emscripten_DeviceMotionEvent.accelerationY, 'e.acceleration.y', 'double') }}} - {{{ makeSetValue('JsEvents.deviceMotionEvent', C_STRUCTS.emscripten_DeviceMotionEvent.accelerationZ, 'e.acceleration.z', 'double') }}} - {{{ makeSetValue('JsEvents.deviceMotionEvent', C_STRUCTS.emscripten_DeviceMotionEvent.accelerationIncludingGravityX, 'e.accelerationIncludingGravity.x', 'double') }}} - {{{ makeSetValue('JsEvents.deviceMotionEvent', C_STRUCTS.emscripten_DeviceMotionEvent.accelerationIncludingGravityY, 'e.accelerationIncludingGravity.y', 'double') }}} - {{{ makeSetValue('JsEvents.deviceMotionEvent', C_STRUCTS.emscripten_DeviceMotionEvent.accelerationIncludingGravityZ, 'e.accelerationIncludingGravity.z', 'double') }}} - {{{ makeSetValue('JsEvents.deviceMotionEvent', C_STRUCTS.emscripten_DeviceMotionEvent.rotationRateAlpha, 'e.rotationRate.alpha', 'double') }}} - {{{ makeSetValue('JsEvents.deviceMotionEvent', C_STRUCTS.emscripten_DeviceMotionEvent.rotationRateBeta, 'e.rotationRate.beta', 'double') }}} - {{{ makeSetValue('JsEvents.deviceMotionEvent', C_STRUCTS.emscripten_DeviceMotionEvent.rotationRateGamma, 'e.rotationRate.gamma', 'double') }}} - - var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.deviceMotionEvent, userData]); + {{{ makeSetValue('JSEvents.deviceOrientationEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.timestamp, 'JSEvents.tick()', 'double') }}} + {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.accelerationX, 'e.acceleration.x', 'double') }}} + {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.accelerationY, 'e.acceleration.y', 'double') }}} + {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.accelerationZ, 'e.acceleration.z', 'double') }}} + {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.accelerationIncludingGravityX, 'e.accelerationIncludingGravity.x', 'double') }}} + {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.accelerationIncludingGravityY, 'e.accelerationIncludingGravity.y', 'double') }}} + {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.accelerationIncludingGravityZ, 'e.accelerationIncludingGravity.z', 'double') }}} + {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.rotationRateAlpha, 'e.rotationRate.alpha', 'double') }}} + {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.rotationRateBeta, 'e.rotationRate.beta', 'double') }}} + {{{ makeSetValue('JSEvents.deviceMotionEvent', C_STRUCTS.EmscriptenDeviceMotionEvent.rotationRateGamma, 'e.rotationRate.gamma', 'double') }}} + + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.deviceMotionEvent, userData]); if (shouldCancel) { e.preventDefault(); } }; var eventHandler = { - target: JsEvents.findEventTarget(target), + target: JSEvents.findEventTarget(target), allowsDeferredCalls: false, eventTypeString: eventTypeString, callbackfunc: callbackfunc, handlerFunc: handlerFunc, useCapture: useCapture }; - JsEvents.registerOrRemoveHandler(eventHandler); + JSEvents.registerOrRemoveHandler(eventHandler); + }, + + screenOrientation: function() { + if (!window.screen) return undefined; + return window.screen.orientation || window.screen.mozOrientation || window.screen.webkitOrientation || window.screen.msOrientation; }, fillOrientationChangeEventData: function(eventStruct, e) { var orientations = ["portrait-primary", "portrait-secondary", "landscape-primary", "landscape-secondary"]; var orientations2 = ["portrait", "portrait", "landscape", "landscape"]; - var orientationString = window.screen.orientation || window.screen.mozOrientation; + var orientationString = JSEvents.screenOrientation(); var orientation = orientations.indexOf(orientationString); if (orientation == -1) { orientation = orientations2.indexOf(orientationString); } - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_OrientationChangeEvent.orientationIndex, '1 << orientation', 'i32') }}} - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_OrientationChangeEvent.orientationAngle, 'window.orientation', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenOrientationChangeEvent.orientationIndex, '1 << orientation', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenOrientationChangeEvent.orientationAngle, 'window.orientation', 'i32') }}} }, registerOrientationChangeEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { - if (!JsEvents.orientationChangeEvent) { - JsEvents.orientationChangeEvent = _malloc( {{{ C_STRUCTS.emscripten_OrientationChangeEvent.__size__ }}} ); + if (!JSEvents.orientationChangeEvent) { + JSEvents.orientationChangeEvent = _malloc( {{{ C_STRUCTS.EmscriptenOrientationChangeEvent.__size__ }}} ); } if (!target) { target = window.screen; // Orientation events need to be captured from 'window.screen' instead of 'window' } else { - target = JsEvents.findEventTarget(target); + target = JSEvents.findEventTarget(target); } var handlerFunc = function(event) { var e = event || window.event; - JsEvents.fillOrientationChangeEventData(JsEvents.orientationChangeEvent, e); + JSEvents.fillOrientationChangeEventData(JSEvents.orientationChangeEvent, e); - var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.orientationChangeEvent, userData]); + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.orientationChangeEvent, userData]); if (shouldCancel) { e.preventDefault(); } @@ -484,37 +472,41 @@ var LibraryJsEvents = { handlerFunc: handlerFunc, useCapture: useCapture }; - JsEvents.registerOrRemoveHandler(eventHandler); + JSEvents.registerOrRemoveHandler(eventHandler); }, + fullscreenEnabled: function() { + return document.fullscreenEnabled || document.mozFullscreenEnabled || document.mozFullScreenEnabled || document.webkitFullscreenEnabled || document.msFullscreenEnabled; + }, + fillFullscreenChangeEventData: function(eventStruct, e) { var fullscreenElement = document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement; var isFullscreen = !!fullscreenElement; - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_FullscreenChangeEvent.isFullscreen, 'isFullscreen', 'i32') }}} - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_FullscreenChangeEvent.fullscreenEnabled, 'document.fullscreenEnabled', 'i32') }}} - var nodeName = JsEvents.getNodeNameForTarget(fullscreenElement); + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenFullscreenChangeEvent.isFullscreen, 'isFullscreen', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenFullscreenChangeEvent.fullscreenEnabled, 'JSEvents.fullscreenEnabled()', 'i32') }}} + var nodeName = JSEvents.getNodeNameForTarget(fullscreenElement); var id = (fullscreenElement && fullscreenElement.id) ? fullscreenElement.id : ''; - writeStringToMemory(nodeName, eventStruct + {{{ C_STRUCTS.emscripten_FullscreenChangeEvent.nodeName }}} ); - writeStringToMemory(id, eventStruct + {{{ C_STRUCTS.emscripten_FullscreenChangeEvent.id }}} ); + writeStringToMemory(nodeName, eventStruct + {{{ C_STRUCTS.EmscriptenFullscreenChangeEvent.nodeName }}} ); + writeStringToMemory(id, eventStruct + {{{ C_STRUCTS.EmscriptenFullscreenChangeEvent.id }}} ); }, registerFullscreenChangeEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { - if (!JsEvents.fullscreenChangeEvent) { - JsEvents.fullscreenChangeEvent = _malloc( {{{ C_STRUCTS.emscripten_FullscreenChangeEvent.__size__ }}} ); + if (!JSEvents.fullscreenChangeEvent) { + JSEvents.fullscreenChangeEvent = _malloc( {{{ C_STRUCTS.EmscriptenFullscreenChangeEvent.__size__ }}} ); } if (!target) { target = document; // Fullscreen change events need to be captured from 'document' by default instead of 'window' } else { - target = JsEvents.findEventTarget(target); + target = JSEvents.findEventTarget(target); } var handlerFunc = function(event) { var e = event || window.event; - JsEvents.fillFullscreenChangeEventData(JsEvents.fullscreenChangeEvent, e); + JSEvents.fillFullscreenChangeEventData(JSEvents.fullscreenChangeEvent, e); - var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.fullscreenChangeEvent, userData]); + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.fullscreenChangeEvent, userData]); if (shouldCancel) { e.preventDefault(); } @@ -528,7 +520,7 @@ var LibraryJsEvents = { handlerFunc: handlerFunc, useCapture: useCapture }; - JsEvents.registerOrRemoveHandler(eventHandler); + JSEvents.registerOrRemoveHandler(eventHandler); }, requestFullscreen: function(target) { @@ -538,10 +530,16 @@ var LibraryJsEvents = { target.msRequestFullscreen(); } else if (target.mozRequestFullScreen) { target.mozRequestFullScreen(); + } else if (target.mozRequestFullscreen) { + target.mozRequestFullscreen(); } else if (target.webkitRequestFullscreen) { target.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); } else { - return 2 /* Target does not support requesting fullscreen */; + if (typeof JSEvents.fullscreenEnabled() === 'undefined') { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } else { + return {{{ cDefine('EMSCRIPTEN_RESULT_INVALID_TARGET') }}}; + } } return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, @@ -549,30 +547,30 @@ var LibraryJsEvents = { fillPointerlockChangeEventData: function(eventStruct, e) { var pointerLockElement = document.pointerLockElement || document.mozPointerLockElement || document.webkitPointerLockElement || document.msPointerLockElement; var isPointerlocked = !!pointerLockElement; - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_PointerlockChangeEvent.isActive, 'isPointerlocked', 'i32') }}} - var nodeName = JsEvents.getNodeNameForTarget(pointerLockElement); + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenPointerlockChangeEvent.isActive, 'isPointerlocked', 'i32') }}} + var nodeName = JSEvents.getNodeNameForTarget(pointerLockElement); var id = (pointerLockElement && pointerLockElement.id) ? pointerLockElement.id : ''; - writeStringToMemory(nodeName, eventStruct + {{{ C_STRUCTS.emscripten_PointerlockChangeEvent.nodeName }}} ); - writeStringToMemory(id, eventStruct + {{{ C_STRUCTS.emscripten_PointerlockChangeEvent.id }}}); + writeStringToMemory(nodeName, eventStruct + {{{ C_STRUCTS.EmscriptenPointerlockChangeEvent.nodeName }}} ); + writeStringToMemory(id, eventStruct + {{{ C_STRUCTS.EmscriptenPointerlockChangeEvent.id }}}); }, registerPointerlockChangeEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { - if (!JsEvents.pointerlockChangeEvent) { - JsEvents.pointerlockChangeEvent = _malloc( {{{ C_STRUCTS.emscripten_PointerlockChangeEvent.__size__ }}} ); + if (!JSEvents.pointerlockChangeEvent) { + JSEvents.pointerlockChangeEvent = _malloc( {{{ C_STRUCTS.EmscriptenPointerlockChangeEvent.__size__ }}} ); } if (!target) { target = document; // Pointer lock change events need to be captured from 'document' by default instead of 'window' } else { - target = JsEvents.findEventTarget(target); + target = JSEvents.findEventTarget(target); } var handlerFunc = function(event) { var e = event || window.event; - JsEvents.fillPointerlockChangeEventData(JsEvents.pointerlockChangeEvent, e); + JSEvents.fillPointerlockChangeEventData(JSEvents.pointerlockChangeEvent, e); - var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.pointerlockChangeEvent, userData]); + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.pointerlockChangeEvent, userData]); if (shouldCancel) { e.preventDefault(); } @@ -586,7 +584,7 @@ var LibraryJsEvents = { handlerFunc: handlerFunc, useCapture: useCapture }; - JsEvents.registerOrRemoveHandler(eventHandler); + JSEvents.registerOrRemoveHandler(eventHandler); }, requestPointerLock: function(target) { @@ -599,7 +597,13 @@ var LibraryJsEvents = { } else if (target.msRequestPointerLock) { target.msRequestPointerLock(); } else { - return 2 /* Target does not support requesting pointer lock */; + // document.body is known to accept pointer lock, so use that to differentiate if the user passed a bad element, + // or if the whole browser just doesn't support the feature. + if (document.body.requestPointerLock || document.body.mozRequestPointerLock || document.body.webkitRequestPointerLock || document.body.msRequestPointerLock) { + return {{{ cDefine('EMSCRIPTEN_RESULT_INVALID_TARGET') }}}; + } else { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } } return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, @@ -608,27 +612,27 @@ var LibraryJsEvents = { var visibilityStates = [ "hidden", "visible", "prerender", "unloaded" ]; var visibilityState = visibilityStates.indexOf(document.visibilityState); - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_VisibilityChangeEvent.hidden, 'document.hidden', 'i32') }}} - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_VisibilityChangeEvent.visibilityState, 'visibilityState', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenVisibilityChangeEvent.hidden, 'document.hidden', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenVisibilityChangeEvent.visibilityState, 'visibilityState', 'i32') }}} }, registerVisibilityChangeEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { - if (!JsEvents.visibilityChangeEvent) { - JsEvents.visibilityChangeEvent = _malloc( {{{ C_STRUCTS.emscripten_VisibilityChangeEvent.__size__ }}} ); + if (!JSEvents.visibilityChangeEvent) { + JSEvents.visibilityChangeEvent = _malloc( {{{ C_STRUCTS.EmscriptenVisibilityChangeEvent.__size__ }}} ); } if (!target) { target = document; // Visibility change events need to be captured from 'document' by default instead of 'window' } else { - target = JsEvents.findEventTarget(target); + target = JSEvents.findEventTarget(target); } var handlerFunc = function(event) { var e = event || window.event; - JsEvents.fillVisibilityChangeEventData(JsEvents.visibilityChangeEvent, e); + JSEvents.fillVisibilityChangeEventData(JSEvents.visibilityChangeEvent, e); - var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.visibilityChangeEvent, userData]); + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.visibilityChangeEvent, userData]); if (shouldCancel) { e.preventDefault(); } @@ -642,12 +646,12 @@ var LibraryJsEvents = { handlerFunc: handlerFunc, useCapture: useCapture }; - JsEvents.registerOrRemoveHandler(eventHandler); + JSEvents.registerOrRemoveHandler(eventHandler); }, registerTouchEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { - if (!JsEvents.touchEvent) { - JsEvents.touchEvent = _malloc( {{{ C_STRUCTS.emscripten_TouchEvent.__size__ }}} ); + if (!JSEvents.touchEvent) { + JSEvents.touchEvent = _malloc( {{{ C_STRUCTS.EmscriptenTouchEvent.__size__ }}} ); } var handlerFunc = function(event) { @@ -668,96 +672,96 @@ var LibraryJsEvents = { touches[touch.identifier].onTarget = true; } - var ptr = JsEvents.touchEvent; - {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchEvent.ctrlKey, 'e.ctrlKey', 'i32') }}} - {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchEvent.shiftKey, 'e.shiftKey', 'i32') }}} - {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchEvent.altKey, 'e.altKey', 'i32') }}} - {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchEvent.metaKey, 'e.metaKey', 'i32') }}} - ptr += {{{ C_STRUCTS.emscripten_TouchEvent.touches }}}; // Advance to the start of the touch array. + var ptr = JSEvents.touchEvent; + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchEvent.ctrlKey, 'e.ctrlKey', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchEvent.shiftKey, 'e.shiftKey', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchEvent.altKey, 'e.altKey', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchEvent.metaKey, 'e.metaKey', 'i32') }}} + ptr += {{{ C_STRUCTS.EmscriptenTouchEvent.touches }}}; // Advance to the start of the touch array. var rect = Module['canvas'].getBoundingClientRect(); var numTouches = 0; for(var i in touches) { var t = touches[i]; - {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchPoint.identifier, 't.identifier', 'i32') }}} - {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchPoint.screenX, 't.screenX', 'i32') }}} - {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchPoint.screenY, 't.screenY', 'i32') }}} - {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchPoint.clientX, 't.clientX', 'i32') }}} - {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchPoint.clientY, 't.clientY', 'i32') }}} - {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchPoint.pageX, 't.pageX', 'i32') }}} - {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchPoint.pageY, 't.pageY', 'i32') }}} - {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchPoint.isChanged, 't.changed', 'i32') }}} - {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchPoint.onTarget, 't.onTarget', 'i32') }}} - {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchPoint.canvasX, 't.clientX - rect.left', 'i32') }}} - {{{ makeSetValue('ptr', C_STRUCTS.emscripten_TouchPoint.canvasY, 't.clientY - rect.top', 'i32') }}} - ptr += {{{ C_STRUCTS.emscripten_TouchPoint.__size__ }}}; + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.identifier, 't.identifier', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.screenX, 't.screenX', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.screenY, 't.screenY', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.clientX, 't.clientX', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.clientY, 't.clientY', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.pageX, 't.pageX', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.pageY, 't.pageY', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.isChanged, 't.changed', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.onTarget, 't.onTarget', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.canvasX, 't.clientX - rect.left', 'i32') }}} + {{{ makeSetValue('ptr', C_STRUCTS.EmscriptenTouchPoint.canvasY, 't.clientY - rect.top', 'i32') }}} + ptr += {{{ C_STRUCTS.EmscriptenTouchPoint.__size__ }}}; if (++numTouches >= 32) { break; } } - {{{ makeSetValue('JsEvents.touchEvent', C_STRUCTS.emscripten_TouchEvent.numTouches, 'numTouches', 'i32') }}} + {{{ makeSetValue('JSEvents.touchEvent', C_STRUCTS.EmscriptenTouchEvent.numTouches, 'numTouches', 'i32') }}} - var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.touchEvent, userData]); + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.touchEvent, userData]); if (shouldCancel) { e.preventDefault(); } }; var eventHandler = { - target: JsEvents.findEventTarget(target), + target: JSEvents.findEventTarget(target), allowsDeferredCalls: true, eventTypeString: eventTypeString, callbackfunc: callbackfunc, handlerFunc: handlerFunc, useCapture: useCapture }; - JsEvents.registerOrRemoveHandler(eventHandler); + JSEvents.registerOrRemoveHandler(eventHandler); }, fillGamepadEventData: function(eventStruct, e) { - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_GamepadEvent.timestamp, 'e.timestamp', 'double') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenGamepadEvent.timestamp, 'e.timestamp', 'double') }}} for(var i = 0; i < e.axes.length; ++i) { - {{{ makeSetValue('eventStruct+i*8', C_STRUCTS.emscripten_GamepadEvent.axis, 'e.axes[i]', 'double') }}} + {{{ makeSetValue('eventStruct+i*8', C_STRUCTS.EmscriptenGamepadEvent.axis, 'e.axes[i]', 'double') }}} } for(var i = 0; i < e.buttons.length; ++i) { - {{{ makeSetValue('eventStruct+i*8', C_STRUCTS.emscripten_GamepadEvent.analogButton, 'e.buttons[i].value', 'double') }}} + {{{ makeSetValue('eventStruct+i*8', C_STRUCTS.EmscriptenGamepadEvent.analogButton, 'e.buttons[i].value', 'double') }}} } for(var i = 0; i < e.buttons.length; ++i) { - {{{ makeSetValue('eventStruct+i*4', C_STRUCTS.emscripten_GamepadEvent.digitalButton, 'e.buttons[i].pressed', 'i32') }}} + {{{ makeSetValue('eventStruct+i*4', C_STRUCTS.EmscriptenGamepadEvent.digitalButton, 'e.buttons[i].pressed', 'i32') }}} } - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_GamepadEvent.connected, 'e.connected', 'i32') }}} - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_GamepadEvent.index, 'e.index', 'i32') }}} - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_GamepadEvent.numAxes, 'e.axes.length', 'i32') }}} - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_GamepadEvent.numButtons, 'e.buttons.length', 'i32') }}} - writeStringToMemory(e.id, eventStruct + {{{ C_STRUCTS.emscripten_GamepadEvent.id }}} ); - writeStringToMemory(e.mapping, eventStruct + {{{ C_STRUCTS.emscripten_GamepadEvent.mapping }}} ); + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenGamepadEvent.connected, 'e.connected', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenGamepadEvent.index, 'e.index', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenGamepadEvent.numAxes, 'e.axes.length', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenGamepadEvent.numButtons, 'e.buttons.length', 'i32') }}} + writeStringToMemory(e.id, eventStruct + {{{ C_STRUCTS.EmscriptenGamepadEvent.id }}} ); + writeStringToMemory(e.mapping, eventStruct + {{{ C_STRUCTS.EmscriptenGamepadEvent.mapping }}} ); }, registerGamepadEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { - if (!JsEvents.gamepadEvent) { - JsEvents.gamepadEvent = _malloc( {{{ C_STRUCTS.emscripten_GamepadEvent.__size__ }}} ); + if (!JSEvents.gamepadEvent) { + JSEvents.gamepadEvent = _malloc( {{{ C_STRUCTS.EmscriptenGamepadEvent.__size__ }}} ); } var handlerFunc = function(event) { var e = event || window.event; - JsEvents.fillGamepadEventData(JsEvents.gamepadEvent, e.gamepad); + JSEvents.fillGamepadEventData(JSEvents.gamepadEvent, e.gamepad); - var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.gamepadEvent, userData]); + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.gamepadEvent, userData]); if (shouldCancel) { e.preventDefault(); } }; var eventHandler = { - target: JsEvents.findEventTarget(target), + target: JSEvents.findEventTarget(target), allowsDeferredCalls: true, eventTypeString: eventTypeString, callbackfunc: callbackfunc, handlerFunc: handlerFunc, useCapture: useCapture }; - JsEvents.registerOrRemoveHandler(eventHandler); + JSEvents.registerOrRemoveHandler(eventHandler); }, registerBeforeUnloadEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { @@ -775,50 +779,50 @@ var LibraryJsEvents = { }; var eventHandler = { - target: JsEvents.findEventTarget(target), + target: JSEvents.findEventTarget(target), allowsDeferredCalls: false, eventTypeString: eventTypeString, callbackfunc: callbackfunc, handlerFunc: handlerFunc, useCapture: useCapture }; - JsEvents.registerOrRemoveHandler(eventHandler); + JSEvents.registerOrRemoveHandler(eventHandler); }, battery: function() { return navigator.battery || navigator.mozBattery || navigator.webkitBattery; }, fillBatteryEventData: function(eventStruct, e) { - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_BatteryEvent.chargingTime, 'e.chargingTime', 'double') }}} - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_BatteryEvent.dischargingTime, 'e.dischargingTime', 'double') }}} - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_BatteryEvent.level, 'e.level', 'double') }}} - {{{ makeSetValue('eventStruct', C_STRUCTS.emscripten_BatteryEvent.charging, 'e.charging', 'i32') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenBatteryEvent.chargingTime, 'e.chargingTime', 'double') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenBatteryEvent.dischargingTime, 'e.dischargingTime', 'double') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenBatteryEvent.level, 'e.level', 'double') }}} + {{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenBatteryEvent.charging, 'e.charging', 'i32') }}} }, registerBatteryEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { - if (!JsEvents.batteryEvent) { - JsEvents.batteryEvent = _malloc( {{{ C_STRUCTS.emscripten_BatteryEvent.__size__ }}} ); + if (!JSEvents.batteryEvent) { + JSEvents.batteryEvent = _malloc( {{{ C_STRUCTS.EmscriptenBatteryEvent.__size__ }}} ); } var handlerFunc = function(event) { var e = event || window.event; - JsEvents.fillBatteryEventData(JsEvents.batteryEvent, JsEvents.battery()); + JSEvents.fillBatteryEventData(JSEvents.batteryEvent, JSEvents.battery()); - var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JsEvents.batteryEvent, userData]); + var shouldCancel = Runtime.dynCall('iiii', callbackfunc, [eventTypeId, JSEvents.batteryEvent, userData]); if (shouldCancel) { e.preventDefault(); } }; var eventHandler = { - target: JsEvents.findEventTarget(target), + target: JSEvents.findEventTarget(target), allowsDeferredCalls: false, eventTypeString: eventTypeString, callbackfunc: callbackfunc, handlerFunc: handlerFunc, useCapture: useCapture }; - JsEvents.registerOrRemoveHandler(eventHandler); + JSEvents.registerOrRemoveHandler(eventHandler); }, registerWebGlEventCallback: function(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { @@ -835,145 +839,138 @@ var LibraryJsEvents = { }; var eventHandler = { - target: JsEvents.findEventTarget(target), + target: JSEvents.findEventTarget(target), allowsDeferredCalls: false, eventTypeString: eventTypeString, callbackfunc: callbackfunc, handlerFunc: handlerFunc, useCapture: useCapture }; - JsEvents.registerOrRemoveHandler(eventHandler); + JSEvents.registerOrRemoveHandler(eventHandler); }, }, emscripten_set_keypress_callback: function(target, userData, useCapture, callbackfunc) { - JsEvents.registerKeyEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_KEYPRESS') }}}, "keypress"); + JSEvents.registerKeyEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_KEYPRESS') }}}, "keypress"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_keydown_callback: function(target, userData, useCapture, callbackfunc) { - JsEvents.registerKeyEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_KEYDOWN') }}}, "keydown"); + JSEvents.registerKeyEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_KEYDOWN') }}}, "keydown"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_keyup_callback: function(target, userData, useCapture, callbackfunc) { - JsEvents.registerKeyEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_KEYUP') }}}, "keyup"); + JSEvents.registerKeyEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_KEYUP') }}}, "keyup"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_click_callback: function(target, userData, useCapture, callbackfunc) { - JsEvents.registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_CLICK') }}}, "click"); + JSEvents.registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_CLICK') }}}, "click"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_mousedown_callback: function(target, userData, useCapture, callbackfunc) { - JsEvents.registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_MOUSEDOWN') }}}, "mousedown"); + JSEvents.registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_MOUSEDOWN') }}}, "mousedown"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_mouseup_callback: function(target, userData, useCapture, callbackfunc) { - JsEvents.registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_MOUSEUP') }}}, "mouseup"); + JSEvents.registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_MOUSEUP') }}}, "mouseup"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_dblclick_callback: function(target, userData, useCapture, callbackfunc) { - JsEvents.registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_DBLCLICK') }}}, "dblclick"); + JSEvents.registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_DBLCLICK') }}}, "dblclick"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_mousemove_callback: function(target, userData, useCapture, callbackfunc) { - JsEvents.registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_MOUSEMOVE') }}}, "mousemove"); + JSEvents.registerMouseEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_MOUSEMOVE') }}}, "mousemove"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_get_mouse_status: function(mouseState) { - if (!JsEvents.mouseEvent) { - return {{{ cDefine('EMSCRIPTEN_RESULT_NO_DATA') }}}; - } + if (!JSEvents.mouseEvent) return {{{ cDefine('EMSCRIPTEN_RESULT_NO_DATA') }}}; // HTML5 does not really have a polling API for mouse events, so implement one manually by // returning the data from the most recently received event. This requires that user has registered // at least some no-op function as an event handler to any of the mouse function. - HEAP32.set(HEAP32.subarray(JsEvents.mouseEvent, {{{ C_STRUCTS.emscripten_MouseEvent.__size__ }}}), mouseState); + HEAP32.set(HEAP32.subarray(JSEvents.mouseEvent, {{{ C_STRUCTS.EmscriptenMouseEvent.__size__ }}}), mouseState); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_wheel_callback: function(target, userData, useCapture, callbackfunc) { - JsEvents.registerWheelEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_WHEEL') }}}, "wheel"); + JSEvents.registerWheelEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_WHEEL') }}}, "wheel"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_resize_callback: function(target, userData, useCapture, callbackfunc) { - JsEvents.registerUiEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_RESIZE') }}}, "resize"); + JSEvents.registerUiEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_RESIZE') }}}, "resize"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_scroll_callback: function(target, userData, useCapture, callbackfunc) { - JsEvents.registerUiEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_SCROLL') }}}, "scroll"); + JSEvents.registerUiEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_SCROLL') }}}, "scroll"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_blur_callback: function(target, userData, useCapture, callbackfunc) { - JsEvents.registerFocusEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_BLUR') }}}, "blur"); + JSEvents.registerFocusEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_BLUR') }}}, "blur"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_focus_callback: function(target, userData, useCapture, callbackfunc) { - JsEvents.registerFocusEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FOCUS') }}}, "focus"); + JSEvents.registerFocusEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FOCUS') }}}, "focus"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_focusin_callback: function(target, userData, useCapture, callbackfunc) { - JsEvents.registerFocusEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FOCUSIN') }}}, "focusin"); + JSEvents.registerFocusEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FOCUSIN') }}}, "focusin"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_focusout_callback: function(target, userData, useCapture, callbackfunc) { - JsEvents.registerFocusEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FOCUSOUT') }}}, "focusout"); + JSEvents.registerFocusEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FOCUSOUT') }}}, "focusout"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_deviceorientation_callback: function(userData, useCapture, callbackfunc) { - JsEvents.registerDeviceOrientationEventCallback(window, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_DEVICEORIENTATION') }}}, "deviceorientation"); + JSEvents.registerDeviceOrientationEventCallback(window, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_DEVICEORIENTATION') }}}, "deviceorientation"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_get_deviceorientation_status: function(orientationState) { - if (!JsEvents.deviceOrientationEvent) { - return {{{ cDefine('EMSCRIPTEN_RESULT_NO_DATA') }}}; - } + if (!JSEvents.deviceOrientationEvent) return {{{ cDefine('EMSCRIPTEN_RESULT_NO_DATA') }}}; // HTML5 does not really have a polling API for device orientation events, so implement one manually by // returning the data from the most recently received event. This requires that user has registered // at least some no-op function as an event handler. - HEAP32.set(HEAP32.subarray(JsEvents.deviceOrientationEvent, {{{ C_STRUCTS.emscripten_DeviceOrientationEvent.__size__ }}}), orientationState); + HEAP32.set(HEAP32.subarray(JSEvents.deviceOrientationEvent, {{{ C_STRUCTS.EmscriptenDeviceOrientationEvent.__size__ }}}), orientationState); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_devicemotion_callback: function(userData, useCapture, callbackfunc) { - JsEvents.registerDeviceMotionEventCallback(window, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_DEVICEMOTION') }}}, "devicemotion"); + JSEvents.registerDeviceMotionEventCallback(window, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_DEVICEMOTION') }}}, "devicemotion"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_get_devicemotion_status: function(motionState) { - if (!JsEvents.deviceMotionEvent) { - return {{{ cDefine('EMSCRIPTEN_RESULT_NO_DATA') }}}; - } + if (!JSEvents.deviceMotionEvent) return {{{ cDefine('EMSCRIPTEN_RESULT_NO_DATA') }}}; // HTML5 does not really have a polling API for device motion events, so implement one manually by // returning the data from the most recently received event. This requires that user has registered // at least some no-op function as an event handler. - HEAP32.set(HEAP32.subarray(JsEvents.deviceMotionEvent, {{{ C_STRUCTS.emscripten_DeviceMotionEvent.__size__ }}}), motionState); + HEAP32.set(HEAP32.subarray(JSEvents.deviceMotionEvent, {{{ C_STRUCTS.EmscriptenDeviceMotionEvent.__size__ }}}), motionState); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_orientationchange_callback: function(userData, useCapture, callbackfunc) { - if (!window.screen || !window.screen.addEventListener) { - return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; - } - JsEvents.registerOrientationChangeEventCallback(window.screen, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_ORIENTATIONCHANGE') }}}, "orientationchange"); + if (!window.screen || !window.screen.addEventListener) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + JSEvents.registerOrientationChangeEventCallback(window.screen, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_ORIENTATIONCHANGE') }}}, "orientationchange"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_get_orientation_status: function(orientationChangeEvent) { - JsEvents.fillOrientationChangeEventData(orientationChangeEvent); + if (!JSEvents.screenOrientation() && typeof window.orientation === 'undefined') return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + JSEvents.fillOrientationChangeEventData(orientationChangeEvent); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, @@ -1012,48 +1009,57 @@ var LibraryJsEvents = { return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, - emscripten_set_fullscreenchange_callback: function(userData, useCapture, callbackfunc) { - JsEvents.registerFullscreenChangeEventCallback(userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FULLSCREENCHANGE') }}}, "fullscreenchange"); + emscripten_set_fullscreenchange_callback: function(target, userData, useCapture, callbackfunc) { + if (typeof JSEvents.fullscreenEnabled() === 'undefined') return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + if (!target) target = document; + else { + target = JSEvents.findEventTarget(target); + if (!target) return {{{ cDefine('EMSCRIPTEN_RESULT_UNKNOWN_TARGET') }}}; + } + JSEvents.registerFullscreenChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FULLSCREENCHANGE') }}}, "fullscreenchange"); + JSEvents.registerFullscreenChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FULLSCREENCHANGE') }}}, "mozfullscreenchange"); + JSEvents.registerFullscreenChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FULLSCREENCHANGE') }}}, "webkitfullscreenchange"); + JSEvents.registerFullscreenChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_FULLSCREENCHANGE') }}}, "msfullscreenchange"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_get_fullscreen_status: function(fullscreenStatus) { - JsEvents.fillFullscreenChangeEventData(fullscreenStatus); + if (typeof JSEvents.fullscreenEnabled() === 'undefined') return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + JSEvents.fillFullscreenChangeEventData(fullscreenStatus); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, // https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Using_full_screen_mode emscripten_request_fullscreen: function(target, deferUntilInEventHandler) { - if (!target) { - target = '#canvas'; - } - target = JsEvents.findEventTarget(target); - if (!target) { - return {{{ cDefine('EMSCRIPTEN_RESULT_INVALID_TARGET') }}}; - } + if (typeof JSEvents.fullscreenEnabled() === 'undefined') return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + if (!JSEvents.fullscreenEnabled()) return {{{ cDefine('EMSCRIPTEN_RESULT_INVALID_TARGET') }}}; + if (!target) target = '#canvas'; + target = JSEvents.findEventTarget(target); + if (!target) return {{{ cDefine('EMSCRIPTEN_RESULT_UNKNOWN_TARGET') }}}; - if (!target.requestFullscreen && !target.msRequestFullscreen && !target.mozRequestFullScreen && !target.webkitRequestFullscreen) { - return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + if (!target.requestFullscreen && !target.msRequestFullscreen && !target.mozRequestFullScreen && !target.mozRequestFullscreen && !target.webkitRequestFullscreen) { + return {{{ cDefine('EMSCRIPTEN_RESULT_INVALID_TARGET') }}}; } - var canPerformRequests = JsEvents.canPerformEventHandlerRequests(); + var canPerformRequests = JSEvents.canPerformEventHandlerRequests(); // Queue this function call if we're not currently in an event handler and the user saw it appropriate to do so. if (!canPerformRequests) { if (deferUntilInEventHandler) { - JsEvents.deferCall(JsEvents.requestFullscreen, 1 /* priority over pointer lock */, [target]); + JSEvents.deferCall(JSEvents.requestFullscreen, 1 /* priority over pointer lock */, [target]); return {{{ cDefine('EMSCRIPTEN_RESULT_DEFERRED') }}}; } else { return {{{ cDefine('EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED') }}}; } } - return JsEvents.requestFullscreen(target); + return JSEvents.requestFullscreen(target); }, emscripten_exit_fullscreen: function() { + if (typeof JSEvents.fullscreenEnabled() === 'undefined') return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; // Make sure no queued up calls will fire after this. - JsEvents.removeDeferredCalls(JsEvents.requestFullscreen); + JSEvents.removeDeferredCalls(JSEvents.requestFullscreen); if (document.exitFullscreen) { document.exitFullscreen(); @@ -1069,46 +1075,56 @@ var LibraryJsEvents = { return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, - emscripten_set_pointerlockchange_callback: function(userData, useCapture, callbackfunc) { - JsEvents.registerPointerlockChangeEventCallback(document, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_POINTERLOCKCHANGE') }}}, "pointerlockchange"); + emscripten_set_pointerlockchange_callback: function(target, userData, useCapture, callbackfunc) { + if (!document.body.requestPointerLock && !document.body.mozRequestPointerLock && !document.body.webkitRequestPointerLock && !document.body.msRequestPointerLock) { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } + if (!target) target = document; + else { + target = JSEvents.findEventTarget(target); + if (!target) return {{{ cDefine('EMSCRIPTEN_RESULT_UNKNOWN_TARGET') }}}; + } + JSEvents.registerPointerlockChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_POINTERLOCKCHANGE') }}}, "pointerlockchange"); + JSEvents.registerPointerlockChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_POINTERLOCKCHANGE') }}}, "mozpointerlockchange"); + JSEvents.registerPointerlockChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_POINTERLOCKCHANGE') }}}, "webkitpointerlockchange"); + JSEvents.registerPointerlockChangeEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_POINTERLOCKCHANGE') }}}, "mspointerlockchange"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_get_pointerlock_status: function(pointerlockStatus) { - JsEvents.fillPointerlockChangeEventData(pointerlockStatus); + if (!document.body.requestPointerLock && !document.body.mozRequestPointerLock && !document.body.webkitRequestPointerLock && !document.body.msRequestPointerLock) { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } + JSEvents.fillPointerlockChangeEventData(pointerlockStatus); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_request_pointerlock: function(target, deferUntilInEventHandler) { - if (!target) { - target = '#canvas'; - } - target = JsEvents.findEventTarget(target); - if (!target) { - return {{{ cDefine('EMSCRIPTEN_RESULT_INVALID_TARGET') }}}; - } + if (!target) target = '#canvas'; + target = JSEvents.findEventTarget(target); + if (!target) return {{{ cDefine('EMSCRIPTEN_RESULT_UNKNOWN_TARGET') }}}; if (!target.requestPointerLock && !target.mozRequestPointerLock && !target.webkitRequestPointerLock && !target.msRequestPointerLock) { return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; } - var canPerformRequests = JsEvents.canPerformEventHandlerRequests(); + var canPerformRequests = JSEvents.canPerformEventHandlerRequests(); // Queue this function call if we're not currently in an event handler and the user saw it appropriate to do so. if (!canPerformRequests) { if (deferUntilInEventHandler) { - JsEvents.deferCall(JsEvents.requestPointerLock, 2 /* priority below fullscreen */, [target]); + JSEvents.deferCall(JSEvents.requestPointerLock, 2 /* priority below fullscreen */, [target]); return {{{ cDefine('EMSCRIPTEN_RESULT_DEFERRED') }}}; } else { return {{{ cDefine('EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED') }}}; } } - return JsEvents.requestPointerLock(target); + return JSEvents.requestPointerLock(target); }, emscripten_exit_pointerlock: function() { // Make sure no queued up calls will fire after this. - JsEvents.removeDeferredCalls(JsEvents.requestPointerLock); + JSEvents.removeDeferredCalls(JSEvents.requestPointerLock); if (document.exitPointerLock) { document.exitPointerLock(); @@ -1125,18 +1141,13 @@ var LibraryJsEvents = { }, emscripten_vibrate: function(msecs) { - if (!navigator.vibrate) { - return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; - } - + if (!navigator.vibrate) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; navigator.vibrate(msecs); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_vibrate_pattern: function(msecsArray, numEntries) { - if (!navigator.vibrate) { - return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; - } + if (!navigator.vibrate) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; var vibrateList = []; for(var i = 0; i < numEntries; ++i) { @@ -1148,94 +1159,99 @@ var LibraryJsEvents = { }, emscripten_set_visibilitychange_callback: function(userData, useCapture, callbackfunc) { - JsEvents.registerVisibilityChangeEventCallback(document, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_VISIBILITYCHANGE') }}}, "visibilitychange"); + JSEvents.registerVisibilityChangeEventCallback(document, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_VISIBILITYCHANGE') }}}, "visibilitychange"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_get_visibility_status: function(visibilityStatus) { - JsEvents.fillVisibilityChangeEventData(visibilityStatus); + if (typeof document.visibilityState === 'undefined' && typeof document.hidden === 'undefined') { + return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + } + JSEvents.fillVisibilityChangeEventData(visibilityStatus); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_touchstart_callback: function(target, userData, useCapture, callbackfunc) { - JsEvents.registerTouchEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_TOUCHSTART') }}}, "touchstart"); + JSEvents.registerTouchEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_TOUCHSTART') }}}, "touchstart"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_touchend_callback: function(target, userData, useCapture, callbackfunc) { - JsEvents.registerTouchEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_TOUCHEND') }}}, "touchend"); + JSEvents.registerTouchEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_TOUCHEND') }}}, "touchend"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_touchmove_callback: function(target, userData, useCapture, callbackfunc) { - JsEvents.registerTouchEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_TOUCHMOVE') }}}, "touchmove"); + JSEvents.registerTouchEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_TOUCHMOVE') }}}, "touchmove"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_touchcancel_callback: function(target, userData, useCapture, callbackfunc) { - JsEvents.registerTouchEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_TOUCHCANCEL') }}}, "touchcancel"); + JSEvents.registerTouchEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_TOUCHCANCEL') }}}, "touchcancel"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_gamepadconnected_callback: function(userData, useCapture, callbackfunc) { - JsEvents.registerGamepadEventCallback(window, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_GAMEPADCONNECTED') }}}, "gamepadconnected"); + if (!navigator.getGamepads) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + JSEvents.registerGamepadEventCallback(window, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_GAMEPADCONNECTED') }}}, "gamepadconnected"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_gamepaddisconnected_callback: function(userData, useCapture, callbackfunc) { - JsEvents.registerGamepadEventCallback(window, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_GAMEPADDISCONNECTED') }}}, "gamepaddisconnected"); + if (!navigator.getGamepads) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + JSEvents.registerGamepadEventCallback(window, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_GAMEPADDISCONNECTED') }}}, "gamepaddisconnected"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_get_num_gamepads: function() { - if (!navigator.getGamepads) { - return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; - } + if (!navigator.getGamepads) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; return navigator.getGamepads().length; }, emscripten_get_gamepad_status: function(index, gamepadState) { - if (!navigator.getGamepads) { - return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; - } + if (!navigator.getGamepads) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; var gamepads = navigator.getGamepads(); if (index < 0 || index >= gamepads.length) { return {{{ cDefine('EMSCRIPTEN_RESULT_INVALID_PARAM') }}}; } - JsEvents.fillGamepadEventData(gamepadState, gamepads[index]); + JSEvents.fillGamepadEventData(gamepadState, gamepads[index]); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_beforeunload_callback: function(userData, callbackfunc) { - JsEvents.registerBeforeUnloadEventCallback(window, userData, true, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_BEFOREUNLOAD') }}}, "beforeunload"); + if (typeof window.onbeforeunload === 'undefined') return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + JSEvents.registerBeforeUnloadEventCallback(window, userData, true, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_BEFOREUNLOAD') }}}, "beforeunload"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_batterychargingchange_callback: function(userData, callbackfunc) { - JsEvents.registerBatteryEventCallback(JsEvents.battery(), userData, true, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_BATTERYCHARGINGCHANGE') }}}, "chargingchange"); + if (!JSEvents.battery()) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + JSEvents.registerBatteryEventCallback(JSEvents.battery(), userData, true, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_BATTERYCHARGINGCHANGE') }}}, "chargingchange"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_batterylevelchange_callback: function(userData, callbackfunc) { - JsEvents.registerBatteryEventCallback(JsEvents.battery(), userData, true, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_BATTERYLEVELCHANGE') }}}, "levelchange"); + if (!JSEvents.battery()) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + JSEvents.registerBatteryEventCallback(JSEvents.battery(), userData, true, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_BATTERYLEVELCHANGE') }}}, "levelchange"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_get_battery_status: function(batteryState) { - JsEvents.fillBatteryEventData(batteryState, JsEvents.battery()); + if (!JSEvents.battery()) return {{{ cDefine('EMSCRIPTEN_RESULT_NOT_SUPPORTED') }}}; + JSEvents.fillBatteryEventData(batteryState, JSEvents.battery()); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_webglcontextlost_callback: function(target, userData, useCapture, callbackfunc) { - JsEvents.registerWebGlEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST') }}}, "webglcontextlost"); + JSEvents.registerWebGlEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST') }}}, "webglcontextlost"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, emscripten_set_webglcontextrestored_callback: function(target, userData, useCapture, callbackfunc) { - JsEvents.registerWebGlEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED') }}}, "webglcontextrestored"); + JSEvents.registerWebGlEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED') }}}, "webglcontextrestored"); return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}}; }, }; -autoAddDeps(LibraryJsEvents, '$JsEvents'); -mergeInto(LibraryManager.library, LibraryJsEvents); +autoAddDeps(LibraryJSEvents, '$JSEvents'); +mergeInto(LibraryManager.library, LibraryJSEvents); diff --git a/src/modules.js b/src/modules.js index 7b267971faec0..ea1509e9de448 100644 --- a/src/modules.js +++ b/src/modules.js @@ -424,7 +424,7 @@ var LibraryManager = { load: function() { if (this.library) return; - var libraries = ['library.js', 'library_path.js', 'library_fs.js', 'library_idbfs.js', 'library_memfs.js', 'library_nodefs.js', 'library_sockfs.js', 'library_tty.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js', 'library_jansson.js', 'library_openal.js', 'library_glfw.js', 'library_uuid.js', 'library_glew.js', 'library_events.js'].concat(additionalLibraries); + var libraries = ['library.js', 'library_path.js', 'library_fs.js', 'library_idbfs.js', 'library_memfs.js', 'library_nodefs.js', 'library_sockfs.js', 'library_tty.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js', 'library_jansson.js', 'library_openal.js', 'library_glfw.js', 'library_uuid.js', 'library_glew.js', 'library_html5.js'].concat(additionalLibraries); for (var i = 0; i < libraries.length; i++) { eval(processMacros(preprocess(read(libraries[i])))); } diff --git a/src/struct_info.json b/src/struct_info.json index ec44909744cdb..32261c0a7a3c8 100644 --- a/src/struct_info.json +++ b/src/struct_info.json @@ -1075,10 +1075,10 @@ "structs": {} }, // =========================================== - // emscripten events library + // emscripten html5 library // =========================================== { - "file": "emscripten/events.h", + "file": "emscripten/html5.h", "defines": [ "EMSCRIPTEN_EVENT_KEYPRESS", "EMSCRIPTEN_EVENT_KEYDOWN", @@ -1117,13 +1117,14 @@ "EMSCRIPTEN_RESULT_DEFERRED", "EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED", "EMSCRIPTEN_RESULT_INVALID_TARGET", + "EMSCRIPTEN_RESULT_UNKNOWN_TARGET", "EMSCRIPTEN_RESULT_INVALID_PARAM", "EMSCRIPTEN_RESULT_NOT_SUPPORTED", "EMSCRIPTEN_RESULT_FAILED", "EMSCRIPTEN_RESULT_NO_DATA" ], "structs": { - "emscripten_KeyboardEvent": [ + "EmscriptenKeyboardEvent": [ "key", "code", "location", @@ -1138,7 +1139,7 @@ "keyCode", "which" ], - "emscripten_MouseEvent": [ + "EmscriptenMouseEvent": [ "timestamp", "screenX", "screenY", @@ -1155,14 +1156,14 @@ "canvasX", "canvasY" ], - "emscripten_WheelEvent": [ + "EmscriptenWheelEvent": [ "mouse", "deltaX", "deltaY", "deltaZ", "deltaMode" ], - "emscripten_UiEvent": [ + "EmscriptenUiEvent": [ "detail", "documentBodyClientWidth", "documentBodyClientHeight", @@ -1173,18 +1174,18 @@ "scrollTop", "scrollLeft" ], - "emscripten_FocusEvent": [ + "EmscriptenFocusEvent": [ "nodeName", "id" ], - "emscripten_DeviceOrientationEvent": [ + "EmscriptenDeviceOrientationEvent": [ "timestamp", "alpha", "beta", "gamma", "absolute" ], - "emscripten_DeviceMotionEvent": [ + "EmscriptenDeviceMotionEvent": [ "timestamp", "accelerationX", "accelerationY", @@ -1196,26 +1197,26 @@ "rotationRateBeta", "rotationRateGamma" ], - "emscripten_OrientationChangeEvent": [ + "EmscriptenOrientationChangeEvent": [ "orientationIndex", "orientationAngle" ], - "emscripten_FullscreenChangeEvent": [ + "EmscriptenFullscreenChangeEvent": [ "isFullscreen", "fullscreenEnabled", "nodeName", "id" ], - "emscripten_PointerlockChangeEvent": [ + "EmscriptenPointerlockChangeEvent": [ "isActive", "nodeName", "id" ], - "emscripten_VisibilityChangeEvent": [ + "EmscriptenVisibilityChangeEvent": [ "hidden", "visibilityState" ], - "emscripten_TouchPoint": [ + "EmscriptenTouchPoint": [ "identifier", "screenX", "screenY", @@ -1228,7 +1229,7 @@ "canvasX", "canvasY" ], - "emscripten_TouchEvent": [ + "EmscriptenTouchEvent": [ "numTouches", "ctrlKey", "shiftKey", @@ -1236,7 +1237,7 @@ "metaKey", "touches" ], - "emscripten_GamepadEvent": [ + "EmscriptenGamepadEvent": [ "timestamp", "axis", "analogButton", @@ -1248,7 +1249,7 @@ "id", "mapping" ], - "emscripten_BatteryEvent": [ + "EmscriptenBatteryEvent": [ "chargingTime", "dischargingTime", "level", diff --git a/system/include/emscripten/events.h b/system/include/emscripten/html5.h similarity index 86% rename from system/include/emscripten/events.h rename to system/include/emscripten/html5.h index 4caae3cfd5fd8..06c647bfd757e 100644 --- a/system/include/emscripten/events.h +++ b/system/include/emscripten/html5.h @@ -99,14 +99,17 @@ extern "C" { // The given target element for the operation is invalid. #define EMSCRIPTEN_RESULT_INVALID_TARGET -3 +// The given target element for the operation was not found. +#define EMSCRIPTEN_RESULT_UNKNOWN_TARGET -4 + // An invalid parameter was passed to the function. -#define EMSCRIPTEN_RESULT_INVALID_PARAM -4 +#define EMSCRIPTEN_RESULT_INVALID_PARAM -5 // The operation failed due to some generic reason. -#define EMSCRIPTEN_RESULT_FAILED -5 +#define EMSCRIPTEN_RESULT_FAILED -6 // Operation failed since no data is currently available. -#define EMSCRIPTEN_RESULT_NO_DATA -6 +#define EMSCRIPTEN_RESULT_NO_DATA -7 #define EM_BOOL int #define EM_UTF8 char @@ -121,7 +124,7 @@ extern "C" { * The event structure passed in keyboard events keypress, keydown and keyup. * https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#keys */ -typedef struct emscripten_KeyboardEvent { +typedef struct EmscriptenKeyboardEvent { // The printed representation of the pressed key. EM_UTF8 key[32]; // A string that identifies the physical key being pressed. The value is not affected by the current keyboard @@ -150,22 +153,22 @@ typedef struct emscripten_KeyboardEvent { unsigned long keyCode; // A system and implementation dependent numeric code identifying the unmodified value of the pressed key; this is usually the same as keyCode. unsigned long which; -} emscripten_KeyboardEvent; +} EmscriptenKeyboardEvent; /* * Registers a callback function for receiving browser-generated keyboard input events. * See https://developer.mozilla.org/en/DOM/Event/UIEvent/KeyEvent * and http://www.javascriptkit.com/jsref/eventkeyboardmouse.shtml */ -extern EMSCRIPTEN_RESULT emscripten_set_keypress_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const emscripten_KeyboardEvent *keyEvent, void *userData)); -extern EMSCRIPTEN_RESULT emscripten_set_keydown_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const emscripten_KeyboardEvent *keyEvent, void *userData)); -extern EMSCRIPTEN_RESULT emscripten_set_keyup_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const emscripten_KeyboardEvent *keyEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_keypress_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_keydown_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_keyup_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)); /* * The event structure passed in mouse events click, mousedown, mouseup, dblclick and mousemove. * https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#interface-MouseEvent */ -typedef struct emscripten_MouseEvent { +typedef struct EmscriptenMouseEvent { // A timestamp of when this data was generated by the browser. This is an absolute wallclock time in milliseconds. double timestamp; // The coordinate relative to the browser screen coordinate system. @@ -191,22 +194,22 @@ typedef struct emscripten_MouseEvent { long canvasY; // Pad this struct to multiple of 8 bytes to make WheelEvent unambiguously align to 8 bytes. long padding; -} emscripten_MouseEvent; +} EmscriptenMouseEvent; /* * Registers a callback function for receiving browser-generated mouse input events. * See https://developer.mozilla.org/en/DOM/MouseEvent */ -extern EMSCRIPTEN_RESULT emscripten_set_click_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const emscripten_MouseEvent *mouseEvent, void *userData)); -extern EMSCRIPTEN_RESULT emscripten_set_mousedown_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const emscripten_MouseEvent *mouseEvent, void *userData)); -extern EMSCRIPTEN_RESULT emscripten_set_mouseup_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const emscripten_MouseEvent *mouseEvent, void *userData)); -extern EMSCRIPTEN_RESULT emscripten_set_dblclick_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const emscripten_MouseEvent *mouseEvent, void *userData)); -extern EMSCRIPTEN_RESULT emscripten_set_mousemove_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const emscripten_MouseEvent *mouseEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_click_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_mousedown_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_mouseup_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_dblclick_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_mousemove_callback(const char *target, void *userData, int useCapture, int (*func)(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)); /* * Returns the most recently received mouse event state. Note that for this function call to succeed, emscripten_set_xx_callback must have first * been called with one of the mouse event types and a non-zero callback function pointer to enable the Mouse state capture. */ -extern EMSCRIPTEN_RESULT emscripten_get_mouse_status(emscripten_MouseEvent *mouseState); +extern EMSCRIPTEN_RESULT emscripten_get_mouse_status(EmscriptenMouseEvent *mouseState); #define DOM_DELTA_PIXEL 0x00 #define DOM_DELTA_LINE 0x01 @@ -216,28 +219,28 @@ extern EMSCRIPTEN_RESULT emscripten_get_mouse_status(emscripten_MouseEvent *mous * The event structure passed in mouse wheelevent. * https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#interface-WheelEvent */ -typedef struct emscripten_WheelEvent { +typedef struct EmscriptenWheelEvent { // Specifies general mouse information related to this event. - emscripten_MouseEvent mouse; + EmscriptenMouseEvent mouse; // Measures along different axes the movement of the wheel. double deltaX; double deltaY; double deltaZ; // One of the DOM_DELTA_ values that indicates the units of measurement for the delta values. unsigned long deltaMode; -} emscripten_WheelEvent; +} EmscriptenWheelEvent; /* * Registers a callback function for receiving browser-generated mouse wheel events. * See http://www.w3.org/TR/DOM-Level-3-Events/#event-type-wheel */ -extern EMSCRIPTEN_RESULT emscripten_set_wheel_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_WheelEvent *wheelEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_wheel_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData)); /* * The event structure passed in DOM element resize and scroll events. * https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#interface-UIEvent */ -typedef struct emscripten_UiEvent { +typedef struct EmscriptenUiEvent { // Specifies detail information about this event. long detail; // The clientWidth/clientHeight of the document.body element. @@ -252,40 +255,40 @@ typedef struct emscripten_UiEvent { // The page scroll position. int scrollTop; int scrollLeft; -} emscripten_UiEvent; +} EmscriptenUiEvent; /* * Registers a callback function for receiving DOM element resize and scroll events. * See https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-resize */ -extern EMSCRIPTEN_RESULT emscripten_set_resize_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_UiEvent *uiEvent, void *userData)); -extern EMSCRIPTEN_RESULT emscripten_set_scroll_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_UiEvent *uiEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_resize_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenUiEvent *uiEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_scroll_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenUiEvent *uiEvent, void *userData)); /* * The event structure passed in DOM element blur, focus, focusin and focusout events. * https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#interface-FocusEvent */ -typedef struct emscripten_FocusEvent { +typedef struct EmscriptenFocusEvent { // The nodeName of the target HTML Element. See https://developer.mozilla.org/en-US/docs/Web/API/Node.nodeName EM_UTF8 nodeName[128]; // The HTML Element ID of the target element. EM_UTF8 id[128]; -} emscripten_FocusEvent; +} EmscriptenFocusEvent; /* * Registers a callback function for receiving DOM element blur, focus, focusin and focusout events. * See https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-blur */ -extern EMSCRIPTEN_RESULT emscripten_set_blur_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_FocusEvent *focusEvent, void *userData)); -extern EMSCRIPTEN_RESULT emscripten_set_focus_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_FocusEvent *focusEvent, void *userData)); -extern EMSCRIPTEN_RESULT emscripten_set_focusin_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_FocusEvent *focusEvent, void *userData)); -extern EMSCRIPTEN_RESULT emscripten_set_focusout_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_FocusEvent *focusEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_blur_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_focus_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_focusin_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_focusout_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData)); /* * The event structure passed in the deviceorientation event. * http://dev.w3.org/geo/api/spec-source-orientation.html#deviceorientation */ -typedef struct emscripten_DeviceOrientationEvent { +typedef struct EmscriptenDeviceOrientationEvent { // Absolute wallclock time in msec units of when the event occurred. double timestamp; // The orientation of the device in terms of the transformation from a coordinate frame fixed on the Earth to a coordinate frame fixed in the device. @@ -294,24 +297,24 @@ typedef struct emscripten_DeviceOrientationEvent { double gamma; // If false, the orientation is only relative to some other bas orinetation, not to the fixed coordinate frame. EM_BOOL absolute; -} emscripten_DeviceOrientationEvent; +} EmscriptenDeviceOrientationEvent; /* * Registers a callback function for receiving the deviceorientation event. * See http://dev.w3.org/geo/api/spec-source-orientation.html */ -extern EMSCRIPTEN_RESULT emscripten_set_deviceorientation_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_DeviceOrientationEvent *orientationEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_deviceorientation_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenDeviceOrientationEvent *orientationEvent, void *userData)); /* * Returns the most recently received deviceorientation event state. Note that for this function call to succeed, emscripten_set_deviceorientation_callback * must have first been called with one of the mouse event types and a non-zero callback function pointer to enable the Device Orientation state capture. */ -extern EMSCRIPTEN_RESULT emscripten_get_deviceorientation_status(emscripten_DeviceOrientationEvent *orientationState); +extern EMSCRIPTEN_RESULT emscripten_get_deviceorientation_status(EmscriptenDeviceOrientationEvent *orientationState); /* * The event structure passed in the devicemotion event. * http://dev.w3.org/geo/api/spec-source-orientation.html#devicemotion */ -typedef struct emscripten_DeviceMotionEvent { +typedef struct EmscriptenDeviceMotionEvent { // Absolute wallclock time in msec units of when the event occurred. double timestamp; // Acceleration of the device excluding gravity. @@ -326,18 +329,18 @@ typedef struct emscripten_DeviceMotionEvent { double rotationRateAlpha; double rotationRateBeta; double rotationRateGamma; -} emscripten_DeviceMotionEvent; +} EmscriptenDeviceMotionEvent; /* * Registers a callback function for receiving the devicemotion event. * See http://dev.w3.org/geo/api/spec-source-orientation.html */ -extern EMSCRIPTEN_RESULT emscripten_set_devicemotion_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_DeviceMotionEvent *motionEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_devicemotion_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenDeviceMotionEvent *motionEvent, void *userData)); /* * Returns the most recently received deviceomotion event state. Note that for this function call to succeed, emscripten_set_devicemotion_callback * must have first been called with one of the mouse event types and a non-zero callback function pointer to enable the Device Motion state capture. */ -extern EMSCRIPTEN_RESULT emscripten_get_devicemotion_status(emscripten_DeviceMotionEvent *motionState); +extern EMSCRIPTEN_RESULT emscripten_get_devicemotion_status(EmscriptenDeviceMotionEvent *motionState); #define EMSCRIPTEN_ORIENTATION_PORTRAIT_PRIMARY 1 #define EMSCRIPTEN_ORIENTATION_PORTRAIT_SECONDARY 2 @@ -348,23 +351,23 @@ extern EMSCRIPTEN_RESULT emscripten_get_devicemotion_status(emscripten_DeviceMot * The event structure passed in the orientationchange event. * https://dvcs.w3.org/hg/screen-orientation/raw-file/tip/Overview.html */ -typedef struct emscripten_OrientationChangeEvent { +typedef struct EmscriptenOrientationChangeEvent { // One of EM_ORIENTATION_PORTRAIT_xx fields, or -1 if unknown. int orientationIndex; // Emscripten-specific extension: Some browsers refer to 'window.orientation', so report that as well. // Orientation angle in degrees. 0: "default orientation", i.e. default upright orientation to hold the mobile device in. Could be either landscape or portrait. int orientationAngle; -} emscripten_OrientationChangeEvent; +} EmscriptenOrientationChangeEvent; /* * Registers a callback function for receiving the orientationchange event. * https://dvcs.w3.org/hg/screen-orientation/raw-file/tip/Overview.html */ -extern EMSCRIPTEN_RESULT emscripten_set_orientationchange_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_OrientationChangeEvent *orientationChangeEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_orientationchange_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenOrientationChangeEvent *orientationChangeEvent, void *userData)); /* * Returns the current device orientation state. */ -extern EMSCRIPTEN_RESULT emscripten_get_orientation_status(emscripten_OrientationChangeEvent *orientationStatus); +extern EMSCRIPTEN_RESULT emscripten_get_orientation_status(EmscriptenOrientationChangeEvent *orientationStatus); /* * Locks the screen orientation to the given set of allowed orientations. * allowedOrientations: A bitfield set of EM_ORIENTATION_xx flags. @@ -379,7 +382,7 @@ extern EMSCRIPTEN_RESULT emscripten_unlock_orientation(); * The event structure passed in the fullscreenchange event. * https://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html */ -typedef struct emscripten_FullscreenChangeEvent { +typedef struct EmscriptenFullscreenChangeEvent { // Specifies whether an element on the browser page is currently fullscreen. EM_BOOL isFullscreen; // Specifies if the current page has the ability to display elements fullscreen. @@ -388,17 +391,17 @@ typedef struct emscripten_FullscreenChangeEvent { EM_UTF8 nodeName[128]; // The HTML Element ID of the target HTML element that is in full screen mode. EM_UTF8 id[128]; -} emscripten_FullscreenChangeEvent; +} EmscriptenFullscreenChangeEvent; /* * Registers a callback function for receiving the fullscreenchange event. * https://dvcs.w3.org/hg/screen-orientation/raw-file/tip/Overview.html */ -extern EMSCRIPTEN_RESULT emscripten_set_fullscreenchange_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_FullscreenChangeEvent *fullscreenChangeEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_fullscreenchange_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenFullscreenChangeEvent *fullscreenChangeEvent, void *userData)); /* * Returns the current page fullscreen state. */ -extern EMSCRIPTEN_RESULT emscripten_get_fullscreen_status(emscripten_FullscreenChangeEvent *fullscreenStatus); +extern EMSCRIPTEN_RESULT emscripten_get_fullscreen_status(EmscriptenFullscreenChangeEvent *fullscreenStatus); /* * Requests the given target element to transition to full screen mode. * Note: This function can only run inside a user-generated JavaScript event handler. @@ -416,25 +419,25 @@ extern EMSCRIPTEN_RESULT emscripten_exit_fullscreen(); * The event structure passed in the pointerlockchange event. * http://www.w3.org/TR/pointerlock/ */ -typedef struct emscripten_PointerlockChangeEvent { +typedef struct EmscriptenPointerlockChangeEvent { // Specifies whether an element on the browser page currently has pointer lock enabled. EM_BOOL isActive; // The nodeName of the target HTML Element that has the pointer lock active. See https://developer.mozilla.org/en-US/docs/Web/API/Node.nodeName EM_UTF8 nodeName[128]; // The HTML Element ID of the target HTML element that has the pointer lock active. EM_UTF8 id[128]; -} emscripten_PointerlockChangeEvent; +} EmscriptenPointerlockChangeEvent; /* * Registers a callback function for receiving the pointerlockchange event. * Pointer lock hides the mouse cursor and exclusively gives the target element relative mouse movement events via the mousemove event. * http://www.w3.org/TR/pointerlock/ */ -extern EMSCRIPTEN_RESULT emscripten_set_pointerlockchange_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_PointerlockChangeEvent *pointerlockChangeEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_pointerlockchange_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenPointerlockChangeEvent *pointerlockChangeEvent, void *userData)); /* * Returns the current page pointerlock state. */ -extern EMSCRIPTEN_RESULT emscripten_get_pointerlock_status(emscripten_PointerlockChangeEvent *pointerlockStatus); +extern EMSCRIPTEN_RESULT emscripten_get_pointerlock_status(EmscriptenPointerlockChangeEvent *pointerlockStatus); /* * Requests the given target element to grab pointerlock. * Note: This function can only run inside a user-generated JavaScript event handler. @@ -457,28 +460,28 @@ extern EMSCRIPTEN_RESULT emscripten_exit_pointerlock(); * The event structure passed in the visibilitychange event. * http://www.w3c-test.org/webperf/specs/PageVisibility/ */ -typedef struct emscripten_VisibilityChangeEvent { +typedef struct EmscriptenVisibilityChangeEvent { // If true, the current browser page is now hidden. EM_BOOL hidden; // Specifies a more fine-grained state of the current page visibility status. One of the EMSCRIPTEN_VISIBILITY_ values. int visibilityState; -} emscripten_VisibilityChangeEvent; +} EmscriptenVisibilityChangeEvent; /* * Registers a callback function for receiving the visibilitychange event. * http://www.w3c-test.org/webperf/specs/PageVisibility/ */ -extern EMSCRIPTEN_RESULT emscripten_set_visibilitychange_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_VisibilityChangeEvent *visibilityChangeEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_visibilitychange_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenVisibilityChangeEvent *visibilityChangeEvent, void *userData)); /* * Returns the current page visibility state. */ -extern EMSCRIPTEN_RESULT emscripten_get_visibility_status(emscripten_VisibilityChangeEvent *visibilityStatus); +extern EMSCRIPTEN_RESULT emscripten_get_visibility_status(EmscriptenVisibilityChangeEvent *visibilityStatus); /* * Specifies the status of a single touch point on the page. * See http://www.w3.org/TR/touch-events/#touch-interface */ -typedef struct emscripten_TouchPoint +typedef struct EmscriptenTouchPoint { // An identification number for each touch point. long identifier; @@ -498,13 +501,13 @@ typedef struct emscripten_TouchPoint // The touch coordinates mapped to the Emscripten canvas client area, in pixels. long canvasX; long canvasY; -} emscripten_TouchPoint; +} EmscriptenTouchPoint; /* * Specifies the data of a single touch event. * See http://www.w3.org/TR/touch-events/#touchevent-interface */ -typedef struct emscripten_TouchEvent { +typedef struct EmscriptenTouchEvent { // The number of valid elements in the touches array. int numTouches; // Specifies which modifiers were active during the key event. @@ -513,23 +516,23 @@ typedef struct emscripten_TouchEvent { EM_BOOL altKey; EM_BOOL metaKey; // An array of currently active touches, one for each finger. - emscripten_TouchPoint touches[32]; -} emscripten_TouchEvent; + EmscriptenTouchPoint touches[32]; +} EmscriptenTouchEvent; /* * Registers a callback function for receiving the touchstart, touchend, touchmove and touchcancel events. * http://www.w3.org/TR/touch-events/ */ -extern EMSCRIPTEN_RESULT emscripten_set_touchstart_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_TouchEvent *touchEvent, void *userData)); -extern EMSCRIPTEN_RESULT emscripten_set_touchend_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_TouchEvent *touchEvent, void *userData)); -extern EMSCRIPTEN_RESULT emscripten_set_touchmove_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_TouchEvent *touchEvent, void *userData)); -extern EMSCRIPTEN_RESULT emscripten_set_touchcancel_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_TouchEvent *touchEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_touchstart_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_touchend_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_touchmove_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_touchcancel_callback(const char *target, void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)); /* * Represents the current snapshot state of a gamepad. * http://www.w3.org/TR/gamepad/#gamepad-interface */ -typedef struct emscripten_GamepadEvent { +typedef struct EmscriptenGamepadEvent { // Absolute wallclock time in msec units of when the data was recorded. double timestamp; // The number of valid axes entries in the axis array. @@ -550,14 +553,14 @@ typedef struct emscripten_GamepadEvent { EM_UTF8 id[64]; // A string that identifies the layout or control mapping of this device. EM_UTF8 mapping[64]; -} emscripten_GamepadEvent; +} EmscriptenGamepadEvent; /* * Registers a callback function for receiving the gamepadconnected and gamepaddisconnected events. * http://www.w3.org/TR/gamepad/ */ -extern EMSCRIPTEN_RESULT emscripten_set_gamepadconnected_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_GamepadEvent *gamepadEvent, void *userData)); -extern EMSCRIPTEN_RESULT emscripten_set_gamepaddisconnected_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const emscripten_GamepadEvent *gamepadEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_gamepadconnected_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_gamepaddisconnected_callback(void *userData, int useCapture, EM_BOOL (*func)(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)); /* * Returns the number of gamepads connected to the system or EMSCRIPTEN_RESULT_NOT_SUPPORTED if the current browser does not support gamepads. @@ -567,29 +570,29 @@ extern int emscripten_get_num_gamepads(); /* * Returns a snapshot of the current gamepad state. */ -extern EMSCRIPTEN_RESULT emscripten_get_gamepad_status(int index, emscripten_GamepadEvent *gamepadState); +extern EMSCRIPTEN_RESULT emscripten_get_gamepad_status(int index, EmscriptenGamepadEvent *gamepadState); /* * The event structure passed in the battery chargingchange and levelchange event. * http://www.w3.org/TR/battery-status/#batterymanager-interface */ -typedef struct emscripten_BatteryEvent { +typedef struct EmscriptenBatteryEvent { double chargingTime; double dischargingTime; double level; EM_BOOL charging; -} emscripten_BatteryEvent; +} EmscriptenBatteryEvent; /* * Registers a callback function for receiving the battery chargingchange and levelchange events. * http://www.w3.org/TR/battery-status/ */ -extern EMSCRIPTEN_RESULT emscripten_set_batterychargingchange_callback(void *userData, EM_BOOL (*func)(int eventType, const emscripten_BatteryEvent *batteryEvent, void *userData)); -extern EMSCRIPTEN_RESULT emscripten_set_batterylevelchange_callback(void *userData, EM_BOOL (*func)(int eventType, const emscripten_BatteryEvent *batteryEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_batterychargingchange_callback(void *userData, EM_BOOL (*func)(int eventType, const EmscriptenBatteryEvent *batteryEvent, void *userData)); +extern EMSCRIPTEN_RESULT emscripten_set_batterylevelchange_callback(void *userData, EM_BOOL (*func)(int eventType, const EmscriptenBatteryEvent *batteryEvent, void *userData)); /* * Returns the current battery status. */ -extern EMSCRIPTEN_RESULT emscripten_get_battery_status(emscripten_BatteryEvent *batteryState); +extern EMSCRIPTEN_RESULT emscripten_get_battery_status(EmscriptenBatteryEvent *batteryState); /* * Produces a vibration feedback for given msecs. diff --git a/tests/test_browser.py b/tests/test_browser.py index e7139f6efb3b5..226eddee3b229 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -1768,5 +1768,5 @@ def test_doublestart_bug(self): self.btest('doublestart.c', args=['--pre-js', 'pre.js', '-o', 'test.html'], expected='1') - def test_events(self): - self.btest(path_from_root('tests', 'test_events.c'), expected='0') + def test_html5(self): + self.btest(path_from_root('tests', 'test_html5.c'), expected='0') diff --git a/tests/test_events.c b/tests/test_events.c deleted file mode 100644 index 23fa87045112d..0000000000000 --- a/tests/test_events.c +++ /dev/null @@ -1,382 +0,0 @@ -#include -#include -#include -#include - -static inline const char *emscripten_event_type_to_string(int eventType) { - const char *events[] = { "(invalid)", "(none)", "keypress", "keydown", "keyup", "click", "mousedown", "mouseup", "dblclick", "mousemove", "wheel", "resize", - "scroll", "blur", "focus", "focusin", "focusout", "deviceorientation", "devicemotion", "orientationchange", "fullscreenchange", "pointerlockchange", - "visibilitychange", "touchstart", "touchend", "touchmove", "touchcancel", "gamepadconnected", "gamepaddisconnected", "beforeunload", - "batterychargingchange", "batterylevelchange", "webglcontextlost", "webglcontextrestored", "(invalid)" }; - ++eventType; - if (eventType < 0) eventType = 0; - if (eventType >= sizeof(events)/sizeof(events[0])) eventType = sizeof(events)/sizeof(events[0])-1; - return events[eventType]; -} - -// The event handler functions can return 1 to suppress the event and disable the default action. That calls event.preventDefault(); -// Returning 0 signals that the event was not consumed by the code, and will allow the event to pass on and bubble up normally. -EM_BOOL key_callback(int eventType, const emscripten_KeyboardEvent *e, void *userData) -{ - printf("%s, key: \"%s\", code: \"%s\", location: %lu,%s%s%s%s repeat: %d, locale: \"%s\", char: \"%s\", charCode: %lu, keyCode: %lu, which: %lu\n", - emscripten_event_type_to_string(eventType), e->key, e->code, e->location, - e->ctrlKey ? " CTRL" : "", e->shiftKey ? " SHIFT" : "", e->altKey ? " ALT" : "", e->metaKey ? " META" : "", - e->repeat, e->locale, e->charValue, e->charCode, e->keyCode, e->which); - - if (eventType == EMSCRIPTEN_EVENT_KEYPRESS && !strcmp(e->key, "f")) { - emscripten_FullscreenChangeEvent fsce; - emscripten_get_fullscreen_status(&fsce); - if (!fsce.isFullscreen) { - emscripten_request_fullscreen(0, 1); - } else { - emscripten_exit_fullscreen(); - } - } - - if (eventType == EMSCRIPTEN_EVENT_KEYPRESS && !strcmp(e->key, "p")) { - emscripten_PointerlockChangeEvent plce; - emscripten_get_pointerlock_status(&plce); - if (!plce.isActive) { - emscripten_request_pointerlock(0, 1); - } else { - emscripten_exit_pointerlock(); - } - } - - return 0; -} - -EM_BOOL mouse_callback(int eventType, const emscripten_MouseEvent *e, void *userData) -{ - printf("%s, screen: (%ld,%ld), client: (%ld,%ld),%s%s%s%s button: %hu, buttons: %hu, movement: (%ld,%ld), canvas: (%ld,%ld)\n", - emscripten_event_type_to_string(eventType), e->screenX, e->screenY, e->clientX, e->clientY, - e->ctrlKey ? " CTRL" : "", e->shiftKey ? " SHIFT" : "", e->altKey ? " ALT" : "", e->metaKey ? " META" : "", - e->button, e->buttons, e->movementX, e->movementY, e->canvasX, e->canvasY); - - return 0; -} - -EM_BOOL wheel_callback(int eventType, const emscripten_WheelEvent *e, void *userData) -{ - printf("%s, screen: (%ld,%ld), client: (%ld,%ld),%s%s%s%s button: %hu, buttons: %hu, canvas: (%ld,%ld), delta:(%g,%g,%g), deltaMode:%lu\n", - emscripten_event_type_to_string(eventType), e->mouse.screenX, e->mouse.screenY, e->mouse.clientX, e->mouse.clientY, - e->mouse.ctrlKey ? " CTRL" : "", e->mouse.shiftKey ? " SHIFT" : "", e->mouse.altKey ? " ALT" : "", e->mouse.metaKey ? " META" : "", - e->mouse.button, e->mouse.buttons, e->mouse.canvasX, e->mouse.canvasY, - (float)e->deltaX, (float)e->deltaY, (float)e->deltaZ, e->deltaMode); - - return 0; -} - -EM_BOOL uievent_callback(int eventType, const emscripten_UiEvent *e, void *userData) -{ - printf("%s, detail: %ld, document.body.client size: (%d,%d), window.inner size: (%d,%d), scrollPos: (%d, %d)\n", - emscripten_event_type_to_string(eventType), e->detail, e->documentBodyClientWidth, e->documentBodyClientHeight, - e->windowInnerWidth, e->windowInnerHeight, e->scrollTop, e->scrollLeft); - - return 0; -} - -EM_BOOL focusevent_callback(int eventType, const emscripten_FocusEvent *e, void *userData) -{ - printf("%s, nodeName: \"%s\", id: \"%s\"\n", emscripten_event_type_to_string(eventType), e->nodeName, e->id[0] == '\0' ? "(empty string)" : e->id); - - return 0; -} - -EM_BOOL deviceorientation_callback(int eventType, const emscripten_DeviceOrientationEvent *e, void *userData) -{ - printf("%s, (%g, %g, %g)\n", emscripten_event_type_to_string(eventType), e->alpha, e->beta, e->gamma); - - return 0; -} - -EM_BOOL devicemotion_callback(int eventType, const emscripten_DeviceMotionEvent *e, void *userData) -{ - printf("%s, accel: (%g, %g, %g), accelInclGravity: (%g, %g, %g), rotationRate: (%g, %g, %g)\n", - emscripten_event_type_to_string(eventType), - e->accelerationX, e->accelerationY, e->accelerationZ, - e->accelerationIncludingGravityX, e->accelerationIncludingGravityY, e->accelerationIncludingGravityZ, - e->rotationRateAlpha, e->rotationRateBeta, e->rotationRateGamma); - - return 0; -} - -EM_BOOL orientationchange_callback(int eventType, const emscripten_OrientationChangeEvent *e, void *userData) -{ - printf("%s, orientationAngle: %d, orientationIndex: %d\n", emscripten_event_type_to_string(eventType), e->orientationAngle, e->orientationIndex); - - return 0; -} - -EM_BOOL fullscreenchange_callback(int eventType, const emscripten_FullscreenChangeEvent *e, void *userData) -{ - printf("%s, isFullscreen: %d, fullscreenEnabled: %d, fs element nodeName: \"%s\", fs element id: \"%s\"\n", - emscripten_event_type_to_string(eventType), e->isFullscreen, e->fullscreenEnabled, e->nodeName, e->id); - - return 0; -} - -EM_BOOL pointerlockchange_callback(int eventType, const emscripten_PointerlockChangeEvent *e, void *userData) -{ - printf("%s, isActive: %d, pointerlock element nodeName: \"%s\", id: \"%s\"\n", - emscripten_event_type_to_string(eventType), e->isActive, e->nodeName, e->id); - - return 0; -} - -EM_BOOL visibilitychange_callback(int eventType, const emscripten_VisibilityChangeEvent *e, void *userData) -{ - printf("%s, hidden: %d, visibilityState: %d\n", emscripten_event_type_to_string(eventType), e->hidden, e->visibilityState); - - return 0; -} - -EM_BOOL touch_callback(int eventType, const emscripten_TouchEvent *e, void *userData) -{ - printf("%s, numTouches: %d %s%s%s%s\n", - emscripten_event_type_to_string(eventType), e->numTouches, - e->ctrlKey ? " CTRL" : "", e->shiftKey ? " SHIFT" : "", e->altKey ? " ALT" : "", e->metaKey ? " META" : ""); - for(int i = 0; i < e->numTouches; ++i) - { - const emscripten_TouchPoint *t = &e->touches[i]; - printf(" %ld: screen: (%ld,%ld), client: (%ld,%ld), page: (%ld,%ld), isChanged: %d, onTarget: %d, canvas: (%ld, %ld)\n", - t->identifier, t->screenX, t->screenY, t->clientX, t->clientY, t->pageX, t->pageY, t->isChanged, t->onTarget, t->canvasX, t->canvasY); - } - - return 0; -} - -EM_BOOL gamepad_callback(int eventType, const emscripten_GamepadEvent *e, void *userData) -{ - printf("%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"\n", - eventType != 0 ? emscripten_event_type_to_string(eventType) : "Gamepad state", e->timestamp, e->connected, e->index, - e->numAxes, e->numButtons, e->id, e->mapping); - - if (e->connected) - { - for(int i = 0; i < e->numAxes; ++i) - printf("Axis %d: %g\n", i, e->axis[i]); - - for(int i = 0; i < e->numButtons; ++i) - printf("Button %d: Digital: %d, Analog: %g\n", i, e->digitalButton[i], e->analogButton[i]); - } - - return 0; -} - -const char *beforeunload_callback(int eventType, const void *reserved, void *userData) -{ -#ifdef REPORT_RESULT - return ""; // For test harness, don't show a confirmation dialog to not block and keep the test runner automated. -#else - return "Do you really want to leave the page?"; -#endif -} - -void formatTime(char *str, int seconds) -{ - int h = seconds / (60*60); - seconds -= h*60*60; - int m = seconds / 60; - seconds -= m*60; - if (h > 0) - { - sprintf(str, "%dh:%02dm:%02ds", h, m, seconds); - } - else - { - sprintf(str, "%02dm:%02ds", m, seconds); - } -} - -EM_BOOL battery_callback(int eventType, const emscripten_BatteryEvent *e, void *userData) -{ - char t1[64]; - formatTime(t1, (int)e->chargingTime); - char t2[64]; - formatTime(t2, (int)e->dischargingTime); - printf("%s: chargingTime: %s, dischargingTime: %s, level: %g%%, charging: %d\n", - emscripten_event_type_to_string(eventType), t1, t2, e->level*100, e->charging); - - return 0; -} - -EM_BOOL webglcontext_callback(int eventType, const void *reserved, void *userData) -{ - printf("%s.\n", emscripten_event_type_to_string(eventType)); - - return 0; -} - -emscripten_GamepadEvent prevState[32]; -int prevNumGamepads = 0; - -void mainloop() -{ - int numGamepads = emscripten_get_num_gamepads(); - if (numGamepads != prevNumGamepads) - { - printf("Number of connected gamepads: %d\n", numGamepads); - prevNumGamepads = numGamepads; - } - - for(int i = 0; i < numGamepads && i < 32; ++i) - { - emscripten_GamepadEvent ge; - int failed = emscripten_get_gamepad_status(i, &ge); - if (!failed) - { - int g = ge.index; - for(int j = 0; j < ge.numAxes; ++j) - { - if (ge.axis[j] != prevState[g].axis[j]) - printf("Gamepad %d, axis %d: %g\n", g, j, ge.axis[j]); - } - - for(int j = 0; j < ge.numButtons; ++j) - { - if (ge.analogButton[j] != prevState[g].analogButton[j] || ge.digitalButton[j] != prevState[g].digitalButton[j]) - printf("Gamepad %d, button %d: Digital: %d, Analog: %g\n", g, j, ge.digitalButton[j], ge.analogButton[j]); - } - prevState[g] = ge; - } - } - -} - -#ifdef REPORT_RESULT -void report_result(void *arg) -{ - int result = 0; - REPORT_RESULT(); -} -#endif - -int main() -{ - emscripten_set_keypress_callback(0, 0, 1, key_callback); - emscripten_set_keydown_callback(0, 0, 1, key_callback); - emscripten_set_keyup_callback(0, 0, 1, key_callback); - - emscripten_set_click_callback(0, 0, 1, mouse_callback); - emscripten_set_mousedown_callback(0, 0, 1, mouse_callback); - emscripten_set_mouseup_callback(0, 0, 1, mouse_callback); - emscripten_set_dblclick_callback(0, 0, 1, mouse_callback); - emscripten_set_mousemove_callback(0, 0, 1, mouse_callback); - - emscripten_set_wheel_callback(0, 0, 1, wheel_callback); - - emscripten_set_resize_callback(0, 0, 1, uievent_callback); - emscripten_set_scroll_callback(0, 0, 1, uievent_callback); - - emscripten_set_blur_callback(0, 0, 1, focusevent_callback); - emscripten_set_focus_callback(0, 0, 1, focusevent_callback); - emscripten_set_focusin_callback(0, 0, 1, focusevent_callback); - emscripten_set_focusout_callback(0, 0, 1, focusevent_callback); - - emscripten_set_deviceorientation_callback(0, 1, deviceorientation_callback); - emscripten_set_devicemotion_callback(0, 1, devicemotion_callback); - - emscripten_set_orientationchange_callback(0, 1, orientationchange_callback); - - // Test the polling of orientation. - emscripten_OrientationChangeEvent oce; - emscripten_get_orientation_status(&oce); - printf("The current orientation is:\n"); - orientationchange_callback(EMSCRIPTEN_EVENT_ORIENTATIONCHANGE, &oce, 0); - - int newOrientation = (oce.orientationIndex == EMSCRIPTEN_ORIENTATION_PORTRAIT_PRIMARY - || oce.orientationIndex == EMSCRIPTEN_ORIENTATION_PORTRAIT_SECONDARY) ? EMSCRIPTEN_ORIENTATION_LANDSCAPE_PRIMARY : EMSCRIPTEN_ORIENTATION_PORTRAIT_PRIMARY; - printf("Locking orientation to state %d..\n", newOrientation); - // Test locking of orientation. - int failed = emscripten_lock_orientation(newOrientation); - - if (failed) - printf("No support for orientation lock!\n"); - - emscripten_get_orientation_status(&oce); - printf("The current orientation is after locking:\n"); - orientationchange_callback(18, &oce, 0); - - printf("Unlocking orientation..\n"); - emscripten_unlock_orientation(); - - emscripten_FullscreenChangeEvent fsce; - emscripten_get_fullscreen_status(&fsce); - printf("The current fullscreen status is:\n"); - fullscreenchange_callback(EMSCRIPTEN_EVENT_FULLSCREENCHANGE, &fsce, 0); - - emscripten_set_fullscreenchange_callback(0, 1, fullscreenchange_callback); - - // These won't do anything, since fullscreen must be requested in an event handler, - // but call these anyways to confirm that they don't crash in an exception in the test suite. - failed = emscripten_request_fullscreen(0, 1); - if (failed != 0 && failed != 3) - printf("Fullscreen request failed! (%d)\n", failed); - emscripten_exit_fullscreen(); - - emscripten_PointerlockChangeEvent plce; - emscripten_get_pointerlock_status(&plce); - printf("The current pointerlock status is:\n"); - pointerlockchange_callback(EMSCRIPTEN_EVENT_POINTERLOCKCHANGE, &plce, 0); - - emscripten_set_pointerlockchange_callback(0, 1, pointerlockchange_callback); - - // These won't do anything, since pointer lock must be requested in an event handler, - // but call these anyways to confirm that they don't crash in an exception in the test suite. - failed = emscripten_request_pointerlock(0, 1); - if (failed != 0 && failed != 3) - printf("Pointer lock request failed! (%d)\n", failed); - emscripten_exit_pointerlock(); - - int vibratePattern[] = { - 150, 500, - 300, 500, - 450 - }; - emscripten_vibrate_pattern(vibratePattern, sizeof(vibratePattern)/sizeof(vibratePattern[0])); - - emscripten_VisibilityChangeEvent vce; - emscripten_get_visibility_status(&vce); - printf("Current visibility status:\n"); - visibilitychange_callback(EMSCRIPTEN_EVENT_VISIBILITYCHANGE, &vce, 0); - - emscripten_set_visibilitychange_callback(0, 1, visibilitychange_callback); - - emscripten_set_touchstart_callback(0, 0, 1, touch_callback); - emscripten_set_touchend_callback(0, 0, 1, touch_callback); - emscripten_set_touchmove_callback(0, 0, 1, touch_callback); - emscripten_set_touchcancel_callback(0, 0, 1, touch_callback); - - emscripten_set_gamepadconnected_callback(0, 1, gamepad_callback); - emscripten_set_gamepaddisconnected_callback(0, 1, gamepad_callback); - - emscripten_set_main_loop(mainloop, 10, 0); - - emscripten_set_beforeunload_callback(0, beforeunload_callback); - - emscripten_set_batterychargingchange_callback(0, battery_callback); - emscripten_set_batterylevelchange_callback(0, battery_callback); - - emscripten_BatteryEvent bs; - failed = emscripten_get_battery_status(&bs); - if (failed) { - printf("Browser can not provide Battery Status information!\n"); - } else { - printf("Current battery status:\n"); - battery_callback(EMSCRIPTEN_EVENT_BATTERYLEVELCHANGE, &bs, 0); - } - - emscripten_set_webglcontextlost_callback(0, 0, 1, webglcontext_callback); - emscripten_set_webglcontextrestored_callback(0, 0, 1, webglcontext_callback); - - /* For the events to function, one must either call emscripten_set_main_loop or enable Module.noExitRuntime by some other means. - Otherwise the application will exit after leaving main(), and the atexit handlers will clean up all event hooks (by design). */ - EM_ASM(Module['noExitRuntime'] = true); - -#ifdef REPORT_RESULT - // Keep the page running for a moment. - emscripten_async_call(report_result, 0, 5000); -#endif - return 0; -} diff --git a/tests/test_html5.c b/tests/test_html5.c new file mode 100644 index 0000000000000..77ddea98a6996 --- /dev/null +++ b/tests/test_html5.c @@ -0,0 +1,474 @@ +#include +#include +#include +#include + +static inline const char *emscripten_event_type_to_string(int eventType) { + const char *events[] = { "(invalid)", "(none)", "keypress", "keydown", "keyup", "click", "mousedown", "mouseup", "dblclick", "mousemove", "wheel", "resize", + "scroll", "blur", "focus", "focusin", "focusout", "deviceorientation", "devicemotion", "orientationchange", "fullscreenchange", "pointerlockchange", + "visibilitychange", "touchstart", "touchend", "touchmove", "touchcancel", "gamepadconnected", "gamepaddisconnected", "beforeunload", + "batterychargingchange", "batterylevelchange", "webglcontextlost", "webglcontextrestored", "(invalid)" }; + ++eventType; + if (eventType < 0) eventType = 0; + if (eventType >= sizeof(events)/sizeof(events[0])) eventType = sizeof(events)/sizeof(events[0])-1; + return events[eventType]; +} + +const char *emscripten_result_to_string(EMSCRIPTEN_RESULT result) { + if (result == EMSCRIPTEN_RESULT_SUCCESS) return "EMSCRIPTEN_RESULT_SUCCESS"; + if (result == EMSCRIPTEN_RESULT_DEFERRED) return "EMSCRIPTEN_RESULT_DEFERRED"; + if (result == EMSCRIPTEN_RESULT_NOT_SUPPORTED) return "EMSCRIPTEN_RESULT_NOT_SUPPORTED"; + if (result == EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED) return "EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED"; + if (result == EMSCRIPTEN_RESULT_INVALID_TARGET) return "EMSCRIPTEN_RESULT_INVALID_TARGET"; + if (result == EMSCRIPTEN_RESULT_UNKNOWN_TARGET) return "EMSCRIPTEN_RESULT_UNKNOWN_TARGET"; + if (result == EMSCRIPTEN_RESULT_INVALID_PARAM) return "EMSCRIPTEN_RESULT_INVALID_PARAM"; + if (result == EMSCRIPTEN_RESULT_FAILED) return "EMSCRIPTEN_RESULT_FAILED"; + if (result == EMSCRIPTEN_RESULT_NO_DATA) return "EMSCRIPTEN_RESULT_NO_DATA"; + return "Unknown EMSCRIPTEN_RESULT!"; +} + +#define TEST_RESULT(x) if (ret != EMSCRIPTEN_RESULT_SUCCESS) printf("%s returned %s.\n", #x, emscripten_result_to_string(ret)); + +// The event handler functions can return 1 to suppress the event and disable the default action. That calls event.preventDefault(); +// Returning 0 signals that the event was not consumed by the code, and will allow the event to pass on and bubble up normally. +EM_BOOL key_callback(int eventType, const EmscriptenKeyboardEvent *e, void *userData) +{ + printf("%s, key: \"%s\", code: \"%s\", location: %lu,%s%s%s%s repeat: %d, locale: \"%s\", char: \"%s\", charCode: %lu, keyCode: %lu, which: %lu\n", + emscripten_event_type_to_string(eventType), e->key, e->code, e->location, + e->ctrlKey ? " CTRL" : "", e->shiftKey ? " SHIFT" : "", e->altKey ? " ALT" : "", e->metaKey ? " META" : "", + e->repeat, e->locale, e->charValue, e->charCode, e->keyCode, e->which); + + if (eventType == EMSCRIPTEN_EVENT_KEYPRESS && (!strcmp(e->key, "f") || e->which == 102)) { + EmscriptenFullscreenChangeEvent fsce; + EMSCRIPTEN_RESULT ret = emscripten_get_fullscreen_status(&fsce); + TEST_RESULT(emscripten_get_fullscreen_status); + if (!fsce.isFullscreen) { + printf("Requesting fullscreen..\n"); + ret = emscripten_request_fullscreen(0, 1); + TEST_RESULT(emscripten_request_fullscreen); + } else { + printf("Exiting fullscreen..\n"); + ret = emscripten_exit_fullscreen(); + TEST_RESULT(emscripten_exit_fullscreen); + ret = emscripten_get_fullscreen_status(&fsce); + TEST_RESULT(emscripten_get_fullscreen_status); + if (fsce.isFullscreen) { + fprintf(stderr, "Fullscreen exit did not work!\n"); + } + } + } + + if (eventType == EMSCRIPTEN_EVENT_KEYPRESS && (!strcmp(e->key, "p") || e->which == 112)) { + EmscriptenPointerlockChangeEvent plce; + EMSCRIPTEN_RESULT ret = emscripten_get_pointerlock_status(&plce); + TEST_RESULT(emscripten_get_pointerlock_status); + if (!plce.isActive) { + printf("Requesting pointer lock..\n"); + ret = emscripten_request_pointerlock(0, 1); + TEST_RESULT(emscripten_request_pointerlock); + } else { + printf("Exiting pointer lock..\n"); + ret = emscripten_exit_pointerlock(); + TEST_RESULT(emscripten_exit_pointerlock); + ret = emscripten_get_pointerlock_status(&plce); + TEST_RESULT(emscripten_get_pointerlock_status); + if (plce.isActive) { + fprintf(stderr, "Pointer lock exit did not work!\n"); + } + } + } + + return 0; +} + +EM_BOOL mouse_callback(int eventType, const EmscriptenMouseEvent *e, void *userData) +{ + printf("%s, screen: (%ld,%ld), client: (%ld,%ld),%s%s%s%s button: %hu, buttons: %hu, movement: (%ld,%ld), canvas: (%ld,%ld)\n", + emscripten_event_type_to_string(eventType), e->screenX, e->screenY, e->clientX, e->clientY, + e->ctrlKey ? " CTRL" : "", e->shiftKey ? " SHIFT" : "", e->altKey ? " ALT" : "", e->metaKey ? " META" : "", + e->button, e->buttons, e->movementX, e->movementY, e->canvasX, e->canvasY); + + return 0; +} + +EM_BOOL wheel_callback(int eventType, const EmscriptenWheelEvent *e, void *userData) +{ + printf("%s, screen: (%ld,%ld), client: (%ld,%ld),%s%s%s%s button: %hu, buttons: %hu, canvas: (%ld,%ld), delta:(%g,%g,%g), deltaMode:%lu\n", + emscripten_event_type_to_string(eventType), e->mouse.screenX, e->mouse.screenY, e->mouse.clientX, e->mouse.clientY, + e->mouse.ctrlKey ? " CTRL" : "", e->mouse.shiftKey ? " SHIFT" : "", e->mouse.altKey ? " ALT" : "", e->mouse.metaKey ? " META" : "", + e->mouse.button, e->mouse.buttons, e->mouse.canvasX, e->mouse.canvasY, + (float)e->deltaX, (float)e->deltaY, (float)e->deltaZ, e->deltaMode); + + return 0; +} + +EM_BOOL uievent_callback(int eventType, const EmscriptenUiEvent *e, void *userData) +{ + printf("%s, detail: %ld, document.body.client size: (%d,%d), window.inner size: (%d,%d), scrollPos: (%d, %d)\n", + emscripten_event_type_to_string(eventType), e->detail, e->documentBodyClientWidth, e->documentBodyClientHeight, + e->windowInnerWidth, e->windowInnerHeight, e->scrollTop, e->scrollLeft); + + return 0; +} + +EM_BOOL focusevent_callback(int eventType, const EmscriptenFocusEvent *e, void *userData) +{ + printf("%s, nodeName: \"%s\", id: \"%s\"\n", emscripten_event_type_to_string(eventType), e->nodeName, e->id[0] == '\0' ? "(empty string)" : e->id); + + return 0; +} + +EM_BOOL deviceorientation_callback(int eventType, const EmscriptenDeviceOrientationEvent *e, void *userData) +{ + printf("%s, (%g, %g, %g)\n", emscripten_event_type_to_string(eventType), e->alpha, e->beta, e->gamma); + + return 0; +} + +EM_BOOL devicemotion_callback(int eventType, const EmscriptenDeviceMotionEvent *e, void *userData) +{ + printf("%s, accel: (%g, %g, %g), accelInclGravity: (%g, %g, %g), rotationRate: (%g, %g, %g)\n", + emscripten_event_type_to_string(eventType), + e->accelerationX, e->accelerationY, e->accelerationZ, + e->accelerationIncludingGravityX, e->accelerationIncludingGravityY, e->accelerationIncludingGravityZ, + e->rotationRateAlpha, e->rotationRateBeta, e->rotationRateGamma); + + return 0; +} + +EM_BOOL orientationchange_callback(int eventType, const EmscriptenOrientationChangeEvent *e, void *userData) +{ + printf("%s, orientationAngle: %d, orientationIndex: %d\n", emscripten_event_type_to_string(eventType), e->orientationAngle, e->orientationIndex); + + return 0; +} + +EM_BOOL fullscreenchange_callback(int eventType, const EmscriptenFullscreenChangeEvent *e, void *userData) +{ + printf("%s, isFullscreen: %d, fullscreenEnabled: %d, fs element nodeName: \"%s\", fs element id: \"%s\"\n", + emscripten_event_type_to_string(eventType), e->isFullscreen, e->fullscreenEnabled, e->nodeName, e->id); + + return 0; +} + +EM_BOOL pointerlockchange_callback(int eventType, const EmscriptenPointerlockChangeEvent *e, void *userData) +{ + printf("%s, isActive: %d, pointerlock element nodeName: \"%s\", id: \"%s\"\n", + emscripten_event_type_to_string(eventType), e->isActive, e->nodeName, e->id); + + return 0; +} + +EM_BOOL visibilitychange_callback(int eventType, const EmscriptenVisibilityChangeEvent *e, void *userData) +{ + printf("%s, hidden: %d, visibilityState: %d\n", emscripten_event_type_to_string(eventType), e->hidden, e->visibilityState); + + return 0; +} + +EM_BOOL touch_callback(int eventType, const EmscriptenTouchEvent *e, void *userData) +{ + printf("%s, numTouches: %d %s%s%s%s\n", + emscripten_event_type_to_string(eventType), e->numTouches, + e->ctrlKey ? " CTRL" : "", e->shiftKey ? " SHIFT" : "", e->altKey ? " ALT" : "", e->metaKey ? " META" : ""); + for(int i = 0; i < e->numTouches; ++i) + { + const EmscriptenTouchPoint *t = &e->touches[i]; + printf(" %ld: screen: (%ld,%ld), client: (%ld,%ld), page: (%ld,%ld), isChanged: %d, onTarget: %d, canvas: (%ld, %ld)\n", + t->identifier, t->screenX, t->screenY, t->clientX, t->clientY, t->pageX, t->pageY, t->isChanged, t->onTarget, t->canvasX, t->canvasY); + } + + return 0; +} + +EM_BOOL gamepad_callback(int eventType, const EmscriptenGamepadEvent *e, void *userData) +{ + printf("%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"\n", + eventType != 0 ? emscripten_event_type_to_string(eventType) : "Gamepad state", e->timestamp, e->connected, e->index, + e->numAxes, e->numButtons, e->id, e->mapping); + + if (e->connected) + { + for(int i = 0; i < e->numAxes; ++i) + printf("Axis %d: %g\n", i, e->axis[i]); + + for(int i = 0; i < e->numButtons; ++i) + printf("Button %d: Digital: %d, Analog: %g\n", i, e->digitalButton[i], e->analogButton[i]); + } + + return 0; +} + +const char *beforeunload_callback(int eventType, const void *reserved, void *userData) +{ +#ifdef REPORT_RESULT + return ""; // For test harness, don't show a confirmation dialog to not block and keep the test runner automated. +#else + return "Do you really want to leave the page?"; +#endif +} + +void formatTime(char *str, int seconds) +{ + int h = seconds / (60*60); + seconds -= h*60*60; + int m = seconds / 60; + seconds -= m*60; + if (h > 0) + { + sprintf(str, "%dh:%02dm:%02ds", h, m, seconds); + } + else + { + sprintf(str, "%02dm:%02ds", m, seconds); + } +} + +EM_BOOL battery_callback(int eventType, const EmscriptenBatteryEvent *e, void *userData) +{ + char t1[64]; + formatTime(t1, (int)e->chargingTime); + char t2[64]; + formatTime(t2, (int)e->dischargingTime); + printf("%s: chargingTime: %s, dischargingTime: %s, level: %g%%, charging: %d\n", + emscripten_event_type_to_string(eventType), t1, t2, e->level*100, e->charging); + + return 0; +} + +EM_BOOL webglcontext_callback(int eventType, const void *reserved, void *userData) +{ + printf("%s.\n", emscripten_event_type_to_string(eventType)); + + return 0; +} + +EmscriptenGamepadEvent prevState[32]; +int prevNumGamepads = 0; + +void mainloop() +{ + int numGamepads = emscripten_get_num_gamepads(); + if (numGamepads != prevNumGamepads) + { + if (numGamepads == EMSCRIPTEN_RESULT_NOT_SUPPORTED) { + printf("emscripten_get_num_gamepads returned EMSCRIPTEN_RESULT_NOT_SUPPORTED.\n"); + emscripten_cancel_main_loop(); + return; + } else { + printf("Number of connected gamepads: %d\n", numGamepads); + } + prevNumGamepads = numGamepads; + } + + for(int i = 0; i < numGamepads && i < 32; ++i) + { + EmscriptenGamepadEvent ge; + int failed = emscripten_get_gamepad_status(i, &ge); + if (!failed) + { + int g = ge.index; + for(int j = 0; j < ge.numAxes; ++j) + { + if (ge.axis[j] != prevState[g].axis[j]) + printf("Gamepad %d, axis %d: %g\n", g, j, ge.axis[j]); + } + + for(int j = 0; j < ge.numButtons; ++j) + { + if (ge.analogButton[j] != prevState[g].analogButton[j] || ge.digitalButton[j] != prevState[g].digitalButton[j]) + printf("Gamepad %d, button %d: Digital: %d, Analog: %g\n", g, j, ge.digitalButton[j], ge.analogButton[j]); + } + prevState[g] = ge; + } + } + +} + +#ifdef REPORT_RESULT +void report_result(void *arg) +{ + int result = 0; + REPORT_RESULT(); +} +#endif + +int main() +{ + + EMSCRIPTEN_RESULT ret = emscripten_set_keypress_callback(0, 0, 1, key_callback); + TEST_RESULT(emscripten_set_keypress_callback); + ret = emscripten_set_keydown_callback(0, 0, 1, key_callback); + TEST_RESULT(emscripten_set_keydown_callback); + ret = emscripten_set_keyup_callback(0, 0, 1, key_callback); + TEST_RESULT(emscripten_set_keyup_callback); + + ret = emscripten_set_click_callback(0, 0, 1, mouse_callback); + TEST_RESULT(emscripten_set_click_callback); + ret = emscripten_set_mousedown_callback(0, 0, 1, mouse_callback); + TEST_RESULT(emscripten_set_mousedown_callback); + ret = emscripten_set_mouseup_callback(0, 0, 1, mouse_callback); + TEST_RESULT(emscripten_set_mouseup_callback); + ret = emscripten_set_dblclick_callback(0, 0, 1, mouse_callback); + TEST_RESULT(emscripten_set_dblclick_callback); + ret = emscripten_set_mousemove_callback(0, 0, 1, mouse_callback); + TEST_RESULT(emscripten_set_mousemove_callback); + + ret = emscripten_set_wheel_callback(0, 0, 1, wheel_callback); + TEST_RESULT(emscripten_set_wheel_callback); + + ret = emscripten_set_resize_callback(0, 0, 1, uievent_callback); + TEST_RESULT(emscripten_set_resize_callback); + ret = emscripten_set_scroll_callback(0, 0, 1, uievent_callback); + TEST_RESULT(emscripten_set_scroll_callback); + + ret = emscripten_set_blur_callback(0, 0, 1, focusevent_callback); + TEST_RESULT(emscripten_set_blur_callback); + ret = emscripten_set_focus_callback(0, 0, 1, focusevent_callback); + TEST_RESULT(emscripten_set_focus_callback); + ret = emscripten_set_focusin_callback(0, 0, 1, focusevent_callback); + TEST_RESULT(emscripten_set_focusin_callback); + ret = emscripten_set_focusout_callback(0, 0, 1, focusevent_callback); + TEST_RESULT(emscripten_set_focusout_callback); + + ret = emscripten_set_deviceorientation_callback(0, 1, deviceorientation_callback); + TEST_RESULT(emscripten_set_deviceorientation_callback); + ret = emscripten_set_devicemotion_callback(0, 1, devicemotion_callback); + TEST_RESULT(emscripten_set_devicemotion_callback); + + ret = emscripten_set_orientationchange_callback(0, 1, orientationchange_callback); + TEST_RESULT(emscripten_set_orientationchange_callback); + + // Test the polling of orientation. + EmscriptenOrientationChangeEvent oce; + ret = emscripten_get_orientation_status(&oce); + TEST_RESULT(emscripten_get_orientation_status); + if (ret == EMSCRIPTEN_RESULT_SUCCESS) { + printf("The current orientation is:\n"); + orientationchange_callback(EMSCRIPTEN_EVENT_ORIENTATIONCHANGE, &oce, 0); + } + + int newOrientation = (oce.orientationIndex == EMSCRIPTEN_ORIENTATION_PORTRAIT_PRIMARY + || oce.orientationIndex == EMSCRIPTEN_ORIENTATION_PORTRAIT_SECONDARY) ? EMSCRIPTEN_ORIENTATION_LANDSCAPE_PRIMARY : EMSCRIPTEN_ORIENTATION_PORTRAIT_PRIMARY; + // Test locking of orientation. + ret = emscripten_lock_orientation(newOrientation); + TEST_RESULT(emscripten_lock_orientation); + if (ret == EMSCRIPTEN_RESULT_SUCCESS) { + printf("Locked orientation to state %d.\n", newOrientation); + } + + ret = emscripten_get_orientation_status(&oce); + TEST_RESULT(emscripten_get_orientation_status); + if (ret == EMSCRIPTEN_RESULT_SUCCESS) { + printf("The current orientation is after locking:\n"); + orientationchange_callback(18, &oce, 0); + } + + ret = emscripten_unlock_orientation(); + TEST_RESULT(emscripten_unlock_orientation); + if (ret == EMSCRIPTEN_RESULT_SUCCESS) { + printf("Unlocked orientation.\n"); + } + + EmscriptenFullscreenChangeEvent fsce; + ret = emscripten_get_fullscreen_status(&fsce); + TEST_RESULT(emscripten_get_fullscreen_status); + if (ret == EMSCRIPTEN_RESULT_SUCCESS) { + printf("The current fullscreen status is:\n"); + fullscreenchange_callback(EMSCRIPTEN_EVENT_FULLSCREENCHANGE, &fsce, 0); + } + + ret = emscripten_set_fullscreenchange_callback(0, 0, 1, fullscreenchange_callback); + TEST_RESULT(emscripten_set_fullscreenchange_callback); + + // These won't do anything, since fullscreen must be requested in an event handler, + // but call these anyways to confirm that they don't crash in an exception in the test suite. + ret = emscripten_request_fullscreen(0, 1); + TEST_RESULT(emscripten_request_fullscreen); + ret = emscripten_exit_fullscreen(); + TEST_RESULT(emscripten_exit_fullscreen); + + EmscriptenPointerlockChangeEvent plce; + ret = emscripten_get_pointerlock_status(&plce); + TEST_RESULT(emscripten_get_pointerlock_status); + if (ret == EMSCRIPTEN_RESULT_SUCCESS) { + printf("The current pointerlock status is:\n"); + pointerlockchange_callback(EMSCRIPTEN_EVENT_POINTERLOCKCHANGE, &plce, 0); + } + + ret = emscripten_set_pointerlockchange_callback(0, 0, 1, pointerlockchange_callback); + TEST_RESULT(emscripten_set_pointerlockchange_callback); + + // These won't do anything, since pointer lock must be requested in an event handler, + // but call these anyways to confirm that they don't crash in an exception in the test suite. + ret = emscripten_request_pointerlock(0, 1); + TEST_RESULT(emscripten_request_pointerlock); + ret = emscripten_exit_pointerlock(); + TEST_RESULT(emscripten_exit_pointerlock); + + int vibratePattern[] = { + 150, 500, + 300, 500, + 450 + }; + ret = emscripten_vibrate_pattern(vibratePattern, sizeof(vibratePattern)/sizeof(vibratePattern[0])); + TEST_RESULT(emscripten_vibrate_pattern); + + EmscriptenVisibilityChangeEvent vce; + ret = emscripten_get_visibility_status(&vce); + TEST_RESULT(emscripten_get_visibility_status); + if (ret == EMSCRIPTEN_RESULT_SUCCESS) { + printf("Current visibility status:\n"); + visibilitychange_callback(EMSCRIPTEN_EVENT_VISIBILITYCHANGE, &vce, 0); + } + + ret = emscripten_set_visibilitychange_callback(0, 1, visibilitychange_callback); + TEST_RESULT(emscripten_set_visibilitychange_callback); + + ret = emscripten_set_touchstart_callback(0, 0, 1, touch_callback); + TEST_RESULT(emscripten_set_touchstart_callback); + ret = emscripten_set_touchend_callback(0, 0, 1, touch_callback); + TEST_RESULT(emscripten_set_touchend_callback); + ret = emscripten_set_touchmove_callback(0, 0, 1, touch_callback); + TEST_RESULT(emscripten_set_touchmove_callback); + ret = emscripten_set_touchcancel_callback(0, 0, 1, touch_callback); + TEST_RESULT(emscripten_set_touchcancel_callback); + + ret = emscripten_set_gamepadconnected_callback(0, 1, gamepad_callback); + TEST_RESULT(emscripten_set_gamepadconnected_callback); + ret = emscripten_set_gamepaddisconnected_callback(0, 1, gamepad_callback); + TEST_RESULT(emscripten_set_gamepaddisconnected_callback); + + emscripten_set_main_loop(mainloop, 10, 0); + + ret = emscripten_set_beforeunload_callback(0, beforeunload_callback); + TEST_RESULT(emscripten_set_beforeunload_callback); + + ret = emscripten_set_batterychargingchange_callback(0, battery_callback); + TEST_RESULT(emscripten_set_batterychargingchange_callback); + ret = emscripten_set_batterylevelchange_callback(0, battery_callback); + TEST_RESULT(emscripten_set_batterylevelchange_callback); + + EmscriptenBatteryEvent bs; + ret = emscripten_get_battery_status(&bs); + TEST_RESULT(emscripten_get_battery_status); + if (ret == EMSCRIPTEN_RESULT_SUCCESS) { + printf("Current battery status:\n"); + battery_callback(EMSCRIPTEN_EVENT_BATTERYLEVELCHANGE, &bs, 0); + } + + ret = emscripten_set_webglcontextlost_callback(0, 0, 1, webglcontext_callback); + TEST_RESULT(emscripten_set_webglcontextlost_callback); + ret = emscripten_set_webglcontextrestored_callback(0, 0, 1, webglcontext_callback); + TEST_RESULT(emscripten_set_webglcontextrestored_callback); + + /* For the events to function, one must either call emscripten_set_main_loop or enable Module.noExitRuntime by some other means. + Otherwise the application will exit after leaving main(), and the atexit handlers will clean up all event hooks (by design). */ + EM_ASM(Module['noExitRuntime'] = true); + +#ifdef REPORT_RESULT + // Keep the page running for a moment. + emscripten_async_call(report_result, 0, 5000); +#endif + return 0; +} From 95bde35523b77f87c43aed877259607a77814d6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Fri, 24 Jan 2014 19:16:10 +0200 Subject: [PATCH 85/85] Add test for storing special float literals in source code. Currently fails in fastcomp. --- tests/test_core.py | 3 ++ tests/test_float_literals.cpp | 92 +++++++++++++++++++++++++++++++++++ tests/test_float_literals.out | 22 +++++++++ 3 files changed, 117 insertions(+) create mode 100644 tests/test_float_literals.cpp create mode 100644 tests/test_float_literals.out diff --git a/tests/test_core.py b/tests/test_core.py index d25847d74ee67..99c69459c9563 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -6267,6 +6267,9 @@ def test_linespecific(self): self.do_run(src.replace('TYPE', 'unsigned int'), '*2147483645**2**-5**5*') Settings.CORRECT_SIGNS = 0 + def test_float_literals(self): + self.do_run_from_file(path_from_root('tests', 'test_float_literals.cpp'), path_from_root('tests', 'test_float_literals.out')) + def test_exit_status(self): if self.emcc_args is None: return self.skip('need emcc') src = r''' diff --git a/tests/test_float_literals.cpp b/tests/test_float_literals.cpp new file mode 100644 index 0000000000000..fdae2764056a7 --- /dev/null +++ b/tests/test_float_literals.cpp @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) || defined(EMSCRIPTEN) +#define FLOAT_NAN ((float)std::numeric_limits::quiet_NaN()) +#define FLOAT_INF ((float)std::numeric_limits::infinity()) +#else +#define FLOAT_NAN ((float)NAN) +#define FLOAT_INF ((float)INFINITY) +#endif + +#if defined(_MSC_VER) || defined(EMSCRIPTEN) +#define DOUBLE_NAN ((double)std::numeric_limits::quiet_NaN()) +#define DOUBLE_INF ((double)std::numeric_limits::infinity()) +#else +#define DOUBLE_NAN ((double)NAN) +#define DOUBLE_INF ((double)INFINITY) +#endif + +#ifdef _MSC_VER +#define NOINLINE +#else +#define NOINLINE __attribute__((noinline)) +#endif + +float NOINLINE ret_e() { return (float)2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274; } +float NOINLINE ret_minuspi() { return (float)-3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679; } +float NOINLINE val() { return 10.f; } +float NOINLINE val2() { return -10.f; } +float NOINLINE zero() { return 0.f; } +float NOINLINE zero2() { return -0.f; } + +double NOINLINE dret_e() { return (double)2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274; } +double NOINLINE dret_minuspi() { return (double)-3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679; } +double NOINLINE dval() { return 10.0; } +double NOINLINE dval2() { return -10.0; } +double NOINLINE dzero() { return 0.0; } +double NOINLINE dzero2() { return -0.0; } + +const float e = ret_e(); +const float negpi = ret_minuspi(); +const float inf = FLOAT_INF; +const float negInf = -FLOAT_INF; +const float floatNan = FLOAT_NAN; +const float floatMax = FLT_MAX; +const float floatMin = -FLT_MAX; +const float posValue = val(); +const float negValue = val2(); +const float posZero = zero(); +const float negZero = zero2(); + +const double de = dret_e(); +const double dnegpi = dret_minuspi(); +const double dinf = DOUBLE_INF; +const double dnegInf = -DOUBLE_INF; +const double doubleNan = DOUBLE_NAN; +const double doubleMax = DBL_MAX; +const double doubleMin = -DBL_MAX; +const double dposValue = dval(); +const double dnegValue = dval2(); +const double dposZero = dzero(); +const double dnegZero = dzero2(); + +int main() +{ + printf("e: %f\n", e); + printf("negpi: %f\n", negpi); + printf("inf: %f\n", inf); + printf("negInf: %f\n", negInf); + printf("floatNan: %f\n", floatNan); + printf("floatMax: %f\n", floatMax); + printf("floatMin: %f\n", floatMin); + printf("posValue: %f\n", posValue); + printf("negValue: %f\n", negValue); + printf("posZero: %f\n", posZero); + printf("negZero: %f\n", negZero); + + printf("e: %f\n", de); + printf("negpi: %f\n", dnegpi); + printf("inf: %f\n", dinf); + printf("negInf: %f\n", dnegInf); + printf("doubleNan: %f\n", doubleNan); + printf("doubleMax: %f\n", doubleMax); + printf("doubleMin: %f\n", doubleMin); + printf("posValue: %f\n", dposValue); + printf("negValue: %f\n", dnegValue); + printf("posZero: %f\n", dposZero); + printf("negZero: %f\n", dnegZero); +} diff --git a/tests/test_float_literals.out b/tests/test_float_literals.out new file mode 100644 index 0000000000000..ab52d6c49d54e --- /dev/null +++ b/tests/test_float_literals.out @@ -0,0 +1,22 @@ +e: 2.718282 +negpi: -3.141593 +inf: inf +negInf: -inf +floatNan: nan +floatMax: 3.4028234663852886e+38 +floatMin: -3.4028234663852886e+38 +posValue: 10.000000 +negValue: -10.000000 +posZero: 0.000000 +negZero: -0.000000 +e: 2.718282 +negpi: -3.141593 +inf: inf +negInf: -inf +doubleNan: nan +doubleMax: 1.7976931348623157e+308 +doubleMin: -1.7976931348623157e+308 +posValue: 10.000000 +negValue: -10.000000 +posZero: 0.000000 +negZero: -0.000000