]> git.lizzy.rs Git - minetest.git/blob - src/touchscreengui.cpp
Isolate irrlicht references and use a singleton (#6041)
[minetest.git] / src / touchscreengui.cpp
1 /*
2 Copyright (C) 2014 sapier
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation; either version 2.1 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #include "touchscreengui.h"
20 #include "irrlichttypes.h"
21 #include "irr_v2d.h"
22 #include "log.h"
23 #include "keycode.h"
24 #include "settings.h"
25 #include "gettime.h"
26 #include "util/numeric.h"
27 #include "porting.h"
28 #include "guiscalingfilter.h"
29
30 #include <iostream>
31 #include <algorithm>
32
33 #include <ISceneCollisionManager.h>
34
35 // Very slow button repeat frequency (in seconds)
36 #define SLOW_BUTTON_REPEAT      (1.0f)
37
38 using namespace irr::core;
39
40 extern Settings *g_settings;
41
42 const char** touchgui_button_imagenames = (const char*[]) {
43         "up_arrow.png",
44         "down_arrow.png",
45         "left_arrow.png",
46         "right_arrow.png",
47         "jump_btn.png",
48         "down.png"
49 };
50
51 static irr::EKEY_CODE id2keycode(touch_gui_button_id id)
52 {
53         std::string key = "";
54         switch (id) {
55                 case forward_id:
56                         key = "forward";
57                         break;
58                 case left_id:
59                         key = "left";
60                         break;
61                 case right_id:
62                         key = "right";
63                         break;
64                 case backward_id:
65                         key = "backward";
66                         break;
67                 case inventory_id:
68                         key = "inventory";
69                         break;
70                 case drop_id:
71                         key = "drop";
72                         break;
73                 case jump_id:
74                         key = "jump";
75                         break;
76                 case crunch_id:
77                         key = "sneak";
78                         break;
79                 case fly_id:
80                         key = "freemove";
81                         break;
82                 case noclip_id:
83                         key = "noclip";
84                         break;
85                 case fast_id:
86                         key = "fastmove";
87                         break;
88                 case debug_id:
89                         key = "toggle_debug";
90                         break;
91                 case chat_id:
92                         key = "chat";
93                         break;
94                 case camera_id:
95                         key = "camera_mode";
96                         break;
97                 case range_id:
98                         key = "rangeselect";
99                         break;
100         }
101         assert(key != "");
102         return keyname_to_keycode(g_settings->get("keymap_" + key).c_str());
103 }
104
105 TouchScreenGUI *g_touchscreengui;
106
107 static void load_button_texture(button_info* btn, const char* path,
108                 rect<s32> button_rect, ISimpleTextureSource* tsrc, video::IVideoDriver *driver)
109 {
110         unsigned int tid;
111         video::ITexture *texture = guiScalingImageButton(driver,
112                         tsrc->getTexture(path, &tid), button_rect.getWidth(),
113                         button_rect.getHeight());
114         if (texture) {
115                 btn->guibutton->setUseAlphaChannel(true);
116                 if (g_settings->getBool("gui_scaling_filter")) {
117                         rect<s32> txr_rect = rect<s32>(0, 0, button_rect.getWidth(), button_rect.getHeight());
118                         btn->guibutton->setImage(texture, txr_rect);
119                         btn->guibutton->setPressedImage(texture, txr_rect);
120                         btn->guibutton->setScaleImage(false);
121                 } else {
122                         btn->guibutton->setImage(texture);
123                         btn->guibutton->setPressedImage(texture);
124                         btn->guibutton->setScaleImage(true);
125                 }
126                 btn->guibutton->setDrawBorder(false);
127                 btn->guibutton->setText(L"");
128                 }
129 }
130
131 AutoHideButtonBar::AutoHideButtonBar(IrrlichtDevice *device,
132                 IEventReceiver* receiver) :
133                         m_driver(device->getVideoDriver()),
134                         m_guienv(device->getGUIEnvironment()),
135                         m_receiver(receiver)
136 {
137 }
138
139 void AutoHideButtonBar::init(ISimpleTextureSource* tsrc,
140                 const char* starter_img, int button_id, v2s32 UpperLeft,
141                 v2s32 LowerRight, autohide_button_bar_dir dir, float timeout)
142 {
143         m_texturesource = tsrc;
144
145         m_upper_left = UpperLeft;
146         m_lower_right = LowerRight;
147
148         /* init settings bar */
149
150         irr::core::rect<int> current_button = rect<s32>(UpperLeft.X, UpperLeft.Y,
151                         LowerRight.X, LowerRight.Y);
152
153         m_starter.guibutton         = m_guienv->addButton(current_button, 0, button_id, L"", 0);
154         m_starter.guibutton->grab();
155         m_starter.repeatcounter     = -1;
156         m_starter.keycode           = KEY_OEM_8; // use invalid keycode as it's not relevant
157         m_starter.immediate_release = true;
158         m_starter.ids.clear();
159
160         load_button_texture(&m_starter, starter_img, current_button,
161                         m_texturesource, m_driver);
162
163         m_dir = dir;
164         m_timeout_value = timeout;
165
166         m_initialized = true;
167 }
168
169 AutoHideButtonBar::~AutoHideButtonBar()
170 {
171         if (m_starter.guibutton) {
172                 m_starter.guibutton->setVisible(false);
173                 m_starter.guibutton->drop();
174         }
175 }
176
177 void AutoHideButtonBar::addButton(touch_gui_button_id button_id,
178                 const wchar_t* caption, const char* btn_image)
179 {
180
181         if (!m_initialized) {
182                 errorstream << "AutoHideButtonBar::addButton not yet initialized!"
183                                 << std::endl;
184                 return;
185         }
186         int button_size = 0;
187
188         if ((m_dir == AHBB_Dir_Top_Bottom) || (m_dir == AHBB_Dir_Bottom_Top)) {
189                 button_size = m_lower_right.X - m_upper_left.X;
190         } else {
191                 button_size = m_lower_right.Y - m_upper_left.Y;
192         }
193
194         irr::core::rect<int> current_button;
195
196         if ((m_dir == AHBB_Dir_Right_Left) || (m_dir == AHBB_Dir_Left_Right)) {
197
198                 int x_start = 0;
199                 int x_end = 0;
200
201                 if (m_dir == AHBB_Dir_Left_Right) {
202                         x_start = m_lower_right.X + (button_size * 1.25 * m_buttons.size())
203                                         + (button_size * 0.25);
204                         x_end = x_start + button_size;
205                 } else {
206                         x_end = m_upper_left.X - (button_size * 1.25 * m_buttons.size())
207                                         - (button_size * 0.25);
208                         x_start = x_end - button_size;
209                 }
210
211                 current_button = rect<s32>(x_start, m_upper_left.Y, x_end,
212                                 m_lower_right.Y);
213         } else {
214                 int y_start = 0;
215                 int y_end = 0;
216
217                 if (m_dir == AHBB_Dir_Top_Bottom) {
218                         y_start = m_lower_right.X + (button_size * 1.25 * m_buttons.size())
219                                         + (button_size * 0.25);
220                         y_end = y_start + button_size;
221                 } else {
222                         y_end = m_upper_left.X - (button_size * 1.25 * m_buttons.size())
223                                         - (button_size * 0.25);
224                         y_start = y_end - button_size;
225                 }
226
227                 current_button = rect<s32>(m_upper_left.X, y_start, m_lower_right.Y,
228                                 y_end);
229         }
230
231         button_info* btn       = new button_info();
232         btn->guibutton         = m_guienv->addButton(current_button, 0, button_id, caption, 0);
233         btn->guibutton->grab();
234         btn->guibutton->setVisible(false);
235         btn->guibutton->setEnabled(false);
236         btn->repeatcounter     = -1;
237         btn->keycode           = id2keycode(button_id);
238         btn->immediate_release = true;
239         btn->ids.clear();
240
241         load_button_texture(btn, btn_image, current_button, m_texturesource,
242                         m_driver);
243
244         m_buttons.push_back(btn);
245 }
246
247 bool AutoHideButtonBar::isButton(const SEvent &event)
248 {
249         IGUIElement* rootguielement = m_guienv->getRootGUIElement();
250
251         if (rootguielement == NULL) {
252                 return false;
253         }
254
255         gui::IGUIElement *element = rootguielement->getElementFromPoint(
256                         core::position2d<s32>(event.TouchInput.X, event.TouchInput.Y));
257
258         if (element == NULL) {
259                 return false;
260         }
261
262         if (m_active) {
263                 /* check for all buttons in vector */
264
265                 std::vector<button_info*>::iterator iter = m_buttons.begin();
266
267                 while (iter != m_buttons.end()) {
268                         if ((*iter)->guibutton == element) {
269
270                                 SEvent* translated = new SEvent();
271                                 memset(translated, 0, sizeof(SEvent));
272                                 translated->EventType            = irr::EET_KEY_INPUT_EVENT;
273                                 translated->KeyInput.Key         = (*iter)->keycode;
274                                 translated->KeyInput.Control     = false;
275                                 translated->KeyInput.Shift       = false;
276                                 translated->KeyInput.Char        = 0;
277
278                                 /* add this event */
279                                 translated->KeyInput.PressedDown = true;
280                                 m_receiver->OnEvent(*translated);
281
282                                 /* remove this event */
283                                 translated->KeyInput.PressedDown = false;
284                                 m_receiver->OnEvent(*translated);
285
286                                 delete translated;
287
288                                 (*iter)->ids.push_back(event.TouchInput.ID);
289
290                                 m_timeout = 0;
291
292                                 return true;
293                         }
294                         ++iter;
295                 }
296         } else {
297                 /* check for starter button only */
298                 if (element == m_starter.guibutton) {
299                         m_starter.ids.push_back(event.TouchInput.ID);
300                         m_starter.guibutton->setVisible(false);
301                         m_starter.guibutton->setEnabled(false);
302                         m_active = true;
303                         m_timeout = 0;
304
305                         std::vector<button_info*>::iterator iter = m_buttons.begin();
306
307                         while (iter != m_buttons.end()) {
308                                 (*iter)->guibutton->setVisible(true);
309                                 (*iter)->guibutton->setEnabled(true);
310                                 ++iter;
311                         }
312
313                         return true;
314                 }
315         }
316         return false;
317 }
318
319 bool AutoHideButtonBar::isReleaseButton(int eventID)
320 {
321         std::vector<int>::iterator id = std::find(m_starter.ids.begin(),
322                         m_starter.ids.end(), eventID);
323
324         if (id != m_starter.ids.end()) {
325                 m_starter.ids.erase(id);
326                 return true;
327         }
328
329         std::vector<button_info*>::iterator iter = m_buttons.begin();
330
331         while (iter != m_buttons.end()) {
332                 std::vector<int>::iterator id = std::find((*iter)->ids.begin(),
333                                 (*iter)->ids.end(), eventID);
334
335                 if (id != (*iter)->ids.end()) {
336                         (*iter)->ids.erase(id);
337                         // TODO handle settings button release
338                         return true;
339                 }
340                 ++iter;
341         }
342
343         return false;
344 }
345
346 void AutoHideButtonBar::step(float dtime)
347 {
348         if (m_active) {
349                 m_timeout += dtime;
350
351                 if (m_timeout > m_timeout_value) {
352                         deactivate();
353                 }
354         }
355 }
356
357 void AutoHideButtonBar::deactivate()
358 {
359         if (m_visible == true) {
360                 m_starter.guibutton->setVisible(true);
361                 m_starter.guibutton->setEnabled(true);
362         }
363         m_active = false;
364
365         std::vector<button_info*>::iterator iter = m_buttons.begin();
366
367         while (iter != m_buttons.end()) {
368                         (*iter)->guibutton->setVisible(false);
369                         (*iter)->guibutton->setEnabled(false);
370                 ++iter;
371         }
372 }
373
374 void AutoHideButtonBar::hide()
375 {
376         m_visible = false;
377         m_starter.guibutton->setVisible(false);
378         m_starter.guibutton->setEnabled(false);
379
380         std::vector<button_info*>::iterator iter = m_buttons.begin();
381
382         while (iter != m_buttons.end()) {
383                 (*iter)->guibutton->setVisible(false);
384                 (*iter)->guibutton->setEnabled(false);
385                 ++iter;
386         }
387 }
388
389 void AutoHideButtonBar::show()
390 {
391         m_visible = true;
392
393         if (m_active) {
394                 std::vector<button_info*>::iterator iter = m_buttons.begin();
395
396                 while (iter != m_buttons.end()) {
397                         (*iter)->guibutton->setVisible(true);
398                         (*iter)->guibutton->setEnabled(true);
399                         ++iter;
400                 }
401         } else {
402                 m_starter.guibutton->setVisible(true);
403                 m_starter.guibutton->setEnabled(true);
404         }
405 }
406
407 TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, IEventReceiver* receiver):
408         m_device(device),
409         m_guienv(device->getGUIEnvironment()),
410         m_receiver(receiver),
411         m_settingsbar(device, receiver),
412         m_rarecontrolsbar(device, receiver)
413 {
414         for (unsigned int i=0; i < after_last_element_id; i++) {
415                 m_buttons[i].guibutton     =  0;
416                 m_buttons[i].repeatcounter = -1;
417                 m_buttons[i].repeatdelay   = BUTTON_REPEAT_DELAY;
418         }
419
420         m_screensize = m_device->getVideoDriver()->getScreenSize();
421 }
422
423 void TouchScreenGUI::initButton(touch_gui_button_id id, rect<s32> button_rect,
424                 std::wstring caption, bool immediate_release, float repeat_delay)
425 {
426
427         button_info* btn       = &m_buttons[id];
428         btn->guibutton         = m_guienv->addButton(button_rect, 0, id, caption.c_str());
429         btn->guibutton->grab();
430         btn->repeatcounter     = -1;
431         btn->repeatdelay       = repeat_delay;
432         btn->keycode           = id2keycode(id);
433         btn->immediate_release = immediate_release;
434         btn->ids.clear();
435
436         load_button_texture(btn,touchgui_button_imagenames[id],button_rect,
437                         m_texturesource, m_device->getVideoDriver());
438 }
439
440 static int getMaxControlPadSize(float density) {
441         return 200 * density * g_settings->getFloat("hud_scaling");
442 }
443
444 int TouchScreenGUI::getGuiButtonSize()
445 {
446         u32 control_pad_size = MYMIN((2 * m_screensize.Y) / 3,
447                         getMaxControlPadSize(porting::getDisplayDensity()));
448
449         return control_pad_size / 3;
450 }
451
452 void TouchScreenGUI::init(ISimpleTextureSource* tsrc)
453 {
454         assert(tsrc != 0);
455
456         u32 button_size      = getGuiButtonSize();
457         m_visible            = true;
458         m_texturesource      = tsrc;
459         /*
460         draw control pad
461         0 1 2
462         3 4 5
463         for now only 0, 1, 2, and 4 are used
464         */
465         int number = 0;
466         for (int y = 0; y < 2; ++y)
467                 for (int x = 0; x < 3; ++x, ++number) {
468                         rect<s32> button_rect(
469                                         x * button_size, m_screensize.Y - button_size * (2 - y),
470                                         (x + 1) * button_size, m_screensize.Y - button_size * (1 - y)
471                         );
472                         touch_gui_button_id id = after_last_element_id;
473                         std::wstring caption;
474                         switch (number) {
475                         case 0:
476                                 id = left_id;
477                                 caption = L"<";
478                                 break;
479                         case 1:
480                                 id = forward_id;
481                                 caption = L"^";
482                                 break;
483                         case 2:
484                                 id = right_id;
485                                 caption = L">";
486                                 break;
487                         case 4:
488                                 id = backward_id;
489                                 caption = L"v";
490                                 break;
491                         }
492                         if (id != after_last_element_id) {
493                                 initButton(id, button_rect, caption, false);
494                                 }
495                 }
496
497         /* init jump button */
498         initButton(jump_id,
499                         rect<s32>(m_screensize.X-(1.75*button_size),
500                                         m_screensize.Y - (0.5*button_size),
501                                         m_screensize.X-(0.25*button_size),
502                                         m_screensize.Y),
503                         L"x",false);
504
505         /* init crunch button */
506         initButton(crunch_id,
507                         rect<s32>(m_screensize.X-(3.25*button_size),
508                                         m_screensize.Y - (0.5*button_size),
509                                         m_screensize.X-(1.75*button_size),
510                                         m_screensize.Y),
511                         L"H",false);
512
513         m_settingsbar.init(m_texturesource, "gear_icon.png", settings_starter_id,
514                         v2s32(m_screensize.X - (button_size / 2),
515                                         m_screensize.Y - ((SETTINGS_BAR_Y_OFFSET + 1) * button_size)
516                                                         + (button_size * 0.5)),
517                         v2s32(m_screensize.X,
518                                         m_screensize.Y - (SETTINGS_BAR_Y_OFFSET * button_size)
519                                                         + (button_size * 0.5)), AHBB_Dir_Right_Left,
520                         3.0);
521
522         m_settingsbar.addButton(fly_id,    L"fly",       "fly_btn.png");
523         m_settingsbar.addButton(noclip_id, L"noclip",    "noclip_btn.png");
524         m_settingsbar.addButton(fast_id,   L"fast",      "fast_btn.png");
525         m_settingsbar.addButton(debug_id,  L"debug",     "debug_btn.png");
526         m_settingsbar.addButton(camera_id, L"camera",    "camera_btn.png");
527         m_settingsbar.addButton(range_id,  L"rangeview", "rangeview_btn.png");
528
529         m_rarecontrolsbar.init(m_texturesource, "rare_controls.png",
530                         rare_controls_starter_id,
531                         v2s32(0,
532                                         m_screensize.Y
533                                                         - ((RARE_CONTROLS_BAR_Y_OFFSET + 1) * button_size)
534                                                         + (button_size * 0.5)),
535                         v2s32(button_size / 2,
536                                         m_screensize.Y - (RARE_CONTROLS_BAR_Y_OFFSET * button_size)
537                                                         + (button_size * 0.5)), AHBB_Dir_Left_Right,
538                         2);
539
540         m_rarecontrolsbar.addButton(chat_id,      L"Chat", "chat_btn.png");
541         m_rarecontrolsbar.addButton(inventory_id, L"inv",  "inventory_btn.png");
542         m_rarecontrolsbar.addButton(drop_id,      L"drop", "drop_btn.png");
543
544 }
545
546 touch_gui_button_id TouchScreenGUI::getButtonID(s32 x, s32 y)
547 {
548         IGUIElement* rootguielement = m_guienv->getRootGUIElement();
549
550         if (rootguielement != NULL) {
551                 gui::IGUIElement *element =
552                                 rootguielement->getElementFromPoint(core::position2d<s32>(x,y));
553
554                 if (element) {
555                         for (unsigned int i=0; i < after_last_element_id; i++) {
556                                 if (element == m_buttons[i].guibutton) {
557                                         return (touch_gui_button_id) i;
558                                 }
559                         }
560                 }
561         }
562         return after_last_element_id;
563 }
564
565 touch_gui_button_id TouchScreenGUI::getButtonID(int eventID)
566 {
567         for (unsigned int i=0; i < after_last_element_id; i++) {
568                 button_info* btn = &m_buttons[i];
569
570                 std::vector<int>::iterator id =
571                                 std::find(btn->ids.begin(),btn->ids.end(), eventID);
572
573                 if (id != btn->ids.end())
574                         return (touch_gui_button_id) i;
575         }
576
577         return after_last_element_id;
578 }
579
580 bool TouchScreenGUI::isHUDButton(const SEvent &event)
581 {
582         // check if hud item is pressed
583         for (std::map<int,rect<s32> >::iterator iter = m_hud_rects.begin();
584                         iter != m_hud_rects.end(); ++iter) {
585                 if (iter->second.isPointInside(
586                                 v2s32(event.TouchInput.X,
587                                                 event.TouchInput.Y)
588                         )) {
589                         if ( iter->first < 8) {
590                                 SEvent* translated = new SEvent();
591                                 memset(translated,0,sizeof(SEvent));
592                                 translated->EventType = irr::EET_KEY_INPUT_EVENT;
593                                 translated->KeyInput.Key         = (irr::EKEY_CODE) (KEY_KEY_1 + iter->first);
594                                 translated->KeyInput.Control     = false;
595                                 translated->KeyInput.Shift       = false;
596                                 translated->KeyInput.PressedDown = true;
597                                 m_receiver->OnEvent(*translated);
598                                 m_hud_ids[event.TouchInput.ID]   = translated->KeyInput.Key;
599                                 delete translated;
600                                 return true;
601                         }
602                 }
603         }
604         return false;
605 }
606
607 bool TouchScreenGUI::isReleaseHUDButton(int eventID)
608 {
609         std::map<int,irr::EKEY_CODE>::iterator iter = m_hud_ids.find(eventID);
610
611         if (iter != m_hud_ids.end()) {
612                 SEvent* translated = new SEvent();
613                 memset(translated,0,sizeof(SEvent));
614                 translated->EventType            = irr::EET_KEY_INPUT_EVENT;
615                 translated->KeyInput.Key         = iter->second;
616                 translated->KeyInput.PressedDown = false;
617                 translated->KeyInput.Control     = false;
618                 translated->KeyInput.Shift       = false;
619                 m_receiver->OnEvent(*translated);
620                 m_hud_ids.erase(iter);
621                 delete translated;
622                 return true;
623         }
624         return false;
625 }
626
627 void TouchScreenGUI::handleButtonEvent(touch_gui_button_id button,
628                 int eventID, bool action)
629 {
630         button_info* btn = &m_buttons[button];
631         SEvent* translated = new SEvent();
632         memset(translated,0,sizeof(SEvent));
633         translated->EventType            = irr::EET_KEY_INPUT_EVENT;
634         translated->KeyInput.Key         = btn->keycode;
635         translated->KeyInput.Control     = false;
636         translated->KeyInput.Shift       = false;
637         translated->KeyInput.Char        = 0;
638
639         /* add this event */
640         if (action) {
641                 assert(std::find(btn->ids.begin(),btn->ids.end(), eventID) == btn->ids.end());
642
643                 btn->ids.push_back(eventID);
644
645                 if (btn->ids.size() > 1) return;
646
647                 btn->repeatcounter = 0;
648                 translated->KeyInput.PressedDown = true;
649                 translated->KeyInput.Key = btn->keycode;
650                 m_receiver->OnEvent(*translated);
651         }
652         /* remove event */
653         if ((!action) || (btn->immediate_release)) {
654
655                 std::vector<int>::iterator pos =
656                                 std::find(btn->ids.begin(),btn->ids.end(), eventID);
657                 /* has to be in touch list */
658                 assert(pos != btn->ids.end());
659                 btn->ids.erase(pos);
660
661                 if (btn->ids.size() > 0)  { return; }
662
663                 translated->KeyInput.PressedDown = false;
664                 btn->repeatcounter               = -1;
665                 m_receiver->OnEvent(*translated);
666         }
667         delete translated;
668 }
669
670
671 void TouchScreenGUI::handleReleaseEvent(int evt_id)
672 {
673         touch_gui_button_id button = getButtonID(evt_id);
674
675         /* handle button events */
676         if (button != after_last_element_id) {
677                 handleButtonEvent(button, evt_id, false);
678         }
679         /* handle hud button events */
680         else if (isReleaseHUDButton(evt_id)) {
681                 /* nothing to do here */
682         } else if (m_settingsbar.isReleaseButton(evt_id)) {
683                 /* nothing to do here */
684         } else if (m_rarecontrolsbar.isReleaseButton(evt_id)) {
685                 /* nothing to do here */
686         }
687         /* handle the point used for moving view */
688         else if (evt_id == m_move_id) {
689                 m_move_id = -1;
690
691                 /* if this pointer issued a mouse event issue symmetric release here */
692                 if (m_move_sent_as_mouse_event) {
693                         SEvent* translated = new SEvent;
694                         memset(translated,0,sizeof(SEvent));
695                         translated->EventType               = EET_MOUSE_INPUT_EVENT;
696                         translated->MouseInput.X            = m_move_downlocation.X;
697                         translated->MouseInput.Y            = m_move_downlocation.Y;
698                         translated->MouseInput.Shift        = false;
699                         translated->MouseInput.Control      = false;
700                         translated->MouseInput.ButtonStates = 0;
701                         translated->MouseInput.Event        = EMIE_LMOUSE_LEFT_UP;
702                         m_receiver->OnEvent(*translated);
703                         delete translated;
704                 }
705                 else {
706                         /* do double tap detection */
707                         doubleTapDetection();
708                 }
709         }
710         else {
711                 infostream
712                         << "TouchScreenGUI::translateEvent released unknown button: "
713                         << evt_id << std::endl;
714         }
715
716         for (std::vector<id_status>::iterator iter = m_known_ids.begin();
717                         iter != m_known_ids.end(); ++iter) {
718                 if (iter->id == evt_id) {
719                         m_known_ids.erase(iter);
720                         break;
721                 }
722         }
723 }
724
725 void TouchScreenGUI::translateEvent(const SEvent &event)
726 {
727         if (!m_visible) {
728                 infostream << "TouchScreenGUI::translateEvent got event but not visible?!" << std::endl;
729                 return;
730         }
731
732         if (event.EventType != EET_TOUCH_INPUT_EVENT) {
733                 return;
734         }
735
736         if (event.TouchInput.Event == ETIE_PRESSED_DOWN) {
737
738                 /* add to own copy of eventlist ...
739                  * android would provide this information but irrlicht guys don't
740                  * wanna design a efficient interface
741                  */
742                 id_status toadd;
743                 toadd.id = event.TouchInput.ID;
744                 toadd.X  = event.TouchInput.X;
745                 toadd.Y  = event.TouchInput.Y;
746                 m_known_ids.push_back(toadd);
747
748                 int eventID = event.TouchInput.ID;
749
750                 touch_gui_button_id button =
751                                 getButtonID(event.TouchInput.X, event.TouchInput.Y);
752
753                 /* handle button events */
754                 if (button != after_last_element_id) {
755                         handleButtonEvent(button, eventID, true);
756                         m_settingsbar.deactivate();
757                         m_rarecontrolsbar.deactivate();
758                 } else if (isHUDButton(event)) {
759                         m_settingsbar.deactivate();
760                         m_rarecontrolsbar.deactivate();
761                         /* already handled in isHUDButton() */
762                 } else if (m_settingsbar.isButton(event)) {
763                         m_rarecontrolsbar.deactivate();
764                         /* already handled in isSettingsBarButton() */
765                 } else if (m_rarecontrolsbar.isButton(event)) {
766                         m_settingsbar.deactivate();
767                         /* already handled in isSettingsBarButton() */
768                 }
769                 /* handle non button events */
770                 else {
771                         m_settingsbar.deactivate();
772                         m_rarecontrolsbar.deactivate();
773                         /* if we don't already have a moving point make this the moving one */
774                         if (m_move_id == -1) {
775                                 m_move_id                  = event.TouchInput.ID;
776                                 m_move_has_really_moved    = false;
777                                 m_move_downtime            = porting::getTimeMs();
778                                 m_move_downlocation        = v2s32(event.TouchInput.X, event.TouchInput.Y);
779                                 m_move_sent_as_mouse_event = false;
780                         }
781                 }
782
783                 m_pointerpos[event.TouchInput.ID] = v2s32(event.TouchInput.X, event.TouchInput.Y);
784         }
785         else if (event.TouchInput.Event == ETIE_LEFT_UP) {
786                 verbosestream << "Up event for pointerid: " << event.TouchInput.ID << std::endl;
787                 handleReleaseEvent(event.TouchInput.ID);
788         }
789         else {
790                 assert(event.TouchInput.Event == ETIE_MOVED);
791                 int move_idx = event.TouchInput.ID;
792
793                 if (m_pointerpos[event.TouchInput.ID] ==
794                                 v2s32(event.TouchInput.X, event.TouchInput.Y)) {
795                         return;
796                 }
797
798                 if (m_move_id != -1) {
799                         if ((event.TouchInput.ID == m_move_id) &&
800                                 (!m_move_sent_as_mouse_event)) {
801
802                                 double distance = sqrt(
803                                                 (m_pointerpos[event.TouchInput.ID].X - event.TouchInput.X) *
804                                                 (m_pointerpos[event.TouchInput.ID].X - event.TouchInput.X) +
805                                                 (m_pointerpos[event.TouchInput.ID].Y - event.TouchInput.Y) *
806                                                 (m_pointerpos[event.TouchInput.ID].Y - event.TouchInput.Y));
807
808                                 if ((distance > g_settings->getU16("touchscreen_threshold")) ||
809                                                 (m_move_has_really_moved)) {
810                                         m_move_has_really_moved = true;
811                                         s32 X = event.TouchInput.X;
812                                         s32 Y = event.TouchInput.Y;
813
814                                         // update camera_yaw and camera_pitch
815                                         s32 dx = X - m_pointerpos[event.TouchInput.ID].X;
816                                         s32 dy = Y - m_pointerpos[event.TouchInput.ID].Y;
817
818                                         /* adapt to similar behaviour as pc screen */
819                                         double d         = g_settings->getFloat("mouse_sensitivity") *4;
820                                         double old_yaw   = m_camera_yaw_change;
821                                         double old_pitch = m_camera_pitch;
822
823                                         m_camera_yaw_change -= dx * d;
824                                         m_camera_pitch = MYMIN(MYMAX(m_camera_pitch + (dy * d), -180), 180);
825
826                                         // update shootline
827                                         m_shootline = m_device
828                                                         ->getSceneManager()
829                                                         ->getSceneCollisionManager()
830                                                         ->getRayFromScreenCoordinates(v2s32(X, Y));
831                                         m_pointerpos[event.TouchInput.ID] = v2s32(X, Y);
832                                 }
833                         }
834                         else if ((event.TouchInput.ID == m_move_id) &&
835                                         (m_move_sent_as_mouse_event)) {
836                                 m_shootline = m_device
837                                                 ->getSceneManager()
838                                                 ->getSceneCollisionManager()
839                                                 ->getRayFromScreenCoordinates(
840                                                                 v2s32(event.TouchInput.X,event.TouchInput.Y));
841                         }
842                 } else {
843                         handleChangedButton(event);
844                 }
845         }
846 }
847
848 void TouchScreenGUI::handleChangedButton(const SEvent &event)
849 {
850         for (unsigned int i = 0; i < after_last_element_id; i++) {
851
852                 if (m_buttons[i].ids.empty()) {
853                         continue;
854                 }
855                 for (std::vector<int>::iterator iter = m_buttons[i].ids.begin();
856                                 iter != m_buttons[i].ids.end(); ++iter) {
857
858                         if (event.TouchInput.ID == *iter) {
859
860                                 int current_button_id =
861                                                 getButtonID(event.TouchInput.X, event.TouchInput.Y);
862
863                                 if (current_button_id == i) {
864                                         continue;
865                                 }
866
867                                 /* remove old button */
868                                 handleButtonEvent((touch_gui_button_id) i,*iter,false);
869
870                                 if (current_button_id == after_last_element_id) {
871                                         return;
872                                 }
873                                 handleButtonEvent((touch_gui_button_id) current_button_id,*iter,true);
874                                 return;
875
876                         }
877                 }
878         }
879
880         int current_button_id = getButtonID(event.TouchInput.X, event.TouchInput.Y);
881
882         if (current_button_id == after_last_element_id) {
883                 return;
884         }
885
886         button_info* btn = &m_buttons[current_button_id];
887         if (std::find(btn->ids.begin(),btn->ids.end(), event.TouchInput.ID)
888                         == btn->ids.end())
889         {
890                 handleButtonEvent((touch_gui_button_id) current_button_id,
891                                 event.TouchInput.ID, true);
892         }
893
894 }
895
896 bool TouchScreenGUI::doubleTapDetection()
897 {
898         m_key_events[0].down_time = m_key_events[1].down_time;
899         m_key_events[0].x         = m_key_events[1].x;
900         m_key_events[0].y         = m_key_events[1].y;
901         m_key_events[1].down_time = m_move_downtime;
902         m_key_events[1].x         = m_move_downlocation.X;
903         m_key_events[1].y         = m_move_downlocation.Y;
904
905         u64 delta = porting::getDeltaMs(m_key_events[0].down_time, porting::getTimeMs());
906         if (delta > 400)
907                 return false;
908
909         double distance = sqrt(
910                         (m_key_events[0].x - m_key_events[1].x) * (m_key_events[0].x - m_key_events[1].x) +
911                         (m_key_events[0].y - m_key_events[1].y) * (m_key_events[0].y - m_key_events[1].y));
912
913
914         if (distance > (20 + g_settings->getU16("touchscreen_threshold")))
915                 return false;
916
917         SEvent* translated = new SEvent();
918         memset(translated, 0, sizeof(SEvent));
919         translated->EventType               = EET_MOUSE_INPUT_EVENT;
920         translated->MouseInput.X            = m_key_events[0].x;
921         translated->MouseInput.Y            = m_key_events[0].y;
922         translated->MouseInput.Shift        = false;
923         translated->MouseInput.Control      = false;
924         translated->MouseInput.ButtonStates = EMBSM_RIGHT;
925
926         // update shootline
927         m_shootline = m_device
928                         ->getSceneManager()
929                         ->getSceneCollisionManager()
930                         ->getRayFromScreenCoordinates(v2s32(m_key_events[0].x, m_key_events[0].y));
931
932         translated->MouseInput.Event = EMIE_RMOUSE_PRESSED_DOWN;
933         verbosestream << "TouchScreenGUI::translateEvent right click press" << std::endl;
934         m_receiver->OnEvent(*translated);
935
936         translated->MouseInput.ButtonStates = 0;
937         translated->MouseInput.Event        = EMIE_RMOUSE_LEFT_UP;
938         verbosestream << "TouchScreenGUI::translateEvent right click release" << std::endl;
939         m_receiver->OnEvent(*translated);
940         delete translated;
941         return true;
942
943 }
944
945 TouchScreenGUI::~TouchScreenGUI()
946 {
947         for (unsigned int i = 0; i < after_last_element_id; i++) {
948                 button_info* btn = &m_buttons[i];
949                 if (btn->guibutton != 0) {
950                         btn->guibutton->drop();
951                         btn->guibutton = NULL;
952                 }
953         }
954 }
955
956 void TouchScreenGUI::step(float dtime)
957 {
958         /* simulate keyboard repeats */
959         for (unsigned int i = 0; i < after_last_element_id; i++) {
960                 button_info* btn = &m_buttons[i];
961
962                 if (btn->ids.size() > 0) {
963                         btn->repeatcounter += dtime;
964
965                         /* in case we're moving around digging does not happen */
966                         if (m_move_id != -1)
967                                 m_move_has_really_moved = true;
968
969                         if (btn->repeatcounter < btn->repeatdelay) continue;
970
971                         btn->repeatcounter              = 0;
972                         SEvent translated;
973                         memset(&translated, 0, sizeof(SEvent));
974                         translated.EventType            = irr::EET_KEY_INPUT_EVENT;
975                         translated.KeyInput.Key         = btn->keycode;
976                         translated.KeyInput.PressedDown = false;
977                         m_receiver->OnEvent(translated);
978
979                         translated.KeyInput.PressedDown = true;
980                         m_receiver->OnEvent(translated);
981                 }
982         }
983
984         /* if a new placed pointer isn't moved for some time start digging */
985         if ((m_move_id != -1) &&
986                         (!m_move_has_really_moved) &&
987                         (!m_move_sent_as_mouse_event)) {
988
989                 u64 delta = porting::getDeltaMs(m_move_downtime, porting::getTimeMs());
990
991                 if (delta > MIN_DIG_TIME_MS) {
992                         m_shootline = m_device
993                                         ->getSceneManager()
994                                         ->getSceneCollisionManager()
995                                         ->getRayFromScreenCoordinates(
996                                                         v2s32(m_move_downlocation.X,m_move_downlocation.Y));
997
998                         SEvent translated;
999                         memset(&translated, 0, sizeof(SEvent));
1000                         translated.EventType               = EET_MOUSE_INPUT_EVENT;
1001                         translated.MouseInput.X            = m_move_downlocation.X;
1002                         translated.MouseInput.Y            = m_move_downlocation.Y;
1003                         translated.MouseInput.Shift        = false;
1004                         translated.MouseInput.Control      = false;
1005                         translated.MouseInput.ButtonStates = EMBSM_LEFT;
1006                         translated.MouseInput.Event        = EMIE_LMOUSE_PRESSED_DOWN;
1007                         verbosestream << "TouchScreenGUI::step left click press" << std::endl;
1008                         m_receiver->OnEvent(translated);
1009                         m_move_sent_as_mouse_event         = true;
1010                 }
1011         }
1012
1013         m_settingsbar.step(dtime);
1014         m_rarecontrolsbar.step(dtime);
1015 }
1016
1017 void TouchScreenGUI::resetHud()
1018 {
1019         m_hud_rects.clear();
1020 }
1021
1022 void TouchScreenGUI::registerHudItem(int index, const rect<s32> &rect)
1023 {
1024         m_hud_rects[index] = rect;
1025 }
1026
1027 void TouchScreenGUI::Toggle(bool visible)
1028 {
1029         m_visible = visible;
1030         for (unsigned int i = 0; i < after_last_element_id; i++) {
1031                 button_info* btn = &m_buttons[i];
1032                 if (btn->guibutton != 0) {
1033                         btn->guibutton->setVisible(visible);
1034                 }
1035         }
1036
1037         /* clear all active buttons */
1038         if (!visible) {
1039                 while (m_known_ids.size() > 0) {
1040                         handleReleaseEvent(m_known_ids.begin()->id);
1041                 }
1042
1043                 m_settingsbar.hide();
1044                 m_rarecontrolsbar.hide();
1045         } else {
1046                 m_settingsbar.show();
1047                 m_rarecontrolsbar.show();
1048         }
1049 }
1050
1051 void TouchScreenGUI::hide()
1052 {
1053         if (!m_visible)
1054                 return;
1055
1056         Toggle(false);
1057 }
1058
1059 void TouchScreenGUI::show()
1060 {
1061         if (m_visible)
1062                 return;
1063
1064         Toggle(true);
1065 }