Skip to content

Commit 59c006b

Browse files
authored
Use system-native cursor for custom cursor (#3343)
* Use system-specific cursor for custom cursor Currently, the game draws the cursor as it's own sprite. This is fine in most cases, however, this will cause input lag when the user moves their cursor. This is because the sprite cursor is drawn on every flip, which may lag behind. The OS (which SDL interfaces) has it's own cursor api, and therefore is preferred, since that sets the display systems physical cursor instead of rendering our own. * Error handling * Reset custom cursor when disabled * Disable by default
1 parent 4cb2509 commit 59c006b

File tree

6 files changed

+83
-6
lines changed

6 files changed

+83
-6
lines changed

src/gui/mousecursor.cpp

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,18 @@
1717
#include "gui/mousecursor.hpp"
1818

1919
#include <SDL.h>
20+
#include <SDL_error.h>
21+
#include <SDL_events.h>
22+
#include <SDL_mouse.h>
23+
#include <memory>
2024

2125
#include "supertux/gameconfig.hpp"
2226
#include "supertux/globals.hpp"
2327
#include "sprite/sprite.hpp"
28+
#include "util/log.hpp"
2429
#include "video/drawing_context.hpp"
2530
#include "video/renderer.hpp"
31+
#include "video/sdl_surface.hpp"
2632
#include "video/surface.hpp"
2733
#include "video/video_system.hpp"
2834
#include "video/viewport.hpp"
@@ -33,13 +39,49 @@ MouseCursor::MouseCursor(SpritePtr sprite) :
3339
m_state(MouseCursorState::NORMAL),
3440
m_applied_state(MouseCursorState::HIDE),
3541
m_sprite(std::move(sprite)),
42+
m_custom_cursor_last(false),
43+
m_cursors(),
3644
m_x(),
3745
m_y(),
3846
m_mobile_mode(false),
3947
m_icon()
4048
{
4149
}
4250

51+
void
52+
MouseCursor::set_cursor_action(const std::string& action)
53+
{
54+
if (!g_config->custom_system_cursor)
55+
{
56+
m_sprite->set_action(action);
57+
return;
58+
}
59+
60+
if (m_cursors.find(action) == m_cursors.end())
61+
{
62+
auto surfaces = m_sprite->get_action_surfaces(action);
63+
if (surfaces->empty())
64+
return;
65+
std::string filename = (*surfaces)[0]->get_filename();
66+
SDLSurfacePtr surface = SDLSurface::from_file(filename);
67+
m_cursors[action] =
68+
std::move(std::shared_ptr<SDL_Cursor>(SDL_CreateColorCursor(surface.get(), 0, 0), &SDL_FreeCursor));
69+
if (m_cursors[action])
70+
{
71+
SDL_SetCursor(m_cursors[action].get());
72+
}
73+
else
74+
{
75+
log_warning << "Couldn't load cursor: " << SDL_GetError() << "\nRendering cursor instead.\n";
76+
g_config->custom_system_cursor = false;
77+
}
78+
}
79+
else
80+
{
81+
SDL_SetCursor(m_cursors[action].get());
82+
}
83+
}
84+
4385
void
4486
MouseCursor::apply_state(MouseCursorState state)
4587
{
@@ -50,18 +92,20 @@ MouseCursor::apply_state(MouseCursorState state)
5092
switch(state)
5193
{
5294
case MouseCursorState::NORMAL:
53-
m_sprite->set_action("default");
95+
set_cursor_action("default");
5496
break;
5597

5698
case MouseCursorState::CLICK:
57-
m_sprite->set_action("click");
99+
set_cursor_action("click");
58100
break;
59101

60102
case MouseCursorState::LINK:
61-
m_sprite->set_action("link");
103+
set_cursor_action("link");
62104
break;
63105

64106
case MouseCursorState::HIDE:
107+
if (g_config->custom_system_cursor)
108+
SDL_ShowCursor(SDL_DISABLE);
65109
break;
66110
}
67111
}
@@ -70,11 +114,28 @@ MouseCursor::apply_state(MouseCursorState state)
70114
void
71115
MouseCursor::draw(DrawingContext& context)
72116
{
73-
if (!g_config->custom_mouse_cursor) return;
117+
if (!g_config->custom_mouse_cursor)
118+
{
119+
if (m_custom_cursor_last)
120+
{
121+
SDL_Cursor* default_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
122+
if (default_cursor)
123+
SDL_SetCursor(default_cursor);
124+
SDL_FreeCursor(default_cursor);
125+
m_custom_cursor_last = false;
126+
}
127+
return;
128+
}
129+
74130
if (m_state != MouseCursorState::HIDE)
75131
{
76132
int x, y;
77133
Uint32 ispressed = SDL_GetMouseState(&x, &y);
134+
135+
if (g_config->custom_system_cursor && SDL_ShowCursor(SDL_QUERY) == SDL_DISABLE)
136+
{
137+
SDL_ShowCursor(SDL_ENABLE);
138+
}
78139

79140
if (m_mobile_mode)
80141
{
@@ -93,7 +154,8 @@ MouseCursor::draw(DrawingContext& context)
93154

94155
Vector mouse_pos = VideoSystem::current()->get_viewport().to_logical(x, y);
95156

96-
m_sprite->draw(context.color(), mouse_pos, LAYER_GUI + 100);
157+
if (!g_config->custom_system_cursor)
158+
m_sprite->draw(context.color(), mouse_pos, LAYER_GUI + 100);
97159

98160
if (m_icon) {
99161
context.color().draw_surface(m_icon,
@@ -102,4 +164,6 @@ MouseCursor::draw(DrawingContext& context)
102164
LAYER_GUI + 100);
103165
}
104166
}
167+
168+
m_custom_cursor_last = g_config->custom_mouse_cursor;
105169
}

src/gui/mousecursor.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616

1717
#pragma once
1818

19+
#include <SDL_mouse.h>
1920
#include <string>
21+
#include <unordered_map>
2022
#include <vector>
2123

2224
#include "config.h"
@@ -60,6 +62,7 @@ class MouseCursor final : public Currenton<MouseCursor>
6062

6163
private:
6264
void apply_state(MouseCursorState state);
65+
void set_cursor_action(const std::string& action);
6366

6467
private:
6568
MouseCursorState m_state;
@@ -68,6 +71,9 @@ class MouseCursor final : public Currenton<MouseCursor>
6871
int m_x, m_y;
6972
bool m_mobile_mode;
7073
SurfacePtr m_icon;
74+
bool m_custom_cursor_last;
75+
76+
std::unordered_map<std::string, std::shared_ptr<SDL_Cursor>> m_cursors;
7177

7278
private:
7379
MouseCursor(const MouseCursor&) = delete;

src/supertux/gameconfig.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ Config::Config() :
7979
confirmation_dialog(false),
8080
pause_on_focusloss(true),
8181
custom_mouse_cursor(true),
82+
custom_system_cursor(false),
8283
do_release_check(false),
8384
disable_network(true),
8485
custom_title_levels(true),
@@ -152,6 +153,7 @@ Config::load()
152153
config_mapping.get("confirmation_dialog", confirmation_dialog);
153154
config_mapping.get("pause_on_focusloss", pause_on_focusloss);
154155
config_mapping.get("custom_mouse_cursor", custom_mouse_cursor);
156+
config_mapping.get("custom_system_cursor", custom_system_cursor);
155157
config_mapping.get("do_release_check", do_release_check);
156158
config_mapping.get("disable_network", disable_network);
157159
config_mapping.get("custom_title_levels", custom_title_levels);
@@ -373,6 +375,7 @@ Config::save()
373375
writer.write("confirmation_dialog", confirmation_dialog);
374376
writer.write("pause_on_focusloss", pause_on_focusloss);
375377
writer.write("custom_mouse_cursor", custom_mouse_cursor);
378+
writer.write("custom_system_cursor", custom_system_cursor);
376379
writer.write("do_release_check", do_release_check);
377380
writer.write("disable_network", disable_network);
378381
writer.write("custom_title_levels", custom_title_levels);

src/supertux/gameconfig.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ class Config final
111111
bool confirmation_dialog;
112112
bool pause_on_focusloss;
113113
bool custom_mouse_cursor;
114+
bool custom_system_cursor;
114115
bool do_release_check;
115116
bool disable_network;
116117
bool custom_title_levels;

src/supertux/main.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,8 @@ Main::init_video()
465465
SDLSurfacePtr icon = SDLSurface::from_file(icon_fname);
466466
VideoSystem::current()->set_icon(*icon);
467467

468-
SDL_ShowCursor(g_config->custom_mouse_cursor ? 0 : 1);
468+
SDL_ShowCursor(
469+
(g_config->custom_mouse_cursor && !g_config->custom_system_cursor) ? SDL_DISABLE : SDL_ENABLE);
469470

470471
log_info << (g_config->use_fullscreen?"fullscreen ":"window ")
471472
<< " Window: " << g_config->window_size

src/supertux/menu/options_menu.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,8 @@ OptionsMenu::refresh()
227227
.set_help(_("Automatically pause the game when the window loses focus"));
228228

229229
add_toggle(MNID_CUSTOM_CURSOR, _("Use custom mouse cursor"), &g_config->custom_mouse_cursor).set_help(_("Whether the game renders its own cursor or uses the system's cursor"));
230+
231+
add_toggle(MNID_CUSTOM_CURSOR, _("Use native custom cursor"), &g_config->custom_system_cursor).set_help(_("Whether the game uses a native custom cursor or renders it in the game"));
230232

231233
#ifndef __EMSCRIPTEN__
232234
if (!g_config->disable_network)

0 commit comments

Comments
 (0)