The Battle for Wesnoth  1.19.10+dev
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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 
27 #include <boost/logic/tribool.hpp>
28 
29 #include <SDL2/SDL_events.h>
30 #include <SDL2/SDL_mouse.h>
31 
32 #include <array>
33 #include <memory>
34 
35 namespace cursor
36 {
37 namespace
38 {
39 using cursor_ptr_t = std::unique_ptr<SDL_Cursor, std::function<void(SDL_Cursor*)>>;
40 
41 struct cursor_data
42 {
43  cursor_ptr_t cursor;
44 
45  boost::tribool is_color;
46 
47  std::string image_bw;
48  std::string image_color;
49 
50  int hot_x;
51  int hot_y;
52 };
53 
54 //
55 // Array with each available cursor type.
56 // macOS needs 16x16 b&w cursors. TODO: is that still the case?
57 //
58 std::array<cursor_data, cursor::NUM_CURSORS> available_cursors {{
59 #ifdef __APPLE__
60  { nullptr, boost::indeterminate, "normal.png", "normal.png", 0, 0 },
61  { nullptr, boost::indeterminate, "wait-alt.png", "wait.png", 0, 0 },
62  { nullptr, boost::indeterminate, "ibeam.png", "ibeam.png", 14, 14 },
63  { nullptr, boost::indeterminate, "move.png", "move.png", 0, 0 },
64  { nullptr, boost::indeterminate, "attack.png", "attack.png", 0, 0 },
65  { nullptr, boost::indeterminate, "select.png", "select.png", 0, 0 },
66  { nullptr, boost::indeterminate, "move_drag_alt.png", "move_drag.png", 2, 20 },
67  { nullptr, boost::indeterminate, "attack_drag_alt.png", "attack_drag.png", 3, 22 },
68  { nullptr, boost::indeterminate, "no_cursor.png", "", 0, 0 }
69 #else
70  { nullptr, boost::indeterminate, "normal.png", "normal.png", 0, 0 },
71  { nullptr, boost::indeterminate, "wait.png", "wait.png", 0, 0 },
72  { nullptr, boost::indeterminate, "ibeam.png", "ibeam.png", 14, 14 },
73  { nullptr, boost::indeterminate, "move.png", "move.png", 0, 0 },
74  { nullptr, boost::indeterminate, "attack.png", "attack.png", 0, 0 },
75  { nullptr, boost::indeterminate, "select.png", "select.png", 0, 0 },
76  { nullptr, boost::indeterminate, "move_drag.png", "move_drag.png", 2, 20 },
77  { nullptr, boost::indeterminate, "attack_drag.png", "attack_drag.png", 3, 22 },
78  { nullptr, boost::indeterminate, "no_cursor.png", "", 0, 0 }
79 
80 #endif
81 }};
82 
83 cursor::CURSOR_TYPE current_cursor = cursor::NORMAL;
84 
85 bool have_focus = true;
86 
87 bool use_color_cursors()
88 {
89  return game_config::editor == false && prefs::get().use_color_cursors();
90 }
91 
92 SDL_Cursor* create_cursor(const surface& surf)
93 {
94  if(surf == nullptr) {
95  return nullptr;
96  }
97 
98  // The width must be a multiple of 8 (SDL requirement)
99 
100 #ifdef __APPLE__
101  std::size_t cursor_width = 16;
102 #else
103  std::size_t cursor_width = surf->w;
104  if((cursor_width % 8) != 0) {
105  cursor_width += 8 - (cursor_width % 8);
106  }
107 #endif
108 
109  std::vector<uint8_t> data((cursor_width * surf->h) / 8, 0);
110  std::vector<uint8_t> mask(data.size(), 0);
111 
112  // See https://wiki.libsdl.org/SDL_CreateCursor for documentation
113  // on the format that data has to be in to pass to SDL_CreateCursor
114  const_surface_lock lock(surf);
115  const uint32_t* const pixels = lock.pixels();
116 
117  for(int y = 0; y != surf->h; ++y) {
118  for(int x = 0; x != surf->w; ++x) {
119  if(static_cast<std::size_t>(x) < cursor_width) {
120  uint8_t r, g, b, a;
121  SDL_GetRGBA(pixels[y * surf->w + x], surf->format, &r, &g, &b, &a);
122 
123  const std::size_t index = y * cursor_width + x;
124  const std::size_t shift = 7 - (index % 8);
125 
126  const uint8_t trans = (a < 128 ? 0 : 1) << shift;
127  const uint8_t black = (trans == 0 || (r + g + b) / 3 > 128 ? 0 : 1) << shift;
128 
129  data[index / 8] |= black;
130  mask[index / 8] |= trans;
131  }
132  }
133  }
134 
135  return SDL_CreateCursor(&data[0], &mask[0], cursor_width, surf->h, 0, 0);
136 }
137 
138 SDL_Cursor* get_cursor(cursor::CURSOR_TYPE type)
139 {
140  const bool use_color = use_color_cursors();
141  cursor_data& data = available_cursors[type];
142 
143  if(data.cursor == nullptr || boost::indeterminate(data.is_color) || data.is_color != use_color) {
144  static const std::string color_prefix = "cursors/";
145  static const std::string bw_prefix = "cursors-bw/";
146 
147  if(use_color) {
148  const surface& surf(image::get_surface(color_prefix + data.image_color));
149 
150  // Construct a temporary ptr to provide a new deleter.
151  data.cursor = cursor_ptr_t(SDL_CreateColorCursor(surf, data.hot_x, data.hot_y), SDL_FreeCursor);
152  } else {
153  const surface& surf(image::get_surface(bw_prefix + data.image_bw));
154 
155  // Construct a temporary ptr to provide a new deleter.
156  data.cursor = cursor_ptr_t(create_cursor(surf), SDL_FreeCursor);
157  }
158 
159  data.is_color = use_color;
160  }
161 
162  return data.cursor.get();
163 }
164 
165 } // end anon namespace
166 
168 {
169  SDL_ShowCursor(SDL_ENABLE);
170  set();
171 }
172 
174 {
175  SDL_ShowCursor(SDL_ENABLE);
176 }
177 
179 {
180  // Change only if it's a valid cursor
181  if(type != NUM_CURSORS) {
182  current_cursor = type;
183  } else if(current_cursor == NUM_CURSORS) {
184  // Except if the current one is also invalid.
185  // In this case, change to a valid one.
186  current_cursor = NORMAL;
187  }
188 
189  SDL_Cursor* cursor_image = get_cursor(current_cursor);
190 
191  // Causes problem on Mac:
192  // if (cursor_image != nullptr && cursor_image != SDL_GetCursor())
193  SDL_SetCursor(cursor_image);
194 
195  SDL_ShowCursor(SDL_ENABLE);
196 }
197 
198 void set_dragging(bool drag)
199 {
200  switch(current_cursor) {
201  case MOVE:
202  if(drag) cursor::set(MOVE_DRAG);
203  break;
204  case ATTACK:
205  if(drag) cursor::set(ATTACK_DRAG);
206  break;
207  case MOVE_DRAG:
208  if(!drag) cursor::set(MOVE);
209  break;
210  case ATTACK_DRAG:
211  if(!drag) cursor::set(ATTACK);
212  break;
213  default:
214  break;
215  }
216 }
217 
219 {
220  return current_cursor;
221 }
222 
223 void set_focus(bool focus)
224 {
225  have_focus = focus;
226 
227  if(!focus) {
228  set();
229  }
230 }
231 
233  : old_(current_cursor)
234 {
235  set(type);
236 }
237 
239 {
240  set(old_);
241 }
242 
243 } // end namespace cursor
double g
Definition: astarsearch.cpp:63
static prefs & get()
bool use_color_cursors()
Helper class for pinning SDL surfaces into memory.
Definition: surface.hpp:71
std::string image_bw
Definition: cursor.cpp:47
boost::tribool is_color
Definition: cursor.cpp:45
cursor_ptr_t cursor
Definition: cursor.cpp:43
int hot_x
Definition: cursor.cpp:50
std::string image_color
Definition: cursor.cpp:48
int hot_y
Definition: cursor.cpp:51
CURSOR_TYPE get()
Definition: cursor.cpp:218
void set_dragging(bool drag)
Definition: cursor.cpp:198
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:178
void set_focus(bool focus)
Definition: cursor.cpp:223
surface get_surface(const image::locator &i_locator, TYPE type, bool skip_cache)
Returns an image surface suitable for software manipulation.
Definition: picture.cpp:668
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
std::string_view data
Definition: picture.cpp:178
surface surf
Image.
setter(CURSOR_TYPE type)
Definition: cursor.cpp:232
CURSOR_TYPE old_
Definition: cursor.hpp:46
constexpr uint32_t black
Definition: test_sdl.cpp:30
#define b