The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
events.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2003 - 2025
3 by David White <dave@whitevine.net>
4 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY.
12
13 See the COPYING file for more details.
14*/
15
16#include "events.hpp"
17
18#include "cursor.hpp"
19#include "desktop/clipboard.hpp"
20#include "log.hpp"
21#include "draw_manager.hpp"
23#include "quit_confirmation.hpp"
24#include "sdl/userevent.hpp"
25#include "utils/ranges.hpp"
26#include "utils/general.hpp"
27#include "video.hpp"
28
29#ifdef _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 <SDL3/SDL_events.h>
43
44#define ERR_GEN LOG_STREAM(err, lg::general)
45
46static lg::log_domain log_display("display");
47#define LOG_DP LOG_STREAM(info, log_display)
48
49static 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
53namespace
54{
55struct 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
87namespace 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
98bool context::has_handler(const sdl_handler* ptr) const
99{
101}
102
104{
105 static int depth = 0;
106 ++depth;
107
108 // The handler is most likely on the back of the events list,
109 // so look there first, otherwise do a complete search.
110 if(!handlers.empty() && handlers.back() == ptr) {
111 if(focused_handler != handlers.end() && *focused_handler == ptr) {
113 }
114
115 handlers.pop_back();
116 } else {
117 const handler_list::iterator i = std::find(handlers.begin(), handlers.end(), ptr);
118
119 if(i == handlers.end()) {
120 --depth;
121
122 // The handler may be in the staging area. Search it from there.
123 auto j = std::find(staging_handlers.begin(), staging_handlers.end(), ptr);
124 if(j != staging_handlers.end()) {
125 staging_handlers.erase(j);
126 return true;
127 } else {
128 return false;
129 }
130 }
131
132 if(i == focused_handler) {
134 }
135
136 handlers.erase(i);
137 }
138
139 --depth;
140
141 if(depth == 0) {
142 cycle_focus();
143 } else {
145 }
146
147 return true;
148}
149
151{
152 if(handlers.begin() == handlers.end()) {
153 return;
154 }
155
156 handler_list::iterator current = focused_handler;
157 handler_list::iterator last = focused_handler;
158
159 if(last != handlers.begin()) {
160 --last;
161 }
162
163 if(current == handlers.end()) {
164 current = handlers.begin();
165 } else {
166 ++current;
167 }
168
169 while(current != last) {
170 if(current != handlers.end() && (*current)->requires_event_focus()) {
171 focused_handler = current;
172 break;
173 }
174
175 if(current == handlers.end()) {
176 current = handlers.begin();
177 } else {
178 ++current;
179 }
180 }
181}
182
184{
185 const handler_list::iterator i = std::find(handlers.begin(), handlers.end(), ptr);
186 if(i != handlers.end() && (*i)->requires_event_focus()) {
188 }
189}
190
192{
193 std::copy(staging_handlers.begin(), staging_handlers.end(), std::back_inserter(handlers));
194 staging_handlers.clear();
195}
196
198{
199 for(sdl_handler* h : handlers) {
200 if(h->has_joined()) {
201 h->has_joined_ = false;
202 }
203
204 if(h->has_joined_global()) {
205 h->has_joined_global_ = false;
206 }
207 }
208}
209
210// This object stores all the event handlers. It is a stack of event 'contexts'.
211// a new event context is created when e.g. a modal dialog is opened, and then
212// closed when that dialog is closed. Each context contains a list of the handlers
213// in that context. The current context is the one on the top of the stack.
214// The global context must always be in the first position.
215std::deque<context> event_contexts;
216
217std::vector<pump_monitor*> pump_monitors;
218
220{
221 pump_monitors.push_back(this);
222}
223
228
230{
231 event_contexts.emplace_back();
232}
233
235{
236 assert(event_contexts.empty() == false);
237 event_contexts.pop_back();
238}
239
240sdl_handler::sdl_handler(const bool auto_join)
241 : has_joined_(false)
242 , has_joined_global_(false)
243{
244 if(auto_join) {
245 assert(!event_contexts.empty());
246 event_contexts.back().add_handler(this);
247 has_joined_ = true;
248 }
249}
250
252 : has_joined_(that.has_joined_)
253 , has_joined_global_(that.has_joined_global_)
254{
256 assert(!event_contexts.empty());
257 event_contexts.front().add_handler(this);
258 } else if(has_joined_) {
259 bool found_context = false;
261 if(context.has_handler(&that)) {
262 found_context = true;
263 context.add_handler(this);
264 break;
265 }
266 }
267
268 if (!found_context) {
269 throw std::logic_error("Copy-constructing a sdl_handler that has_joined_ but can't be found by searching contexts");
270 }
271 }
272}
273
275{
276 if(that.has_joined_global_) {
277 join_global();
278 } else if(that.has_joined_) {
280 if(context.has_handler(&that)) {
281 join(context);
282 break;
283 }
284 }
285 } else if(has_joined_) {
286 leave();
287 } else if(has_joined_global_) {
288 leave_global();
289 }
290
291 return *this;
292}
293
295{
296 if(has_joined_) {
297 leave();
298 }
299
301 leave_global();
302 }
303}
304
306{
307 // this assert will fire if someone will inadvertently try to join
308 // an event context but might end up in the global context instead.
309 assert(&event_contexts.back() != &event_contexts.front());
310
311 join(event_contexts.back());
312}
313
315{
317 leave_global();
318 }
319
320 if(has_joined_) {
321 leave(); // should not be in multiple event contexts
322 }
323
324 // join self
325 c.add_handler(this);
326 has_joined_ = true;
327
328 // instruct members to join
329 for(auto member : handler_members()) {
330 member->join(c);
331 }
332}
333
335{
336 if(has_joined_) {
337 leave(); // should not be in multiple event contexts
338 }
339
341 if(context.has_handler(parent)) {
342 join(context);
343 return;
344 }
345 }
346
347 join(event_contexts.back());
348}
349
351{
353
354 if(members.empty()) {
355 assert(event_contexts.empty() == false);
356 }
357
358 for(auto member : members) {
359 member->leave();
360 }
361
363 if(context.remove_handler(this)) {
364 break;
365 }
366 }
367
368 has_joined_ = false;
369}
370
372{
373 if(has_joined_) {
374 leave();
375 }
376
378 leave_global(); // Should not be in multiple event contexts
379 }
380
381 // Join self
382 event_contexts.front().add_handler(this);
383 has_joined_global_ = true;
384
385 // Instruct members to join
386 for(auto member : handler_members()) {
387 member->join_global();
388 }
389}
390
392{
393 for(auto member : handler_members()) {
394 member->leave_global();
395 }
396
397 event_contexts.front().remove_handler(this);
398
399 has_joined_global_ = false;
400}
401
403{
404 if(event_contexts.empty() == false) {
405 event_contexts.back().set_focus(ptr);
406 }
407}
408
409bool has_focus(const sdl_handler* hand, const SDL_Event* event)
410{
411 if(event_contexts.empty()) {
412 return true;
413 }
414
415 if(hand->requires_event_focus(event) == false) {
416 return true;
417 }
418
419 const handler_list::iterator foc = event_contexts.back().focused_handler;
420 auto& handlers = event_contexts.back().handlers;
421
422 // If no-one has focus at the moment, this handler obviously wants
423 // focus, so give it to it.
424 if(foc == handlers.end()) {
425 focus_handler(hand);
426 return true;
427 }
428
429 sdl_handler* const foc_hand = *foc;
430 if(foc_hand == hand) {
431 return true;
432 } else if(!foc_hand->requires_event_focus(event)) {
433 // If the currently focused handler doesn't need focus for this event
434 // allow the most recent interested handler to take care of it
435 for(auto i = handlers.rbegin(); i != handlers.rend(); ++i) {
436 sdl_handler* const thief_hand = *i;
437
438 if(thief_hand != foc_hand && thief_hand->requires_event_focus(event)) {
439 // Steal focus
440 focus_handler(thief_hand);
441
442 // Position the previously focused handler to allow stealing back
443 handlers.splice(handlers.end(), handlers, foc);
444
445 return thief_hand == hand;
446 }
447 }
448 }
449
450 return false;
451}
452
453static void raise_window_event(const SDL_Event& event)
454{
455 for(auto& context : event_contexts) {
456 for(auto handler : context.handlers) {
457 handler->handle_window_event(event);
458 }
459 }
460
461 for(auto global_handler : event_contexts.front().handlers) {
462 global_handler->handle_window_event(event);
463 }
464}
465
466static std::thread::id main_thread;
467
469{
470 main_thread = std::this_thread::get_id();
471}
472
473// this should probably be elsewhere, but as the main thread is already
474// being tracked here, this went here.
476{
477 return std::this_thread::get_id() == main_thread;
478}
479
480void pump()
481{
482 if(!is_in_main_thread()) {
483 // Can only call this on the main thread!
484 return;
485 }
486
487 SDL_Event temp_event;
488 int poll_count = 0;
489 int begin_ignoring = 0;
490
491 std::vector<SDL_Event> events;
492 while(SDL_PollEvent(&temp_event)) {
493 if(temp_event.type == INVOKE_FUNCTION_EVENT) {
494 static_cast<invoked_function_data*>(temp_event.user.data1)->call();
495 continue;
496 }
497
498 ++poll_count;
499
500 if(!begin_ignoring && (
501 temp_event.type == SDL_EVENT_WINDOW_MOUSE_ENTER ||
502 temp_event.type == SDL_EVENT_WINDOW_FOCUS_GAINED)
503 ) {
504 begin_ignoring = poll_count;
505 } else if(begin_ignoring > 0 && is_input(temp_event)) {
506 // ignore user input events that occurred after the window was activated
507 continue;
508 }
509
510 events.push_back(temp_event);
511 }
512
513 auto ev_it = events.begin();
514 for(int i = 1; i < begin_ignoring; ++i) {
515 if(is_input(*ev_it)) {
516 // ignore user input events that occurred before the window was activated
517 ev_it = events.erase(ev_it);
518 } else {
519 ++ev_it;
520 }
521 }
522
523 for(SDL_Event& event : events) {
524 for(context& c : event_contexts) {
525 c.add_staging_handlers();
526 }
527
528 SDL_ConvertEventToRenderCoordinates(video::get_renderer(), &event);
529
530#ifdef MOUSE_TOUCH_EMULATION
531 switch (event.type) {
532 // TODO: Implement SDL_MULTIGESTURE. Some day.
533 case SDL_EVENT_MOUSE_MOTION:
534 if(!events::is_touch(event.motion) && event.motion.state == 0) {
535 return;
536 }
537
538 if(event.motion.state & SDL_BUTTON(SDL_BUTTON_RIGHT))
539 {
540 // Events are given by SDL in draw space
542
543 // TODO: Check if SDL_EVENT_FINGER_MOTION is actually signaled for COMPLETE motions (I doubt, but tbs)
544 SDL_Event touch_event;
545 touch_event.type = SDL_EVENT_FINGER_MOTION;
546 touch_event.tfinger.type = SDL_EVENT_FINGER_MOTION;
547 touch_event.tfinger.timestamp = event.motion.timestamp;
548 touch_event.tfinger.touchId = 1;
549 touch_event.tfinger.fingerId = 1;
550 touch_event.tfinger.dx = static_cast<float>(event.motion.xrel) / c.x;
551 touch_event.tfinger.dy = static_cast<float>(event.motion.yrel) / c.y;
552 touch_event.tfinger.x = static_cast<float>(event.motion.x) / c.x;
553 touch_event.tfinger.y = static_cast<float>(event.motion.y) / c.y;
554 touch_event.tfinger.pressure = 1;
555 ::SDL_PushEvent(&touch_event);
556
557 event.motion.state = SDL_BUTTON(SDL_BUTTON_LEFT);
558 event.motion.which = SDL_TOUCH_MOUSEID;
559 }
560 break;
561 case SDL_EVENT_MOUSE_BUTTON_DOWN:
562 case SDL_EVENT_MOUSE_BUTTON_UP:
563 if(event.button.button == SDL_BUTTON_RIGHT)
564 {
565 event.button.button = SDL_BUTTON_LEFT;
566 event.button.which = SDL_TOUCH_MOUSEID;
567
568 // Events are given by SDL in draw space
570
571 SDL_Event touch_event;
572 touch_event.type = (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) ? SDL_EVENT_FINGER_DOWN : SDL_EVENT_FINGER_UP;
573 touch_event.tfinger.type = touch_event.type;
574 touch_event.tfinger.timestamp = event.button.timestamp;
575 touch_event.tfinger.touchId = 1;
576 touch_event.tfinger.fingerId = 1;
577 touch_event.tfinger.dx = 0;
578 touch_event.tfinger.dy = 0;
579 touch_event.tfinger.x = static_cast<float>(event.button.x) / c.x;
580 touch_event.tfinger.y = static_cast<float>(event.button.y) / c.y;
581 touch_event.tfinger.pressure = 1;
582 ::SDL_PushEvent(&touch_event);
583
584 }
585 break;
586 default:
587 break;
588 }
589#endif
590
591 if(event.type >= SDL_EVENT_WINDOW_FIRST && event.type <= SDL_EVENT_WINDOW_LAST) {
592 switch(event.type) {
593 case SDL_EVENT_WINDOW_MOUSE_ENTER:
594 case SDL_EVENT_WINDOW_FOCUS_GAINED:
596 break;
597
598 case SDL_EVENT_WINDOW_MOUSE_LEAVE:
599 case SDL_EVENT_WINDOW_FOCUS_LOST:
601 break;
602
603 // Size changed is called before resized.
604 // We can ensure the video framebuffer is valid here.
605 case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
606 LOG_DP << "events/SIZE_CHANGED "
607 << event.window.data1 << 'x' << event.window.data2;
609 break;
610
611 // Resized comes after size_changed.
612 // Here we can trigger any watchers for resize events.
613 // Video settings such as game_canvas_size() will be correct.
614 case SDL_EVENT_WINDOW_RESIZED:
615 LOG_DP << "events/RESIZED "
616 << event.window.data1 << 'x' << event.window.data2;
618 break;
619
620 // Once everything has had a chance to respond to the resize,
621 // an expose is triggered to display the changed content.
622 case SDL_EVENT_WINDOW_EXPOSED:
623 LOG_DP << "events/EXPOSED";
625 break;
626
627 case SDL_EVENT_WINDOW_MAXIMIZED:
628 LOG_DP << "events/MAXIMIZED";
629 prefs::get().set_maximized(true);
630 break;
631 case SDL_EVENT_WINDOW_RESTORED:
632 LOG_DP << "events/RESTORED";
633 prefs::get().set_maximized(prefs::get().fullscreen());
634 break;
635 case SDL_EVENT_WINDOW_SHOWN:
636 case SDL_EVENT_WINDOW_MOVED:
637 // Not used.
638 break;
639 }
640
641 raise_window_event(event);
642
643 // This event was just distributed, don't re-distribute.
644 continue;
645 }
646
647 switch(event.type) {
648 case SDL_EVENT_MOUSE_MOTION: {
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_EVENT_MOUSE_BUTTON_DOWN: {
656 // Always make sure a cursor is displayed if the mouse moves or if the user clicks
657 cursor::set_focus(true);
658 break;
659 }
660
661#ifndef __APPLE__
662 case SDL_EVENT_KEY_DOWN: {
663 if(event.key.key == SDLK_F4 &&
664 (event.key.mod == SDL_KMOD_RALT || event.key.mod == SDL_KMOD_LALT)
665 ) {
667 continue; // this event is already handled
668 }
669 break;
670 }
671#endif
672
673 case SDL_EVENT_QUIT: {
675 continue; // this event is already handled.
676 }
677 }
678
679 for(auto global_handler : event_contexts.front().handlers) {
680 global_handler->handle_event(event);
681 }
682
683 if(event_contexts.empty() == false) {
684 // As pump() can recurse, pretty much anything can happen here
685 // including destroying handlers or the event context.
686 std::size_t ec_index = event_contexts.size();
687 context& c = event_contexts.back();
689 std::size_t h_size = h.size();
690 for(auto it = h.begin(); it != h.end(); ++it) {
691 // Pass the event on to the handler.
692 (*it)->handle_event(event);
693 // Escape if anything has changed.
694 if(event_contexts.size() != ec_index) {
695 LOG_EV << "ec size changed! bugging out";
696 break;
697 }
698 if(h_size != h.size()) {
699 LOG_EV << "h size changed! bugging out";
700 break;
701 }
702 }
703 }
704 }
705
706 // Inform the pump monitors that an events::pump() has occurred
707 for(auto monitor : pump_monitors) {
708 monitor->process();
709 }
710}
711
712void draw()
713{
715}
716
718{
719 if(event_contexts.empty() == false) {
720 event_contexts.back().add_staging_handlers();
721
722 for(auto handler : event_contexts.back().handlers) {
723 handler->process_event();
724 }
725 }
726}
727
729{
730 point size = video::window_size();
731 SDL_Event event;
732 event.type = SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED;
733 event.window.windowID = 0; // We don't check this anyway... I think...
734 event.window.data1 = size.x;
735 event.window.data2 = size.y;
736
737 raise_window_event(event);
738}
739
740void process_tooltip_strings(int mousex, int mousey)
741{
742 if(event_contexts.empty() == false) {
743 for(auto handler : event_contexts.back().handlers) {
744 handler->process_tooltip_string(mousex, mousey);
745 }
746 }
747}
748
749/* The constants for the minimum and maximum are picked from the headers. */
750#define INPUT_MIN 0x300
751#define INPUT_MAX 0x8FF
752
753bool is_input(const SDL_Event& event)
754{
755 return event.type >= INPUT_MIN && event.type <= INPUT_MAX;
756}
757
759{
760 SDL_FlushEvents(INPUT_MIN, INPUT_MAX);
761}
762
763bool is_touch(const SDL_MouseButtonEvent &event)
764{
765 return event.which == SDL_TOUCH_MOUSEID;
766}
767
768bool is_touch(const SDL_MouseMotionEvent &event)
769{
770 return event.which == SDL_TOUCH_MOUSEID;
771}
772
773void 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#ifdef _WIN32
796bool handle_windows_message([[maybe_unused]] void* userdata, MSG* msg)
797{
799 return false;
800 }
801
802 // Continue further processing
803 return true;
804}
805#endif
806
807} // end events namespace
#define LOG_DP
void set_focus(const sdl_handler *ptr)
Definition events.cpp:183
bool has_handler(const sdl_handler *ptr) const
Returns true if ptr is found in either the handlers or staging_handlers lists.
Definition events.cpp:98
void cycle_focus()
Definition events.cpp:150
handler_list::iterator focused_handler
Definition events.hpp:66
std::vector< sdl_handler * > staging_handlers
Definition events.hpp:67
handler_list handlers
Definition events.hpp:65
void add_handler(sdl_handler *ptr)
Definition events.cpp:89
bool remove_handler(sdl_handler *ptr)
Definition events.cpp:103
void add_staging_handlers()
Definition events.cpp:191
virtual ~pump_monitor()
Definition events.cpp:224
virtual bool requires_event_focus(const SDL_Event *=nullptr) const
Definition events.hpp:85
virtual void handle_window_event(const SDL_Event &)
Definition events.hpp:82
virtual std::vector< sdl_handler * > handler_members()
Definition events.hpp:113
virtual void join()
Definition events.cpp:305
virtual void leave()
Definition events.cpp:350
virtual void join_global()
Definition events.cpp:371
virtual void join_same(sdl_handler *parent)
Definition events.cpp:334
virtual void leave_global()
Definition events.cpp:391
sdl_handler & operator=(sdl_handler &&)=delete
Moving would require two instances' context membership to be handled, it's simpler to delete these an...
virtual ~sdl_handler()
Definition events.cpp:294
sdl_handler(sdl_handler &&)=delete
static prefs & get()
void set_resolution(const point &res)
static void quit_to_desktop()
Type that can be thrown as an exception to quit to desktop.
Definition video.hpp:328
#define INPUT_MAX
Definition events.cpp:751
#define LOG_EV
Definition events.cpp:50
#define INPUT_MIN
Definition events.cpp:750
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:34
std::vector< events::sdl_handler * > sdl_handler_vector
Definition events.hpp:219
Standard logging facilities (interface).
void set_focus(bool focus)
Definition cursor.cpp:190
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:728
bool has_focus(const sdl_handler *hand, const SDL_Event *event)
Definition events.cpp:409
void discard_input()
Discards all input events.
Definition events.cpp:758
void draw()
Trigger a draw cycle.
Definition events.cpp:712
static std::thread::id main_thread
Definition events.cpp:466
static void raise_window_event(const SDL_Event &event)
Definition events.cpp:453
bool is_input(const SDL_Event &event)
Is the event an input event?
Definition events.cpp:753
void set_main_thread()
Definition events.cpp:468
std::deque< context > event_contexts
Definition events.cpp:215
std::vector< pump_monitor * > pump_monitors
Definition events.cpp:217
void call_in_main_thread(const std::function< void(void)> &f)
Definition events.cpp:773
void raise_process_event()
Definition events.cpp:717
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:740
std::list< sdl_handler * > handler_list
Definition events.hpp:41
bool is_in_main_thread()
Definition events.cpp:475
void pump()
Process all events currently in the queue.
Definition events.cpp:480
bool is_touch(const SDL_MouseButtonEvent &event)
Check if this mouse button event is caused by a touch.
Definition events.cpp:763
void focus_handler(const sdl_handler *ptr)
Definition events.cpp:402
constexpr auto reverse
Definition ranges.hpp:44
std::size_t erase(Container &container, const Value &value)
Convenience wrapper for using std::remove on a container.
Definition general.hpp:119
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition general.hpp:88
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:432
point window_size()
Returns the size of the window in display units / screen coordinates.
Definition video.cpp:419
SDL_Renderer * get_renderer()
Definition video.cpp:703
void update_buffers(bool autoupdate)
Update buffers to match current resolution and pixel scale settings.
Definition video.cpp:882
bool message_hook(const MSG &msg)
Frees resources when a notification disappears, switches user to the wesnoth window if the notificati...
Holds a 2D point.
Definition point.hpp:25
struct tagMSG MSG
#define i
#define h
#define f
#define c