The Battle for Wesnoth  1.19.1+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 {
229  pump_monitors.erase(std::remove(pump_monitors.begin(), pump_monitors.end(), this), pump_monitors.end());
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  pump_info info;
487 
488  // Used to keep track of double click events
489  static int last_mouse_down = -1;
490  static int last_click_x = -1, last_click_y = -1;
491 
492  SDL_Event temp_event;
493  int poll_count = 0;
494  int begin_ignoring = 0;
495 
496  std::vector<SDL_Event> events;
497  while(SDL_PollEvent(&temp_event)) {
498  if(temp_event.type == INVOKE_FUNCTION_EVENT) {
499  static_cast<invoked_function_data*>(temp_event.user.data1)->call();
500  continue;
501  }
502 
503  ++poll_count;
504 
505  if(!begin_ignoring && temp_event.type == SDL_WINDOWEVENT && (
506  temp_event.window.event == SDL_WINDOWEVENT_ENTER ||
507  temp_event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED)
508  ) {
509  begin_ignoring = poll_count;
510  } else if(begin_ignoring > 0 && is_input(temp_event)) {
511  // ignore user input events that occurred after the window was activated
512  continue;
513  }
514 
515  events.push_back(temp_event);
516  }
517 
518  auto ev_it = events.begin();
519  for(int i = 1; i < begin_ignoring; ++i) {
520  if(is_input(*ev_it)) {
521  // ignore user input events that occurred before the window was activated
522  ev_it = events.erase(ev_it);
523  } else {
524  ++ev_it;
525  }
526  }
527 
528  for(SDL_Event& event : events) {
529  for(context& c : event_contexts) {
530  c.add_staging_handlers();
531  }
532 
533 #ifdef MOUSE_TOUCH_EMULATION
534  switch (event.type) {
535  // TODO: Implement SDL_MULTIGESTURE. Some day.
536  case SDL_MOUSEMOTION:
537  if(event.motion.which != SDL_TOUCH_MOUSEID && event.motion.state == 0) {
538  return;
539  }
540 
541  if(event.motion.state & SDL_BUTTON(SDL_BUTTON_RIGHT))
542  {
543  // Events are given by SDL in draw space
545 
546  // TODO: Check if SDL_FINGERMOTION is actually signaled for COMPLETE motions (I doubt, but tbs)
547  SDL_Event touch_event;
548  touch_event.type = SDL_FINGERMOTION;
549  touch_event.tfinger.type = SDL_FINGERMOTION;
550  touch_event.tfinger.timestamp = event.motion.timestamp;
551  touch_event.tfinger.touchId = 1;
552  touch_event.tfinger.fingerId = 1;
553  touch_event.tfinger.dx = static_cast<float>(event.motion.xrel) / c.x;
554  touch_event.tfinger.dy = static_cast<float>(event.motion.yrel) / c.y;
555  touch_event.tfinger.x = static_cast<float>(event.motion.x) / c.x;
556  touch_event.tfinger.y = static_cast<float>(event.motion.y) / c.y;
557  touch_event.tfinger.pressure = 1;
558  ::SDL_PushEvent(&touch_event);
559 
560  event.motion.state = SDL_BUTTON(SDL_BUTTON_LEFT);
561  event.motion.which = SDL_TOUCH_MOUSEID;
562  }
563  break;
564  case SDL_MOUSEBUTTONDOWN:
565  case SDL_MOUSEBUTTONUP:
566  if(event.button.button == SDL_BUTTON_RIGHT)
567  {
568  event.button.button = SDL_BUTTON_LEFT;
569  event.button.which = SDL_TOUCH_MOUSEID;
570 
571  // Events are given by SDL in draw space
573 
574  SDL_Event touch_event;
575  touch_event.type = (event.type == SDL_MOUSEBUTTONDOWN) ? SDL_FINGERDOWN : SDL_FINGERUP;
576  touch_event.tfinger.type = touch_event.type;
577  touch_event.tfinger.timestamp = event.button.timestamp;
578  touch_event.tfinger.touchId = 1;
579  touch_event.tfinger.fingerId = 1;
580  touch_event.tfinger.dx = 0;
581  touch_event.tfinger.dy = 0;
582  touch_event.tfinger.x = static_cast<float>(event.button.x) / c.x;
583  touch_event.tfinger.y = static_cast<float>(event.button.y) / c.y;
584  touch_event.tfinger.pressure = 1;
585  ::SDL_PushEvent(&touch_event);
586 
587  }
588  break;
589  default:
590  break;
591  }
592 #endif
593 
594  switch(event.type) {
595  case SDL_WINDOWEVENT:
596  switch(event.window.event) {
597  case SDL_WINDOWEVENT_ENTER:
598  case SDL_WINDOWEVENT_FOCUS_GAINED:
600  break;
601 
602  case SDL_WINDOWEVENT_LEAVE:
603  case SDL_WINDOWEVENT_FOCUS_LOST:
605  break;
606 
607  // Size changed is called before resized.
608  // We can ensure the video framebuffer is valid here.
609  case SDL_WINDOWEVENT_SIZE_CHANGED:
610  LOG_DP << "events/SIZE_CHANGED "
611  << event.window.data1 << 'x' << event.window.data2;
612  video::update_buffers(false);
613  break;
614 
615  // Resized comes after size_changed.
616  // Here we can trigger any watchers for resize events.
617  // Video settings such as game_canvas_size() will be correct.
618  case SDL_WINDOWEVENT_RESIZED:
619  LOG_DP << "events/RESIZED "
620  << event.window.data1 << 'x' << event.window.data2;
621  info.resize_dimensions.first = event.window.data1;
622  info.resize_dimensions.second = event.window.data2;
624  break;
625 
626  // Once everything has had a chance to respond to the resize,
627  // an expose is triggered to display the changed content.
628  case SDL_WINDOWEVENT_EXPOSED:
629  LOG_DP << "events/EXPOSED";
631  break;
632 
633  case SDL_WINDOWEVENT_MAXIMIZED:
634  LOG_DP << "events/MAXIMIZED";
635  prefs::get().set_maximized(true);
636  break;
637  case SDL_WINDOWEVENT_RESTORED:
638  LOG_DP << "events/RESTORED";
639  prefs::get().set_maximized(prefs::get().fullscreen());
640  break;
641  case SDL_WINDOWEVENT_SHOWN:
642  case SDL_WINDOWEVENT_MOVED:
643  // Not used.
644  break;
645  }
646 
647  raise_window_event(event);
648 
649  // This event was just distributed, don't re-distribute.
650  continue;
651 
652  case SDL_MOUSEMOTION: {
653  // Always make sure a cursor is displayed if the mouse moves or if the user clicks
654  cursor::set_focus(true);
655  process_tooltip_strings(event.motion.x, event.motion.y);
656  break;
657  }
658 
659  case SDL_MOUSEBUTTONDOWN: {
660  // Always make sure a cursor is displayed if the mouse moves or if the user clicks
661  cursor::set_focus(true);
662  if(event.button.button == SDL_BUTTON_LEFT || event.button.which == SDL_TOUCH_MOUSEID) {
663  static const int DoubleClickTime = 500;
664 #ifdef __IPHONEOS__
665  static const int DoubleClickMaxMove = 15;
666 #else
667  static const int DoubleClickMaxMove = 3;
668 #endif
669 
670  if(last_mouse_down >= 0 && info.ticks() - last_mouse_down < DoubleClickTime
671  && std::abs(event.button.x - last_click_x) < DoubleClickMaxMove
672  && std::abs(event.button.y - last_click_y) < DoubleClickMaxMove
673  ) {
674  sdl::UserEvent user_event(DOUBLE_CLICK_EVENT, event.button.which, event.button.x, event.button.y);
675  ::SDL_PushEvent(reinterpret_cast<SDL_Event*>(&user_event));
676  }
677 
678  last_mouse_down = info.ticks();
679  last_click_x = event.button.x;
680  last_click_y = event.button.y;
681  }
682  break;
683  }
684 
685 #ifndef __APPLE__
686  case SDL_KEYDOWN: {
687  if(event.key.keysym.sym == SDLK_F4 &&
688  (event.key.keysym.mod == KMOD_RALT || event.key.keysym.mod == KMOD_LALT)
689  ) {
691  continue; // this event is already handled
692  }
693  break;
694  }
695 #endif
696 
697 #if defined(_X11) && !defined(__APPLE__)
698  case SDL_SYSWMEVENT: {
699  // clipboard support for X11
701  break;
702  }
703 #endif
704 
705 #if defined _WIN32
706  case SDL_SYSWMEVENT: {
708  break;
709  }
710 #endif
711 
712  case SDL_QUIT: {
714  continue; // this event is already handled.
715  }
716  }
717 
718  for(auto global_handler : event_contexts.front().handlers) {
719  global_handler->handle_event(event);
720  }
721 
722  if(event_contexts.empty() == false) {
723  // As pump() can recurse, pretty much anything can happen here
724  // including destroying handlers or the event context.
725  size_t ec_index = event_contexts.size();
726  context& c = event_contexts.back();
727  handler_list& h = c.handlers;
728  size_t h_size = h.size();
729  for(auto it = h.begin(); it != h.end(); ++it) {
730  // Pass the event on to the handler.
731  (*it)->handle_event(event);
732  // Escape if anything has changed.
733  if(event_contexts.size() != ec_index) {
734  LOG_EV << "ec size changed! bugging out";
735  break;
736  }
737  if(h_size != h.size()) {
738  LOG_EV << "h size changed! bugging out";
739  break;
740  }
741  }
742  }
743  }
744 
745  // Inform the pump monitors that an events::pump() has occurred
746  for(auto monitor : pump_monitors) {
747  monitor->process(info);
748  }
749 }
750 
751 void draw()
752 {
754 }
755 
757 {
758  if(event_contexts.empty() == false) {
759  event_contexts.back().add_staging_handlers();
760 
761  for(auto handler : event_contexts.back().handlers) {
762  handler->process_event();
763  }
764  }
765 }
766 
768 {
770  SDL_Event event;
771  event.window.type = SDL_WINDOWEVENT;
772  event.window.event = SDL_WINDOWEVENT_SIZE_CHANGED;
773  event.window.windowID = 0; // We don't check this anyway... I think...
774  event.window.data1 = size.x;
775  event.window.data2 = size.y;
776 
777  raise_window_event(event);
778 }
779 
780 void process_tooltip_strings(int mousex, int mousey)
781 {
782  if(event_contexts.empty() == false) {
783  for(auto handler : event_contexts.back().handlers) {
784  handler->process_tooltip_string(mousex, mousey);
785  }
786  }
787 }
788 
789 int pump_info::ticks(unsigned* refresh_counter, unsigned refresh_rate)
790 {
791  if(!ticks_ && !(refresh_counter && ++*refresh_counter % refresh_rate)) {
792  ticks_ = ::SDL_GetTicks();
793  }
794 
795  return ticks_;
796 }
797 
798 /* The constants for the minimum and maximum are picked from the headers. */
799 #define INPUT_MIN 0x300
800 #define INPUT_MAX 0x8FF
801 
802 bool is_input(const SDL_Event& event)
803 {
804  return event.type >= INPUT_MIN && event.type <= INPUT_MAX;
805 }
806 
808 {
809  SDL_FlushEvents(INPUT_MIN, INPUT_MAX);
810 }
811 
812 void call_in_main_thread(const std::function<void(void)>& f)
813 {
814  if(is_in_main_thread()) {
815  // nothing special to do if called from the main thread.
816  f();
817  return;
818  }
819 
820  invoked_function_data fdata{f};
821 
822  SDL_Event sdl_event;
823  sdl::UserEvent sdl_userevent(INVOKE_FUNCTION_EVENT, &fdata);
824 
825  sdl_event.type = INVOKE_FUNCTION_EVENT;
826  sdl_event.user = sdl_userevent;
827 
828  SDL_PushEvent(&sdl_event);
829 
830  // Block until execution is complete in the main thread. Rethrows any exceptions.
831  fdata.finished.get_future().wait();
832 }
833 
834 } // 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)
void set_maximized(bool ison)
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:800
#define LOG_EV
Definition: events.cpp:50
#define INPUT_MIN
Definition: events.cpp:799
#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:190
std::size_t i
Definition: function.cpp:968
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:205
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:767
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:807
void draw()
Trigger a draw cycle.
Definition: events.cpp:751
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:802
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:812
void raise_process_event()
Definition: events.cpp:756
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:780
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
void remove()
Removes a tip.
Definition: tooltip.cpp:95
logger & info()
Definition: log.cpp:316
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:789
Holds a 2D point.
Definition: point.hpp:25
mock_char c
#define h
#define f