diff --git a/app/wm/waylandinterface.cpp b/app/wm/waylandinterface.cpp index f6bd23b1..806cf51a 100644 --- a/app/wm/waylandinterface.cpp +++ b/app/wm/waylandinterface.cpp @@ -1,452 +1,472 @@ /* * Copyright 2016 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "waylandinterface.h" // local #include "view/screenedgeghostwindow.h" #include "view/view.h" #include "../lattecorona.h" #include "../liblatte2/extras.h" // Qt #include #include #include #include #include #include // KDE #include #include #include // X11 #include using namespace KWayland::Client; namespace Latte { class Private::GhostWindow : public QRasterWindow { Q_OBJECT public: GhostWindow(WaylandInterface *waylandInterface) : m_waylandInterface(waylandInterface) { setFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::NoDropShadowWindowHint | Qt::WindowDoesNotAcceptFocus); setupWaylandIntegration(); show(); } ~GhostWindow() { delete m_shellSurface; } void setGeometry(const QRect &rect) { QWindow::setGeometry(rect); setMaximumSize(rect.size()); m_shellSurface->setPosition(rect.topLeft()); } void setupWaylandIntegration() { using namespace KWayland::Client; if (m_shellSurface) return; Surface *s{Surface::fromWindow(this)}; if (!s) return; m_shellSurface = m_waylandInterface->waylandCoronaInterface()->createSurface(s, this); qDebug() << "wayland ghost window surface was created..."; m_shellSurface->setSkipTaskbar(true); m_shellSurface->setPanelTakesFocus(false); m_shellSurface->setRole(PlasmaShellSurface::Role::Panel); m_shellSurface->setPanelBehavior(PlasmaShellSurface::PanelBehavior::AlwaysVisible); } KWayland::Client::PlasmaShellSurface *m_shellSurface{nullptr}; WaylandInterface *m_waylandInterface{nullptr}; }; WaylandInterface::WaylandInterface(QObject *parent) : AbstractWindowInterface(parent) { m_corona = qobject_cast(parent); m_activities = new KActivities::Consumer(this); connect(m_activities.data(), &KActivities::Consumer::currentActivityChanged , this, &WaylandInterface::currentActivityChanged); } WaylandInterface::~WaylandInterface() { } void WaylandInterface::init() { } void WaylandInterface::initWindowManagement(KWayland::Client::PlasmaWindowManagement *windowManagement) { m_windowManagement = windowManagement; connect(m_windowManagement, &PlasmaWindowManagement::windowCreated, this, &WaylandInterface::windowCreatedProxy); connect(m_windowManagement, &PlasmaWindowManagement::activeWindowChanged, this, [&]() noexcept { auto w = m_windowManagement->activeWindow(); - if (!w || (w && w->appId() != QLatin1String("latte-dock"))) { + if (!w || (w && (!isPlasmaDesktop(w) && w->appId() != QLatin1String("latte-dock")))) { emit activeWindowChanged(w ? w->internalId() : 0); } }, Qt::QueuedConnection); } KWayland::Client::PlasmaShell *WaylandInterface::waylandCoronaInterface() const { return m_corona->waylandCoronaInterface(); } void WaylandInterface::setViewExtraFlags(QWindow &view) { Q_UNUSED(view) } void WaylandInterface::setViewStruts(QWindow &view, const QRect &rect, Plasma::Types::Location location) { if (!m_ghostWindows.contains(view.winId())) m_ghostWindows[view.winId()] = new Private::GhostWindow(this); auto w = m_ghostWindows[view.winId()]; switch (location) { case Plasma::Types::TopEdge: case Plasma::Types::BottomEdge: w->setGeometry({rect.x() + rect.width() / 2, rect.y(), 1, rect.height()}); break; case Plasma::Types::LeftEdge: case Plasma::Types::RightEdge: w->setGeometry({rect.x(), rect.y() + rect.height() / 2, rect.width(), 1}); break; default: break; } } void WaylandInterface::setWindowOnActivities(QWindow &window, const QStringList &activities) { //! needs to updated to wayland case // KWindowSystem::setOnActivities(view.winId(), activities); } void WaylandInterface::removeViewStruts(QWindow &view) const { delete m_ghostWindows.take(view.winId()); } WindowId WaylandInterface::activeWindow() const { if (!m_windowManagement) { return 0; } auto wid = m_windowManagement->activeWindow(); return wid ? wid->internalId() : 0; } const std::list &WaylandInterface::windows() const { return m_windows; } void WaylandInterface::setKeepAbove(const QDialog &dialog, bool above) const { if (above) { KWindowSystem::setState(dialog.winId(), NET::KeepAbove); } else { KWindowSystem::clearState(dialog.winId(), NET::KeepAbove); } } void WaylandInterface::skipTaskBar(const QDialog &dialog) const { KWindowSystem::setState(dialog.winId(), NET::SkipTaskbar); } void WaylandInterface::slideWindow(QWindow &view, AbstractWindowInterface::Slide location) const { auto slideLocation = KWindowEffects::NoEdge; switch (location) { case Slide::Top: slideLocation = KWindowEffects::TopEdge; break; case Slide::Bottom: slideLocation = KWindowEffects::BottomEdge; break; case Slide::Left: slideLocation = KWindowEffects::LeftEdge; break; case Slide::Right: slideLocation = KWindowEffects::RightEdge; break; default: break; } KWindowEffects::slideWindow(view.winId(), slideLocation, -1); } void WaylandInterface::enableBlurBehind(QWindow &view) const { KWindowEffects::enableBlurBehind(view.winId()); } void WaylandInterface::setEdgeStateFor(QWindow *view, bool active) const { ViewPart::ScreenEdgeGhostWindow *window = qobject_cast(view); if (!window) { return; } if (window->parentView()->surface() && window->parentView()->visibility() && (window->parentView()->visibility()->mode() == Types::DodgeActive || window->parentView()->visibility()->mode() == Types::DodgeMaximized || window->parentView()->visibility()->mode() == Types::DodgeAllWindows || window->parentView()->visibility()->mode() == Types::AutoHide)) { if (active) { window->showWithMask(); window->surface()->requestHideAutoHidingPanel(); } else { window->hideWithMask(); window->surface()->requestShowAutoHidingPanel(); } } } WindowInfoWrap WaylandInterface::requestInfoActive() const { if (!m_windowManagement) { return {}; } auto w = m_windowManagement->activeWindow(); if (!w) return {}; WindowInfoWrap winfoWrap; winfoWrap.setIsValid(true); winfoWrap.setWid(w->internalId()); winfoWrap.setIsActive(w->isActive()); winfoWrap.setIsMinimized(w->isMinimized()); winfoWrap.setIsMaxVert(w->isMaximized()); winfoWrap.setIsMaxHoriz(w->isMaximized()); winfoWrap.setIsFullscreen(w->isFullscreen()); winfoWrap.setIsShaded(w->isShaded()); winfoWrap.setGeometry(w->geometry()); winfoWrap.setIsKeepAbove(w->isKeepAbove()); winfoWrap.setHasSkipTaskbar(w->skipTaskbar()); return winfoWrap; } bool WaylandInterface::isOnCurrentDesktop(WindowId wid) const { if (!m_windowManagement) { return false; } auto it = std::find_if(m_windowManagement->windows().constBegin(), m_windowManagement->windows().constEnd(), [&wid](PlasmaWindow * w) noexcept { return w->isValid() && w->internalId() == wid; }); //qDebug() << "desktop:" << (it != m_windowManagement->windows().constEnd() ? (*it)->virtualDesktop() : -1) << KWindowSystem::currentDesktop(); //return true; return it != m_windowManagement->windows().constEnd() && ((*it)->virtualDesktop() == KWindowSystem::currentDesktop() || (*it)->isOnAllDesktops()); } bool WaylandInterface::isOnCurrentActivity(WindowId wid) const { auto it = std::find_if(m_windowManagement->windows().constBegin(), m_windowManagement->windows().constEnd(), [&wid](PlasmaWindow * w) noexcept { return w->isValid() && w->internalId() == wid; }); //TODO: Not yet implemented return it != m_windowManagement->windows().constEnd() && true; } WindowInfoWrap WaylandInterface::requestInfo(WindowId wid) const { WindowInfoWrap winfoWrap; auto w = windowFor(wid); if (w) { - if (isValidWindow(w)) { + if (isPlasmaDesktop(w)) { + winfoWrap.setIsValid(true); + winfoWrap.setIsPlasmaDesktop(true); + winfoWrap.setWid(wid); + } else if (isValidWindow(w)) { winfoWrap.setIsValid(true); winfoWrap.setWid(wid); winfoWrap.setIsActive(w->isActive()); winfoWrap.setIsMinimized(w->isMinimized()); winfoWrap.setIsMaxVert(w->isMaximized()); winfoWrap.setIsMaxHoriz(w->isMaximized()); winfoWrap.setIsFullscreen(w->isFullscreen()); winfoWrap.setIsShaded(w->isShaded()); winfoWrap.setGeometry(w->geometry()); winfoWrap.setHasSkipTaskbar(w->skipTaskbar()); - } else if (w->appId() == QLatin1String("org.kde.plasmashell")) { - winfoWrap.setIsValid(true); - winfoWrap.setIsPlasmaDesktop(true); - winfoWrap.setWid(wid); } } else { return {}; } return winfoWrap; } KWayland::Client::PlasmaWindow *WaylandInterface::windowFor(WindowId wid) const { auto it = std::find_if(m_windowManagement->windows().constBegin(), m_windowManagement->windows().constEnd(), [&wid](PlasmaWindow * w) noexcept { return w->isValid() && w->internalId() == wid; }); if (it == m_windowManagement->windows().constEnd()) { return nullptr; } return *it; } bool WaylandInterface::windowCanBeDragged(WindowId wid) const { WindowInfoWrap winfo = requestInfo(wid); return (winfo.isValid() && !winfo.isPlasmaDesktop() && !winfo.hasSkipTaskbar()); } void WaylandInterface::releaseMouseEventFor(WindowId wid) const { // this isnt really needed under wayland } void WaylandInterface::requestMoveWindow(WindowId wid, QPoint from) const { if (windowCanBeDragged(wid)) { auto w = windowFor(wid); if (w && isValidWindow(w)) { w->requestMove(); } } } void WaylandInterface::requestToggleMaximized(WindowId wid) const { auto w = windowFor(wid); if (w && isValidWindow(w)) { w->requestToggleMaximized(); } } -inline bool WaylandInterface::isValidWindow(const KWayland::Client::PlasmaWindow *w) const +bool WaylandInterface::isPlasmaDesktop(const KWayland::Client::PlasmaWindow *w) const +{ + if (!w || (w->appId() != QLatin1String("org.kde.plasmashell"))) { + return false; + } + + bool hasScreenGeometry{false}; + + foreach(auto scr, qGuiApp->screens()) { + if (!w->geometry().isEmpty() && w->geometry() == scr->geometry()) { + hasScreenGeometry = true; + break; + } + } + + return hasScreenGeometry; +} + +bool WaylandInterface::isValidWindow(const KWayland::Client::PlasmaWindow *w) const { + //! DEPRECATED comment is case we must reenable this //! because wayland does not have any way yet to identify the window type //! a trick is to just consider windows as valid when they can be shown in the //! taskbar. Of course that creates issues with plasma native dialogs //! e.g. widgets explorer, Activities etc. that are not used to hide //! the dodge views appropriately - return w->isValid() && w->appId()!=QLatin1String("latte-dock") && !w->skipTaskbar(); + + return w->isValid() && !isPlasmaDesktop(w) && w->appId()!=QLatin1String("latte-dock");// && !w->skipTaskbar(); } void WaylandInterface::windowCreatedProxy(KWayland::Client::PlasmaWindow *w) { if (!isValidWindow(w)) return; if (!mapper) mapper = new QSignalMapper(this); mapper->setMapping(w, w); connect(w, &PlasmaWindow::unmapped, this, [ &, win = w]() noexcept { mapper->removeMappings(win); m_windows.remove(win->internalId()); emit windowRemoved(win->internalId()); }); connect(w, SIGNAL(activeChanged()), mapper, SLOT(map())); connect(w, SIGNAL(fullscreenChanged()), mapper, SLOT(map())); connect(w, SIGNAL(geometryChanged()), mapper, SLOT(map())); connect(w, SIGNAL(maximizedChanged()), mapper, SLOT(map())); connect(w, SIGNAL(minimizedChanged()), mapper, SLOT(map())); connect(w, SIGNAL(shadedChanged()), mapper, SLOT(map())); connect(w, SIGNAL(skipTaskbarChanged()), mapper, SLOT(map())); connect(w, SIGNAL(onAllDesktopsChanged()), mapper, SLOT(map())); connect(w, SIGNAL(virtualDesktopChanged()), mapper, SLOT(map())); connect(mapper, static_cast(&QSignalMapper::mapped) , this, [&](QObject * w) noexcept { //qDebug() << "window changed:" << qobject_cast(w)->appId(); PlasmaWindow *pW = qobject_cast(w); - if (pW && pW->appId() != QLatin1String("latte-dock")) { + if (pW && !isPlasmaDesktop(pW) && pW->appId() != QLatin1String("latte-dock")) { emit windowChanged(pW->internalId()); } }); m_windows.push_back(w->internalId()); emit windowAdded(w->internalId()); } } #include "waylandinterface.moc" diff --git a/app/wm/waylandinterface.h b/app/wm/waylandinterface.h index c9553ebe..856f33b0 100644 --- a/app/wm/waylandinterface.h +++ b/app/wm/waylandinterface.h @@ -1,111 +1,112 @@ /* * Copyright 2016 Smith AR * Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Latte-Dock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef WAYLANDINTERFACE_H #define WAYLANDINTERFACE_H // local #include "abstractwindowinterface.h" #include "windowinfowrap.h" // Qt #include #include #include // KDE #include #include #include #include #include #include #include namespace Latte { class Corona; namespace Private { /** * @brief this class is use for create the struts inside wayland */ class GhostWindow; } class WaylandInterface : public AbstractWindowInterface { Q_OBJECT public: explicit WaylandInterface(QObject *parent = nullptr); ~WaylandInterface() override; void setViewExtraFlags(QWindow &view) override; void setViewStruts(QWindow &view, const QRect &rect , Plasma::Types::Location location) override; void setWindowOnActivities(QWindow &view, const QStringList &activities) override; void removeViewStruts(QWindow &view) const override; WindowId activeWindow() const override; WindowInfoWrap requestInfo(WindowId wid) const override; WindowInfoWrap requestInfoActive() const override; bool isOnCurrentDesktop(WindowId wid) const override; bool isOnCurrentActivity(WindowId wid) const override; const std::list &windows() const override; void setKeepAbove(const QDialog &dialog, bool above = true) const override; void skipTaskBar(const QDialog &dialog) const override; void slideWindow(QWindow &view, Slide location) const override; void enableBlurBehind(QWindow &view) const override; void releaseMouseEventFor(WindowId wid) const override; void requestToggleMaximized(WindowId wid) const override; void requestMoveWindow(WindowId wid, QPoint from) const override; bool windowCanBeDragged(WindowId wid) const; void setEdgeStateFor(QWindow *view, bool active) const override; void initWindowManagement(KWayland::Client::PlasmaWindowManagement *windowManagement); private: void init(); - inline bool isValidWindow(const KWayland::Client::PlasmaWindow *w) const; + bool isValidWindow(const KWayland::Client::PlasmaWindow *w) const; + bool isPlasmaDesktop(const KWayland::Client::PlasmaWindow *w) const; void windowCreatedProxy(KWayland::Client::PlasmaWindow *w); KWayland::Client::PlasmaWindow *windowFor(WindowId wid) const; KWayland::Client::PlasmaShell *waylandCoronaInterface() const; QSignalMapper *mapper{nullptr}; friend class Private::GhostWindow; mutable QMap m_ghostWindows; KWayland::Client::PlasmaWindowManagement *m_windowManagement{nullptr}; Latte::Corona *m_corona{nullptr}; }; } #endif // WAYLANDINTERFACE_H