The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
cursor.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2003 - 2025
3 by David White <dave@whitevine.net>
4 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY.
12
13 See the COPYING file for more details.
14*/
15
16/**
17 * @file
18 * Support for different cursors-shapes.
19 */
20
21#include "cursor.hpp"
22
23#include "picture.hpp"
25#include "sdl/utils.hpp"
26#include "video.hpp"
27
28#include <boost/logic/tribool.hpp>
29
30#include <SDL3/SDL_mouse.h>
31
32#include <array>
33#include <memory>
34
35namespace cursor
36{
37namespace
38{
39struct cursor_data
40{
41 std::string image_bw;
42 std::string image_color;
43
44 int hot_x{0};
45 int hot_y{0};
46
47 boost::tribool is_color{boost::indeterminate};
48 int scale{0};
49
50 using cursor_ptr = std::unique_ptr<SDL_Cursor, void(*)(SDL_Cursor*)>;
51 cursor_ptr cursor{nullptr, SDL_DestroyCursor};
52};
53
54// Array with each available cursor type.
55auto available_cursors = std::array
56{
57 cursor_data{ "normal.png", "normal.png", 0, 0 },
58 cursor_data{ "wait.png", "wait.png", 0, 0 },
59 cursor_data{ "ibeam.png", "ibeam.png", 14, 14 },
60 cursor_data{ "move.png", "move.png", 0, 0 },
61 cursor_data{ "attack.png", "attack.png", 2, 2 },
62 cursor_data{ "select.png", "select.png", 10, 2 },
63 cursor_data{ "move_drag.png", "move_drag.png", 2, 20 },
64 cursor_data{ "attack_drag.png", "attack_drag.png", 3, 22 },
65 cursor_data{ "no_cursor.png", "", 0, 0 }
66};
67
68static_assert(available_cursors.size() == cursor::NUM_CURSORS);
69
70cursor::CURSOR_TYPE current_cursor = cursor::NORMAL;
71bool have_focus = true;
72
73bool use_color_cursors()
74{
76}
77
78SDL_Cursor* create_cursor(const surface& surf)
79{
80 if(surf == nullptr) {
81 return nullptr;
82 }
83
84 // The width must be a multiple of 8 (SDL requirement)
85 std::size_t cursor_width = surf->w;
86 if((cursor_width % 8) != 0) {
87 cursor_width += 8 - (cursor_width % 8);
88 }
89
90 std::vector<uint8_t> data((cursor_width * surf->h) / 8, 0);
91 std::vector<uint8_t> mask(data.size(), 0);
92
93 // See https://wiki.libsdl.org/SDL_CreateCursor for documentation
94 // on the format that data has to be in to pass to SDL_CreateCursor
96 const utils::span pixels = lock.pixel_span();
97
98 for(int y = 0; y != surf->h; ++y) {
99 for(int x = 0; x != surf->w; ++x) {
100 if(static_cast<std::size_t>(x) < cursor_width) {
101 uint8_t r, g, b, a;
102 SDL_GetRGBA(pixels[y * surf->w + x], SDL_GetPixelFormatDetails(surf->format), SDL_GetSurfacePalette(surf), &r, &g, &b, &a);
103
104 const std::size_t index = y * cursor_width + x;
105 const std::size_t shift = 7 - (index % 8);
106
107 const uint8_t trans = (a < 128 ? 0 : 1) << shift;
108 const uint8_t black = (trans == 0 || (r + g + b) / 3 > 128 ? 0 : 1) << shift;
109
110 data[index / 8] |= black;
111 mask[index / 8] |= trans;
112 }
113 }
114 }
115
116 return SDL_CreateCursor(&data[0], &mask[0], cursor_width, surf->h, 0, 0);
117}
118
119SDL_Cursor* get_cursor(cursor::CURSOR_TYPE type)
120{
121 const bool use_color = use_color_cursors();
122 const int scale = prefs::get().scale_cursors() ? video::get_pixel_scale() : 1;
123 cursor_data& data = available_cursors[type];
124
125 if(data.cursor == nullptr || boost::indeterminate(data.is_color) || data.is_color != use_color || data.scale != scale) {
126 static const std::string color_prefix = "cursors/";
127 static const std::string bw_prefix = "cursors-bw/";
128
129 if(use_color) {
130 surface surf = image::get_surface(color_prefix + data.image_color);
131 if(surf != nullptr && scale > 1) {
133 }
134 data.cursor.reset(SDL_CreateColorCursor(surf, data.hot_x * scale, data.hot_y * scale));
135 } else {
136 surface surf = image::get_surface(bw_prefix + data.image_bw);
137 if(surf != nullptr && scale > 1) {
139 }
140 data.cursor.reset(create_cursor(surf));
141 }
142
143 data.is_color = use_color;
144 data.scale = scale;
145 }
146
147 return data.cursor.get();
148}
149
150} // end anon namespace
151
153{
154 set();
155}
156
160
161void set(CURSOR_TYPE type)
162{
163 // Change only if it's a valid cursor
164 if(type != NUM_CURSORS) {
165 current_cursor = type;
166 } else if(current_cursor == NUM_CURSORS) {
167 // Except if the current one is also invalid.
168 // In this case, change to a valid one.
169 current_cursor = NORMAL;
170 }
171
172 SDL_SetCursor(get_cursor(current_cursor));
173}
174
175void set_dragging(bool drag)
176{
177 switch(current_cursor) {
178 case MOVE:
179 if(drag) cursor::set(MOVE_DRAG);
180 break;
181 case ATTACK:
182 if(drag) cursor::set(ATTACK_DRAG);
183 break;
184 case MOVE_DRAG:
185 if(!drag) cursor::set(MOVE);
186 break;
187 case ATTACK_DRAG:
188 if(!drag) cursor::set(ATTACK);
189 break;
190 default:
191 break;
192 }
193}
194
196{
197 return current_cursor;
198}
199
200void set_focus(bool focus)
201{
202 have_focus = focus;
203
204 if(!focus) {
205 set();
206 }
207}
208
210 : old_(current_cursor)
211{
212 set(type);
213}
214
216{
217 set(old_);
218}
219
220} // end namespace cursor
static prefs & get()
bool use_color_cursors()
Helper class for pinning SDL surfaces into memory.
Definition surface.hpp:98
std::string image_bw
Definition cursor.cpp:41
boost::tribool is_color
Definition cursor.cpp:47
int scale
Definition cursor.cpp:48
int hot_x
Definition cursor.cpp:44
std::string image_color
Definition cursor.cpp:42
int hot_y
Definition cursor.cpp:45
CURSOR_TYPE get()
Definition cursor.cpp:195
void set_dragging(bool drag)
Definition cursor.cpp:175
CURSOR_TYPE
Definition cursor.hpp:28
@ ATTACK
Definition cursor.hpp:28
@ MOVE_DRAG
Definition cursor.hpp:28
@ NORMAL
Definition cursor.hpp:28
@ ATTACK_DRAG
Definition cursor.hpp:28
@ NUM_CURSORS
Definition cursor.hpp:28
@ MOVE
Definition cursor.hpp:28
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
Definition cursor.cpp:161
void set_focus(bool focus)
Definition cursor.cpp:200
surface get_surface(const image::locator &i_locator, TYPE type, bool skip_cache)
Returns an image surface suitable for software manipulation.
Definition picture.cpp:691
std::size_t index(std::string_view str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition unicode.cpp:70
int get_pixel_scale()
Get the current active pixel scale multiplier.
Definition video.cpp:480
std::string_view data
Definition picture.cpp:188
surface surf
Image.
setter(CURSOR_TYPE type)
Definition cursor.cpp:209
CURSOR_TYPE old_
Definition cursor.hpp:46
surface scale_surface_sharp(const surface &surf, int w, int h)
Scale a surface using modified nearest neighbour algorithm.
Definition utils.cpp:358
#define g
#define b