diff --git a/AUTHORS b/AUTHORS index 08acc3e909cf9..860d6082ed914 100644 --- a/AUTHORS +++ b/AUTHORS @@ -212,4 +212,6 @@ a license to everyone to use it as detailed in LICENSE.) * Yeonjun Lim * Andrew Karpushin * Felix Zimmermann +* Sven-Hendrik Haase +* Simon Sandström diff --git a/emscripten-version.txt b/emscripten-version.txt index 6b899e68da870..0b6fa2854d627 100644 --- a/emscripten-version.txt +++ b/emscripten-version.txt @@ -1,2 +1,2 @@ -"1.35.7" +"1.35.8" diff --git a/site/source/docs/api_reference/preamble.js.rst b/site/source/docs/api_reference/preamble.js.rst index fe1d58fd8a43a..62fb46f2f4b9d 100644 --- a/site/source/docs/api_reference/preamble.js.rst +++ b/site/source/docs/api_reference/preamble.js.rst @@ -100,7 +100,7 @@ Calling compiled C functions from JavaScript my_func(12) :param ident: The name of the C function to be called. - :param returnType: The return type of the function. This will be one of the JavaScript types ``number``, ``string`` or ``array`` (use ``number`` for any C pointer, and ``array`` for JavaScript arrays and typed arrays; note that arrays are 8-bit). + :param returnType: The return type of the function. This can be ``"number"``, ``"string"`` or ``"array"``, which correspond to the appropriate JavaScript types (use ``"number"`` for any C pointer, and ``"array"`` for JavaScript arrays and typed arrays; note that arrays are 8-bit), or for a void function it can be ``null`` (note: the JavaScript ``null`` value, not a string containing the word "null"). :param argTypes: An array of the types of arguments for the function (if there are no arguments, this can be omitted). Types are as in ``returnType``, except that ``array`` is not supported as there is no way for us to know the length of the array). :returns: A JavaScript function that can be used for running the C function. diff --git a/site/source/docs/compiling/Building-Projects.rst b/site/source/docs/compiling/Building-Projects.rst index e04653102e932..437856e2ae0ee 100644 --- a/site/source/docs/compiling/Building-Projects.rst +++ b/site/source/docs/compiling/Building-Projects.rst @@ -174,7 +174,7 @@ Emscripten Ports is a collection of useful libraries, ported to Emscripten. They You should see some notifications about SDL2 being used, and built if it wasn't previously. You can then view ``sdl2.html`` in your browser. -.. note:: *SDL_image* has also been added to ports, use it with ``-s USE_SDL_IMAGE=2``. To see a list of all available ports, run ``emcc --show-ports``. +.. note:: *SDL_image* has also been added to ports, use it with ``-s USE_SDL_IMAGE=2``. To see a list of all available ports, run ``emcc --show-ports``. For SDL2_image to be useful, you generally need to specify the image formats you are planning on using with -s SDL2_IMAGE_FORMATS='["png"]'. This will also ensure that ``IMG_Init`` works properly. Alternatively, you can use specify ``emcc --use-preload-plugins`` (and ``--preload-file`` your images, so the browser codecs decode them), but then your calls to ``IMG_Init`` will fail. .. note:: Emscripten also has support for older SDL1, which is built-in. If you do not specify SDL2 as in the command above, then SDL1 is linked in and the SDL1 include paths are used. SDL1 has support for *sdl-config*, which is present in `system/bin `_. Using the native *sdl-config* may result in compilation or missing-symbol errors. You will need to modify the build system to look for files in **emscripten/system** or **emscripten/system/bin** in order to use the Emscripten *sdl-config*. diff --git a/src/compiler.js b/src/compiler.js index dce9e709d53d3..159930f5e72ed 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -152,7 +152,6 @@ if (settings_file) { EXPORTED_FUNCTIONS = set(EXPORTED_FUNCTIONS); -EXPORTED_GLOBALS = set(EXPORTED_GLOBALS); EXCEPTION_CATCHING_WHITELIST = set(EXCEPTION_CATCHING_WHITELIST); // TODO: Implement support for proper preprocessing, e.g. "#if A || B" and "#if defined(A) || defined(B)" to diff --git a/src/library.js b/src/library.js index 22880de057a60..1c48ab200b4b2 100644 --- a/src/library.js +++ b/src/library.js @@ -1480,6 +1480,20 @@ LibraryManager.library = { llvm_exp_f32: 'Math_exp', llvm_exp_f64: 'Math_exp', + round__asm: true, + round__sig: 'dd', + round: function(d) { + d = +d; + return d >= +0 ? +Math_floor(d + +0.5) : +Math_ceil(d - +0.5); + }, + + roundf__asm: true, + roundf__sig: 'dd', + roundf: function(f) { + f = +f; + return f >= +0 ? +Math_floor(f + +0.5) : +Math_ceil(f - +0.5); // TODO: use fround? + }, + _reallyNegative: function(x) { return x < 0 || (x === 0 && (1/x) === -Infinity); }, diff --git a/src/library_fs.js b/src/library_fs.js index 3899a4faee7b1..39e79daa6bac1 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -350,8 +350,8 @@ mergeInto(LibraryManager.library, { if (FS.isLink(node.mode)) { return ERRNO_CODES.ELOOP; } else if (FS.isDir(node.mode)) { - if ((flags & {{{ cDefine('O_ACCMODE') }}}) !== {{{ cDefine('O_RDONLY')}}} || // opening for write - (flags & {{{ cDefine('O_TRUNC') }}})) { + if (FS.flagsToPermissionString(flags) !== 'r' || // opening for write + (flags & {{{ cDefine('O_TRUNC') }}})) { // TODO: check for O_SEARCH? (== search for dir only) return ERRNO_CODES.EISDIR; } } diff --git a/src/library_glfw.js b/src/library_glfw.js index afa9f1029fbde..bc9088bcfa661 100644 --- a/src/library_glfw.js +++ b/src/library_glfw.js @@ -513,6 +513,58 @@ var LibraryGLFW = { win.windowRefreshFunc = cbfun; }, + onClickRequestPointerLock: function(e) { + if (!Browser.pointerLock && Module['canvas'].requestPointerLock) { + Module['canvas'].requestPointerLock(); + e.preventDefault(); + } + }, + + setInputMode: function(winid, mode, value) { + var win = GLFW.WindowFromId(winid); + if (!win) return; + + switch(mode) { + case 0x00033001: { // GLFW_CURSOR + switch(value) { + case 0x00034001: { // GLFW_CURSOR_NORMAL + win.inputModes[mode] = value; + Module['canvas'].removeEventListener('click', GLFW.onClickRequestPointerLock, true); + Module['canvas'].exitPointerLock(); + break; + } + case 0x00034002: { // GLFW_CURSOR_HIDDEN + console.log("glfwSetInputMode called with GLFW_CURSOR_HIDDEN value not implemented."); + break; + } + case 0x00034003: { // GLFW_CURSOR_DISABLED + win.inputModes[mode] = value; + Module['canvas'].addEventListener('click', GLFW.onClickRequestPointerLock, true); + Module['canvas'].requestPointerLock(); + break; + } + default: { + console.log("glfwSetInputMode called with unknown value parameter value: " + value + "."); + break; + } + } + break; + } + case 0x00033002: { // GLFW_STICKY_KEYS + console.log("glfwSetInputMode called with GLFW_STICKY_KEYS mode not implemented."); + break; + } + case 0x00033003: { // GLFW_STICKY_MOUSE_BUTTONS + console.log("glfwSetInputMode called with GLFW_STICKY_MOUSE_BUTTONS mode not implemented."); + break; + } + default: { + console.log("glfwSetInputMode called with unknown mode parameter value: " + mode + "."); + break; + } + } + }, + getKey: function(winid, key) { var win = GLFW.WindowFromId(winid); if (!win) return 0; @@ -588,14 +640,14 @@ var LibraryGLFW = { } } - if (!win.windowResizeFunc) return; + if (!win.windowSizeFunc) return; #if USE_GLFW == 2 - Runtime.dynCall('vii', win.windowResizeFunc, [width, height]); + Runtime.dynCall('vii', win.windowSizeFunc, [width, height]); #endif #if USE_GLFW == 3 - Runtime.dynCall('viii', win.windowResizeFunc, [win.id, width, height]); + Runtime.dynCall('viii', win.windowSizeFunc, [win.id, width, height]); #endif }, @@ -1004,11 +1056,9 @@ var LibraryGLFW = { return win.inputModes[mode]; }, - // TODO: implement - // GLFW_STICKY_KEYS == key stays pressed until pressed second time - // GLFW_STICKY_MOUSE_BUTTONS == same as above but for mouse buttons - // GLFW_CURSOR == NORMAL, HIDDEN (no pointer), DISABLED (hidden && always centered) - glfwSetInputMode: function(winid, mode, value) {}, + glfwSetInputMode: function(winid, mode, value) { + GLFW.setInputMode(winid, mode, value); + }, glfwGetKey: function(winid, key) { return GLFW.getKey(winid, key); diff --git a/src/postamble.js b/src/postamble.js index 1058eed687ef2..029014a7e7fac 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -1,6 +1,8 @@ // === Auto-generated postamble setup entry stuff === +{{{ maybeExport('FS') }}} + #if MEM_INIT_METHOD == 2 #if USE_PTHREADS if (memoryInitializer && !ENVIRONMENT_IS_PTHREAD) (function(s) { diff --git a/src/settings.js b/src/settings.js index ed2e781830e7c..093e6a9e8e37f 100644 --- a/src/settings.js +++ b/src/settings.js @@ -415,10 +415,6 @@ var LIBRARY_DEPS_TO_AUTOEXPORT = ['memcpy']; // This list is also used to determ // so we must export so that if they are implemented in C // they will be accessible, in ASM_JS mode). -var EXPORTED_GLOBALS = []; // Global non-function variables that are explicitly - // exported, so they are guaranteed to be - // accessible outside of the generated code. - var INCLUDE_FULL_LIBRARY = 0; // Include all JS library functions instead of the sum of // DEFAULT_LIBRARY_FUNCS_TO_INCLUDE + any functions used // by the generated code. This is needed when dynamically diff --git a/tests/core/test_rounding.in b/tests/core/test_rounding.in index f8ddc335cd99f..4b5e347ac9939 100644 --- a/tests/core/test_rounding.in +++ b/tests/core/test_rounding.in @@ -34,5 +34,15 @@ int main() { fractpart = modf (param , &intpart); printf ("%f = %f + %f \n", param, intpart, fractpart); + printf("%.1f ", roundf(1.4)); + printf("%.1f ", roundf(1.6)); + printf("%.1f ", roundf(-1.4)); + printf("%.1f ", roundf(-1.6)); + + printf("%.1f ", roundf(1.5)); + printf("%.1f ", roundf(2.5)); + printf("%.1f ", roundf(-1.5)); + printf("%.1f ", roundf(-2.5)); + return 0; } diff --git a/tests/core/test_rounding.out b/tests/core/test_rounding.out index e964813ab8010..c8e38ceabd3b9 100644 --- a/tests/core/test_rounding.out +++ b/tests/core/test_rounding.out @@ -1,3 +1,4 @@ 1.0 2.0 -1.0 -2.0 2.0 3.0 -2.0 -3.0 1 2 -1 -2 2 2 -2 -2 3.141593 = 3.000000 + 0.141593 -3.141593 = -3.000000 + -0.141593 +1.0 2.0 -1.0 -2.0 2.0 3.0 -2.0 -3.0 diff --git a/tests/test_core.py b/tests/test_core.py index 5789870db33ce..8e5d6048930c4 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -837,12 +837,14 @@ def test_frexp(self): self.do_run_from_file(src, output) def test_rounding(self): - Settings.PRECISE_F32 = 1 # in the move to llvm 3.7, froundf in musl became more sensitive to float/double differences + for precise_f32 in [0, 1]: + print precise_f32 + Settings.PRECISE_F32 = precise_f32 - test_path = path_from_root('tests', 'core', 'test_rounding') - src, output = (test_path + s for s in ('.in', '.out')) + test_path = path_from_root('tests', 'core', 'test_rounding') + src, output = (test_path + s for s in ('.in', '.out')) - self.do_run_from_file(src, output) + self.do_run_from_file(src, output) def test_fcvt(self): test_path = path_from_root('tests', 'core', 'test_fcvt') diff --git a/tests/test_glfw_pointerlock.c b/tests/test_glfw_pointerlock.c new file mode 100644 index 0000000000000..1c6298d947af2 --- /dev/null +++ b/tests/test_glfw_pointerlock.c @@ -0,0 +1,56 @@ +#include +#include +#include +#define GLFW_INCLUDE_ES2 +#include + +int result = 1; + +GLFWwindow* g_window; + +void render(); +void error_callback(int error, const char* description); + +void render() { + glClearColor(1.0f, 1.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); +} + +int main() { + // Setup g_window + glfwSetErrorCallback(error_callback); + if (!glfwInit()) + { + result = 0; + printf("Could not create window. Test failed.\n"); +#ifdef REPORT_RESULT + REPORT_RESULT(); +#endif + return -1; + } + glfwWindowHint(GLFW_RESIZABLE , 1); + g_window = glfwCreateWindow(600, 450, "GLFW pointerlock test", NULL, NULL); + if (!g_window) + { + result = 0; + printf("Could not create window. Test failed.\n"); +#ifdef REPORT_RESULT + REPORT_RESULT(); +#endif + glfwTerminate(); + return -1; + } + glfwMakeContextCurrent(g_window); + + // Disable cursor: pointer lock + glfwSetInputMode(g_window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + // Main loop + printf("Click the canvas to enter pointer lock mode. The browser might offer to allow hidding mouse pointer. Make sure to accept it.\n"); + printf("Escaping the pointer lock mode should work again upon clicking the canvas.\n"); + emscripten_set_main_loop(render, 0, 1); + + glfwTerminate(); + + return 0; +} diff --git a/tests/test_interactive.py b/tests/test_interactive.py index 188a51c49ae4e..502ff05136911 100644 --- a/tests/test_interactive.py +++ b/tests/test_interactive.py @@ -123,5 +123,8 @@ def test_vr(self): def test_glfw_fullscreen(self): self.btest('test_glfw_fullscreen.c', expected='1', args=['-s', 'NO_EXIT_RUNTIME=1', '-s', 'USE_GLFW=3']) + def test_glfw_pointerlock(self): + self.btest('test_glfw_pointerlock.c', expected='1', args=['-s', 'NO_EXIT_RUNTIME=1', '-s', 'USE_GLFW=3']) + def test_cpuprofiler_memoryprofiler(self): self.btest('hello_world_gles.c', expected='0', args=['-DLONGTEST=1', '-DTEST_MEMORYPROFILER_ALLOCATIONS_MAP=1', '-O2', '--cpuprofiler', '--memoryprofiler']) diff --git a/tests/test_other.py b/tests/test_other.py index 12edc50f7d5fb..4681cfdd035e0 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -5321,6 +5321,54 @@ def test_realpath(self): Popen([PYTHON, EMCC, 'src.c', '--embed-file', 'boot']).communicate() self.assertContained('Resolved: /boot/README.txt', run_js('a.out.js')) + def test_realpath_2(self): + open('src.c', 'w').write(r''' +#include +#include +#include + +int testrealpath(const char* path) { + errno = 0; + char *t_realpath_buf = realpath(path, NULL); + if (NULL == t_realpath_buf) { + printf("Resolve failed: \"%s\"\n",path);fflush(stdout); + return 1; + } else { + printf("Resolved: \"%s\" => \"%s\"\n", path, t_realpath_buf);fflush(stdout); + free(t_realpath_buf); + return 0; + } +} + +int main(int argc, char **argv) +{ + // files: + testrealpath("testfile.txt"); + testrealpath("Folder/testfile.txt"); + testrealpath("testnonexistentfile.txt"); + // folders + testrealpath("Folder"); + testrealpath("/Folder"); + testrealpath("./"); + testrealpath(""); + testrealpath("/"); + return 0; +} +''') + open('testfile.txt', 'w').write('') + if not os.path.exists('Folder'): os.mkdir('Folder') + open(os.path.join('Folder', 'testfile.txt'), 'w').write('') + check_execute([PYTHON, EMCC, 'src.c', '--embed-file', 'testfile.txt', '--embed-file', 'Folder']) + self.assertContained('''Resolved: "testfile.txt" => "/testfile.txt" +Resolved: "Folder/testfile.txt" => "/Folder/testfile.txt" +Resolve failed: "testnonexistentfile.txt" +Resolved: "Folder" => "/Folder" +Resolved: "/Folder" => "/Folder" +Resolved: "./" => "/" +Resolve failed: "" +Resolved: "/" => "/" +''', run_js('a.out.js')) + def test_no_warnings(self): out, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_libcxx.cpp')], stderr=PIPE).communicate() assert err == '', err diff --git a/tools/emterpretify.py b/tools/emterpretify.py index d14cc4209d7b0..524e846976dc3 100755 --- a/tools/emterpretify.py +++ b/tools/emterpretify.py @@ -635,8 +635,8 @@ def process(code): lx = (inst >> 8) & 255; ly = (inst >> 16) & 255; lz = inst >>> 24; - //Module.print([pc, inst&255, %s[inst&255], lx, ly, lz, HEAPU8[pc + 4],HEAPU8[pc + 5],HEAPU8[pc + 6],HEAPU8[pc + 7]].join(', ')); -''' % (json.dumps(OPCODES)) + //Module.print([pc, inst&255, ''' + json.dumps(OPCODES) + '''[inst&255], lx, ly, lz, HEAPU8[pc + 4],HEAPU8[pc + 5],HEAPU8[pc + 6],HEAPU8[pc + 7]]); +''' if not INNERTERPRETER_LAST_OPCODE: main_loop = main_loop_prefix + r''' diff --git a/tools/shared.py b/tools/shared.py index 43f7fb32c9b06..e99eedea14ec0 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -528,6 +528,13 @@ def check_sanity(force=False): logging.critical('Cannot find %s, check the paths in %s' % (cmd, EM_CONFIG)) sys.exit(1) + if not os.path.exists(PYTHON) and not os.path.exists(cmd + '.exe'): + try: + subprocess.check_call([PYTHON, '--version'], stdout=PIPE, stderr=PIPE) + except: + logging.critical('Cannot find %s, check the paths in %s' % (PYTHON, EM_CONFIG)) + sys.exit(1) + if not fastcomp_ok: logging.critical('failing sanity checks due to previous fastcomp failure') sys.exit(1) diff --git a/tools/system_libs.py b/tools/system_libs.py index e7a47c3788b61..b45db463f400a 100644 --- a/tools/system_libs.py +++ b/tools/system_libs.py @@ -95,7 +95,7 @@ def create_libc(libname): blacklist = set( ['ipc', 'passwd', 'thread', 'signal', 'sched', 'ipc', 'time', 'linux', 'aio', 'exit', 'legacy', 'mq', 'process', 'search', 'setjmp', 'env', 'ldso', 'conf'] + # musl modules ['memcpy.c', 'memset.c', 'memmove.c', 'getaddrinfo.c', 'getnameinfo.c', 'inet_addr.c', 'res_query.c', 'gai_strerror.c', 'proto.c', 'gethostbyaddr.c', 'gethostbyaddr_r.c', 'gethostbyname.c', 'gethostbyname2_r.c', 'gethostbyname_r.c', 'gethostbyname2.c', 'usleep.c', 'alarm.c', 'syscall.c'] + # individual files - ['abs.c', 'cos.c', 'cosf.c', 'cosl.c', 'sin.c', 'sinf.c', 'sinl.c', 'tan.c', 'tanf.c', 'tanl.c', 'acos.c', 'acosf.c', 'acosl.c', 'asin.c', 'asinf.c', 'asinl.c', 'atan.c', 'atanf.c', 'atanl.c', 'atan2.c', 'atan2f.c', 'atan2l.c', 'exp.c', 'expf.c', 'expl.c', 'log.c', 'logf.c', 'logl.c', 'sqrt.c', 'sqrtf.c', 'sqrtl.c', 'fabs.c', 'fabsf.c', 'fabsl.c', 'ceil.c', 'ceilf.c', 'ceill.c', 'floor.c', 'floorf.c', 'floorl.c', 'pow.c', 'powf.c', 'powl.c'] # individual math files + ['abs.c', 'cos.c', 'cosf.c', 'cosl.c', 'sin.c', 'sinf.c', 'sinl.c', 'tan.c', 'tanf.c', 'tanl.c', 'acos.c', 'acosf.c', 'acosl.c', 'asin.c', 'asinf.c', 'asinl.c', 'atan.c', 'atanf.c', 'atanl.c', 'atan2.c', 'atan2f.c', 'atan2l.c', 'exp.c', 'expf.c', 'expl.c', 'log.c', 'logf.c', 'logl.c', 'sqrt.c', 'sqrtf.c', 'sqrtl.c', 'fabs.c', 'fabsf.c', 'fabsl.c', 'ceil.c', 'ceilf.c', 'ceill.c', 'floor.c', 'floorf.c', 'floorl.c', 'pow.c', 'powf.c', 'powl.c', 'round.c', 'roundf.c'] # individual math files ) # TODO: consider using more math code from musl, doing so makes box2d faster for dirpath, dirnames, filenames in os.walk(musl_srcdir):