2 Copyright (C) 2014 sapier
3 Copyright (C) 2018 srifqi, Muhammad Rifqi Priyo Susanto
4 <muhammadrifqipriyosusanto@gmail.com>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "touchscreengui.h"
22 #include "irrlichttypes.h"
28 #include "util/numeric.h"
30 #include "guiscalingfilter.h"
35 #include <ISceneCollisionManager.h>
37 // Very slow button repeat frequency (in seconds)
38 #define SLOW_BUTTON_REPEAT (1.0f)
40 using namespace irr::core;
42 const char **touchgui_button_imagenames = (const char*[]) {
47 const char **touchgui_joystick_imagenames = (const char *[]) {
53 static irr::EKEY_CODE id2keycode(touch_gui_button_id id)
104 return keyname_to_keycode(g_settings->get("keymap_" + key).c_str());
107 TouchScreenGUI *g_touchscreengui;
109 static void load_button_texture(button_info* btn, const char* path,
110 rect<s32> button_rect, ISimpleTextureSource* tsrc, video::IVideoDriver *driver)
113 video::ITexture *texture = guiScalingImageButton(driver,
114 tsrc->getTexture(path, &tid), button_rect.getWidth(),
115 button_rect.getHeight());
117 btn->guibutton->setUseAlphaChannel(true);
118 if (g_settings->getBool("gui_scaling_filter")) {
119 rect<s32> txr_rect = rect<s32>(0, 0, button_rect.getWidth(), button_rect.getHeight());
120 btn->guibutton->setImage(texture, txr_rect);
121 btn->guibutton->setPressedImage(texture, txr_rect);
122 btn->guibutton->setScaleImage(false);
124 btn->guibutton->setImage(texture);
125 btn->guibutton->setPressedImage(texture);
126 btn->guibutton->setScaleImage(true);
128 btn->guibutton->setDrawBorder(false);
129 btn->guibutton->setText(L"");
133 AutoHideButtonBar::AutoHideButtonBar(IrrlichtDevice *device,
134 IEventReceiver* receiver) :
135 m_driver(device->getVideoDriver()),
136 m_guienv(device->getGUIEnvironment()),
141 void AutoHideButtonBar::init(ISimpleTextureSource* tsrc,
142 const char* starter_img, int button_id, v2s32 UpperLeft,
143 v2s32 LowerRight, autohide_button_bar_dir dir, float timeout)
145 m_texturesource = tsrc;
147 m_upper_left = UpperLeft;
148 m_lower_right = LowerRight;
152 irr::core::rect<int> current_button = rect<s32>(UpperLeft.X, UpperLeft.Y,
153 LowerRight.X, LowerRight.Y);
155 m_starter.guibutton = m_guienv->addButton(current_button, 0, button_id, L"", 0);
156 m_starter.guibutton->grab();
157 m_starter.repeatcounter = -1;
158 m_starter.keycode = KEY_OEM_8; // use invalid keycode as it's not relevant
159 m_starter.immediate_release = true;
160 m_starter.ids.clear();
162 load_button_texture(&m_starter, starter_img, current_button,
163 m_texturesource, m_driver);
166 m_timeout_value = timeout;
168 m_initialized = true;
171 AutoHideButtonBar::~AutoHideButtonBar()
173 if (m_starter.guibutton) {
174 m_starter.guibutton->setVisible(false);
175 m_starter.guibutton->drop();
179 void AutoHideButtonBar::addButton(touch_gui_button_id button_id,
180 const wchar_t* caption, const char* btn_image)
183 if (!m_initialized) {
184 errorstream << "AutoHideButtonBar::addButton not yet initialized!"
190 if ((m_dir == AHBB_Dir_Top_Bottom) || (m_dir == AHBB_Dir_Bottom_Top)) {
191 button_size = m_lower_right.X - m_upper_left.X;
193 button_size = m_lower_right.Y - m_upper_left.Y;
196 irr::core::rect<int> current_button;
198 if ((m_dir == AHBB_Dir_Right_Left) || (m_dir == AHBB_Dir_Left_Right)) {
203 if (m_dir == AHBB_Dir_Left_Right) {
204 x_start = m_lower_right.X + (button_size * 1.25 * m_buttons.size())
205 + (button_size * 0.25);
206 x_end = x_start + button_size;
208 x_end = m_upper_left.X - (button_size * 1.25 * m_buttons.size())
209 - (button_size * 0.25);
210 x_start = x_end - button_size;
213 current_button = rect<s32>(x_start, m_upper_left.Y, x_end,
219 if (m_dir == AHBB_Dir_Top_Bottom) {
220 y_start = m_lower_right.X + (button_size * 1.25 * m_buttons.size())
221 + (button_size * 0.25);
222 y_end = y_start + button_size;
224 y_end = m_upper_left.X - (button_size * 1.25 * m_buttons.size())
225 - (button_size * 0.25);
226 y_start = y_end - button_size;
229 current_button = rect<s32>(m_upper_left.X, y_start, m_lower_right.Y,
233 button_info* btn = new button_info();
234 btn->guibutton = m_guienv->addButton(current_button, 0, button_id, caption, 0);
235 btn->guibutton->grab();
236 btn->guibutton->setVisible(false);
237 btn->guibutton->setEnabled(false);
238 btn->repeatcounter = -1;
239 btn->keycode = id2keycode(button_id);
240 btn->immediate_release = true;
243 load_button_texture(btn, btn_image, current_button, m_texturesource,
246 m_buttons.push_back(btn);
249 bool AutoHideButtonBar::isButton(const SEvent &event)
251 IGUIElement* rootguielement = m_guienv->getRootGUIElement();
253 if (rootguielement == NULL) {
257 gui::IGUIElement *element = rootguielement->getElementFromPoint(
258 core::position2d<s32>(event.TouchInput.X, event.TouchInput.Y));
260 if (element == NULL) {
265 // check for all buttons in vector
267 std::vector<button_info*>::iterator iter = m_buttons.begin();
269 while (iter != m_buttons.end()) {
270 if ((*iter)->guibutton == element) {
272 SEvent* translated = new SEvent();
273 memset(translated, 0, sizeof(SEvent));
274 translated->EventType = irr::EET_KEY_INPUT_EVENT;
275 translated->KeyInput.Key = (*iter)->keycode;
276 translated->KeyInput.Control = false;
277 translated->KeyInput.Shift = false;
278 translated->KeyInput.Char = 0;
281 translated->KeyInput.PressedDown = true;
282 m_receiver->OnEvent(*translated);
285 translated->KeyInput.PressedDown = false;
286 m_receiver->OnEvent(*translated);
290 (*iter)->ids.push_back(event.TouchInput.ID);
299 // check for starter button only
300 if (element == m_starter.guibutton) {
301 m_starter.ids.push_back(event.TouchInput.ID);
302 m_starter.guibutton->setVisible(false);
303 m_starter.guibutton->setEnabled(false);
307 std::vector<button_info*>::iterator iter = m_buttons.begin();
309 while (iter != m_buttons.end()) {
310 (*iter)->guibutton->setVisible(true);
311 (*iter)->guibutton->setEnabled(true);
321 bool AutoHideButtonBar::isReleaseButton(int eventID)
323 std::vector<int>::iterator id = std::find(m_starter.ids.begin(),
324 m_starter.ids.end(), eventID);
326 if (id != m_starter.ids.end()) {
327 m_starter.ids.erase(id);
331 std::vector<button_info*>::iterator iter = m_buttons.begin();
333 while (iter != m_buttons.end()) {
334 std::vector<int>::iterator id = std::find((*iter)->ids.begin(),
335 (*iter)->ids.end(), eventID);
337 if (id != (*iter)->ids.end()) {
338 (*iter)->ids.erase(id);
339 // TODO handle settings button release
348 void AutoHideButtonBar::step(float dtime)
353 if (m_timeout > m_timeout_value) {
359 void AutoHideButtonBar::deactivate()
362 m_starter.guibutton->setVisible(true);
363 m_starter.guibutton->setEnabled(true);
367 std::vector<button_info*>::iterator iter = m_buttons.begin();
369 while (iter != m_buttons.end()) {
370 (*iter)->guibutton->setVisible(false);
371 (*iter)->guibutton->setEnabled(false);
376 void AutoHideButtonBar::hide()
379 m_starter.guibutton->setVisible(false);
380 m_starter.guibutton->setEnabled(false);
382 std::vector<button_info*>::iterator iter = m_buttons.begin();
384 while (iter != m_buttons.end()) {
385 (*iter)->guibutton->setVisible(false);
386 (*iter)->guibutton->setEnabled(false);
391 void AutoHideButtonBar::show()
396 std::vector<button_info*>::iterator iter = m_buttons.begin();
398 while (iter != m_buttons.end()) {
399 (*iter)->guibutton->setVisible(true);
400 (*iter)->guibutton->setEnabled(true);
404 m_starter.guibutton->setVisible(true);
405 m_starter.guibutton->setEnabled(true);
409 TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, IEventReceiver* receiver):
411 m_guienv(device->getGUIEnvironment()),
412 m_receiver(receiver),
413 m_settingsbar(device, receiver),
414 m_rarecontrolsbar(device, receiver)
416 for (unsigned int i=0; i < after_last_element_id; i++) {
417 m_buttons[i].guibutton = 0;
418 m_buttons[i].repeatcounter = -1;
419 m_buttons[i].repeatdelay = BUTTON_REPEAT_DELAY;
422 m_touchscreen_threshold = g_settings->getU16("touchscreen_threshold");
423 m_fixed_joystick = g_settings->getBool("fixed_virtual_joystick");
424 m_screensize = m_device->getVideoDriver()->getScreenSize();
427 void TouchScreenGUI::initButton(touch_gui_button_id id, rect<s32> button_rect,
428 std::wstring caption, bool immediate_release, float repeat_delay)
431 button_info* btn = &m_buttons[id];
432 btn->guibutton = m_guienv->addButton(button_rect, 0, id, caption.c_str());
433 btn->guibutton->grab();
434 btn->repeatcounter = -1;
435 btn->repeatdelay = repeat_delay;
436 btn->keycode = id2keycode(id);
437 btn->immediate_release = immediate_release;
440 load_button_texture(btn,touchgui_button_imagenames[id],button_rect,
441 m_texturesource, m_device->getVideoDriver());
444 button_info *TouchScreenGUI::initJoystickButton(touch_gui_button_id id, rect<s32> button_rect,
445 int texture_id, bool visible)
447 button_info *btn = new button_info();
448 btn->guibutton = m_guienv->addButton(button_rect, 0, id, L"O");
449 btn->guibutton->setVisible(visible);
450 btn->guibutton->grab();
453 load_button_texture(btn, touchgui_joystick_imagenames[texture_id], button_rect,
454 m_texturesource, m_device->getVideoDriver());
459 static int getMaxControlPadSize(float density) {
460 return 200 * density * g_settings->getFloat("hud_scaling");
463 int TouchScreenGUI::getGuiButtonSize()
465 u32 control_pad_size = MYMIN((2 * m_screensize.Y) / 3,
466 getMaxControlPadSize(porting::getDisplayDensity()));
468 return control_pad_size / 3;
471 void TouchScreenGUI::init(ISimpleTextureSource* tsrc)
475 u32 button_size = getGuiButtonSize();
477 m_texturesource = tsrc;
479 /* Init joystick display "button"
480 * Joystick is placed on bottom left of screen.
482 if (m_fixed_joystick) {
483 m_joystick_btn_off = initJoystickButton(joystick_off_id,
484 rect<s32>(button_size,
485 m_screensize.Y - button_size * 4,
487 m_screensize.Y - button_size), 0);
489 m_joystick_btn_off = initJoystickButton(joystick_off_id,
490 rect<s32>(button_size,
491 m_screensize.Y - button_size * 3,
493 m_screensize.Y - button_size), 0);
496 m_joystick_btn_bg = initJoystickButton(joystick_bg_id,
497 rect<s32>(button_size,
498 m_screensize.Y - button_size * 4,
500 m_screensize.Y - button_size),
503 m_joystick_btn_center = initJoystickButton(joystick_center_id,
504 rect<s32>(0, 0, button_size, button_size), 2, false);
508 rect<s32>(m_screensize.X-(1.75*button_size),
509 m_screensize.Y - (0.5*button_size),
510 m_screensize.X-(0.25*button_size),
514 // init crunch button
515 initButton(crunch_id,
516 rect<s32>(m_screensize.X-(3.25*button_size),
517 m_screensize.Y - (0.5*button_size),
518 m_screensize.X-(1.75*button_size),
522 m_settingsbar.init(m_texturesource, "gear_icon.png", settings_starter_id,
523 v2s32(m_screensize.X - (button_size / 2),
524 m_screensize.Y - ((SETTINGS_BAR_Y_OFFSET + 1) * button_size)
525 + (button_size * 0.5)),
526 v2s32(m_screensize.X,
527 m_screensize.Y - (SETTINGS_BAR_Y_OFFSET * button_size)
528 + (button_size * 0.5)), AHBB_Dir_Right_Left,
531 m_settingsbar.addButton(fly_id, L"fly", "fly_btn.png");
532 m_settingsbar.addButton(noclip_id, L"noclip", "noclip_btn.png");
533 m_settingsbar.addButton(fast_id, L"fast", "fast_btn.png");
534 m_settingsbar.addButton(debug_id, L"debug", "debug_btn.png");
535 m_settingsbar.addButton(camera_id, L"camera", "camera_btn.png");
536 m_settingsbar.addButton(range_id, L"rangeview", "rangeview_btn.png");
538 m_rarecontrolsbar.init(m_texturesource, "rare_controls.png",
539 rare_controls_starter_id,
542 - ((RARE_CONTROLS_BAR_Y_OFFSET + 1) * button_size)
543 + (button_size * 0.5)),
544 v2s32(button_size / 2,
545 m_screensize.Y - (RARE_CONTROLS_BAR_Y_OFFSET * button_size)
546 + (button_size * 0.5)), AHBB_Dir_Left_Right,
549 m_rarecontrolsbar.addButton(chat_id, L"Chat", "chat_btn.png");
550 m_rarecontrolsbar.addButton(inventory_id, L"inv", "inventory_btn.png");
551 m_rarecontrolsbar.addButton(drop_id, L"drop", "drop_btn.png");
555 touch_gui_button_id TouchScreenGUI::getButtonID(s32 x, s32 y)
557 IGUIElement* rootguielement = m_guienv->getRootGUIElement();
559 if (rootguielement != NULL) {
560 gui::IGUIElement *element =
561 rootguielement->getElementFromPoint(core::position2d<s32>(x,y));
564 for (unsigned int i=0; i < after_last_element_id; i++) {
565 if (element == m_buttons[i].guibutton) {
566 return (touch_gui_button_id) i;
571 return after_last_element_id;
574 touch_gui_button_id TouchScreenGUI::getButtonID(int eventID)
576 for (unsigned int i=0; i < after_last_element_id; i++) {
577 button_info* btn = &m_buttons[i];
579 std::vector<int>::iterator id =
580 std::find(btn->ids.begin(),btn->ids.end(), eventID);
582 if (id != btn->ids.end())
583 return (touch_gui_button_id) i;
586 return after_last_element_id;
589 bool TouchScreenGUI::isHUDButton(const SEvent &event)
591 // check if hud item is pressed
592 for (std::map<int,rect<s32> >::iterator iter = m_hud_rects.begin();
593 iter != m_hud_rects.end(); ++iter) {
594 if (iter->second.isPointInside(
595 v2s32(event.TouchInput.X,
598 if ( iter->first < 8) {
599 SEvent* translated = new SEvent();
600 memset(translated,0,sizeof(SEvent));
601 translated->EventType = irr::EET_KEY_INPUT_EVENT;
602 translated->KeyInput.Key = (irr::EKEY_CODE) (KEY_KEY_1 + iter->first);
603 translated->KeyInput.Control = false;
604 translated->KeyInput.Shift = false;
605 translated->KeyInput.PressedDown = true;
606 m_receiver->OnEvent(*translated);
607 m_hud_ids[event.TouchInput.ID] = translated->KeyInput.Key;
616 bool TouchScreenGUI::isReleaseHUDButton(int eventID)
618 std::map<int,irr::EKEY_CODE>::iterator iter = m_hud_ids.find(eventID);
620 if (iter != m_hud_ids.end()) {
621 SEvent* translated = new SEvent();
622 memset(translated,0,sizeof(SEvent));
623 translated->EventType = irr::EET_KEY_INPUT_EVENT;
624 translated->KeyInput.Key = iter->second;
625 translated->KeyInput.PressedDown = false;
626 translated->KeyInput.Control = false;
627 translated->KeyInput.Shift = false;
628 m_receiver->OnEvent(*translated);
629 m_hud_ids.erase(iter);
636 void TouchScreenGUI::handleButtonEvent(touch_gui_button_id button,
637 int eventID, bool action)
639 button_info* btn = &m_buttons[button];
640 SEvent* translated = new SEvent();
641 memset(translated,0,sizeof(SEvent));
642 translated->EventType = irr::EET_KEY_INPUT_EVENT;
643 translated->KeyInput.Key = btn->keycode;
644 translated->KeyInput.Control = false;
645 translated->KeyInput.Shift = false;
646 translated->KeyInput.Char = 0;
650 assert(std::find(btn->ids.begin(),btn->ids.end(), eventID) == btn->ids.end());
652 btn->ids.push_back(eventID);
654 if (btn->ids.size() > 1) return;
656 btn->repeatcounter = 0;
657 translated->KeyInput.PressedDown = true;
658 translated->KeyInput.Key = btn->keycode;
659 m_receiver->OnEvent(*translated);
662 if ((!action) || (btn->immediate_release)) {
664 std::vector<int>::iterator pos =
665 std::find(btn->ids.begin(),btn->ids.end(), eventID);
666 // has to be in touch list
667 assert(pos != btn->ids.end());
670 if (btn->ids.size() > 0) { return; }
672 translated->KeyInput.PressedDown = false;
673 btn->repeatcounter = -1;
674 m_receiver->OnEvent(*translated);
680 void TouchScreenGUI::handleReleaseEvent(int evt_id)
682 touch_gui_button_id button = getButtonID(evt_id);
684 // handle button events
685 if (button != after_last_element_id) {
686 handleButtonEvent(button, evt_id, false);
688 // handle hud button events
689 else if (isReleaseHUDButton(evt_id)) {
690 // nothing to do here
691 } else if (m_settingsbar.isReleaseButton(evt_id)) {
692 // nothing to do here
693 } else if (m_rarecontrolsbar.isReleaseButton(evt_id)) {
694 // nothing to do here
696 // handle the point used for moving view
697 else if (evt_id == m_move_id) {
700 // if this pointer issued a mouse event issue symmetric release here
701 if (m_move_sent_as_mouse_event) {
702 SEvent* translated = new SEvent;
703 memset(translated,0,sizeof(SEvent));
704 translated->EventType = EET_MOUSE_INPUT_EVENT;
705 translated->MouseInput.X = m_move_downlocation.X;
706 translated->MouseInput.Y = m_move_downlocation.Y;
707 translated->MouseInput.Shift = false;
708 translated->MouseInput.Control = false;
709 translated->MouseInput.ButtonStates = 0;
710 translated->MouseInput.Event = EMIE_LMOUSE_LEFT_UP;
711 m_receiver->OnEvent(*translated);
715 // do double tap detection
716 doubleTapDetection();
720 else if (evt_id == m_joystick_id) {
724 for (unsigned int i = 0; i < 4; i ++)
725 m_joystick_status[i] = false;
726 applyJoystickStatus();
728 m_joystick_btn_off->guibutton->setVisible(true);
729 m_joystick_btn_bg->guibutton->setVisible(false);
730 m_joystick_btn_center->guibutton->setVisible(false);
734 << "TouchScreenGUI::translateEvent released unknown button: "
735 << evt_id << std::endl;
738 for (std::vector<id_status>::iterator iter = m_known_ids.begin();
739 iter != m_known_ids.end(); ++iter) {
740 if (iter->id == evt_id) {
741 m_known_ids.erase(iter);
747 void TouchScreenGUI::translateEvent(const SEvent &event)
750 infostream << "TouchScreenGUI::translateEvent got event but not visible?!" << std::endl;
754 if (event.EventType != EET_TOUCH_INPUT_EVENT) {
758 if (event.TouchInput.Event == ETIE_PRESSED_DOWN) {
760 /* add to own copy of eventlist ...
761 * android would provide this information but irrlicht guys don't
762 * wanna design a efficient interface
765 toadd.id = event.TouchInput.ID;
766 toadd.X = event.TouchInput.X;
767 toadd.Y = event.TouchInput.Y;
768 m_known_ids.push_back(toadd);
770 int eventID = event.TouchInput.ID;
772 touch_gui_button_id button =
773 getButtonID(event.TouchInput.X, event.TouchInput.Y);
775 // handle button events
776 if (button != after_last_element_id) {
777 handleButtonEvent(button, eventID, true);
778 m_settingsbar.deactivate();
779 m_rarecontrolsbar.deactivate();
780 } else if (isHUDButton(event)) {
781 m_settingsbar.deactivate();
782 m_rarecontrolsbar.deactivate();
783 // already handled in isHUDButton()
784 } else if (m_settingsbar.isButton(event)) {
785 m_rarecontrolsbar.deactivate();
786 // already handled in isSettingsBarButton()
787 } else if (m_rarecontrolsbar.isButton(event)) {
788 m_settingsbar.deactivate();
789 // already handled in isSettingsBarButton()
791 // handle non button events
793 m_settingsbar.deactivate();
794 m_rarecontrolsbar.deactivate();
796 u32 button_size = getGuiButtonSize();
797 s32 dxj = event.TouchInput.X - button_size * 5 / 2;
798 s32 dyj = event.TouchInput.Y - m_screensize.Y + button_size * 5 / 2;
800 /* Select joystick when left 1/3 of screen dragged or
801 * when joystick tapped (fixed joystick position)
803 if ((m_fixed_joystick && dxj * dxj + dyj * dyj <= button_size * button_size * 1.5 * 1.5) ||
804 (!m_fixed_joystick && event.TouchInput.X < m_screensize.X / 3)) {
805 // If we don't already have a starting point for joystick make this the one.
806 if (m_joystick_id == -1) {
807 m_joystick_id = event.TouchInput.ID;
808 m_joystick_has_really_moved = false;
810 m_joystick_btn_off->guibutton->setVisible(false);
811 m_joystick_btn_bg->guibutton->setVisible(true);
812 m_joystick_btn_center->guibutton->setVisible(true);
814 // If it's a fixed joystick, don't move the joystick "button".
815 if (!m_fixed_joystick) {
816 m_joystick_btn_bg->guibutton->setRelativePosition(v2s32(
817 event.TouchInput.X - button_size * 3 / 2,
818 event.TouchInput.Y - button_size * 3 / 2));
820 m_joystick_btn_center->guibutton->setRelativePosition(v2s32(
821 event.TouchInput.X - button_size / 2,
822 event.TouchInput.Y - button_size / 2));
825 // If we don't already have a moving point make this the moving one.
826 if (m_move_id == -1) {
827 m_move_id = event.TouchInput.ID;
828 m_move_has_really_moved = false;
829 m_move_downtime = porting::getTimeMs();
830 m_move_downlocation = v2s32(event.TouchInput.X, event.TouchInput.Y);
831 m_move_sent_as_mouse_event = false;
836 m_pointerpos[event.TouchInput.ID] = v2s32(event.TouchInput.X, event.TouchInput.Y);
838 else if (event.TouchInput.Event == ETIE_LEFT_UP) {
839 verbosestream << "Up event for pointerid: " << event.TouchInput.ID << std::endl;
840 handleReleaseEvent(event.TouchInput.ID);
843 assert(event.TouchInput.Event == ETIE_MOVED);
844 int move_idx = event.TouchInput.ID;
846 if (m_pointerpos[event.TouchInput.ID] ==
847 v2s32(event.TouchInput.X, event.TouchInput.Y)) {
851 if (m_move_id != -1) {
852 if ((event.TouchInput.ID == m_move_id) &&
853 (!m_move_sent_as_mouse_event)) {
855 double distance = sqrt(
856 (m_pointerpos[event.TouchInput.ID].X - event.TouchInput.X) *
857 (m_pointerpos[event.TouchInput.ID].X - event.TouchInput.X) +
858 (m_pointerpos[event.TouchInput.ID].Y - event.TouchInput.Y) *
859 (m_pointerpos[event.TouchInput.ID].Y - event.TouchInput.Y));
861 if ((distance > m_touchscreen_threshold) ||
862 (m_move_has_really_moved)) {
863 m_move_has_really_moved = true;
864 s32 X = event.TouchInput.X;
865 s32 Y = event.TouchInput.Y;
867 // update camera_yaw and camera_pitch
868 s32 dx = X - m_pointerpos[event.TouchInput.ID].X;
869 s32 dy = Y - m_pointerpos[event.TouchInput.ID].Y;
871 // adapt to similar behaviour as pc screen
872 double d = g_settings->getFloat("mouse_sensitivity") * 4;
873 double old_yaw = m_camera_yaw_change;
874 double old_pitch = m_camera_pitch;
876 m_camera_yaw_change -= dx * d;
877 m_camera_pitch = MYMIN(MYMAX(m_camera_pitch + (dy * d), -180), 180);
880 m_shootline = m_device
882 ->getSceneCollisionManager()
883 ->getRayFromScreenCoordinates(v2s32(X, Y));
884 m_pointerpos[event.TouchInput.ID] = v2s32(X, Y);
887 else if ((event.TouchInput.ID == m_move_id) &&
888 (m_move_sent_as_mouse_event)) {
889 m_shootline = m_device
891 ->getSceneCollisionManager()
892 ->getRayFromScreenCoordinates(
893 v2s32(event.TouchInput.X, event.TouchInput.Y));
897 if (m_joystick_id != -1 && event.TouchInput.ID == m_joystick_id) {
898 u32 button_size = getGuiButtonSize();
899 s32 X = event.TouchInput.X;
900 s32 Y = event.TouchInput.Y;
902 s32 dx = X - m_pointerpos[event.TouchInput.ID].X;
903 s32 dy = Y - m_pointerpos[event.TouchInput.ID].Y;
904 if (m_fixed_joystick) {
905 dx = X - button_size * 5 / 2;
906 dy = Y - m_screensize.Y + button_size * 5 / 2;
909 double distance_sq = dx * dx + dy * dy;
911 s32 dxj = event.TouchInput.X - button_size * 5 / 2;
912 s32 dyj = event.TouchInput.Y - m_screensize.Y + button_size * 5 / 2;
913 bool inside_joystick = (dxj * dxj + dyj * dyj <= button_size * button_size * 1.5 * 1.5);
915 if (m_joystick_has_really_moved ||
916 (!m_joystick_has_really_moved && inside_joystick) ||
917 (!m_fixed_joystick &&
918 distance_sq > m_touchscreen_threshold * m_touchscreen_threshold)) {
919 m_joystick_has_really_moved = true;
920 double distance = sqrt(distance_sq);
923 double angle = acos(dx / distance) * 180 / M_PI;
926 // rotate to make comparing easier
927 angle = fmod(angle + 180 + 22.5, 360);
929 // reset state before applying
930 for (unsigned int i = 0; i < 4; i ++)
931 m_joystick_status[i] = false;
933 if (distance <= m_touchscreen_threshold) {
935 } else if (angle < 45)
936 m_joystick_status[j_left] = true;
937 else if (angle < 90) {
938 m_joystick_status[j_forward] = true;
939 m_joystick_status[j_left] = true;
940 } else if (angle < 135)
941 m_joystick_status[j_forward] = true;
942 else if (angle < 180) {
943 m_joystick_status[j_forward] = true;
944 m_joystick_status[j_right] = true;
945 } else if (angle < 225)
946 m_joystick_status[j_right] = true;
947 else if (angle < 270) {
948 m_joystick_status[j_backward] = true;
949 m_joystick_status[j_right] = true;
950 } else if (angle < 315)
951 m_joystick_status[j_backward] = true;
952 else if (angle <= 360) {
953 m_joystick_status[j_backward] = true;
954 m_joystick_status[j_left] = true;
957 // move joystick "button"
958 if (distance > button_size) {
959 s32 ndx = (s32) button_size * dx / distance - (s32) button_size / 2;
960 s32 ndy = (s32) button_size * dy / distance - (s32) button_size / 2;
961 if (m_fixed_joystick) {
962 m_joystick_btn_center->guibutton->setRelativePosition(v2s32(
963 button_size * 5 / 2 + ndx,
964 m_screensize.Y - button_size * 5 / 2 + ndy));
966 m_joystick_btn_center->guibutton->setRelativePosition(v2s32(
967 m_pointerpos[event.TouchInput.ID].X + ndx,
968 m_pointerpos[event.TouchInput.ID].Y + ndy));
971 m_joystick_btn_center->guibutton->setRelativePosition(v2s32(
972 X - button_size / 2, Y - button_size / 2));
977 if (m_move_id == -1 && m_joystick_id == -1) {
978 handleChangedButton(event);
983 void TouchScreenGUI::handleChangedButton(const SEvent &event)
985 for (unsigned int i = 0; i < after_last_element_id; i++) {
987 if (m_buttons[i].ids.empty()) {
990 for (std::vector<int>::iterator iter = m_buttons[i].ids.begin();
991 iter != m_buttons[i].ids.end(); ++iter) {
993 if (event.TouchInput.ID == *iter) {
995 int current_button_id =
996 getButtonID(event.TouchInput.X, event.TouchInput.Y);
998 if (current_button_id == i) {
1002 // remove old button
1003 handleButtonEvent((touch_gui_button_id) i,*iter,false);
1005 if (current_button_id == after_last_element_id) {
1008 handleButtonEvent((touch_gui_button_id) current_button_id,*iter,true);
1015 int current_button_id = getButtonID(event.TouchInput.X, event.TouchInput.Y);
1017 if (current_button_id == after_last_element_id) {
1021 button_info* btn = &m_buttons[current_button_id];
1022 if (std::find(btn->ids.begin(),btn->ids.end(), event.TouchInput.ID)
1025 handleButtonEvent((touch_gui_button_id) current_button_id,
1026 event.TouchInput.ID, true);
1031 bool TouchScreenGUI::doubleTapDetection()
1033 m_key_events[0].down_time = m_key_events[1].down_time;
1034 m_key_events[0].x = m_key_events[1].x;
1035 m_key_events[0].y = m_key_events[1].y;
1036 m_key_events[1].down_time = m_move_downtime;
1037 m_key_events[1].x = m_move_downlocation.X;
1038 m_key_events[1].y = m_move_downlocation.Y;
1040 u64 delta = porting::getDeltaMs(m_key_events[0].down_time, porting::getTimeMs());
1044 double distance = sqrt(
1045 (m_key_events[0].x - m_key_events[1].x) * (m_key_events[0].x - m_key_events[1].x) +
1046 (m_key_events[0].y - m_key_events[1].y) * (m_key_events[0].y - m_key_events[1].y));
1049 if (distance > (20 + m_touchscreen_threshold))
1052 SEvent* translated = new SEvent();
1053 memset(translated, 0, sizeof(SEvent));
1054 translated->EventType = EET_MOUSE_INPUT_EVENT;
1055 translated->MouseInput.X = m_key_events[0].x;
1056 translated->MouseInput.Y = m_key_events[0].y;
1057 translated->MouseInput.Shift = false;
1058 translated->MouseInput.Control = false;
1059 translated->MouseInput.ButtonStates = EMBSM_RIGHT;
1062 m_shootline = m_device
1064 ->getSceneCollisionManager()
1065 ->getRayFromScreenCoordinates(v2s32(m_key_events[0].x, m_key_events[0].y));
1067 translated->MouseInput.Event = EMIE_RMOUSE_PRESSED_DOWN;
1068 verbosestream << "TouchScreenGUI::translateEvent right click press" << std::endl;
1069 m_receiver->OnEvent(*translated);
1071 translated->MouseInput.ButtonStates = 0;
1072 translated->MouseInput.Event = EMIE_RMOUSE_LEFT_UP;
1073 verbosestream << "TouchScreenGUI::translateEvent right click release" << std::endl;
1074 m_receiver->OnEvent(*translated);
1080 void TouchScreenGUI::applyJoystickStatus()
1082 for (unsigned int i = 0; i < 4; i ++) {
1083 SEvent translated{};
1084 translated.EventType = irr::EET_KEY_INPUT_EVENT;
1085 translated.KeyInput.Key = id2keycode(m_joystick_names[i]);
1086 translated.KeyInput.PressedDown = false;
1087 m_receiver->OnEvent(translated);
1089 if (m_joystick_status[i]) {
1090 translated.KeyInput.PressedDown = true;
1091 m_receiver->OnEvent(translated);
1096 TouchScreenGUI::~TouchScreenGUI()
1098 for (unsigned int i = 0; i < after_last_element_id; i++) {
1099 button_info* btn = &m_buttons[i];
1100 if (btn->guibutton) {
1101 btn->guibutton->drop();
1102 btn->guibutton = NULL;
1106 if (m_joystick_btn_off->guibutton) {
1107 m_joystick_btn_off->guibutton->drop();
1108 m_joystick_btn_off->guibutton = NULL;
1111 if (m_joystick_btn_bg->guibutton) {
1112 m_joystick_btn_bg->guibutton->drop();
1113 m_joystick_btn_bg->guibutton = NULL;
1116 if (m_joystick_btn_center->guibutton) {
1117 m_joystick_btn_center->guibutton->drop();
1118 m_joystick_btn_center->guibutton = NULL;
1122 void TouchScreenGUI::step(float dtime)
1124 // simulate keyboard repeats
1125 for (unsigned int i = 0; i < after_last_element_id; i++) {
1126 button_info* btn = &m_buttons[i];
1128 if (btn->ids.size() > 0) {
1129 btn->repeatcounter += dtime;
1131 // in case we're moving around digging does not happen
1132 if (m_move_id != -1)
1133 m_move_has_really_moved = true;
1135 if (btn->repeatcounter < btn->repeatdelay) continue;
1137 btn->repeatcounter = 0;
1139 memset(&translated, 0, sizeof(SEvent));
1140 translated.EventType = irr::EET_KEY_INPUT_EVENT;
1141 translated.KeyInput.Key = btn->keycode;
1142 translated.KeyInput.PressedDown = false;
1143 m_receiver->OnEvent(translated);
1145 translated.KeyInput.PressedDown = true;
1146 m_receiver->OnEvent(translated);
1151 applyJoystickStatus();
1153 // if a new placed pointer isn't moved for some time start digging
1154 if ((m_move_id != -1) &&
1155 (!m_move_has_really_moved) &&
1156 (!m_move_sent_as_mouse_event)) {
1158 u64 delta = porting::getDeltaMs(m_move_downtime, porting::getTimeMs());
1160 if (delta > MIN_DIG_TIME_MS) {
1161 m_shootline = m_device
1163 ->getSceneCollisionManager()
1164 ->getRayFromScreenCoordinates(
1165 v2s32(m_move_downlocation.X,m_move_downlocation.Y));
1168 memset(&translated, 0, sizeof(SEvent));
1169 translated.EventType = EET_MOUSE_INPUT_EVENT;
1170 translated.MouseInput.X = m_move_downlocation.X;
1171 translated.MouseInput.Y = m_move_downlocation.Y;
1172 translated.MouseInput.Shift = false;
1173 translated.MouseInput.Control = false;
1174 translated.MouseInput.ButtonStates = EMBSM_LEFT;
1175 translated.MouseInput.Event = EMIE_LMOUSE_PRESSED_DOWN;
1176 verbosestream << "TouchScreenGUI::step left click press" << std::endl;
1177 m_receiver->OnEvent(translated);
1178 m_move_sent_as_mouse_event = true;
1182 m_settingsbar.step(dtime);
1183 m_rarecontrolsbar.step(dtime);
1186 void TouchScreenGUI::resetHud()
1188 m_hud_rects.clear();
1191 void TouchScreenGUI::registerHudItem(int index, const rect<s32> &rect)
1193 m_hud_rects[index] = rect;
1196 void TouchScreenGUI::Toggle(bool visible)
1198 m_visible = visible;
1199 for (unsigned int i = 0; i < after_last_element_id; i++) {
1200 button_info* btn = &m_buttons[i];
1201 if (btn->guibutton) {
1202 btn->guibutton->setVisible(visible);
1206 if (m_joystick_btn_off->guibutton) {
1207 m_joystick_btn_off->guibutton->setVisible(visible);
1210 // clear all active buttons
1212 while (m_known_ids.size() > 0) {
1213 handleReleaseEvent(m_known_ids.begin()->id);
1216 m_settingsbar.hide();
1217 m_rarecontrolsbar.hide();
1219 m_settingsbar.show();
1220 m_rarecontrolsbar.show();
1224 void TouchScreenGUI::hide()
1232 void TouchScreenGUI::show()