File indexing completed on 2024-04-28 05:31:06

0001 /*
0002     SPDX-FileCopyrightText: 2020 Michail Vourlakos <mvourlakos@gmail.com>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "subwindow.h"
0007 
0008 // local
0009 #include "../view.h"
0010 #include "../visibilitymanager.h"
0011 
0012 // Qt
0013 #include <QDebug>
0014 #include <QSurfaceFormat>
0015 #include <QQuickView>
0016 #include <QTimer>
0017 
0018 // KDE
0019 #include <KWayland/Client/plasmashell.h>
0020 #include <KWayland/Client/surface.h>
0021 #include <KWindowSystem>
0022 
0023 // X11
0024 #include <NETWM>
0025 
0026 namespace Latte {
0027 namespace ViewPart {
0028 
0029 SubWindow::SubWindow(Latte::View *view, QString debugType) :
0030     m_latteView(view)
0031 {
0032     m_corona = qobject_cast<Latte::Corona *>(view->corona());
0033 
0034     m_debugMode = (qApp->arguments().contains("-d") && qApp->arguments().contains("--kwinedges"));
0035     m_debugType = debugType;
0036 
0037     m_showColor = QColor(Qt::transparent);
0038     m_hideColor = QColor(Qt::transparent);
0039 
0040     setTitle(validTitle());
0041     setColor(m_showColor);
0042     setDefaultAlphaBuffer(true);
0043 
0044     setFlags(Qt::FramelessWindowHint
0045              | Qt::WindowStaysOnTopHint
0046              | Qt::NoDropShadowWindowHint
0047              | Qt::WindowDoesNotAcceptFocus);
0048 
0049     m_fixGeometryTimer.setSingleShot(true);
0050     m_fixGeometryTimer.setInterval(500);
0051     connect(&m_fixGeometryTimer, &QTimer::timeout, this, &SubWindow::fixGeometry);
0052 
0053     connect(this, &QQuickView::xChanged, this, &SubWindow::startGeometryTimer);
0054     connect(this, &QQuickView::yChanged, this, &SubWindow::startGeometryTimer);
0055     connect(this, &QQuickView::widthChanged, this, &SubWindow::startGeometryTimer);
0056     connect(this, &QQuickView::heightChanged, this, &SubWindow::startGeometryTimer);
0057 
0058     connect(this, &SubWindow::calculatedGeometryChanged, this, &SubWindow::fixGeometry);
0059 
0060     connect(m_latteView, &Latte::View::absoluteGeometryChanged, this, &SubWindow::updateGeometry);
0061     connect(m_latteView, &Latte::View::screenGeometryChanged, this, &SubWindow::updateGeometry);
0062     connect(m_latteView, &Latte::View::locationChanged, this, &SubWindow::updateGeometry);
0063     connect(m_latteView, &QQuickView::screenChanged, this, [this]() {
0064         setScreen(m_latteView->screen());
0065         updateGeometry();
0066     });
0067 
0068     if (!KWindowSystem::isPlatformWayland()) {
0069         //! IMPORTANT!!! ::: This fixes a bug when closing an Activity all views from all Activities are
0070         //!  disappearing! With this code parts they reappear!!!
0071         m_visibleHackTimer1.setInterval(400);
0072         m_visibleHackTimer2.setInterval(2500);
0073         m_visibleHackTimer1.setSingleShot(true);
0074         m_visibleHackTimer2.setSingleShot(true);
0075 
0076         connectionsHack << connect(this, &QWindow::visibleChanged, this, [&]() {
0077             if (!m_inDelete && m_latteView && m_latteView->layout() && !isVisible()) {
0078                 m_visibleHackTimer1.start();
0079                 m_visibleHackTimer2.start();
0080             } else if (!m_inDelete) {
0081                 //! For some reason when the window is hidden in the edge under X11 afterwards
0082                 //! is losing its window flags
0083                 m_corona->wm()->setViewExtraFlags(this);
0084             }
0085         });
0086 
0087         connectionsHack << connect(&m_visibleHackTimer1, &QTimer::timeout, this, [&]() {
0088             if (!m_inDelete && m_latteView && m_latteView->layout() && !isVisible()) {
0089                 show();
0090                 emit forcedShown();
0091                 //qDebug() << m_debugType + ":: Enforce reshow from timer 1...";
0092             } else {
0093                 //qDebug() << m_debugType + ":: No needed reshow from timer 1...";
0094             }
0095         });
0096 
0097         connectionsHack << connect(&m_visibleHackTimer2, &QTimer::timeout, this, [&]() {
0098             if (!m_inDelete && m_latteView && m_latteView->layout() && !isVisible()) {
0099                 show();
0100                 emit forcedShown();
0101                 //qDebug() << m_debugType + ":: Enforce reshow from timer 2...";
0102             } else {
0103                 //qDebug() << m_debugType + ":: No needed reshow from timer 2...";
0104             }
0105         });
0106 
0107         connectionsHack << connect(this, &SubWindow::forcedShown, this, [&]() {
0108             m_corona->wm()->unregisterIgnoredWindow(m_trackedWindowId);
0109             m_trackedWindowId = winId();
0110             m_corona->wm()->registerIgnoredWindow(m_trackedWindowId);
0111         });
0112     }
0113 
0114     setupWaylandIntegration();
0115 
0116     if (KWindowSystem::isPlatformX11()) {
0117         m_trackedWindowId = winId();
0118         m_corona->wm()->registerIgnoredWindow(m_trackedWindowId);
0119     } else {
0120         connect(m_corona->wm(), &WindowSystem::AbstractWindowInterface::latteWindowAdded, this, &SubWindow::updateWaylandId);
0121     }
0122 
0123     setScreen(m_latteView->screen());
0124     show();
0125     hideWithMask();
0126 }
0127 
0128 SubWindow::~SubWindow()
0129 {
0130     m_inDelete = true;
0131 
0132     m_corona->wm()->unregisterIgnoredWindow(KWindowSystem::isPlatformX11() ? winId() : m_trackedWindowId);
0133 
0134     m_latteView = nullptr;
0135 
0136     // clear mode
0137     m_visibleHackTimer1.stop();
0138     m_visibleHackTimer2.stop();
0139     for (auto &c : connectionsHack) {
0140         disconnect(c);
0141     }
0142 
0143     if (m_shellSurface) {
0144         delete m_shellSurface;
0145     }
0146 }
0147 
0148 int SubWindow::location()
0149 {
0150     return (int)m_latteView->location();
0151 }
0152 
0153 int SubWindow::thickness() const
0154 {
0155     return m_thickness;
0156 }
0157 
0158 QString SubWindow::validTitlePrefix() const
0159 {
0160     return QString("#subwindow#");
0161 }
0162 
0163 QString SubWindow::validTitle() const
0164 {
0165     return QString(validTitlePrefix() + QString::number(m_latteView->containment()->id()));
0166 }
0167 
0168 Latte::View *SubWindow::parentView()
0169 {
0170     return m_latteView;
0171 }
0172 
0173 Latte::WindowSystem::WindowId SubWindow::trackedWindowId()
0174 {
0175     if (KWindowSystem::isPlatformWayland() && m_trackedWindowId.toInt() <= 0) {
0176         updateWaylandId();
0177     }
0178 
0179     return m_trackedWindowId;
0180 }
0181 
0182 KWayland::Client::PlasmaShellSurface *SubWindow::surface()
0183 {
0184     return m_shellSurface;
0185 }
0186 
0187 void SubWindow::fixGeometry()
0188 {
0189     if (!m_calculatedGeometry.isEmpty()
0190             && (m_calculatedGeometry.x() != x() || m_calculatedGeometry.y() != y()
0191                 || m_calculatedGeometry.width() != width() || m_calculatedGeometry.height() != height())) {
0192         setMinimumSize(m_calculatedGeometry.size());
0193         setMaximumSize(m_calculatedGeometry.size());
0194         resize(m_calculatedGeometry.size());
0195         setPosition(m_calculatedGeometry.x(), m_calculatedGeometry.y());
0196 
0197         if (m_shellSurface) {
0198             m_shellSurface->setPosition(m_calculatedGeometry.topLeft());
0199         }
0200     }
0201 }
0202 
0203 void SubWindow::updateWaylandId()
0204 {
0205     Latte::WindowSystem::WindowId newId = m_corona->wm()->winIdFor("latte-dock", validTitle());
0206 
0207     if (m_trackedWindowId != newId) {
0208         if (!m_trackedWindowId.isNull()) {
0209             m_corona->wm()->unregisterIgnoredWindow(m_trackedWindowId);
0210         }
0211 
0212         m_trackedWindowId = newId;
0213         m_corona->wm()->registerIgnoredWindow(m_trackedWindowId);
0214     }
0215 }
0216 
0217 void SubWindow::startGeometryTimer()
0218 {
0219     m_fixGeometryTimer.start();
0220 }
0221 
0222 void SubWindow::setupWaylandIntegration()
0223 {
0224     if (m_shellSurface || !KWindowSystem::isPlatformWayland() || !m_latteView || !m_latteView->containment()) {
0225         // already setup
0226         return;
0227     }
0228 
0229     if (m_corona) {
0230         using namespace KWayland::Client;
0231 
0232         PlasmaShell *interface = m_corona->waylandCoronaInterface();
0233 
0234         if (!interface) {
0235             return;
0236         }
0237 
0238         Surface *s = Surface::fromWindow(this);
0239 
0240         if (!s) {
0241             return;
0242         }
0243 
0244         qDebug() << "wayland screen edge ghost window surface was created...";
0245         m_shellSurface = interface->createSurface(s, this);
0246         m_corona->wm()->setViewExtraFlags(m_shellSurface);
0247 
0248         m_shellSurface->setPanelTakesFocus(false);
0249     }
0250 }
0251 
0252 bool SubWindow::event(QEvent *e)
0253 {
0254     if (e->type() == QEvent::Show) {
0255         m_corona->wm()->setViewExtraFlags(this);
0256     }
0257 
0258     return QQuickView::event(e);
0259 }
0260 
0261 
0262 void SubWindow::hideWithMask()
0263 {
0264     if (m_debugMode) {
0265         qDebug() << m_debugType + " :: MASK HIDE...";
0266     }
0267 
0268     setMask(VisibilityManager::ISHIDDENMASK);
0269 
0270     //! repaint in order to update mask immediately
0271     setColor(m_hideColor);
0272 }
0273 
0274 void SubWindow::showWithMask()
0275 {
0276     if (m_debugMode) {
0277         qDebug() << m_debugType + " :: MASK SHOW...";
0278     }
0279 
0280     setMask(QRegion());
0281 
0282     //! repaint in order to update mask immediately
0283     setColor(m_showColor);
0284 }
0285 
0286 }
0287 }