The Battle for Wesnoth  1.13.11+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
events.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 #include "events.hpp"
16 #include "cursor.hpp"
17 #include "desktop/clipboard.hpp"
18 #include "log.hpp"
19 #include "quit_confirmation.hpp"
20 #include "video.hpp"
21 #include "sdl/userevent.hpp"
22 
23 #if defined _WIN32
25 #endif
26 
27 #include <algorithm>
28 #include <cassert>
29 #include <deque>
30 #include <iterator>
31 #include <utility>
32 #include <vector>
33 
34 #include <SDL.h>
35 
36 #include <boost/range/adaptor/reversed.hpp>
37 #include <boost/thread.hpp>
38 
39 #define ERR_GEN LOG_STREAM(err, lg::general)
40 
41 namespace
42 {
43 struct invoked_function_data
44 {
45  bool finished;
46  const std::function<void(void)>& f;
47 
48  invoked_function_data(bool finished_, const std::function<void(void)>& func)
49  : finished(finished_)
50  , f(func)
51  {
52  }
53 
54  void call()
55  {
56  f();
57  finished = true;
58  }
59 };
60 }
61 
62 namespace events
63 {
65 {
66  /* Add new handlers to the staging list initially.
67  * This ensures that if an event handler adds more handlers, the new handlers
68  * won't be called for the event that caused them to be added.
69  */
70  staging_handlers.push_back(ptr);
71 }
72 
74 {
75  static int depth = 0;
76  ++depth;
77 
78  // The handler is most likely on the back of the events list,
79  // so look there first, otherwise do a complete search.
80  if(!handlers.empty() && handlers.back() == ptr) {
81  if(focused_handler != handlers.end() && *focused_handler == ptr) {
82  focused_handler = handlers.end();
83  }
84 
85  handlers.pop_back();
86  } else {
87  const handler_list::iterator i = std::find(handlers.begin(), handlers.end(), ptr);
88 
89  if(i == handlers.end()) {
90  --depth;
91 
92  // The handler may be in the staging area. Search it from there.
93  auto j = std::find(staging_handlers.begin(), staging_handlers.end(), ptr);
94  if(j != staging_handlers.end()) {
95  staging_handlers.erase(j);
96  return true;
97  } else {
98  return false;
99  }
100  }
101 
102  if(i == focused_handler) {
104  }
105 
106  handlers.erase(i);
107  }
108 
109  --depth;
110 
111  if(depth == 0) {
112  cycle_focus();
113  } else {
114  focused_handler = handlers.end();
115  }
116 
117  return true;
118 }
119 
121 {
122  if(handlers.begin() == handlers.end()) {
123  return;
124  }
125 
128 
129  if(last != handlers.begin()) {
130  --last;
131  }
132 
133  if(current == handlers.end()) {
134  current = handlers.begin();
135  } else {
136  ++current;
137  }
138 
139  while(current != last) {
140  if(current != handlers.end() && (*current)->requires_event_focus()) {
141  focused_handler = current;
142  break;
143  }
144 
145  if(current == handlers.end()) {
146  current = handlers.begin();
147  } else {
148  ++current;
149  }
150  }
151 }
152 
154 {
155  const handler_list::iterator i = std::find(handlers.begin(), handlers.end(), ptr);
156  if(i != handlers.end() && (*i)->requires_event_focus()) {
157  focused_handler = i;
158  }
159 }
160 
162 {
163  std::copy(staging_handlers.begin(), staging_handlers.end(), std::back_inserter(handlers));
164  staging_handlers.clear();
165 }
166 
168 {
169  for(sdl_handler* h : handlers) {
170  if(h->has_joined()) {
171  h->has_joined_ = false;
172  }
173 
174  if(h->has_joined_global()) {
175  h->has_joined_global_ = false;
176  }
177  }
178 }
179 
180 // This object stores all the event handlers. It is a stack of event 'contexts'.
181 // a new event context is created when e.g. a modal dialog is opened, and then
182 // closed when that dialog is closed. Each context contains a list of the handlers
183 // in that context. The current context is the one on the top of the stack.
184 // The global context must always be in the first position.
185 std::deque<context> event_contexts;
186 
187 std::vector<pump_monitor*> pump_monitors;
188 
190 {
191  pump_monitors.push_back(this);
192 }
193 
195 {
196  pump_monitors.erase(std::remove(pump_monitors.begin(), pump_monitors.end(), this), pump_monitors.end());
197 }
198 
200 {
201  event_contexts.emplace_back();
202 }
203 
205 {
206  assert(event_contexts.empty() == false);
207  event_contexts.pop_back();
208 }
209 
210 sdl_handler::sdl_handler(const bool auto_join)
211  : has_joined_(false)
212  , has_joined_global_(false)
213 {
214  if(auto_join) {
215  assert(!event_contexts.empty());
216  event_contexts.back().add_handler(this);
217  has_joined_ = true;
218  }
219 }
220 
222 {
223  if(has_joined_) {
224  leave();
225  }
226 
227  if(has_joined_global_) {
228  leave_global();
229  }
230 }
231 
233 {
234  // this assert will fire if someone will inadvertently try to join
235  // an event context but might end up in the global context instead.
236  assert(&event_contexts.back() != &event_contexts.front());
237 
238  join(event_contexts.back());
239 }
240 
242 {
243  if(has_joined_global_) {
244  leave_global();
245  }
246 
247  if(has_joined_) {
248  leave(); // should not be in multiple event contexts
249  }
250 
251  // join self
252  c.add_handler(this);
253  has_joined_ = true;
254 
255  // instruct members to join
256  for(auto member : handler_members()) {
257  member->join(c);
258  }
259 }
260 
262 {
263  if(has_joined_) {
264  leave(); // should not be in multiple event contexts
265  }
266 
267  for(auto& context : boost::adaptors::reverse(event_contexts)) {
268  handler_list& handlers = context.handlers;
269  if(std::find(handlers.begin(), handlers.end(), parent) != handlers.end()) {
270  join(context);
271  return;
272  }
273  }
274 
275  join(event_contexts.back());
276 }
277 
279 {
281 
282  if(members.empty()) {
283  assert(event_contexts.empty() == false);
284  }
285 
286  for(auto member : members) {
287  member->leave();
288  }
289 
290  for(auto& context : boost::adaptors::reverse(event_contexts)) {
291  if(context.remove_handler(this)) {
292  break;
293  }
294  }
295 
296  has_joined_ = false;
297 }
298 
300 {
301  if(has_joined_) {
302  leave();
303  }
304 
305  if(has_joined_global_) {
306  leave_global(); // Should not be in multiple event contexts
307  }
308 
309  // Join self
310  event_contexts.front().add_handler(this);
311  has_joined_global_ = true;
312 
313  // Instruct members to join
314  for(auto member : handler_members()) {
315  member->join_global();
316  }
317 }
318 
320 {
321  for(auto member : handler_members()) {
322  member->leave_global();
323  }
324 
325  event_contexts.front().remove_handler(this);
326 
327  has_joined_global_ = false;
328 }
329 
330 void focus_handler(const sdl_handler* ptr)
331 {
332  if(event_contexts.empty() == false) {
333  event_contexts.back().set_focus(ptr);
334  }
335 }
336 
337 bool has_focus(const sdl_handler* hand, const SDL_Event* event)
338 {
339  if(event_contexts.empty()) {
340  return true;
341  }
342 
343  if(hand->requires_event_focus(event) == false) {
344  return true;
345  }
346 
347  const handler_list::iterator foc = event_contexts.back().focused_handler;
348  auto& handlers = event_contexts.back().handlers;
349 
350  // If no-one has focus at the moment, this handler obviously wants
351  // focus, so give it to it.
352  if(foc == handlers.end()) {
353  focus_handler(hand);
354  return true;
355  }
356 
357  sdl_handler* const foc_hand = *foc;
358  if(foc_hand == hand) {
359  return true;
360  } else if(!foc_hand->requires_event_focus(event)) {
361  // If the currently focused handler doesn't need focus for this event
362  // allow the most recent interested handler to take care of it
363  for(auto i = handlers.rbegin(); i != handlers.rend(); ++i) {
364  sdl_handler* const thief_hand = *i;
365 
366  if(thief_hand != foc_hand && thief_hand->requires_event_focus(event)) {
367  // Steal focus
368  focus_handler(thief_hand);
369 
370  // Position the previously focused handler to allow stealing back
371  handlers.splice(handlers.end(), handlers, foc);
372 
373  return thief_hand == hand;
374  }
375  }
376  }
377 
378  return false;
379 }
380 
381 const uint32_t resize_timeout = 100;
384 
385 static bool remove_on_resize(const SDL_Event& a)
386 {
387  if(a.type == DRAW_EVENT || a.type == DRAW_ALL_EVENT) {
388  return true;
389  }
390 
391  if(a.type == SHOW_HELPTIP_EVENT) {
392  return true;
393  }
394 
395  if((a.type == SDL_WINDOWEVENT) && (
396  a.window.event == SDL_WINDOWEVENT_RESIZED ||
397  a.window.event == SDL_WINDOWEVENT_SIZE_CHANGED ||
398  a.window.event == SDL_WINDOWEVENT_EXPOSED)
399  ) {
400  return true;
401  }
402 
403  return false;
404 }
405 
406 // TODO: I'm uncertain if this is always safe to call at static init; maybe set in main() instead?
407 static const boost::thread::id main_thread = boost::this_thread::get_id();
408 
409 void pump()
410 {
411  if(boost::this_thread::get_id() != main_thread) {
412  // Can only call this on the main thread!
413  return;
414  }
415 
416  peek_for_resize();
417  pump_info info;
418 
419  // Used to keep track of double click events
420  static int last_mouse_down = -1;
421  static int last_click_x = -1, last_click_y = -1;
422 
423  SDL_Event temp_event;
424  int poll_count = 0;
425  int begin_ignoring = 0;
426 
427  std::vector<SDL_Event> events;
428  while(SDL_PollEvent(&temp_event)) {
429  if(temp_event.type == INVOKE_FUNCTION_EVENT) {
430  static_cast<invoked_function_data*>(temp_event.user.data1)->call();
431  continue;
432  }
433 
434  ++poll_count;
435  peek_for_resize();
436 
437  if(!begin_ignoring && temp_event.type == SDL_WINDOWEVENT && (
438  temp_event.window.event == SDL_WINDOWEVENT_ENTER ||
439  temp_event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED)
440  ) {
441  begin_ignoring = poll_count;
442  } else if(begin_ignoring > 0 && is_input(temp_event)) {
443  // ignore user input events that occurred after the window was activated
444  continue;
445  }
446 
447  events.push_back(temp_event);
448  }
449 
450  std::vector<SDL_Event>::iterator ev_it = events.begin();
451  for(int i = 1; i < begin_ignoring; ++i) {
452  if(is_input(*ev_it)) {
453  // ignore user input events that occurred before the window was activated
454  ev_it = events.erase(ev_it);
455  } else {
456  ++ev_it;
457  }
458  }
459 
460  std::vector<SDL_Event>::iterator ev_end = events.end();
461  bool resize_found = false;
462  for(ev_it = events.begin(); ev_it != ev_end; ++ev_it) {
463  SDL_Event& event = *ev_it;
464  if(event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) {
465  resize_found = true;
466  last_resize_event = event;
467  last_resize_event_used = false;
468  }
469  }
470 
471  // remove all inputs, draw events and only keep the last of the resize events
472  // This will turn horrible after ~38 days when the uint32_t wraps.
473  if(resize_found || SDL_GetTicks() <= last_resize_event.window.timestamp + resize_timeout) {
474  events.erase(std::remove_if(events.begin(), events.end(), remove_on_resize), events.end());
475  } else if(SDL_GetTicks() > last_resize_event.window.timestamp + resize_timeout && !last_resize_event_used) {
476  events.insert(events.begin(), last_resize_event);
477  last_resize_event_used = true;
478  }
479 
480  // Move all draw events to the end of the queue
481  auto first_draw_event = std::stable_partition(events.begin(), events.end(),
482  [](const SDL_Event& e) { return e.type != DRAW_EVENT; }
483  );
484 
485  if(first_draw_event != events.end()) {
486  // Remove all draw events except one
487  events.erase(first_draw_event + 1, events.end());
488  }
489 
490  ev_end = events.end();
491 
492  for(ev_it = events.begin(); ev_it != ev_end; ++ev_it) {
493  for(context& c : event_contexts) {
494  c.add_staging_handlers();
495  }
496 
497  SDL_Event& event = *ev_it;
498  switch(event.type) {
499  case SDL_WINDOWEVENT:
500  switch(event.window.event) {
501  case SDL_WINDOWEVENT_ENTER:
502  case SDL_WINDOWEVENT_FOCUS_GAINED:
504  break;
505 
506  case SDL_WINDOWEVENT_LEAVE:
507  case SDL_WINDOWEVENT_FOCUS_LOST:
509  break;
510 
511  case SDL_WINDOWEVENT_RESIZED:
512  info.resize_dimensions.first = event.window.data1;
513  info.resize_dimensions.second = event.window.data2;
514  break;
515  }
516 
517  // make sure this runs in it's own scope.
518  {
519  flip_locker flip_lock(CVideo::get_singleton());
520  for(auto& context : event_contexts) {
521  for(auto handler : context.handlers) {
522  handler->handle_window_event(event);
523  }
524  }
525 
526  for(auto global_handler : event_contexts.front().handlers) {
527  global_handler->handle_window_event(event);
528  }
529  }
530 
531  // This event was just distributed, don't re-distribute.
532  continue;
533 
534  case SDL_MOUSEMOTION: {
535  // Always make sure a cursor is displayed if the mouse moves or if the user clicks
536  cursor::set_focus(true);
537  raise_help_string_event(event.motion.x, event.motion.y);
538  break;
539  }
540 
541  case SDL_MOUSEBUTTONDOWN: {
542  // Always make sure a cursor is displayed if the mouse moves or if the user clicks
543  cursor::set_focus(true);
544  if(event.button.button == SDL_BUTTON_LEFT) {
545  static const int DoubleClickTime = 500;
546  static const int DoubleClickMaxMove = 3;
547 
548  if(last_mouse_down >= 0 && info.ticks() - last_mouse_down < DoubleClickTime
549  && std::abs(event.button.x - last_click_x) < DoubleClickMaxMove
550  && std::abs(event.button.y - last_click_y) < DoubleClickMaxMove
551  ) {
552  sdl::UserEvent user_event(DOUBLE_CLICK_EVENT, event.button.x, event.button.y);
553  ::SDL_PushEvent(reinterpret_cast<SDL_Event*>(&user_event));
554  }
555 
556  last_mouse_down = info.ticks();
557  last_click_x = event.button.x;
558  last_click_y = event.button.y;
559  }
560  break;
561  }
562 
563  case DRAW_ALL_EVENT: {
564  flip_locker flip_lock(CVideo::get_singleton());
565 
566  /* Iterate backwards as the most recent things will be at the top */
567  // FIXME? ^ that isn't happening here.
568  for(auto& context : event_contexts) {
569  for(auto handler : context.handlers) {
570  handler->handle_event(event);
571  }
572  }
573 
574  continue; // do not do further handling here
575  }
576 
577 #ifndef __APPLE__
578  case SDL_KEYDOWN: {
579  if(event.key.keysym.sym == SDLK_F4 &&
580  (event.key.keysym.mod == KMOD_RALT || event.key.keysym.mod == KMOD_LALT)
581  ) {
583  continue; // this event is already handled
584  }
585  break;
586  }
587 #endif
588 
589 #if defined(_X11) && !defined(__APPLE__)
590  case SDL_SYSWMEVENT: {
591  // clipboard support for X11
593  break;
594  }
595 #endif
596 
597 #if defined _WIN32
598  case SDL_SYSWMEVENT: {
600  break;
601  }
602 #endif
603 
604  case SDL_QUIT: {
606  continue; // this event is already handled.
607  }
608  }
609 
610  for(auto global_handler : event_contexts.front().handlers) {
611  global_handler->handle_event(event);
612  }
613 
614  if(event_contexts.empty() == false) {
615  for(auto handler : event_contexts.back().handlers) {
616  handler->handle_event(event);
617  }
618  }
619  }
620 
621  // Inform the pump monitors that an events::pump() has occurred
622  for(auto monitor : pump_monitors) {
623  monitor->process(info);
624  }
625 }
626 
628 {
629  if(event_contexts.empty() == false) {
630  event_contexts.back().add_staging_handlers();
631 
632  for(auto handler : event_contexts.back().handlers) {
633  handler->process_event();
634  }
635  }
636 }
637 
639 {
640  SDL_Event event;
641  event.window.type = SDL_WINDOWEVENT;
642  event.window.event = SDL_WINDOWEVENT_RESIZED;
643  event.window.windowID = 0; // We don't check this anyway... I think...
644  event.window.data1 = CVideo::get_singleton().get_width();
645  event.window.data2 = CVideo::get_singleton().get_height();
646 
647  SDL_PushEvent(&event);
648 }
649 
651 {
652  if(event_contexts.empty() == false) {
653  event_contexts.back().add_staging_handlers();
654 
655  // Events may cause more event handlers to be added and/or removed,
656  // so we must use indexes instead of iterators here.
657  for(auto handler : event_contexts.back().handlers) {
658  handler->draw();
659  }
660  }
661 }
662 
664 {
665  for(auto& context : event_contexts) {
666  for(auto handler : context.handlers) {
667  handler->draw();
668  }
669  }
670 }
671 
673 {
674  if(event_contexts.empty() == false) {
675  for(auto handler : event_contexts.back().handlers) {
676  handler->volatile_draw();
677  }
678  }
679 }
680 
682 {
683  for(auto& context : event_contexts) {
684  for(auto handler : context.handlers) {
685  handler->volatile_draw();
686  }
687  }
688 }
689 
691 {
692  if(event_contexts.empty() == false) {
693  for(auto handler : event_contexts.back().handlers) {
694  handler->volatile_undraw();
695  }
696  }
697 }
698 
699 void raise_help_string_event(int mousex, int mousey)
700 {
701  if(event_contexts.empty() == false) {
702  for(auto handler : event_contexts.back().handlers) {
703  handler->process_help_string(mousex, mousey);
704  handler->process_tooltip_string(mousex, mousey);
705  }
706  }
707 }
708 
709 int pump_info::ticks(unsigned* refresh_counter, unsigned refresh_rate)
710 {
711  if(!ticks_ && !(refresh_counter && ++*refresh_counter % refresh_rate)) {
712  ticks_ = ::SDL_GetTicks();
713  }
714 
715  return ticks_;
716 }
717 
718 /* The constants for the minimum and maximum are picked from the headers. */
719 #define INPUT_MIN 0x300
720 #define INPUT_MAX 0x8FF
721 
722 bool is_input(const SDL_Event& event)
723 {
724  return event.type >= INPUT_MIN && event.type <= INPUT_MAX;
725 }
726 
728 {
729  SDL_FlushEvents(INPUT_MIN, INPUT_MAX);
730 }
731 
733 {
734  SDL_Event events[100];
735  int num = SDL_PeepEvents(events, 100, SDL_PEEKEVENT, SDL_WINDOWEVENT, SDL_WINDOWEVENT);
736  for(int i = 0; i < num; ++i) {
737  if(events[i].type == SDL_WINDOWEVENT && events[i].window.event == SDL_WINDOWEVENT_RESIZED) {
739  }
740  }
741 }
742 
743 void call_in_main_thread(const std::function<void(void)>& f)
744 {
745  if(boost::this_thread::get_id() == main_thread) {
746  // nothing special to do if called from the main thread.
747  f();
748  return;
749  }
750 
751  invoked_function_data fdata{false, f};
752 
753  SDL_Event sdl_event;
754  sdl::UserEvent sdl_userevent(INVOKE_FUNCTION_EVENT, &fdata);
755 
756  sdl_event.type = INVOKE_FUNCTION_EVENT;
757  sdl_event.user = sdl_userevent;
758 
759  SDL_PushEvent(&sdl_event);
760 
761  while(!fdata.finished) {
762  SDL_Delay(10);
763  }
764 }
765 
766 } // end events namespace
void raise_resize_event()
Definition: events.cpp:638
void raise_volatile_undraw_event()
Definition: events.cpp:690
void remove()
Removes a tip.
Definition: tooltip.cpp:189
void discard_input()
Discards all input events.
Definition: events.cpp:727
void set_focus(const sdl_handler *ptr)
Definition: events.cpp:153
std::vector< events::sdl_handler * > sdl_handler_vector
Definition: events.hpp:176
bool last_resize_event_used
Definition: events.cpp:383
#define INVOKE_FUNCTION_EVENT
Definition: events.hpp:30
logger & info()
Definition: log.cpp:91
#define a
int ticks(unsigned *refresh_counter=nullptr, unsigned refresh_rate=1)
Definition: events.cpp:709
void add_handler(sdl_handler *ptr)
Definition: events.cpp:64
#define DRAW_EVENT
Definition: events.hpp:26
virtual void leave_global()
Definition: events.cpp:319
static CVideo & get_singleton()
Definition: video.hpp:43
#define h
bool remove_handler(sdl_handler *ptr)
Definition: events.cpp:73
-file util.hpp
void cycle_focus()
Definition: events.cpp:120
virtual void join_same(sdl_handler *parent)
Definition: events.cpp:261
void call_in_main_thread(const std::function< void(void)> &f)
Definition: events.cpp:743
void raise_draw_all_event()
Definition: events.cpp:663
static bool remove_on_resize(const SDL_Event &a)
Definition: events.cpp:385
void focus_handler(const sdl_handler *ptr)
Definition: events.cpp:330
void set_focus(bool focus)
Definition: cursor.cpp:199
void add_staging_handlers()
Definition: events.cpp:161
void raise_volatile_draw_all_event()
Definition: events.cpp:681
int get_width(bool as_pixels=true) const
Returns the window renderer width in pixels or screen coordinates.
Definition: video.cpp:316
#define INPUT_MAX
Definition: events.cpp:720
virtual void join()
Definition: events.cpp:232
std::list< sdl_handler * > handler_list
Definition: events.hpp:35
void peek_for_resize()
Definition: events.cpp:732
static const boost::thread::id main_thread
Definition: events.cpp:407
const uint32_t resize_timeout
Definition: events.cpp:381
void raise_draw_event()
Definition: events.cpp:650
void pump()
Definition: events.cpp:409
#define DRAW_ALL_EVENT
Definition: events.hpp:29
virtual ~sdl_handler()
Definition: events.cpp:221
#define DOUBLE_CLICK_EVENT
Definition: events.hpp:23
void raise_process_event()
Definition: events.cpp:627
static void quit_to_desktop()
#define SHOW_HELPTIP_EVENT
Definition: events.hpp:28
bool is_input(const SDL_Event &event)
Is the event an input event?
Definition: events.cpp:722
std::vector< sdl_handler * > staging_handlers
Definition: events.hpp:61
void raise_help_string_event(int mousex, int mousey)
Definition: events.cpp:699
void handle_system_event(const SDL_Event &)
Definition: clipboard.cpp:52
virtual bool requires_event_focus(const SDL_Event *=nullptr) const
Definition: events.hpp:83
size_t i
Definition: function.cpp:933
void raise_volatile_draw_event()
Definition: events.cpp:672
virtual std::vector< sdl_handler * > handler_members()
Definition: events.hpp:101
std::deque< context > event_contexts
Definition: events.cpp:185
handler_list::iterator focused_handler
Definition: events.hpp:60
Handling of system events.
Definition: manager.hpp:41
std::vector< pump_monitor * > pump_monitors
Definition: events.cpp:187
sdl_handler(const bool auto_join=true)
Definition: events.cpp:210
int get_height(bool as_pixels=true) const
Returns the window renderer height in pixels or in screen coordinates.
Definition: video.cpp:321
#define f
bool find(E event, F functor)
Tests whether an event handler is available.
bool has_focus(const sdl_handler *hand, const SDL_Event *event)
Definition: events.cpp:337
Standard logging facilities (interface).
std::pair< int, int > resize_dimensions
Definition: events.hpp:140
#define e
SDL_Event last_resize_event
Definition: events.cpp:382
void update_framebuffer()
Updates and ensures the framebuffer surface is valid.
Definition: video.cpp:199
static void reverse(lua_State *L, StkId from, StkId to)
Definition: lapi.cpp:193
static void handle_system_event(const SDL_Event &event)
Frees resources when a notification disappears, switches user to the wesnoth window if the notificati...
virtual void leave()
Definition: events.cpp:278
handler_list handlers
Definition: events.hpp:59
mock_char c
virtual ~pump_monitor()
Definition: events.cpp:194
virtual void join_global()
Definition: events.cpp:299
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
HOTKEY_COMMAND get_id(const std::string &command)
returns get_hotkey_command(command).id
#define INPUT_MIN
Definition: events.cpp:719