From 11d90d012e7ece04052c1b82e948182ae559cb86 Mon Sep 17 00:00:00 2001 From: Grant Karapetyan Date: Thu, 29 Oct 2026 22:53:50 +0400 Subject: [PATCH 1/2] Don't throw when the Pointer Lock API is missing Since #24603, `glfwSetInputMode(..., GLFW_CURSOR_NORMAL)` calls `document.exitPointerLock()` unconditionally. In browsers without the Pointer Lock API (Safari on iOS) that throws, so GLFW apps that set the cursor mode fail to start. Before #24603 these calls went through no-op fallbacks set up in `Browser.init`. Make the pointer lock calls in libglfw.js, libsdl.js and libbrowser.js that #24603 left unguarded no-ops when the API is missing, and test GLFW with the API removed. Fixes #28102 Co-Authored-By: Claude Opus 5.5 --- src/lib/libbrowser.js | 2 +- src/lib/libglfw.js | 6 +++--- src/lib/libsdl.js | 4 ++-- test/browser/test_glfw3.c | 2 ++ test/test_browser.py | 6 ++++++ 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/lib/libbrowser.js b/src/lib/libbrowser.js index 79d6c00ec3c4f..a41e52f6bf268 100644 --- a/src/lib/libbrowser.js +++ b/src/lib/libbrowser.js @@ -226,7 +226,7 @@ var LibraryBrowser = { var canvasContainer = canvas.parentNode; if (getFullscreenElement() === canvasContainer) { canvas.exitFullscreen = Browser.exitFullscreen; - if (Browser.lockPointer) canvas.requestPointerLock(); + if (Browser.lockPointer) canvas.requestPointerLock?.(); Browser.isFullscreen = true; if (Browser.resizeCanvas) { Browser.setFullscreenCanvasSize(); diff --git a/src/lib/libglfw.js b/src/lib/libglfw.js index cfbdef315cfb2..781eaf9463c90 100644 --- a/src/lib/libglfw.js +++ b/src/lib/libglfw.js @@ -994,7 +994,7 @@ var LibraryGLFW = { case 0x00034001: { // GLFW_CURSOR_NORMAL win.inputModes[mode] = value; canvas.removeEventListener('click', GLFW.onClickRequestPointerLock, true); - document.exitPointerLock(); + document.exitPointerLock?.(); break; } case 0x00034002: { // GLFW_CURSOR_HIDDEN @@ -1004,7 +1004,7 @@ var LibraryGLFW = { case 0x00034003: { // GLFW_CURSOR_DISABLED win.inputModes[mode] = value; canvas.addEventListener('click', GLFW.onClickRequestPointerLock, true); - canvas.requestPointerLock(); + canvas.requestPointerLock?.(); break; } default: { @@ -1222,7 +1222,7 @@ var LibraryGLFW = { var canvasContainer = canvas.parentNode; if (getFullscreenElement() === canvasContainer) { canvas.exitFullscreen = Browser.exitFullscreen; - if (Browser.lockPointer) canvas.requestPointerLock(); + if (Browser.lockPointer) canvas.requestPointerLock?.(); Browser.isFullscreen = true; if (Browser.resizeCanvas) { Browser.setFullscreenCanvasSize(); diff --git a/src/lib/libsdl.js b/src/lib/libsdl.js index fe0e0fd34c191..b2b94e94c6d0e 100644 --- a/src/lib/libsdl.js +++ b/src/lib/libsdl.js @@ -1742,13 +1742,13 @@ var LibrarySDL = { switch (toggle) { case 0: // SDL_DISABLE if (Browser.isFullscreen) { // only try to lock the pointer when in full screen mode - Browser.getCanvas().requestPointerLock(); + Browser.getCanvas().requestPointerLock?.(); return 0; } // else return SDL_ENABLE to indicate the failure return 1; case 1: // SDL_ENABLE - document.exitPointerLock(); + document.exitPointerLock?.(); return 1; case -1: // SDL_QUERY return !Browser.pointerLock; diff --git a/test/browser/test_glfw3.c b/test/browser/test_glfw3.c index 95748fceccd3d..8af8451797997 100644 --- a/test/browser/test_glfw3.c +++ b/test/browser/test_glfw3.c @@ -205,6 +205,8 @@ int main() { // glfwPollEvents(); // glfwWaitEvents(); + assert(glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_NORMAL); + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); assert(glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_NORMAL); // XXX: not implemented diff --git a/test/test_browser.py b/test/test_browser.py index 0a140216d242b..01a706a2912f7 100644 --- a/test/test_browser.py +++ b/test/test_browser.py @@ -2841,6 +2841,12 @@ def test_glfw3_default_hints(self): def test_glfw3(self, args, opts): self.btest_exit('test_glfw3.c', cflags=['-sUSE_GLFW=3', '-lglfw', '-lGL'] + args + opts) + @requires_graphics_hardware + def test_glfw3_no_pointer_lock(self): + # Browsers without the Pointer Lock API, such as Safari on iOS + create_file('pre.js', 'delete Document.prototype.exitPointerLock; delete Element.prototype.requestPointerLock;') + self.btest_exit('test_glfw3.c', cflags=['-sUSE_GLFW=3', '-lglfw', '-lGL', '-DCLIENT_API=GLFW_OPENGL_ES_API', '-sGL_ENABLE_GET_PROC_ADDRESS', '--pre-js', 'pre.js']) + @parameterized({ '': (['-sUSE_GLFW=2', '-DUSE_GLFW=2'],), 'glfw3': (['-sUSE_GLFW=3', '-DUSE_GLFW=3'],), From df50dd35113a743e871ae55e1a490d96c6d9075c Mon Sep 17 00:00:00 2001 From: Grant Karapetyan Date: Thu, 29 Oct 2026 23:09:11 +0400 Subject: [PATCH 2/2] Rebaseline codesize expectations. NFC Co-Authored-By: Claude Opus 5.5 --- test/codesize/test_codesize_hello_dylink_all.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/codesize/test_codesize_hello_dylink_all.json b/test/codesize/test_codesize_hello_dylink_all.json index 46a391f5d3793..be1ecb37c57ff 100644 --- a/test/codesize/test_codesize_hello_dylink_all.json +++ b/test/codesize/test_codesize_hello_dylink_all.json @@ -1,7 +1,7 @@ { - "a.out.js": 272928, + "a.out.js": 272934, "a.out.nodebug.wasm": 589443, - "total": 862371, + "total": 862377, "sent": [ "IMG_Init", "IMG_Load",