From 437b46e13f782b25c16300fa9f97c353bb5444db Mon Sep 17 00:00:00 2001 From: Min Date: Wed, 31 Jul 2024 12:42:47 +0100 Subject: [PATCH] Added working canvas/glfw pointer lock feature --- window/canvas.go | 60 ++++++++++++++++++++++++++++++++++++++++-------- window/glfw.go | 9 ++++++++ window/window.go | 7 ++++++ 3 files changed, 66 insertions(+), 10 deletions(-) diff --git a/window/canvas.go b/window/canvas.go index baa469a0..cf72f22b 100644 --- a/window/canvas.go +++ b/window/canvas.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build wasm // +build wasm package window @@ -326,16 +327,18 @@ type WebGlCanvas struct { sizeEv SizeEvent cursorEv CursorEvent scrollEv ScrollEvent + lockEv LockEvent // Callbacks - onCtxMenu js.Func - keyDown js.Func - keyUp js.Func - mouseDown js.Func - mouseUp js.Func - mouseMove js.Func - mouseWheel js.Func - winResize js.Func + onCtxMenu js.Func + keyDown js.Func + keyUp js.Func + mouseDown js.Func + mouseUp js.Func + mouseMove js.Func + mouseWheel js.Func + winResize js.Func + pointerLock js.Func } // Init initializes the WebGlCanvas singleton. @@ -382,6 +385,13 @@ func Init(canvasId string) error { // TODO scaling/hidpi (device pixel ratio) + w.pointerLock = js.FuncOf(func(this js.Value, args []js.Value) interface{} { + w.lockEv.Locked = doc.Get("pointerLockElement").Equal(w.canvas) + w.Dispatch(OnLockChange, &w.lockEv) + return nil + }) + doc.Call("addEventListener", "pointerlockchange", w.pointerLock) + // Set up key down callback to dispatch event w.keyDown = js.FuncOf(func(this js.Value, args []js.Value) interface{} { event := args[0] @@ -431,8 +441,14 @@ func Init(canvasId string) error { // Set up mouse move callback to dispatch event w.mouseMove = js.FuncOf(func(this js.Value, args []js.Value) interface{} { event := args[0] - w.cursorEv.Xpos = float32(event.Get("offsetX").Float()) //* float32(w.scaleX) TODO - w.cursorEv.Ypos = float32(event.Get("offsetY").Float()) //* float32(w.scaleY) + if w.lockEv.Locked { + w.cursorEv.Xpos += float32(event.Get("movementX").Float()) + w.cursorEv.Ypos += float32(event.Get("movementY").Float()) + } else { + w.cursorEv.Xpos = float32(event.Get("offsetX").Float()) //* float32(w.scaleX) TODO + w.cursorEv.Ypos = float32(event.Get("offsetY").Float()) //* float32(w.scaleY) + } + w.cursorEv.Mods = getModifiers(event) w.Dispatch(OnCursor, &w.cursorEv) return nil @@ -536,6 +552,7 @@ func (w *WebGlCanvas) Destroy() { w.canvas.Call("removeEventListener", "mousemove", w.mouseMove) w.canvas.Call("removeEventListener", "wheel", w.mouseWheel) js.Global().Get("window").Call("removeEventListener", "resize", w.winResize) + js.Global().Get("document").Call("removeEventListener", "pointerlockchange", w.pointerLock) // Release callbacks w.onCtxMenu.Release() @@ -546,6 +563,7 @@ func (w *WebGlCanvas) Destroy() { w.mouseMove.Release() w.mouseWheel.Release() w.winResize.Release() + w.pointerLock.Release() } // GetFramebufferSize returns the framebuffer size. @@ -588,6 +606,28 @@ func (w *WebGlCanvas) SetCursor(cursor Cursor) { // TODO } +// SetCursorMode sets the window's cursor mode. +// More info: https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API +func (w *WebGlCanvas) SetCursorMode(mode CursorMode) { + switch mode { + case CursorNormal: + js.Global().Get("document").Call("exitPointerLock") + case CursorHidden: + case CursorDisabled: + promise := w.Canvas().Call("requestPointerLock", map[string]interface{}{"unadjustedMovement": true}) + if promise.IsUndefined() { + w.Canvas().Call("requestPointerLock") + } else { + promise.Call("catch", js.FuncOf(func(this js.Value, args []js.Value) interface{} { + if len(args) > 0 && args[0].Get("name").Equal(js.ValueOf("NotSupportedError")) { + w.Canvas().Call("requestPointerLock") + } + return nil + })) + } + } +} + // DisposeAllCursors deletes all existing custom cursors. func (w *WebGlCanvas) DisposeAllCustomCursors() { diff --git a/window/glfw.go b/window/glfw.go index 7173bf8a..d5380293 100644 --- a/window/glfw.go +++ b/window/glfw.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !wasm // +build !wasm package window @@ -205,6 +206,7 @@ type GlfwWindow struct { sizeEv SizeEvent cursorEv CursorEvent scrollEv ScrollEvent + lockEv LockEvent mods ModifierKey // Current modifier keys @@ -450,6 +452,13 @@ func (w *GlfwWindow) SetCursor(cursor Cursor) { w.Window.SetCursor(cur) } +// SetCursorMode sets the window's cursor mode. +func (w *GlfwWindow) SetCursorMode(mode CursorMode) { + w.Window.SetInputMode(glfw.CursorMode, int(mode)) + w.lockEv.Locked = mode == CursorDisabled + w.Dispatch(OnLockChange, &w.lockEv) +} + // CreateCursor creates a new custom cursor and returns an int handle. func (w *GlfwWindow) CreateCursor(imgFile string, xhot, yhot int) (Cursor, error) { diff --git a/window/window.go b/window/window.go index a7511b36..749d9dcd 100644 --- a/window/window.go +++ b/window/window.go @@ -38,6 +38,7 @@ type IWindow interface { GetScale() (x float64, y float64) CreateCursor(imgFile string, xhot, yhot int) (Cursor, error) SetCursor(cursor Cursor) + SetCursorMode(mode CursorMode) DisposeAllCustomCursors() Destroy() } @@ -85,6 +86,7 @@ const ( // Desktop | Browser | OnMouseUp = "w.OnMouseUp" // x | x | OnMouseDown = "w.OnMouseDown" // x | x | OnScroll = "w.OnScroll" // x | x | + OnLockChange = "w.OnLockChange" // x | x | ) // PosEvent describes a windows position changed event @@ -132,3 +134,8 @@ type ScrollEvent struct { Yoffset float32 Mods ModifierKey } + +// LockEvent describes if cursor is locked or not +type LockEvent struct { + Locked bool +}