Slow down the "key repeat" touch speed for some Android controls
[oweals/minetest.git] / src / touchscreengui.cpp
1 /*
2 Copyright (C) 2014 sapier
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation; either version 2.1 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #include "touchscreengui.h"
20 #include "irrlichttypes.h"
21 #include "irr_v2d.h"
22 #include "log.h"
23 #include "keycode.h"
24 #include "settings.h"
25 #include "gettime.h"
26 #include "util/numeric.h"
27 #include "porting.h"
28
29 #include <iostream>
30 #include <algorithm>
31
32 #include <ISceneCollisionManager.h>
33
34 // Very slow button repeat frequency (in seconds)
35 #define SLOW_BUTTON_REPEAT      (1.0f)
36
37 using namespace irr::core;
38
39 extern Settings *g_settings;
40
41 const char** touchgui_button_imagenames = (const char*[]) {
42         "up_arrow.png",
43         "down_arrow.png",
44         "left_arrow.png",
45         "right_arrow.png",
46         "inventory_btn.png",
47         "drop_btn.png",
48         "jump_btn.png",
49         "down.png",
50         "fly_btn.png",
51         "noclip_btn.png",
52         "fast_btn.png",
53         "debug_btn.png",
54         "chat_btn.png",
55         "camera_btn.png",
56         "rangeview_btn.png"
57 };
58
59 static irr::EKEY_CODE id2keycode(touch_gui_button_id id)
60 {
61         std::string key = "";
62         switch (id) {
63                 case forward_id:
64                         key = "forward";
65                         break;
66                 case left_id:
67                         key = "left";
68                         break;
69                 case right_id:
70                         key = "right";
71                         break;
72                 case backward_id:
73                         key = "backward";
74                         break;
75                 case inventory_id:
76                         key = "inventory";
77                         break;
78                 case drop_id:
79                         key = "drop";
80                         break;
81                 case jump_id:
82                         key = "jump";
83                         break;
84                 case crunch_id:
85                         key = "sneak";
86                         break;
87                 case fly_id:
88                         key = "freemove";
89                         break;
90                 case noclip_id:
91                         key = "noclip";
92                         break;
93                 case fast_id:
94                         key = "fastmove";
95                         break;
96                 case debug_id:
97                         key = "toggle_debug";
98                         break;
99                 case chat_id:
100                         key = "chat";
101                         break;
102                 case camera_id:
103                         key = "camera_mode";
104                         break;
105                 case range_id:
106                         key = "rangeselect";
107                         break;
108         }
109         assert(key != "");
110         return keyname_to_keycode(g_settings->get("keymap_" + key).c_str());
111 }
112
113 TouchScreenGUI *g_touchscreengui;
114
115 TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, IEventReceiver* receiver):
116         m_device(device),
117         m_guienv(device->getGUIEnvironment()),
118         m_camera_yaw(0.0),
119         m_camera_pitch(0.0),
120         m_visible(false),
121         m_move_id(-1),
122         m_receiver(receiver)
123 {
124         for (unsigned int i=0; i < after_last_element_id; i++) {
125                 m_buttons[i].guibutton     =  0;
126                 m_buttons[i].repeatcounter = -1;
127                 m_buttons[i].repeatdelay   = BUTTON_REPEAT_DELAY;
128         }
129
130         m_screensize = m_device->getVideoDriver()->getScreenSize();
131 }
132
133 void TouchScreenGUI::loadButtonTexture(button_info* btn, const char* path)
134 {
135         unsigned int tid;
136         video::ITexture *texture = m_texturesource->getTexture(path,&tid);
137         if (texture) {
138                 btn->guibutton->setUseAlphaChannel(true);
139                 btn->guibutton->setImage(texture);
140                 btn->guibutton->setPressedImage(texture);
141                 btn->guibutton->setScaleImage(true);
142                 btn->guibutton->setDrawBorder(false);
143                 btn->guibutton->setText(L"");
144                 }
145 }
146
147 void TouchScreenGUI::initButton(touch_gui_button_id id, rect<s32> button_rect,
148                 std::wstring caption, bool immediate_release, float repeat_delay)
149 {
150
151         button_info* btn       = &m_buttons[id];
152         btn->guibutton         = m_guienv->addButton(button_rect, 0, id, caption.c_str());
153         btn->guibutton->grab();
154         btn->repeatcounter     = -1;
155         btn->repeatdelay       = repeat_delay;
156         btn->keycode           = id2keycode(id);
157         btn->immediate_release = immediate_release;
158         btn->ids.clear();
159
160         loadButtonTexture(btn,touchgui_button_imagenames[id]);
161 }
162
163 static int getMaxControlPadSize(float density) {
164         return 200 * density * g_settings->getFloat("hud_scaling");
165 }
166
167 void TouchScreenGUI::init(ISimpleTextureSource* tsrc, float density)
168 {
169         assert(tsrc != 0);
170
171         u32 control_pad_size =
172                         MYMIN((2 * m_screensize.Y) / 3,getMaxControlPadSize(density));
173
174         u32 button_size      = control_pad_size / 3;
175         m_visible            = true;
176         m_texturesource      = tsrc;
177         m_control_pad_rect   = rect<s32>(0, m_screensize.Y - 3 * button_size,
178                         3 * button_size, m_screensize.Y);
179         /*
180         draw control pad
181         0 1 2
182         3 4 5
183         for now only 0, 1, 2, and 4 are used
184         */
185         int number = 0;
186         for (int y = 0; y < 2; ++y)
187                 for (int x = 0; x < 3; ++x, ++number) {
188                         rect<s32> button_rect(
189                                         x * button_size, m_screensize.Y - button_size * (2 - y),
190                                         (x + 1) * button_size, m_screensize.Y - button_size * (1 - y)
191                         );
192                         touch_gui_button_id id = after_last_element_id;
193                         std::wstring caption;
194                         switch (number) {
195                         case 0:
196                                 id = left_id;
197                                 caption = L"<";
198                                 break;
199                         case 1:
200                                 id = forward_id;
201                                 caption = L"^";
202                                 break;
203                         case 2:
204                                 id = right_id;
205                                 caption = L">";
206                                 break;
207                         case 4:
208                                 id = backward_id;
209                                 caption = L"v";
210                                 break;
211                         }
212                         if (id != after_last_element_id) {
213                                 initButton(id, button_rect, caption, false);
214                                 }
215                 }
216
217         /* init inventory button */
218         initButton(inventory_id,
219                         rect<s32>(0, m_screensize.Y - (button_size/2),
220                                         (button_size/2), m_screensize.Y), L"inv", true);
221
222         /* init drop button */
223         initButton(drop_id,
224                         rect<s32>(2.5*button_size, m_screensize.Y - (button_size/2),
225                                         3*button_size, m_screensize.Y), L"drop", true);
226
227         /* init jump button */
228         initButton(jump_id,
229                         rect<s32>(m_screensize.X-(1.75*button_size),
230                                         m_screensize.Y - (0.5*button_size),
231                                         m_screensize.X-(0.25*button_size),
232                                         m_screensize.Y),
233                         L"x",false);
234
235         /* init crunch button */
236         initButton(crunch_id,
237                         rect<s32>(m_screensize.X-(3.25*button_size),
238                                         m_screensize.Y - (0.5*button_size),
239                                         m_screensize.X-(1.75*button_size),
240                                         m_screensize.Y),
241                         L"H",false);
242
243         /* init fly button */
244         initButton(fly_id,
245                         rect<s32>(m_screensize.X - (0.75*button_size),
246                                         m_screensize.Y - (2.25*button_size),
247                                         m_screensize.X, m_screensize.Y - (button_size*1.5)),
248                         L"fly", false, SLOW_BUTTON_REPEAT);
249
250         /* init noclip button */
251         initButton(noclip_id,
252                         rect<s32>(m_screensize.X - (0.75*button_size), 2.25*button_size,
253                                         m_screensize.X, 3*button_size),
254                         L"clip", false, SLOW_BUTTON_REPEAT);
255
256         /* init fast button */
257         initButton(fast_id,
258                         rect<s32>(m_screensize.X - (0.75*button_size), 1.5*button_size,
259                                         m_screensize.X, 2.25*button_size),
260                         L"fast", false, SLOW_BUTTON_REPEAT);
261
262         /* init debug button */
263         initButton(debug_id,
264                         rect<s32>(m_screensize.X - (0.75*button_size), 0.75*button_size,
265                                         m_screensize.X, 1.5*button_size),
266                         L"dbg", false, SLOW_BUTTON_REPEAT);
267
268         /* init chat button */
269         initButton(chat_id,
270                         rect<s32>(m_screensize.X - (0.75*button_size), 0,
271                                         m_screensize.X, 0.75*button_size),
272                         L"Chat", true);
273
274         /* init camera button */
275         initButton(camera_id,
276                         rect<s32>(m_screensize.X - (1.5*button_size), 0,
277                                         m_screensize.X - (0.75*button_size), 0.75*button_size),
278                         L"cam", false, SLOW_BUTTON_REPEAT);
279
280         /* init rangeselect button */
281         initButton(range_id,
282                         rect<s32>(m_screensize.X - (2.25*button_size), 0,
283                                         m_screensize.X - (1.5*button_size), 0.75*button_size),
284                         L"far", false, SLOW_BUTTON_REPEAT);
285 }
286
287 touch_gui_button_id TouchScreenGUI::getButtonID(s32 x, s32 y)
288 {
289         IGUIElement* rootguielement = m_guienv->getRootGUIElement();
290
291         if (rootguielement != NULL) {
292                 gui::IGUIElement *element =
293                                 rootguielement->getElementFromPoint(core::position2d<s32>(x,y));
294
295                 if (element) {
296                         for (unsigned int i=0; i < after_last_element_id; i++) {
297                                 if (element == m_buttons[i].guibutton) {
298                                         return (touch_gui_button_id) i;
299                                 }
300                         }
301                 }
302         }
303         return after_last_element_id;
304 }
305
306 touch_gui_button_id TouchScreenGUI::getButtonID(int eventID)
307 {
308         for (unsigned int i=0; i < after_last_element_id; i++) {
309                 button_info* btn = &m_buttons[i];
310
311                 std::vector<int>::iterator id =
312                                 std::find(btn->ids.begin(),btn->ids.end(), eventID);
313
314                 if (id != btn->ids.end())
315                         return (touch_gui_button_id) i;
316         }
317
318         return after_last_element_id;
319 }
320
321 bool TouchScreenGUI::isHUDButton(const SEvent &event)
322 {
323         // check if hud item is pressed
324         for (std::map<int,rect<s32> >::iterator iter = m_hud_rects.begin();
325                         iter != m_hud_rects.end(); iter++) {
326                 if (iter->second.isPointInside(
327                                 v2s32(event.TouchInput.X,
328                                                 event.TouchInput.Y)
329                         )) {
330                         if ( iter->first < 8) {
331                                 SEvent* translated = new SEvent();
332                                 memset(translated,0,sizeof(SEvent));
333                                 translated->EventType = irr::EET_KEY_INPUT_EVENT;
334                                 translated->KeyInput.Key         = (irr::EKEY_CODE) (KEY_KEY_1 + iter->first);
335                                 translated->KeyInput.Control     = false;
336                                 translated->KeyInput.Shift       = false;
337                                 translated->KeyInput.PressedDown = true;
338                                 m_receiver->OnEvent(*translated);
339                                 m_hud_ids[event.TouchInput.ID]   = translated->KeyInput.Key;
340                                 delete translated;
341                                 return true;
342                         }
343                 }
344         }
345         return false;
346 }
347
348 bool TouchScreenGUI::isReleaseHUDButton(int eventID)
349 {
350         std::map<int,irr::EKEY_CODE>::iterator iter = m_hud_ids.find(eventID);
351
352         if (iter != m_hud_ids.end()) {
353                 SEvent* translated = new SEvent();
354                 memset(translated,0,sizeof(SEvent));
355                 translated->EventType            = irr::EET_KEY_INPUT_EVENT;
356                 translated->KeyInput.Key         = iter->second;
357                 translated->KeyInput.PressedDown = false;
358                 translated->KeyInput.Control     = false;
359                 translated->KeyInput.Shift       = false;
360                 m_receiver->OnEvent(*translated);
361                 m_hud_ids.erase(iter);
362                 delete translated;
363                 return true;
364         }
365         return false;
366 }
367
368 void TouchScreenGUI::ButtonEvent(touch_gui_button_id button,
369                 int eventID, bool action)
370 {
371         button_info* btn = &m_buttons[button];
372         SEvent* translated = new SEvent();
373         memset(translated,0,sizeof(SEvent));
374         translated->EventType            = irr::EET_KEY_INPUT_EVENT;
375         translated->KeyInput.Key         = btn->keycode;
376         translated->KeyInput.Control     = false;
377         translated->KeyInput.Shift       = false;
378         translated->KeyInput.Char        = 0;
379
380         /* add this event */
381         if (action) {
382                 assert(std::find(btn->ids.begin(),btn->ids.end(), eventID) == btn->ids.end());
383
384                 btn->ids.push_back(eventID);
385
386                 if (btn->ids.size() > 1) return;
387
388                 btn->repeatcounter = 0;
389                 translated->KeyInput.PressedDown = true;
390                 translated->KeyInput.Key = btn->keycode;
391                 m_receiver->OnEvent(*translated);
392         }
393         /* remove event */
394         if ((!action) || (btn->immediate_release)) {
395
396                 std::vector<int>::iterator pos =
397                                 std::find(btn->ids.begin(),btn->ids.end(), eventID);
398                 /* has to be in touch list */
399                 assert(pos != btn->ids.end());
400                 btn->ids.erase(pos);
401
402                 if (btn->ids.size() > 0)  { return; }
403
404                 translated->KeyInput.PressedDown = false;
405                 btn->repeatcounter               = -1;
406                 m_receiver->OnEvent(*translated);
407         }
408         delete translated;
409 }
410
411 void TouchScreenGUI::translateEvent(const SEvent &event)
412 {
413         if (!m_visible) {
414                 infostream << "TouchScreenGUI::translateEvent got event but not visible?!" << std::endl;
415                 return;
416         }
417
418         if (event.EventType != EET_TOUCH_INPUT_EVENT) {
419                 return;
420         }
421
422         if (event.TouchInput.Event == ETIE_PRESSED_DOWN) {
423
424                 /* add to own copy of eventlist ...
425                  * android would provide this information but irrlicht guys don't
426                  * wanna design a efficient interface
427                  */
428                 id_status toadd;
429                 toadd.id = event.TouchInput.ID;
430                 toadd.X  = event.TouchInput.X;
431                 toadd.Y  = event.TouchInput.Y;
432                 m_known_ids.push_back(toadd);
433
434                 int eventID = event.TouchInput.ID;
435
436                 touch_gui_button_id button =
437                                 getButtonID(event.TouchInput.X, event.TouchInput.Y);
438
439                 /* handle button events */
440                 if (button != after_last_element_id) {
441                         ButtonEvent(button,eventID,true);
442                 }
443                 else if (isHUDButton(event))
444                 {
445                         /* already handled in isHUDButton() */
446                 }
447                 /* handle non button events */
448                 else {
449                         /* if we don't already have a moving point make this the moving one */
450                         if (m_move_id == -1) {
451                                 m_move_id                  = event.TouchInput.ID;
452                                 m_move_has_really_moved    = false;
453                                 m_move_downtime            = getTimeMs();
454                                 m_move_downlocation        = v2s32(event.TouchInput.X, event.TouchInput.Y);
455                                 m_move_sent_as_mouse_event = false;
456                         }
457                 }
458
459                 m_pointerpos[event.TouchInput.ID] = v2s32(event.TouchInput.X, event.TouchInput.Y);
460         }
461         else if (event.TouchInput.Event == ETIE_LEFT_UP) {
462                 verbosestream << "Up event for pointerid: " << event.TouchInput.ID << std::endl;
463
464                 touch_gui_button_id button = getButtonID(event.TouchInput.ID);
465
466                 /* handle button events */
467                 if (button != after_last_element_id) {
468                         ButtonEvent(button,event.TouchInput.ID,false);
469                 }
470                 /* handle hud button events */
471                 else if (isReleaseHUDButton(event.TouchInput.ID)) {
472                         /* nothing to do here */
473                 }
474                 /* handle the point used for moving view */
475                 else if (event.TouchInput.ID == m_move_id) {
476                         m_move_id = -1;
477
478                         /* if this pointer issued a mouse event issue symmetric release here */
479                         if (m_move_sent_as_mouse_event) {
480                                 SEvent* translated = new SEvent;
481                                 memset(translated,0,sizeof(SEvent));
482                                 translated->EventType               = EET_MOUSE_INPUT_EVENT;
483                                 translated->MouseInput.X            = m_move_downlocation.X;
484                                 translated->MouseInput.Y            = m_move_downlocation.Y;
485                                 translated->MouseInput.Shift        = false;
486                                 translated->MouseInput.Control      = false;
487                                 translated->MouseInput.ButtonStates = 0;
488                                 translated->MouseInput.Event        = EMIE_LMOUSE_LEFT_UP;
489                                 m_receiver->OnEvent(*translated);
490                                 delete translated;
491                         }
492                         else {
493                                 /* do double tap detection */
494                                 doubleTapDetection();
495                         }
496                 }
497                 else {
498                         infostream
499                                 << "TouchScreenGUI::translateEvent released unknown button: "
500                                 << event.TouchInput.ID << std::endl;
501                 }
502
503                 for (std::vector<id_status>::iterator iter = m_known_ids.begin();
504                                 iter != m_known_ids.end(); iter++) {
505                         if (iter->id == event.TouchInput.ID) {
506                                 m_known_ids.erase(iter);
507                                 break;
508                         }
509                 }
510         }
511         else {
512                 assert(event.TouchInput.Event == ETIE_MOVED);
513                 int move_idx = event.TouchInput.ID;
514
515                 if (m_pointerpos[event.TouchInput.ID] ==
516                                 v2s32(event.TouchInput.X, event.TouchInput.Y)) {
517                         return;
518                 }
519
520                 if (m_move_id != -1) {
521                         if ((event.TouchInput.ID == m_move_id) &&
522                                 (!m_move_sent_as_mouse_event)) {
523
524                                 double distance = sqrt(
525                                                 (m_pointerpos[event.TouchInput.ID].X - event.TouchInput.X) *
526                                                 (m_pointerpos[event.TouchInput.ID].X - event.TouchInput.X) +
527                                                 (m_pointerpos[event.TouchInput.ID].Y - event.TouchInput.Y) *
528                                                 (m_pointerpos[event.TouchInput.ID].Y - event.TouchInput.Y));
529
530                                 if ((distance > g_settings->getU16("touchscreen_threshold")) ||
531                                                 (m_move_has_really_moved)) {
532                                         m_move_has_really_moved = true;
533                                         s32 X = event.TouchInput.X;
534                                         s32 Y = event.TouchInput.Y;
535
536                                         // update camera_yaw and camera_pitch
537                                         s32 dx = X - m_pointerpos[event.TouchInput.ID].X;
538                                         s32 dy = Y - m_pointerpos[event.TouchInput.ID].Y;
539
540                                         /* adapt to similar behaviour as pc screen */
541                                         double d         = g_settings->getFloat("mouse_sensitivity") *4;
542                                         double old_yaw   = m_camera_yaw;
543                                         double old_pitch = m_camera_pitch;
544
545                                         m_camera_yaw   -= dx * d;
546                                         m_camera_pitch  = MYMIN(MYMAX( m_camera_pitch + (dy * d),-180),180);
547
548                                         while (m_camera_yaw < 0)
549                                                 m_camera_yaw += 360;
550
551                                         while (m_camera_yaw > 360)
552                                                 m_camera_yaw -= 360;
553
554                                         // update shootline
555                                         m_shootline = m_device
556                                                         ->getSceneManager()
557                                                         ->getSceneCollisionManager()
558                                                         ->getRayFromScreenCoordinates(v2s32(X, Y));
559                                         m_pointerpos[event.TouchInput.ID] = v2s32(X, Y);
560                                 }
561                         }
562                         else if ((event.TouchInput.ID == m_move_id) &&
563                                         (m_move_sent_as_mouse_event)) {
564                                 m_shootline = m_device
565                                                 ->getSceneManager()
566                                                 ->getSceneCollisionManager()
567                                                 ->getRayFromScreenCoordinates(
568                                                                 v2s32(event.TouchInput.X,event.TouchInput.Y));
569                         }
570                 }
571                 else {
572                         handleChangedButton(event);
573                 }
574         }
575 }
576
577 void TouchScreenGUI::handleChangedButton(const SEvent &event)
578 {
579         for (unsigned int i = 0; i < after_last_element_id; i++) {
580
581                 if (m_buttons[i].ids.empty()) {
582                         continue;
583                 }
584                 for(std::vector<int>::iterator iter = m_buttons[i].ids.begin();
585                                 iter != m_buttons[i].ids.end(); iter++) {
586
587                         if (event.TouchInput.ID == *iter) {
588
589                                 int current_button_id =
590                                                 getButtonID(event.TouchInput.X, event.TouchInput.Y);
591
592                                 if (current_button_id == i) {
593                                         continue;
594                                 }
595
596                                 /* remove old button */
597                                 ButtonEvent((touch_gui_button_id) i,*iter,false);
598
599                                 if (current_button_id == after_last_element_id) {
600                                         return;
601                                 }
602                                 ButtonEvent((touch_gui_button_id) current_button_id,*iter,true);
603                                 return;
604
605                         }
606                 }
607         }
608
609         int current_button_id = getButtonID(event.TouchInput.X, event.TouchInput.Y);
610
611         if (current_button_id == after_last_element_id) {
612                 return;
613         }
614
615         button_info* btn = &m_buttons[current_button_id];
616         if (std::find(btn->ids.begin(),btn->ids.end(), event.TouchInput.ID) == btn->ids.end()) {
617                 ButtonEvent((touch_gui_button_id) current_button_id,event.TouchInput.ID,true);
618         }
619
620 }
621
622 bool TouchScreenGUI::doubleTapDetection()
623 {
624         m_key_events[0].down_time = m_key_events[1].down_time;
625         m_key_events[0].x         = m_key_events[1].x;
626         m_key_events[0].y         = m_key_events[1].y;
627         m_key_events[1].down_time = m_move_downtime;
628         m_key_events[1].x         = m_move_downlocation.X;
629         m_key_events[1].y         = m_move_downlocation.Y;
630
631         u32 delta = porting::getDeltaMs(m_key_events[0].down_time,getTimeMs());
632         if (delta > 400)
633                 return false;
634
635         double distance = sqrt(
636                         (m_key_events[0].x - m_key_events[1].x) * (m_key_events[0].x - m_key_events[1].x) +
637                         (m_key_events[0].y - m_key_events[1].y) * (m_key_events[0].y - m_key_events[1].y));
638
639
640         if (distance >(20 + g_settings->getU16("touchscreen_threshold")))
641                 return false;
642
643         SEvent* translated = new SEvent();
644         memset(translated,0,sizeof(SEvent));
645         translated->EventType               = EET_MOUSE_INPUT_EVENT;
646         translated->MouseInput.X            = m_key_events[0].x;
647         translated->MouseInput.Y            = m_key_events[0].y;
648         translated->MouseInput.Shift        = false;
649         translated->MouseInput.Control      = false;
650         translated->MouseInput.ButtonStates = EMBSM_RIGHT;
651
652         // update shootline
653         m_shootline = m_device
654                         ->getSceneManager()
655                         ->getSceneCollisionManager()
656                         ->getRayFromScreenCoordinates(v2s32(m_key_events[0].x, m_key_events[0].y));
657
658         translated->MouseInput.Event = EMIE_RMOUSE_PRESSED_DOWN;
659         verbosestream << "TouchScreenGUI::translateEvent right click press" << std::endl;
660         m_receiver->OnEvent(*translated);
661
662         translated->MouseInput.ButtonStates = 0;
663         translated->MouseInput.Event        = EMIE_RMOUSE_LEFT_UP;
664         verbosestream << "TouchScreenGUI::translateEvent right click release" << std::endl;
665         m_receiver->OnEvent(*translated);
666         delete translated;
667         return true;
668
669 }
670
671 TouchScreenGUI::~TouchScreenGUI()
672 {
673         for (unsigned int i=0; i < after_last_element_id; i++) {
674                 button_info* btn = &m_buttons[i];
675                 if (btn->guibutton != 0) {
676                         btn->guibutton->drop();
677                         btn->guibutton = NULL;
678                 }
679         }
680 }
681
682 void TouchScreenGUI::step(float dtime)
683 {
684         /* simulate keyboard repeats */
685         for (unsigned int i=0; i < after_last_element_id; i++) {
686                 button_info* btn = &m_buttons[i];
687
688                 if (btn->ids.size() > 0) {
689                         btn->repeatcounter += dtime;
690
691                         /* in case we're moving around digging does not happen */
692                         if (m_move_id != -1)
693                                 m_move_has_really_moved = true;
694
695                         if (btn->repeatcounter < btn->repeatdelay) continue;
696
697                         btn->repeatcounter              = 0;
698                         SEvent translated;
699                         memset(&translated,0,sizeof(SEvent));
700                         translated.EventType            = irr::EET_KEY_INPUT_EVENT;
701                         translated.KeyInput.Key         = btn->keycode;
702                         translated.KeyInput.PressedDown = false;
703                         m_receiver->OnEvent(translated);
704
705                         translated.KeyInput.PressedDown = true;
706                         m_receiver->OnEvent(translated);
707                 }
708         }
709
710         /* if a new placed pointer isn't moved for some time start digging */
711         if ((m_move_id != -1) &&
712                         (!m_move_has_really_moved) &&
713                         (!m_move_sent_as_mouse_event)) {
714
715                 u32 delta = porting::getDeltaMs(m_move_downtime,getTimeMs());
716
717                 if (delta > MIN_DIG_TIME_MS) {
718                         m_shootline = m_device
719                                         ->getSceneManager()
720                                         ->getSceneCollisionManager()
721                                         ->getRayFromScreenCoordinates(
722                                                         v2s32(m_move_downlocation.X,m_move_downlocation.Y));
723
724                         SEvent translated;
725                         memset(&translated,0,sizeof(SEvent));
726                         translated.EventType               = EET_MOUSE_INPUT_EVENT;
727                         translated.MouseInput.X            = m_move_downlocation.X;
728                         translated.MouseInput.Y            = m_move_downlocation.Y;
729                         translated.MouseInput.Shift        = false;
730                         translated.MouseInput.Control      = false;
731                         translated.MouseInput.ButtonStates = EMBSM_LEFT;
732                         translated.MouseInput.Event        = EMIE_LMOUSE_PRESSED_DOWN;
733                         verbosestream << "TouchScreenGUI::step left click press" << std::endl;
734                         m_receiver->OnEvent(translated);
735                         m_move_sent_as_mouse_event         = true;
736                 }
737         }
738 }
739
740 void TouchScreenGUI::resetHud()
741 {
742         m_hud_rects.clear();
743 }
744
745 void TouchScreenGUI::registerHudItem(int index, const rect<s32> &rect)
746 {
747         m_hud_rects[index] = rect;
748 }
749
750 void TouchScreenGUI::Toggle(bool visible)
751 {
752         m_visible = visible;
753         for (unsigned int i=0; i < after_last_element_id; i++) {
754                 button_info* btn = &m_buttons[i];
755                 if (btn->guibutton != 0) {
756                         btn->guibutton->setVisible(visible);
757                 }
758         }
759 }
760
761 void TouchScreenGUI::Hide()
762 {
763         Toggle(false);
764 }
765
766 void TouchScreenGUI::Show()
767 {
768         Toggle(true);
769 }