diff --git a/builtin/mainmenu/settings/dlg_settings.lua b/builtin/mainmenu/settings/dlg_settings.lua index d6bbf2570..fe1e1924f 100644 --- a/builtin/mainmenu/settings/dlg_settings.lua +++ b/builtin/mainmenu/settings/dlg_settings.lua @@ -723,6 +723,11 @@ local function eventhandler(event) mm_game_theme.set_engine(true) return true end + if event == "FullscreenChange" then + -- Refresh the formspec to keep the fullscreen checkbox up to date. + ui.update() + return true + end return false end diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 8dc37c3e7..613e54f6d 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -2399,6 +2399,9 @@ keymap_minimap (Minimap key) key KEY_KEY_V # Key for taking screenshots. keymap_screenshot (Screenshot) key KEY_F12 +# Key for toggling fullscreen mode. +keymap_fullscreen (Fullscreen key) key KEY_F11 + # Key for dropping the currently selected item. keymap_drop (Drop item key) key KEY_KEY_Q diff --git a/doc/menu_lua_api.md b/doc/menu_lua_api.md index cb1f07a90..19540a607 100644 --- a/doc/menu_lua_api.md +++ b/doc/menu_lua_api.md @@ -14,7 +14,8 @@ Callbacks * `core.button_handler(fields)`: called when a button is pressed. * `fields` = `{name1 = value1, name2 = value2, ...}` * `core.event_handler(event)` - * `event`: `"MenuQuit"`, `"KeyEnter"`, `"ExitButton"` or `"EditBoxEnter"` + * `event`: `"MenuQuit"`, `"KeyEnter"`, `"ExitButton"`, `"EditBoxEnter"` or + `"FullscreenChange"` Gamedata diff --git a/irr/include/IrrlichtDevice.h b/irr/include/IrrlichtDevice.h index 38ba8c63d..11619010c 100644 --- a/irr/include/IrrlichtDevice.h +++ b/irr/include/IrrlichtDevice.h @@ -179,6 +179,11 @@ class IrrlichtDevice : public virtual IReferenceCounted /** \return True if window is fullscreen. */ virtual bool isFullscreen() const = 0; + //! Enables or disables fullscreen mode. + /** Only works on SDL. + \return True on success. */ + virtual bool setFullscreen(bool fullscreen) { return false; } + //! Checks if the window could possibly be visible. /** If this returns false, you should not do any rendering. */ virtual bool isWindowVisible() const { return true; }; diff --git a/irr/src/CIrrDeviceSDL.cpp b/irr/src/CIrrDeviceSDL.cpp index b50fd4b4a..6f2772aa6 100644 --- a/irr/src/CIrrDeviceSDL.cpp +++ b/irr/src/CIrrDeviceSDL.cpp @@ -463,13 +463,7 @@ bool CIrrDeviceSDL::createWindowWithContext() { u32 SDL_Flags = 0; - if (CreationParams.Fullscreen) { -#ifdef _IRR_EMSCRIPTEN_PLATFORM_ - SDL_Flags |= SDL_WINDOW_FULLSCREEN; -#else - SDL_Flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; -#endif - } + SDL_Flags |= getFullscreenFlag(CreationParams.Fullscreen); if (Resizable) SDL_Flags |= SDL_WINDOW_RESIZABLE; if (CreationParams.WindowMaximized) @@ -889,6 +883,14 @@ bool CIrrDeviceSDL::run() IsInBackground = false; break; + case SDL_RENDER_TARGETS_RESET: + os::Printer::log("Received SDL_RENDER_TARGETS_RESET. Rendering is probably broken.", ELL_ERROR); + break; + + case SDL_RENDER_DEVICE_RESET: + os::Printer::log("Received SDL_RENDER_DEVICE_RESET. Rendering is probably broken.", ELL_ERROR); + break; + default: break; } // end switch @@ -1157,14 +1159,37 @@ bool CIrrDeviceSDL::isWindowMaximized() const bool CIrrDeviceSDL::isFullscreen() const { -#ifdef _IRR_EMSCRIPTEN_PLATFORM_ - return SDL_GetWindowFlags(0) == SDL_WINDOW_FULLSCREEN; -#else + if (!Window) + return false; + u32 flags = SDL_GetWindowFlags(Window); + return (flags & SDL_WINDOW_FULLSCREEN) != 0 || + (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; +} - return CIrrDeviceStub::isFullscreen(); +u32 CIrrDeviceSDL::getFullscreenFlag(bool fullscreen) +{ + if (!fullscreen) + return 0; +#ifdef _IRR_EMSCRIPTEN_PLATFORM_ + return SDL_WINDOW_FULLSCREEN; +#else + return SDL_WINDOW_FULLSCREEN_DESKTOP; #endif } +bool CIrrDeviceSDL::setFullscreen(bool fullscreen) +{ + if (!Window) + return false; + // The SDL wiki says that this may trigger SDL_RENDER_TARGETS_RESET, but + // looking at the SDL source, this only happens with D3D, so it's not + // relevant to us. + bool success = SDL_SetWindowFullscreen(Window, getFullscreenFlag(fullscreen)) == 0; + if (!success) + os::Printer::log("SDL_SetWindowFullscreen failed", SDL_GetError(), ELL_ERROR); + return success; +} + bool CIrrDeviceSDL::isWindowVisible() const { return !IsInBackground; diff --git a/irr/src/CIrrDeviceSDL.h b/irr/src/CIrrDeviceSDL.h index a83e8cf5e..c536a8149 100644 --- a/irr/src/CIrrDeviceSDL.h +++ b/irr/src/CIrrDeviceSDL.h @@ -86,6 +86,10 @@ class CIrrDeviceSDL : public CIrrDeviceStub /** \return True if window is fullscreen. */ bool isFullscreen() const override; + //! Enables or disables fullscreen mode. + /** \return True on success. */ + bool setFullscreen(bool fullscreen) override; + //! Checks if the window could possibly be visible. bool isWindowVisible() const override; @@ -299,6 +303,8 @@ class CIrrDeviceSDL : public CIrrDeviceStub bool Resizable; + static u32 getFullscreenFlag(bool fullscreen); + core::rect lastElemPos; struct SKeyMap diff --git a/src/client/game.cpp b/src/client/game.cpp index d35bf8e08..e6e478b04 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1164,7 +1164,8 @@ void Game::run() g_settings->getU16("screen_w"), g_settings->getU16("screen_h") ); - const bool initial_window_maximized = g_settings->getBool("window_maximized"); + const bool initial_window_maximized = !g_settings->getBool("fullscreen") && + g_settings->getBool("window_maximized"); while (m_rendering_engine->run() && !(*kill || g_gamecallback->shutdown_requested diff --git a/src/client/inputhandler.cpp b/src/client/inputhandler.cpp index 6dfd2ad35..665d77dc5 100644 --- a/src/client/inputhandler.cpp +++ b/src/client/inputhandler.cpp @@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "settings.h" #include "util/numeric.h" #include "inputhandler.h" #include "gui/mainmenumanager.h" @@ -113,6 +114,19 @@ bool MyEventReceiver::OnEvent(const SEvent &event) return true; } + // This is separate from other keyboard handling so that it also works in menus. + if (event.EventType == EET_KEY_INPUT_EVENT) { + const KeyPress keyCode(event.KeyInput); + if (keyCode == getKeySetting("keymap_fullscreen")) { + if (event.KeyInput.PressedDown && !fullscreen_is_down) { + bool fullscreen = RenderingEngine::get_raw_device()->isFullscreen(); + g_settings->setBool("fullscreen", !fullscreen); + } + fullscreen_is_down = event.KeyInput.PressedDown; + return true; + } + } + // Let the menu handle events, if one is active. if (isMenuActive()) { if (g_touchscreengui) diff --git a/src/client/inputhandler.h b/src/client/inputhandler.h index f4fae2b0b..4c6db3618 100644 --- a/src/client/inputhandler.h +++ b/src/client/inputhandler.h @@ -220,6 +220,9 @@ class MyEventReceiver : public IEventReceiver // often changing keys, and keysListenedFor is expected // to change seldomly but contain lots of keys. KeyList keysListenedFor; + + // Intentionally not reset by clearInput/releaseAllKeys. + bool fullscreen_is_down = false; }; class InputHandler diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp index 27ea2b76f..b0033c0e2 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -207,7 +207,12 @@ RenderingEngine::RenderingEngine(IEventReceiver *receiver) #else u16 screen_w = std::max(g_settings->getU16("screen_w"), 1); u16 screen_h = std::max(g_settings->getU16("screen_h"), 1); - bool window_maximized = g_settings->getBool("window_maximized"); + // If I… + // 1. … set fullscreen = true and window_maximized = true on startup + // 2. … set fullscreen = false later + // on Linux with SDL, everything breaks. + // => Don't do it. + bool window_maximized = !fullscreen && g_settings->getBool("window_maximized"); #endif // bpp, fsaa, vsync @@ -249,18 +254,40 @@ RenderingEngine::RenderingEngine(IEventReceiver *receiver) gui::EGST_WINDOWS_METALLIC, driver); m_device->getGUIEnvironment()->setSkin(skin); skin->drop(); + + g_settings->registerChangedCallback("fullscreen", settingChangedCallback, this); + g_settings->registerChangedCallback("window_maximized", settingChangedCallback, this); } RenderingEngine::~RenderingEngine() { sanity_check(s_singleton == this); + g_settings->deregisterChangedCallback("fullscreen", settingChangedCallback, this); + g_settings->deregisterChangedCallback("window_maximized", settingChangedCallback, this); + core.reset(); m_device->closeDevice(); m_device->drop(); s_singleton = nullptr; } +void RenderingEngine::settingChangedCallback(const std::string &name, void *data) +{ + IrrlichtDevice *device = static_cast(data)->m_device; + if (name == "fullscreen") { + device->setFullscreen(g_settings->getBool("fullscreen")); + + } else if (name == "window_maximized") { + if (!device->isFullscreen()) { + if (g_settings->getBool("window_maximized")) + device->maximizeWindow(); + else + device->restoreWindow(); + } + } +} + v2u32 RenderingEngine::_getWindowSize() const { if (core) diff --git a/src/client/renderingengine.h b/src/client/renderingengine.h index 684df1237..1a2a63513 100644 --- a/src/client/renderingengine.h +++ b/src/client/renderingengine.h @@ -168,6 +168,7 @@ class RenderingEngine const bool initial_window_maximized); private: + static void settingChangedCallback(const std::string &name, void *data); v2u32 _getWindowSize() const; std::unique_ptr core; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 378d09759..f16c56db4 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -182,6 +182,7 @@ void set_default_settings() settings->setDefault("keymap_toggle_profiler", "KEY_F6"); settings->setDefault("keymap_camera_mode", "KEY_KEY_C"); settings->setDefault("keymap_screenshot", "KEY_F12"); + settings->setDefault("keymap_fullscreen", "KEY_F11"); settings->setDefault("keymap_increase_viewing_range_min", "+"); settings->setDefault("keymap_decrease_viewing_range_min", "-"); settings->setDefault("keymap_slot1", "KEY_KEY_1"); diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp index 66f9f9864..fdc13fa14 100644 --- a/src/gui/guiEngine.cpp +++ b/src/gui/guiEngine.cpp @@ -193,6 +193,8 @@ GUIEngine::GUIEngine(JoystickController *joystick, m_script = std::make_unique(this); + g_settings->registerChangedCallback("fullscreen", fullscreenChangedCallback, this); + try { m_script->setMainMenuData(&m_data->script_data); m_data->script_data.errormessage.clear(); @@ -319,7 +321,8 @@ void GUIEngine::run() g_settings->getU16("screen_w"), g_settings->getU16("screen_h") ); - const bool initial_window_maximized = g_settings->getBool("window_maximized"); + const bool initial_window_maximized = !g_settings->getBool("fullscreen") && + g_settings->getBool("window_maximized"); FpsControl fps_control; f32 dtime = 0.0f; @@ -377,6 +380,8 @@ void GUIEngine::run() /******************************************************************************/ GUIEngine::~GUIEngine() { + g_settings->deregisterChangedCallback("fullscreen", fullscreenChangedCallback, this); + // deinitialize script first. gc destructors might depend on other stuff infostream << "GUIEngine: Deinitializing scripting" << std::endl; m_script.reset(); @@ -666,3 +671,9 @@ void GUIEngine::updateTopLeftTextSize() m_irr_toplefttext = gui::StaticText::add(m_rendering_engine->get_gui_env(), m_toplefttext, rect, false, true, 0, -1); } + +/******************************************************************************/ +void GUIEngine::fullscreenChangedCallback(const std::string &name, void *data) +{ + static_cast(data)->getScriptIface()->handleMainMenuEvent("FullscreenChange"); +} diff --git a/src/gui/guiEngine.h b/src/gui/guiEngine.h index b2b396537..fa4e1ebd3 100644 --- a/src/gui/guiEngine.h +++ b/src/gui/guiEngine.h @@ -296,4 +296,6 @@ class GUIEngine { bool m_clouds_enabled = true; /** data used to draw clouds */ clouddata m_cloud; + + static void fullscreenChangedCallback(const std::string &name, void *data); };