touchscreengui.cpp 34 KB

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