The Battle for Wesnoth  1.15.0-dev
controller_base.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2018 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
3  wesnoth playlevel Copyright (C) 2003 by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project http://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 #include "controller_base.hpp"
17 
18 #include "display.hpp"
19 #include "events.hpp"
20 #include "gui/core/event/handler.hpp" // gui2::is_in_dialog
24 #include "log.hpp"
25 #include "map/map.hpp"
26 #include "mouse_handler_base.hpp"
27 #include "preferences/game.hpp"
29 #include "soundsource.hpp"
30 
31 static lg::log_domain log_display("display");
32 #define ERR_DP LOG_STREAM(err, log_display)
33 
35  : events::sdl_handler(false)
36  , game_config_(game_config)
37  , key_()
38  , scrolling_(false)
39  , scroll_up_(false)
40  , scroll_down_(false)
41  , scroll_left_(false)
42  , scroll_right_(false)
43  , key_release_listener_(*this)
44 {
45 }
46 
48 {
49 }
50 
51 void controller_base::handle_event(const SDL_Event& event)
52 {
53  /* TODO: since GUI2 and the main game are now part of the same event context, there is some conflict
54  * between the GUI2 and event handlers such as these. By design, the GUI2 sdl handler is always on top
55  * of the handler queue, so its events are handled last. This means events here have a chance to fire
56  * first. have_keyboard_focus currently returns false if a dialog open, but this is just as stopgap
57  * measure. We need to figure out a better way to filter out events.
58  */
59  //if(gui2::is_in_dialog()) {
60  // return;
61  //}
62 
64  return;
65  }
66 
68 
69  switch(event.type) {
70  case SDL_TEXTINPUT:
71  // TODO: remove, not necessary anymore
72  if(have_keyboard_focus()) {
74  }
75  break;
76 
77  case SDL_TEXTEDITING:
78  if(have_keyboard_focus()) {
79  SDL_Event evt = event;
80  evt.type = SDL_TEXTINPUT;
82  SDL_StopTextInput();
83  SDL_StartTextInput();
84  }
85  break;
86 
87  case SDL_KEYDOWN:
88  // Detect key press events, unless there something that has keyboard focus
89  // in which case the key press events should go only to it.
90  if(have_keyboard_focus()) {
91  if(event.key.keysym.sym == SDLK_ESCAPE) {
93  break;
94  }
95 
96  process_keydown_event(event);
98  process_keyup_event(event);
99  }
100  break;
101 
102  case SDL_KEYUP:
103  process_keyup_event(event);
105  break;
106 
107  case SDL_JOYBUTTONDOWN:
108  process_keydown_event(event);
110  break;
111 
112  case SDL_JOYHATMOTION:
113  process_keydown_event(event);
115  break;
116 
117  case SDL_MOUSEMOTION:
118  // Ignore old mouse motion events in the event queue
119  SDL_Event new_event;
120  if(SDL_PeepEvents(&new_event, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION) > 0) {
121  while(SDL_PeepEvents(&new_event, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION) > 0) {
122  };
123  mh_base.mouse_motion_event(new_event.motion, is_browsing());
124  } else {
125  mh_base.mouse_motion_event(event.motion, is_browsing());
126  }
127  break;
128 
129  case SDL_MOUSEBUTTONDOWN:
130  process_keydown_event(event);
131  mh_base.mouse_press(event.button, is_browsing());
133  break;
134 
135  case SDL_MOUSEBUTTONUP:
136  mh_base.mouse_press(event.button, is_browsing());
137  if(mh_base.get_show_menu()) {
138  show_menu(get_display().get_theme().context_menu()->items(), event.button.x, event.button.y, true,
139  get_display());
140  }
141  break;
142 
143  case SDL_MOUSEWHEEL:
144 #if defined(_WIN32) || defined(__APPLE__)
145  mh_base.mouse_wheel(-event.wheel.x, event.wheel.y, is_browsing());
146 #else
147  mh_base.mouse_wheel(event.wheel.x, event.wheel.y, is_browsing());
148 #endif
149  break;
150 
151  default:
152  break;
153  }
154 }
155 
157 {
158  if(gui2::is_in_dialog()) {
159  return;
160  }
161 
163 }
164 
166 {
167  if(event.type == SDL_KEYUP) {
168  hotkey::keyup_event(event, controller_.get_hotkey_command_executor());
169  }
170 }
171 
173 {
174  return !gui2::is_in_dialog();
175 }
176 
177 bool controller_base::handle_scroll(int mousex, int mousey, int mouse_flags)
178 {
179  const bool mouse_in_window =
180  CVideo::get_singleton().window_has_flags(SDL_WINDOW_MOUSE_FOCUS)
181  || preferences::get("scroll_when_mouse_outside", true);
182 
184  int dx = 0, dy = 0;
185 
186  int scroll_threshold = preferences::mouse_scroll_enabled()
188  : 0;
189 
190  for(const theme::menu& m : get_display().get_theme().menus()) {
191  if(sdl::point_in_rect(mousex, mousey, m.get_location())) {
192  scroll_threshold = 0;
193  }
194  }
195 
196  // Apply keyboard scrolling
197  dy -= scroll_up_ * scroll_speed;
198  dy += scroll_down_ * scroll_speed;
199  dx -= scroll_left_ * scroll_speed;
200  dx += scroll_right_ * scroll_speed;
201 
202  // Scroll if mouse is placed near the edge of the screen
203  if(mouse_in_window) {
204  if(mousey < scroll_threshold) {
205  dy -= scroll_speed;
206  }
207 
208  if(mousey > get_display().video().get_height() - scroll_threshold) {
209  dy += scroll_speed;
210  }
211 
212  if(mousex < scroll_threshold) {
213  dx -= scroll_speed;
214  }
215 
216  if(mousex > get_display().video().get_width() - scroll_threshold) {
217  dx += scroll_speed;
218  }
219  }
220 
222 
223  // Scroll with middle-mouse if enabled
224  if((mouse_flags & SDL_BUTTON_MMASK) != 0 && preferences::middle_click_scrolls()) {
225  const SDL_Point original_loc = mh_base.get_scroll_start();
226 
227  if(mh_base.scroll_started()) {
228  const SDL_Rect& rect = get_display().map_outside_area();
229 
230  if(sdl::point_in_rect(mousex, mousey, rect) && mh_base.scroll_started()) {
231  // Scroll speed is proportional from the distance from the first
232  // middle click and scrolling speed preference.
233  const double speed = 0.04 * std::sqrt(static_cast<double>(scroll_speed));
234  const double snap_dist = 16; // Snap to horizontal/vertical scrolling
235  const double x_diff = (mousex - original_loc.x);
236  const double y_diff = (mousey - original_loc.y);
237 
238  if(std::fabs(x_diff) > snap_dist || std::fabs(y_diff) <= snap_dist) {
239  dx += speed * x_diff;
240  }
241 
242  if(std::fabs(y_diff) > snap_dist || std::fabs(x_diff) <= snap_dist) {
243  dy += speed * y_diff;
244  }
245  }
246  } else { // Event may fire mouse down out of order with respect to initial click
247  mh_base.set_scroll_start(mousex, mousey);
248  }
249  }
250 
251  return get_display().scroll(dx, dy);
252 }
253 
254 void controller_base::play_slice(bool is_delay_enabled)
255 {
256  CKey key;
257 
259  l->play_slice();
260  }
261 
264 
265  // Update sound sources before scrolling
267  l->update();
268  }
269 
270 #if 0
271  const theme::menu* const m = get_display().menu_pressed();
272  if(m != nullptr) {
273  const SDL_Rect& menu_loc = m->location(get_display().video().screen_area());
274  show_menu(m->items(), menu_loc.x + 1, menu_loc.y + menu_loc.h + 1, false, get_display());
275 
276  return;
277  }
278 
279  const theme::action* const a = get_display().action_pressed();
280  if(a != nullptr) {
281  const SDL_Rect& action_loc = a->location(get_display().video().screen_area());
282  execute_action(a->items(), action_loc.x + 1, action_loc.y + action_loc.h + 1, false);
283 
284  return;
285  }
286 #endif
287 
288  auto str_vec = additional_actions_pressed();
289  if(!str_vec.empty()) {
290  execute_action(str_vec, 0, 0, false);
291  return;
292  }
293 
294  bool was_scrolling = scrolling_;
295 
296  int mousex, mousey;
297  uint8_t mouse_flags = SDL_GetMouseState(&mousex, &mousey);
298 
299  scrolling_ = handle_scroll(mousex, mousey, mouse_flags);
300 
301  map_location highlighted_hex = get_display().mouseover_hex();
302 
303  // be nice when window is not visible
304  // NOTE should be handled by display instead, to only disable drawing
305  if(is_delay_enabled && !CVideo::get_singleton().window_has_flags(SDL_WINDOW_SHOWN)) {
306  CVideo::delay(200);
307  }
308 
309  // Scrolling ended, update the cursor and the brightened hex
310  if(!scrolling_ && was_scrolling) {
311  get_mouse_handler_base().mouse_update(is_browsing(), highlighted_hex);
312  }
313 }
314 
316  const std::vector<config>& items_arg, int xloc, int yloc, bool context_menu, display& disp)
317 {
319  if(!cmd_exec) {
320  return;
321  }
322 
323  std::vector<config> items;
324  for(const config& c : items_arg) {
325  const std::string& id = c["id"];
327 
328  if(cmd_exec->can_execute_command(command) && (!context_menu || in_context_menu(command.id))) {
329  items.emplace_back("id", id);
330  }
331  }
332 
333  if(items.empty()) {
334  return;
335  }
336 
337  cmd_exec->show_menu(items, xloc, yloc, context_menu, disp);
338 }
339 
340 void controller_base::execute_action(const std::vector<std::string>& items_arg, int xloc, int yloc, bool context_menu)
341 {
343  if(!cmd_exec) {
344  return;
345  }
346 
347  std::vector<std::string> items;
348  for(const std::string& item : items_arg) {
349  const hotkey::hotkey_command& command = hotkey::get_hotkey_command(item);
350  if(cmd_exec->can_execute_command(command)) {
351  items.push_back(item);
352  }
353  }
354 
355  if(items.empty()) {
356  return;
357  }
358 
359  cmd_exec->execute_action(items, xloc, yloc, context_menu, get_display());
360 }
361 
363 {
364  return true;
365 }
366 
367 const config& controller_base::get_theme(const config& game_config, std::string theme_name)
368 {
369  if(theme_name.empty()) {
370  theme_name = preferences::theme();
371  }
372 
373  if(const config& c = game_config.find_child("theme", "id", theme_name)) {
374  return c;
375  }
376 
377  ERR_DP << "Theme '" << theme_name << "' not found. Trying the default theme." << std::endl;
378 
379  if(const config& c = game_config.find_child("theme", "id", "Default")) {
380  return c;
381  }
382 
383  ERR_DP << "Default theme not found." << std::endl;
384 
385  static config empty;
386  return empty;
387 }
virtual bool in_context_menu(hotkey::HOTKEY_COMMAND command) const
static const config & get_theme(const config &game_config, std::string theme_name)
const map_location & mouseover_hex() const
Definition: display.hpp:358
virtual plugins_context * get_plugins_context()
Get (optionally) a plugins context a derived class uses.
SDL_Rect & location(const SDL_Rect &screen) const
Definition: theme.cpp:312
const SDL_Point get_scroll_start() const
virtual void process_keyup_event(const SDL_Event &)
Process keyup (always).
HOTKEY_COMMAND id
the names are strange: the "hotkey::HOTKEY_COMMAND" is named id, and the string to identify the objec...
void set_scroll_start(int x, int y)
Called when the middle click scrolling.
int mouse_scroll_threshold()
Gets the threshold for when to scroll.
Definition: general.cpp:752
static lg::log_domain log_display("display")
virtual void show_menu(const std::vector< config > &items_arg, int xloc, int yloc, bool context_menu, display &disp)
config & find_child(config_key_type key, const std::string &name, const std::string &value)
Returns the first child of tag key with a name attribute containing value.
Definition: config.cpp:789
#define a
int scroll_speed()
Definition: general.cpp:723
bool is_in_dialog()
Is a dialog open?
Definition: handler.cpp:977
const SDL_Rect map_outside_area() const
Returns the available area for a map, this may differ from the above.
Definition: display.cpp:477
Stores all information related to functions that can be bound to hotkeys.
virtual void play_slice(bool is_delay_enabled=true)
static CVideo & get_singleton()
Definition: video.hpp:48
void mbutton_event(const SDL_Event &event, command_executor *executor)
virtual display & get_display()=0
Get a reference to a display member a derived class uses.
const std::vector< std::string > items
void mouse_motion_event(const SDL_MouseMotionEvent &event, const bool browse)
controller_base framework: controller_base is roughly analogous to a "dialog" class in a GUI toolkit ...
virtual bool have_keyboard_focus()
Derived classes should override this to return false when arrow keys should not scroll the map...
controller_base(const config &game_config)
virtual bool can_execute_command(const hotkey_command &command, int index=-1) const =0
virtual std::vector< std::string > additional_actions_pressed()
void run_events(command_executor *executor)
virtual void mouse_press(const SDL_MouseButtonEvent &event, const bool browse)
virtual soundsource::manager * get_soundsource_man()
Get (optionally) a soundsources manager a derived class uses.
virtual bool is_browsing() const
std::string get(const std::string &key)
Definition: general.cpp:230
void jbutton_event(const SDL_Event &event, command_executor *executor)
std::string theme()
Definition: game.cpp:819
void handle_event(const SDL_Event &event) override
virtual void show_menu(const std::vector< config > &items_arg, int xloc, int yloc, bool context_menu, display &gui)
void keyup_event(const SDL_Event &, command_executor *executor)
void handle_event(const SDL_Event &event) override
Process mouse- and keypress-events from SDL.
map_display and display: classes which take care of displaying the map and game-data on the screen...
int get_width(bool as_pixels=true) const
Returns the window renderer width in pixels or screen coordinates.
Definition: video.cpp:222
const std::vector< std::string > & items() const
Definition: theme.hpp:177
virtual ~controller_base()
void jhat_event(const SDL_Event &event, command_executor *executor)
virtual events::mouse_handler_base & get_mouse_handler_base()=0
Get a reference to a mouse handler member a derived class uses.
bool point_in_rect(int x, int y, const SDL_Rect &rect)
Tests whether a point is inside a rectangle.
Definition: rect.cpp:22
SDL_Rect rect
The coordinates of this image on the spritesheet.
virtual void process(events::pump_info &) override
virtual void execute_action(const std::vector< std::string > &items_arg, int xloc, int yloc, bool context_menu)
Encapsulates the map of the game.
Definition: location.hpp:42
virtual void process_keydown_event(const SDL_Event &)
Process keydown (always).
void raise_process_event()
Definition: events.cpp:629
const std::vector< config > & items() const
Definition: theme.hpp:230
Game configuration data as global variables.
Definition: build_info.cpp:46
bool middle_click_scrolls()
Definition: general.cpp:737
void execute_action(const std::vector< std::string > &items_arg, int xloc, int yloc, bool context_menu, display &gui)
virtual void mouse_wheel(int xscroll, int yscroll, bool browse)
Called when scrolling with the mouse wheel.
int get_height(bool as_pixels=true) const
Returns the window renderer height in pixels or in screen coordinates.
Definition: video.cpp:227
void key_event(const SDL_Event &event, command_executor *executor)
bool handle_scroll(int mousex, int mousey, int mouse_flags)
Handle scrolling by keyboard, joystick and moving mouse near map edges.
Handling of system events.
Definition: manager.hpp:41
virtual hotkey::command_executor * get_hotkey_command_executor()
Optionally get a command executor to handle context menu events.
bool mouse_scroll_enabled()
Definition: general.cpp:742
Standard logging facilities (interface).
void run_event_loop()
Definition: events.cpp:420
CVideo & video()
Gets the underlying screen object.
Definition: display.hpp:237
static void delay(unsigned int milliseconds)
Waits a given number of milliseconds before returning.
Definition: video.cpp:232
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
Class that keeps track of all the keys on the keyboard.
Definition: key.hpp:27
mock_char c
const hotkey_command & get_hotkey_command(const std::string &command)
returns the hotkey_command with the given name
void mouse_update(const bool browse, map_location loc)
Update the mouse with a fake mouse motion.
bool scroll(int xmov, int ymov, bool force=false)
Scrolls the display by xmov,ymov pixels.
Definition: display.cpp:955
#define ERR_DP
bool window_has_flags(uint32_t flags) const
Tests whether the given flags are currently set on the SDL window.
Definition: video.cpp:332