/* KWin - the KDE window manager This file is part of the KDE project. SPDX-FileCopyrightText: 2022 Xaver Hugl SPDX-License-Identifier: GPL-2.0-or-later */ #include "placementtracker.h" #include "core/output.h" #include "window.h" #include "workspace.h" namespace KWin { PlacementTracker::PlacementTracker(Workspace *workspace) : m_workspace(workspace) { } PlacementTracker::WindowData PlacementTracker::dataForWindow(Window *window) const { return WindowData{ .outputUuid = window->moveResizeOutput()->uuid(), .geometry = window->moveResizeGeometry(), .maximize = window->requestedMaximizeMode(), .quickTile = window->requestedQuickTileMode(), .geometryRestore = window->geometryRestore(), .fullscreen = window->isRequestedFullScreen(), .fullscreenGeometryRestore = window->fullscreenGeometryRestore(), .interactiveMoveResizeCount = window->interactiveMoveResizeCount(), }; } void PlacementTracker::add(Window *window) { if (window->isUnmanaged() || window->isAppletPopup() || window->isSpecialWindow()) { return; } connect(window, &Window::frameGeometryChanged, this, [this, window]() { saveGeometry(window); }); connect(window, &Window::maximizedChanged, this, [this, window]() { saveMaximize(window); }); connect(window, &Window::quickTileModeChanged, this, [this, window]() { saveQuickTile(window); }); connect(window, &Window::fullScreenChanged, this, [this, window]() { saveFullscreen(window); }); connect(window, &Window::interactiveMoveResizeFinished, this, [this, window]() { saveInteractionCounter(window); }); connect(window, &Window::maximizeGeometryRestoreChanged, this, [this, window]() { saveMaximizeGeometryRestore(window); }); connect(window, &Window::fullscreenGeometryRestoreChanged, this, [this, window]() { saveFullscreenGeometryRestore(window); }); WindowData data = dataForWindow(window); m_data[m_currentKey][window] = data; m_savedWindows.push_back(window); } void PlacementTracker::remove(Window *window) { if (m_savedWindows.contains(window)) { disconnect(window, nullptr, this, nullptr); for (auto &dataMap : m_data) { dataMap.remove(window); } m_savedWindows.removeOne(window); } } void PlacementTracker::restore(const QString &key) { if (key == m_currentKey) { return; } auto &dataMap = m_data[key]; auto &oldDataMap = m_data[m_currentKey]; const auto outputs = m_workspace->outputs(); inhibit(); for (const auto window : std::as_const(m_savedWindows)) { const auto it = dataMap.find(window); if (it != dataMap.end()) { const WindowData &newData = it.value(); // don't touch windows where the user intentionally changed their state bool restore = window->interactiveMoveResizeCount() == newData.interactiveMoveResizeCount && window->requestedMaximizeMode() == newData.maximize && window->requestedQuickTileMode() == newData.quickTile && window->isFullScreen() == newData.fullscreen; if (!restore) { // the logic above can have false negatives if PlacementTracker changed the window state // to prevent that, also restore if the window still has the same state from that if (const auto it = m_lastRestoreData.find(window); it != m_lastRestoreData.end()) { restore = window->interactiveMoveResizeCount() == it->interactiveMoveResizeCount && window->requestedMaximizeMode() == it->maximize && window->requestedQuickTileMode() == it->quickTile && window->isFullScreen() == it->fullscreen && window->moveResizeOutput()->uuid() == it->outputUuid; } } if (!restore) { // restore anyways if the output the window was on got removed if (const auto oldData = oldDataMap.find(window); oldData != oldDataMap.end()) { restore = std::none_of(outputs.begin(), outputs.end(), [&oldData](const auto output) { return output->uuid() == oldData->outputUuid; }); } } if (restore) { window->setQuickTileMode(newData.quickTile, newData.geometry.center()); window->setMaximize(newData.maximize & MaximizeMode::MaximizeVertical, newData.maximize & MaximizeMode::MaximizeHorizontal); window->setFullScreen(newData.fullscreen); window->moveResize(newData.geometry); window->setGeometryRestore(newData.geometryRestore); window->setFullscreenGeometryRestore(newData.fullscreenGeometryRestore); m_lastRestoreData[window] = dataForWindow(window); } } // ensure data in current map is always up to date dataMap[window] = dataForWindow(window); } uninhibit(); m_currentKey = key; } void PlacementTracker::init(const QString &key) { m_currentKey = key; } void PlacementTracker::saveGeometry(Window *window) { if (m_inhibitCount == 0) { auto &data = m_data[m_currentKey][window]; data.geometry = window->moveResizeGeometry(); data.outputUuid = window->moveResizeOutput()->uuid(); } } void PlacementTracker::saveInteractionCounter(Window *window) { if (m_inhibitCount == 0) { m_data[m_currentKey][window].interactiveMoveResizeCount = window->interactiveMoveResizeCount(); } } void PlacementTracker::saveMaximize(Window *window) { if (m_inhibitCount == 0) { auto &data = m_data[m_currentKey][window]; data.maximize = window->maximizeMode(); } } void PlacementTracker::saveQuickTile(Window *window) { if (m_inhibitCount == 0) { auto &data = m_data[m_currentKey][window]; data.quickTile = window->quickTileMode(); } } void PlacementTracker::saveFullscreen(Window *window) { if (m_inhibitCount == 0) { auto &data = m_data[m_currentKey][window]; data.fullscreen = window->isFullScreen(); } } void PlacementTracker::saveMaximizeGeometryRestore(Window *window) { if (m_inhibitCount == 0) { auto &data = m_data[m_currentKey][window]; data.geometryRestore = window->geometryRestore(); } } void PlacementTracker::saveFullscreenGeometryRestore(Window *window) { if (m_inhibitCount == 0) { auto &data = m_data[m_currentKey][window]; data.fullscreenGeometryRestore = window->fullscreenGeometryRestore(); } } void PlacementTracker::inhibit() { m_inhibitCount++; } void PlacementTracker::uninhibit() { Q_ASSERT(m_inhibitCount > 0); m_inhibitCount--; } } #include "moc_placementtracker.cpp"