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