Maelstrom: Hide the mouse cursor unless it's actually used

From 07b48a993d4830ec444571fc31fd07b91c5e61fd Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Tue, 28 Apr 2026 11:42:13 -0700
Subject: [PATCH] Hide the mouse cursor unless it's actually used

---
 screenlib/SDL_FrameBuf.cpp | 96 +++++++++++++++++++++++++++++++-------
 screenlib/SDL_FrameBuf.h   | 16 +++----
 2 files changed, 86 insertions(+), 26 deletions(-)

diff --git a/screenlib/SDL_FrameBuf.cpp b/screenlib/SDL_FrameBuf.cpp
index e6c78e07..e630f45d 100644
--- a/screenlib/SDL_FrameBuf.cpp
+++ b/screenlib/SDL_FrameBuf.cpp
@@ -84,6 +84,8 @@ FrameBuf::Init(int width, int height, Uint32 window_flags, const char *title, SD
 	m_clip.w = (float)width;
 	m_clip.h = (float)height;
 
+	UpdateCursorVisibility();
+
 	return(0);
 }
 
@@ -117,6 +119,16 @@ void
 FrameBuf::ProcessEvent(SDL_Event *event)
 {
 	SDL_ConvertEventToRenderCoordinates(m_renderer, event);
+
+	if (!m_cursorActive) {
+		if (event->type == SDL_EVENT_MOUSE_MOTION &&
+		    event->motion.which != SDL_TOUCH_MOUSEID &&
+		    event->motion.which != SDL_PEN_MOUSEID) {
+			m_cursorActive = true;
+			UpdateCursorVisibility();
+		}
+	}
+
 	ProcessGamepadEvent(event);
 }
 
@@ -340,14 +352,74 @@ FrameBuf::SetCursor(SDL_Surface *image, int hotX, int hotY)
 		m_cursor = SDL_CreateColorCursor(image, hotX, hotY);
 	}
 #endif
+
 	if (m_cursor) {
 		SDL_SetCursor(m_cursor);
 	} else {
-		m_cursorTexture = SDL_CreateTextureFromSurface(m_renderer, image);
-		m_cursorWidth = image->w;
-		m_cursorHeight = image->h;
-		m_cursorOffsetX = -hotX;
-		m_cursorOffsetY = -hotY;
+		bool draw_cursor = true;
+#ifdef SDL_PLATFORM_IOS
+		if (SDL_IsTablet()) {
+			// iPadOS shows its own circular cursor
+			draw_cursor = false;
+		}
+#endif
+		if (draw_cursor) {
+			m_cursorTexture = SDL_CreateTextureFromSurface(m_renderer, image);
+			m_cursorWidth = image->w;
+			m_cursorHeight = image->h;
+			m_cursorOffsetX = -hotX;
+			m_cursorOffsetY = -hotY;
+		}
+	}
+	UpdateCursorVisibility();
+}
+
+void
+FrameBuf::ShowCursor()
+{
+	m_cursorDesired = true;
+
+	UpdateCursorVisibility();
+}
+
+void
+FrameBuf::HideCursor()
+{
+	m_cursorDesired = false;
+
+	UpdateCursorVisibility();
+}
+
+void
+FrameBuf::UpdateCursorVisibility()
+{
+	bool visible = false;
+
+	if (m_cursorDesired && m_cursorActive) {
+		visible = true;
+	}
+
+	if (visible && !m_cursorTexture) {
+		SDL_ShowCursor();
+	} else {
+		SDL_HideCursor();
+	}
+	m_cursorVisible = visible;
+}
+
+void
+FrameBuf::DrawCursor()
+{
+	if (m_cursorVisible && m_cursorTexture) {
+		int x, y;
+		GetCursorPosition(&x, &y);
+
+		SDL_FRect dstrect;
+		dstrect.x = (float)x + m_cursorOffsetX;
+		dstrect.y = (float)y + m_cursorOffsetY;
+		dstrect.w = (float)m_cursorWidth;
+		dstrect.h = (float)m_cursorHeight;
+		SDL_RenderTexture(m_renderer, m_cursorTexture, NULL, &dstrect);
 	}
 }
 
@@ -500,18 +572,7 @@ FrameBuf::Update(void)
 		return;
 	}
 
-	if (m_cursorTexture && SDL_CursorVisible()) {
-		int x, y;
-		GetCursorPosition(&x, &y);
-
-		SDL_FRect dstrect;
-		dstrect.x = (float)x + m_cursorOffsetX;
-		dstrect.y = (float)y + m_cursorOffsetY;
-		dstrect.w = (float)m_cursorWidth;
-		dstrect.h = (float)m_cursorHeight;
-		SDL_RenderTexture(m_renderer, m_cursorTexture, NULL, &dstrect);
-	}
-
+	DrawCursor();
 	SDL_RenderPresent(m_renderer);
 }
 
@@ -536,6 +597,7 @@ FrameBuf::FadeStep(void)
 	SDL_RenderClear(m_renderer);
 	SDL_SetTextureColorMod(m_fadeTexture, value, value, value);
 	SDL_RenderTexture(m_renderer, m_fadeTexture, NULL, NULL);
+	DrawCursor();
 	SDL_RenderPresent(m_renderer);
 	SDL_Delay(10);
 	++m_fadeStep;
diff --git a/screenlib/SDL_FrameBuf.h b/screenlib/SDL_FrameBuf.h
index 3bfff7fe..ac14da14 100644
--- a/screenlib/SDL_FrameBuf.h
+++ b/screenlib/SDL_FrameBuf.h
@@ -190,16 +190,11 @@ class FrameBuf : public ErrorBase {
 
 	/* Cursor handling routines */
 	void SetCursor(SDL_Surface *image, int hotX, int hotY);
-	void ShowCursor(void) {
-		SDL_ShowCursor();
-	}
-	void HideCursor(void) {
-		SDL_HideCursor();
-	}
+	void ShowCursor();
+	void HideCursor();
+	void UpdateCursorVisibility();
+	void DrawCursor();
 	void GetCursorPosition(int *x, int *y);
-	void SetCaption(const char *caption, const char *icon = NULL) {
-		SDL_SetWindowTitle(m_window, caption);
-	}
 
 private:
 	/* The current display */
@@ -208,6 +203,9 @@ class FrameBuf : public ErrorBase {
 	SDL_Texture *m_fadeTexture = nullptr;
 	SDL_Cursor *m_cursor = nullptr;
 	SDL_Texture *m_cursorTexture = nullptr;
+	bool m_cursorDesired = true;
+	bool m_cursorActive = false;
+	bool m_cursorVisible = false;
 	int m_cursorWidth = 0;
 	int m_cursorHeight = 0;
 	int m_cursorOffsetX = 0;