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