The Battle for Wesnoth  1.19.25+dev
events.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2025
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 "draw_manager.hpp"
23 #include "quit_confirmation.hpp"
24 #include "sdl/userevent.hpp"
25 #include "utils/ranges.hpp"
26 #include "utils/general.hpp"
27 #include "video.hpp"
28 
29 #ifdef _WIN32
31 #endif
32 
33 #include <algorithm>
34 #include <cassert>
35 #include <deque>
36 #include <future>
37 #include <iterator>
38 #include <thread>
39 #include <utility>
40 #include <vector>
41 
42 #include <SDL3/SDL.h>
43 
44 #define ERR_GEN LOG_STREAM(err, lg::general)
45 
46 static lg::log_domain log_display("display");
47 #define LOG_DP LOG_STREAM(info, log_display)
48 
49 static lg::log_domain log_event("event");
50 #define LOG_EV LOG_STREAM(info, log_event)
51 #define DBG_EV LOG_STREAM(debug, log_event)
52 
53 namespace
54 {
55 struct invoked_function_data
56 {
57  explicit invoked_function_data(const std::function<void(void)>& func)
58  : f(func)
59  , finished()
60  {
61  }
62 
63  /** The actual function to call. */
64  const std::function<void(void)>& f;
65 
66  /** Whether execution in the main thread is complete. */
67  std::promise<void> finished;
68 
69  void call()
70  {
71  try {
72  f();
73  } catch(const video::quit&) {
74  // Handle this exception in the main thread.
75  throw;
76  } catch(...) {
77  DBG_EV << "Caught exception in invoked function: " << utils::get_unknown_exception_type();
78  finished.set_exception(std::current_exception());
79  return;
80  }
81 
82  finished.set_value();
83  }
84 };
85 }
86 
87 namespace events
88 {
90 {
91  /* Add new handlers to the staging list initially.
92  * This ensures that if an event handler adds more handlers, the new handlers
93  * won't be called for the event that caused them to be added.
94  */
95  staging_handlers.push_back(ptr);
96 }
97 
98 bool context::has_handler(const sdl_handler* ptr) const
99 {
101 }
102 
104 {
105  static int depth = 0;
106  ++depth;
107 
108  // The handler is most likely on the back of the events list,
109  // so look there first, otherwise do a complete search.
110  if(!handlers.empty() && handlers.back() == ptr) {
111  if(focused_handler != handlers.end() && *focused_handler == ptr) {
112  focused_handler = handlers.end();
113  }
114 
115  handlers.pop_back();
116  } else {
117  const handler_list::iterator i = std::find(handlers.begin(), handlers.end(), ptr);
118 
119  if(i == handlers.end()) {
120  --depth;
121 
122  // The handler may be in the staging area. Search it from there.
123  auto j = std::find(staging_handlers.begin(), staging_handlers.end(), ptr);
124  if(j != staging_handlers.end()) {
125  staging_handlers.erase(j);
126  return true;
127  } else {
128  return false;
129  }
130  }
131 
132  if(i == focused_handler) {
134  }
135 
136  handlers.erase(i);
137  }
138 
139  --depth;
140 
141  if(depth == 0) {
142  cycle_focus();
143  } else {
144  focused_handler = handlers.end();
145  }
146 
147  return true;
148 }
149 
151 {
152  if(handlers.begin() == handlers.end()) {
153  return;
154  }
155 
158 
159  if(last != handlers.begin()) {
160  --last;
161  }
162 
163  if(current == handlers.end()) {
164  current = handlers.begin();
165  } else {
166  ++current;
167  }
168 
169  while(current != last) {
170  if(current != handlers.end() && (*current)->requires_event_focus()) {
171  focused_handler = current;
172  break;
173  }
174 
175  if(current == handlers.end()) {
176  current = handlers.begin();
177  } else {
178  ++current;
179  }
180  }
181 }
182 
184 {
185  const handler_list::iterator i = std::find(handlers.begin(), handlers.end(), ptr);
186  if(i != handlers.end() && (*i)->requires_event_focus()) {
187  focused_handler = i;
188  }
189 }
190 
192 {
193  std::copy(staging_handlers.begin(), staging_handlers.end(), std::back_inserter(handlers));
194  staging_handlers.clear();
195 }
196 
198 {
199  for(sdl_handler* h : handlers) {
200  if(h->has_joined()) {
201  h->has_joined_ = false;
202  }
203 
204  if(h->has_joined_global()) {
205  h->has_joined_global_ = false;
206  }
207  }
208 }
209 
210 // This object stores all the event handlers. It is a stack of event 'contexts'.
211 // a new event context is created when e.g. a modal dialog is opened, and then
212 // closed when that dialog is closed. Each context contains a list of the handlers
213 // in that context. The current context is the one on the top of the stack.
214 // The global context must always be in the first position.
215 std::deque<context> event_contexts;
216 
217 std::vector<pump_monitor*> pump_monitors;
218 
220 {
221  pump_monitors.push_back(this);
222 }
223 
225 {
227 }
228 
230 {
231  event_contexts.emplace_back();
232 }
233 
235 {
236  assert(event_contexts.empty() == false);
237  event_contexts.pop_back();
238 }
239 
240 sdl_handler::sdl_handler(const bool auto_join)
241  : has_joined_(false)
242  , has_joined_global_(false)
243 {
244  if(auto_join) {
245  assert(!event_contexts.empty());
246  event_contexts.back().add_handler(this);
247  has_joined_ = true;
248  }
249 }
250 
252  : has_joined_(that.has_joined_)
253  , has_joined_global_(that.has_joined_global_)
254 {
255  if(has_joined_global_) {
256  assert(!event_contexts.empty());
257  event_contexts.front().add_handler(this);
258  } else if(has_joined_) {
259  bool found_context = false;
261  if(context.has_handler(&that)) {
262  found_context = true;
263  context.add_handler(this);
264  break;
265  }
266  }
267 
268  if (!found_context) {
269  throw std::logic_error("Copy-constructing a sdl_handler that has_joined_ but can't be found by searching contexts");
270  }
271  }
272 }
273 
275 {
276  if(that.has_joined_global_) {
277  join_global();
278  } else if(that.has_joined_) {
280  if(context.has_handler(&that)) {
281  join(context);
282  break;
283  }
284  }
285  } else if(has_joined_) {
286  leave();
287  } else if(has_joined_global_) {
288  leave_global();
289  }
290 
291  return *this;
292 }
293 
295 {
296  if(has_joined_) {
297  leave();
298  }
299 
300  if(has_joined_global_) {
301  leave_global();
302  }
303 }
304 
306 {
307  // this assert will fire if someone will inadvertently try to join
308  // an event context but might end up in the global context instead.
309  assert(&event_contexts.back() != &event_contexts.front());
310 
311  join(event_contexts.back());
312 }
313 
315 {
316  if(has_joined_global_) {
317  leave_global();
318  }
319 
320  if(has_joined_) {
321  leave(); // should not be in multiple event contexts
322  }
323 
324  // join self
325  c.add_handler(this);
326  has_joined_ = true;
327 
328  // instruct members to join
329  for(auto member : handler_members()) {
330  member->join(c);
331  }
332 }
333 
335 {
336  if(has_joined_) {
337  leave(); // should not be in multiple event contexts
338  }
339 
341  if(context.has_handler(parent)) {
342  join(context);
343  return;
344  }
345  }
346 
347  join(event_contexts.back());
348 }
349 
351 {
353 
354  if(members.empty()) {
355  assert(event_contexts.empty() == false);
356  }
357 
358  for(auto member : members) {
359  member->leave();
360  }
361 
363  if(context.remove_handler(this)) {
364  break;
365  }
366  }
367 
368  has_joined_ = false;
369 }
370 
372 {
373  if(has_joined_) {
374  leave();
375  }
376 
377  if(has_joined_global_) {
378  leave_global(); // Should not be in multiple event contexts
379  }
380 
381  // Join self
382  event_contexts.front().add_handler(this);
383  has_joined_global_ = true;
384 
385  // Instruct members to join
386  for(auto member : handler_members()) {
387  member->join_global();
388  }
389 }
390 
392 {
393  for(auto member : handler_members()) {
394  member->leave_global();
395  }
396 
397  event_contexts.front().remove_handler(this);
398 
399  has_joined_global_ = false;
400 }
401 
402 void focus_handler(const sdl_handler* ptr)
403 {
404  if(event_contexts.empty() == false) {
405  event_contexts.back().set_focus(ptr);
406  }
407 }
408 
409 bool has_focus(const sdl_handler* hand, const SDL_Event* event)
410 {
411  if(event_contexts.empty()) {
412  return true;
413  }
414 
415  if(hand->requires_event_focus(event) == false) {
416  return true;
417  }
418 
419  const handler_list::iterator foc = event_contexts.back().focused_handler;
420  auto& handlers = event_contexts.back().handlers;
421 
422  // If no-one has focus at the moment, this handler obviously wants
423  // focus, so give it to it.
424  if(foc == handlers.end()) {
425  focus_handler(hand);
426  return true;
427  }
428 
429  sdl_handler* const foc_hand = *foc;
430  if(foc_hand == hand) {
431  return true;
432  } else if(!foc_hand->requires_event_focus(event)) {
433  // If the currently focused handler doesn't need focus for this event
434  // allow the most recent interested handler to take care of it
435  for(auto i = handlers.rbegin(); i != handlers.rend(); ++i) {
436  sdl_handler* const thief_hand = *i;
437 
438  if(thief_hand != foc_hand && thief_hand->requires_event_focus(event)) {
439  // Steal focus
440  focus_handler(thief_hand);
441 
442  // Position the previously focused handler to allow stealing back
443  handlers.splice(handlers.end(), handlers, foc);
444 
445  return thief_hand == hand;
446  }
447  }
448  }
449 
450  return false;
451 }
452 
453 static void raise_window_event(const SDL_Event& event)
454 {
455  for(auto& context : event_contexts) {
456  for(auto handler : context.handlers) {
457  handler->handle_window_event(event);
458  }
459  }
460 
461  for(auto global_handler : event_contexts.front().handlers) {
462  global_handler->handle_window_event(event);
463  }
464 }
465 
467 
469 {
470  main_thread = std::this_thread::get_id();
471 }
472 
473 // this should probably be elsewhere, but as the main thread is already
474 // being tracked here, this went here.
476 {
477  return std::this_thread::get_id() == main_thread;
478 }
479 
480 void pump()
481 {
482  if(!is_in_main_thread()) {
483  // Can only call this on the main thread!
484  return;
485  }
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 
500  if(!begin_ignoring && (
501  temp_event.type == SDL_EVENT_WINDOW_MOUSE_ENTER ||
502  temp_event.type == SDL_EVENT_WINDOW_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  for(SDL_Event& event : events) {
524  for(context& c : event_contexts) {
525  c.add_staging_handlers();
526  }
527 
528  SDL_ConvertEventToRenderCoordinates(video::get_renderer(), &event);
529 
530 #ifdef MOUSE_TOUCH_EMULATION
531  switch (event.type) {
532  // TODO: Implement SDL_MULTIGESTURE. Some day.
533  case SDL_EVENT_MOUSE_MOTION:
534  if(!events::is_touch(event.motion) && event.motion.state == 0) {
535  return;
536  }
537 
538  if(event.motion.state & SDL_BUTTON(SDL_BUTTON_RIGHT))
539  {
540  // Events are given by SDL in draw space
542 
543  // TODO: Check if SDL_EVENT_FINGER_MOTION is actually signaled for COMPLETE motions (I doubt, but tbs)
544  SDL_Event touch_event;
545  touch_event.type = SDL_EVENT_FINGER_MOTION;
546  touch_event.tfinger.type = SDL_EVENT_FINGER_MOTION;
547  touch_event.tfinger.timestamp = event.motion.timestamp;
548  touch_event.tfinger.touchId = 1;
549  touch_event.tfinger.fingerId = 1;
550  touch_event.tfinger.dx = static_cast<float>(event.motion.xrel) / c.x;
551  touch_event.tfinger.dy = static_cast<float>(event.motion.yrel) / c.y;
552  touch_event.tfinger.x = static_cast<float>(event.motion.x) / c.x;
553  touch_event.tfinger.y = static_cast<float>(event.motion.y) / c.y;
554  touch_event.tfinger.pressure = 1;
555  ::SDL_PushEvent(&touch_event);
556 
557  event.motion.state = SDL_BUTTON(SDL_BUTTON_LEFT);
558  event.motion.which = SDL_TOUCH_MOUSEID;
559  }
560  break;
561  case SDL_EVENT_MOUSE_BUTTON_DOWN:
562  case SDL_EVENT_MOUSE_BUTTON_UP:
563  if(event.button.button == SDL_BUTTON_RIGHT)
564  {
565  event.button.button = SDL_BUTTON_LEFT;
566  event.button.which = SDL_TOUCH_MOUSEID;
567 
568  // Events are given by SDL in draw space
570 
571  SDL_Event touch_event;
572  touch_event.type = (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) ? SDL_EVENT_FINGER_DOWN : SDL_EVENT_FINGER_UP;
573  touch_event.tfinger.type = touch_event.type;
574  touch_event.tfinger.timestamp = event.button.timestamp;
575  touch_event.tfinger.touchId = 1;
576  touch_event.tfinger.fingerId = 1;
577  touch_event.tfinger.dx = 0;
578  touch_event.tfinger.dy = 0;
579  touch_event.tfinger.x = static_cast<float>(event.button.x) / c.x;
580  touch_event.tfinger.y = static_cast<float>(event.button.y) / c.y;
581  touch_event.tfinger.pressure = 1;
582  ::SDL_PushEvent(&touch_event);
583 
584  }
585  break;
586  default:
587  break;
588  }
589 #endif
590 
591  if(event.type >= SDL_EVENT_WINDOW_FIRST && event.type <= SDL_EVENT_WINDOW_LAST) {
592  switch(event.type) {
593  case SDL_EVENT_WINDOW_MOUSE_ENTER:
594  case SDL_EVENT_WINDOW_FOCUS_GAINED:
596  break;
597 
598  case SDL_EVENT_WINDOW_MOUSE_LEAVE:
599  case SDL_EVENT_WINDOW_FOCUS_LOST:
601  break;
602 
603  // Size changed is called before resized.
604  // We can ensure the video framebuffer is valid here.
605  case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
606  LOG_DP << "events/SIZE_CHANGED "
607  << event.window.data1 << 'x' << event.window.data2;
608  video::update_buffers(false);
609  break;
610 
611  // Resized comes after size_changed.
612  // Here we can trigger any watchers for resize events.
613  // Video settings such as game_canvas_size() will be correct.
614  case SDL_EVENT_WINDOW_RESIZED:
615  LOG_DP << "events/RESIZED "
616  << event.window.data1 << 'x' << event.window.data2;
618  break;
619 
620  // Once everything has had a chance to respond to the resize,
621  // an expose is triggered to display the changed content.
622  case SDL_EVENT_WINDOW_EXPOSED:
623  LOG_DP << "events/EXPOSED";
625  break;
626 
627  case SDL_EVENT_WINDOW_MAXIMIZED:
628  LOG_DP << "events/MAXIMIZED";
629  prefs::get().set_maximized(true);
630  break;
631  case SDL_EVENT_WINDOW_RESTORED:
632  LOG_DP << "events/RESTORED";
633  prefs::get().set_maximized(prefs::get().fullscreen());
634  break;
635  case SDL_EVENT_WINDOW_SHOWN:
636  case SDL_EVENT_WINDOW_MOVED:
637  // Not used.
638  break;
639  }
640 
641  raise_window_event(event);
642 
643  // This event was just distributed, don't re-distribute.
644  continue;
645  }
646 
647  switch(event.type) {
648  case SDL_EVENT_MOUSE_MOTION: {
649  // Always make sure a cursor is displayed if the mouse moves or if the user clicks
650  cursor::set_focus(true);
651  process_tooltip_strings(event.motion.x, event.motion.y);
652  break;
653  }
654 
655  case SDL_EVENT_MOUSE_BUTTON_DOWN: {
656  // Always make sure a cursor is displayed if the mouse moves or if the user clicks
657  cursor::set_focus(true);
658  break;
659  }
660 
661 #ifndef __APPLE__
662  case SDL_EVENT_KEY_DOWN: {
663  if(event.key.key == SDLK_F4 &&
664  (event.key.mod == SDL_KMOD_RALT || event.key.mod == SDL_KMOD_LALT)
665  ) {
667  continue; // this event is already handled
668  }
669  break;
670  }
671 #endif
672 
673  case SDL_EVENT_QUIT: {
675  continue; // this event is already handled.
676  }
677  }
678 
679  for(auto global_handler : event_contexts.front().handlers) {
680  global_handler->handle_event(event);
681  }
682 
683  if(event_contexts.empty() == false) {
684  // As pump() can recurse, pretty much anything can happen here
685  // including destroying handlers or the event context.
686  std::size_t ec_index = event_contexts.size();
687  context& c = event_contexts.back();
688  handler_list& h = c.handlers;
689  std::size_t h_size = h.size();
690  for(auto it = h.begin(); it != h.end(); ++it) {
691  // Pass the event on to the handler.
692  (*it)->handle_event(event);
693  // Escape if anything has changed.
694  if(event_contexts.size() != ec_index) {
695  LOG_EV << "ec size changed! bugging out";
696  break;
697  }
698  if(h_size != h.size()) {
699  LOG_EV << "h size changed! bugging out";
700  break;
701  }
702  }
703  }
704  }
705 
706  // Inform the pump monitors that an events::pump() has occurred
707  for(auto monitor : pump_monitors) {
708  monitor->process();
709  }
710 }
711 
712 void draw()
713 {
715 }
716 
718 {
719  if(event_contexts.empty() == false) {
720  event_contexts.back().add_staging_handlers();
721 
722  for(auto handler : event_contexts.back().handlers) {
723  handler->process_event();
724  }
725  }
726 }
727 
729 {
731  SDL_Event event;
732  event.type = SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED;
733  event.window.windowID = 0; // We don't check this anyway... I think...
734  event.window.data1 = size.x;
735  event.window.data2 = size.y;
736 
737  raise_window_event(event);
738 }
739 
740 void process_tooltip_strings(int mousex, int mousey)
741 {
742  if(event_contexts.empty() == false) {
743  for(auto handler : event_contexts.back().handlers) {
744  handler->process_tooltip_string(mousex, mousey);
745  }
746  }
747 }
748 
749 /* The constants for the minimum and maximum are picked from the headers. */
750 #define INPUT_MIN 0x300
751 #define INPUT_MAX 0x8FF
752 
753 bool is_input(const SDL_Event& event)
754 {
755  return event.type >= INPUT_MIN && event.type <= INPUT_MAX;
756 }
757 
759 {
760  SDL_FlushEvents(INPUT_MIN, INPUT_MAX);
761 }
762 
763 bool is_touch(const SDL_MouseButtonEvent &event)
764 {
765  return event.which == SDL_TOUCH_MOUSEID;
766 }
767 
768 bool is_touch(const SDL_MouseMotionEvent &event)
769 {
770  return event.which == SDL_TOUCH_MOUSEID;
771 }
772 
773 void call_in_main_thread(const std::function<void(void)>& f)
774 {
775  if(is_in_main_thread()) {
776  // nothing special to do if called from the main thread.
777  f();
778  return;
779  }
780 
781  invoked_function_data fdata{f};
782 
783  SDL_Event sdl_event;
784  sdl::UserEvent sdl_userevent(INVOKE_FUNCTION_EVENT, &fdata);
785 
786  sdl_event.type = INVOKE_FUNCTION_EVENT;
787  sdl_event.user = sdl_userevent;
788 
789  SDL_PushEvent(&sdl_event);
790 
791  // Block until execution is complete in the main thread. Rethrows any exceptions.
792  fdata.finished.get_future().wait();
793 }
794 
795 #ifdef _WIN32
796 bool handle_windows_message([[maybe_unused]] void* userdata, MSG* msg)
797 {
799  return false;
800  }
801 
802  // Continue further processing
803  return true;
804 }
805 #endif
806 
807 } // end events namespace
void set_focus(const sdl_handler *ptr)
Definition: events.cpp:183
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:98
void cycle_focus()
Definition: events.cpp:150
handler_list::iterator focused_handler
Definition: events.hpp:66
std::vector< sdl_handler * > staging_handlers
Definition: events.hpp:67
handler_list handlers
Definition: events.hpp:65
void add_handler(sdl_handler *ptr)
Definition: events.cpp:89
bool remove_handler(sdl_handler *ptr)
Definition: events.cpp:103
void add_staging_handlers()
Definition: events.cpp:191
virtual ~pump_monitor()
Definition: events.cpp:224
virtual bool requires_event_focus(const SDL_Event *=nullptr) const
Definition: events.hpp:85
virtual std::vector< sdl_handler * > handler_members()
Definition: events.hpp:113
virtual void join()
Definition: events.cpp:305
virtual void leave()
Definition: events.cpp:350
virtual void join_global()
Definition: events.cpp:371
virtual void join_same(sdl_handler *parent)
Definition: events.cpp:334
virtual void leave_global()
Definition: events.cpp:391
sdl_handler & operator=(sdl_handler &&)=delete
Moving would require two instances' context membership to be handled, it's simpler to delete these an...
virtual ~sdl_handler()
Definition: events.cpp:294
sdl_handler(sdl_handler &&)=delete
static prefs & get()
void set_resolution(const point &res)
static void quit_to_desktop()
Type that can be thrown as an exception to quit to desktop.
Definition: video.hpp:328
#define INPUT_MAX
Definition: events.cpp:751
#define LOG_EV
Definition: events.cpp:50
#define INPUT_MIN
Definition: events.cpp:750
#define LOG_DP
Definition: events.cpp:47
static lg::log_domain log_display("display")
static lg::log_domain log_event("event")
#define DBG_EV
Definition: events.cpp:51
#define INVOKE_FUNCTION_EVENT
Definition: events.hpp:34
std::vector< events::sdl_handler * > sdl_handler_vector
Definition: events.hpp:219
std::size_t i
Definition: function.cpp:1031
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:199
Standard logging facilities (interface).
void set_focus(bool focus)
Definition: cursor.cpp:210
void invalidate_all()
Mark the entire screen as requiring redraw.
void sparkle()
Ensure that everything which needs to be drawn is drawn.
Handling of system events.
void raise_resize_event()
Definition: events.cpp:728
bool has_focus(const sdl_handler *hand, const SDL_Event *event)
Definition: events.cpp:409
void discard_input()
Discards all input events.
Definition: events.cpp:758
void draw()
Trigger a draw cycle.
Definition: events.cpp:712
static std::thread::id main_thread
Definition: events.cpp:466
static void raise_window_event(const SDL_Event &event)
Definition: events.cpp:453
bool is_input(const SDL_Event &event)
Is the event an input event?
Definition: events.cpp:753
void set_main_thread()
Definition: events.cpp:468
std::deque< context > event_contexts
Definition: events.cpp:215
std::vector< pump_monitor * > pump_monitors
Definition: events.cpp:217
void call_in_main_thread(const std::function< void(void)> &f)
Definition: events.cpp:773
void raise_process_event()
Definition: events.cpp:717
void process_tooltip_strings(int mousex, int mousey)
Triggered by mouse-motion, sends the cursor position to all handlers to check whether a tooltip shoul...
Definition: events.cpp:740
std::list< sdl_handler * > handler_list
Definition: events.hpp:39
bool is_in_main_thread()
Definition: events.cpp:475
void pump()
Process all events currently in the queue.
Definition: events.cpp:480
bool is_touch(const SDL_MouseButtonEvent &event)
Check if this mouse button event is caused by a touch.
Definition: events.cpp:763
void focus_handler(const sdl_handler *ptr)
Definition: events.cpp:402
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:81
constexpr auto reverse
Definition: ranges.hpp:44
std::size_t erase(Container &container, const Value &value)
Convenience wrapper for using std::remove on a container.
Definition: general.hpp:118
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition: general.hpp:87
std::string get_unknown_exception_type()
Utility function for finding the type of thing caught with catch(...).
Definition: general.cpp:23
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
Definition: general.hpp:141
point game_canvas_size()
The size of the game canvas, in drawing coordinates / game pixels.
Definition: video.cpp:432
point window_size()
Returns the size of the window in display units / screen coordinates.
Definition: video.cpp:419
SDL_Renderer * get_renderer()
Definition: video.cpp:681
void update_buffers(bool autoupdate)
Update buffers to match current resolution and pixel scale settings.
Definition: video.cpp:860
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
bool message_hook(const MSG &msg)
Frees resources when a notification disappears, switches user to the wesnoth window if the notificati...
Holds a 2D point.
Definition: point.hpp:25
mock_char c
struct tagMSG MSG
#define h
#define f