From 5b3121171e9f98330520725d048820d63c545f05 Mon Sep 17 00:00:00 2001 From: Noah Petherbridge Date: Fri, 19 Apr 2024 22:01:33 -0700 Subject: [PATCH] Fix touchscreen mode detection * Touchscreen mode used to be detected based on SDL2 GetNumTouchDevices but on a Macbook, the trackpad registers as a touch device - worse, GetNumTouchDevices will only start returning 1 the first time some devices are touched. * The result was that on macOS the custom mouse cursor was drawn by default, but on the first trackpad touch, would disappear in favor of assuming the game is running on a touch screen device (which is not the case). * New method: the render engine has an IsFingerDown boolean which will be true as long as at least one finger has registered a FingerDown event, but not yet a FingerUp event. * So as long as one finger is down, the mouse cursor can disappear and then it comes back on release. This isn't perfectly ideal for pure touch devices (ideally the cursor remains hidden until a mouse movement without touch occurs). --- cmd/doodle/main.go | 4 +--- pkg/cursor/cursor.go | 2 +- pkg/doodle.go | 14 ++++++++++++++ pkg/native/engine_sdl.go | 3 +++ pkg/native/touch_screen.go | 12 ++++++++++++ pkg/uix/canvas_scrolling.go | 2 +- 6 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 pkg/native/touch_screen.go diff --git a/cmd/doodle/main.go b/cmd/doodle/main.go index 9f4082d..0f54587 100644 --- a/cmd/doodle/main.go +++ b/cmd/doodle/main.go @@ -215,9 +215,7 @@ func main() { usercfg.Load() // Hide the mouse cursor over the window, we draw our own sprite image for it. - if !native.HasTouchscreen(engine) { - engine.ShowCursor(false) - } + engine.ShowCursor(false) // Set the app window icon. if engine, ok := game.Engine.(*sdl.Renderer); ok { diff --git a/pkg/cursor/cursor.go b/pkg/cursor/cursor.go index 0698b8f..629812b 100644 --- a/pkg/cursor/cursor.go +++ b/pkg/cursor/cursor.go @@ -25,7 +25,7 @@ var NoCursor = &Cursor{} // Draw the cursor on screen. NOTE: Does not draw on touchscreen devices. func Draw(e render.Engine) { - if native.HasTouchscreen(e) { + if native.IsTouchScreenMode { return } diff --git a/pkg/doodle.go b/pkg/doodle.go index 0a619c9..80c28e0 100644 --- a/pkg/doodle.go +++ b/pkg/doodle.go @@ -141,6 +141,20 @@ func (d *Doodle) Run() error { } d.event = ev + // If they have touched a touch screen, set the touchscreen mode boolean. This + // will hide the mouse cursor until they let up off the screen. + if !native.IsTouchScreenMode { + if ev.IsFingerDown { + log.Debug("TouchScreenMode ON!") + native.IsTouchScreenMode = true + } + } else { + if !ev.IsFingerDown { + log.Debug("TouchScreenMode OFF") + native.IsTouchScreenMode = false + } + } + // Always have an accurate idea of the window size. if ev.WindowResized { d.width, d.height = d.Engine.WindowSize() diff --git a/pkg/native/engine_sdl.go b/pkg/native/engine_sdl.go index c7998d8..57b6cb0 100644 --- a/pkg/native/engine_sdl.go +++ b/pkg/native/engine_sdl.go @@ -20,6 +20,9 @@ import ( // not for JavaScript/WASM yet. // HasTouchscreen checks if the device has at least one SDL_GetNumTouchDevices. +// +// Note: SDL2 GetNumTouchDevices will sometimes return 0 until a touch device is actually touched. On Macbooks, +// the trackpad counts as a touch device and on first touch, HasTouchscreen may begin returning true. func HasTouchscreen(e render.Engine) bool { if _, ok := e.(*sdl.Renderer); ok { return sdl2.GetNumTouchDevices() > 0 diff --git a/pkg/native/touch_screen.go b/pkg/native/touch_screen.go new file mode 100644 index 0000000..ce5c136 --- /dev/null +++ b/pkg/native/touch_screen.go @@ -0,0 +1,12 @@ +package native + +// Common code to handle basic touch screen detection. + +/* +IsTouchScreenMode is set to true when the user has touched the screen, and false when the mouse is moved. + +This informs the game's control scheme: if the user has a touch screen and they've touched it, +the custom mouse cursor is hidden (as it becomes distracting) and if they then move a real mouse +over the window, the custom cursor appears again and we assume non-touchscreen mode once more. +*/ +var IsTouchScreenMode bool diff --git a/pkg/uix/canvas_scrolling.go b/pkg/uix/canvas_scrolling.go index bd076ca..18f9d32 100644 --- a/pkg/uix/canvas_scrolling.go +++ b/pkg/uix/canvas_scrolling.go @@ -56,7 +56,7 @@ func (w *Canvas) loopEditorScroll(ev *event.State) error { } // Multitouch events to pan the level, like middle click on desktop. - if ev.Touching { + if ev.Touching && ev.TouchNumFingers > 1 { // Intention: user drags with 2 fingers to scroll the canvas. // SDL2 will register one finger also as a Button1 mouse click. // We need to record the "mouse cursor" as start point but then