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 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 #include "controller_base.hpp"
17 
18 #include "display.hpp"
19 #include "events.hpp"
20 #include "game_config_manager.hpp"
21 #include "gui/core/event/handler.hpp" // gui2::is_in_dialog
25 #include "log.hpp"
26 #include "map/map.hpp"
27 #include "mouse_handler_base.hpp"
28 #include "preferences/game.hpp"
30 #include "soundsource.hpp"
31 
32 static lg::log_domain log_display("display");
33 #define ERR_DP LOG_STREAM(err, log_display)
34 
36  : events::sdl_handler(false)
37  , game_config_(game_config_manager::get()->game_config())
38  , key_()
39  , scrolling_(false)
40  , scroll_up_(false)
41  , scroll_down_(false)
42  , scroll_left_(false)
43  , scroll_right_(false)
44  , key_release_listener_(*this)
45 {
46 }
47 
49 {
50 }
51 
52 void controller_base::handle_event(const SDL_Event& event)
53 {
54  /* TODO: since GUI2 and the main game are now part of the same event context, there is some conflict
55  * between the GUI2 and event handlers such as these. By design, the GUI2 sdl handler is always on top
56  * of the handler queue, so its events are handled last. This means events here have a chance to fire
57  * first. have_keyboard_focus currently returns false if a dialog open, but this is just as stopgap
58  * measure. We need to figure out a better way to filter out events.
59  */
60  //if(gui2::is_in_dialog()) {
61  // return;
62  //}
63 
65  return;
66  }
67 
69 
70  switch(event.type) {
71  case SDL_TEXTINPUT:
72  // TODO: remove, not necessary anymore
73  if(have_keyboard_focus()) {
75  }
76  break;
77 
78  case SDL_TEXTEDITING:
79  if(have_keyboard_focus()) {
80  SDL_Event evt = event;
81  evt.type = SDL_TEXTINPUT;
83  SDL_StopTextInput();
84  SDL_StartTextInput();
85  }
86  break;
87 
88  case SDL_KEYDOWN:
89  // Detect key press events, unless there something that has keyboard focus
90  // in which case the key press events should go only to it.
91  if(have_keyboard_focus()) {
92  if(event.key.keysym.sym == SDLK_ESCAPE) {
94  break;
95  }
96 
97  process_keydown_event(event);
99  process_keyup_event(event);
100  }
101  break;
102 
103  case SDL_KEYUP:
104  process_keyup_event(event);
106  break;
107 
108  case SDL_JOYBUTTONDOWN:
109  process_keydown_event(event);
111  break;
112 
113  case SDL_JOYHATMOTION:
114  process_keydown_event(event);
116  break;
117 
118  case SDL_MOUSEMOTION:
119  // Ignore old mouse motion events in the event queue
120  SDL_Event new_event;
121  if(SDL_PeepEvents(&new_event, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION) > 0) {
122  while(SDL_PeepEvents(&new_event, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION) > 0) {
123  };
124  mh_base.mouse_motion_event(new_event.motion, is_browsing());
125  } else {
126  mh_base.mouse_motion_event(event.motion, is_browsing());
127  }
128  break;
129 
130  case SDL_MOUSEBUTTONDOWN:
131  process_keydown_event(event);
132  mh_base.mouse_press(event.button, is_browsing());
134  break;
135 
136  case SDL_MOUSEBUTTONUP:
137  mh_base.mouse_press(event.button, is_browsing());
138  if(mh_base.get_show_menu()) {
139  show_menu(get_display().get_theme().context_menu()->items(), event.button.x, event.button.y, true,
140  get_display());
141  }
142  break;
143 
144  case SDL_MOUSEWHEEL:
145 #if defined(_WIN32) || defined(__APPLE__)
146  mh_base.mouse_wheel(-event.wheel.x, event.wheel.y, is_browsing());
147 #else
148  mh_base.mouse_wheel(event.wheel.x, event.wheel.y, is_browsing());
149 #endif
150  break;
151 
152  default:
153  break;
154  }
155 }
156 
158 {
159  if(gui2::is_in_dialog()) {
160  return;
161  }
162 
164 }
165 
167 {
168  if(event.type == SDL_KEYUP) {
169  hotkey::keyup_event(event, controller_.get_hotkey_command_executor());
170  }
171 }
172 
174 {
175  return !gui2::is_in_dialog();
176 }
177 
178 bool controller_base::handle_scroll(int mousex, int mousey, int mouse_flags)
179 {
180  const bool mouse_in_window =
181  CVideo::get_singleton().window_has_flags(SDL_WINDOW_MOUSE_FOCUS)
182  || preferences::get("scroll_when_mouse_outside", true);
183 
185  int dx = 0, dy = 0;
186 
187  int scroll_threshold = preferences::mouse_scroll_enabled()
189  : 0;
190 
191  for(const theme::menu& m : get_display().get_theme().menus()) {
192  if(sdl::point_in_rect(mousex, mousey, m.get_location())) {
193  scroll_threshold = 0;
194  }
195  }
196 
197  // Apply keyboard scrolling
198  dy -= scroll_up_ * scroll_speed;
199  dy += scroll_down_ * scroll_speed;
200  dx -= scroll_left_ * scroll_speed;
201  dx += scroll_right_ * scroll_speed;
202 
203  // Scroll if mouse is placed near the edge of the screen
204  if(mouse_in_window) {
205  if(mousey < scroll_threshold) {
206  dy -= scroll_speed;
207  }
208 
209  if(mousey > get_display().video().get_height() - scroll_threshold) {
210  dy += scroll_speed;
211  }
212 
213  if(mousex < scroll_threshold) {
214  dx -= scroll_speed;
215  }
216 
217  if(mousex > get_display().video().get_width() - scroll_threshold) {
218  dx += scroll_speed;
219  }
220  }
221 
223 
224  // Scroll with middle-mouse if enabled
225  if((mouse_flags & SDL_BUTTON_MMASK) != 0 && preferences::middle_click_scrolls()) {
226  const SDL_Point original_loc = mh_base.get_scroll_start();
227 
228  if(mh_base.scroll_started()) {
229  const SDL_Rect& rect = get_display().map_outside_area();
230 
231  if(sdl::point_in_rect(mousex, mousey, rect) && mh_base.scroll_started()) {
232  // Scroll speed is proportional from the distance from the first
233  // middle click and scrolling speed preference.
234  const double speed = 0.04 * std::sqrt(static_cast<double>(scroll_speed));
235  const double snap_dist = 16; // Snap to horizontal/vertical scrolling
236  const double x_diff = (mousex - original_loc.x);
237  const double y_diff = (mousey - original_loc.y);
238 
239  if(std::fabs(x_diff) > snap_dist || std::fabs(y_diff) <= snap_dist) {
240  dx += speed * x_diff;
241  }
242 
243  if(std::fabs(y_diff) > snap_dist || std::fabs(x_diff) <= snap_dist) {
244  dy += speed * y_diff;
245  }
246  }
247  } else { // Event may fire mouse down out of order with respect to initial click
248  mh_base.set_scroll_start(mousex, mousey);
249  }
250  }
251 
252  return get_display().scroll(dx, dy);
253 }
254 
255 void controller_base::play_slice(bool is_delay_enabled)
256 {
257  CKey key;
258 
260  l->play_slice();
261  }
262 
265 
266  // Update sound sources before scrolling
268  l->update();
269  }
270 
271 #if 0
272  const theme::menu* const m = get_display().menu_pressed();
273  if(m != nullptr) {
274  const SDL_Rect& menu_loc = m->location(get_display().video().screen_area());
275  show_menu(m->items(), menu_loc.x + 1, menu_loc.y + menu_loc.h + 1, false, get_display());
276 
277  return;
278  }
279 
280  const theme::action* const a = get_display().action_pressed();
281  if(a != nullptr) {
282  const SDL_Rect& action_loc = a->location(get_display().video().screen_area());
283  execute_action(a->items(), action_loc.x + 1, action_loc.y + action_loc.h + 1, false);
284 
285  return;
286  }
287 #endif
288 
289  auto str_vec = additional_actions_pressed();
290  if(!str_vec.empty()) {
291  execute_action(str_vec, 0, 0, false);
292  return;
293  }
294 
295  bool was_scrolling = scrolling_;
296 
297  int mousex, mousey;
298  uint8_t mouse_flags = SDL_GetMouseState(&mousex, &mousey);
299 
300  scrolling_ = handle_scroll(mousex, mousey, mouse_flags);
301 
302  map_location highlighted_hex = get_display().mouseover_hex();
303 
304  // be nice when window is not visible
305  // NOTE should be handled by display instead, to only disable drawing
306  if(is_delay_enabled && !CVideo::get_singleton().window_has_flags(SDL_WINDOW_SHOWN)) {
307  CVideo::delay(200);
308  }
309 
310  // Scrolling ended, update the cursor and the brightened hex
311  if(!scrolling_ && was_scrolling) {
312  get_mouse_handler_base().mouse_update(is_browsing(), highlighted_hex);
313  }
314 }
315 
317  const std::vector<config>& items_arg, int xloc, int yloc, bool context_menu, display& disp)
318 {
320  if(!cmd_exec) {
321  return;
322  }
323 
324  std::vector<config> items;
325  for(const config& c : items_arg) {
326  const std::string& id = c["id"];
328 
329  if(cmd_exec->can_execute_command(command) && (!context_menu || in_context_menu(command.id))) {
330  items.emplace_back("id", id);
331  }
332  }
333 
334  if(items.empty()) {
335  return;
336  }
337 
338  cmd_exec->show_menu(items, xloc, yloc, context_menu, disp);
339 }
340 
341 void controller_base::execute_action(const std::vector<std::string>& items_arg, int xloc, int yloc, bool context_menu)
342 {
344  if(!cmd_exec) {
345  return;
346  }
347 
348  std::vector<std::string> items;
349  for(const std::string& item : items_arg) {
350  const hotkey::hotkey_command& command = hotkey::get_hotkey_command(item);
351  if(cmd_exec->can_execute_command(command)) {
352  items.push_back(item);
353  }
354  }
355 
356  if(items.empty()) {
357  return;
358  }
359 
360  cmd_exec->execute_action(items, xloc, yloc, context_menu, get_display());
361 }
362 
364 {
365  return true;
366 }
367 
368 const config& controller_base::get_theme(const config& game_config, std::string theme_name)
369 {
370  if(theme_name.empty()) {
371  theme_name = preferences::theme();
372  }
373 
374  if(const config& c = game_config.find_child("theme", "id", theme_name)) {
375  return c;
376  }
377 
378  ERR_DP << "Theme '" << theme_name << "' not found. Trying the default theme." << std::endl;
379 
380  if(const config& c = game_config.find_child("theme", "id", "Default")) {
381  return c;
382  }
383 
384  ERR_DP << "Default theme not found." << std::endl;
385 
386  static config empty;
387  return empty;
388 }
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:475
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...
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:816
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:225
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:642
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)
CURSOR_TYPE get()
Definition: cursor.cpp:213
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:230
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:436
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:235
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:952
#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:335