The Battle for Wesnoth  1.19.8+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 
470 
472 {
473  main_thread = std::this_thread::get_id();
474 }
475 
476 // this should probably be elsewhere, but as the main thread is already
477 // being tracked here, this went here.
479 {
480  return std::this_thread::get_id() == main_thread;
481 }
482 
483 void pump()
484 {
485  if(!is_in_main_thread()) {
486  // Can only call this on the main thread!
487  return;
488  }
489 
490  SDL_Event temp_event;
491  int poll_count = 0;
492  int begin_ignoring = 0;
493 
494  std::vector<SDL_Event> events;
495  while(SDL_PollEvent(&temp_event)) {
496  if(temp_event.type == INVOKE_FUNCTION_EVENT) {
497  static_cast<invoked_function_data*>(temp_event.user.data1)->call();
498  continue;
499  }
500 
501  ++poll_count;
502 
503  if(!begin_ignoring && temp_event.type == SDL_WINDOWEVENT && (
504  temp_event.window.event == SDL_WINDOWEVENT_ENTER ||
505  temp_event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED)
506  ) {
507  begin_ignoring = poll_count;
508  } else if(begin_ignoring > 0 && is_input(temp_event)) {
509  // ignore user input events that occurred after the window was activated
510  continue;
511  }
512 
513  events.push_back(temp_event);
514  }
515 
516  auto ev_it = events.begin();
517  for(int i = 1; i < begin_ignoring; ++i) {
518  if(is_input(*ev_it)) {
519  // ignore user input events that occurred before the window was activated
520  ev_it = events.erase(ev_it);
521  } else {
522  ++ev_it;
523  }
524  }
525 
526  for(SDL_Event& event : events) {
527  for(context& c : event_contexts) {
528  c.add_staging_handlers();
529  }
530 
531 #ifdef MOUSE_TOUCH_EMULATION
532  switch (event.type) {
533  // TODO: Implement SDL_MULTIGESTURE. Some day.
534  case SDL_MOUSEMOTION:
535  if(event.motion.which != SDL_TOUCH_MOUSEID && event.motion.state == 0) {
536  return;
537  }
538 
539  if(event.motion.state & SDL_BUTTON(SDL_BUTTON_RIGHT))
540  {
541  // Events are given by SDL in draw space
543 
544  // TODO: Check if SDL_FINGERMOTION is actually signaled for COMPLETE motions (I doubt, but tbs)
545  SDL_Event touch_event;
546  touch_event.type = SDL_FINGERMOTION;
547  touch_event.tfinger.type = SDL_FINGERMOTION;
548  touch_event.tfinger.timestamp = event.motion.timestamp;
549  touch_event.tfinger.touchId = 1;
550  touch_event.tfinger.fingerId = 1;
551  touch_event.tfinger.dx = static_cast<float>(event.motion.xrel) / c.x;
552  touch_event.tfinger.dy = static_cast<float>(event.motion.yrel) / c.y;
553  touch_event.tfinger.x = static_cast<float>(event.motion.x) / c.x;
554  touch_event.tfinger.y = static_cast<float>(event.motion.y) / c.y;
555  touch_event.tfinger.pressure = 1;
556  ::SDL_PushEvent(&touch_event);
557 
558  event.motion.state = SDL_BUTTON(SDL_BUTTON_LEFT);
559  event.motion.which = SDL_TOUCH_MOUSEID;
560  }
561  break;
562  case SDL_MOUSEBUTTONDOWN:
563  case SDL_MOUSEBUTTONUP:
564  if(event.button.button == SDL_BUTTON_RIGHT)
565  {
566  event.button.button = SDL_BUTTON_LEFT;
567  event.button.which = SDL_TOUCH_MOUSEID;
568 
569  // Events are given by SDL in draw space
571 
572  SDL_Event touch_event;
573  touch_event.type = (event.type == SDL_MOUSEBUTTONDOWN) ? SDL_FINGERDOWN : SDL_FINGERUP;
574  touch_event.tfinger.type = touch_event.type;
575  touch_event.tfinger.timestamp = event.button.timestamp;
576  touch_event.tfinger.touchId = 1;
577  touch_event.tfinger.fingerId = 1;
578  touch_event.tfinger.dx = 0;
579  touch_event.tfinger.dy = 0;
580  touch_event.tfinger.x = static_cast<float>(event.button.x) / c.x;
581  touch_event.tfinger.y = static_cast<float>(event.button.y) / c.y;
582  touch_event.tfinger.pressure = 1;
583  ::SDL_PushEvent(&touch_event);
584 
585  }
586  break;
587  default:
588  break;
589  }
590 #endif
591 
592  switch(event.type) {
593  case SDL_WINDOWEVENT:
594  switch(event.window.event) {
595  case SDL_WINDOWEVENT_ENTER:
596  case SDL_WINDOWEVENT_FOCUS_GAINED:
598  break;
599 
600  case SDL_WINDOWEVENT_LEAVE:
601  case SDL_WINDOWEVENT_FOCUS_LOST:
603  break;
604 
605  // Size changed is called before resized.
606  // We can ensure the video framebuffer is valid here.
607  case SDL_WINDOWEVENT_SIZE_CHANGED:
608  LOG_DP << "events/SIZE_CHANGED "
609  << event.window.data1 << 'x' << event.window.data2;
610  video::update_buffers(false);
611  break;
612 
613  // Resized comes after size_changed.
614  // Here we can trigger any watchers for resize events.
615  // Video settings such as game_canvas_size() will be correct.
616  case SDL_WINDOWEVENT_RESIZED:
617  LOG_DP << "events/RESIZED "
618  << event.window.data1 << 'x' << event.window.data2;
620  break;
621 
622  // Once everything has had a chance to respond to the resize,
623  // an expose is triggered to display the changed content.
624  case SDL_WINDOWEVENT_EXPOSED:
625  LOG_DP << "events/EXPOSED";
627  break;
628 
629  case SDL_WINDOWEVENT_MAXIMIZED:
630  LOG_DP << "events/MAXIMIZED";
631  prefs::get().set_maximized(true);
632  break;
633  case SDL_WINDOWEVENT_RESTORED:
634  LOG_DP << "events/RESTORED";
635  prefs::get().set_maximized(prefs::get().fullscreen());
636  break;
637  case SDL_WINDOWEVENT_SHOWN:
638  case SDL_WINDOWEVENT_MOVED:
639  // Not used.
640  break;
641  }
642 
643  raise_window_event(event);
644 
645  // This event was just distributed, don't re-distribute.
646  continue;
647 
648  case SDL_MOUSEMOTION: {
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_MOUSEBUTTONDOWN: {
656  // Always make sure a cursor is displayed if the mouse moves or if the user clicks
657  cursor::set_focus(true);
658  if(event.button.button == SDL_BUTTON_LEFT || event.button.which == SDL_TOUCH_MOUSEID) {
659  if(event.button.clicks == 2) {
660  sdl::UserEvent user_event(DOUBLE_CLICK_EVENT, event.button.which, event.button.x, event.button.y);
661  ::SDL_PushEvent(reinterpret_cast<SDL_Event*>(&user_event));
662  }
663  }
664  break;
665  }
666 
667 #ifndef __APPLE__
668  case SDL_KEYDOWN: {
669  if(event.key.keysym.sym == SDLK_F4 &&
670  (event.key.keysym.mod == KMOD_RALT || event.key.keysym.mod == KMOD_LALT)
671  ) {
673  continue; // this event is already handled
674  }
675  break;
676  }
677 #endif
678 
679 #if defined _WIN32
680  case SDL_SYSWMEVENT: {
682  break;
683  }
684 #endif
685 
686  case SDL_QUIT: {
688  continue; // this event is already handled.
689  }
690  }
691 
692  for(auto global_handler : event_contexts.front().handlers) {
693  global_handler->handle_event(event);
694  }
695 
696  if(event_contexts.empty() == false) {
697  // As pump() can recurse, pretty much anything can happen here
698  // including destroying handlers or the event context.
699  size_t ec_index = event_contexts.size();
700  context& c = event_contexts.back();
701  handler_list& h = c.handlers;
702  size_t h_size = h.size();
703  for(auto it = h.begin(); it != h.end(); ++it) {
704  // Pass the event on to the handler.
705  (*it)->handle_event(event);
706  // Escape if anything has changed.
707  if(event_contexts.size() != ec_index) {
708  LOG_EV << "ec size changed! bugging out";
709  break;
710  }
711  if(h_size != h.size()) {
712  LOG_EV << "h size changed! bugging out";
713  break;
714  }
715  }
716  }
717  }
718 
719  // Inform the pump monitors that an events::pump() has occurred
720  for(auto monitor : pump_monitors) {
721  monitor->process();
722  }
723 }
724 
725 void draw()
726 {
728 }
729 
731 {
732  if(event_contexts.empty() == false) {
733  event_contexts.back().add_staging_handlers();
734 
735  for(auto handler : event_contexts.back().handlers) {
736  handler->process_event();
737  }
738  }
739 }
740 
742 {
744  SDL_Event event;
745  event.window.type = SDL_WINDOWEVENT;
746  event.window.event = SDL_WINDOWEVENT_SIZE_CHANGED;
747  event.window.windowID = 0; // We don't check this anyway... I think...
748  event.window.data1 = size.x;
749  event.window.data2 = size.y;
750 
751  raise_window_event(event);
752 }
753 
754 void process_tooltip_strings(int mousex, int mousey)
755 {
756  if(event_contexts.empty() == false) {
757  for(auto handler : event_contexts.back().handlers) {
758  handler->process_tooltip_string(mousex, mousey);
759  }
760  }
761 }
762 
763 /* The constants for the minimum and maximum are picked from the headers. */
764 #define INPUT_MIN 0x300
765 #define INPUT_MAX 0x8FF
766 
767 bool is_input(const SDL_Event& event)
768 {
769  return event.type >= INPUT_MIN && event.type <= INPUT_MAX;
770 }
771 
773 {
774  SDL_FlushEvents(INPUT_MIN, INPUT_MAX);
775 }
776 
777 void call_in_main_thread(const std::function<void(void)>& f)
778 {
779  if(is_in_main_thread()) {
780  // nothing special to do if called from the main thread.
781  f();
782  return;
783  }
784 
785  invoked_function_data fdata{f};
786 
787  SDL_Event sdl_event;
788  sdl::UserEvent sdl_userevent(INVOKE_FUNCTION_EVENT, &fdata);
789 
790  sdl_event.type = INVOKE_FUNCTION_EVENT;
791  sdl_event.user = sdl_userevent;
792 
793  SDL_PushEvent(&sdl_event);
794 
795  // Block until execution is complete in the main thread. Rethrows any exceptions.
796  fdata.finished.get_future().wait();
797 }
798 
799 } // 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:765
#define LOG_EV
Definition: events.cpp:50
#define INPUT_MIN
Definition: events.cpp:764
#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:184
std::size_t i
Definition: function.cpp:1029
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: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:741
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:772
void draw()
Trigger a draw cycle.
Definition: events.cpp:725
static std::thread::id main_thread
Definition: events.cpp:469
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:767
void set_main_thread()
Definition: events.cpp:471
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:777
void raise_process_event()
Definition: events.cpp:730
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:754
std::list< sdl_handler * > handler_list
Definition: events.hpp:35
bool is_in_main_thread()
Definition: events.cpp:478
void pump()
Process all events currently in the queue.
Definition: events.cpp:483
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
constexpr auto reverse
Definition: ranges.hpp:40
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:844
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
Holds a 2D point.
Definition: point.hpp:25
mock_char c
#define h
#define f