The Battle for Wesnoth  1.19.7+dev
events.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2024
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
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 <SDL2/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 {
100  if(handlers.cend() != std::find(handlers.cbegin(), handlers.cend(), ptr)) {
101  return true;
102  }
103  return staging_handlers.cend() != std::find(staging_handlers.cbegin(), staging_handlers.cend(), ptr);
104 }
105 
107 {
108  static int depth = 0;
109  ++depth;
110 
111  // The handler is most likely on the back of the events list,
112  // so look there first, otherwise do a complete search.
113  if(!handlers.empty() && handlers.back() == ptr) {
114  if(focused_handler != handlers.end() && *focused_handler == ptr) {
115  focused_handler = handlers.end();
116  }
117 
118  handlers.pop_back();
119  } else {
120  const handler_list::iterator i = std::find(handlers.begin(), handlers.end(), ptr);
121 
122  if(i == handlers.end()) {
123  --depth;
124 
125  // The handler may be in the staging area. Search it from there.
126  auto j = std::find(staging_handlers.begin(), staging_handlers.end(), ptr);
127  if(j != staging_handlers.end()) {
128  staging_handlers.erase(j);
129  return true;
130  } else {
131  return false;
132  }
133  }
134 
135  if(i == focused_handler) {
137  }
138 
139  handlers.erase(i);
140  }
141 
142  --depth;
143 
144  if(depth == 0) {
145  cycle_focus();
146  } else {
147  focused_handler = handlers.end();
148  }
149 
150  return true;
151 }
152 
154 {
155  if(handlers.begin() == handlers.end()) {
156  return;
157  }
158 
161 
162  if(last != handlers.begin()) {
163  --last;
164  }
165 
166  if(current == handlers.end()) {
167  current = handlers.begin();
168  } else {
169  ++current;
170  }
171 
172  while(current != last) {
173  if(current != handlers.end() && (*current)->requires_event_focus()) {
174  focused_handler = current;
175  break;
176  }
177 
178  if(current == handlers.end()) {
179  current = handlers.begin();
180  } else {
181  ++current;
182  }
183  }
184 }
185 
187 {
188  const handler_list::iterator i = std::find(handlers.begin(), handlers.end(), ptr);
189  if(i != handlers.end() && (*i)->requires_event_focus()) {
190  focused_handler = i;
191  }
192 }
193 
195 {
196  std::copy(staging_handlers.begin(), staging_handlers.end(), std::back_inserter(handlers));
197  staging_handlers.clear();
198 }
199 
201 {
202  for(sdl_handler* h : handlers) {
203  if(h->has_joined()) {
204  h->has_joined_ = false;
205  }
206 
207  if(h->has_joined_global()) {
208  h->has_joined_global_ = false;
209  }
210  }
211 }
212 
213 // This object stores all the event handlers. It is a stack of event 'contexts'.
214 // a new event context is created when e.g. a modal dialog is opened, and then
215 // closed when that dialog is closed. Each context contains a list of the handlers
216 // in that context. The current context is the one on the top of the stack.
217 // The global context must always be in the first position.
218 std::deque<context> event_contexts;
219 
220 std::vector<pump_monitor*> pump_monitors;
221 
223 {
224  pump_monitors.push_back(this);
225 }
226 
228 {
230 }
231 
233 {
234  event_contexts.emplace_back();
235 }
236 
238 {
239  assert(event_contexts.empty() == false);
240  event_contexts.pop_back();
241 }
242 
243 sdl_handler::sdl_handler(const bool auto_join)
244  : has_joined_(false)
245  , has_joined_global_(false)
246 {
247  if(auto_join) {
248  assert(!event_contexts.empty());
249  event_contexts.back().add_handler(this);
250  has_joined_ = true;
251  }
252 }
253 
255  : has_joined_(that.has_joined_)
256  , has_joined_global_(that.has_joined_global_)
257 {
258  if(has_joined_global_) {
259  assert(!event_contexts.empty());
260  event_contexts.front().add_handler(this);
261  } else if(has_joined_) {
262  bool found_context = false;
264  if(context.has_handler(&that)) {
265  found_context = true;
266  context.add_handler(this);
267  break;
268  }
269  }
270 
271  if (!found_context) {
272  throw std::logic_error("Copy-constructing a sdl_handler that has_joined_ but can't be found by searching contexts");
273  }
274  }
275 }
276 
278 {
279  if(that.has_joined_global_) {
280  join_global();
281  } else if(that.has_joined_) {
283  if(context.has_handler(&that)) {
284  join(context);
285  break;
286  }
287  }
288  } else if(has_joined_) {
289  leave();
290  } else if(has_joined_global_) {
291  leave_global();
292  }
293 
294  return *this;
295 }
296 
298 {
299  if(has_joined_) {
300  leave();
301  }
302 
303  if(has_joined_global_) {
304  leave_global();
305  }
306 }
307 
309 {
310  // this assert will fire if someone will inadvertently try to join
311  // an event context but might end up in the global context instead.
312  assert(&event_contexts.back() != &event_contexts.front());
313 
314  join(event_contexts.back());
315 }
316 
318 {
319  if(has_joined_global_) {
320  leave_global();
321  }
322 
323  if(has_joined_) {
324  leave(); // should not be in multiple event contexts
325  }
326 
327  // join self
328  c.add_handler(this);
329  has_joined_ = true;
330 
331  // instruct members to join
332  for(auto member : handler_members()) {
333  member->join(c);
334  }
335 }
336 
338 {
339  if(has_joined_) {
340  leave(); // should not be in multiple event contexts
341  }
342 
344  if(context.has_handler(parent)) {
345  join(context);
346  return;
347  }
348  }
349 
350  join(event_contexts.back());
351 }
352 
354 {
356 
357  if(members.empty()) {
358  assert(event_contexts.empty() == false);
359  }
360 
361  for(auto member : members) {
362  member->leave();
363  }
364 
366  if(context.remove_handler(this)) {
367  break;
368  }
369  }
370 
371  has_joined_ = false;
372 }
373 
375 {
376  if(has_joined_) {
377  leave();
378  }
379 
380  if(has_joined_global_) {
381  leave_global(); // Should not be in multiple event contexts
382  }
383 
384  // Join self
385  event_contexts.front().add_handler(this);
386  has_joined_global_ = true;
387 
388  // Instruct members to join
389  for(auto member : handler_members()) {
390  member->join_global();
391  }
392 }
393 
395 {
396  for(auto member : handler_members()) {
397  member->leave_global();
398  }
399 
400  event_contexts.front().remove_handler(this);
401 
402  has_joined_global_ = false;
403 }
404 
405 void focus_handler(const sdl_handler* ptr)
406 {
407  if(event_contexts.empty() == false) {
408  event_contexts.back().set_focus(ptr);
409  }
410 }
411 
412 bool has_focus(const sdl_handler* hand, const SDL_Event* event)
413 {
414  if(event_contexts.empty()) {
415  return true;
416  }
417 
418  if(hand->requires_event_focus(event) == false) {
419  return true;
420  }
421 
422  const handler_list::iterator foc = event_contexts.back().focused_handler;
423  auto& handlers = event_contexts.back().handlers;
424 
425  // If no-one has focus at the moment, this handler obviously wants
426  // focus, so give it to it.
427  if(foc == handlers.end()) {
428  focus_handler(hand);
429  return true;
430  }
431 
432  sdl_handler* const foc_hand = *foc;
433  if(foc_hand == hand) {
434  return true;
435  } else if(!foc_hand->requires_event_focus(event)) {
436  // If the currently focused handler doesn't need focus for this event
437  // allow the most recent interested handler to take care of it
438  for(auto i = handlers.rbegin(); i != handlers.rend(); ++i) {
439  sdl_handler* const thief_hand = *i;
440 
441  if(thief_hand != foc_hand && thief_hand->requires_event_focus(event)) {
442  // Steal focus
443  focus_handler(thief_hand);
444 
445  // Position the previously focused handler to allow stealing back
446  handlers.splice(handlers.end(), handlers, foc);
447 
448  return thief_hand == hand;
449  }
450  }
451  }
452 
453  return false;
454 }
455 
456 static void raise_window_event(const SDL_Event& event)
457 {
458  for(auto& context : event_contexts) {
459  for(auto handler : context.handlers) {
460  handler->handle_window_event(event);
461  }
462  }
463 
464  for(auto global_handler : event_contexts.front().handlers) {
465  global_handler->handle_window_event(event);
466  }
467 }
468 
469 // TODO: I'm uncertain if this is always safe to call at static init; maybe set in main() instead?
470 static const std::thread::id main_thread = std::this_thread::get_id();
471 
472 // this should probably be elsewhere, but as the main thread is already
473 // being tracked here, this went here.
475 {
476  return std::this_thread::get_id() == main_thread;
477 }
478 
479 void pump()
480 {
481  if(!is_in_main_thread()) {
482  // Can only call this on the main thread!
483  return;
484  }
485 
486  SDL_Event temp_event;
487  int poll_count = 0;
488  int begin_ignoring = 0;
489 
490  std::vector<SDL_Event> events;
491  while(SDL_PollEvent(&temp_event)) {
492  if(temp_event.type == INVOKE_FUNCTION_EVENT) {
493  static_cast<invoked_function_data*>(temp_event.user.data1)->call();
494  continue;
495  }
496 
497  ++poll_count;
498 
499  if(!begin_ignoring && temp_event.type == SDL_WINDOWEVENT && (
500  temp_event.window.event == SDL_WINDOWEVENT_ENTER ||
501  temp_event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED)
502  ) {
503  begin_ignoring = poll_count;
504  } else if(begin_ignoring > 0 && is_input(temp_event)) {
505  // ignore user input events that occurred after the window was activated
506  continue;
507  }
508 
509  events.push_back(temp_event);
510  }
511 
512  auto ev_it = events.begin();
513  for(int i = 1; i < begin_ignoring; ++i) {
514  if(is_input(*ev_it)) {
515  // ignore user input events that occurred before the window was activated
516  ev_it = events.erase(ev_it);
517  } else {
518  ++ev_it;
519  }
520  }
521 
522  for(SDL_Event& event : events) {
523  for(context& c : event_contexts) {
524  c.add_staging_handlers();
525  }
526 
527 #ifdef MOUSE_TOUCH_EMULATION
528  switch (event.type) {
529  // TODO: Implement SDL_MULTIGESTURE. Some day.
530  case SDL_MOUSEMOTION:
531  if(event.motion.which != SDL_TOUCH_MOUSEID && event.motion.state == 0) {
532  return;
533  }
534 
535  if(event.motion.state & SDL_BUTTON(SDL_BUTTON_RIGHT))
536  {
537  // Events are given by SDL in draw space
539 
540  // TODO: Check if SDL_FINGERMOTION is actually signaled for COMPLETE motions (I doubt, but tbs)
541  SDL_Event touch_event;
542  touch_event.type = SDL_FINGERMOTION;
543  touch_event.tfinger.type = SDL_FINGERMOTION;
544  touch_event.tfinger.timestamp = event.motion.timestamp;
545  touch_event.tfinger.touchId = 1;
546  touch_event.tfinger.fingerId = 1;
547  touch_event.tfinger.dx = static_cast<float>(event.motion.xrel) / c.x;
548  touch_event.tfinger.dy = static_cast<float>(event.motion.yrel) / c.y;
549  touch_event.tfinger.x = static_cast<float>(event.motion.x) / c.x;
550  touch_event.tfinger.y = static_cast<float>(event.motion.y) / c.y;
551  touch_event.tfinger.pressure = 1;
552  ::SDL_PushEvent(&touch_event);
553 
554  event.motion.state = SDL_BUTTON(SDL_BUTTON_LEFT);
555  event.motion.which = SDL_TOUCH_MOUSEID;
556  }
557  break;
558  case SDL_MOUSEBUTTONDOWN:
559  case SDL_MOUSEBUTTONUP:
560  if(event.button.button == SDL_BUTTON_RIGHT)
561  {
562  event.button.button = SDL_BUTTON_LEFT;
563  event.button.which = SDL_TOUCH_MOUSEID;
564 
565  // Events are given by SDL in draw space
567 
568  SDL_Event touch_event;
569  touch_event.type = (event.type == SDL_MOUSEBUTTONDOWN) ? SDL_FINGERDOWN : SDL_FINGERUP;
570  touch_event.tfinger.type = touch_event.type;
571  touch_event.tfinger.timestamp = event.button.timestamp;
572  touch_event.tfinger.touchId = 1;
573  touch_event.tfinger.fingerId = 1;
574  touch_event.tfinger.dx = 0;
575  touch_event.tfinger.dy = 0;
576  touch_event.tfinger.x = static_cast<float>(event.button.x) / c.x;
577  touch_event.tfinger.y = static_cast<float>(event.button.y) / c.y;
578  touch_event.tfinger.pressure = 1;
579  ::SDL_PushEvent(&touch_event);
580 
581  }
582  break;
583  default:
584  break;
585  }
586 #endif
587 
588  switch(event.type) {
589  case SDL_WINDOWEVENT:
590  switch(event.window.event) {
591  case SDL_WINDOWEVENT_ENTER:
592  case SDL_WINDOWEVENT_FOCUS_GAINED:
594  break;
595 
596  case SDL_WINDOWEVENT_LEAVE:
597  case SDL_WINDOWEVENT_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_WINDOWEVENT_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_WINDOWEVENT_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_WINDOWEVENT_EXPOSED:
621  LOG_DP << "events/EXPOSED";
623  break;
624 
625  case SDL_WINDOWEVENT_MAXIMIZED:
626  LOG_DP << "events/MAXIMIZED";
627  prefs::get().set_maximized(true);
628  break;
629  case SDL_WINDOWEVENT_RESTORED:
630  LOG_DP << "events/RESTORED";
631  prefs::get().set_maximized(prefs::get().fullscreen());
632  break;
633  case SDL_WINDOWEVENT_SHOWN:
634  case SDL_WINDOWEVENT_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  case SDL_MOUSEMOTION: {
645  // Always make sure a cursor is displayed if the mouse moves or if the user clicks
646  cursor::set_focus(true);
647  process_tooltip_strings(event.motion.x, event.motion.y);
648  break;
649  }
650 
651  case SDL_MOUSEBUTTONDOWN: {
652  // Always make sure a cursor is displayed if the mouse moves or if the user clicks
653  cursor::set_focus(true);
654  if(event.button.button == SDL_BUTTON_LEFT || event.button.which == SDL_TOUCH_MOUSEID) {
655  if(event.button.clicks == 2) {
656  sdl::UserEvent user_event(DOUBLE_CLICK_EVENT, event.button.which, event.button.x, event.button.y);
657  ::SDL_PushEvent(reinterpret_cast<SDL_Event*>(&user_event));
658  }
659  }
660  break;
661  }
662 
663 #ifndef __APPLE__
664  case SDL_KEYDOWN: {
665  if(event.key.keysym.sym == SDLK_F4 &&
666  (event.key.keysym.mod == KMOD_RALT || event.key.keysym.mod == KMOD_LALT)
667  ) {
669  continue; // this event is already handled
670  }
671  break;
672  }
673 #endif
674 
675 #if defined _WIN32
676  case SDL_SYSWMEVENT: {
678  break;
679  }
680 #endif
681 
682  case SDL_QUIT: {
684  continue; // this event is already handled.
685  }
686  }
687 
688  for(auto global_handler : event_contexts.front().handlers) {
689  global_handler->handle_event(event);
690  }
691 
692  if(event_contexts.empty() == false) {
693  // As pump() can recurse, pretty much anything can happen here
694  // including destroying handlers or the event context.
695  size_t ec_index = event_contexts.size();
696  context& c = event_contexts.back();
697  handler_list& h = c.handlers;
698  size_t h_size = h.size();
699  for(auto it = h.begin(); it != h.end(); ++it) {
700  // Pass the event on to the handler.
701  (*it)->handle_event(event);
702  // Escape if anything has changed.
703  if(event_contexts.size() != ec_index) {
704  LOG_EV << "ec size changed! bugging out";
705  break;
706  }
707  if(h_size != h.size()) {
708  LOG_EV << "h size changed! bugging out";
709  break;
710  }
711  }
712  }
713  }
714 
715  // Inform the pump monitors that an events::pump() has occurred
716  for(auto monitor : pump_monitors) {
717  monitor->process();
718  }
719 }
720 
721 void draw()
722 {
724 }
725 
727 {
728  if(event_contexts.empty() == false) {
729  event_contexts.back().add_staging_handlers();
730 
731  for(auto handler : event_contexts.back().handlers) {
732  handler->process_event();
733  }
734  }
735 }
736 
738 {
740  SDL_Event event;
741  event.window.type = SDL_WINDOWEVENT;
742  event.window.event = SDL_WINDOWEVENT_SIZE_CHANGED;
743  event.window.windowID = 0; // We don't check this anyway... I think...
744  event.window.data1 = size.x;
745  event.window.data2 = size.y;
746 
747  raise_window_event(event);
748 }
749 
750 void process_tooltip_strings(int mousex, int mousey)
751 {
752  if(event_contexts.empty() == false) {
753  for(auto handler : event_contexts.back().handlers) {
754  handler->process_tooltip_string(mousex, mousey);
755  }
756  }
757 }
758 
759 /* The constants for the minimum and maximum are picked from the headers. */
760 #define INPUT_MIN 0x300
761 #define INPUT_MAX 0x8FF
762 
763 bool is_input(const SDL_Event& event)
764 {
765  return event.type >= INPUT_MIN && event.type <= INPUT_MAX;
766 }
767 
769 {
770  SDL_FlushEvents(INPUT_MIN, INPUT_MAX);
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 } // end events namespace
void set_focus(const sdl_handler *ptr)
Definition: events.cpp:186
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:153
handler_list::iterator focused_handler
Definition: events.hpp:62
std::vector< sdl_handler * > staging_handlers
Definition: events.hpp:63
handler_list handlers
Definition: events.hpp:61
void add_handler(sdl_handler *ptr)
Definition: events.cpp:89
bool remove_handler(sdl_handler *ptr)
Definition: events.cpp:106
void add_staging_handlers()
Definition: events.cpp:194
virtual ~pump_monitor()
Definition: events.cpp:227
virtual bool requires_event_focus(const SDL_Event *=nullptr) const
Definition: events.hpp:81
virtual std::vector< sdl_handler * > handler_members()
Definition: events.hpp:109
virtual void join()
Definition: events.cpp:308
virtual void leave()
Definition: events.cpp:353
virtual void join_global()
Definition: events.cpp:374
virtual void join_same(sdl_handler *parent)
Definition: events.cpp:337
virtual void leave_global()
Definition: events.cpp:394
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:297
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:323
static void handle_system_event(const SDL_Event &event)
Frees resources when a notification disappears, switches user to the wesnoth window if the notificati...
#define INPUT_MAX
Definition: events.cpp:761
#define LOG_EV
Definition: events.cpp:50
#define INPUT_MIN
Definition: events.cpp:760
#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:30
#define DOUBLE_CLICK_EVENT
Definition: events.hpp:24
std::vector< events::sdl_handler * > sdl_handler_vector
Definition: events.hpp:182
std::size_t i
Definition: function.cpp:1029
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:198
Standard logging facilities (interface).
void set_focus(bool focus)
Definition: cursor.cpp:221
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:737
bool has_focus(const sdl_handler *hand, const SDL_Event *event)
Definition: events.cpp:412
void discard_input()
Discards all input events.
Definition: events.cpp:768
void draw()
Trigger a draw cycle.
Definition: events.cpp:721
static void raise_window_event(const SDL_Event &event)
Definition: events.cpp:456
bool is_input(const SDL_Event &event)
Is the event an input event?
Definition: events.cpp:763
std::deque< context > event_contexts
Definition: events.cpp:218
std::vector< pump_monitor * > pump_monitors
Definition: events.cpp:220
void call_in_main_thread(const std::function< void(void)> &f)
Definition: events.cpp:773
void raise_process_event()
Definition: events.cpp:726
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:750
std::list< sdl_handler * > handler_list
Definition: events.hpp:35
bool is_in_main_thread()
Definition: events.cpp:474
static const std::thread::id main_thread
Definition: events.cpp:470
void pump()
Process all events currently in the queue.
Definition: events.cpp:479
void focus_handler(const sdl_handler *ptr)
Definition: events.cpp:405
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
auto reversed_view(T &container)
Definition: ranges.hpp:26
std::size_t erase(Container &container, const Value &value)
Convenience wrapper for using std::remove on a container.
Definition: general.hpp:117
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:140
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
void update_buffers(bool autoupdate)
Update buffers to match current resolution and pixel scale settings.
Definition: video.cpp:838
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
Holds a 2D point.
Definition: point.hpp:25
mock_char c
#define h
#define f