The Battle for Wesnoth  1.19.0-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"
22 #include "quit_confirmation.hpp"
23 #include "sdl/userevent.hpp"
24 #include "utils/ranges.hpp"
25 #include "utils/general.hpp"
26 #include "video.hpp"
27 
28 #if defined _WIN32
30 #endif
31 
32 #include <algorithm>
33 #include <cassert>
34 #include <deque>
35 #include <future>
36 #include <iterator>
37 #include <thread>
38 #include <utility>
39 #include <vector>
40 
41 #include <SDL2/SDL.h>
42 
43 #define ERR_GEN LOG_STREAM(err, lg::general)
44 
45 static lg::log_domain log_display("display");
46 #define LOG_DP LOG_STREAM(info, log_display)
47 
48 static lg::log_domain log_event("event");
49 #define LOG_EV LOG_STREAM(info, log_event)
50 #define DBG_EV LOG_STREAM(debug, log_event)
51 
52 namespace
53 {
54 struct invoked_function_data
55 {
56  explicit invoked_function_data(const std::function<void(void)>& func)
57  : f(func)
58  , finished()
59  {
60  }
61 
62  /** The actual function to call. */
63  const std::function<void(void)>& f;
64 
65  /** Whether execution in the main thread is complete. */
66  std::promise<void> finished;
67 
68  void call()
69  {
70  try {
71  f();
72  } catch(const video::quit&) {
73  // Handle this exception in the main thread.
74  throw;
75  } catch(...) {
76  DBG_EV << "Caught exception in invoked function: " << utils::get_unknown_exception_type();
77  finished.set_exception(std::current_exception());
78  return;
79  }
80 
81  finished.set_value();
82  }
83 };
84 }
85 
86 namespace events
87 {
89 {
90  /* Add new handlers to the staging list initially.
91  * This ensures that if an event handler adds more handlers, the new handlers
92  * won't be called for the event that caused them to be added.
93  */
94  staging_handlers.push_back(ptr);
95 }
96 
97 bool context::has_handler(const sdl_handler* ptr) const
98 {
99  if(handlers.cend() != std::find(handlers.cbegin(), handlers.cend(), ptr)) {
100  return true;
101  }
102  return staging_handlers.cend() != std::find(staging_handlers.cbegin(), staging_handlers.cend(), ptr);
103 }
104 
106 {
107  static int depth = 0;
108  ++depth;
109 
110  // The handler is most likely on the back of the events list,
111  // so look there first, otherwise do a complete search.
112  if(!handlers.empty() && handlers.back() == ptr) {
113  if(focused_handler != handlers.end() && *focused_handler == ptr) {
114  focused_handler = handlers.end();
115  }
116 
117  handlers.pop_back();
118  } else {
119  const handler_list::iterator i = std::find(handlers.begin(), handlers.end(), ptr);
120 
121  if(i == handlers.end()) {
122  --depth;
123 
124  // The handler may be in the staging area. Search it from there.
125  auto j = std::find(staging_handlers.begin(), staging_handlers.end(), ptr);
126  if(j != staging_handlers.end()) {
127  staging_handlers.erase(j);
128  return true;
129  } else {
130  return false;
131  }
132  }
133 
134  if(i == focused_handler) {
136  }
137 
138  handlers.erase(i);
139  }
140 
141  --depth;
142 
143  if(depth == 0) {
144  cycle_focus();
145  } else {
146  focused_handler = handlers.end();
147  }
148 
149  return true;
150 }
151 
153 {
154  if(handlers.begin() == handlers.end()) {
155  return;
156  }
157 
160 
161  if(last != handlers.begin()) {
162  --last;
163  }
164 
165  if(current == handlers.end()) {
166  current = handlers.begin();
167  } else {
168  ++current;
169  }
170 
171  while(current != last) {
172  if(current != handlers.end() && (*current)->requires_event_focus()) {
173  focused_handler = current;
174  break;
175  }
176 
177  if(current == handlers.end()) {
178  current = handlers.begin();
179  } else {
180  ++current;
181  }
182  }
183 }
184 
186 {
187  const handler_list::iterator i = std::find(handlers.begin(), handlers.end(), ptr);
188  if(i != handlers.end() && (*i)->requires_event_focus()) {
189  focused_handler = i;
190  }
191 }
192 
194 {
195  std::copy(staging_handlers.begin(), staging_handlers.end(), std::back_inserter(handlers));
196  staging_handlers.clear();
197 }
198 
200 {
201  for(sdl_handler* h : handlers) {
202  if(h->has_joined()) {
203  h->has_joined_ = false;
204  }
205 
206  if(h->has_joined_global()) {
207  h->has_joined_global_ = false;
208  }
209  }
210 }
211 
212 // This object stores all the event handlers. It is a stack of event 'contexts'.
213 // a new event context is created when e.g. a modal dialog is opened, and then
214 // closed when that dialog is closed. Each context contains a list of the handlers
215 // in that context. The current context is the one on the top of the stack.
216 // The global context must always be in the first position.
217 std::deque<context> event_contexts;
218 
219 std::vector<pump_monitor*> pump_monitors;
220 
222 {
223  pump_monitors.push_back(this);
224 }
225 
227 {
228  pump_monitors.erase(std::remove(pump_monitors.begin(), pump_monitors.end(), this), pump_monitors.end());
229 }
230 
232 {
233  event_contexts.emplace_back();
234 }
235 
237 {
238  assert(event_contexts.empty() == false);
239  event_contexts.pop_back();
240 }
241 
242 sdl_handler::sdl_handler(const bool auto_join)
243  : has_joined_(false)
244  , has_joined_global_(false)
245 {
246  if(auto_join) {
247  assert(!event_contexts.empty());
248  event_contexts.back().add_handler(this);
249  has_joined_ = true;
250  }
251 }
252 
254  : has_joined_(that.has_joined_)
255  , has_joined_global_(that.has_joined_global_)
256 {
257  if(has_joined_global_) {
258  assert(!event_contexts.empty());
259  event_contexts.front().add_handler(this);
260  } else if(has_joined_) {
261  bool found_context = false;
263  if(context.has_handler(&that)) {
264  found_context = true;
265  context.add_handler(this);
266  break;
267  }
268  }
269 
270  if (!found_context) {
271  throw std::logic_error("Copy-constructing a sdl_handler that has_joined_ but can't be found by searching contexts");
272  }
273  }
274 }
275 
277 {
278  if(that.has_joined_global_) {
279  join_global();
280  } else if(that.has_joined_) {
282  if(context.has_handler(&that)) {
283  join(context);
284  break;
285  }
286  }
287  } else if(has_joined_) {
288  leave();
289  } else if(has_joined_global_) {
290  leave_global();
291  }
292 
293  return *this;
294 }
295 
297 {
298  if(has_joined_) {
299  leave();
300  }
301 
302  if(has_joined_global_) {
303  leave_global();
304  }
305 }
306 
308 {
309  // this assert will fire if someone will inadvertently try to join
310  // an event context but might end up in the global context instead.
311  assert(&event_contexts.back() != &event_contexts.front());
312 
313  join(event_contexts.back());
314 }
315 
317 {
318  if(has_joined_global_) {
319  leave_global();
320  }
321 
322  if(has_joined_) {
323  leave(); // should not be in multiple event contexts
324  }
325 
326  // join self
327  c.add_handler(this);
328  has_joined_ = true;
329 
330  // instruct members to join
331  for(auto member : handler_members()) {
332  member->join(c);
333  }
334 }
335 
337 {
338  if(has_joined_) {
339  leave(); // should not be in multiple event contexts
340  }
341 
343  if(context.has_handler(parent)) {
344  join(context);
345  return;
346  }
347  }
348 
349  join(event_contexts.back());
350 }
351 
353 {
355 
356  if(members.empty()) {
357  assert(event_contexts.empty() == false);
358  }
359 
360  for(auto member : members) {
361  member->leave();
362  }
363 
365  if(context.remove_handler(this)) {
366  break;
367  }
368  }
369 
370  has_joined_ = false;
371 }
372 
374 {
375  if(has_joined_) {
376  leave();
377  }
378 
379  if(has_joined_global_) {
380  leave_global(); // Should not be in multiple event contexts
381  }
382 
383  // Join self
384  event_contexts.front().add_handler(this);
385  has_joined_global_ = true;
386 
387  // Instruct members to join
388  for(auto member : handler_members()) {
389  member->join_global();
390  }
391 }
392 
394 {
395  for(auto member : handler_members()) {
396  member->leave_global();
397  }
398 
399  event_contexts.front().remove_handler(this);
400 
401  has_joined_global_ = false;
402 }
403 
404 void focus_handler(const sdl_handler* ptr)
405 {
406  if(event_contexts.empty() == false) {
407  event_contexts.back().set_focus(ptr);
408  }
409 }
410 
411 bool has_focus(const sdl_handler* hand, const SDL_Event* event)
412 {
413  if(event_contexts.empty()) {
414  return true;
415  }
416 
417  if(hand->requires_event_focus(event) == false) {
418  return true;
419  }
420 
421  const handler_list::iterator foc = event_contexts.back().focused_handler;
422  auto& handlers = event_contexts.back().handlers;
423 
424  // If no-one has focus at the moment, this handler obviously wants
425  // focus, so give it to it.
426  if(foc == handlers.end()) {
427  focus_handler(hand);
428  return true;
429  }
430 
431  sdl_handler* const foc_hand = *foc;
432  if(foc_hand == hand) {
433  return true;
434  } else if(!foc_hand->requires_event_focus(event)) {
435  // If the currently focused handler doesn't need focus for this event
436  // allow the most recent interested handler to take care of it
437  for(auto i = handlers.rbegin(); i != handlers.rend(); ++i) {
438  sdl_handler* const thief_hand = *i;
439 
440  if(thief_hand != foc_hand && thief_hand->requires_event_focus(event)) {
441  // Steal focus
442  focus_handler(thief_hand);
443 
444  // Position the previously focused handler to allow stealing back
445  handlers.splice(handlers.end(), handlers, foc);
446 
447  return thief_hand == hand;
448  }
449  }
450  }
451 
452  return false;
453 }
454 
455 static void raise_window_event(const SDL_Event& event)
456 {
457  for(auto& context : event_contexts) {
458  for(auto handler : context.handlers) {
459  handler->handle_window_event(event);
460  }
461  }
462 
463  for(auto global_handler : event_contexts.front().handlers) {
464  global_handler->handle_window_event(event);
465  }
466 }
467 
468 // TODO: I'm uncertain if this is always safe to call at static init; maybe set in main() instead?
469 static const std::thread::id main_thread = std::this_thread::get_id();
470 
471 // this should probably be elsewhere, but as the main thread is already
472 // being tracked here, this went here.
474 {
475  return std::this_thread::get_id() == main_thread;
476 }
477 
478 void pump()
479 {
480  if(!is_in_main_thread()) {
481  // Can only call this on the main thread!
482  return;
483  }
484 
485  pump_info info;
486 
487  // Used to keep track of double click events
488  static int last_mouse_down = -1;
489  static int last_click_x = -1, last_click_y = -1;
490 
491  SDL_Event temp_event;
492  int poll_count = 0;
493  int begin_ignoring = 0;
494 
495  std::vector<SDL_Event> events;
496  while(SDL_PollEvent(&temp_event)) {
497  if(temp_event.type == INVOKE_FUNCTION_EVENT) {
498  static_cast<invoked_function_data*>(temp_event.user.data1)->call();
499  continue;
500  }
501 
502  ++poll_count;
503 
504  if(!begin_ignoring && temp_event.type == SDL_WINDOWEVENT && (
505  temp_event.window.event == SDL_WINDOWEVENT_ENTER ||
506  temp_event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED)
507  ) {
508  begin_ignoring = poll_count;
509  } else if(begin_ignoring > 0 && is_input(temp_event)) {
510  // ignore user input events that occurred after the window was activated
511  continue;
512  }
513 
514  events.push_back(temp_event);
515  }
516 
517  auto ev_it = events.begin();
518  for(int i = 1; i < begin_ignoring; ++i) {
519  if(is_input(*ev_it)) {
520  // ignore user input events that occurred before the window was activated
521  ev_it = events.erase(ev_it);
522  } else {
523  ++ev_it;
524  }
525  }
526 
527  for(SDL_Event& event : events) {
528  for(context& c : event_contexts) {
529  c.add_staging_handlers();
530  }
531 
532 #ifdef MOUSE_TOUCH_EMULATION
533  switch (event.type) {
534  // TODO: Implement SDL_MULTIGESTURE. Some day.
535  case SDL_MOUSEMOTION:
536  if(event.motion.which != SDL_TOUCH_MOUSEID && event.motion.state == 0) {
537  return;
538  }
539 
540  if(event.motion.state & SDL_BUTTON(SDL_BUTTON_RIGHT))
541  {
542  // Events are given by SDL in draw space
544 
545  // TODO: Check if SDL_FINGERMOTION is actually signaled for COMPLETE motions (I doubt, but tbs)
546  SDL_Event touch_event;
547  touch_event.type = SDL_FINGERMOTION;
548  touch_event.tfinger.type = SDL_FINGERMOTION;
549  touch_event.tfinger.timestamp = event.motion.timestamp;
550  touch_event.tfinger.touchId = 1;
551  touch_event.tfinger.fingerId = 1;
552  touch_event.tfinger.dx = static_cast<float>(event.motion.xrel) / c.x;
553  touch_event.tfinger.dy = static_cast<float>(event.motion.yrel) / c.y;
554  touch_event.tfinger.x = static_cast<float>(event.motion.x) / c.x;
555  touch_event.tfinger.y = static_cast<float>(event.motion.y) / c.y;
556  touch_event.tfinger.pressure = 1;
557  ::SDL_PushEvent(&touch_event);
558 
559  event.motion.state = SDL_BUTTON(SDL_BUTTON_LEFT);
560  event.motion.which = SDL_TOUCH_MOUSEID;
561  }
562  break;
563  case SDL_MOUSEBUTTONDOWN:
564  case SDL_MOUSEBUTTONUP:
565  if(event.button.button == SDL_BUTTON_RIGHT)
566  {
567  event.button.button = SDL_BUTTON_LEFT;
568  event.button.which = SDL_TOUCH_MOUSEID;
569 
570  // Events are given by SDL in draw space
572 
573  SDL_Event touch_event;
574  touch_event.type = (event.type == SDL_MOUSEBUTTONDOWN) ? SDL_FINGERDOWN : SDL_FINGERUP;
575  touch_event.tfinger.type = touch_event.type;
576  touch_event.tfinger.timestamp = event.button.timestamp;
577  touch_event.tfinger.touchId = 1;
578  touch_event.tfinger.fingerId = 1;
579  touch_event.tfinger.dx = 0;
580  touch_event.tfinger.dy = 0;
581  touch_event.tfinger.x = static_cast<float>(event.button.x) / c.x;
582  touch_event.tfinger.y = static_cast<float>(event.button.y) / c.y;
583  touch_event.tfinger.pressure = 1;
584  ::SDL_PushEvent(&touch_event);
585 
586  }
587  break;
588  default:
589  break;
590  }
591 #endif
592 
593  switch(event.type) {
594  case SDL_WINDOWEVENT:
595  switch(event.window.event) {
596  case SDL_WINDOWEVENT_ENTER:
597  case SDL_WINDOWEVENT_FOCUS_GAINED:
599  break;
600 
601  case SDL_WINDOWEVENT_LEAVE:
602  case SDL_WINDOWEVENT_FOCUS_LOST:
604  break;
605 
606  // Size changed is called before resized.
607  // We can ensure the video framebuffer is valid here.
608  case SDL_WINDOWEVENT_SIZE_CHANGED:
609  LOG_DP << "events/SIZE_CHANGED "
610  << event.window.data1 << 'x' << event.window.data2;
611  video::update_buffers(false);
612  break;
613 
614  // Resized comes after size_changed.
615  // Here we can trigger any watchers for resize events.
616  // Video settings such as game_canvas_size() will be correct.
617  case SDL_WINDOWEVENT_RESIZED:
618  LOG_DP << "events/RESIZED "
619  << event.window.data1 << 'x' << event.window.data2;
620  info.resize_dimensions.first = event.window.data1;
621  info.resize_dimensions.second = event.window.data2;
622  break;
623 
624  // Once everything has had a chance to respond to the resize,
625  // an expose is triggered to display the changed content.
626  case SDL_WINDOWEVENT_EXPOSED:
627  LOG_DP << "events/EXPOSED";
629  break;
630 
631  case SDL_WINDOWEVENT_MAXIMIZED:
632  case SDL_WINDOWEVENT_RESTORED:
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  static const int DoubleClickTime = 500;
656 #ifdef __IPHONEOS__
657  static const int DoubleClickMaxMove = 15;
658 #else
659  static const int DoubleClickMaxMove = 3;
660 #endif
661 
662  if(last_mouse_down >= 0 && info.ticks() - last_mouse_down < DoubleClickTime
663  && std::abs(event.button.x - last_click_x) < DoubleClickMaxMove
664  && std::abs(event.button.y - last_click_y) < DoubleClickMaxMove
665  ) {
666  sdl::UserEvent user_event(DOUBLE_CLICK_EVENT, event.button.which, event.button.x, event.button.y);
667  ::SDL_PushEvent(reinterpret_cast<SDL_Event*>(&user_event));
668  }
669 
670  last_mouse_down = info.ticks();
671  last_click_x = event.button.x;
672  last_click_y = event.button.y;
673  }
674  break;
675  }
676 
677 #ifndef __APPLE__
678  case SDL_KEYDOWN: {
679  if(event.key.keysym.sym == SDLK_F4 &&
680  (event.key.keysym.mod == KMOD_RALT || event.key.keysym.mod == KMOD_LALT)
681  ) {
683  continue; // this event is already handled
684  }
685  break;
686  }
687 #endif
688 
689 #if defined(_X11) && !defined(__APPLE__)
690  case SDL_SYSWMEVENT: {
691  // clipboard support for X11
693  break;
694  }
695 #endif
696 
697 #if defined _WIN32
698  case SDL_SYSWMEVENT: {
700  break;
701  }
702 #endif
703 
704  case SDL_QUIT: {
706  continue; // this event is already handled.
707  }
708  }
709 
710  for(auto global_handler : event_contexts.front().handlers) {
711  global_handler->handle_event(event);
712  }
713 
714  if(event_contexts.empty() == false) {
715  // As pump() can recurse, pretty much anything can happen here
716  // including destroying handlers or the event context.
717  size_t ec_index = event_contexts.size();
718  context& c = event_contexts.back();
719  handler_list& h = c.handlers;
720  size_t h_size = h.size();
721  for(auto it = h.begin(); it != h.end(); ++it) {
722  // Pass the event on to the handler.
723  (*it)->handle_event(event);
724  // Escape if anything has changed.
725  if(event_contexts.size() != ec_index) {
726  LOG_EV << "ec size changed! bugging out";
727  break;
728  }
729  if(h_size != h.size()) {
730  LOG_EV << "h size changed! bugging out";
731  break;
732  }
733  }
734  }
735  }
736 
737  // Inform the pump monitors that an events::pump() has occurred
738  for(auto monitor : pump_monitors) {
739  monitor->process(info);
740  }
741 }
742 
743 void draw()
744 {
746 }
747 
749 {
750  if(event_contexts.empty() == false) {
751  event_contexts.back().add_staging_handlers();
752 
753  for(auto handler : event_contexts.back().handlers) {
754  handler->process_event();
755  }
756  }
757 }
758 
760 {
762  SDL_Event event;
763  event.window.type = SDL_WINDOWEVENT;
764  event.window.event = SDL_WINDOWEVENT_SIZE_CHANGED;
765  event.window.windowID = 0; // We don't check this anyway... I think...
766  event.window.data1 = size.x;
767  event.window.data2 = size.y;
768 
769  raise_window_event(event);
770 }
771 
772 void process_tooltip_strings(int mousex, int mousey)
773 {
774  if(event_contexts.empty() == false) {
775  for(auto handler : event_contexts.back().handlers) {
776  handler->process_tooltip_string(mousex, mousey);
777  }
778  }
779 }
780 
781 int pump_info::ticks(unsigned* refresh_counter, unsigned refresh_rate)
782 {
783  if(!ticks_ && !(refresh_counter && ++*refresh_counter % refresh_rate)) {
784  ticks_ = ::SDL_GetTicks();
785  }
786 
787  return ticks_;
788 }
789 
790 /* The constants for the minimum and maximum are picked from the headers. */
791 #define INPUT_MIN 0x300
792 #define INPUT_MAX 0x8FF
793 
794 bool is_input(const SDL_Event& event)
795 {
796  return event.type >= INPUT_MIN && event.type <= INPUT_MAX;
797 }
798 
800 {
801  SDL_FlushEvents(INPUT_MIN, INPUT_MAX);
802 }
803 
804 void call_in_main_thread(const std::function<void(void)>& f)
805 {
806  if(is_in_main_thread()) {
807  // nothing special to do if called from the main thread.
808  f();
809  return;
810  }
811 
812  invoked_function_data fdata{f};
813 
814  SDL_Event sdl_event;
815  sdl::UserEvent sdl_userevent(INVOKE_FUNCTION_EVENT, &fdata);
816 
817  sdl_event.type = INVOKE_FUNCTION_EVENT;
818  sdl_event.user = sdl_userevent;
819 
820  SDL_PushEvent(&sdl_event);
821 
822  // Block until execution is complete in the main thread. Rethrows any exceptions.
823  fdata.finished.get_future().wait();
824 }
825 
826 } // end events namespace
void set_focus(const sdl_handler *ptr)
Definition: events.cpp:185
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:97
void cycle_focus()
Definition: events.cpp:152
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:88
bool remove_handler(sdl_handler *ptr)
Definition: events.cpp:105
void add_staging_handlers()
Definition: events.cpp:193
virtual ~pump_monitor()
Definition: events.cpp:226
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:307
virtual void leave()
Definition: events.cpp:352
virtual void join_global()
Definition: events.cpp:373
virtual void join_same(sdl_handler *parent)
Definition: events.cpp:336
virtual void leave_global()
Definition: events.cpp:393
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:296
sdl_handler(sdl_handler &&)=delete
static void quit_to_desktop()
Type that can be thrown as an exception to quit to desktop.
Definition: video.hpp:317
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:792
#define LOG_EV
Definition: events.cpp:49
#define INPUT_MIN
Definition: events.cpp:791
#define LOG_DP
Definition: events.cpp:46
static lg::log_domain log_display("display")
static lg::log_domain log_event("event")
#define DBG_EV
Definition: events.cpp:50
#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:190
std::size_t i
Definition: function.cpp:968
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:207
Standard logging facilities (interface).
void set_focus(bool focus)
Definition: cursor.cpp:221
void handle_system_event(const SDL_Event &)
Definition: clipboard.cpp:49
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:759
bool has_focus(const sdl_handler *hand, const SDL_Event *event)
Definition: events.cpp:411
void discard_input()
Discards all input events.
Definition: events.cpp:799
void draw()
Trigger a draw cycle.
Definition: events.cpp:743
static void raise_window_event(const SDL_Event &event)
Definition: events.cpp:455
bool is_input(const SDL_Event &event)
Is the event an input event?
Definition: events.cpp:794
std::deque< context > event_contexts
Definition: events.cpp:217
std::vector< pump_monitor * > pump_monitors
Definition: events.cpp:219
void call_in_main_thread(const std::function< void(void)> &f)
Definition: events.cpp:804
void raise_process_event()
Definition: events.cpp:748
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:772
std::list< sdl_handler * > handler_list
Definition: events.hpp:35
bool is_in_main_thread()
Definition: events.cpp:473
static const std::thread::id main_thread
Definition: events.cpp:469
void pump()
Process all events currently in the queue.
Definition: events.cpp:478
void focus_handler(const sdl_handler *ptr)
Definition: events.cpp:404
void remove()
Removes a tip.
Definition: tooltip.cpp:109
logger & info()
Definition: log.cpp:314
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
auto reversed_view(T &container)
Definition: ranges.hpp:28
std::string get_unknown_exception_type()
Utility function for finding the type of thing caught with catch(...).
Definition: general.cpp:23
point game_canvas_size()
The size of the game canvas, in drawing coordinates / game pixels.
Definition: video.cpp:434
point window_size()
Returns the size of the window in display units / screen coordinates.
Definition: video.cpp:421
void update_buffers(bool autoupdate)
Update buffers to match current resolution and pixel scale settings.
Definition: video.cpp:831
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
int ticks(unsigned *refresh_counter=nullptr, unsigned refresh_rate=1)
Definition: events.cpp:781
Holds a 2D point.
Definition: point.hpp:25
mock_char c
#define h
#define f