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