File indexing completed on 2024-04-28 05:30:27

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2022 Xaver Hugl <xaver.hugl@gmail.com>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "placementtracker.h"
0010 #include "core/output.h"
0011 #include "window.h"
0012 #include "workspace.h"
0013 
0014 namespace KWin
0015 {
0016 
0017 PlacementTracker::PlacementTracker(Workspace *workspace)
0018     : m_workspace(workspace)
0019 {
0020 }
0021 
0022 PlacementTracker::WindowData PlacementTracker::dataForWindow(Window *window) const
0023 {
0024     return WindowData{
0025         .outputUuid = window->moveResizeOutput()->uuid(),
0026         .geometry = window->moveResizeGeometry(),
0027         .maximize = window->requestedMaximizeMode(),
0028         .quickTile = window->quickTileMode(),
0029         .geometryRestore = window->geometryRestore(),
0030         .fullscreen = window->isFullScreen(),
0031         .fullscreenGeometryRestore = window->fullscreenGeometryRestore(),
0032         .interactiveMoveResizeCount = window->interactiveMoveResizeCount(),
0033     };
0034 }
0035 
0036 void PlacementTracker::add(Window *window)
0037 {
0038     if (window->isUnmanaged() || window->isAppletPopup() || window->isSpecialWindow()) {
0039         return;
0040     }
0041     connect(window, &Window::frameGeometryChanged, this, [this, window]() {
0042         saveGeometry(window);
0043     });
0044     connect(window, &Window::maximizedChanged, this, [this, window]() {
0045         saveMaximize(window);
0046     });
0047     connect(window, &Window::quickTileModeChanged, this, [this, window]() {
0048         saveQuickTile(window);
0049     });
0050     connect(window, &Window::fullScreenChanged, this, [this, window]() {
0051         saveFullscreen(window);
0052     });
0053     connect(window, &Window::interactiveMoveResizeFinished, this, [this, window]() {
0054         saveInteractionCounter(window);
0055     });
0056     WindowData data = dataForWindow(window);
0057     m_data[m_currentKey][window] = data;
0058     m_savedWindows.push_back(window);
0059 }
0060 
0061 void PlacementTracker::remove(Window *window)
0062 {
0063     if (m_savedWindows.contains(window)) {
0064         disconnect(window, nullptr, this, nullptr);
0065         for (auto &dataMap : m_data) {
0066             dataMap.remove(window);
0067         }
0068         m_savedWindows.removeOne(window);
0069     }
0070 }
0071 
0072 void PlacementTracker::restore(const QString &key)
0073 {
0074     if (key == m_currentKey) {
0075         return;
0076     }
0077     auto &dataMap = m_data[key];
0078     auto &oldDataMap = m_data[m_currentKey];
0079     const auto outputs = m_workspace->outputs();
0080 
0081     inhibit();
0082     for (const auto window : std::as_const(m_savedWindows)) {
0083         const auto it = dataMap.find(window);
0084         if (it != dataMap.end()) {
0085             const WindowData &newData = it.value();
0086 
0087             // don't touch windows where the user intentionally changed their state
0088             bool restore = window->interactiveMoveResizeCount() == newData.interactiveMoveResizeCount
0089                 && window->requestedMaximizeMode() == newData.maximize
0090                 && window->quickTileMode() == newData.quickTile
0091                 && window->isFullScreen() == newData.fullscreen;
0092             if (!restore) {
0093                 // the logic above can have false negatives if PlacementTracker changed the window state
0094                 // to prevent that, also restore if the window still has the same state from that
0095                 if (const auto it = m_lastRestoreData.find(window); it != m_lastRestoreData.end()) {
0096                     restore = window->interactiveMoveResizeCount() == it->interactiveMoveResizeCount
0097                         && window->requestedMaximizeMode() == it->maximize
0098                         && window->quickTileMode() == it->quickTile
0099                         && window->isFullScreen() == it->fullscreen
0100                         && window->moveResizeOutput()->uuid() == it->outputUuid;
0101                 }
0102             }
0103             if (!restore) {
0104                 // restore anyways if the output the window was on got removed
0105                 if (const auto oldData = oldDataMap.find(window); oldData != oldDataMap.end()) {
0106                     restore = std::none_of(outputs.begin(), outputs.end(), [&oldData](const auto output) {
0107                         return output->uuid() == oldData->outputUuid;
0108                     });
0109                 }
0110             }
0111             if (restore) {
0112                 window->setFullScreen(false);
0113                 window->setQuickTileMode(QuickTileFlag::None, true);
0114                 window->setMaximize(false, false);
0115                 if (newData.quickTile || newData.maximize) {
0116                     window->moveResize(newData.geometryRestore);
0117                     window->setQuickTileMode(newData.quickTile, true);
0118                     window->setMaximize(newData.maximize & MaximizeMode::MaximizeVertical, newData.maximize & MaximizeMode::MaximizeHorizontal);
0119                 }
0120                 if (newData.fullscreen) {
0121                     window->moveResize(newData.fullscreenGeometryRestore);
0122                     window->setFullScreen(newData.fullscreen);
0123                 }
0124                 if (newData.quickTile || newData.maximize || newData.fullscreen) {
0125                     // restore geometry isn't necessarily on the output the window was, so explicitly restore it
0126                     const auto outputIt = std::find_if(outputs.begin(), outputs.end(), [&newData](const auto output) {
0127                         return output->uuid() == newData.outputUuid;
0128                     });
0129                     if (outputIt != outputs.end()) {
0130                         window->sendToOutput(*outputIt);
0131                     }
0132                 } else {
0133                     window->moveResize(newData.geometry);
0134                 }
0135                 m_lastRestoreData[window] = dataForWindow(window);
0136             }
0137         }
0138         // ensure data in current map is always up to date
0139         dataMap[window] = dataForWindow(window);
0140     }
0141     uninhibit();
0142     m_currentKey = key;
0143 }
0144 
0145 void PlacementTracker::init(const QString &key)
0146 {
0147     m_currentKey = key;
0148 }
0149 
0150 void PlacementTracker::saveGeometry(Window *window)
0151 {
0152     if (m_inhibitCount == 0) {
0153         auto &data = m_data[m_currentKey][window];
0154         data.geometry = window->moveResizeGeometry();
0155         data.outputUuid = window->moveResizeOutput()->uuid();
0156     }
0157 }
0158 
0159 void PlacementTracker::saveInteractionCounter(Window *window)
0160 {
0161     if (m_inhibitCount == 0) {
0162         m_data[m_currentKey][window].interactiveMoveResizeCount = window->interactiveMoveResizeCount();
0163     }
0164 }
0165 
0166 void PlacementTracker::saveMaximize(Window *window)
0167 {
0168     if (m_inhibitCount == 0) {
0169         auto &data = m_data[m_currentKey][window];
0170         data.maximize = window->maximizeMode();
0171         data.geometryRestore = window->geometryRestore();
0172     }
0173 }
0174 
0175 void PlacementTracker::saveQuickTile(Window *window)
0176 {
0177     if (m_inhibitCount == 0) {
0178         auto &data = m_data[m_currentKey][window];
0179         data.quickTile = window->quickTileMode();
0180         data.geometryRestore = window->geometryRestore();
0181     }
0182 }
0183 
0184 void PlacementTracker::saveFullscreen(Window *window)
0185 {
0186     if (m_inhibitCount == 0) {
0187         auto &data = m_data[m_currentKey][window];
0188         data.fullscreen = window->isFullScreen();
0189         data.fullscreenGeometryRestore = window->fullscreenGeometryRestore();
0190     }
0191 }
0192 
0193 void PlacementTracker::inhibit()
0194 {
0195     m_inhibitCount++;
0196 }
0197 
0198 void PlacementTracker::uninhibit()
0199 {
0200     Q_ASSERT(m_inhibitCount > 0);
0201     m_inhibitCount--;
0202 }
0203 }
0204 
0205 #include "moc_placementtracker.cpp"