diff --git a/builtin/fstk/buttonbar.lua b/builtin/fstk/buttonbar.lua index c7da41280122..d750ae64a230 100644 --- a/builtin/fstk/buttonbar.lua +++ b/builtin/fstk/buttonbar.lua @@ -19,7 +19,7 @@ local BASE_SPACING = 0.1 local function get_scroll_btn_width() - return core.settings:get_bool("enable_touch") and 0.8 or 0.5 + return core.settings:get_bool("touch_gui") and 0.8 or 0.5 end local function buttonbar_formspec(self) diff --git a/builtin/mainmenu/content/dlg_contentdb.lua b/builtin/mainmenu/content/dlg_contentdb.lua index a975e13266fa..323ac1e978e8 100644 --- a/builtin/mainmenu/content/dlg_contentdb.lua +++ b/builtin/mainmenu/content/dlg_contentdb.lua @@ -190,7 +190,7 @@ local function get_info_formspec(text) return table.concat({ "formspec_version[6]", "size[15.75,9.5]", - core.settings:get_bool("enable_touch") and "padding[0.01,0.01]" or "position[0.5,0.55]", + core.settings:get_bool("touch_gui") and "padding[0.01,0.01]" or "position[0.5,0.55]", "label[4,4.35;", text, "]", "container[0,", H - 0.8 - 0.375, "]", @@ -221,7 +221,7 @@ local function get_formspec(dlgdata) local formspec = { "formspec_version[6]", "size[15.75,9.5]", - core.settings:get_bool("enable_touch") and "padding[0.01,0.01]" or "position[0.5,0.55]", + core.settings:get_bool("touch_gui") and "padding[0.01,0.01]" or "position[0.5,0.55]", "style[status,downloading,queued;border=false]", @@ -472,7 +472,7 @@ end local function handle_events(event) if event == "DialogShow" then -- On touchscreen, don't show the "MINETEST" header behind the dialog. - mm_game_theme.set_engine(core.settings:get_bool("enable_touch")) + mm_game_theme.set_engine(core.settings:get_bool("touch_gui")) -- If ContentDB is already loaded, auto-install packages here. do_auto_install() diff --git a/builtin/mainmenu/content/dlg_install.lua b/builtin/mainmenu/content/dlg_install.lua index 11cb0674ba2d..8ea83bf16cd5 100644 --- a/builtin/mainmenu/content/dlg_install.lua +++ b/builtin/mainmenu/content/dlg_install.lua @@ -66,7 +66,7 @@ local function get_formspec(data) message_bg = mt_color_orange end - local ENABLE_TOUCH = core.settings:get_bool("enable_touch") + local ENABLE_TOUCH = core.settings:get_bool("touch_gui") local w = ENABLE_TOUCH and 14 or 7 local padded_w = w - 2*0.375 diff --git a/builtin/mainmenu/settings/dlg_settings.lua b/builtin/mainmenu/settings/dlg_settings.lua index fe1e1924f621..44a87a1fc4fc 100644 --- a/builtin/mainmenu/settings/dlg_settings.lua +++ b/builtin/mainmenu/settings/dlg_settings.lua @@ -49,7 +49,7 @@ end local change_keys = { query_text = "Controls", requires = { - keyboard_mouse = true, + touch_controls = false, }, get_formspec = function(self, avail_w) local btn_w = math.min(avail_w, 3) @@ -313,11 +313,14 @@ local function check_requirements(name, requires) local video_driver = core.get_active_driver() local shaders_support = video_driver == "opengl" or video_driver == "opengl3" or video_driver == "ogles2" + local touch_controls = core.settings:get("touch_controls") local special = { android = PLATFORM == "Android", desktop = PLATFORM ~= "Android", - touchscreen_gui = core.settings:get_bool("enable_touch"), - keyboard_mouse = not core.settings:get_bool("enable_touch"), + -- When touch_controls is "auto", we don't which input method will be used, + -- so we show settings for both. + touchscreen = touch_controls == "auto" or core.is_yes(touch_controls), + keyboard_mouse = touch_controls == "auto" or not core.is_yes(touch_controls), shaders_support = shaders_support, shaders = core.settings:get_bool("enable_shaders") and shaders_support, opengl = video_driver == "opengl", @@ -449,13 +452,13 @@ local function get_formspec(dialogdata) local extra_h = 1 -- not included in tabsize.height local tabsize = { - width = core.settings:get_bool("enable_touch") and 16.5 or 15.5, - height = core.settings:get_bool("enable_touch") and (10 - extra_h) or 12, + width = core.settings:get_bool("touch_gui") and 16.5 or 15.5, + height = core.settings:get_bool("touch_gui") and (10 - extra_h) or 12, } - local scrollbar_w = core.settings:get_bool("enable_touch") and 0.6 or 0.4 + local scrollbar_w = core.settings:get_bool("touch_gui") and 0.6 or 0.4 - local left_pane_width = core.settings:get_bool("enable_touch") and 4.5 or 4.25 + local left_pane_width = core.settings:get_bool("touch_gui") and 4.5 or 4.25 local left_pane_padding = 0.25 local search_width = left_pane_width + scrollbar_w - (0.75 * 2) @@ -469,7 +472,7 @@ local function get_formspec(dialogdata) local fs = { "formspec_version[6]", "size[", tostring(tabsize.width), ",", tostring(tabsize.height + extra_h), "]", - core.settings:get_bool("enable_touch") and "padding[0.01,0.01]" or "", + core.settings:get_bool("touch_gui") and "padding[0.01,0.01]" or "", "bgcolor[#0000]", -- HACK: this is needed to allow resubmitting the same formspec @@ -618,6 +621,18 @@ function write_settings_early() end end +local function regenerate_page_list(dialogdata) + local suggested_page_id = update_filtered_pages(dialogdata.query) + + dialogdata.components = nil + + if not filtered_page_by_id[dialogdata.page_id] then + dialogdata.leftscroll = 0 + dialogdata.rightscroll = 0 + + dialogdata.page_id = suggested_page_id + end +end local function buttonhandler(this, fields) local dialogdata = this.data @@ -642,27 +657,7 @@ local function buttonhandler(this, fields) local value = core.is_yes(fields.show_advanced) core.settings:set_bool("show_advanced", value) write_settings_early() - end - - -- enable_touch is a checkbox in a setting component. We handle this - -- setting differently so we can hide/show pages using the next if-statement - if fields.enable_touch ~= nil then - local value = core.is_yes(fields.enable_touch) - core.settings:set_bool("enable_touch", value) - write_settings_early() - end - - if fields.show_advanced ~= nil or fields.enable_touch ~= nil then - local suggested_page_id = update_filtered_pages(dialogdata.query) - - dialogdata.components = nil - - if not filtered_page_by_id[dialogdata.page_id] then - dialogdata.leftscroll = 0 - dialogdata.rightscroll = 0 - - dialogdata.page_id = suggested_page_id - end + regenerate_page_list(dialogdata) return true end @@ -695,20 +690,26 @@ local function buttonhandler(this, fields) end end - for i, comp in ipairs(dialogdata.components) do - if comp.on_submit and comp:on_submit(fields, this) then - write_settings_early() - + local function after_setting_change(comp) + write_settings_early() + if comp.setting.name == "touch_controls" then + -- Changing the "touch_controls" setting may result in a different + -- page list. + regenerate_page_list(dialogdata) + else -- Clear components so they regenerate dialogdata.components = nil + end + end + + for i, comp in ipairs(dialogdata.components) do + if comp.on_submit and comp:on_submit(fields, this) then + after_setting_change(comp) return true end if comp.setting and fields["reset_" .. i] then core.settings:remove(comp.setting.name) - write_settings_early() - - -- Clear components so they regenerate - dialogdata.components = nil + after_setting_change(comp) return true end end diff --git a/builtin/mainmenu/tab_local.lua b/builtin/mainmenu/tab_local.lua index 1ed08d825db7..7f46be213d4f 100644 --- a/builtin/mainmenu/tab_local.lua +++ b/builtin/mainmenu/tab_local.lua @@ -94,7 +94,7 @@ function singleplayer_refresh_gamebar() local btnbar = buttonbar_create( "game_button_bar", - core.settings:get_bool("enable_touch") and {x = 0, y = 7.25} or {x = 0, y = 7.475}, + core.settings:get_bool("touch_gui") and {x = 0, y = 7.25} or {x = 0, y = 7.475}, {x = 15.5, y = 1.25}, "#000000", game_buttonbar_button_handler) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index aca960590338..1931c8097462 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -61,7 +61,7 @@ # # # This is a comment # # -# # Requires: shaders, enable_dynamic_shadows, !touchscreen_gui +# # Requires: shaders, enable_dynamic_shadows, !enable_waving_leaves # name (Readable name) type type_args # # A requirement can be the name of a boolean setting or an engine-defined value. @@ -72,7 +72,7 @@ # * shaders_support (a video driver that supports shaders, may not be enabled) # * shaders (both enable_shaders and shaders_support) # * desktop / android -# * touchscreen_gui / keyboard_mouse +# * touchscreen / keyboard_mouse # * opengl / gles # * You can negate any requirement by prepending with ! # @@ -152,40 +152,42 @@ invert_hotbar_mouse_wheel (Hotbar: Invert mouse wheel direction) bool false [*Touchscreen] -# Enables touchscreen mode, allowing you to play the game with a touchscreen. -enable_touch (Enable touchscreen) bool true +# Enables the touchscreen controls, allowing you to play the game with a touchscreen. +# "auto" means that the touchscreen controls will be enabled and disabled +# automatically depending on the last used input method. +touch_controls (Enable touchscreen controls) enum auto auto,true,false # Touchscreen sensitivity multiplier. # -# Requires: touchscreen_gui +# Requires: touchscreen touchscreen_sensitivity (Touchscreen sensitivity) float 0.2 0.001 10.0 # The length in pixels after which a touch interaction is considered movement. # -# Requires: touchscreen_gui +# Requires: touchscreen touchscreen_threshold (Movement threshold) int 20 0 100 # The delay in milliseconds after which a touch interaction is considered a long tap. # -# Requires: touchscreen_gui +# Requires: touchscreen touch_long_tap_delay (Threshold for long taps) int 400 100 1000 # Use crosshair to select object instead of whole screen. # If enabled, a crosshair will be shown and will be used for selecting object. # -# Requires: touchscreen_gui +# Requires: touchscreen touch_use_crosshair (Use crosshair for touch screen) bool false # Fixes the position of virtual joystick. # If disabled, virtual joystick will center to first-touch's position. # -# Requires: touchscreen_gui +# Requires: touchscreen fixed_virtual_joystick (Fixed virtual joystick) bool false # Use virtual joystick to trigger "Aux1" button. # If enabled, virtual joystick will also tap "Aux1" button when out of main circle. # -# Requires: touchscreen_gui +# Requires: touchscreen virtual_joystick_triggers_aux1 (Virtual joystick triggers Aux1 button) bool false # The gesture for for punching players/entities. @@ -198,7 +200,7 @@ virtual_joystick_triggers_aux1 (Virtual joystick triggers Aux1 button) bool fals # Known from the classic Minetest mobile controls. # Combat is more or less impossible. # -# Requires: touchscreen_gui +# Requires: touchscreen touch_punch_gesture (Punch gesture) enum short_tap short_tap,long_tap @@ -685,6 +687,10 @@ language (Language) enum ,be,bg,ca,cs,da,de,el,en,eo,es,et,eu,fi,fr,gd,gl,hu,i [**GUI] +# When enabled, the GUI is optimized to be more usable on touchscreens. +# Whether this is enabled by default depends on your hardware form-factor. +touch_gui (Optimize GUI for touchscreens) bool false + # Scale GUI by a user specified value. # Use a nearest-neighbor-anti-alias filter to scale the GUI. # This will smooth over some of the rough edges, and blend diff --git a/irr/include/IEventReceiver.h b/irr/include/IEventReceiver.h index cf7dee3abf6c..b8adb691c5ca 100644 --- a/irr/include/IEventReceiver.h +++ b/irr/include/IEventReceiver.h @@ -344,6 +344,9 @@ struct SEvent //! Type of mouse event EMOUSE_INPUT_EVENT Event; + + //! Is this a simulated mouse event generated by Minetest itself? + bool Simulated; }; //! Any kind of keyboard event. diff --git a/irr/src/CIrrDeviceLinux.cpp b/irr/src/CIrrDeviceLinux.cpp index 71eb0f2604cd..e50ad7c9050a 100644 --- a/irr/src/CIrrDeviceLinux.cpp +++ b/irr/src/CIrrDeviceLinux.cpp @@ -773,6 +773,7 @@ bool CIrrDeviceLinux::run() case MotionNotify: irrevent.EventType = irr::EET_MOUSE_INPUT_EVENT; + irrevent.MouseInput.Simulated = false; irrevent.MouseInput.Event = irr::EMIE_MOUSE_MOVED; irrevent.MouseInput.X = event.xbutton.x; irrevent.MouseInput.Y = event.xbutton.y; @@ -791,6 +792,7 @@ bool CIrrDeviceLinux::run() case ButtonRelease: irrevent.EventType = irr::EET_MOUSE_INPUT_EVENT; + irrevent.MouseInput.Simulated = false; irrevent.MouseInput.X = event.xbutton.x; irrevent.MouseInput.Y = event.xbutton.y; irrevent.MouseInput.Control = (event.xkey.state & ControlMask) != 0; diff --git a/irr/src/CIrrDeviceOSX.mm b/irr/src/CIrrDeviceOSX.mm index 67c0ce05ce0a..72ff15ebe3cb 100644 --- a/irr/src/CIrrDeviceOSX.mm +++ b/irr/src/CIrrDeviceOSX.mm @@ -788,6 +788,7 @@ - (BOOL)isQuit case NSLeftMouseDown: ievent.EventType = irr::EET_MOUSE_INPUT_EVENT; + ievent.MouseInput.Simulated = false; ievent.MouseInput.Event = irr::EMIE_LMOUSE_PRESSED_DOWN; MouseButtonStates |= irr::EMBSM_LEFT; ievent.MouseInput.ButtonStates = MouseButtonStates; @@ -796,6 +797,7 @@ - (BOOL)isQuit case NSLeftMouseUp: ievent.EventType = irr::EET_MOUSE_INPUT_EVENT; + ievent.MouseInput.Simulated = false; MouseButtonStates &= !irr::EMBSM_LEFT; ievent.MouseInput.ButtonStates = MouseButtonStates; ievent.MouseInput.Event = irr::EMIE_LMOUSE_LEFT_UP; @@ -804,6 +806,7 @@ - (BOOL)isQuit case NSOtherMouseDown: ievent.EventType = irr::EET_MOUSE_INPUT_EVENT; + ievent.MouseInput.Simulated = false; ievent.MouseInput.Event = irr::EMIE_MMOUSE_PRESSED_DOWN; MouseButtonStates |= irr::EMBSM_MIDDLE; ievent.MouseInput.ButtonStates = MouseButtonStates; @@ -812,6 +815,7 @@ - (BOOL)isQuit case NSOtherMouseUp: ievent.EventType = irr::EET_MOUSE_INPUT_EVENT; + ievent.MouseInput.Simulated = false; MouseButtonStates &= !irr::EMBSM_MIDDLE; ievent.MouseInput.ButtonStates = MouseButtonStates; ievent.MouseInput.Event = irr::EMIE_MMOUSE_LEFT_UP; @@ -823,6 +827,7 @@ - (BOOL)isQuit case NSRightMouseDragged: case NSOtherMouseDragged: ievent.EventType = irr::EET_MOUSE_INPUT_EVENT; + ievent.MouseInput.Simulated = false; ievent.MouseInput.Event = irr::EMIE_MOUSE_MOVED; ievent.MouseInput.ButtonStates = MouseButtonStates; postMouseEvent(event, ievent); @@ -830,6 +835,7 @@ - (BOOL)isQuit case NSRightMouseDown: ievent.EventType = irr::EET_MOUSE_INPUT_EVENT; + ievent.MouseInput.Simulated = false; ievent.MouseInput.Event = irr::EMIE_RMOUSE_PRESSED_DOWN; MouseButtonStates |= irr::EMBSM_RIGHT; ievent.MouseInput.ButtonStates = MouseButtonStates; @@ -838,6 +844,7 @@ - (BOOL)isQuit case NSRightMouseUp: ievent.EventType = irr::EET_MOUSE_INPUT_EVENT; + ievent.MouseInput.Simulated = false; ievent.MouseInput.Event = irr::EMIE_RMOUSE_LEFT_UP; MouseButtonStates &= !irr::EMBSM_RIGHT; ievent.MouseInput.ButtonStates = MouseButtonStates; @@ -846,6 +853,7 @@ - (BOOL)isQuit case NSScrollWheel: ievent.EventType = irr::EET_MOUSE_INPUT_EVENT; + ievent.MouseInput.Simulated = false; ievent.MouseInput.Event = irr::EMIE_MOUSE_WHEEL; ievent.MouseInput.Wheel = [(NSEvent *)event deltaY]; if (ievent.MouseInput.Wheel < 1.0f) @@ -1046,6 +1054,7 @@ - (BOOL)isQuit if (curr.X != x || curr.Y != y) { irr::SEvent ievent; ievent.EventType = irr::EET_MOUSE_INPUT_EVENT; + ievent.MouseInput.Simulated = false; ievent.MouseInput.Event = irr::EMIE_MOUSE_MOVED; ievent.MouseInput.X = x; ievent.MouseInput.Y = y; diff --git a/irr/src/CIrrDeviceSDL.cpp b/irr/src/CIrrDeviceSDL.cpp index f5859372e678..435e6f8d5606 100644 --- a/irr/src/CIrrDeviceSDL.cpp +++ b/irr/src/CIrrDeviceSDL.cpp @@ -93,6 +93,7 @@ EM_BOOL CIrrDeviceSDL::MouseEnterCallback(int eventType, const EmscriptenMouseEv SEvent irrevent; irrevent.EventType = irr::EET_MOUSE_INPUT_EVENT; + irrevent.MouseInput.Simulated = false; irrevent.MouseInput.Event = irr::EMIE_MOUSE_ENTER_CANVAS; This->MouseX = irrevent.MouseInput.X = mouseEvent->canvasX; This->MouseY = irrevent.MouseInput.Y = mouseEvent->canvasY; @@ -114,6 +115,7 @@ EM_BOOL CIrrDeviceSDL::MouseLeaveCallback(int eventType, const EmscriptenMouseEv SEvent irrevent; irrevent.EventType = irr::EET_MOUSE_INPUT_EVENT; + irrevent.MouseInput.Simulated = false; irrevent.MouseInput.Event = irr::EMIE_MOUSE_LEAVE_CANVAS; This->MouseX = irrevent.MouseInput.X = mouseEvent->canvasX; This->MouseY = irrevent.MouseInput.Y = mouseEvent->canvasY; @@ -658,11 +660,19 @@ bool CIrrDeviceSDL::run() SDL_Keymod keymod = SDL_GetModState(); irrevent.EventType = irr::EET_MOUSE_INPUT_EVENT; + irrevent.MouseInput.Simulated = false; irrevent.MouseInput.Event = irr::EMIE_MOUSE_MOVED; - MouseX = irrevent.MouseInput.X = SDL_event.motion.x; - MouseY = irrevent.MouseInput.Y = SDL_event.motion.y; + + if (!SDL_GetRelativeMouseMode()) { + MouseX = irrevent.MouseInput.X = SDL_event.motion.x; + MouseY = irrevent.MouseInput.Y = SDL_event.motion.y; + } else { + MouseX = irrevent.MouseInput.X = MouseX + SDL_event.motion.xrel; + MouseY = irrevent.MouseInput.Y = MouseY + SDL_event.motion.yrel; + } MouseXRel = SDL_event.motion.xrel; MouseYRel = SDL_event.motion.yrel; + irrevent.MouseInput.ButtonStates = MouseButtonStates; irrevent.MouseInput.Shift = (keymod & KMOD_SHIFT) != 0; irrevent.MouseInput.Control = (keymod & KMOD_CTRL) != 0; @@ -674,6 +684,7 @@ bool CIrrDeviceSDL::run() SDL_Keymod keymod = SDL_GetModState(); irrevent.EventType = irr::EET_MOUSE_INPUT_EVENT; + irrevent.MouseInput.Simulated = false; irrevent.MouseInput.Event = irr::EMIE_MOUSE_WHEEL; #if SDL_VERSION_ATLEAST(2, 0, 18) irrevent.MouseInput.Wheel = SDL_event.wheel.preciseY; @@ -694,6 +705,7 @@ bool CIrrDeviceSDL::run() SDL_Keymod keymod = SDL_GetModState(); irrevent.EventType = irr::EET_MOUSE_INPUT_EVENT; + irrevent.MouseInput.Simulated = false; irrevent.MouseInput.X = SDL_event.button.x; irrevent.MouseInput.Y = SDL_event.button.y; irrevent.MouseInput.Shift = (keymod & KMOD_SHIFT) != 0; diff --git a/irr/src/CIrrDeviceSDL.h b/irr/src/CIrrDeviceSDL.h index c536a8149937..e62774890ca6 100644 --- a/irr/src/CIrrDeviceSDL.h +++ b/irr/src/CIrrDeviceSDL.h @@ -158,8 +158,11 @@ class CIrrDeviceSDL : public CIrrDeviceStub //! Sets the new position of the cursor. void setPosition(s32 x, s32 y) override { +#ifndef __ANDROID__ + // On Android, this somehow results in a camera jump when enabling + // relative mouse mode and it isn't supported anyway. SDL_WarpMouseInWindow(Device->Window, x, y); - +#endif if (SDL_GetRelativeMouseMode()) { // There won't be an event for this warp (details on libsdl-org/SDL/issues/6034) Device->MouseX = x; @@ -296,6 +299,7 @@ class CIrrDeviceSDL : public CIrrDeviceStub #endif s32 MouseX, MouseY; + // these two only continue to exist for some Emscripten stuff idk about s32 MouseXRel, MouseYRel; u32 MouseButtonStates; diff --git a/irr/src/CIrrDeviceWin32.cpp b/irr/src/CIrrDeviceWin32.cpp index c2876fccefc3..c0a9c509c634 100644 --- a/irr/src/CIrrDeviceWin32.cpp +++ b/irr/src/CIrrDeviceWin32.cpp @@ -563,6 +563,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } event.EventType = irr::EET_MOUSE_INPUT_EVENT; + event.MouseInput.Simulated = false; event.MouseInput.Event = (irr::EMOUSE_INPUT_EVENT)m->irrMessage; event.MouseInput.X = (short)LOWORD(lParam); event.MouseInput.Y = (short)HIWORD(lParam); diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index 6379fc141667..0ced9f09ebcf 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gui/mainmenumanager.h" #include "clouds.h" -#include "gui/touchscreengui.h" +#include "gui/touchcontrols.h" #include "server.h" #include "filesys.h" #include "gui/guiMainMenu.h" @@ -228,9 +228,9 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args) m_rendering_engine->get_scene_manager()->clear(); - if (g_touchscreengui) { - delete g_touchscreengui; - g_touchscreengui = NULL; + if (g_touchcontrols) { + delete g_touchcontrols; + g_touchcontrols = NULL; } /* Save the settings when leaving the game. diff --git a/src/client/game.cpp b/src/client/game.cpp index 2e27d6b0d5c5..119e0794101f 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -40,7 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content/subgames.h" #include "client/event_manager.h" #include "fontengine.h" -#include "gui/touchscreengui.h" +#include "gui/touchcontrols.h" #include "itemdef.h" #include "log.h" #include "filesys.h" @@ -725,6 +725,7 @@ class Game { void processUserInput(f32 dtime); void processKeyInput(); void processItemSelection(u16 *new_playeritem); + bool shouldShowTouchControls(); void dropSelectedItem(bool single_item = false); void openInventory(); @@ -1245,8 +1246,8 @@ void Game::shutdown() // Clear text when exiting. m_game_ui->clearText(); - if (g_touchscreengui) - g_touchscreengui->hide(); + if (g_touchcontrols) + g_touchcontrols->hide(); // only if the shutdown progress bar isn't shown yet if (m_shutdown_progress == 0.0f) @@ -1446,7 +1447,7 @@ void Game::copyServerClientCache() { // It would be possible to let the client directly read the media files // from where the server knows they are. But aside from being more complicated - // it would also *not* fill the media cache and cause slower joining of + // it would also *not* fill the media cache and cause slower joining of // remote servers. // (Imagine that you launch a game once locally and then connect to a server.) @@ -1510,8 +1511,8 @@ bool Game::createClient(const GameStartData &start_data) client->getScript()->on_camera_ready(camera); client->setCamera(camera); - if (g_touchscreengui) { - g_touchscreengui->setUseCrosshair(!isTouchCrosshairDisabled()); + if (g_touchcontrols) { + g_touchcontrols->setUseCrosshair(!isTouchCrosshairDisabled()); } /* Clouds @@ -1564,6 +1565,14 @@ bool Game::createClient(const GameStartData &start_data) return true; } +bool Game::shouldShowTouchControls() +{ + const std::string &touch_controls = g_settings->get("touch_controls"); + if (touch_controls == "auto") + return RenderingEngine::getLastPointerType() == PointerType::Touch; + return is_yes(touch_controls); +} + bool Game::initGui() { m_game_ui->init(); @@ -1578,8 +1587,8 @@ bool Game::initGui() gui_chat_console = new GUIChatConsole(guienv, guienv->getRootGUIElement(), -1, chat_backend, client, &g_menumgr); - if (g_settings->getBool("enable_touch")) - g_touchscreengui = new TouchScreenGUI(device, texture_src); + if (shouldShowTouchControls()) + g_touchcontrols = new TouchControls(device, texture_src); return true; } @@ -2008,6 +2017,15 @@ void Game::updateStats(RunStats *stats, const FpsControl &draw_times, void Game::processUserInput(f32 dtime) { + bool desired = shouldShowTouchControls(); + if (desired && !g_touchcontrols) { + g_touchcontrols = new TouchControls(device, texture_src); + + } else if (!desired && g_touchcontrols) { + delete g_touchcontrols; + g_touchcontrols = nullptr; + } + // Reset input if window not active or some menu is active if (!device->isWindowActive() || isMenuActive() || guienv->hasFocus(gui_chat_console)) { if (m_game_focused) { @@ -2018,15 +2036,15 @@ void Game::processUserInput(f32 dtime) input->clear(); } - if (g_touchscreengui) - g_touchscreengui->hide(); + if (g_touchcontrols) + g_touchcontrols->hide(); } else { - if (g_touchscreengui) { - /* on touchscreengui step may generate own input events which ain't + if (g_touchcontrols) { + /* on touchcontrols step may generate own input events which ain't * what we want in case we just did clear them */ - g_touchscreengui->show(); - g_touchscreengui->step(dtime); + g_touchcontrols->show(); + g_touchcontrols->step(dtime); } m_game_focused = true; @@ -2219,8 +2237,8 @@ void Game::processItemSelection(u16 *new_playeritem) } } - if (g_touchscreengui) { - std::optional selection = g_touchscreengui->getHotbarSelection(); + if (g_touchcontrols) { + std::optional selection = g_touchcontrols->getHotbarSelection(); if (selection) *new_playeritem = *selection; } @@ -2625,7 +2643,7 @@ void Game::updateCameraDirection(CameraOrientation *cam, float dtime) this results in duplicated input. To avoid that, we don't enable relative mouse mode if we're in touchscreen mode. */ if (cur_control) - cur_control->setRelativeMode(!g_touchscreengui && !isMenuActive()); + cur_control->setRelativeMode(!g_touchcontrols && !isMenuActive()); if ((device->isWindowActive() && device->isWindowFocused() && !isMenuActive()) || input->isRandom()) { @@ -2636,7 +2654,7 @@ void Game::updateCameraDirection(CameraOrientation *cam, float dtime) cur_control->setVisible(false); } - if (m_first_loop_after_window_activation) { + if (m_first_loop_after_window_activation && !g_touchcontrols) { m_first_loop_after_window_activation = false; input->setMousePos(driver->getScreenSize().Width / 2, @@ -2652,6 +2670,8 @@ void Game::updateCameraDirection(CameraOrientation *cam, float dtime) m_first_loop_after_window_activation = true; } + if (g_touchcontrols) + m_first_loop_after_window_activation = true; } // Get the factor to multiply with sensitivity to get the same mouse/joystick @@ -2668,9 +2688,9 @@ f32 Game::getSensitivityScaleFactor() const void Game::updateCameraOrientation(CameraOrientation *cam, float dtime) { - if (g_touchscreengui) { - cam->camera_yaw += g_touchscreengui->getYawChange(); - cam->camera_pitch += g_touchscreengui->getPitchChange(); + if (g_touchcontrols) { + cam->camera_yaw += g_touchcontrols->getYawChange(); + cam->camera_pitch += g_touchcontrols->getPitchChange(); } else { v2s32 center(driver->getScreenSize().Width / 2, driver->getScreenSize().Height / 2); v2s32 dist = input->getMousePos() - center; @@ -2735,7 +2755,7 @@ void Game::updatePlayerControl(const CameraOrientation &cam) * touch then its meaning is inverted (i.e. holding aux1 means walk and * not fast) */ - if (g_touchscreengui && m_touch_simulate_aux1) { + if (g_touchcontrols && m_touch_simulate_aux1) { control.aux1 = control.aux1 ^ true; } @@ -3224,8 +3244,8 @@ void Game::updateCamera(f32 dtime) camera->toggleCameraMode(); - if (g_touchscreengui) - g_touchscreengui->setUseCrosshair(!isTouchCrosshairDisabled()); + if (g_touchcontrols) + g_touchcontrols->setUseCrosshair(!isTouchCrosshairDisabled()); // Make the player visible depending on camera mode. playercao->updateMeshCulling(); @@ -3326,8 +3346,8 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud) } shootline.end = shootline.start + camera_direction * BS * d; - if (g_touchscreengui && isTouchCrosshairDisabled()) { - shootline = g_touchscreengui->getShootline(); + if (g_touchcontrols && isTouchCrosshairDisabled()) { + shootline = g_touchcontrols->getShootline(); // Scale shootline to the acual distance the player can reach shootline.end = shootline.start + shootline.getVector().normalize() * BS * d; @@ -3344,9 +3364,9 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud) if (pointed != runData.pointed_old) infostream << "Pointing at " << pointed.dump() << std::endl; - if (g_touchscreengui) { + if (g_touchcontrols) { auto mode = selected_def.touch_interaction.getMode(pointed.type); - g_touchscreengui->applyContextControls(mode); + g_touchcontrols->applyContextControls(mode); } // Note that updating the selection mesh every frame is not particularly efficient, @@ -4333,7 +4353,7 @@ void Game::drawScene(ProfilerGraph *graph, RunStats *stats) (player->hud_flags & HUD_FLAG_CROSSHAIR_VISIBLE) && (this->camera->getCameraMode() != CAMERA_MODE_THIRD_FRONT)); - if (g_touchscreengui && isTouchCrosshairDisabled()) + if (g_touchcontrols && isTouchCrosshairDisabled()) draw_crosshair = false; this->m_rendering_engine->draw_scene(sky_color, this->m_game_ui->m_flags.show_hud, @@ -4444,7 +4464,7 @@ void Game::showPauseMenu() { std::string control_text; - if (g_touchscreengui) { + if (g_touchcontrols) { control_text = strgettext("Controls:\n" "No menu open:\n" "- slide finger: look around\n" diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 637a2f5002f4..30a9c64caa6c 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -39,7 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "wieldmesh.h" #include "client/renderingengine.h" #include "client/minimap.h" -#include "gui/touchscreengui.h" +#include "gui/touchcontrols.h" #include "util/enriched_string.h" #include "irrlicht_changes/CGUITTFont.h" @@ -305,8 +305,8 @@ void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount, drawItem(mainlist->getItem(i), item_rect, (i + 1) == selectitem); - if (is_hotbar && g_touchscreengui) - g_touchscreengui->registerHotbarRect(i, item_rect); + if (is_hotbar && g_touchcontrols) + g_touchcontrols->registerHotbarRect(i, item_rect); } } @@ -770,8 +770,8 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, void Hud::drawHotbar(u16 playeritem) { - if (g_touchscreengui) - g_touchscreengui->resetHotbarRects(); + if (g_touchcontrols) + g_touchcontrols->resetHotbarRects(); InventoryList *mainlist = inventory->getList("main"); if (mainlist == NULL) { diff --git a/src/client/inputhandler.cpp b/src/client/inputhandler.cpp index ae994b8b5368..074130691c21 100644 --- a/src/client/inputhandler.cpp +++ b/src/client/inputhandler.cpp @@ -22,8 +22,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/numeric.h" #include "inputhandler.h" #include "gui/mainmenumanager.h" -#include "gui/touchscreengui.h" +#include "gui/touchcontrols.h" #include "hud.h" +#include "log.h" +#include "client/renderingengine.h" void KeyCache::populate_nonchanging() { @@ -126,10 +128,15 @@ bool MyEventReceiver::OnEvent(const SEvent &event) } } + if (event.EventType == EET_MOUSE_INPUT_EVENT && !event.MouseInput.Simulated) + last_pointer_type = PointerType::Mouse; + else if (event.EventType == EET_TOUCH_INPUT_EVENT) + last_pointer_type = PointerType::Touch; + // Let the menu handle events, if one is active. if (isMenuActive()) { - if (g_touchscreengui) - g_touchscreengui->setVisible(false); + if (g_touchcontrols) + g_touchcontrols->hide(); return g_menumgr.preprocessEvent(event); } @@ -153,9 +160,9 @@ bool MyEventReceiver::OnEvent(const SEvent &event) return true; } - } else if (g_touchscreengui && event.EventType == irr::EET_TOUCH_INPUT_EVENT) { - // In case of touchscreengui, we have to handle different events - g_touchscreengui->translateEvent(event); + } else if (g_touchcontrols && event.EventType == irr::EET_TOUCH_INPUT_EVENT) { + // In case of touchcontrols, we have to handle different events + g_touchcontrols->translateEvent(event); return true; } else if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) { // joystick may be nullptr if game is launched with '--random-input' parameter @@ -222,8 +229,8 @@ float RealInputHandler::getMovementSpeed() return 0.0f; return 1.0f; // If there is a keyboard event, assume maximum speed } - if (g_touchscreengui && g_touchscreengui->getMovementSpeed()) - return g_touchscreengui->getMovementSpeed(); + if (g_touchcontrols && g_touchcontrols->getMovementSpeed()) + return g_touchcontrols->getMovementSpeed(); return joystick.getMovementSpeed(); } @@ -245,11 +252,29 @@ float RealInputHandler::getMovementDirection() return std::atan2(x, z); // `getMovementDirection() == 0` means forward, so we cannot use // `getMovementDirection()` as a condition. - else if (g_touchscreengui && g_touchscreengui->getMovementSpeed()) - return g_touchscreengui->getMovementDirection(); + else if (g_touchcontrols && g_touchcontrols->getMovementSpeed()) + return g_touchcontrols->getMovementDirection(); return joystick.getMovementDirection(); } +v2s32 RealInputHandler::getMousePos() { + auto control = RenderingEngine::get_raw_device()->getCursorControl(); + if (control) { + return control->getPosition(); + } + + return m_mousepos; +} + +void RealInputHandler::setMousePos(s32 x, s32 y) { + auto control = RenderingEngine::get_raw_device()->getCursorControl(); + if (control) { + control->setPosition(x, y); + } else { + m_mousepos = v2s32(x, y); + } +} + /* * RandomInputHandler */ diff --git a/src/client/inputhandler.h b/src/client/inputhandler.h index 400503a3db20..df7ae3073081 100644 --- a/src/client/inputhandler.h +++ b/src/client/inputhandler.h @@ -23,10 +23,14 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "joystick_controller.h" #include #include "keycode.h" -#include "renderingengine.h" class InputHandler; +enum class PointerType { + Mouse, + Touch, +}; + /**************************************************************************** Fast key cache for main game loop ****************************************************************************/ @@ -199,6 +203,8 @@ class MyEventReceiver : public IEventReceiver JoystickController *joystick = nullptr; + PointerType getLastPointerType() { return last_pointer_type; } + private: s32 mouse_wheel = 0; @@ -223,6 +229,8 @@ class MyEventReceiver : public IEventReceiver // Intentionally not reset by clearInput/releaseAllKeys. bool fullscreen_is_down = false; + + PointerType last_pointer_type = PointerType::Mouse; }; class InputHandler @@ -330,25 +338,8 @@ class RealInputHandler : public InputHandler m_receiver->dontListenForKeys(); } - virtual v2s32 getMousePos() - { - auto control = RenderingEngine::get_raw_device()->getCursorControl(); - if (control) { - return control->getPosition(); - } - - return m_mousepos; - } - - virtual void setMousePos(s32 x, s32 y) - { - auto control = RenderingEngine::get_raw_device()->getCursorControl(); - if (control) { - control->setPosition(x, y); - } else { - m_mousepos = v2s32(x, y); - } - } + virtual v2s32 getMousePos(); + virtual void setMousePos(s32 x, s32 y); virtual s32 getMouseWheel() { diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp index b0033c0e23d8..9a929dc8ee5c 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -195,7 +195,7 @@ static irr::IrrlichtDevice *createDevice(SIrrlichtCreationParameters params, std /* RenderingEngine class */ -RenderingEngine::RenderingEngine(IEventReceiver *receiver) +RenderingEngine::RenderingEngine(MyEventReceiver *receiver) { sanity_check(!s_singleton); @@ -248,6 +248,8 @@ RenderingEngine::RenderingEngine(IEventReceiver *receiver) // This changes the minimum allowed number of vertices in a VBO. Default is 500. driver->setMinHardwareBufferVertexCount(4); + m_receiver = receiver; + s_singleton = this; auto skin = createSkin(m_device->getGUIEnvironment(), diff --git a/src/client/renderingengine.h b/src/client/renderingengine.h index 1a2a63513ebd..a69730d13a0e 100644 --- a/src/client/renderingengine.h +++ b/src/client/renderingengine.h @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include +#include "client/inputhandler.h" #include "irrlichttypes_extrabloated.h" #include "debug.h" #include "client/shader.h" @@ -82,7 +83,7 @@ class RenderingEngine static const video::SColor MENU_SKY_COLOR; static const float BASE_BLOOM_STRENGTH; - RenderingEngine(IEventReceiver *eventReceiver); + RenderingEngine(MyEventReceiver *eventReceiver); ~RenderingEngine(); void setResizable(bool resize); @@ -167,6 +168,12 @@ class RenderingEngine const irr::core::dimension2d initial_screen_size, const bool initial_window_maximized); + static PointerType getLastPointerType() + { + sanity_check(s_singleton && s_singleton->m_receiver); + return s_singleton->m_receiver->getLastPointerType(); + } + private: static void settingChangedCallback(const std::string &name, void *data); v2u32 _getWindowSize() const; @@ -174,5 +181,6 @@ class RenderingEngine std::unique_ptr core; irr::IrrlichtDevice *m_device = nullptr; irr::video::IVideoDriver *driver; + MyEventReceiver *m_receiver = nullptr; static RenderingEngine *s_singleton; }; diff --git a/src/clientdynamicinfo.cpp b/src/clientdynamicinfo.cpp index a9b2a6ef3fe0..c206018f3ea1 100644 --- a/src/clientdynamicinfo.cpp +++ b/src/clientdynamicinfo.cpp @@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "client/renderingengine.h" -#include "gui/touchscreengui.h" +#include "gui/touchcontrols.h" ClientDynamicInfo ClientDynamicInfo::getCurrent() { @@ -33,7 +33,7 @@ ClientDynamicInfo ClientDynamicInfo::getCurrent() f32 hud_scaling = g_settings->getFloat("hud_scaling", 0.5f, 20.0f); f32 real_gui_scaling = gui_scaling * density; f32 real_hud_scaling = hud_scaling * density; - bool touch_controls = g_touchscreengui; + bool touch_controls = g_touchcontrols; return { screen_size, real_gui_scaling, real_hud_scaling, @@ -44,7 +44,7 @@ ClientDynamicInfo ClientDynamicInfo::getCurrent() v2f32 ClientDynamicInfo::calculateMaxFSSize(v2u32 render_target_size, f32 gui_scaling) { - f32 factor = (g_settings->getBool("enable_touch") ? 10 : 15) / gui_scaling; + f32 factor = (g_settings->getBool("touch_gui") ? 10 : 15) / gui_scaling; f32 ratio = (f32)render_target_size.X / (f32)render_target_size.Y; if (ratio < 1) return { factor, factor / ratio }; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index f16c56db4180..eeca5717956f 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -104,7 +104,21 @@ void set_default_settings() // Client settings->setDefault("address", ""); settings->setDefault("enable_sound", "true"); - settings->setDefault("enable_touch", bool_to_cstr(has_touch)); +#if defined(__linux__) && !defined (__ANDROID__) + // On Linux+X11 (not Linux+Wayland or Linux+XWayland), I've encountered a bug + // where fake mouse events were generated from touch events if in relative + // mouse mode, resulting in the touchscreen controls being instantly disabled + // again and thus making them unusable. + // => We can't switch based on the last input method used. + // => Fall back to hardware detection. + settings->setDefault("touch_controls", bool_to_cstr(has_touch)); +#else + settings->setDefault("touch_controls", "auto"); +#endif + // Since GUI scaling shouldn't suddenly change during a session, we use + // hardware detection here instead of switching based on the last input + // method used. + settings->setDefault("touch_gui", bool_to_cstr(has_touch)); settings->setDefault("sound_volume", "0.8"); settings->setDefault("sound_volume_unfocused", "0.3"); settings->setDefault("mute_sound", "false"); diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 66d6a9ee8da3..73bbecb02954 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -25,6 +25,6 @@ set(gui_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/guiVolumeChange.cpp ${CMAKE_CURRENT_SOURCE_DIR}/modalMenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/profilergraph.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/touchscreengui.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/touchcontrols.cpp PARENT_SCOPE ) diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index ca9401091dc4..5b5e6da006c5 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -315,7 +315,7 @@ void GUIFormSpecMenu::parseSize(parserData* data, const std::string &element) data->invsize.Y = MYMAX(0, stof(parts[1])); lockSize(false); - if (!g_settings->getBool("enable_touch") && parts.size() == 3) { + if (!g_settings->getBool("touch_gui") && parts.size() == 3) { if (parts[2] == "true") { lockSize(true,v2u32(800,600)); } @@ -3283,7 +3283,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) s32 min_screen_dim = std::min(padded_screensize.X, padded_screensize.Y); double prefer_imgsize; - if (g_settings->getBool("enable_touch")) { + if (g_settings->getBool("touch_gui")) { // The preferred imgsize should be larger to accommodate the // smaller screensize. prefer_imgsize = min_screen_dim / 10 * gui_scaling; @@ -3770,7 +3770,7 @@ void GUIFormSpecMenu::showTooltip(const std::wstring &text, int tooltip_offset_x = m_btn_height; int tooltip_offset_y = m_btn_height; - if (m_pointer_type == PointerType::Touch) { + if (RenderingEngine::getLastPointerType() == PointerType::Touch) { tooltip_offset_x *= 3; tooltip_offset_y = 0; if (m_pointer.X > (s32)screenSize.X / 2) diff --git a/src/gui/guiInventoryList.cpp b/src/gui/guiInventoryList.cpp index 02505d436a4d..c787abb9d59f 100644 --- a/src/gui/guiInventoryList.cpp +++ b/src/gui/guiInventoryList.cpp @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiFormSpecMenu.h" #include "client/hud.h" #include "client/client.h" +#include "client/renderingengine.h" GUIInventoryList::GUIInventoryList(gui::IGUIEnvironment *env, gui::IGUIElement *parent, @@ -153,7 +154,7 @@ void GUIInventoryList::draw() // Add hovering tooltip bool show_tooltip = !item.empty() && hovering && !selected_item; // Make it possible to see item tooltips on touchscreens - if (m_fs_menu->getPointerType() == PointerType::Touch) { + if (RenderingEngine::getLastPointerType() == PointerType::Touch) { show_tooltip |= hovering && selected && m_fs_menu->getSelectedAmount() != 0; } if (show_tooltip) { diff --git a/src/gui/modalMenu.cpp b/src/gui/modalMenu.cpp index 00bc143bb2f1..2a7e68d73e28 100644 --- a/src/gui/modalMenu.cpp +++ b/src/gui/modalMenu.cpp @@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gui/guiInventoryList.h" #include "porting.h" #include "settings.h" -#include "touchscreengui.h" +#include "touchcontrols.h" PointerAction PointerAction::fromEvent(const SEvent &event) { switch (event.EventType) { @@ -62,7 +62,7 @@ GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent, m_gui_scale = std::max(g_settings->getFloat("gui_scaling"), 0.5f); const float screen_dpi_scale = RenderingEngine::getDisplayDensity(); - if (g_settings->getBool("enable_touch")) { + if (g_settings->getBool("touch_gui")) { m_gui_scale *= 1.1f - 0.3f * screen_dpi_scale + 0.2f * screen_dpi_scale * screen_dpi_scale; } else { m_gui_scale *= screen_dpi_scale; @@ -117,8 +117,8 @@ void GUIModalMenu::quitMenu() Environment->removeFocus(this); m_menumgr->deletingMenu(this); this->remove(); - if (g_touchscreengui) - g_touchscreengui->show(); + if (g_touchcontrols) + g_touchcontrols->show(); } static bool isChild(gui::IGUIElement *tocheck, gui::IGUIElement *parent) @@ -193,6 +193,7 @@ bool GUIModalMenu::simulateMouseEvent(ETOUCH_INPUT_EVENT touch_event, bool secon mouse_event.EventType = EET_MOUSE_INPUT_EVENT; mouse_event.MouseInput.X = m_pointer.X; mouse_event.MouseInput.Y = m_pointer.Y; + mouse_event.MouseInput.Simulated = true; switch (touch_event) { case ETIE_PRESSED_DOWN: mouse_event.MouseInput.Event = EMIE_LMOUSE_PRESSED_DOWN; @@ -216,7 +217,6 @@ bool GUIModalMenu::simulateMouseEvent(ETOUCH_INPUT_EVENT touch_event, bool secon } bool retval; - m_simulated_mouse = true; do { if (preprocessEvent(mouse_event)) { retval = true; @@ -228,7 +228,6 @@ bool GUIModalMenu::simulateMouseEvent(ETOUCH_INPUT_EVENT touch_event, bool secon } retval = target->OnEvent(mouse_event); } while (false); - m_simulated_mouse = false; if (!retval && !second_try) return simulateMouseEvent(touch_event, true); @@ -331,7 +330,6 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event) holder.grab(this); // keep this alive until return (it might be dropped downstream [?]) if (event.TouchInput.touchedCount == 1) { - m_pointer_type = PointerType::Touch; m_pointer = v2s32(event.TouchInput.X, event.TouchInput.Y); gui::IGUIElement *hovered = Environment->getRootGUIElement()->getElementFromPoint(core::position2d(m_pointer)); @@ -374,9 +372,8 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event) } if (event.EventType == EET_MOUSE_INPUT_EVENT) { - if (!m_simulated_mouse) { - // Only set the pointer type to mouse if this is a real mouse event. - m_pointer_type = PointerType::Mouse; + if (!event.MouseInput.Simulated) { + // Only process if this is a real mouse event. m_pointer = v2s32(event.MouseInput.X, event.MouseInput.Y); m_touch_hovered.reset(); } diff --git a/src/gui/modalMenu.h b/src/gui/modalMenu.h index 0710241204e1..2f770f9f5a49 100644 --- a/src/gui/modalMenu.h +++ b/src/gui/modalMenu.h @@ -26,11 +26,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #endif -enum class PointerType { - Mouse, - Touch, -}; - struct PointerAction { v2s32 pos; u64 time; // ms @@ -74,14 +69,10 @@ class GUIModalMenu : public gui::IGUIElement porting::AndroidDialogState getAndroidUIInputState(); #endif - PointerType getPointerType() { return m_pointer_type; }; - protected: virtual std::wstring getLabelByID(s32 id) = 0; virtual std::string getNameByID(s32 id) = 0; - // Stores the last known pointer type. - PointerType m_pointer_type = PointerType::Mouse; // Stores the last known pointer position. // If the last input event was a mouse event, it's the cursor position. // If the last input event was a touch event, it's the finger position. @@ -102,9 +93,6 @@ class GUIModalMenu : public gui::IGUIElement // This is set to true if the menu is currently processing a second-touch event. bool m_second_touch = false; - // This is set to true if the menu is currently processing a mouse event - // that was synthesized by the menu itself from a touch event. - bool m_simulated_mouse = false; private: IMenuManager *m_menumgr; diff --git a/src/gui/touchscreengui.cpp b/src/gui/touchcontrols.cpp similarity index 93% rename from src/gui/touchscreengui.cpp rename to src/gui/touchcontrols.cpp index 2504c4eb55e2..643a8a1f2aab 100644 --- a/src/gui/touchscreengui.cpp +++ b/src/gui/touchcontrols.cpp @@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "touchscreengui.h" +#include "touchcontrols.h" #include "gettime.h" #include "irr_v2d.h" @@ -36,7 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include -TouchScreenGUI *g_touchscreengui; +TouchControls *g_touchcontrols; static const char *button_image_names[] = { "jump_btn.png", @@ -237,7 +237,7 @@ static EKEY_CODE id_to_keycode(touch_gui_button_id id) code = keyname_to_keycode(resolved.c_str()); } catch (UnknownKeycode &e) { code = KEY_UNKNOWN; - warningstream << "TouchScreenGUI: Unknown key '" << resolved + warningstream << "TouchControls: Unknown key '" << resolved << "' for '" << key << "', hiding button." << std::endl; } return code; @@ -403,7 +403,7 @@ void AutoHideButtonBar::hide() updateVisibility(); } -TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, ISimpleTextureSource *tsrc): +TouchControls::TouchControls(IrrlichtDevice *device, ISimpleTextureSource *tsrc): m_device(device), m_guienv(device->getGUIEnvironment()), m_receiver(device->getEventReceiver()), @@ -515,7 +515,12 @@ TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, ISimpleTextureSource *tsr } } -void TouchScreenGUI::addButton(touch_gui_button_id id, const std::string &image, const recti &rect) +TouchControls::~TouchControls() +{ + releaseAll(); +} + +void TouchControls::addButton(touch_gui_button_id id, const std::string &image, const recti &rect) { IGUIImage *btn_gui_button = m_guienv->addImage(rect, nullptr, id); load_button_texture(btn_gui_button, image, rect, @@ -526,7 +531,7 @@ void TouchScreenGUI::addButton(touch_gui_button_id id, const std::string &image, btn.gui_button = grab_gui_element(btn_gui_button); } -IGUIImage *TouchScreenGUI::makeJoystickButton(touch_gui_button_id id, +IGUIImage *TouchControls::makeJoystickButton(touch_gui_button_id id, const recti &button_rect, bool visible) { IGUIImage *btn_gui_button = m_guienv->addImage(button_rect, nullptr, id); @@ -537,7 +542,7 @@ IGUIImage *TouchScreenGUI::makeJoystickButton(touch_gui_button_id id, return btn_gui_button; } -bool TouchScreenGUI::isHotbarButton(const SEvent &event) +bool TouchControls::isHotbarButton(const SEvent &event) { const v2s32 touch_pos = v2s32(event.TouchInput.X, event.TouchInput.Y); // check if hotbar item is pressed @@ -552,14 +557,14 @@ bool TouchScreenGUI::isHotbarButton(const SEvent &event) return false; } -std::optional TouchScreenGUI::getHotbarSelection() +std::optional TouchControls::getHotbarSelection() { auto selection = m_hotbar_selection; m_hotbar_selection = std::nullopt; return selection; } -void TouchScreenGUI::handleReleaseEvent(size_t pointer_id) +void TouchControls::handleReleaseEvent(size_t pointer_id) { // By the way: Android reuses pointer IDs, so m_pointer_pos[pointer_id] // will be overwritten soon anyway. @@ -605,15 +610,15 @@ void TouchScreenGUI::handleReleaseEvent(size_t pointer_id) m_joystick_btn_bg->setVisible(false); m_joystick_btn_center->setVisible(false); } else { - infostream << "TouchScreenGUI::translateEvent released unknown button: " + infostream << "TouchControls::translateEvent released unknown button: " << pointer_id << std::endl; } } -void TouchScreenGUI::translateEvent(const SEvent &event) +void TouchControls::translateEvent(const SEvent &event) { if (!m_visible) { - infostream << "TouchScreenGUI::translateEvent got event but is not visible!" + infostream << "TouchControls::translateEvent got event but is not visible!" << std::endl; return; } @@ -780,7 +785,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event) } } -void TouchScreenGUI::applyJoystickStatus() +void TouchControls::applyJoystickStatus() { if (m_joystick_triggers_aux1) { SEvent translated{}; @@ -796,7 +801,24 @@ void TouchScreenGUI::applyJoystickStatus() } } -void TouchScreenGUI::step(float dtime) +void TouchControls::releaseAll() +{ + while (!m_pointer_pos.empty()) + handleReleaseEvent(m_pointer_pos.begin()->first); + + // Release those manually too since the change initiated by + // handleReleaseEvent will only be applied later by applyContextControls. + if (m_dig_pressed) { + emitMouseEvent(EMIE_LMOUSE_LEFT_UP); + m_dig_pressed = false; + } + if (m_place_pressed) { + emitMouseEvent(EMIE_RMOUSE_LEFT_UP); + m_place_pressed = false; + } +} + +void TouchControls::step(float dtime) { // simulate keyboard repeats buttons_step(m_buttons, dtime, m_device->getVideoDriver(), m_receiver, m_texturesource); @@ -832,18 +854,21 @@ void TouchScreenGUI::step(float dtime) m_had_move_id = false; } -void TouchScreenGUI::resetHotbarRects() +void TouchControls::resetHotbarRects() { m_hotbar_rects.clear(); } -void TouchScreenGUI::registerHotbarRect(u16 index, const recti &rect) +void TouchControls::registerHotbarRect(u16 index, const recti &rect) { m_hotbar_rects[index] = rect; } -void TouchScreenGUI::setVisible(bool visible) +void TouchControls::setVisible(bool visible) { + if (m_visible == visible) + return; + m_visible = visible; for (auto &button : m_buttons) { if (button.gui_button) @@ -853,10 +878,8 @@ void TouchScreenGUI::setVisible(bool visible) if (m_joystick_btn_off) m_joystick_btn_off->setVisible(visible); - // clear all active buttons if (!visible) { - while (!m_pointer_pos.empty()) - handleReleaseEvent(m_pointer_pos.begin()->first); + releaseAll(); for (AutoHideButtonBar &bar : m_buttonbars) { bar.deactivate(); bar.hide(); @@ -867,23 +890,17 @@ void TouchScreenGUI::setVisible(bool visible) } } -void TouchScreenGUI::hide() +void TouchControls::hide() { - if (!m_visible) - return; - setVisible(false); } -void TouchScreenGUI::show() +void TouchControls::show() { - if (m_visible) - return; - setVisible(true); } -v2s32 TouchScreenGUI::getPointerPos() +v2s32 TouchControls::getPointerPos() { if (m_draw_crosshair) return v2s32(m_screensize.X / 2, m_screensize.Y / 2); @@ -892,7 +909,7 @@ v2s32 TouchScreenGUI::getPointerPos() return m_move_pos; } -void TouchScreenGUI::emitMouseEvent(EMOUSE_INPUT_EVENT type) +void TouchControls::emitMouseEvent(EMOUSE_INPUT_EVENT type) { v2s32 pointer_pos = getPointerPos(); @@ -904,10 +921,11 @@ void TouchScreenGUI::emitMouseEvent(EMOUSE_INPUT_EVENT type) event.MouseInput.Control = false; event.MouseInput.ButtonStates = 0; event.MouseInput.Event = type; + event.MouseInput.Simulated = true; m_receiver->OnEvent(event); } -void TouchScreenGUI::applyContextControls(const TouchInteractionMode &mode) +void TouchControls::applyContextControls(const TouchInteractionMode &mode) { // Since the pointed thing has already been determined when this function // is called, we cannot use this function to update the shootline. diff --git a/src/gui/touchscreengui.h b/src/gui/touchcontrols.h similarity index 96% rename from src/gui/touchscreengui.h rename to src/gui/touchcontrols.h index ca6c05e1819d..9b29b2d03a2d 100644 --- a/src/gui/touchscreengui.h +++ b/src/gui/touchcontrols.h @@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "itemdef.h" #include "client/game.h" +#include "util/basic_macros.h" using namespace irr; using namespace irr::core; @@ -183,10 +184,12 @@ class AutoHideButtonBar void updateVisibility(); }; -class TouchScreenGUI +class TouchControls { public: - TouchScreenGUI(IrrlichtDevice *device, ISimpleTextureSource *tsrc); + TouchControls(IrrlichtDevice *device, ISimpleTextureSource *tsrc); + ~TouchControls(); + DISABLE_CLASS_COPY(TouchControls); void translateEvent(const SEvent &event); void applyContextControls(const TouchInteractionMode &mode); @@ -220,7 +223,6 @@ class TouchScreenGUI void step(float dtime); inline void setUseCrosshair(bool use_crosshair) { m_draw_crosshair = use_crosshair; } - void setVisible(bool visible); void hide(); void show(); @@ -237,7 +239,8 @@ class TouchScreenGUI s32 m_button_size; double m_touchscreen_threshold; u16 m_long_tap_delay; - bool m_visible = true; // is the whole touch screen gui visible + bool m_visible = true; + void setVisible(bool visible); std::unordered_map m_hotbar_rects; std::optional m_hotbar_selection = std::nullopt; @@ -295,6 +298,9 @@ class TouchScreenGUI // apply joystick status void applyJoystickStatus(); + // release all buttons etc. + void releaseAll(); + // map to store the IDs and original positions of currently pressed pointers std::unordered_map m_pointer_downpos; // map to store the IDs and positions of currently pressed pointers @@ -314,4 +320,4 @@ class TouchScreenGUI u64 m_place_pressed_until = 0; }; -extern TouchScreenGUI *g_touchscreengui; +extern TouchControls *g_touchcontrols;