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