From ead84553a0f1188700de316da94fe933f4c69f3b Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 16 Apr 2021 02:26:10 +0900 Subject: [PATCH] internal/uidriver/js: Implement CursorModeCaptured Closes #1572 --- internal/uidriver/js/input_js.go | 22 ++++++++--- internal/uidriver/js/ui_js.go | 68 ++++++++++++++++++++------------ 2 files changed, 59 insertions(+), 31 deletions(-) diff --git a/internal/uidriver/js/input_js.go b/internal/uidriver/js/input_js.go index 493705108..15907554b 100644 --- a/internal/uidriver/js/input_js.go +++ b/internal/uidriver/js/input_js.go @@ -74,6 +74,8 @@ type Input struct { mouseButtonPressed map[int]bool cursorX int cursorY int + origCursorX int + origCursorY int wheelX float64 wheelY float64 gamepads map[driver.GamepadID]gamepad @@ -284,10 +286,6 @@ func (i *Input) mouseUp(code int) { i.mouseButtonPressed[code] = false } -func (i *Input) setMouseCursor(x, y int) { - i.cursorX, i.cursorY = x, y -} - func (i *Input) updateGamepads() { nav := js.Global().Get("navigator") if !nav.Truthy() { @@ -401,8 +399,22 @@ func (i *Input) updateFromEvent(e js.Value) { } func (i *Input) setMouseCursorFromEvent(e js.Value) { + if i.ui.cursorMode == driver.CursorModeCaptured { + x, y := e.Get("clientX").Int(), e.Get("clientY").Int() + i.origCursorX, i.origCursorY = x, y + dx, dy := e.Get("movementX").Int(), e.Get("movementY").Int() + i.cursorX += dx + i.cursorY += dy + return + } + x, y := e.Get("clientX").Int(), e.Get("clientY").Int() - i.setMouseCursor(x, y) + i.cursorX, i.cursorY = x, y + i.origCursorX, i.origCursorY = x, y +} + +func (i *Input) recoverCursorPosition() { + i.cursorX, i.cursorY = i.origCursorX, i.origCursorY } func (in *Input) updateTouchesFromEvent(e js.Value) { diff --git a/internal/uidriver/js/ui_js.go b/internal/uidriver/js/ui_js.go index 8bf6c9a62..7baf7f9e2 100644 --- a/internal/uidriver/js/ui_js.go +++ b/internal/uidriver/js/ui_js.go @@ -50,7 +50,8 @@ type UserInterface struct { vsync bool running bool initFocused bool - cursorHidden bool + cursorMode driver.CursorMode + cursorPrevMode driver.CursorMode cursorShape driver.CursorShape sizeChanged bool @@ -122,38 +123,34 @@ func (u *UserInterface) CursorMode() driver.CursorMode { if !canvas.Truthy() { return driver.CursorModeHidden } - - if u.cursorHidden { - return driver.CursorModeHidden - } - return driver.CursorModeVisible + return u.cursorMode } func (u *UserInterface) SetCursorMode(mode driver.CursorMode) { if !canvas.Truthy() { return } - - switch mode { - case driver.CursorModeVisible: - if !u.cursorHidden { - return - } - u.cursorHidden = false - case driver.CursorModeHidden: - if u.cursorHidden { - return - } - u.cursorHidden = true - default: + if u.cursorMode == mode { return } - - if u.cursorHidden { - canvas.Get("style").Set("cursor", stringNone) - } else { - canvas.Get("style").Set("cursor", driverCursorShapeToCSSCursor(u.cursorShape)) + // Remember the previous cursor mode in the case when the pointer lock exit by pressing ESC. + u.cursorPrevMode = u.cursorMode + if u.cursorMode == driver.CursorModeCaptured { + document.Call("exitPointerLock") } + u.cursorMode = mode + switch mode { + case driver.CursorModeVisible: + canvas.Get("style").Set("cursor", driverCursorShapeToCSSCursor(u.cursorShape)) + case driver.CursorModeHidden: + canvas.Get("style").Set("cursor", stringNone) + case driver.CursorModeCaptured: + canvas.Call("requestPointerLock") + } +} + +func (u *UserInterface) recoverCursorMode() { + u.SetCursorMode(u.cursorPrevMode) } func (u *UserInterface) CursorShape() driver.CursorShape { @@ -170,8 +167,9 @@ func (u *UserInterface) SetCursorShape(shape driver.CursorShape) { if u.cursorShape == shape { return } + u.cursorShape = shape - if !u.cursorHidden { + if u.cursorMode == driver.CursorModeVisible { canvas.Get("style").Set("cursor", driverCursorShapeToCSSCursor(u.cursorShape)) } } @@ -380,6 +378,24 @@ func init() { canvas.Get("style").Set("outline", "none") setCanvasEventHandlers(canvas) + + // Pointer Lock + document.Call("addEventListener", "pointerlockchange", js.FuncOf(func(this js.Value, args []js.Value) interface{} { + if document.Get("pointerLockElement").Truthy() { + return nil + } + // Recover the state correctly when exiting from the pointer lock. + + // A user can exit the pointer lock by pressing ESC. In this case, sync the cursor mode state. + if theUI.cursorMode == driver.CursorModeCaptured { + if theUI.cursorPrevMode == driver.CursorModeCaptured { + panic("js: cursorPrevMode must not be driver.CursorModeCaptured") + } + theUI.recoverCursorMode() + } + theUI.input.recoverCursorPosition() + return nil + })) } func setWindowEventHandlers(v js.Value) { @@ -473,7 +489,7 @@ func setCanvasEventHandlers(v js.Value) { return nil })) - // Gamepad + // Context menu v.Call("addEventListener", "contextmenu", js.FuncOf(func(this js.Value, args []js.Value) interface{} { e := args[0] e.Call("preventDefault")