The Battle for Wesnoth  1.13.11+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 - 2018 by David White <dave@whitevine.net>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Support for different cursors-shapes.
18  */
19 
20 #include "cursor.hpp"
21 
22 #include "preferences/game.hpp"
23 #include "image.hpp"
24 #include "preferences/display.hpp"
25 #include "sdl/rect.hpp"
26 
27 #include <iostream>
28 #include <boost/logic/tribool.hpp>
29 using boost::logic::tribool;
30 using boost::logic::indeterminate;
31 
32 static bool use_color_cursors()
33 {
35 }
36 
37 static SDL_Cursor* create_cursor(surface surf)
38 {
39  const surface nsurf(make_neutral_surface(surf));
40  if(nsurf == nullptr) {
41  return nullptr;
42  }
43 
44  // The width must be a multiple of 8 (SDL requirement)
45 
46 #ifdef __APPLE__
47  size_t cursor_width = 16;
48 #else
49  size_t cursor_width = nsurf->w;
50  if((cursor_width%8) != 0) {
51  cursor_width += 8 - (cursor_width%8);
52  }
53 #endif
54  std::vector<uint8_t> data((cursor_width*nsurf->h)/8,0);
55  std::vector<uint8_t> mask(data.size(),0);
56 
57  // See http://sdldoc.csn.ul.ie/sdlcreatecursor.php for documentation
58  // on the format that data has to be in to pass to SDL_CreateCursor
59  const_surface_lock lock(nsurf);
60  const uint32_t* const pixels = lock.pixels();
61  for(int y = 0; y != nsurf->h; ++y) {
62  for(int x = 0; x != nsurf->w; ++x) {
63 
64  if (static_cast<size_t>(x) < cursor_width) {
65  uint8_t r,g,b,a;
66  SDL_GetRGBA(pixels[y*nsurf->w + x],nsurf->format,&r,&g,&b,&a);
67 
68  const size_t index = y*cursor_width + x;
69  const size_t shift = 7 - (index % 8);
70 
71  const uint8_t trans = (a < 128 ? 0 : 1) << shift;
72  const uint8_t black = (trans == 0 || (r+g + b) / 3 > 128 ? 0 : 1) << shift;
73 
74  data[index/8] |= black;
75  mask[index/8] |= trans;
76  }
77  }
78  }
79 
80  return SDL_CreateCursor(&data[0],&mask[0],cursor_width,nsurf->h,0,0);
81 }
82 
83 namespace {
84 
85 SDL_Cursor* cache[cursor::NUM_CURSORS] { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
86 tribool cache_color[cursor::NUM_CURSORS] {
87  indeterminate, indeterminate, indeterminate, indeterminate,
88  indeterminate, indeterminate, indeterminate, indeterminate,
89 };
90 
91 // This array must have members corresponding to cursor::CURSOR_TYPE enum members
92 // Apple need 16x16 b&w cursors
93 #ifdef __APPLE__
94 const std::string bw_images[cursor::NUM_CURSORS] { "normal.png", "wait-alt.png", "move.png", "attack.png", "select.png", "move_drag_alt.png" , "attack_drag_alt.png", "no_cursor.png"};
95 #else
96 const std::string bw_images[cursor::NUM_CURSORS] { "normal.png", "wait.png", "move.png", "attack.png", "select.png", "move_drag.png", "attack_drag.png", "no_cursor.png"};
97 #endif
98 
99 const std::string color_images[cursor::NUM_CURSORS] { "normal.png", "wait.png", "move.png", "attack.png", "select.png", "move_drag.png", "attack_drag.png", ""};
100 
101 // Position of the hotspot of the cursor, from the normal topleft
102 // These are only for the color cursors
103 const int shift_x[cursor::NUM_CURSORS] {0, 0, 0, 0, 0, 2, 3, 0};
104 const int shift_y[cursor::NUM_CURSORS] {0, 0, 0, 0, 0, 20, 22, 0};
105 
106 cursor::CURSOR_TYPE current_cursor = cursor::NORMAL;
107 
108 bool have_focus = true;
109 
110 }
111 
113 {
114  bool is_color = use_color_cursors();
115  if(cache[type] == nullptr || indeterminate(cache_color[type]) || cache_color[type] != is_color) {
116  const std::string prefix = is_color ? "cursors/" : "cursors-bw/";
117  const surface surf(image::get_image(prefix + (is_color ? color_images : bw_images)[type]));
118  if (is_color) {
119  cache[type] = SDL_CreateColorCursor(surf.get(), shift_x[type], shift_y[type]);
120  } else {
121  cache[type] = create_cursor(surf);
122  }
123  cache_color[type] = is_color;
124  }
125 
126  return cache[type];
127 }
128 
129 static void clear_cache()
130 {
131  for(size_t n = 0; n != cursor::NUM_CURSORS; ++n) {
132  if(cache[n] != nullptr) {
133  SDL_FreeCursor(cache[n]);
134  cache[n] = nullptr;
135  }
136  }
137 }
138 
139 namespace cursor
140 {
141 
143 {
144  SDL_ShowCursor(SDL_ENABLE);
145  set();
146 }
147 
149 {
150  clear_cache();
151  SDL_ShowCursor(SDL_ENABLE);
152 }
153 
155 {
156  // Change only if it's a valid cursor
157  if (type != NUM_CURSORS) {
158  current_cursor = type;
159  } else if (current_cursor == NUM_CURSORS) {
160  // Except if the current one is also invalid.
161  // In this case, change to a valid one.
162  current_cursor = NORMAL;
163  }
164 
165  SDL_Cursor * cursor_image = get_cursor(current_cursor);
166 
167  // Causes problem on Mac:
168  //if (cursor_image != nullptr && cursor_image != SDL_GetCursor())
169  SDL_SetCursor(cursor_image);
170 
171  SDL_ShowCursor(SDL_ENABLE);
172 }
173 
174 void set_dragging(bool drag)
175 {
176  switch(current_cursor) {
177  case MOVE:
178  if (drag) cursor::set(MOVE_DRAG);
179  break;
180  case ATTACK:
181  if (drag) cursor::set(ATTACK_DRAG);
182  break;
183  case MOVE_DRAG:
184  if (!drag) cursor::set(MOVE);
185  break;
186  case ATTACK_DRAG:
187  if (!drag) cursor::set(ATTACK);
188  break;
189  default:
190  break;
191  }
192 }
193 
195 {
196  return current_cursor;
197 }
198 
199 void set_focus(bool focus)
200 {
201  have_focus = focus;
202  if (focus==false) {
203  set();
204  }
205 }
206 
207 setter::setter(CURSOR_TYPE type) : old_(current_cursor)
208 {
209  set(type);
210 }
211 
213 {
214  set(old_);
215 }
216 
217 
218 } // end namespace cursor
219 
surface get_image(const image::locator &i_locator, TYPE type)
function to get the surface corresponding to an image.
Definition: image.cpp:920
std::vector< char_t > string
size_t index(const utf8::string &str, const size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:71
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
Definition: cursor.cpp:154
#define a
CURSOR_TYPE
Definition: cursor.hpp:30
#define b
void set_dragging(bool drag)
Definition: cursor.cpp:174
void set_focus(bool focus)
Definition: cursor.cpp:199
SDL_Surface * get() const
Definition: surface.hpp:75
double g
Definition: astarsearch.cpp:64
static tcache cache
Definition: minimap.cpp:134
Helper class for pinning SDL surfaces into memory.
Definition: surface.hpp:128
bool use_color_cursors()
Definition: general.cpp:985
static bool use_color_cursors()
Definition: cursor.cpp:32
static SDL_Cursor * create_cursor(surface surf)
Definition: cursor.cpp:37
Contains the SDL_Rect helper code.
static SDL_Cursor * get_cursor(cursor::CURSOR_TYPE type)
Definition: cursor.cpp:112
static void clear_cache()
Definition: cursor.cpp:129
surface make_neutral_surface(const surface &surf)
Definition: utils.cpp:70
setter(CURSOR_TYPE type)
Definition: cursor.cpp:207
static map_location::DIRECTION n
CURSOR_TYPE old_
Definition: cursor.hpp:48