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