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