1 /*
2    Copyright (C) 2009 - 2018 by Mark de Wever <koraq@xs4all.nl>
3    Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY.
11 
12    See the COPYING file for more details.
13 */
14 
15 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
17 #include "gui/core/event/handler.hpp"
18 
19 #include "gui/core/event/dispatcher.hpp"
20 #include "gui/core/timer.hpp"
21 #include "gui/core/log.hpp"
22 #include "gui/widgets/helper.hpp"
23 #include "gui/widgets/widget.hpp"
24 #include "gui/widgets/window.hpp"
25 #include "hotkey/hotkey_item.hpp"
26 #include "video.hpp"
27 #include "serialization/unicode_cast.hpp"
28 #include "sdl/userevent.hpp"
29 
30 #include <cassert>
31 
32 #include <boost/range/adaptor/reversed.hpp>
33 
34 /**
35  * @todo The items below are not implemented yet.
36  *
37  * - Tooltips have a fixed short time until showing up.
38  * - Tooltips are shown until the widget is exited.
39  * - Help messages aren't shown yet.
40  *
41  * @note it might be that tooltips will be shown independent of a window and in
42  * their own window, therefore the code will be cleaned up after that has been
43  * determined.
44  */
45 
46 /*
47  * At some point in the future this event handler should become the main event
48  * handler. This switch controls the experimental switch for that change.
49  */
50 //#define MAIN_EVENT_HANDLER
51 
52 /* Since this code is still very experimental it's not enabled yet. */
53 //#define ENABLE
54 
55 namespace gui2
56 {
57 
58 namespace event
59 {
60 
61 /***** Static data. *****/
62 static std::unique_ptr<class sdl_event_handler> handler_ = nullptr;
63 static events::event_context* event_context = nullptr;
64 
65 #ifdef MAIN_EVENT_HANDLER
66 static unsigned draw_interval = 0;
67 static unsigned event_poll_interval = 0;
68 
69 /***** Static functions. *****/
70 
71 /**
72  * SDL_AddTimer() callback for the draw event.
73  *
74  * When this callback is called it pushes a new draw event in the event queue.
75  *
76  * @returns                       The new timer interval, 0 to stop.
77  */
timer_sdl_draw_event(uint32_t,void *)78 static uint32_t timer_sdl_draw_event(uint32_t, void*)
79 {
80 	// DBG_GUI_E << "Pushing draw event in queue.\n";
81 
82 	SDL_Event event;
83 	sdl::UserEvent data(DRAW_EVENT);
84 
85 	event.type = DRAW_EVENT;
86 	event.user = data;
87 
88 	SDL_PushEvent(&event);
89 	return draw_interval;
90 }
91 
92 /**
93  * SDL_AddTimer() callback for the poll event.
94  *
95  * When this callback is called it will run the events in the SDL event queue.
96  *
97  * @returns                       The new timer interval, 0 to stop.
98  */
timer_sdl_poll_events(uint32_t,void *)99 static uint32_t timer_sdl_poll_events(uint32_t, void*)
100 {
101 	try
102 	{
103 		events::pump();
104 	}
105 	catch(CVideo::quit&)
106 	{
107 		return 0;
108 	}
109 	return event_poll_interval;
110 }
111 #endif
112 
113 /***** handler class. *****/
114 
115 /**
116  * This singleton class handles all events.
117  *
118  * It's a new experimental class.
119  */
120 class sdl_event_handler : public events::sdl_handler
121 {
122 	friend bool gui2::is_in_dialog();
123 
124 public:
125 	sdl_event_handler();
126 
127 	~sdl_event_handler();
128 
129 	/** Inherited from events::sdl_handler. */
130 	void handle_event(const SDL_Event& event);
131 
132 	/** Inherited from events::sdl_handler. */
133 	void handle_window_event(const SDL_Event& event);
134 
135 	/**
136 	 * Connects a dispatcher.
137 	 *
138 	 * @param dispatcher              The dispatcher to connect.
139 	 */
140 	void connect(dispatcher* dispatcher);
141 
142 	/**
143 	 * Disconnects a dispatcher.
144 	 *
145 	 * @param dispatcher              The dispatcher to disconnect.
146 	 */
147 	void disconnect(dispatcher* dispatcher);
148 
149 	/**
150 	 * Returns all dispatchers in the Z order.
151 	 */
get_dispatchers()152 	std::vector<dispatcher*>& get_dispatchers() { return dispatchers_; }
153 
154 	/** The dispatcher that captured the mouse focus. */
155 	dispatcher* mouse_focus;
156 
157 private:
158 	/**
159 	 * Reinitializes the state of all dispatchers.
160 	 *
161 	 * This is needed when the application gets activated, to make sure the
162 	 * state of mainly the mouse is set properly.
163 	 */
164 	void activate();
165 
166 	/***** Handlers *****/
167 
168 	/** Fires a raw SDL event. */
169 	void raw_event(const SDL_Event &event);
170 
171 	/** Fires a draw event. */
172 	using events::sdl_handler::draw;
173 	void draw();
174 	void draw_everything();
175 
176 	/**
177 	 * Fires a video resize event.
178 	 *
179 	 * @param new_size               The new size of the window.
180 	 */
181 	void video_resize(const point& new_size);
182 
183 	/**
184 	 * Fires a generic mouse event.
185 	 *
186 	 * @param event                  The event to fire.
187 	 * @param position               The position of the mouse.
188 	 */
189 	void mouse(const ui_event event, const point& position);
190 
191 	/**
192 	 * Fires a mouse button up event.
193 	 *
194 	 * @param position               The position of the mouse.
195 	 * @param button                 The SDL id of the button that caused the
196 	 *                               event.
197 	 */
198 	void mouse_button_up(const point& position, const uint8_t button);
199 
200 	/**
201 	 * Fires a mouse button down event.
202 	 *
203 	 * @param position               The position of the mouse.
204 	 * @param button                 The SDL id of the button that caused the
205 	 *                               event.
206 	 */
207 	void mouse_button_down(const point& position, const uint8_t button);
208 
209 	/**
210 	 * Fires a mouse wheel event.
211 	 *
212 	 * @param position               The position of the mouse.
213 	 * @param scrollx                The amount of horizontal scrolling.
214 	 * @param scrolly                The amount of vertical scrolling.
215 	 */
216 	void mouse_wheel(const point& position, int scrollx, int scrolly);
217 
218 	/**
219 	 * Gets the dispatcher that wants to receive the keyboard input.
220 	 *
221 	 * @returns                   The dispatcher.
222 	 * @retval nullptr               No dispatcher found.
223 	 */
224 	dispatcher* keyboard_dispatcher();
225 
226 	/**
227 	 * Fires a touch-moved event.
228 	 *
229 	 * @param position               The position touched.
230 	 * @param distance               The distance moved.
231 	 */
232 	void touch_motion(const point& position, const point& distance);
233 
234 	/**
235 	 * Fires a touch "finger down" event.
236 	 *
237 	 * @param position               The position touched.
238 	 */
239 	void touch_down(const point& position);
240 
241 	/**
242 	 * Fires a touch "finger up" event.
243 	 *
244 	 * @param position               The position touched.
245 	 */
246 	void touch_up(const point& position);
247 
248 	/**
249 	 * Fires a touch gesture event.
250 	 * @param center 				the center of gesture
251 	 * @param dTheta				the amount that the fingers rotated during this motion
252 	 * @param dDist					the amount that the fingers pinched during this motion
253 	 * @param numFingers			the number of fingers used in the gesture
254 	 */
255 	void touch_multi_gesture(const point& center, float dTheta, float dDist, Uint8 numFingers);
256 
257 	/**
258 	 * Handles a hat motion event.
259 	 *
260 	 * @param event                  The SDL joystick hat event triggered.
261 	 */
262 	void hat_motion(const SDL_Event& event);
263 
264 	/**
265 	 * Handles a joystick button down event.
266 	 *
267 	 * @param event                  The SDL joystick button event triggered.
268 	 */
269 	void button_down(const SDL_Event& event);
270 
271 	/**
272 	 * Fires a key down event.
273 	 *
274 	 * @param event                  The SDL keyboard event triggered.
275 	 */
276 	void key_down(const SDL_Event& event);
277 
278 	/**
279 	 * Handles the pressing of a hotkey.
280 	 *
281 	 * @param key                 The hotkey item pressed.
282 	 *
283 	 * @returns                   True if there was a valid dispatcher with
284 	 *                            which to execute the hotkey callback, false
285 	 *                            otherwise.
286 	 */
287 	bool hotkey_pressed(const hotkey::hotkey_ptr key);
288 
289 	/**
290 	 * Fires a key down event.
291 	 *
292 	 * @param key                    The SDL key code of the key pressed.
293 	 * @param modifier               The SDL key modifiers used.
294 	 * @param unicode                The unicode value for the key pressed.
295 	 */
296 	void key_down(const SDL_Keycode key,
297 				  const SDL_Keymod modifier,
298 				  const utf8::string& unicode);
299 
300 	/**
301 	 * Fires a text input event.
302 	 *
303 	 * @param unicode                The unicode value for the text entered.
304 	 */
305 	void text_input(const std::string& unicode);
306 
307 	/**
308 	 * Fires a text editing event.
309 	 *
310 	 * @param unicode                The unicode value for the text being edited.
311 	 * @param start                  The start position for the text being edited.
312 	 * @param len                    The selection length for the text being edited.
313 	 */
314 	void text_editing(const std::string& unicode, int32_t start, int32_t len);
315 
316 	/**
317 	 * Fires a keyboard event which has no parameters.
318 	 *
319 	 * This can happen for example when the mouse wheel is used.
320 	 *
321 	 * @param event                  The event to fire.
322 	 */
323 	void keyboard(const ui_event event);
324 
325 	/**
326 	 * Fires a CLOSE_WINDOW event for the window with the given ID.
327 	 *
328 	 * @param window_id              The ID of the window to close.
329 	 */
330 	void close_window(const unsigned window_id);
331 
332 	/**
333 	 * The dispatchers.
334 	 *
335 	 * The order of the items in the list is also the z-order the front item
336 	 * being the one completely in the background and the back item the one
337 	 * completely in the foreground.
338 	 */
339 	std::vector<dispatcher*> dispatchers_;
340 
341 	/**
342 	 * Needed to determine which dispatcher gets the keyboard events.
343 	 *
344 	 * NOTE the keyboard events aren't really wired in yet so doesn't do much.
345 	 */
346 	dispatcher* keyboard_focus_;
347 	friend void capture_keyboard(dispatcher*);
348 };
349 
sdl_event_handler()350 sdl_event_handler::sdl_event_handler()
351 	: events::sdl_handler(false)
352 	, mouse_focus(nullptr)
353 	, dispatchers_()
354 	, keyboard_focus_(nullptr)
355 {
356 	if(SDL_WasInit(SDL_INIT_TIMER) == 0) {
357 		if(SDL_InitSubSystem(SDL_INIT_TIMER) == -1) {
358 			assert(false);
359 		}
360 	}
361 
362 // The event context is created now we join it.
363 #ifdef ENABLE
364 	join();
365 #endif
366 }
367 
~sdl_event_handler()368 sdl_event_handler::~sdl_event_handler()
369 {
370 #ifdef ENABLE
371 	leave();
372 #endif
373 }
374 
handle_event(const SDL_Event & event)375 void sdl_event_handler::handle_event(const SDL_Event& event)
376 {
377 	/** No dispatchers drop the event. */
378 	if(dispatchers_.empty()) {
379 		return;
380 	}
381 
382 	Uint8 button = event.button.button;
383 	CVideo& video = dynamic_cast<window&>(*dispatchers_.back()).video();
384 
385 	switch(event.type) {
386 		case SDL_MOUSEMOTION:
387 #ifdef MOUSE_TOUCH_EMULATION
388 			// There's no finger motion when it's not down.
389 			if (event.motion.state != 0)
390 #endif
391 			{
392 				mouse(SDL_MOUSE_MOTION, {event.motion.x, event.motion.y});
393 			}
394 			break;
395 
396 		case SDL_MOUSEBUTTONDOWN:
397 			{
398 				mouse_button_down({event.button.x, event.button.y}, button);
399 			}
400 			break;
401 
402 		case SDL_MOUSEBUTTONUP:
403 			{
404 				mouse_button_up({event.button.x, event.button.y}, button);
405 			}
406 			break;
407 
408 		case SDL_MOUSEWHEEL:
409 			mouse_wheel(get_mouse_position(), event.wheel.x, event.wheel.y);
410 			break;
411 
412 		case SHOW_HELPTIP_EVENT:
413 			mouse(SHOW_HELPTIP, get_mouse_position());
414 			break;
415 
416 		case HOVER_REMOVE_POPUP_EVENT:
417 			// remove_popup();
418 			break;
419 
420 		case DRAW_EVENT:
421 			draw();
422 			break;
423 
424 		case DRAW_ALL_EVENT:
425 			draw_everything();
426 			break;
427 
428 		case TIMER_EVENT:
429 			execute_timer(reinterpret_cast<size_t>(event.user.data1));
430 			break;
431 
432 		case CLOSE_WINDOW_EVENT:
433 			close_window(event.user.code);
434 			break;
435 
436 		case SDL_JOYBUTTONDOWN:
437 			button_down(event);
438 			break;
439 
440 		case SDL_JOYBUTTONUP:
441 			break;
442 
443 		case SDL_JOYAXISMOTION:
444 			break;
445 
446 		case SDL_JOYHATMOTION:
447 			hat_motion(event);
448 			break;
449 
450 		case SDL_KEYDOWN:
451 			key_down(event);
452 			break;
453 
454 		case SDL_WINDOWEVENT:
455 			switch(event.window.event) {
456 				case SDL_WINDOWEVENT_EXPOSED:
457 					draw();
458 					break;
459 
460 				case SDL_WINDOWEVENT_RESIZED:
461 					video_resize({event.window.data1, event.window.data2});
462 					break;
463 
464 				case SDL_WINDOWEVENT_ENTER:
465 				case SDL_WINDOWEVENT_FOCUS_GAINED:
466 					activate();
467 					break;
468 			}
469 
470 			break;
471 
472 		case SDL_TEXTINPUT:
473 			key_down(event);
474 			break;
475 
476 		case SDL_TEXTEDITING:
477 			text_editing(event.edit.text, event.edit.start, event.edit.length);
478 			break;
479 
480 		case SDL_FINGERMOTION:
481 			{
482 				SDL_Rect r = video.screen_area();
483 				touch_motion(point(event.tfinger.x * r.w, event.tfinger.y * r.h),
484 							 point(event.tfinger.dx * r.w, event.tfinger.dy * r.h));
485 			}
486 			break;
487 
488 		case SDL_FINGERUP:
489 			{
490 				SDL_Rect r = video.screen_area();
491 				touch_up(point(event.tfinger.x * r.w, event.tfinger.y * r.h));
492 			}
493 			break;
494 
495 		case SDL_FINGERDOWN:
496 			{
497 				SDL_Rect r = video.screen_area();
498 				touch_down(point(event.tfinger.x * r.w, event.tfinger.y * r.h));
499 			}
500 			break;
501 
502 		case SDL_MULTIGESTURE:
503 			{
504 				SDL_Rect r = video.screen_area();
505 				touch_multi_gesture(point(event.mgesture.x * r.w, event.mgesture.y * r.h),
506 									event.mgesture.dTheta, event.mgesture.dDist, event.mgesture.numFingers);
507 			}
508 			break;
509 
510 #if(defined(_X11) && !defined(__APPLE__)) || defined(_WIN32)
511 		case SDL_SYSWMEVENT:
512 			/* DO NOTHING */
513 			break;
514 #endif
515 
516 		// Silently ignored events.
517 		case SDL_KEYUP:
518 		case DOUBLE_CLICK_EVENT:
519 			break;
520 
521 		default:
522 #ifdef GUI2_SHOW_UNHANDLED_EVENT_WARNINGS
523 			WRN_GUI_E << "Unhandled event " << static_cast<uint32_t>(event.type)
524 			          << ".\n";
525 #endif
526 			break;
527 	}
528 
529 	raw_event(event);
530 }
531 
handle_window_event(const SDL_Event & event)532 void sdl_event_handler::handle_window_event(const SDL_Event& event)
533 {
534 	handle_event(event);
535 }
536 
connect(dispatcher * dispatcher)537 void sdl_event_handler::connect(dispatcher* dispatcher)
538 {
539 	assert(std::find(dispatchers_.begin(), dispatchers_.end(), dispatcher)
540 		   == dispatchers_.end());
541 
542 	if(dispatchers_.empty()) {
543 		event_context = new events::event_context();
544 		join();
545 	}
546 
547 	dispatchers_.push_back(dispatcher);
548 }
549 
disconnect(dispatcher * disp)550 void sdl_event_handler::disconnect(dispatcher* disp)
551 {
552 	/***** Validate pre conditions. *****/
553 	auto itor = std::find(dispatchers_.begin(), dispatchers_.end(), disp);
554 	assert(itor != dispatchers_.end());
555 
556 	/***** Remove dispatcher. *****/
557 	dispatchers_.erase(itor);
558 
559 	if(disp == mouse_focus) {
560 		mouse_focus = nullptr;
561 	}
562 	if(disp == keyboard_focus_) {
563 		keyboard_focus_ = nullptr;
564 	}
565 
566 	/***** Set proper state for the other dispatchers. *****/
567 	for(auto d : dispatchers_)
568 	{
569 		dynamic_cast<widget&>(*d).set_is_dirty(true);
570 	}
571 
572 	activate();
573 
574 	/***** Validate post conditions. *****/
575 	assert(std::find(dispatchers_.begin(), dispatchers_.end(), disp)
576 		   == dispatchers_.end());
577 
578 	if(dispatchers_.empty()) {
579 		leave();
580 		delete event_context;
581 		event_context = nullptr;
582 	}
583 }
584 
activate()585 void sdl_event_handler::activate()
586 {
587 	for(auto dispatcher : dispatchers_)
588 	{
589 		dispatcher->fire(SDL_ACTIVATE, dynamic_cast<widget&>(*dispatcher), nullptr);
590 	}
591 }
592 
draw()593 void sdl_event_handler::draw()
594 {
595 	for(auto dispatcher : dispatchers_)
596 	{
597 		dispatcher->fire(DRAW, dynamic_cast<widget&>(*dispatcher));
598 	}
599 
600 	if(!dispatchers_.empty()) {
601 		CVideo& video = dynamic_cast<window&>(*dispatchers_.back()).video();
602 
603 		video.flip();
604 	}
605 }
606 
draw_everything()607 void sdl_event_handler::draw_everything()
608 {
609 	for(auto dispatcher : dispatchers_) {
610 		dynamic_cast<widget&>(*dispatcher).set_is_dirty(true);
611 	}
612 
613 	draw();
614 }
615 
video_resize(const point & new_size)616 void sdl_event_handler::video_resize(const point& new_size)
617 {
618 	DBG_GUI_E << "Firing: " << SDL_VIDEO_RESIZE << ".\n";
619 
620 	for(auto dispatcher : dispatchers_)
621 	{
622 		dispatcher->fire(SDL_VIDEO_RESIZE, dynamic_cast<widget&>(*dispatcher), new_size);
623 	}
624 }
625 
raw_event(const SDL_Event & event)626 void sdl_event_handler::raw_event(const SDL_Event& event) {
627 	DBG_GUI_E << "Firing raw event\n";
628 
629 	for(auto dispatcher : dispatchers_)
630 	{
631 		dispatcher->fire(SDL_RAW_EVENT, dynamic_cast<widget&>(*dispatcher), event);
632 	}
633 }
634 
mouse(const ui_event event,const point & position)635 void sdl_event_handler::mouse(const ui_event event, const point& position)
636 {
637 	DBG_GUI_E << "Firing: " << event << ".\n";
638 
639 	if(mouse_focus) {
640 		mouse_focus->fire(event, dynamic_cast<widget&>(*mouse_focus), position);
641 		return;
642 	}
643 
644 	for(auto& dispatcher : boost::adaptors::reverse(dispatchers_)) {
645 		if(dispatcher->get_mouse_behavior() == dispatcher::all) {
646 			dispatcher->fire(event, dynamic_cast<widget&>(*dispatcher), position);
647 			break;
648 		}
649 
650 		if(dispatcher->get_mouse_behavior() == dispatcher::none) {
651 			continue;
652 		}
653 
654 		if(dispatcher->is_at(position)) {
655 			dispatcher->fire(event, dynamic_cast<widget&>(*dispatcher), position);
656 			break;
657 		}
658 	}
659 }
660 
mouse_button_up(const point & position,const uint8_t button)661 void sdl_event_handler::mouse_button_up(const point& position, const uint8_t button)
662 {
663 	switch(button) {
664 		case SDL_BUTTON_LEFT:
665 			mouse(SDL_LEFT_BUTTON_UP, position);
666 			break;
667 		case SDL_BUTTON_MIDDLE:
668 			mouse(SDL_MIDDLE_BUTTON_UP, position);
669 			break;
670 		case SDL_BUTTON_RIGHT:
671 			mouse(SDL_RIGHT_BUTTON_UP, position);
672 			break;
673 		default:
674 #ifdef GUI2_SHOW_UNHANDLED_EVENT_WARNINGS
675 			WRN_GUI_E << "Unhandled 'mouse button up' event for button "
676 					  << static_cast<uint32_t>(button) << ".\n";
677 #endif
678 			break;
679 	}
680 }
681 
mouse_button_down(const point & position,const uint8_t button)682 void sdl_event_handler::mouse_button_down(const point& position, const uint8_t button)
683 {
684 	switch(button) {
685 		case SDL_BUTTON_LEFT:
686 			mouse(SDL_LEFT_BUTTON_DOWN, position);
687 			break;
688 		case SDL_BUTTON_MIDDLE:
689 			mouse(SDL_MIDDLE_BUTTON_DOWN, position);
690 			break;
691 		case SDL_BUTTON_RIGHT:
692 			mouse(SDL_RIGHT_BUTTON_DOWN, position);
693 			break;
694 		default:
695 #ifdef GUI2_SHOW_UNHANDLED_EVENT_WARNINGS
696 			WRN_GUI_E << "Unhandled 'mouse button down' event for button "
697 					  << static_cast<uint32_t>(button) << ".\n";
698 #endif
699 			break;
700 	}
701 }
702 
mouse_wheel(const point & position,int x,int y)703 void sdl_event_handler::mouse_wheel(const point& position, int x, int y)
704 {
705 	if(x > 0) {
706 		mouse(SDL_WHEEL_RIGHT, position);
707 	} else if(x < 0) {
708 		mouse(SDL_WHEEL_LEFT, position);
709 	}
710 
711 	if(y < 0) {
712 		mouse(SDL_WHEEL_DOWN, position);
713 	} else if(y > 0) {
714 		mouse(SDL_WHEEL_UP, position);
715 	}
716 }
717 
keyboard_dispatcher()718 dispatcher* sdl_event_handler::keyboard_dispatcher()
719 {
720 	if(keyboard_focus_) {
721 		return keyboard_focus_;
722 	}
723 
724 	for(auto& dispatcher : boost::adaptors::reverse(dispatchers_)) {
725 		if(dispatcher->get_want_keyboard_input()) {
726 			return dispatcher;
727 		}
728 	}
729 
730 	return nullptr;
731 }
732 
touch_motion(const point & position,const point & distance)733 void sdl_event_handler::touch_motion(const point& position, const point& distance)
734 {
735 	for(auto& dispatcher : boost::adaptors::reverse(dispatchers_)) {
736 		dispatcher->fire(SDL_TOUCH_MOTION , dynamic_cast<widget&>(*dispatcher), position, distance);
737 	}
738 }
739 
touch_up(const point & position)740 void sdl_event_handler::touch_up(const point& position)
741 {
742 	for(auto& dispatcher : boost::adaptors::reverse(dispatchers_)) {
743 		dispatcher->fire(SDL_TOUCH_UP, dynamic_cast<widget&>(*dispatcher), position);
744 	}
745 }
746 
touch_down(const point & position)747 void sdl_event_handler::touch_down(const point& position)
748 {
749 	for(auto& dispatcher : boost::adaptors::reverse(dispatchers_)) {
750 		dispatcher->fire(SDL_TOUCH_DOWN, dynamic_cast<widget&>(*dispatcher), position);
751 	}
752 }
753 
touch_multi_gesture(const point & center,float dTheta,float dDist,Uint8 numFingers)754 void sdl_event_handler::touch_multi_gesture(const point& center, float dTheta, float dDist, Uint8 numFingers)
755 {
756 	for(auto& dispatcher : boost::adaptors::reverse(dispatchers_)) {
757 		dispatcher->fire(SDL_TOUCH_MULTI_GESTURE, dynamic_cast<widget&>(*dispatcher), center, dTheta, dDist, numFingers);
758 	}
759 }
760 
hat_motion(const SDL_Event & event)761 void sdl_event_handler::hat_motion(const SDL_Event& event)
762 {
763 	const hotkey::hotkey_ptr& hk = hotkey::get_hotkey(event);
764 	bool done = false;
765 	if(!hk->null()) {
766 		done = hotkey_pressed(hk);
767 	}
768 	if(!done) {
769 		// TODO fendrin think about handling hat motions that are not bound to a
770 		// hotkey.
771 	}
772 }
773 
button_down(const SDL_Event & event)774 void sdl_event_handler::button_down(const SDL_Event& event)
775 {
776 	const hotkey::hotkey_ptr hk = hotkey::get_hotkey(event);
777 	bool done = false;
778 	if(!hk->null()) {
779 		done = hotkey_pressed(hk);
780 	}
781 	if(!done) {
782 		// TODO fendrin think about handling button down events that are not
783 		// bound to a hotkey.
784 	}
785 }
786 
key_down(const SDL_Event & event)787 void sdl_event_handler::key_down(const SDL_Event& event)
788 {
789 	const hotkey::hotkey_ptr hk = hotkey::get_hotkey(event);
790 	bool done = false;
791 	if(!hk->null()) {
792 		done = hotkey_pressed(hk);
793 	}
794 	if(!done) {
795 		if(event.type == SDL_TEXTINPUT) {
796 			text_input(event.text.text);
797 		} else {
798 			key_down(event.key.keysym.sym, static_cast<SDL_Keymod>(event.key.keysym.mod), "");
799 		}
800 	}
801 }
802 
text_input(const std::string & unicode)803 void sdl_event_handler::text_input(const std::string& unicode)
804 {
805 	key_down(SDLK_UNKNOWN, static_cast<SDL_Keymod>(0), unicode);
806 
807 	if(dispatcher* dispatcher = keyboard_dispatcher()) {
808 		dispatcher->fire(SDL_TEXT_INPUT,
809 			dynamic_cast<widget&>(*dispatcher),
810 			unicode, -1, -1);
811 	}
812 }
813 
text_editing(const std::string & unicode,int32_t start,int32_t len)814 void sdl_event_handler::text_editing(const std::string& unicode, int32_t start, int32_t len)
815 {
816 	if(dispatcher* dispatcher = keyboard_dispatcher()) {
817 		dispatcher->fire(SDL_TEXT_EDITING,
818 			dynamic_cast<widget&>(*dispatcher),
819 			unicode, start, len);
820 	}
821 }
822 
hotkey_pressed(const hotkey::hotkey_ptr key)823 bool sdl_event_handler::hotkey_pressed(const hotkey::hotkey_ptr key)
824 {
825 	if(dispatcher* dispatcher = keyboard_dispatcher()) {
826 		return dispatcher->execute_hotkey(hotkey::get_id(key->get_command()));
827 	}
828 
829 	return false;
830 }
831 
key_down(const SDL_Keycode key,const SDL_Keymod modifier,const utf8::string & unicode)832 void sdl_event_handler::key_down(const SDL_Keycode key,
833 						const SDL_Keymod modifier,
834 						const utf8::string& unicode)
835 {
836 	DBG_GUI_E << "Firing: " << SDL_KEY_DOWN << ".\n";
837 
838 	if(dispatcher* dispatcher = keyboard_dispatcher()) {
839 		dispatcher->fire(SDL_KEY_DOWN,
840 						 dynamic_cast<widget&>(*dispatcher),
841 						 key,
842 						 modifier,
843 						 unicode);
844 	}
845 }
846 
keyboard(const ui_event event)847 void sdl_event_handler::keyboard(const ui_event event)
848 {
849 	DBG_GUI_E << "Firing: " << event << ".\n";
850 
851 	if(dispatcher* dispatcher = keyboard_dispatcher()) {
852 		dispatcher->fire(event, dynamic_cast<widget&>(*dispatcher));
853 	}
854 }
855 
close_window(const unsigned window_id)856 void sdl_event_handler::close_window(const unsigned window_id)
857 {
858 	DBG_GUI_E << "Firing " << CLOSE_WINDOW << ".\n";
859 
860 	window* window = window::window_instance(window_id);
861 	if(window) {
862 		window->fire(CLOSE_WINDOW, *window);
863 	}
864 }
865 
866 /***** manager class. *****/
867 
manager()868 manager::manager()
869 {
870 	handler_.reset(new sdl_event_handler());
871 
872 #ifdef MAIN_EVENT_HANDLER
873 	draw_interval = 30;
874 	SDL_AddTimer(draw_interval, timer_sdl_draw_event, nullptr);
875 
876 	event_poll_interval = 10;
877 	SDL_AddTimer(event_poll_interval, timer_sdl_poll_events, nullptr);
878 #endif
879 }
880 
~manager()881 manager::~manager()
882 {
883 	handler_.reset(nullptr);
884 
885 #ifdef MAIN_EVENT_HANDLER
886 	draw_interval = 0;
887 	event_poll_interval = 0;
888 #endif
889 }
890 
891 /***** free functions class. *****/
892 
connect_dispatcher(dispatcher * dispatcher)893 void connect_dispatcher(dispatcher* dispatcher)
894 {
895 	assert(handler_);
896 	assert(dispatcher);
897 	handler_->connect(dispatcher);
898 }
899 
disconnect_dispatcher(dispatcher * dispatcher)900 void disconnect_dispatcher(dispatcher* dispatcher)
901 {
902 	assert(handler_);
903 	assert(dispatcher);
904 	handler_->disconnect(dispatcher);
905 }
906 
get_all_dispatchers()907 std::vector<dispatcher*>& get_all_dispatchers()
908 {
909 	assert(handler_);
910 	return handler_->get_dispatchers();
911 }
912 
init_mouse_location()913 void init_mouse_location()
914 {
915 	point mouse = get_mouse_position();
916 
917 	SDL_Event event{};
918 	event.type = SDL_MOUSEMOTION;
919 	event.motion.type = SDL_MOUSEMOTION;
920 	event.motion.x = mouse.x;
921 	event.motion.y = mouse.y;
922 
923 	SDL_PushEvent(&event);
924 }
925 
capture_mouse(dispatcher * dispatcher)926 void capture_mouse(dispatcher* dispatcher)
927 {
928 	assert(handler_);
929 	assert(dispatcher);
930 	handler_->mouse_focus = dispatcher;
931 }
932 
release_mouse(dispatcher * dispatcher)933 void release_mouse(dispatcher* dispatcher)
934 {
935 	assert(handler_);
936 	assert(dispatcher);
937 	if(handler_->mouse_focus == dispatcher) {
938 		handler_->mouse_focus = nullptr;
939 	}
940 }
941 
capture_keyboard(dispatcher * dispatcher)942 void capture_keyboard(dispatcher* dispatcher)
943 {
944 	assert(handler_);
945 	assert(dispatcher);
946 	assert(dispatcher->get_want_keyboard_input());
947 
948 	handler_->keyboard_focus_ = dispatcher;
949 }
950 
operator <<(std::ostream & stream,const ui_event event)951 std::ostream& operator<<(std::ostream& stream, const ui_event event)
952 {
953 	switch(event) {
954 		case DRAW:
955 			stream << "draw";
956 			break;
957 		case CLOSE_WINDOW:
958 			stream << "close window";
959 			break;
960 		case SDL_VIDEO_RESIZE:
961 			stream << "SDL video resize";
962 			break;
963 		case SDL_MOUSE_MOTION:
964 			stream << "SDL mouse motion";
965 			break;
966 		case MOUSE_ENTER:
967 			stream << "mouse enter";
968 			break;
969 		case MOUSE_LEAVE:
970 			stream << "mouse leave";
971 			break;
972 		case MOUSE_MOTION:
973 			stream << "mouse motion";
974 			break;
975 		case SDL_LEFT_BUTTON_DOWN:
976 			stream << "SDL left button down";
977 			break;
978 		case SDL_LEFT_BUTTON_UP:
979 			stream << "SDL left button up";
980 			break;
981 		case LEFT_BUTTON_DOWN:
982 			stream << "left button down";
983 			break;
984 		case LEFT_BUTTON_UP:
985 			stream << "left button up";
986 			break;
987 		case LEFT_BUTTON_CLICK:
988 			stream << "left button click";
989 			break;
990 		case LEFT_BUTTON_DOUBLE_CLICK:
991 			stream << "left button double click";
992 			break;
993 		case SDL_MIDDLE_BUTTON_DOWN:
994 			stream << "SDL middle button down";
995 			break;
996 		case SDL_MIDDLE_BUTTON_UP:
997 			stream << "SDL middle button up";
998 			break;
999 		case MIDDLE_BUTTON_DOWN:
1000 			stream << "middle button down";
1001 			break;
1002 		case MIDDLE_BUTTON_UP:
1003 			stream << "middle button up";
1004 			break;
1005 		case MIDDLE_BUTTON_CLICK:
1006 			stream << "middle button click";
1007 			break;
1008 		case MIDDLE_BUTTON_DOUBLE_CLICK:
1009 			stream << "middle button double click";
1010 			break;
1011 		case SDL_RIGHT_BUTTON_DOWN:
1012 			stream << "SDL right button down";
1013 			break;
1014 		case SDL_RIGHT_BUTTON_UP:
1015 			stream << "SDL right button up";
1016 			break;
1017 		case RIGHT_BUTTON_DOWN:
1018 			stream << "right button down";
1019 			break;
1020 		case RIGHT_BUTTON_UP:
1021 			stream << "right button up";
1022 			break;
1023 		case RIGHT_BUTTON_CLICK:
1024 			stream << "right button click";
1025 			break;
1026 		case RIGHT_BUTTON_DOUBLE_CLICK:
1027 			stream << "right button double click";
1028 			break;
1029 		case SDL_WHEEL_LEFT:
1030 			stream << "SDL wheel left";
1031 			break;
1032 		case SDL_WHEEL_RIGHT:
1033 			stream << "SDL wheel right";
1034 			break;
1035 		case SDL_WHEEL_UP:
1036 			stream << "SDL wheel up";
1037 			break;
1038 		case SDL_WHEEL_DOWN:
1039 			stream << "SDL wheel down";
1040 			break;
1041 		case SDL_KEY_DOWN:
1042 			stream << "SDL key down";
1043 			break;
1044 		case SDL_TEXT_INPUT:
1045 			stream << "SDL text input";
1046 			break;
1047 		case SDL_TEXT_EDITING:
1048 			stream << "SDL text editing";
1049 			break;
1050 
1051 		case NOTIFY_REMOVAL:
1052 			stream << "notify removal";
1053 			break;
1054 		case NOTIFY_MODIFIED:
1055 			stream << "notify modified";
1056 			break;
1057 		case RECEIVE_KEYBOARD_FOCUS:
1058 			stream << "receive keyboard focus";
1059 			break;
1060 		case LOSE_KEYBOARD_FOCUS:
1061 			stream << "lose keyboard focus";
1062 			break;
1063 		case SHOW_TOOLTIP:
1064 			stream << "show tooltip";
1065 			break;
1066 		case NOTIFY_REMOVE_TOOLTIP:
1067 			stream << "notify remove tooltip";
1068 			break;
1069 		case SDL_ACTIVATE:
1070 			stream << "SDL activate";
1071 			break;
1072 		case MESSAGE_SHOW_TOOLTIP:
1073 			stream << "message show tooltip";
1074 			break;
1075 		case SHOW_HELPTIP:
1076 			stream << "show helptip";
1077 			break;
1078 		case MESSAGE_SHOW_HELPTIP:
1079 			stream << "message show helptip";
1080 			break;
1081 		case REQUEST_PLACEMENT:
1082 			stream << "request placement";
1083 			break;
1084 		case SDL_TOUCH_MOTION:
1085 			stream << "SDL touch motion";
1086 			break;
1087 		case SDL_TOUCH_UP:
1088 			stream << "SDL touch up";
1089 			break;
1090 		case SDL_TOUCH_DOWN:
1091 			stream << "SDL touch down";
1092 			break;
1093 		case SDL_TOUCH_MULTI_GESTURE:
1094 			stream << "SDL multi-touch gesture";
1095 			break;
1096 		case SDL_RAW_EVENT:
1097 			stream << "SDL raw event";
1098 			break;
1099 	}
1100 
1101 	return stream;
1102 }
1103 
1104 } // namespace event
1105 
1106 std::vector<window*> open_window_stack {};
1107 
remove_from_window_stack(window * window)1108 void remove_from_window_stack(window* window)
1109 {
1110 	for(auto iter = open_window_stack.rbegin(); iter != open_window_stack.rend(); ++iter) {
1111 		if(*iter == window) {
1112 			open_window_stack.erase(std::next(iter).base());
1113 			break;
1114 		}
1115 	}
1116 }
1117 
is_in_dialog()1118 bool is_in_dialog()
1119 {
1120 	return !open_window_stack.empty();
1121 }
1122 
1123 } // namespace gui2
1124