What is the proper way of capturing the mouse in a HTML5 game?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By fviktor

Environment: Windows 10 Pro 64bit, Chrome, Godot 3.0.2, GDScript

Goal is to capture the mouse on left clicking the Player node, then keep it captured as long as the left button is released.

Relevant code in the Player’s script:

func _on_Player_input_event( viewport, event, shape_idx ):
    if event.is_action_pressed("player_select"):
        Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)

func _input(event):
    if event.is_action_released("player_select"):
        Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN)

The player_select action is bound to the left mouse button in the project settings.

It works perfectly if run from the editor or exported as a Windows Desktop application (EXE).

The HTML5 exported version yields an error on left clicking the player:

**ERROR**: MOUSE_MODE_CAPTURED can only be entered from within an appropriate input callback
**ERROR**:    At: platform/javascript/os_javascript.cpp:596:set_mouse_mode() - Condition ' result == -2 ' is true.

What is the appropriate input callback to use in this case?

Thank you.

This is the related code from os_javascript.cpp:596:set_mouse_mode()

EMSCRIPTEN_RESULT result = emscripten_request_pointerlock("canvas", false);
ERR_EXPLAIN("MOUSE_MODE_CAPTURED can only be entered from within an appropriate input callback");
ERR_FAIL_COND(result == EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED);
ERR_FAIL_COND(result != EMSCRIPTEN_RESULT_SUCCESS);

The emscripten_request_pointerlock function is documented here:
html5.h — Emscripten 3.1.49-git (dev) documentation

Some functions, including emscripten_request_pointerlock() and emscripten_request_fullscreen(), are affected by web security.

While the functions can be called anywhere, the actual “requests” can only be raised inside the handler for a user-generated event (for example a key, mouse or touch press/release).

What is the right/best place in GDScript code to call Input.set_mouse_mode while satisfying the security requirements?

fviktor | 2018-03-04 19:13

Found a really simple workaround:

var hovered = false

func _on_Player_mouse_entered():
	hovered = true

func _on_Player_mouse_exited():
	hovered = false

func _input(event):
	if event.is_action_pressed("player_select"):
		if hovered:
			select_player()
	elif event.is_action_released("player_select"):
		release_player()

Mouse capture works reliably now both on desktop and the Web.

fviktor | 2018-03-04 23:20

Here is a tutorial on youtube in Spanish. You have to capture the mouse after loading the game, if you capture the mouse in the ready function it will not work.
Another important issue is that you call capture from the input (event) function

TUTORIAL YOUTUBE IN SPANISH CAPTURE MOUSE

ariel | 2019-09-12 00:48

:bust_in_silhouette: Reply From: eska

Guessing from the method signature, I assume _on_Player_input_event is the target of the input_event signal of a CollisionObject2D. Since this signal is emitted during physics processing (when checking collision between mouse position and collision shapes), it’s a physics callback, and not an immediate input callback.

You can use the _input or _unhandled_input callback. In your case, this means you’ll have to check for collision manually.
When working with GUIs, a Control node’s _gui_input callback should also work.

Thank you for your detailed answer and explanation.

It seems I need to do ray casting from the screen via the mouse position as described at the bottom of this tutorial: Ray-casting — Godot Engine (3.0) documentation in English

fviktor | 2018-03-04 21:36

UPDATE: Please see the workaround in the comment on my original post.

fviktor | 2018-03-04 23:22