File indexing completed on 2025-04-20 05:01:04
0001 /* 0002 SPDX-FileCopyrightText: 2016 Smith AR <audoban@openmailbox.org> 0003 SPDX-FileCopyrightText: 2016 Michail Vourlakos <mvourlakos@gmail.com> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "waylandinterface.h" 0009 0010 // local 0011 #include <coretypes.h> 0012 #include "../view/positioner.h" 0013 #include "../view/view.h" 0014 #include "../view/settings/subconfigview.h" 0015 #include "../view/helpers/screenedgeghostwindow.h" 0016 #include "../lattecorona.h" 0017 0018 // Qt 0019 #include <QDebug> 0020 #include <QTimer> 0021 #include <QApplication> 0022 #include <QtX11Extras/QX11Info> 0023 #include <QQuickView> 0024 #include <QLatin1String> 0025 0026 // KDE 0027 #include <KWindowSystem> 0028 #include <KWindowInfo> 0029 #include <KWayland/Client/surface.h> 0030 0031 #include <KWayland/Client/plasmavirtualdesktop.h> 0032 0033 0034 // X11 0035 #include <NETWM> 0036 0037 using namespace KWayland::Client; 0038 0039 namespace Latte { 0040 0041 class Private::GhostWindow : public QQuickView 0042 { 0043 Q_OBJECT 0044 0045 public: 0046 WindowSystem::WindowId m_winId; 0047 0048 GhostWindow(WindowSystem::WaylandInterface *waylandInterface) 0049 : m_waylandInterface(waylandInterface) { 0050 setFlags(Qt::FramelessWindowHint 0051 | Qt::WindowStaysOnTopHint 0052 | Qt::NoDropShadowWindowHint 0053 | Qt::WindowDoesNotAcceptFocus); 0054 0055 setColor(QColor(Qt::transparent)); 0056 setClearBeforeRendering(true); 0057 0058 connect(m_waylandInterface, &WindowSystem::AbstractWindowInterface::latteWindowAdded, this, &GhostWindow::identifyWinId); 0059 0060 setupWaylandIntegration(); 0061 show(); 0062 } 0063 0064 ~GhostWindow() { 0065 m_waylandInterface->unregisterIgnoredWindow(m_winId); 0066 delete m_shellSurface; 0067 } 0068 0069 void setGeometry(const QRect &rect) { 0070 if (geometry() == rect) { 0071 return; 0072 } 0073 0074 m_validGeometry = rect; 0075 0076 setMinimumSize(rect.size()); 0077 setMaximumSize(rect.size()); 0078 resize(rect.size()); 0079 0080 m_shellSurface->setPosition(rect.topLeft()); 0081 } 0082 0083 void setupWaylandIntegration() { 0084 using namespace KWayland::Client; 0085 0086 if (m_shellSurface) 0087 return; 0088 0089 Surface *s{Surface::fromWindow(this)}; 0090 0091 if (!s) 0092 return; 0093 0094 m_shellSurface = m_waylandInterface->waylandCoronaInterface()->createSurface(s, this); 0095 qDebug() << "wayland ghost window surface was created..."; 0096 0097 m_shellSurface->setSkipTaskbar(true); 0098 m_shellSurface->setPanelTakesFocus(false); 0099 m_shellSurface->setRole(PlasmaShellSurface::Role::Panel); 0100 m_shellSurface->setPanelBehavior(PlasmaShellSurface::PanelBehavior::AlwaysVisible); 0101 } 0102 0103 KWayland::Client::PlasmaShellSurface *m_shellSurface{nullptr}; 0104 WindowSystem::WaylandInterface *m_waylandInterface{nullptr}; 0105 0106 //! geometry() function under wayland does not return nice results 0107 QRect m_validGeometry; 0108 0109 public slots: 0110 void identifyWinId() { 0111 if (m_winId.isNull()) { 0112 m_winId = m_waylandInterface->winIdFor("latte-dock", m_validGeometry); 0113 m_waylandInterface->registerIgnoredWindow(m_winId); 0114 } 0115 } 0116 }; 0117 0118 namespace WindowSystem { 0119 0120 WaylandInterface::WaylandInterface(QObject *parent) 0121 : AbstractWindowInterface(parent) 0122 { 0123 m_corona = qobject_cast<Latte::Corona *>(parent); 0124 } 0125 0126 WaylandInterface::~WaylandInterface() 0127 { 0128 } 0129 0130 void WaylandInterface::init() 0131 { 0132 } 0133 0134 void WaylandInterface::initWindowManagement(KWayland::Client::PlasmaWindowManagement *windowManagement) 0135 { 0136 if (m_windowManagement == windowManagement) { 0137 return; 0138 } 0139 0140 m_windowManagement = windowManagement; 0141 0142 connect(m_windowManagement, &PlasmaWindowManagement::windowCreated, this, &WaylandInterface::windowCreatedProxy); 0143 connect(m_windowManagement, &PlasmaWindowManagement::activeWindowChanged, this, [&]() noexcept { 0144 auto w = m_windowManagement->activeWindow(); 0145 if (!w || (w && (!m_ignoredWindows.contains(w->internalId()))) ) { 0146 emit activeWindowChanged(w ? w->internalId() : 0); 0147 } 0148 0149 }, Qt::QueuedConnection); 0150 } 0151 0152 void WaylandInterface::initVirtualDesktopManagement(KWayland::Client::PlasmaVirtualDesktopManagement *virtualDesktopManagement) 0153 { 0154 if (m_virtualDesktopManagement == virtualDesktopManagement) { 0155 return; 0156 } 0157 0158 m_virtualDesktopManagement = virtualDesktopManagement; 0159 0160 connect(m_virtualDesktopManagement, &KWayland::Client::PlasmaVirtualDesktopManagement::desktopCreated, this, 0161 [this](const QString &id, quint32 position) { 0162 addDesktop(id, position); 0163 }); 0164 0165 connect(m_virtualDesktopManagement, &KWayland::Client::PlasmaVirtualDesktopManagement::desktopRemoved, this, 0166 [this](const QString &id) { 0167 m_desktops.removeAll(id); 0168 0169 if (m_currentDesktop == id) { 0170 setCurrentDesktop(QString()); 0171 } 0172 }); 0173 } 0174 0175 void WaylandInterface::addDesktop(const QString &id, quint32 position) 0176 { 0177 if (m_desktops.contains(id)) { 0178 return; 0179 } 0180 0181 m_desktops.append(id); 0182 0183 const KWayland::Client::PlasmaVirtualDesktop *desktop = m_virtualDesktopManagement->getVirtualDesktop(id); 0184 0185 QObject::connect(desktop, &KWayland::Client::PlasmaVirtualDesktop::activated, this, 0186 [desktop, this]() { 0187 setCurrentDesktop(desktop->id()); 0188 } 0189 ); 0190 0191 if (desktop->isActive()) { 0192 setCurrentDesktop(id); 0193 } 0194 } 0195 0196 void WaylandInterface::setCurrentDesktop(QString desktop) 0197 { 0198 if (m_currentDesktop == desktop) { 0199 return; 0200 } 0201 0202 m_currentDesktop = desktop; 0203 emit currentDesktopChanged(); 0204 } 0205 0206 KWayland::Client::PlasmaShell *WaylandInterface::waylandCoronaInterface() const 0207 { 0208 return m_corona->waylandCoronaInterface(); 0209 } 0210 0211 //! Register Latte Ignored Windows in order to NOT be tracked 0212 void WaylandInterface::registerIgnoredWindow(WindowId wid) 0213 { 0214 if (!wid.isNull() && !m_ignoredWindows.contains(wid)) { 0215 m_ignoredWindows.append(wid); 0216 0217 KWayland::Client::PlasmaWindow *w = windowFor(wid); 0218 0219 if (w) { 0220 untrackWindow(w); 0221 } 0222 0223 emit windowChanged(wid); 0224 } 0225 } 0226 0227 void WaylandInterface::unregisterIgnoredWindow(WindowId wid) 0228 { 0229 if (m_ignoredWindows.contains(wid)) { 0230 m_ignoredWindows.removeAll(wid); 0231 emit windowRemoved(wid); 0232 } 0233 } 0234 0235 void WaylandInterface::setViewExtraFlags(QObject *view, bool isPanelWindow, Latte::Types::Visibility mode) 0236 { 0237 KWayland::Client::PlasmaShellSurface *surface = qobject_cast<KWayland::Client::PlasmaShellSurface *>(view); 0238 Latte::View *latteView = qobject_cast<Latte::View *>(view); 0239 Latte::ViewPart::SubConfigView *configView = qobject_cast<Latte::ViewPart::SubConfigView *>(view); 0240 0241 WindowId winId; 0242 0243 if (latteView) { 0244 surface = latteView->surface(); 0245 winId = latteView->positioner()->trackedWindowId(); 0246 } else if (configView) { 0247 surface = configView->surface(); 0248 winId = configView->trackedWindowId(); 0249 } 0250 0251 if (!surface) { 0252 return; 0253 } 0254 0255 surface->setSkipTaskbar(true); 0256 surface->setSkipSwitcher(true); 0257 0258 bool atBottom{!isPanelWindow && (mode == Latte::Types::WindowsCanCover || mode == Latte::Types::WindowsAlwaysCover)}; 0259 0260 if (isPanelWindow) { 0261 surface->setRole(PlasmaShellSurface::Role::Panel); 0262 surface->setPanelBehavior(PlasmaShellSurface::PanelBehavior::AutoHide); 0263 } else { 0264 surface->setRole(PlasmaShellSurface::Role::Normal); 0265 } 0266 0267 if (latteView || configView) { 0268 auto w = windowFor(winId); 0269 if (w && !w->isOnAllDesktops()) { 0270 requestToggleIsOnAllDesktops(winId); 0271 } 0272 0273 //! Layer to be applied 0274 if (mode == Latte::Types::WindowsCanCover || mode == Latte::Types::WindowsAlwaysCover) { 0275 setKeepBelow(winId, true); 0276 } else if (mode == Latte::Types::NormalWindow) { 0277 setKeepBelow(winId, false); 0278 setKeepAbove(winId, false); 0279 } else { 0280 setKeepAbove(winId, true); 0281 } 0282 } 0283 0284 if (atBottom){ 0285 //! trying to workaround WM behavior in order 0286 //! 1. View at the end MUST NOT HAVE FOCUSABILITY (issue example: clicking a single active task is not minimized) 0287 //! 2. View at the end MUST BE AT THE BOTTOM of windows stack 0288 0289 QTimer::singleShot(50, [this, surface]() { 0290 surface->setRole(PlasmaShellSurface::Role::ToolTip); 0291 }); 0292 } 0293 } 0294 0295 void WaylandInterface::setViewStruts(QWindow &view, const QRect &rect, Plasma::Types::Location location) 0296 { 0297 if (!m_ghostWindows.contains(view.winId())) { 0298 m_ghostWindows[view.winId()] = new Private::GhostWindow(this); 0299 } 0300 0301 auto w = m_ghostWindows[view.winId()]; 0302 0303 switch (location) { 0304 case Plasma::Types::TopEdge: 0305 case Plasma::Types::BottomEdge: 0306 w->setGeometry({rect.x() + rect.width() / 2 - rect.height(), rect.y(), rect.height() + 1, rect.height()}); 0307 break; 0308 0309 case Plasma::Types::LeftEdge: 0310 case Plasma::Types::RightEdge: 0311 w->setGeometry({rect.x(), rect.y() + rect.height() / 2 - rect.width(), rect.width(), rect.width() + 1}); 0312 break; 0313 0314 default: 0315 break; 0316 } 0317 } 0318 0319 void WaylandInterface::switchToNextVirtualDesktop() 0320 { 0321 if (!m_virtualDesktopManagement || m_desktops.count() <= 1) { 0322 return; 0323 } 0324 0325 int curPos = m_desktops.indexOf(m_currentDesktop); 0326 int nextPos = curPos + 1; 0327 0328 if (curPos >= m_desktops.count()-1) { 0329 if (isVirtualDesktopNavigationWrappingAround()) { 0330 nextPos = 0; 0331 } else { 0332 return; 0333 } 0334 } 0335 0336 KWayland::Client::PlasmaVirtualDesktop *desktopObj = m_virtualDesktopManagement->getVirtualDesktop(m_desktops[nextPos]); 0337 0338 if (desktopObj) { 0339 desktopObj->requestActivate(); 0340 } 0341 } 0342 0343 void WaylandInterface::switchToPreviousVirtualDesktop() 0344 { 0345 if (!m_virtualDesktopManagement || m_desktops.count() <= 1) { 0346 return; 0347 } 0348 0349 int curPos = m_desktops.indexOf(m_currentDesktop); 0350 int nextPos = curPos - 1; 0351 0352 if (curPos <= 0) { 0353 if (isVirtualDesktopNavigationWrappingAround()) { 0354 nextPos = m_desktops.count()-1; 0355 } else { 0356 return; 0357 } 0358 } 0359 0360 KWayland::Client::PlasmaVirtualDesktop *desktopObj = m_virtualDesktopManagement->getVirtualDesktop(m_desktops[nextPos]); 0361 0362 if (desktopObj) { 0363 desktopObj->requestActivate(); 0364 } 0365 } 0366 0367 void WaylandInterface::setWindowOnActivities(const WindowId &wid, const QStringList &nextactivities) 0368 { 0369 auto winfo = requestInfo(wid); 0370 auto w = windowFor(wid); 0371 0372 if (!w) { 0373 return; 0374 } 0375 0376 QStringList curactivities = winfo.activities(); 0377 0378 if (!winfo.isOnAllActivities() && nextactivities.isEmpty()) { 0379 //! window must be set to all activities 0380 for(int i=0; i<curactivities.count(); ++i) { 0381 w->requestLeaveActivity(curactivities[i]); 0382 } 0383 } else if (curactivities != nextactivities) { 0384 QStringList requestenter; 0385 QStringList requestleave; 0386 0387 for (int i=0; i<nextactivities.count(); ++i) { 0388 if (!curactivities.contains(nextactivities[i])) { 0389 requestenter << nextactivities[i]; 0390 } 0391 } 0392 0393 for (int i=0; i<curactivities.count(); ++i) { 0394 if (!nextactivities.contains(curactivities[i])) { 0395 requestleave << curactivities[i]; 0396 } 0397 } 0398 0399 //! leave afterwards from deprecated activities 0400 for (int i=0; i<requestleave.count(); ++i) { 0401 w->requestLeaveActivity(requestleave[i]); 0402 } 0403 0404 //! first enter to new activities 0405 for (int i=0; i<requestenter.count(); ++i) { 0406 w->requestEnterActivity(requestenter[i]); 0407 } 0408 } 0409 } 0410 0411 void WaylandInterface::removeViewStruts(QWindow &view) 0412 { 0413 delete m_ghostWindows.take(view.winId()); 0414 } 0415 0416 WindowId WaylandInterface::activeWindow() 0417 { 0418 if (!m_windowManagement) { 0419 return 0; 0420 } 0421 0422 auto wid = m_windowManagement->activeWindow(); 0423 0424 return wid ? wid->internalId() : 0; 0425 } 0426 0427 void WaylandInterface::skipTaskBar(const QDialog &dialog) 0428 { 0429 KWindowSystem::setState(dialog.winId(), NET::SkipTaskbar); 0430 } 0431 0432 void WaylandInterface::slideWindow(QWindow &view, AbstractWindowInterface::Slide location) 0433 { 0434 auto slideLocation = KWindowEffects::NoEdge; 0435 0436 switch (location) { 0437 case Slide::Top: 0438 slideLocation = KWindowEffects::TopEdge; 0439 break; 0440 0441 case Slide::Bottom: 0442 slideLocation = KWindowEffects::BottomEdge; 0443 break; 0444 0445 case Slide::Left: 0446 slideLocation = KWindowEffects::LeftEdge; 0447 break; 0448 0449 case Slide::Right: 0450 slideLocation = KWindowEffects::RightEdge; 0451 break; 0452 0453 default: 0454 break; 0455 } 0456 0457 KWindowEffects::slideWindow(view.winId(), slideLocation, -1); 0458 } 0459 0460 void WaylandInterface::enableBlurBehind(QWindow &view) 0461 { 0462 KWindowEffects::enableBlurBehind(view.winId()); 0463 } 0464 0465 void WaylandInterface::setActiveEdge(QWindow *view, bool active) 0466 { 0467 ViewPart::ScreenEdgeGhostWindow *window = qobject_cast<ViewPart::ScreenEdgeGhostWindow *>(view); 0468 0469 if (!window) { 0470 return; 0471 } 0472 0473 if (window->parentView()->surface() && window->parentView()->visibility() 0474 && (window->parentView()->visibility()->mode() == Types::DodgeActive 0475 || window->parentView()->visibility()->mode() == Types::DodgeMaximized 0476 || window->parentView()->visibility()->mode() == Types::DodgeAllWindows 0477 || window->parentView()->visibility()->mode() == Types::AutoHide)) { 0478 if (active) { 0479 window->showWithMask(); 0480 window->surface()->requestHideAutoHidingPanel(); 0481 } else { 0482 window->hideWithMask(); 0483 window->surface()->requestShowAutoHidingPanel(); 0484 } 0485 } 0486 } 0487 0488 void WaylandInterface::setFrameExtents(QWindow *view, const QMargins &extents) 0489 { 0490 //! do nothing until there is a wayland way to provide this 0491 } 0492 0493 void WaylandInterface::setInputMask(QWindow *window, const QRect &rect) 0494 { 0495 //! do nothins, QWindow::mask() is sufficient enough in order to define Window input mask 0496 } 0497 0498 WindowInfoWrap WaylandInterface::requestInfoActive() 0499 { 0500 if (!m_windowManagement) { 0501 return {}; 0502 } 0503 0504 auto w = m_windowManagement->activeWindow(); 0505 0506 if (!w) return {}; 0507 0508 return requestInfo(w->internalId()); 0509 } 0510 0511 WindowInfoWrap WaylandInterface::requestInfo(WindowId wid) 0512 { 0513 WindowInfoWrap winfoWrap; 0514 0515 auto w = windowFor(wid); 0516 0517 //!used to track Plasma DesktopView windows because during startup can not be identified properly 0518 bool plasmaBlockedWindow = w && (w->appId() == QLatin1String("org.kde.plasmashell")) && !isAcceptableWindow(w); 0519 0520 if (w) { 0521 winfoWrap.setIsValid(isValidWindow(w) && !plasmaBlockedWindow); 0522 winfoWrap.setWid(wid); 0523 winfoWrap.setParentId(w->parentWindow() ? w->parentWindow()->internalId() : 0); 0524 winfoWrap.setIsActive(w->isActive()); 0525 winfoWrap.setIsMinimized(w->isMinimized()); 0526 winfoWrap.setIsMaxVert(w->isMaximized()); 0527 winfoWrap.setIsMaxHoriz(w->isMaximized()); 0528 winfoWrap.setIsFullscreen(w->isFullscreen()); 0529 winfoWrap.setIsShaded(w->isShaded()); 0530 winfoWrap.setIsOnAllDesktops(w->isOnAllDesktops()); 0531 winfoWrap.setIsOnAllActivities(w->plasmaActivities().isEmpty()); 0532 winfoWrap.setIsKeepAbove(w->isKeepAbove()); 0533 winfoWrap.setIsKeepBelow(w->isKeepBelow()); 0534 winfoWrap.setGeometry(w->geometry()); 0535 winfoWrap.setHasSkipSwitcher(w->skipSwitcher()); 0536 winfoWrap.setHasSkipTaskbar(w->skipTaskbar()); 0537 0538 //! BEGIN:Window Abilities 0539 winfoWrap.setIsClosable(w->isCloseable()); 0540 winfoWrap.setIsFullScreenable(w->isFullscreenable()); 0541 winfoWrap.setIsMaximizable(w->isMaximizeable()); 0542 winfoWrap.setIsMinimizable(w->isMinimizeable()); 0543 winfoWrap.setIsMovable(w->isMovable()); 0544 winfoWrap.setIsResizable(w->isResizable()); 0545 winfoWrap.setIsShadeable(w->isShadeable()); 0546 winfoWrap.setIsVirtualDesktopsChangeable(w->isVirtualDesktopChangeable()); 0547 //! END:Window Abilities 0548 0549 winfoWrap.setDisplay(w->title()); 0550 winfoWrap.setDesktops(w->plasmaVirtualDesktops()); 0551 winfoWrap.setActivities(w->plasmaActivities()); 0552 0553 } else { 0554 winfoWrap.setIsValid(false); 0555 } 0556 0557 if (plasmaBlockedWindow) { 0558 windowRemoved(w->internalId()); 0559 } 0560 0561 return winfoWrap; 0562 } 0563 0564 AppData WaylandInterface::appDataFor(WindowId wid) 0565 { 0566 auto window = windowFor(wid); 0567 0568 if (window) { 0569 const AppData &data = appDataFromUrl(windowUrlFromMetadata(window->appId(), 0570 window->pid(), rulesConfig)); 0571 0572 return data; 0573 } 0574 0575 AppData empty; 0576 0577 return empty; 0578 } 0579 0580 KWayland::Client::PlasmaWindow *WaylandInterface::windowFor(WindowId wid) 0581 { 0582 auto it = std::find_if(m_windowManagement->windows().constBegin(), m_windowManagement->windows().constEnd(), [&wid](PlasmaWindow * w) noexcept { 0583 return w->isValid() && w->internalId() == wid; 0584 }); 0585 0586 if (it == m_windowManagement->windows().constEnd()) { 0587 return nullptr; 0588 } 0589 0590 return *it; 0591 } 0592 0593 QIcon WaylandInterface::iconFor(WindowId wid) 0594 { 0595 auto window = windowFor(wid); 0596 0597 if (window) { 0598 return window->icon(); 0599 } 0600 0601 0602 return QIcon(); 0603 } 0604 0605 WindowId WaylandInterface::winIdFor(QString appId, QString title) 0606 { 0607 auto it = std::find_if(m_windowManagement->windows().constBegin(), m_windowManagement->windows().constEnd(), [&appId, &title](PlasmaWindow * w) noexcept { 0608 return w->isValid() && w->appId() == appId && w->title().startsWith(title); 0609 }); 0610 0611 if (it == m_windowManagement->windows().constEnd()) { 0612 return QVariant(); 0613 } 0614 0615 return (*it)->internalId(); 0616 } 0617 0618 WindowId WaylandInterface::winIdFor(QString appId, QRect geometry) 0619 { 0620 auto it = std::find_if(m_windowManagement->windows().constBegin(), m_windowManagement->windows().constEnd(), [&appId, &geometry](PlasmaWindow * w) noexcept { 0621 return w->isValid() && w->appId() == appId && w->geometry() == geometry; 0622 }); 0623 0624 if (it == m_windowManagement->windows().constEnd()) { 0625 return QVariant(); 0626 } 0627 0628 return (*it)->internalId(); 0629 } 0630 0631 bool WaylandInterface::windowCanBeDragged(WindowId wid) 0632 { 0633 auto w = windowFor(wid); 0634 0635 if (w && isValidWindow(w)) { 0636 WindowInfoWrap winfo = requestInfo(wid); 0637 return (winfo.isValid() 0638 && w->isMovable() 0639 && !winfo.isMinimized() 0640 && inCurrentDesktopActivity(winfo)); 0641 } 0642 0643 return false; 0644 } 0645 0646 bool WaylandInterface::windowCanBeMaximized(WindowId wid) 0647 { 0648 auto w = windowFor(wid); 0649 0650 if (w && isValidWindow(w)) { 0651 WindowInfoWrap winfo = requestInfo(wid); 0652 return (winfo.isValid() 0653 && w->isMaximizeable() 0654 && !winfo.isMinimized() 0655 && inCurrentDesktopActivity(winfo)); 0656 } 0657 0658 return false; 0659 } 0660 0661 void WaylandInterface::requestActivate(WindowId wid) 0662 { 0663 auto w = windowFor(wid); 0664 0665 if (w) { 0666 w->requestActivate(); 0667 } 0668 } 0669 0670 void WaylandInterface::requestClose(WindowId wid) 0671 { 0672 auto w = windowFor(wid); 0673 0674 if (w) { 0675 w->requestClose(); 0676 } 0677 } 0678 0679 0680 void WaylandInterface::requestMoveWindow(WindowId wid, QPoint from) 0681 { 0682 WindowInfoWrap wInfo = requestInfo(wid); 0683 0684 if (windowCanBeDragged(wid) && inCurrentDesktopActivity(wInfo)) { 0685 auto w = windowFor(wid); 0686 0687 if (w && isValidWindow(w)) { 0688 w->requestMove(); 0689 } 0690 } 0691 } 0692 0693 void WaylandInterface::requestToggleIsOnAllDesktops(WindowId wid) 0694 { 0695 auto w = windowFor(wid); 0696 0697 if (w && isValidWindow(w) && m_desktops.count() > 1) { 0698 if (w->isOnAllDesktops()) { 0699 w->requestEnterVirtualDesktop(m_currentDesktop); 0700 } else { 0701 const QStringList &now = w->plasmaVirtualDesktops(); 0702 0703 foreach (const QString &desktop, now) { 0704 w->requestLeaveVirtualDesktop(desktop); 0705 } 0706 } 0707 } 0708 } 0709 0710 void WaylandInterface::requestToggleKeepAbove(WindowId wid) 0711 { 0712 auto w = windowFor(wid); 0713 0714 if (w) { 0715 w->requestToggleKeepAbove(); 0716 } 0717 } 0718 0719 void WaylandInterface::setKeepAbove(WindowId wid, bool active) 0720 { 0721 auto w = windowFor(wid); 0722 0723 if (w) { 0724 if (active) { 0725 setKeepBelow(wid, false); 0726 } 0727 0728 if ((w->isKeepAbove() && active) || (!w->isKeepAbove() && !active)) { 0729 return; 0730 } 0731 0732 w->requestToggleKeepAbove(); 0733 } 0734 } 0735 0736 void WaylandInterface::setKeepBelow(WindowId wid, bool active) 0737 { 0738 auto w = windowFor(wid); 0739 0740 if (w) { 0741 if (active) { 0742 setKeepAbove(wid, false); 0743 } 0744 0745 if ((w->isKeepBelow() && active) || (!w->isKeepBelow() && !active)) { 0746 return; 0747 } 0748 0749 w->requestToggleKeepBelow(); 0750 } 0751 } 0752 0753 void WaylandInterface::requestToggleMinimized(WindowId wid) 0754 { 0755 auto w = windowFor(wid); 0756 WindowInfoWrap wInfo = requestInfo(wid); 0757 0758 if (w && isValidWindow(w) && inCurrentDesktopActivity(wInfo)) { 0759 if (!m_currentDesktop.isEmpty()) { 0760 w->requestEnterVirtualDesktop(m_currentDesktop); 0761 } 0762 w->requestToggleMinimized(); 0763 } 0764 } 0765 0766 void WaylandInterface::requestToggleMaximized(WindowId wid) 0767 { 0768 auto w = windowFor(wid); 0769 WindowInfoWrap wInfo = requestInfo(wid); 0770 0771 if (w && isValidWindow(w) && windowCanBeMaximized(wid) && inCurrentDesktopActivity(wInfo)) { 0772 if (!m_currentDesktop.isEmpty()) { 0773 w->requestEnterVirtualDesktop(m_currentDesktop); 0774 } 0775 w->requestToggleMaximized(); 0776 } 0777 } 0778 0779 bool WaylandInterface::isPlasmaPanel(const KWayland::Client::PlasmaWindow *w) const 0780 { 0781 if (!w || (w->appId() != QLatin1String("org.kde.plasmashell"))) { 0782 return false; 0783 } 0784 0785 return AbstractWindowInterface::isPlasmaPanel(w->geometry()); 0786 } 0787 0788 bool WaylandInterface::isFullScreenWindow(const KWayland::Client::PlasmaWindow *w) const 0789 { 0790 if (!w) { 0791 return false; 0792 } 0793 0794 return w->isFullscreen() || AbstractWindowInterface::isFullScreenWindow(w->geometry()); 0795 } 0796 0797 bool WaylandInterface::isSidepanel(const KWayland::Client::PlasmaWindow *w) const 0798 { 0799 if (!w) { 0800 return false; 0801 } 0802 0803 return AbstractWindowInterface::isSidepanel(w->geometry()); 0804 } 0805 0806 bool WaylandInterface::isValidWindow(const KWayland::Client::PlasmaWindow *w) 0807 { 0808 if (!w || !w->isValid()) { 0809 return false; 0810 } 0811 0812 if (windowsTracker()->isValidFor(w->internalId())) { 0813 return true; 0814 } 0815 0816 return isAcceptableWindow(w); 0817 } 0818 0819 bool WaylandInterface::isAcceptableWindow(const KWayland::Client::PlasmaWindow *w) 0820 { 0821 if (!w || !w->isValid()) { 0822 return false; 0823 } 0824 0825 //! ignored windows that are not tracked 0826 if (hasBlockedTracking(w->internalId())) { 0827 return false; 0828 } 0829 0830 //! whitelisted/approved windows 0831 if (isWhitelistedWindow(w->internalId())) { 0832 return true; 0833 } 0834 0835 //! Window Checks 0836 bool hasSkipTaskbar = w->skipTaskbar(); 0837 bool isSkipped = hasSkipTaskbar; 0838 bool hasSkipSwitcher = w->skipSwitcher(); 0839 isSkipped = hasSkipTaskbar && hasSkipSwitcher; 0840 0841 if (isSkipped 0842 && ((w->appId() == QLatin1String("yakuake") 0843 || (w->appId() == QLatin1String("krunner"))) )) { 0844 registerWhitelistedWindow(w->internalId()); 0845 } else if (w->appId() == QLatin1String("org.kde.plasmashell")) { 0846 if (isSkipped && isSidepanel(w)) { 0847 registerWhitelistedWindow(w->internalId()); 0848 return true; 0849 } else if (isPlasmaPanel(w) || isFullScreenWindow(w)) { 0850 registerPlasmaIgnoredWindow(w->internalId()); 0851 return false; 0852 } 0853 } else if ((w->appId() == QLatin1String("latte-dock")) 0854 || (w->appId().startsWith(QLatin1String("ksmserver")))) { 0855 if (isFullScreenWindow(w)) { 0856 registerIgnoredWindow(w->internalId()); 0857 return false; 0858 } 0859 } 0860 0861 return !isSkipped; 0862 } 0863 0864 void WaylandInterface::updateWindow() 0865 { 0866 PlasmaWindow *pW = qobject_cast<PlasmaWindow*>(QObject::sender()); 0867 0868 if (isValidWindow(pW)) { 0869 considerWindowChanged(pW->internalId()); 0870 } 0871 } 0872 0873 void WaylandInterface::windowUnmapped() 0874 { 0875 PlasmaWindow *pW = qobject_cast<PlasmaWindow*>(QObject::sender()); 0876 0877 if (pW) { 0878 untrackWindow(pW); 0879 emit windowRemoved(pW->internalId()); 0880 } 0881 } 0882 0883 void WaylandInterface::trackWindow(KWayland::Client::PlasmaWindow *w) 0884 { 0885 if (!w) { 0886 return; 0887 } 0888 0889 connect(w, &PlasmaWindow::activeChanged, this, &WaylandInterface::updateWindow); 0890 connect(w, &PlasmaWindow::titleChanged, this, &WaylandInterface::updateWindow); 0891 connect(w, &PlasmaWindow::fullscreenChanged, this, &WaylandInterface::updateWindow); 0892 connect(w, &PlasmaWindow::geometryChanged, this, &WaylandInterface::updateWindow); 0893 connect(w, &PlasmaWindow::maximizedChanged, this, &WaylandInterface::updateWindow); 0894 connect(w, &PlasmaWindow::minimizedChanged, this, &WaylandInterface::updateWindow); 0895 connect(w, &PlasmaWindow::shadedChanged, this, &WaylandInterface::updateWindow); 0896 connect(w, &PlasmaWindow::skipTaskbarChanged, this, &WaylandInterface::updateWindow); 0897 connect(w, &PlasmaWindow::onAllDesktopsChanged, this, &WaylandInterface::updateWindow); 0898 connect(w, &PlasmaWindow::parentWindowChanged, this, &WaylandInterface::updateWindow); 0899 connect(w, &PlasmaWindow::plasmaVirtualDesktopEntered, this, &WaylandInterface::updateWindow); 0900 connect(w, &PlasmaWindow::plasmaVirtualDesktopLeft, this, &WaylandInterface::updateWindow); 0901 connect(w, &PlasmaWindow::plasmaActivityEntered, this, &WaylandInterface::updateWindow); 0902 connect(w, &PlasmaWindow::plasmaActivityLeft, this, &WaylandInterface::updateWindow); 0903 connect(w, &PlasmaWindow::unmapped, this, &WaylandInterface::windowUnmapped); 0904 } 0905 0906 void WaylandInterface::untrackWindow(KWayland::Client::PlasmaWindow *w) 0907 { 0908 if (!w) { 0909 return; 0910 } 0911 0912 disconnect(w, &PlasmaWindow::activeChanged, this, &WaylandInterface::updateWindow); 0913 disconnect(w, &PlasmaWindow::titleChanged, this, &WaylandInterface::updateWindow); 0914 disconnect(w, &PlasmaWindow::fullscreenChanged, this, &WaylandInterface::updateWindow); 0915 disconnect(w, &PlasmaWindow::geometryChanged, this, &WaylandInterface::updateWindow); 0916 disconnect(w, &PlasmaWindow::maximizedChanged, this, &WaylandInterface::updateWindow); 0917 disconnect(w, &PlasmaWindow::minimizedChanged, this, &WaylandInterface::updateWindow); 0918 disconnect(w, &PlasmaWindow::shadedChanged, this, &WaylandInterface::updateWindow); 0919 disconnect(w, &PlasmaWindow::skipTaskbarChanged, this, &WaylandInterface::updateWindow); 0920 disconnect(w, &PlasmaWindow::onAllDesktopsChanged, this, &WaylandInterface::updateWindow); 0921 disconnect(w, &PlasmaWindow::parentWindowChanged, this, &WaylandInterface::updateWindow); 0922 disconnect(w, &PlasmaWindow::plasmaVirtualDesktopEntered, this, &WaylandInterface::updateWindow); 0923 disconnect(w, &PlasmaWindow::plasmaVirtualDesktopLeft, this, &WaylandInterface::updateWindow); 0924 disconnect(w, &PlasmaWindow::plasmaActivityEntered, this, &WaylandInterface::updateWindow); 0925 disconnect(w, &PlasmaWindow::plasmaActivityLeft, this, &WaylandInterface::updateWindow); 0926 disconnect(w, &PlasmaWindow::unmapped, this, &WaylandInterface::windowUnmapped); 0927 } 0928 0929 0930 void WaylandInterface::windowCreatedProxy(KWayland::Client::PlasmaWindow *w) 0931 { 0932 if (!isAcceptableWindow(w)) { 0933 return; 0934 } 0935 0936 trackWindow(w); 0937 emit windowAdded(w->internalId()); 0938 0939 if (w->appId() == QLatin1String("latte-dock")) { 0940 emit latteWindowAdded(); 0941 } 0942 } 0943 0944 } 0945 } 0946 0947 #include "waylandinterface.moc"