The Battle for Wesnoth  1.19.24+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 // #if defined _WIN32
30 // #include "desktop/windows_tray_notification.hpp"
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 #ifdef MOUSE_TOUCH_EMULATION
529  switch (event.type) {
530  // TODO: Implement SDL_MULTIGESTURE. Some day.
531  case SDL_EVENT_MOUSE_MOTION:
532  if(!events::is_touch(event.motion) && event.motion.state == 0) {
533  return;
534  }
535 
536  if(event.motion.state & SDL_BUTTON(SDL_BUTTON_RIGHT))
537  {
538  // Events are given by SDL in draw space
540 
541  // TODO: Check if SDL_EVENT_FINGER_MOTION is actually signaled for COMPLETE motions (I doubt, but tbs)
542  SDL_Event touch_event;
543  touch_event.type = SDL_EVENT_FINGER_MOTION;
544  touch_event.tfinger.type = SDL_EVENT_FINGER_MOTION;
545  touch_event.tfinger.timestamp = event.motion.timestamp;
546  touch_event.tfinger.touchId = 1;
547  touch_event.tfinger.fingerId = 1;
548  touch_event.tfinger.dx = static_cast<float>(event.motion.xrel) / c.x;
549  touch_event.tfinger.dy = static_cast<float>(event.motion.yrel) / c.y;
550  touch_event.tfinger.x = static_cast<float>(event.motion.x) / c.x;
551  touch_event.tfinger.y = static_cast<float>(event.motion.y) / c.y;
552  touch_event.tfinger.pressure = 1;
553  ::SDL_PushEvent(&touch_event);
554 
555  event.motion.state = SDL_BUTTON(SDL_BUTTON_LEFT);
556  event.motion.which = SDL_TOUCH_MOUSEID;
557  }
558  break;
559  case SDL_EVENT_MOUSE_BUTTON_DOWN:
560  case SDL_EVENT_MOUSE_BUTTON_UP:
561  if(event.button.button == SDL_BUTTON_RIGHT)
562  {
563  event.button.button = SDL_BUTTON_LEFT;
564  event.button.which = SDL_TOUCH_MOUSEID;
565 
566  // Events are given by SDL in draw space
568 
569  SDL_Event touch_event;
570  touch_event.type = (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) ? SDL_EVENT_FINGER_DOWN : SDL_EVENT_FINGER_UP;
571  touch_event.tfinger.type = touch_event.type;
572  touch_event.tfinger.timestamp = event.button.timestamp;
573  touch_event.tfinger.touchId = 1;
574  touch_event.tfinger.fingerId = 1;
575  touch_event.tfinger.dx = 0;
576  touch_event.tfinger.dy = 0;
577  touch_event.tfinger.x = static_cast<float>(event.button.x) / c.x;
578  touch_event.tfinger.y = static_cast<float>(event.button.y) / c.y;
579  touch_event.tfinger.pressure = 1;
580  ::SDL_PushEvent(&touch_event);
581 
582  }
583  break;
584  default:
585  break;
586  }
587 #endif
588 
589  if(event.type >= SDL_EVENT_WINDOW_FIRST && event.type <= SDL_EVENT_WINDOW_LAST) {
590  switch(event.type) {
591  case SDL_EVENT_WINDOW_MOUSE_ENTER:
592  case SDL_EVENT_WINDOW_FOCUS_GAINED:
594  break;
595 
596  case SDL_EVENT_WINDOW_MOUSE_LEAVE:
597  case SDL_EVENT_WINDOW_FOCUS_LOST:
599  break;
600 
601  // Size changed is called before resized.
602  // We can ensure the video framebuffer is valid here.
603  case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
604  LOG_DP << "events/SIZE_CHANGED "
605  << event.window.data1 << 'x' << event.window.data2;
606  video::update_buffers(false);
607  break;
608 
609  // Resized comes after size_changed.
610  // Here we can trigger any watchers for resize events.
611  // Video settings such as game_canvas_size() will be correct.
612  case SDL_EVENT_WINDOW_RESIZED:
613  LOG_DP << "events/RESIZED "
614  << event.window.data1 << 'x' << event.window.data2;
616  break;
617 
618  // Once everything has had a chance to respond to the resize,
619  // an expose is triggered to display the changed content.
620  case SDL_EVENT_WINDOW_EXPOSED:
621  LOG_DP << "events/EXPOSED";
623  break;
624 
625  case SDL_EVENT_WINDOW_MAXIMIZED:
626  LOG_DP << "events/MAXIMIZED";
627  prefs::get().set_maximized(true);
628  break;
629  case SDL_EVENT_WINDOW_RESTORED:
630  LOG_DP << "events/RESTORED";
631  prefs::get().set_maximized(prefs::get().fullscreen());
632  break;
633  case SDL_EVENT_WINDOW_SHOWN:
634  case SDL_EVENT_WINDOW_MOVED:
635  // Not used.
636  break;
637  }
638 
639  raise_window_event(event);
640 
641  // This event was just distributed, don't re-distribute.
642  continue;
643  }
644 
645  switch(event.type) {
646  case SDL_EVENT_MOUSE_MOTION: {
647  // Always make sure a cursor is displayed if the mouse moves or if the user clicks
648  cursor::set_focus(true);
649  process_tooltip_strings(event.motion.x, event.motion.y);
650  break;
651  }
652 
653  case SDL_EVENT_MOUSE_BUTTON_DOWN: {
654  // Always make sure a cursor is displayed if the mouse moves or if the user clicks
655  cursor::set_focus(true);
656  break;
657  }
658 
659 #ifndef __APPLE__
660  case SDL_EVENT_KEY_DOWN: {
661  if(event.key.key == SDLK_F4 &&
662  (event.key.mod == SDL_KMOD_RALT || event.key.mod == SDL_KMOD_LALT)
663  ) {
665  continue; // this event is already handled
666  }
667  break;
668  }
669 #endif
670 
671 #ifdef _WIN32
672 // TODO SDL3: needs to be replaced with https://wiki.libsdl.org/SDL3/SDL_SetWindowsMessageHook
673 // syswm stuff has been removed overall, so windows_tray_notification::handle_system_event is also generally invalid
674  // case SDL_SYSWMEVENT: {
675  // windows_tray_notification::handle_system_event(event);
676  // break;
677  // }
678 #endif
679 
680  case SDL_EVENT_QUIT: {
682  continue; // this event is already handled.
683  }
684  }
685 
686  for(auto global_handler : event_contexts.front().handlers) {
687  global_handler->handle_event(event);
688  }
689 
690  if(event_contexts.empty() == false) {
691  // As pump() can recurse, pretty much anything can happen here
692  // including destroying handlers or the event context.
693  std::size_t ec_index = event_contexts.size();
694  context& c = event_contexts.back();
695  handler_list& h = c.handlers;
696  std::size_t h_size = h.size();
697  for(auto it = h.begin(); it != h.end(); ++it) {
698  // Pass the event on to the handler.
699  (*it)->handle_event(event);
700  // Escape if anything has changed.
701  if(event_contexts.size() != ec_index) {
702  LOG_EV << "ec size changed! bugging out";
703  break;
704  }
705  if(h_size != h.size()) {
706  LOG_EV << "h size changed! bugging out";
707  break;
708  }
709  }
710  }
711  }
712 
713  // Inform the pump monitors that an events::pump() has occurred
714  for(auto monitor : pump_monitors) {
715  monitor->process();
716  }
717 }
718 
719 void draw()
720 {
722 }
723 
725 {
726  if(event_contexts.empty() == false) {
727  event_contexts.back().add_staging_handlers();
728 
729  for(auto handler : event_contexts.back().handlers) {
730  handler->process_event();
731  }
732  }
733 }
734 
736 {
738  SDL_Event event;
739  event.type = SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED;
740  event.window.windowID = 0; // We don't check this anyway... I think...
741  event.window.data1 = size.x;
742  event.window.data2 = size.y;
743 
744  raise_window_event(event);
745 }
746 
747 void process_tooltip_strings(int mousex, int mousey)
748 {
749  if(event_contexts.empty() == false) {
750  for(auto handler : event_contexts.back().handlers) {
751  handler->process_tooltip_string(mousex, mousey);
752  }
753  }
754 }
755 
756 /* The constants for the minimum and maximum are picked from the headers. */
757 #define INPUT_MIN 0x300
758 #define INPUT_MAX 0x8FF
759 
760 bool is_input(const SDL_Event& event)
761 {
762  return event.type >= INPUT_MIN && event.type <= INPUT_MAX;
763 }
764 
766 {
767  SDL_FlushEvents(INPUT_MIN, INPUT_MAX);
768 }
769 
770 bool is_touch(const SDL_MouseButtonEvent &event)
771 {
772  return event.which == SDL_TOUCH_MOUSEID;
773 }
774 
775 bool is_touch(const SDL_MouseMotionEvent &event)
776 {
777  return event.which == SDL_TOUCH_MOUSEID;
778 }
779 
780 void call_in_main_thread(const std::function<void(void)>& f)
781 {
782  if(is_in_main_thread()) {
783  // nothing special to do if called from the main thread.
784  f();
785  return;
786  }
787 
788  invoked_function_data fdata{f};
789 
790  SDL_Event sdl_event;
791  sdl::UserEvent sdl_userevent(INVOKE_FUNCTION_EVENT, &fdata);
792 
793  sdl_event.type = INVOKE_FUNCTION_EVENT;
794  sdl_event.user = sdl_userevent;
795 
796  SDL_PushEvent(&sdl_event);
797 
798  // Block until execution is complete in the main thread. Rethrows any exceptions.
799  fdata.finished.get_future().wait();
800 }
801 
802 } // 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:61
std::vector< sdl_handler * > staging_handlers
Definition: events.hpp:62
handler_list handlers
Definition: events.hpp:60
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:80
virtual std::vector< sdl_handler * > handler_members()
Definition: events.hpp:108
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:758
#define LOG_EV
Definition: events.cpp:50
#define INPUT_MIN
Definition: events.cpp:757
#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:29
std::vector< events::sdl_handler * > sdl_handler_vector
Definition: events.hpp:197
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:211
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:735
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:765
void draw()
Trigger a draw cycle.
Definition: events.cpp:719
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:760
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:780
void raise_process_event()
Definition: events.cpp:724
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:747
std::list< sdl_handler * > handler_list
Definition: events.hpp:34
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:770
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:438
point window_size()
Returns the size of the window in display units / screen coordinates.
Definition: video.cpp:425
void update_buffers(bool autoupdate)
Update buffers to match current resolution and pixel scale settings.
Definition: video.cpp:856
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
Holds a 2D point.
Definition: point.hpp:25
mock_char c
#define h
#define f