File indexing completed on 2024-10-13 10:51:49
0001 /* 0002 * SPDX-FileCopyrightText: 2014 Antonis Tsiapaliokas <antonis.tsiapaliokas@kde.org> 0003 * SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "windowutil.h" 0009 0010 #include <QGuiApplication> 0011 0012 constexpr int ACTIVE_WINDOW_UPDATE_INVERVAL = 0; 0013 0014 WindowUtil::WindowUtil(QObject *parent) 0015 : QObject{parent} 0016 , m_activeWindowTimer{new QTimer{this}} 0017 { 0018 // use 0 tick timer to update active window to ensure window state has finished changing 0019 m_activeWindowTimer->setSingleShot(true); 0020 m_activeWindowTimer->setInterval(ACTIVE_WINDOW_UPDATE_INVERVAL); 0021 connect(m_activeWindowTimer, &QTimer::timeout, this, &WindowUtil::updateActiveWindow); 0022 0023 connect(this, &WindowUtil::activeWindowChanged, this, &WindowUtil::updateActiveWindowIsShell); 0024 0025 initWayland(); 0026 } 0027 0028 WindowUtil *WindowUtil::instance() 0029 { 0030 static WindowUtil *inst = new WindowUtil(); 0031 return inst; 0032 } 0033 0034 bool WindowUtil::isShowingDesktop() const 0035 { 0036 return m_showingDesktop; 0037 } 0038 0039 bool WindowUtil::allWindowsMinimized() const 0040 { 0041 return m_allWindowsMinimized; 0042 } 0043 0044 bool WindowUtil::allWindowsMinimizedExcludingShell() const 0045 { 0046 return m_allWindowsMinimizedExcludingShell; 0047 } 0048 0049 bool WindowUtil::activeWindowIsShell() const 0050 { 0051 return m_activeWindowIsShell; 0052 } 0053 0054 void WindowUtil::initWayland() 0055 { 0056 if (!QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) { 0057 qWarning() << "Plasma Mobile must use wayland! The current platform detected is:" << QGuiApplication::platformName(); 0058 return; 0059 } 0060 0061 using namespace KWayland::Client; 0062 ConnectionThread *connection = ConnectionThread::fromApplication(this); 0063 0064 if (!connection) { 0065 return; 0066 } 0067 0068 auto *registry = new Registry(this); 0069 registry->create(connection); 0070 0071 connect(registry, &Registry::plasmaWindowManagementAnnounced, this, [this, registry](quint32 name, quint32 version) { 0072 m_windowManagement = registry->createPlasmaWindowManagement(name, version, this); 0073 qRegisterMetaType<QVector<int>>("QVector<int>"); 0074 0075 connect(m_windowManagement, &KWayland::Client::PlasmaWindowManagement::windowCreated, this, [this](KWayland::Client::PlasmaWindow *window) { 0076 Q_EMIT windowCreated(window); 0077 }); 0078 connect(m_windowManagement, &KWayland::Client::PlasmaWindowManagement::windowCreated, this, &WindowUtil::windowCreatedSlot); 0079 0080 connect(m_windowManagement, &PlasmaWindowManagement::showingDesktopChanged, this, &WindowUtil::updateShowingDesktop); 0081 connect(m_windowManagement, &PlasmaWindowManagement::activeWindowChanged, m_activeWindowTimer, qOverload<>(&QTimer::start)); 0082 0083 m_activeWindowTimer->start(); 0084 }); 0085 0086 registry->setup(); 0087 connection->roundtrip(); 0088 } 0089 0090 void WindowUtil::updateActiveWindow() 0091 { 0092 if (!m_windowManagement || m_activeWindow == m_windowManagement->activeWindow()) { 0093 return; 0094 } 0095 0096 using namespace KWayland::Client; 0097 if (m_activeWindow) { 0098 disconnect(m_activeWindow.data(), &PlasmaWindow::closeableChanged, this, &WindowUtil::hasCloseableActiveWindowChanged); 0099 disconnect(m_activeWindow.data(), &PlasmaWindow::unmapped, this, &WindowUtil::forgetActiveWindow); 0100 } 0101 m_activeWindow = m_windowManagement->activeWindow(); 0102 Q_EMIT activeWindowChanged(); 0103 0104 if (m_activeWindow) { 0105 connect(m_activeWindow.data(), &PlasmaWindow::closeableChanged, this, &WindowUtil::hasCloseableActiveWindowChanged); 0106 connect(m_activeWindow.data(), &PlasmaWindow::unmapped, this, &WindowUtil::forgetActiveWindow); 0107 } 0108 0109 // loop through windows 0110 bool newAllMinimized = true; 0111 bool newAllMinimizedExcludingShell = true; 0112 for (auto *w : m_windowManagement->windows()) { 0113 if (!w->isMinimized() && !w->skipTaskbar() && !w->isFullscreen()) { 0114 newAllMinimized = false; 0115 0116 if (w->appId() != QStringLiteral("org.kde.plasmashell")) { 0117 newAllMinimizedExcludingShell = false; 0118 } 0119 } 0120 } 0121 0122 if (newAllMinimized != m_allWindowsMinimized) { 0123 m_allWindowsMinimized = newAllMinimized; 0124 Q_EMIT allWindowsMinimizedChanged(); 0125 } 0126 if (newAllMinimizedExcludingShell != m_allWindowsMinimizedExcludingShell) { 0127 m_allWindowsMinimizedExcludingShell = newAllMinimizedExcludingShell; 0128 Q_EMIT allWindowsMinimizedExcludingShellChanged(); 0129 } 0130 0131 // TODO: connect to closeableChanged, not needed right now as KWin doesn't provide this changeable 0132 Q_EMIT hasCloseableActiveWindowChanged(); 0133 } 0134 0135 bool WindowUtil::hasCloseableActiveWindow() const 0136 { 0137 return m_activeWindow && m_activeWindow->isCloseable() /*&& !m_activeWindow->isMinimized()*/; 0138 } 0139 0140 void WindowUtil::closeActiveWindow() 0141 { 0142 if (m_activeWindow) { 0143 m_activeWindow->requestClose(); 0144 } 0145 } 0146 0147 void WindowUtil::requestShowingDesktop(bool showingDesktop) 0148 { 0149 if (!m_windowManagement) { 0150 return; 0151 } 0152 m_windowManagement->setShowingDesktop(showingDesktop); 0153 } 0154 0155 void WindowUtil::minimizeAll() 0156 { 0157 if (!m_windowManagement) { 0158 qWarning() << "Ignoring request for minimizing all windows since window management hasn't been announced yet!"; 0159 return; 0160 } 0161 0162 for (auto *w : m_windowManagement->windows()) { 0163 if (!w->isMinimized()) { 0164 w->requestToggleMinimized(); 0165 } 0166 } 0167 } 0168 0169 void WindowUtil::unsetAllMinimizedGeometries(QQuickItem *parent) 0170 { 0171 if (!m_windowManagement) { 0172 qWarning() << "Ignoring request for minimizing all windows since window management hasn't been announced yet!"; 0173 return; 0174 } 0175 0176 if (!parent) { 0177 return; 0178 } 0179 0180 QWindow *window = parent->window(); 0181 if (!window) { 0182 return; 0183 } 0184 0185 KWayland::Client::Surface *surface = KWayland::Client::Surface::fromWindow(window); 0186 if (!surface) { 0187 return; 0188 } 0189 0190 for (auto *w : m_windowManagement->windows()) { 0191 w->unsetMinimizedGeometry(surface); 0192 } 0193 } 0194 0195 void WindowUtil::updateShowingDesktop(bool showing) 0196 { 0197 if (showing != m_showingDesktop) { 0198 m_showingDesktop = showing; 0199 Q_EMIT showingDesktopChanged(m_showingDesktop); 0200 } 0201 } 0202 0203 void WindowUtil::updateActiveWindowIsShell() 0204 { 0205 if (m_activeWindow) { 0206 if (m_activeWindow->appId() == QStringLiteral("org.kde.plasmashell") && !m_activeWindowIsShell) { 0207 m_activeWindowIsShell = true; 0208 Q_EMIT activeWindowIsShellChanged(); 0209 } else if (m_activeWindow->appId() != QStringLiteral("org.kde.plasmashell") && m_activeWindowIsShell) { 0210 m_activeWindowIsShell = false; 0211 Q_EMIT activeWindowIsShellChanged(); 0212 } 0213 } 0214 } 0215 0216 void WindowUtil::forgetActiveWindow() 0217 { 0218 using namespace KWayland::Client; 0219 if (m_activeWindow) { 0220 disconnect(m_activeWindow.data(), &PlasmaWindow::closeableChanged, this, &WindowUtil::hasCloseableActiveWindowChanged); 0221 disconnect(m_activeWindow.data(), &PlasmaWindow::unmapped, this, &WindowUtil::forgetActiveWindow); 0222 } 0223 m_activeWindow.clear(); 0224 Q_EMIT hasCloseableActiveWindowChanged(); 0225 } 0226 0227 QList<KWayland::Client::PlasmaWindow *> WindowUtil::windowsFromStorageId(const QString &storageId) const 0228 { 0229 if (!m_windows.contains(storageId)) { 0230 return {}; 0231 } 0232 return m_windows[storageId]; 0233 } 0234 0235 void WindowUtil::windowCreatedSlot(KWayland::Client::PlasmaWindow *window) 0236 { 0237 QString storageId = window->appId() + QStringLiteral(".desktop"); 0238 0239 // ignore empty windows 0240 if (storageId == ".desktop" || storageId == "org.kde.plasmashell.desktop") { 0241 return; 0242 } 0243 0244 if (!m_windows.contains(storageId)) { 0245 m_windows[storageId] = {}; 0246 } 0247 m_windows[storageId].push_back(window); 0248 0249 // listen for window close 0250 connect(window, &KWayland::Client::PlasmaWindow::unmapped, this, [this, storageId]() { 0251 m_windows.remove(storageId); 0252 Q_EMIT windowChanged(storageId); 0253 }); 0254 0255 Q_EMIT windowChanged(storageId); 0256 }