/* * The ManaPlus Client * Copyright (C) 2004-2009 The Mana World Development Team * Copyright (C) 2009-2010 The Mana Developers * Copyright (C) 2011-2020 The ManaPlus Developers * Copyright (C) 2020-2023 The ManaVerse Developers * * This file is part of The ManaPlus Client. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "gui/windowmanager.h" #include "client.h" #include "configuration.h" #include "game.h" #include "main.h" #include "settings.h" #include "spellmanager.h" #include "fs/virtfs/tools.h" #include "gui/gui.h" #include "gui/userpalette.h" #include "gui/shortcut/spellshortcut.h" #include "gui/popups/textpopup.h" #ifdef DYECMD #include "render/graphics.h" #else // DYECMD #include "gui/popups/beingpopup.h" #include "gui/popups/itempopup.h" #include "gui/popups/popupmenu.h" #include "gui/popups/skillpopup.h" #include "gui/popups/spellpopup.h" #include "gui/popups/textboxpopup.h" #include "gui/windows/chatwindow.h" #include "gui/windows/debugwindow.h" #include "gui/windows/didyouknowwindow.h" #include "gui/windows/equipmentwindow.h" #include "gui/windows/helpwindow.h" #include "gui/windows/setupwindow.h" #include "gui/windows/skilldialog.h" #include "gui/windows/questswindow.h" #include "gui/widgets/tabs/chat/chattab.h" #include "utils/gettext.h" #endif // DYECMD #include "gui/widgets/createwidget.h" #include "gui/widgets/desktop.h" #include "input/touch/touchmanager.h" #include "utils/delete2.h" #include "utils/sdlcheckutils.h" #include "utils/sdlhelper.h" #ifdef __native_client__ #include "utils/naclmessages.h" #endif // __native_client__ PRAGMA48(GCC diagnostic push) PRAGMA48(GCC diagnostic ignored "-Wshadow") #ifdef ANDROID #ifndef USE_SDL2 #include #endif // USE_SDL2 #endif // ANDROID #ifdef USE_SDL2 #include #else // USE_SDL2 #include #endif // USE_SDL2 #include #ifdef WIN32 #include #endif // WIN32 PRAGMA48(GCC diagnostic pop) #include "debug.h" FPSmanager fpsManager; namespace { SDL_Surface *mIcon(nullptr); #ifndef USE_SDL2 int mKeyboardHeight(0); #endif // USE_SDL2 bool mIsMinimized(false); bool mNewMessageFlag(false); } // namespace void WindowManager::init() { // Initialize frame limiting fpsManager.framecount = 0; fpsManager.rateticks = 0; fpsManager.lastticks = 0; fpsManager.rate = 0; } void WindowManager::createWindows() { userPalette = new UserPalette; #ifndef DYECMD CREATEWIDGETV0(setupWindow, SetupWindow); CREATEWIDGETV0(helpWindow, HelpWindow); CREATEWIDGETV0(didYouKnowWindow, DidYouKnowWindow); CREATEWIDGETV0(popupMenu, PopupMenu); CREATEWIDGETV0(beingPopup, BeingPopup); CREATEWIDGETV0(textBoxPopup, TextBoxPopup); CREATEWIDGETV0(itemPopup, ItemPopup); CREATEWIDGETV0(spellPopup, SpellPopup); CREATEWIDGETV0(skillPopup, SkillPopup); delete2(debugChatTab) if (chatWindow != nullptr) { chatWindow->scheduleDelete(); chatWindow = nullptr; } CREATEWIDGETV(chatWindow, ChatWindow, "DebugChat"); // TRANSLATORS: chat tab header debugChatTab = new ChatTab(chatWindow, _("Debug"), "", "#Debug", ChatTabType::DEBUG); debugChatTab->setAllowHighlight(false); chatWindow->setVisible(Visible_false); if (debugWindow != nullptr) { debugWindow->scheduleDelete(); debugWindow = nullptr; } CREATEWIDGETV(debugWindow, DebugWindow, "DebugDebug"); #endif // DYECMD CREATEWIDGETV0(textPopup, TextPopup); } void WindowManager::createValidateWindows() { #ifndef DYECMD CREATEWIDGETV0(skillDialog, SkillDialog); skillDialog->loadSkills(); CREATEWIDGETV(beingEquipmentWindow, EquipmentWindow, nullptr, nullptr, true); CREATEWIDGETV0(questsWindow, QuestsWindow); #endif // DYECMD } void WindowManager::deleteValidateWindows() { #ifndef DYECMD delete2(skillDialog) delete2(beingEquipmentWindow) delete2(questsWindow) #endif // DYECMD } void WindowManager::deleteWindows() { #ifndef DYECMD delete2(textBoxPopup) delete2(beingPopup) delete2(itemPopup) delete2(spellPopup) delete2(skillPopup) delete2(popupMenu) delete2(didYouKnowWindow) delete2(helpWindow) delete2(setupWindow) delete2(userPalette) delete2(spellManager) delete2(spellShortcut) delete2(debugChatTab) delete2(chatWindow) delete2(debugWindow) #endif // DYECMD delete2(textPopup) } void WindowManager::initTitle() { if (settings.options.test.empty()) { settings.windowCaption = strprintf("%s %s", branding.getStringValue("appName").c_str(), SMALL_VERSION); } else { settings.windowCaption = strprintf( "Please wait - VIDEO MODE TEST - %s %s - Please wait", branding.getStringValue("appName").c_str(), SMALL_VERSION); } SDL::SetWindowTitle(mainGraphics->getWindow(), settings.windowCaption.c_str()); } void WindowManager::updateTitle() { std::string str; if (settings.login.empty()) str = settings.serverName; else { // dont show ugly account names in title, use "Session" insteed. if (settings.options.uniqueSession) str.append("Session "); else str.append(settings.login); str.append(" ").append(settings.serverName); } if (str.empty()) { settings.windowCaption = strprintf("%s %s", branding.getStringValue("appName").c_str(), SMALL_VERSION); } else { settings.windowCaption = strprintf("%s %s - %s", branding.getStringValue("appName").c_str(), SMALL_VERSION, str.c_str()); } SDL::SetWindowTitle(mainGraphics->getWindow(), settings.windowCaption.c_str()); } void WindowManager::setFramerate(const unsigned int fpsLimit) { if (fpsLimit == 0U) return; if (!settings.limitFps) return; SDL_setFramerate(&fpsManager, fpsLimit); } int WindowManager::getFramerate() { if (!settings.limitFps) return 0; return SDL_getFramerate(&fpsManager); } void WindowManager::doResizeVideo(const int actualWidth, const int actualHeight, const bool always) { if (!always && mainGraphics->mActualWidth == actualWidth && mainGraphics->mActualHeight == actualHeight) { return; } #ifdef __native_client__ naclPostMessage("resize-window", strprintf("%d,%d", actualWidth, actualHeight)); #else // __native_client__ resizeVideo(actualWidth, actualHeight, always); #endif // __native_client__ } void WindowManager::resizeVideo(int actualWidth, int actualHeight, const bool always) { // Keep a minimum size. This isn't adhered to by the actual window, but // it keeps some window positions from getting messed up. actualWidth = std::max(470, actualWidth); actualHeight = std::max(320, actualHeight); if (mainGraphics == nullptr) return; if (!always && mainGraphics->mActualWidth == actualWidth && mainGraphics->mActualHeight == actualHeight) { return; } if (mainGraphics->resizeScreen(actualWidth, actualHeight)) { const int width = mainGraphics->mWidth; const int height = mainGraphics->mHeight; touchManager.resize(width, height); if (gui != nullptr) gui->videoResized(); if (desktop != nullptr) desktop->setSize(width, height); client->moveButtons(width); #ifndef DYECMD Game *const game = Game::instance(); if (game != nullptr) Game::videoResized(width, height); #endif // DYECMD if (gui != nullptr) gui->draw(); config.setValue("screenwidth", actualWidth); config.setValue("screenheight", actualHeight); } } bool WindowManager::setFullScreen(const bool fs) { #ifdef __native_client__ naclPostMessage("set-fullscreen", fs ? "on" : "off"); return true; #else // __native_client__ if (mainGraphics == nullptr) return false; return mainGraphics->setFullscreen(fs); #endif // __native_client__ } void WindowManager::applyGrabMode() { SDL::grabInput(mainGraphics->getWindow(), config.getBoolValue("grabinput")); } void WindowManager::applyGamma() { if (config.getBoolValue("enableGamma")) { SDL::setGamma(mainGraphics->getWindow(), config.getFloatValue("gamma")); } } void WindowManager::applyVSync() { const int val = config.getIntValue("vsync"); if (val > 0 && val < 2) SDL::setVsync(val); } void WindowManager::applyKeyRepeat() { #ifndef USE_SDL2 SDL_EnableKeyRepeat(config.getIntValue("repeateDelay"), config.getIntValue("repeateInterval")); #endif // USE_SDL2 } void WindowManager::applyScale() { const int scale = config.getIntValue("scale"); if (mainGraphics->getScale() == scale) return; mainGraphics->setScale(scale); doResizeVideo(mainGraphics->mActualWidth, mainGraphics->mActualHeight, true); } void WindowManager::setIsMinimized(const bool n) { mIsMinimized = n; if (!n && mNewMessageFlag) { mNewMessageFlag = false; SDL::SetWindowTitle(mainGraphics->getWindow(), settings.windowCaption.c_str()); } } void WindowManager::newChatMessage() { if (!mNewMessageFlag && mIsMinimized) { // show * on window caption SDL::SetWindowTitle(mainGraphics->getWindow(), ("*" + settings.windowCaption).c_str()); mNewMessageFlag = true; } } void WindowManager::setIcon() { #if !defined(ANDROID) && !defined(__SWITCH__) std::string iconFile = branding.getValue("appIcon", "icons/manaverse"); #ifdef WIN32 iconFile.append(".ico"); #else // WIN32 iconFile.append(".png"); #endif // WIN32 iconFile = VirtFs::getPath(iconFile); logger->log("Loading icon from file: %s", iconFile.c_str()); #ifdef WIN32 static SDL_SysWMinfo pInfo; if (mainGraphics) SDL::getWindowWMInfo(mainGraphics->getWindow(), &pInfo); else SDL::getWindowWMInfo(nullptr, &pInfo); // Attempt to load icon from .ico file HICON icon = static_cast(LoadImage(nullptr, iconFile.c_str(), IMAGE_ICON, 64, 64, LR_LOADFROMFILE)); // If it's failing, we load the default resource file. if (!icon) { logger->log("icon load error"); icon = LoadIcon(GetModuleHandle(nullptr), "A"); } if (icon) { #ifdef WIN64 #ifdef USE_SDL2 SetClassLongPtr(pInfo.info.win.window, #else SetClassLongPtr(pInfo.window, #endif // USE_SDL2 GCLP_HICON, reinterpret_cast(icon)); #else // WIN64 #ifdef USE_SDL2 SetClassLong(pInfo.info.win.window, #else SetClassLong(pInfo.window, #endif // USE_SDL2 GCL_HICON, reinterpret_cast(icon)); #endif // WIN64 } #else // WIN32 mIcon = MIMG_Load(iconFile.c_str()); if (mIcon != nullptr) { #ifdef USE_SDL2 SDL_SetSurfaceAlphaMod(mIcon, SDL_ALPHA_OPAQUE); #else // USE_SDL2 SDL_SetAlpha(mIcon, SDL_SRCALPHA, SDL_ALPHA_OPAQUE); #endif // USE_SDL2 SDL::SetWindowIcon(mainGraphics->getWindow(), mIcon); } #endif // WIN32 #endif // ANDROID } bool WindowManager::isKeyboardVisible() { #ifdef USE_SDL2 return SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE; #else // USE_SDL2 return mKeyboardHeight > 1; #endif // USE_SDL2 } bool WindowManager::getIsMinimized() { return mIsMinimized; } #ifndef USE_SDL2 void WindowManager::updateScreenKeyboard(const int height) { mKeyboardHeight = height; } #endif // USE_SDL2 void WindowManager::deleteIcon() { MSDL_FreeSurface(mIcon); mIcon = nullptr; }