Skip to content

Don't throw when the Pointer Lock API is missing - #28103

Merged
sbc100 merged 2 commits into
emscripten-core:mainfrom
Grantim:glfw-no-pointer-lock
Oct 29, 2026
Merged

sbc100 merged 2 commits into
emscripten-core:mainfrom
Grantim:glfw-no-pointer-lock

Conversation

@Grantim

@Grantim Grantim commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

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

Tested by building the updated test_glfw3.c with emsdk 4.0.19 and the new test's pre-js, then running it in Chrome: it throws TypeError: document.exitPointerLock is not a function without this change and exits with 0 with it. I could not run test_browser.test_glfw3_no_pointer_lock itself locally.

Generated with Claude Code

Since emscripten-core#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 emscripten-core#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 emscripten-core#24603 left unguarded no-ops when the API is missing, and test GLFW with the API removed.

Fixes emscripten-core#28102

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request safely handles environments that lack Pointer Lock API support (such as Safari on iOS) by using optional calls to requestPointerLock and exitPointerLock across Emscripten's browser, GLFW, and SDL libraries, alongside adding a new test case to verify this behavior. The review feedback points out that using ES2020 optional chaining (?.) in Emscripten system libraries can cause compatibility issues with older JavaScript environments or build toolchains. It is recommended to use standard conditional checks instead of optional chaining to ensure backward compatibility.

Comment thread src/lib/libbrowser.js
if (getFullscreenElement() === canvasContainer) {
canvas.exitFullscreen = Browser.exitFullscreen;
if (Browser.lockPointer) canvas.requestPointerLock();
if (Browser.lockPointer) canvas.requestPointerLock?.();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using ES2020 optional chaining (?.) in Emscripten system libraries can cause compatibility issues with older JavaScript environments or build toolchains that do not downlevel modern JS syntax. It is safer and more backward-compatible to use standard conditional checks.

          if (Browser.lockPointer && canvas.requestPointerLock) canvas.requestPointerLock();

Comment thread src/lib/libglfw.js
win.inputModes[mode] = value;
canvas.removeEventListener('click', GLFW.onClickRequestPointerLock, true);
document.exitPointerLock();
document.exitPointerLock?.();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using ES2020 optional chaining (?.) in Emscripten system libraries can cause compatibility issues with older JavaScript environments or build toolchains that do not downlevel modern JS syntax. It is safer and more backward-compatible to use standard conditional checks.

              if (document.exitPointerLock) document.exitPointerLock();

Comment thread src/lib/libglfw.js
win.inputModes[mode] = value;
canvas.addEventListener('click', GLFW.onClickRequestPointerLock, true);
canvas.requestPointerLock();
canvas.requestPointerLock?.();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using ES2020 optional chaining (?.) in Emscripten system libraries can cause compatibility issues with older JavaScript environments or build toolchains that do not downlevel modern JS syntax. It is safer and more backward-compatible to use standard conditional checks.

              if (canvas.requestPointerLock) canvas.requestPointerLock();

Comment thread src/lib/libglfw.js
if (getFullscreenElement() === canvasContainer) {
canvas.exitFullscreen = Browser.exitFullscreen;
if (Browser.lockPointer) canvas.requestPointerLock();
if (Browser.lockPointer) canvas.requestPointerLock?.();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using ES2020 optional chaining (?.) in Emscripten system libraries can cause compatibility issues with older JavaScript environments or build toolchains that do not downlevel modern JS syntax. It is safer and more backward-compatible to use standard conditional checks.

          if (Browser.lockPointer && canvas.requestPointerLock) canvas.requestPointerLock();

Comment thread src/lib/libsdl.js
case 0: // SDL_DISABLE
if (Browser.isFullscreen) { // only try to lock the pointer when in full screen mode
Browser.getCanvas().requestPointerLock();
Browser.getCanvas().requestPointerLock?.();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using ES2020 optional chaining (?.) in Emscripten system libraries can cause compatibility issues with older JavaScript environments or build toolchains that do not downlevel modern JS syntax. It is safer and more backward-compatible to use standard conditional checks.

          if (Browser.getCanvas().requestPointerLock) Browser.getCanvas().requestPointerLock();

Comment thread src/lib/libsdl.js
return 1;
case 1: // SDL_ENABLE
document.exitPointerLock();
document.exitPointerLock?.();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using ES2020 optional chaining (?.) in Emscripten system libraries can cause compatibility issues with older JavaScript environments or build toolchains that do not downlevel modern JS syntax. It is safer and more backward-compatible to use standard conditional checks.

        if (document.exitPointerLock) document.exitPointerLock();

@Grantim

Grantim commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review. I kept ?.(), because Emscripten's library code already uses optional calls for the same kind of browser API check, including in the files this PR touches:

Generated with Claude Code

@Grantim Grantim reopened this Oct 29, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sbc100

sbc100 commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator

Thanks for fixing this. Sorry I didn't realize when i landed #24603 that not all browsers supported this API.

@sbc100
sbc100 merged commit e14bc51 into emscripten-core:main Oct 29, 2026
42 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GLFW: glfwSetInputMode(GLFW_CURSOR_NORMAL) throws in browsers without Pointer Lock (iOS Safari) since 4.0.11

2 participants