The Battle for Wesnoth  1.19.28+dev
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 <SDL3/SDL_mouse.h>
30 
31 #include <array>
32 #include <memory>
33 
34 namespace cursor
35 {
36 namespace
37 {
38 struct cursor_data
39 {
40  std::string image_bw;
41  std::string image_color;
42 
43  int hot_x{0};
44  int hot_y{0};
45 
46  boost::tribool is_color{boost::indeterminate};
47 
48  using cursor_ptr = std::unique_ptr<SDL_Cursor, void(*)(SDL_Cursor*)>;
49  cursor_ptr cursor{nullptr, SDL_DestroyCursor};
50 };
51 
52 // Array with each available cursor type.
53 auto available_cursors = std::array
54 {
55  cursor_data{ "normal.png", "normal.png", 0, 0 },
56  cursor_data{ "wait.png", "wait.png", 0, 0 },
57  cursor_data{ "ibeam.png", "ibeam.png", 14, 14 },
58  cursor_data{ "move.png", "move.png", 0, 0 },
59  cursor_data{ "attack.png", "attack.png", 0, 0 },
60  cursor_data{ "select.png", "select.png", 0, 0 },
61  cursor_data{ "move_drag.png", "move_drag.png", 2, 20 },
62  cursor_data{ "attack_drag.png", "attack_drag.png", 3, 22 },
63  cursor_data{ "no_cursor.png", "", 0, 0 }
64 };
65 
66 static_assert(available_cursors.size() == cursor::NUM_CURSORS);
67 
68 cursor::CURSOR_TYPE current_cursor = cursor::NORMAL;
69 bool have_focus = true;
70 
71 bool use_color_cursors()
72 {
73  return game_config::editor == false && prefs::get().use_color_cursors();
74 }
75 
76 SDL_Cursor* create_cursor(const surface& surf)
77 {
78  if(surf == nullptr) {
79  return nullptr;
80  }
81 
82  // The width must be a multiple of 8 (SDL requirement)
83  std::size_t cursor_width = surf->w;
84  if((cursor_width % 8) != 0) {
85  cursor_width += 8 - (cursor_width % 8);
86  }
87 
88  std::vector<uint8_t> data((cursor_width * surf->h) / 8, 0);
89  std::vector<uint8_t> mask(data.size(), 0);
90 
91  // See https://wiki.libsdl.org/SDL_CreateCursor for documentation
92  // on the format that data has to be in to pass to SDL_CreateCursor
94  const utils::span pixels = lock.pixel_span();
95 
96  for(int y = 0; y != surf->h; ++y) {
97  for(int x = 0; x != surf->w; ++x) {
98  if(static_cast<std::size_t>(x) < cursor_width) {
99  uint8_t r, g, b, a;
100  SDL_GetRGBA(pixels[y * surf->w + x], SDL_GetPixelFormatDetails(surf->format), SDL_GetSurfacePalette(surf), &r, &g, &b, &a);
101 
102  const std::size_t index = y * cursor_width + x;
103  const std::size_t shift = 7 - (index % 8);
104 
105  const uint8_t trans = (a < 128 ? 0 : 1) << shift;
106  const uint8_t black = (trans == 0 || (r + g + b) / 3 > 128 ? 0 : 1) << shift;
107 
108  data[index / 8] |= black;
109  mask[index / 8] |= trans;
110  }
111  }
112  }
113 
114  return SDL_CreateCursor(&data[0], &mask[0], cursor_width, surf->h, 0, 0);
115 }
116 
117 SDL_Cursor* get_cursor(cursor::CURSOR_TYPE type)
118 {
119  const bool use_color = use_color_cursors();
120  cursor_data& data = available_cursors[type];
121 
122  if(data.cursor == nullptr || boost::indeterminate(data.is_color) || data.is_color != use_color) {
123  static const std::string color_prefix = "cursors/";
124  static const std::string bw_prefix = "cursors-bw/";
125 
126  if(use_color) {
127  surface surf = image::get_surface(color_prefix + data.image_color);
128  data.cursor.reset(SDL_CreateColorCursor(surf, data.hot_x, data.hot_y));
129  } else {
130  surface surf = image::get_surface(bw_prefix + data.image_bw);
131  data.cursor.reset(create_cursor(surf));
132  }
133 
134  data.is_color = use_color;
135  }
136 
137  return data.cursor.get();
138 }
139 
140 } // end anon namespace
141 
143 {
144  set();
145 }
146 
148 {
149 }
150 
152 {
153  // Change only if it's a valid cursor
154  if(type != NUM_CURSORS) {
155  current_cursor = type;
156  } else if(current_cursor == NUM_CURSORS) {
157  // Except if the current one is also invalid.
158  // In this case, change to a valid one.
159  current_cursor = NORMAL;
160  }
161 
162  SDL_SetCursor(get_cursor(current_cursor));
163 }
164 
165 void set_dragging(bool drag)
166 {
167  switch(current_cursor) {
168  case MOVE:
169  if(drag) cursor::set(MOVE_DRAG);
170  break;
171  case ATTACK:
172  if(drag) cursor::set(ATTACK_DRAG);
173  break;
174  case MOVE_DRAG:
175  if(!drag) cursor::set(MOVE);
176  break;
177  case ATTACK_DRAG:
178  if(!drag) cursor::set(ATTACK);
179  break;
180  default:
181  break;
182  }
183 }
184 
186 {
187  return current_cursor;
188 }
189 
190 void set_focus(bool focus)
191 {
192  have_focus = focus;
193 
194  if(!focus) {
195  set();
196  }
197 }
198 
200  : old_(current_cursor)
201 {
202  set(type);
203 }
204 
206 {
207  set(old_);
208 }
209 
210 } // 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:98
std::string image_bw
Definition: cursor.cpp:40
boost::tribool is_color
Definition: cursor.cpp:46
int hot_x
Definition: cursor.cpp:43
std::string image_color
Definition: cursor.cpp:41
int hot_y
Definition: cursor.cpp:44
CURSOR_TYPE get()
Definition: cursor.cpp:185
void set_dragging(bool drag)
Definition: cursor.cpp:165
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:151
void set_focus(bool focus)
Definition: cursor.cpp:190
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
std::string_view data
Definition: picture.cpp:188
surface surf
Image.
setter(CURSOR_TYPE type)
Definition: cursor.cpp:199
CURSOR_TYPE old_
Definition: cursor.hpp:46
#define b