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