The Battle for Wesnoth  1.17.4+dev
events.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2022
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().input_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().input_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_Point size = CVideo::get_singleton().window_size();
765  SDL_Event event;
766  event.window.type = SDL_WINDOWEVENT;
767  event.window.event = SDL_WINDOWEVENT_RESIZED;
768  event.window.windowID = 0; // We don't check this anyway... I think...
769  event.window.data1 = size.x;
770  event.window.data2 = size.y;
771 
772  SDL_PushEvent(&event);
773 }
774 
776 {
777  if(event_contexts.empty() == false) {
778  event_contexts.back().add_staging_handlers();
779 
780  // Events may cause more event handlers to be added and/or removed,
781  // so we must use indexes instead of iterators here.
782  for(auto handler : event_contexts.back().handlers) {
783  handler->draw();
784  }
785  }
786 }
787 
789 {
790  for(auto& context : event_contexts) {
791  for(auto handler : context.handlers) {
792  handler->draw();
793  }
794  }
795 }
796 
798 {
799  if(event_contexts.empty() == false) {
800  for(auto handler : event_contexts.back().handlers) {
801  handler->volatile_draw();
802  }
803  }
804 }
805 
807 {
808  for(auto& context : event_contexts) {
809  for(auto handler : context.handlers) {
810  handler->volatile_draw();
811  }
812  }
813 }
814 
816 {
817  if(event_contexts.empty() == false) {
818  for(auto handler : event_contexts.back().handlers) {
819  handler->volatile_undraw();
820  }
821  }
822 }
823 
824 void raise_help_string_event(int mousex, int mousey)
825 {
826  if(event_contexts.empty() == false) {
827  for(auto handler : event_contexts.back().handlers) {
828  handler->process_help_string(mousex, mousey);
829  handler->process_tooltip_string(mousex, mousey);
830  }
831  }
832 }
833 
834 int pump_info::ticks(unsigned* refresh_counter, unsigned refresh_rate)
835 {
836  if(!ticks_ && !(refresh_counter && ++*refresh_counter % refresh_rate)) {
837  ticks_ = ::SDL_GetTicks();
838  }
839 
840  return ticks_;
841 }
842 
843 /* The constants for the minimum and maximum are picked from the headers. */
844 #define INPUT_MIN 0x300
845 #define INPUT_MAX 0x8FF
846 
847 bool is_input(const SDL_Event& event)
848 {
849  return event.type >= INPUT_MIN && event.type <= INPUT_MAX;
850 }
851 
853 {
854  SDL_FlushEvents(INPUT_MIN, INPUT_MAX);
855 }
856 
858 {
859  SDL_Event events[100];
860  int num = SDL_PeepEvents(events, 100, SDL_PEEKEVENT, SDL_WINDOWEVENT, SDL_WINDOWEVENT);
861  for(int i = 0; i < num; ++i) {
862  if(events[i].type == SDL_WINDOWEVENT && events[i].window.event == SDL_WINDOWEVENT_RESIZED) {
864  break;
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  // Block until execution is complete in the main thread. Rethrows any exceptions.
888  fdata.finished.get_future().wait();
889 }
890 
891 } // end events namespace
void raise_resize_event()
Definition: events.cpp:762
void raise_volatile_undraw_event()
Definition: events.cpp:815
void remove()
Removes a tip.
Definition: tooltip.cpp:175
void discard_input()
Discards all input events.
Definition: events.cpp:852
SDL_Rect input_area() const
Returns the size and location of the window&#39;s input area in pixels.
Definition: video.cpp:381
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:834
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:286
static CVideo & get_singleton()
Definition: video.hpp:52
#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:869
void raise_draw_all_event()
Definition: events.cpp:788
static bool remove_on_resize(const SDL_Event &a)
Definition: events.cpp:449
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:87
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:806
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:845
sdl_handler(sdl_handler &&)=delete
virtual void join()
Definition: events.cpp:297
void peek_for_resize()
Definition: events.cpp:857
const uint32_t resize_timeout
Definition: events.cpp:445
void raise_draw_event()
Definition: events.cpp:775
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
SDL_Point window_size() const
Returns the size of the window in display units / screen coordinates.
Definition: video.cpp:371
#define SHOW_HELPTIP_EVENT
Definition: events.hpp:29
bool is_input(const SDL_Event &event)
Is the event an input event?
Definition: events.cpp:847
void raise_help_string_event(int mousex, int mousey)
Definition: events.cpp:824
void handle_system_event(const SDL_Event &)
Definition: clipboard.cpp:51
void raise_volatile_draw_event()
Definition: events.cpp:797
virtual std::vector< sdl_handler * > handler_members()
Definition: events.hpp:115
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:192
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
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:844
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