File indexing completed on 2024-05-12 03:54:29
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 2012 Benjamin Port <benjamin.port@ben2367.fr> 0004 0005 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0006 */ 0007 0008 #include "kwindowconfig.h" 0009 #include "ksharedconfig.h" 0010 0011 #include <QGuiApplication> 0012 #include <QScreen> 0013 #include <QWindow> 0014 0015 static const char s_initialSizePropertyName[] = "_kconfig_initial_size"; 0016 static const char s_initialScreenSizePropertyName[] = "_kconfig_initial_screen_size"; 0017 0018 // Convenience function to get a space-separated list of all connected screens 0019 static QString allConnectedScreens() 0020 { 0021 QStringList names; 0022 const auto screens = QGuiApplication::screens(); 0023 names.reserve(screens.length()); 0024 for (auto screen : screens) { 0025 names << screen->name(); 0026 } 0027 // A string including the connector names is used in the config file key for 0028 // storing per-screen-arrangement size and position data, which means we 0029 // need this string to be consistent for the same screen arrangement. But 0030 // connector order is non-deterministic. We need to sort the list to keep a 0031 // consistent order and avoid losing multi-screen size and position data. 0032 names.sort(); 0033 return names.join(QLatin1Char(' ')); 0034 } 0035 0036 // Convenience function to return screen by its name from window screen siblings 0037 // returns current window screen if not found 0038 static QScreen *findScreenByName(const QWindow *window, const QString screenName) 0039 { 0040 if (screenName == window->screen()->name()) { 0041 return window->screen(); 0042 } 0043 for (QScreen *s : window->screen()->virtualSiblings()) { 0044 if (s->name() == screenName) { 0045 return s; 0046 } 0047 } 0048 return window->screen(); 0049 } 0050 0051 // Convenience function to get an appropriate config file key under which to 0052 // save window size, position, or maximization information. 0053 static QString configFileString(const QString &key) 0054 { 0055 QString returnString; 0056 const int numberOfScreens = QGuiApplication::screens().length(); 0057 0058 if (numberOfScreens == 1) { 0059 // For single-screen setups, we save data on a per-resolution basis. 0060 const QRect screenGeometry = QGuiApplication::primaryScreen()->geometry(); 0061 returnString = QStringLiteral("%1x%2 screen: %3").arg(QString::number(screenGeometry.width()), QString::number(screenGeometry.height()), key); 0062 } else { 0063 // For multi-screen setups, we save data based on the number of screens. 0064 // Distinguishing individual screens based on their names is unreliable 0065 // due to name strings being inherently volatile. 0066 returnString = QStringLiteral("%1 screens: %2").arg(QString::number(numberOfScreens), key); 0067 } 0068 return returnString; 0069 } 0070 0071 // Convenience function for "window is maximized" string 0072 static QString screenMaximizedString() 0073 { 0074 return configFileString(QStringLiteral("Window-Maximized")); 0075 } 0076 // Convenience function for window width string 0077 static QString windowWidthString() 0078 { 0079 return configFileString(QStringLiteral("Width")); 0080 } 0081 // Convenience function for window height string 0082 static QString windowHeightString() 0083 { 0084 return configFileString(QStringLiteral("Height")); 0085 } 0086 // Convenience function for window X position string 0087 static QString windowXPositionString() 0088 { 0089 return configFileString(QStringLiteral("XPosition")); 0090 } 0091 // Convenience function for window Y position string 0092 static QString windowYPositionString() 0093 { 0094 return configFileString(QStringLiteral("YPosition")); 0095 } 0096 static QString windowScreenPositionString() 0097 { 0098 return QStringLiteral("%1").arg(allConnectedScreens()); 0099 } 0100 0101 void KWindowConfig::saveWindowSize(const QWindow *window, KConfigGroup &config, KConfigGroup::WriteConfigFlags options) 0102 { 0103 // QWindow::screen() shouldn't return null, but it sometimes does due to bugs. 0104 if (!window || !window->screen()) { 0105 return; 0106 } 0107 const QScreen *screen = window->screen(); 0108 0109 const QSize sizeToSave = window->size(); 0110 const bool isMaximized = window->windowState() & Qt::WindowMaximized; 0111 0112 // Save size only if window is not maximized 0113 if (!isMaximized) { 0114 const QSize defaultSize(window->property(s_initialSizePropertyName).toSize()); 0115 const QSize defaultScreenSize(window->property(s_initialScreenSizePropertyName).toSize()); 0116 const bool sizeValid = defaultSize.isValid() && defaultScreenSize.isValid(); 0117 if (!sizeValid || (sizeValid && (defaultSize != sizeToSave || defaultScreenSize != screen->geometry().size()))) { 0118 config.writeEntry(windowWidthString(), sizeToSave.width(), options); 0119 config.writeEntry(windowHeightString(), sizeToSave.height(), options); 0120 // Don't keep the maximized string in the file since the window is 0121 // no longer maximized at this point 0122 config.deleteEntry(screenMaximizedString()); 0123 } 0124 } 0125 if ((isMaximized == false) && !config.hasDefault(screenMaximizedString())) { 0126 config.revertToDefault(screenMaximizedString()); 0127 } else { 0128 config.writeEntry(screenMaximizedString(), isMaximized, options); 0129 } 0130 } 0131 0132 bool KWindowConfig::hasSavedWindowSize(KConfigGroup &config) 0133 { 0134 return config.hasKey(windowWidthString()) || config.hasKey(windowHeightString()) || config.hasKey(screenMaximizedString()); 0135 } 0136 0137 void KWindowConfig::restoreWindowSize(QWindow *window, const KConfigGroup &config) 0138 { 0139 if (!window) { 0140 return; 0141 } 0142 0143 const QString screenName = config.readEntry(windowScreenPositionString(), window->screen()->name()); 0144 0145 const int width = config.readEntry(windowWidthString(), -1); 0146 const int height = config.readEntry(windowHeightString(), -1); 0147 const bool isMaximized = config.readEntry(configFileString(QStringLiteral("Window-Maximized")), false); 0148 0149 // Check default size 0150 const QSize defaultSize(window->property(s_initialSizePropertyName).toSize()); 0151 const QSize defaultScreenSize(window->property(s_initialScreenSizePropertyName).toSize()); 0152 if (!defaultSize.isValid() || !defaultScreenSize.isValid()) { 0153 const QScreen *screen = findScreenByName(window, screenName); 0154 window->setProperty(s_initialSizePropertyName, window->size()); 0155 window->setProperty(s_initialScreenSizePropertyName, screen->geometry().size()); 0156 } 0157 0158 if (width > 0 && height > 0) { 0159 window->resize(width, height); 0160 } 0161 0162 if (isMaximized) { 0163 window->setWindowState(Qt::WindowMaximized); 0164 } 0165 } 0166 0167 void KWindowConfig::saveWindowPosition(const QWindow *window, KConfigGroup &config, KConfigGroup::WriteConfigFlags options) 0168 { 0169 // On Wayland, the compositor is solely responsible for window positioning, 0170 // So this needs to be a no-op 0171 if (!window || QGuiApplication::platformName() == QLatin1String{"wayland"}) { 0172 return; 0173 } 0174 0175 // If the window is maximized, saving the position will only serve to mis-position 0176 // it once de-maximized, so let's not do that 0177 if (window->windowState() & Qt::WindowMaximized) { 0178 return; 0179 } 0180 0181 config.writeEntry(windowXPositionString(), window->x(), options); 0182 config.writeEntry(windowYPositionString(), window->y(), options); 0183 config.writeEntry(windowScreenPositionString(), window->screen()->name(), options); 0184 } 0185 0186 bool KWindowConfig::hasSavedWindowPosition(KConfigGroup &config) 0187 { 0188 // Window position save/restore features outside of the compositor are not 0189 // supported on Wayland 0190 if (QGuiApplication::platformName() == QLatin1String{"wayland"}) { 0191 return false; 0192 } 0193 0194 return config.hasKey(windowXPositionString()) || config.hasKey(windowYPositionString()) || config.hasKey(windowScreenPositionString()); 0195 } 0196 0197 void KWindowConfig::restoreWindowPosition(QWindow *window, const KConfigGroup &config) 0198 { 0199 // On Wayland, the compositor is solely responsible for window positioning, 0200 // So this needs to be a no-op 0201 if (!window || QGuiApplication::platformName() == QLatin1String{"wayland"}) { 0202 return; 0203 } 0204 0205 const bool isMaximized = config.readEntry(configFileString(QStringLiteral("Window-Maximized")), false); 0206 0207 // Don't need to restore position if the window was maximized 0208 if (isMaximized) { 0209 window->setWindowState(Qt::WindowMaximized); 0210 return; 0211 } 0212 0213 // Move window to proper screen 0214 const QScreen *screen = window->screen(); 0215 const QString screenName = config.readEntry(windowScreenPositionString(), screen->name()); 0216 if (screenName != screen->name()) { 0217 QScreen *screenConf = findScreenByName(window, screenName); 0218 window->setScreen(screenConf); 0219 restoreWindowScreenPosition(window, screenConf, config); 0220 return; 0221 } 0222 restoreWindowScreenPosition(window, screen, config); 0223 } 0224 0225 void KWindowConfig::restoreWindowScreenPosition(QWindow *window, const QScreen *screen, const KConfigGroup &config) 0226 { 0227 Q_UNUSED(screen); 0228 const int xPos = config.readEntry(windowXPositionString(), -1); 0229 const int yPos = config.readEntry(windowYPositionString(), -1); 0230 0231 if (xPos == -1 || yPos == -1) { 0232 return; 0233 } 0234 0235 window->setX(xPos); 0236 window->setY(yPos); 0237 }