diff --git a/app/wm/tracker/lastactivewindow.cpp b/app/wm/tracker/lastactivewindow.cpp index 161530f6..9696107e 100644 --- a/app/wm/tracker/lastactivewindow.cpp +++ b/app/wm/tracker/lastactivewindow.cpp @@ -1,480 +1,631 @@ /* * Copyright 2019 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 "lastactivewindow.h" // local #include "schemes.h" #include "trackedgeneralinfo.h" #include "windowstracker.h" #include "../abstractwindowinterface.h" #include "../tasktools.h" #include "../../view/view.h" // Qt #include #include #include #include namespace Latte { namespace WindowSystem { namespace Tracker { const int INVALIDWID = -1; const int PREFHISTORY = 8; const int MAXHISTORY = 15; LastActiveWindow::LastActiveWindow(TrackedGeneralInfo *trackedInfo) : QObject(trackedInfo), m_trackedInfo(trackedInfo), m_windowsTracker(trackedInfo->wm()->windowsTracker()), m_wm(trackedInfo->wm()) { connect(m_wm->schemesTracker(), &Schemes::colorSchemeChanged, this, [&](WindowId wid) { if (wid == m_winId) { updateColorScheme(); } }); connect(m_windowsTracker, &Windows::applicationDataChanged, this, &LastActiveWindow::applicationDataChanged); connect(m_windowsTracker, &Windows::windowChanged, this, &LastActiveWindow::windowChanged); connect(m_windowsTracker, &Windows::windowRemoved, this, &LastActiveWindow::windowRemoved); } LastActiveWindow::~LastActiveWindow() { } bool LastActiveWindow::isActive() const { return m_isActive; } void LastActiveWindow::setActive(bool active) { if (m_isActive == active) { return; } m_isActive = active; emit isActiveChanged(); } bool LastActiveWindow::isMinimized() const { return m_isMinimized; } void LastActiveWindow::setIsMinimized(bool minimized) { if (m_isMinimized == minimized) { return; } m_isMinimized = minimized; emit isMinimizedChanged(); } bool LastActiveWindow::isMaximized() const { return m_isMaximized; } void LastActiveWindow::setIsMaximized(bool maximized) { if (m_isMaximized == maximized) { return; } m_isMaximized = maximized; emit isMaximizedChanged(); } bool LastActiveWindow::isFullScreen() const { return m_isFullScreen; } void LastActiveWindow::setIsFullScreen(bool fullscreen) { if (m_isFullScreen == fullscreen) { return; } m_isFullScreen = fullscreen; emit isFullScreenChanged(); } bool LastActiveWindow::isKeepAbove() const { return m_isKeepAbove; } void LastActiveWindow::setIsKeepAbove(bool above) { if (m_isKeepAbove == above) { return; } m_isKeepAbove = above; emit isKeepAboveChanged(); } bool LastActiveWindow::isOnAllDesktops() const { return m_isOnAllDesktops; } void LastActiveWindow::setIsOnAllDesktops(bool all) { if (m_isOnAllDesktops == all) { return; } m_isOnAllDesktops = all; emit isOnAllDesktopsChanged(); } bool LastActiveWindow::isShaded() const { return m_isShaded; } void LastActiveWindow::setIsShaded(bool shaded) { if (m_isShaded == shaded) { return; } m_isShaded = shaded; emit isShadedChanged(); } bool LastActiveWindow::isValid() const { return m_isValid; } void LastActiveWindow::setIsValid(bool valid) { if (m_isValid == valid) { return; } m_isValid = valid; emit isValidChanged(); } bool LastActiveWindow::hasSkipTaskbar() const { return m_hasSkipTaskbar; } void LastActiveWindow::setHasSkipTaskbar(bool skip) { if (m_hasSkipTaskbar == skip) { return; } m_hasSkipTaskbar = skip; emit hasSkipTaskbarChanged(); } +//! BEGIN: Window Abitilities +bool LastActiveWindow::isClosable() const +{ + return m_isClosable; +} + +void LastActiveWindow::setIsClosable(bool closable) +{ + if (m_isClosable == closable) { + return; + } + + m_isClosable = closable; + emit isClosableChanged(); +} + +bool LastActiveWindow::isFullScreenable() const +{ + return m_isFullScreenable; +} + +void LastActiveWindow::setIsFullScreenable(bool fullscreenable) +{ + if (m_isFullScreenable == fullscreenable) { + return; + } + + m_isFullScreenable = fullscreenable; + emit isFullScreenableChanged(); +} + +bool LastActiveWindow::isGroupable() const +{ + return m_isGroupable; +} + +void LastActiveWindow::setIsGroupable(bool groupable) +{ + if (m_isGroupable == groupable) { + return; + } + + m_isGroupable = groupable; + emit isGroupableChanged(); +} + + +bool LastActiveWindow::isMaximizable() const +{ + return m_isMaximizable; +} + +void LastActiveWindow::setIsMaximizable(bool maximizable) +{ + if (m_isMaximizable == maximizable) { + return; + } + + m_isMaximizable = maximizable; + emit isMaximizableChanged(); +} + +bool LastActiveWindow::isMinimizable() const +{ + return m_isMinimizable; +} + +void LastActiveWindow::setIsMinimizable(bool minimizable) +{ + if (m_isMinimizable == minimizable) { + return; + } + + m_isMinimizable = minimizable; + emit isMinimizableChanged(); +} + +bool LastActiveWindow::isMovable() const +{ + return m_isMovable; +} + +void LastActiveWindow::setIsMovable(bool movable) +{ + if (m_isMovable == movable) { + return; + } + + m_isMovable = movable; + emit isMovableChanged(); +} + +bool LastActiveWindow::isResizable() const +{ + return m_isResizable; +} + +void LastActiveWindow::setIsResizable(bool resizable) +{ + if (m_isResizable == resizable) { + return; + } + + m_isResizable = resizable; + emit isResizableChanged(); +} + +bool LastActiveWindow::isShadeable() const +{ + return m_isShadeable; +} + +void LastActiveWindow::setIsShadeable(bool shadeable) +{ + if (m_isShadeable == shadeable) { + return; + } + + m_isShadeable = shadeable; + emit isShadeableChanged(); +} + +bool LastActiveWindow::isVirtualDesktopChangeable() const +{ + return m_isVirtualDesktopsChangeable; +} + +void LastActiveWindow::setIsVirtualDesktopsChangeable(bool virtualdestkopschangeable) +{ + if (m_isVirtualDesktopsChangeable == virtualdestkopschangeable) { + return; + } + + m_isVirtualDesktopsChangeable = virtualdestkopschangeable; + emit isVirtualDesktopChangeableChanged(); +} +//! END: Window Abitilities + + QRect LastActiveWindow::geometry() const { return m_geometry; } void LastActiveWindow::setGeometry(QRect geometry) { if (m_geometry == geometry) { return; } m_geometry = geometry; emit geometryChanged(); } QString LastActiveWindow::appName() const { return m_appName; } void LastActiveWindow::setAppName(QString appName) { if (m_appName == appName) { return; } m_appName = appName; emit appNameChanged(); } QString LastActiveWindow::colorScheme() const { return m_colorScheme; } void LastActiveWindow::setColorScheme(QString scheme) { if (m_colorScheme == scheme){ return; } m_colorScheme = scheme; emit colorSchemeChanged(); } QString LastActiveWindow::display() const { return m_display; } void LastActiveWindow::setDisplay(QString display) { if (m_display == display) { return; } m_display = display; emit displayChanged(); } QIcon LastActiveWindow::icon() const { return m_icon; } void LastActiveWindow::setIcon(QIcon icon) { m_icon = icon; emit iconChanged(); } QVariant LastActiveWindow::winId() const { return m_winId; } void LastActiveWindow::setWinId(QVariant winId) { if (m_winId == winId && isValid()) { return; } if (!m_history.contains(winId)) { m_history.prepend(winId); clearHistory(); } else { int p = m_history.indexOf(winId); //! move to start m_history.move(p, 0); } m_winId = winId; emit winIdChanged(); } void LastActiveWindow::setInformation(const WindowInfoWrap &info) { bool firstActiveness{false}; if (m_winId != info.wid()) { firstActiveness = true; } setWinId(info.wid()); setIsValid(true); setActive(info.isActive()); setIsMinimized(info.isMinimized()); setIsMaximized(info.isMaximized()); setIsOnAllDesktops(info.isOnAllDesktops()); + //! Window Abilities + setIsClosable(info.isCloseable()); + setIsFullScreenable(info.isFullScreenable()); + setIsGroupable(info.isGroupable()); + setIsMaximizable(info.isMaximizable()); + setIsMinimizable(info.isMinimizable()); + setIsMovable(info.isMovable()); + setIsResizable(info.isResizable()); + setIsShadeable(info.isShadeable()); + setIsVirtualDesktopsChangeable(info.isVirtualDesktopsChangeable()); + //! Window Abilities + setAppName(info.appName()); setDisplay(info.display()); setGeometry(info.geometry()); setIsKeepAbove(info.isKeepAbove()); if (firstActiveness) { updateColorScheme(); } if (info.appName().isEmpty()) { setAppName(m_windowsTracker->appNameFor(info.wid())); } else { setAppName(info.appName()); } if (info.icon().isNull()) { setIcon(m_windowsTracker->iconFor(info.wid())); } else { setIcon(info.icon()); } } //! PRIVATE SLOTS void LastActiveWindow::applicationDataChanged(const WindowId &wid) { setAppName(m_windowsTracker->appNameFor(wid)); setIcon(m_windowsTracker->iconFor(wid)); } void LastActiveWindow::windowChanged(const WindowId &wid) { if (!m_trackedInfo->enabled()) { // qDebug() << " Last Active Window, Window Changed : TrackedInfo is disabled..."; return; } if (m_history.contains(wid)) { //! remove from history minimized windows or windows that changed screen //! and update information accordingly with the first valid window found from //! history after the removal WindowInfoWrap winfo = m_windowsTracker->infoFor(wid); bool firstItemRemoved{false}; //! Remove minimized windows OR NOT-TRACKED windows from history if (winfo.isMinimized() || !m_trackedInfo->isTracking(winfo)) { if (m_history[0] == wid) { firstItemRemoved = true; } m_history.removeAll(wid); clearHistory(); } if (m_history.count() > 0) { if (m_history[0] == wid || firstItemRemoved) { WindowInfoWrap history1 = m_windowsTracker->infoFor(m_history[0]); //! Check if first found History window is still valid to show its information if (history1.isMinimized() || !m_trackedInfo->isTracking(history1)) { windowChanged(m_history[0]); } else { setInformation(history1); } } } else { //! History is empty so any demonstrated information are invalid setIsValid(false); } //qDebug() << " HISTORY ::: " << m_history; } else { //qDebug() << " LastActiveWindow : window is not in history"; } } void LastActiveWindow::windowRemoved(const WindowId &wid) { if (m_history.contains(wid)) { bool firstItemRemoved{false}; if (m_history.count() > 0 && m_history[0] == wid) { firstItemRemoved = true; } m_history.removeAll(wid); m_history.removeAll(wid); if (m_history.count() > 0 && firstItemRemoved) { windowChanged(m_history[0]); } else { setIsValid(false); } } } void LastActiveWindow::clearHistory() { if (m_history.count() > MAXHISTORY) { int size = m_history.count(); for(int i=0; i<(size-PREFHISTORY); ++i) { if (!m_history.isEmpty()) { m_history.removeLast(); } } } } void LastActiveWindow::updateColorScheme() { auto scheme = m_wm->schemesTracker()->schemeForWindow(m_winId); if (scheme) { setColorScheme(scheme->schemeFile()); } } //! FUNCTIONALITY void LastActiveWindow::requestActivate() { m_wm->requestActivate(m_winId); } void LastActiveWindow::requestClose() { m_wm->requestClose(m_winId); } void LastActiveWindow::requestMove(Latte::View *fromView, int localX, int localY) { if (!fromView || !canBeDragged()) { return; } QPoint globalPoint{fromView->x() + localX, fromView->y() + localY}; m_wm->requestMoveWindow(m_winId, globalPoint); fromView->unblockMouse(localX, localY); } void LastActiveWindow::requestToggleIsOnAllDesktops() { m_wm->requestToggleIsOnAllDesktops(m_winId); } void LastActiveWindow::requestToggleKeepAbove() { m_wm->requestToggleKeepAbove(m_winId); } void LastActiveWindow::requestToggleMinimized() { m_wm->requestToggleMinimized(m_winId); } void LastActiveWindow::requestToggleMaximized() { m_wm->requestToggleMaximized(m_winId); } bool LastActiveWindow::canBeDragged() { return m_wm->windowCanBeDragged(m_winId); } } } } diff --git a/app/wm/tracker/lastactivewindow.h b/app/wm/tracker/lastactivewindow.h index 52506fe1..b7c82fa9 100644 --- a/app/wm/tracker/lastactivewindow.h +++ b/app/wm/tracker/lastactivewindow.h @@ -1,188 +1,254 @@ /* * Copyright 2019 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 WINDOWSYSTEMLASTACTIVEWINDOW_H #define WINDOWSYSTEMLASTACTIVEWINDOW_H // local #include "../windowinfowrap.h" #include "../abstractwindowinterface.h" // Qt #include #include namespace Latte { class View; namespace WindowSystem { class AbstractWindowInterface; namespace Tracker { class TrackedGeneralInfo; class Windows; } } } namespace Latte { namespace WindowSystem { namespace Tracker { class LastActiveWindow : public QObject { Q_OBJECT + Q_PROPERTY(bool isValid READ isValid NOTIFY isValidChanged) + Q_PROPERTY(bool isActive READ isActive NOTIFY isActiveChanged) Q_PROPERTY(bool isMinimized READ isMinimized NOTIFY isMinimizedChanged) Q_PROPERTY(bool isMaximized READ isMaximized NOTIFY isMaximizedChanged) Q_PROPERTY(bool isFullScreen READ isFullScreen NOTIFY isFullScreenChanged) Q_PROPERTY(bool isKeepAbove READ isKeepAbove NOTIFY isKeepAboveChanged) Q_PROPERTY(bool isOnAllDesktops READ isOnAllDesktops NOTIFY isOnAllDesktopsChanged) Q_PROPERTY(bool isShaded READ isShaded NOTIFY isShadedChanged) - Q_PROPERTY(bool isValid READ isValid NOTIFY isValidChanged) Q_PROPERTY(bool hasSkipTaskbar READ hasSkipTaskbar NOTIFY hasSkipTaskbarChanged) + //! BEGIN: Window Abitilities + /*since Latte v0.9.8*/ + Q_PROPERTY(bool isClosable READ isClosable NOTIFY isClosableChanged) + Q_PROPERTY(bool isFullScreenable READ isFullScreenable NOTIFY isFullScreenableChanged) + Q_PROPERTY(bool isGroupable READ isGroupable NOTIFY isGroupableChanged) + Q_PROPERTY(bool isMaximizable READ isMaximizable NOTIFY isMaximizableChanged) + Q_PROPERTY(bool isMinimizable READ isMinimizable NOTIFY isMinimizableChanged) + Q_PROPERTY(bool isMovable READ isMovable NOTIFY isMovableChanged) + Q_PROPERTY(bool isResizable READ isResizable NOTIFY isResizableChanged) + Q_PROPERTY(bool isShadeable READ isShadeable NOTIFY isShadeableChanged) + Q_PROPERTY(bool isVirtualDesktopChangeable READ isVirtualDesktopChangeable NOTIFY isVirtualDesktopChangeableChanged) + //! END: Window Abitilities + /*since Latte v0.9.4*/ Q_PROPERTY(QString colorScheme READ colorScheme NOTIFY colorSchemeChanged) Q_PROPERTY(QString appName READ appName NOTIFY appNameChanged) Q_PROPERTY(QString display READ display NOTIFY displayChanged) Q_PROPERTY(QRect geometry READ geometry NOTIFY geometryChanged) Q_PROPERTY(QIcon icon READ icon NOTIFY iconChanged) Q_PROPERTY(QVariant winId READ winId NOTIFY winIdChanged) public: LastActiveWindow(TrackedGeneralInfo *trackedInfo); ~LastActiveWindow() override; + bool isValid() const; bool isActive() const; bool isMinimized() const; bool isMaximized() const; bool isFullScreen() const; bool isKeepAbove() const; bool isOnAllDesktops() const; bool isShaded() const; - bool isValid() const; bool hasSkipTaskbar() const; + //! BEGIN: Window Abitilities + /*since Latte v0.9.8*/ + bool isClosable() const; + bool isFullScreenable() const; + bool isGroupable() const; + bool isMaximizable() const; + bool isMinimizable() const; + bool isMovable() const; + bool isResizable() const; + bool isShadeable() const; + bool isVirtualDesktopChangeable() const; + //! END: Window Abitilities + QString appName() const; QString colorScheme() const; QString display() const; QRect geometry() const; QIcon icon() const; QVariant winId() const; void setInformation(const WindowInfoWrap &info); public slots: Q_INVOKABLE void requestActivate(); Q_INVOKABLE void requestClose(); Q_INVOKABLE void requestToggleIsOnAllDesktops(); Q_INVOKABLE void requestToggleKeepAbove(); Q_INVOKABLE void requestToggleMinimized(); Q_INVOKABLE void requestToggleMaximized(); Q_INVOKABLE bool canBeDragged(); void requestMove(Latte::View *fromView, int localX, int localY); private slots: void applicationDataChanged(const WindowId &wid); void windowChanged(const WindowId &wid); void windowRemoved(const WindowId &wid); signals: void colorSchemeChanged(); void iconChanged(); void isActiveChanged(); void isMinimizedChanged(); void isMaximizedChanged(); void isFullScreenChanged(); void isKeepAboveChanged(); void isOnAllDesktopsChanged(); void isShadedChanged(); void isValidChanged(); void hasSkipTaskbarChanged(); + //! BEGIN: Window Abitilities + /*since Latte v0.9.8*/ + void isClosableChanged(); + void isFullScreenableChanged(); + void isGroupableChanged(); + void isMaximizableChanged(); + void isMinimizableChanged(); + void isMovableChanged(); + void isResizableChanged(); + void isShadeableChanged(); + void isVirtualDesktopChangeableChanged(); + //! END: Window Abitilities + void appNameChanged(); void displayChanged(); void geometryChanged(); void winIdChanged(); private: void setActive(bool active); void setIsMinimized(bool minimized); void setIsMaximized(bool maximized); void setIsFullScreen(bool fullscreen); void setIsKeepAbove(bool above); void setIsOnAllDesktops(bool all); void setIsShaded(bool shaded); void setIsValid(bool valid); void setHasSkipTaskbar(bool skip); + //! BEGIN: Window Abitilities + /*since Latte v0.9.8*/ + void setIsClosable(bool closable); + void setIsFullScreenable(bool fullscreenable); + void setIsGroupable(bool groupable); + void setIsMaximizable(bool maximizable); + void setIsMinimizable(bool minimizable); + void setIsMovable(bool movable); + void setIsResizable(bool resizable); + void setIsShadeable(bool shadeable); + void setIsVirtualDesktopsChangeable(bool virtualdestkopschangeable); + //! END: Window Abitilities + void setColorScheme(QString scheme); void setAppName(QString appName); void setDisplay(QString display); void setGeometry(QRect geometry); void setIcon(QIcon icon); void setWinId(QVariant winId); void clearHistory(); void updateColorScheme(); private: bool m_isActive{false}; bool m_isMinimized{false}; bool m_isMaximized{false}; bool m_isFullScreen{false}; bool m_isKeepAbove{false}; bool m_isOnAllDesktops{false}; bool m_isShaded{false}; bool m_isValid{false}; bool m_hasSkipTaskbar{false}; + //! BEGIN: Window Abitilities + /*since Latte v0.9.8*/ + bool m_isClosable{true}; + bool m_isFullScreenable{true}; + bool m_isGroupable{true}; + bool m_isMaximizable{true}; + bool m_isMinimizable{true}; + bool m_isMovable{true}; + bool m_isResizable{true}; + bool m_isShadeable{true}; + bool m_isVirtualDesktopsChangeable{true}; + //! END: Window Abitilities + QString m_colorScheme; QString m_appName; QString m_display; QRect m_geometry; QIcon m_icon; QVariant m_winId; QList m_history; TrackedGeneralInfo *m_trackedInfo{nullptr}; AbstractWindowInterface *m_wm{nullptr}; Tracker::Windows *m_windowsTracker{nullptr}; }; } } } #endif diff --git a/app/wm/waylandinterface.cpp b/app/wm/waylandinterface.cpp index 33cdac5b..39889d96 100644 --- a/app/wm/waylandinterface.cpp +++ b/app/wm/waylandinterface.cpp @@ -1,853 +1,878 @@ /* * 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/positioner.h" #include "view/screenedgeghostwindow.h" #include "view/view.h" #include "../lattecorona.h" #include "../../liblatte2/extras.h" #include "../../liblatte2/types.h" // Qt #include #include #include #include #include // KDE #include #include #include #if KF5_VERSION_MINOR >= 52 #include #endif // X11 #include using namespace KWayland::Client; namespace Latte { class Private::GhostWindow : public QRasterWindow { Q_OBJECT public: WindowSystem::WindowId m_winId; GhostWindow(WindowSystem::WaylandInterface *waylandInterface) : m_waylandInterface(waylandInterface) { setFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::NoDropShadowWindowHint | Qt::WindowDoesNotAcceptFocus); connect(m_waylandInterface, &WindowSystem::AbstractWindowInterface::latteWindowAdded, this, &GhostWindow::identifyWinId); setupWaylandIntegration(); show(); } ~GhostWindow() { m_waylandInterface->unregisterIgnoredWindow(m_winId); delete m_shellSurface; } void setGeometry(const QRect &rect) { if (geometry() == rect) { return; } m_validGeometry = rect; setMinimumSize(rect.size()); setMaximumSize(rect.size()); resize(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}; WindowSystem::WaylandInterface *m_waylandInterface{nullptr}; //! geometry() function under wayland does not return nice results QRect m_validGeometry; public slots: void identifyWinId() { if (m_winId.isNull()) { m_winId = m_waylandInterface->winIdFor("latte-dock", m_validGeometry); m_waylandInterface->registerIgnoredWindow(m_winId); } } }; namespace WindowSystem { WaylandInterface::WaylandInterface(QObject *parent) : AbstractWindowInterface(parent) { m_corona = qobject_cast(parent); } WaylandInterface::~WaylandInterface() { } void WaylandInterface::init() { } void WaylandInterface::initWindowManagement(KWayland::Client::PlasmaWindowManagement *windowManagement) { if (m_windowManagement == windowManagement) { return; } 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 && (!m_ignoredWindows.contains(w->internalId() && !isPlasmaDesktop(w)))) ) { emit activeWindowChanged(w ? w->internalId() : 0); } }, Qt::QueuedConnection); } #if KF5_VERSION_MINOR >= 52 void WaylandInterface::initVirtualDesktopManagement(KWayland::Client::PlasmaVirtualDesktopManagement *virtualDesktopManagement) { if (m_virtualDesktopManagement == virtualDesktopManagement) { return; } m_virtualDesktopManagement = virtualDesktopManagement; connect(m_virtualDesktopManagement, &KWayland::Client::PlasmaVirtualDesktopManagement::desktopCreated, this, [this](const QString &id, quint32 position) { addDesktop(id, position); }); connect(m_virtualDesktopManagement, &KWayland::Client::PlasmaVirtualDesktopManagement::desktopRemoved, this, [this](const QString &id) { m_desktops.removeAll(id); if (m_currentDesktop == id) { setCurrentDesktop(QString()); } }); } void WaylandInterface::addDesktop(const QString &id, quint32 position) { if (m_desktops.contains(id)) { return; } m_desktops.append(id); const KWayland::Client::PlasmaVirtualDesktop *desktop = m_virtualDesktopManagement->getVirtualDesktop(id); QObject::connect(desktop, &KWayland::Client::PlasmaVirtualDesktop::activated, this, [desktop, this]() { setCurrentDesktop(desktop->id()); } ); if (desktop->isActive()) { setCurrentDesktop(id); } } void WaylandInterface::setCurrentDesktop(QString desktop) { if (m_currentDesktop == desktop) { return; } m_currentDesktop = desktop; emit currentDesktopChanged(); } #endif KWayland::Client::PlasmaShell *WaylandInterface::waylandCoronaInterface() const { return m_corona->waylandCoronaInterface(); } //! Register Latte Ignored Windows in order to NOT be tracked void WaylandInterface::registerIgnoredWindow(WindowId wid) { if (!wid.isNull() && !m_ignoredWindows.contains(wid)) { m_ignoredWindows.append(wid); KWayland::Client::PlasmaWindow *w = windowFor(wid); if (w) { untrackWindow(w); } emit windowChanged(wid); } } void WaylandInterface::unregisterIgnoredWindow(WindowId wid) { if (m_ignoredWindows.contains(wid)) { m_ignoredWindows.removeAll(wid); emit windowRemoved(wid); } } void WaylandInterface::setViewExtraFlags(QObject *view, bool isPanelWindow, Latte::Types::Visibility mode) { KWayland::Client::PlasmaShellSurface *surface = qobject_cast(view); Latte::View *latteView = qobject_cast(view); if (latteView) { surface = latteView->surface(); } if (!surface) { return; } surface->setSkipTaskbar(true); #if KF5_VERSION_MINOR >= 47 surface->setSkipSwitcher(true); #endif bool atBottom{!isPanelWindow && (mode == Latte::Types::WindowsCanCover || mode == Latte::Types::WindowsAlwaysCover)}; if (isPanelWindow) { surface->setRole(PlasmaShellSurface::Role::Panel); surface->setPanelBehavior(PlasmaShellSurface::PanelBehavior::AutoHide); } else { surface->setRole(PlasmaShellSurface::Role::Normal); } if (latteView) { WindowId winId = latteView->positioner()->trackedWindowId(); auto w = windowFor(winId); if (w && !w->isOnAllDesktops()) { requestToggleIsOnAllDesktops(winId); } //! Layer to be applied if (mode == Latte::Types::WindowsCanCover || mode == Latte::Types::WindowsAlwaysCover) { setKeepBelow(winId, true); } else if (mode == Latte::Types::NormalWindow) { setKeepBelow(winId, false); setKeepAbove(winId, false); } else { setKeepAbove(winId, true); } } if (atBottom){ //! trying to workaround WM behavior in order //! 1. View at the end MUST NOT HAVE FOCUSABILITY (issue example: clicking a single active task is not minimized) //! 2. View at the end MUST BE AT THE BOTTOM of windows stack QTimer::singleShot(50, [this, surface]() { surface->setRole(PlasmaShellSurface::Role::ToolTip); }); } } 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::switchToNextVirtualDesktop() const { #if KF5_VERSION_MINOR >= 52 if (!m_virtualDesktopManagement || m_desktops.count() <= 1) { return; } int curPos = m_desktops.indexOf(m_currentDesktop); int nextPos = curPos + 1; if (curPos == m_desktops.count()-1) { nextPos = 0; } KWayland::Client::PlasmaVirtualDesktop *desktopObj = m_virtualDesktopManagement->getVirtualDesktop(m_desktops[nextPos]); if (desktopObj) { desktopObj->requestActivate(); } #endif } void WaylandInterface::switchToPreviousVirtualDesktop() const { #if KF5_VERSION_MINOR >= 52 if (!m_virtualDesktopManagement || m_desktops.count() <= 1) { return; } int curPos = m_desktops.indexOf(m_currentDesktop); int nextPos = curPos - 1; if (curPos == 0) { nextPos = m_desktops.count()-1; } KWayland::Client::PlasmaVirtualDesktop *desktopObj = m_virtualDesktopManagement->getVirtualDesktop(m_desktops[nextPos]); if (desktopObj) { desktopObj->requestActivate(); } #endif } 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; } 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::setActiveEdge(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 {}; return requestInfo(w->internalId()); } WindowInfoWrap WaylandInterface::requestInfo(WindowId wid) const { WindowInfoWrap winfoWrap; auto w = windowFor(wid); if (w) { if (isPlasmaDesktop(w)) { winfoWrap.setIsValid(true); winfoWrap.setIsPlasmaDesktop(true); winfoWrap.setWid(wid); + + //! Window Abilities + winfoWrap.setIsClosable(false); + winfoWrap.setIsFullScreenable(false); + winfoWrap.setIsGroupable(false); + winfoWrap.setIsMaximizable(false); + winfoWrap.setIsMinimizable(false); + winfoWrap.setIsMovable(false); + winfoWrap.setIsResizable(false); + winfoWrap.setIsShadeable(false); + winfoWrap.setIsVirtualDesktopsChangeable(false); + //! Window Abilities + } else if (isValidWindow(w)) { winfoWrap.setIsValid(true); winfoWrap.setWid(wid); winfoWrap.setParentId(w->parentWindow() ? w->parentWindow()->internalId() : 0); 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.setIsOnAllDesktops(w->isOnAllDesktops()); winfoWrap.setIsOnAllActivities(true); winfoWrap.setIsKeepAbove(w->isKeepAbove()); winfoWrap.setIsKeepBelow(w->isKeepBelow()); winfoWrap.setGeometry(w->geometry()); winfoWrap.setHasSkipTaskbar(w->skipTaskbar()); + + //! Window Abilities + winfoWrap.setIsClosable(w->isCloseable()); + winfoWrap.setIsFullScreenable(w->isFullscreenable()); + winfoWrap.setIsMaximizable(w->isMaximizeable()); + winfoWrap.setIsMinimizable(w->isMinimizeable()); + winfoWrap.setIsMovable(w->isMovable()); + winfoWrap.setIsResizable(w->isResizable()); + winfoWrap.setIsShadeable(w->isShadeable()); + winfoWrap.setIsVirtualDesktopsChangeable(w->isVirtualDesktopChangeable()); + //! Window Abilities + winfoWrap.setDisplay(w->title()); #if KF5_VERSION_MINOR >= 52 winfoWrap.setDesktops(w->plasmaVirtualDesktops()); #endif winfoWrap.setActivities(QStringList()); } } else { winfoWrap.setIsValid(false); } return winfoWrap; } AppData WaylandInterface::appDataFor(WindowId wid) const { auto window = windowFor(wid); if (window) { const AppData &data = appDataFromUrl(windowUrlFromMetadata(window->appId(), window->pid(), rulesConfig)); return data; } AppData empty; return empty; } 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; } QIcon WaylandInterface::iconFor(WindowId wid) const { auto window = windowFor(wid); if (window) { return window->icon(); } return QIcon(); } WindowId WaylandInterface::winIdFor(QString appId, QRect geometry) const { auto it = std::find_if(m_windowManagement->windows().constBegin(), m_windowManagement->windows().constEnd(), [&appId, &geometry](PlasmaWindow * w) noexcept { return w->isValid() && w->appId() == appId && w->geometry() == geometry; }); if (it == m_windowManagement->windows().constEnd()) { return QVariant(); } return (*it)->internalId(); } bool WaylandInterface::windowCanBeDragged(WindowId wid) const { auto w = windowFor(wid); if (w && isValidWindow(w)) { WindowInfoWrap winfo = requestInfo(wid); return (winfo.isValid() && w->isMovable() && !winfo.isMinimized() && inCurrentDesktopActivity(winfo) && !winfo.isPlasmaDesktop()); } return false; } bool WaylandInterface::windowCanBeMaximized(WindowId wid) const { auto w = windowFor(wid); if (w && isValidWindow(w)) { WindowInfoWrap winfo = requestInfo(wid); return (winfo.isValid() && w->isMaximizeable() && !winfo.isMinimized() && inCurrentDesktopActivity(winfo) && !winfo.isPlasmaDesktop()); } return false; } void WaylandInterface::requestActivate(WindowId wid) const { auto w = windowFor(wid); if (w) { w->requestActivate(); } } void WaylandInterface::requestClose(WindowId wid) const { auto w = windowFor(wid); if (w) { w->requestClose(); } } void WaylandInterface::requestMoveWindow(WindowId wid, QPoint from) const { WindowInfoWrap wInfo = requestInfo(wid); if (windowCanBeDragged(wid) && inCurrentDesktopActivity(wInfo)) { auto w = windowFor(wid); if (w && isValidWindow(w)) { w->requestMove(); } } } void WaylandInterface::requestToggleIsOnAllDesktops(WindowId wid) const { #if KF5_VERSION_MINOR >= 52 auto w = windowFor(wid); if (w && isValidWindow(w) && m_desktops.count() > 1) { if (w->isOnAllDesktops()) { w->requestEnterVirtualDesktop(m_currentDesktop); } else { const QStringList &now = w->plasmaVirtualDesktops(); foreach (const QString &desktop, now) { w->requestLeaveVirtualDesktop(desktop); } } } #endif } void WaylandInterface::requestToggleKeepAbove(WindowId wid) const { auto w = windowFor(wid); if (w) { w->requestToggleKeepAbove(); } } void WaylandInterface::setKeepAbove(WindowId wid, bool active) const { auto w = windowFor(wid); if (w) { if (active) { setKeepBelow(wid, false); } if ((w->isKeepAbove() && active) || (!w->isKeepAbove() && !active)) { return; } w->requestToggleKeepAbove(); } } void WaylandInterface::setKeepBelow(WindowId wid, bool active) const { auto w = windowFor(wid); if (w) { if (active) { setKeepAbove(wid, false); } if ((w->isKeepBelow() && active) || (!w->isKeepBelow() && !active)) { return; } w->requestToggleKeepBelow(); } } void WaylandInterface::requestToggleMinimized(WindowId wid) const { auto w = windowFor(wid); WindowInfoWrap wInfo = requestInfo(wid); if (w && isValidWindow(w) && inCurrentDesktopActivity(wInfo)) { #if KF5_VERSION_MINOR >= 52 if (!m_currentDesktop.isEmpty()) { w->requestEnterVirtualDesktop(m_currentDesktop); } #endif w->requestToggleMinimized(); } } void WaylandInterface::requestToggleMaximized(WindowId wid) const { auto w = windowFor(wid); WindowInfoWrap wInfo = requestInfo(wid); if (w && isValidWindow(w) && windowCanBeMaximized(wid) && inCurrentDesktopActivity(wInfo)) { #if KF5_VERSION_MINOR >= 52 if (!m_currentDesktop.isEmpty()) { w->requestEnterVirtualDesktop(m_currentDesktop); } #endif w->requestToggleMaximized(); } } bool WaylandInterface::isPlasmaDesktop(const KWayland::Client::PlasmaWindow *w) const { if (!w || (w->appId() != QLatin1String("org.kde.plasmashell"))) { return false; } return AbstractWindowInterface::isPlasmaDesktop(w->geometry()); } bool WaylandInterface::isPlasmaPanel(const KWayland::Client::PlasmaWindow *w) const { if (!w || (w->appId() != QLatin1String("org.kde.plasmashell"))) { return false; } return AbstractWindowInterface::isPlasmaPanel(w->geometry()); } 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() && !isPlasmaDesktop(w) && !m_plasmaPanels.contains(w->internalId()) && !m_ignoredWindows.contains(w->internalId()); } void WaylandInterface::updateWindow() { PlasmaWindow *pW = qobject_cast(QObject::sender()); if (pW && !m_ignoredWindows.contains(pW->internalId() && !isPlasmaDesktop(pW) )) { if (pW->appId() == QLatin1String("org.kde.plasmashell")) { if (isPlasmaDesktop(pW)) { return; } else if (isPlasmaPanel(pW)) { registerIgnoredWindow(pW->internalId()); } } considerWindowChanged(pW->internalId()); } } void WaylandInterface::windowUnmapped() { PlasmaWindow *pW = qobject_cast(QObject::sender()); if (pW) { untrackWindow(pW); emit windowRemoved(pW->internalId()); } } void WaylandInterface::trackWindow(KWayland::Client::PlasmaWindow *w) { if (!w) { return; } connect(w, &PlasmaWindow::activeChanged, this, &WaylandInterface::updateWindow); connect(w, &PlasmaWindow::titleChanged, this, &WaylandInterface::updateWindow); connect(w, &PlasmaWindow::fullscreenChanged, this, &WaylandInterface::updateWindow); connect(w, &PlasmaWindow::geometryChanged, this, &WaylandInterface::updateWindow); connect(w, &PlasmaWindow::maximizedChanged, this, &WaylandInterface::updateWindow); connect(w, &PlasmaWindow::minimizedChanged, this, &WaylandInterface::updateWindow); connect(w, &PlasmaWindow::shadedChanged, this, &WaylandInterface::updateWindow); connect(w, &PlasmaWindow::skipTaskbarChanged, this, &WaylandInterface::updateWindow); connect(w, &PlasmaWindow::onAllDesktopsChanged, this, &WaylandInterface::updateWindow); connect(w, &PlasmaWindow::parentWindowChanged, this, &WaylandInterface::updateWindow); #if KF5_VERSION_MINOR >= 52 connect(w, &PlasmaWindow::plasmaVirtualDesktopEntered, this, &WaylandInterface::updateWindow); connect(w, &PlasmaWindow::plasmaVirtualDesktopLeft, this, &WaylandInterface::updateWindow); #else connect(w, &PlasmaWindow::virtualDesktopChanged, this, &WaylandInterface::updateWindow); #endif connect(w, &PlasmaWindow::unmapped, this, &WaylandInterface::windowUnmapped); } void WaylandInterface::untrackWindow(KWayland::Client::PlasmaWindow *w) { if (!w) { return; } disconnect(w, &PlasmaWindow::activeChanged, this, &WaylandInterface::updateWindow); disconnect(w, &PlasmaWindow::titleChanged, this, &WaylandInterface::updateWindow); disconnect(w, &PlasmaWindow::fullscreenChanged, this, &WaylandInterface::updateWindow); disconnect(w, &PlasmaWindow::geometryChanged, this, &WaylandInterface::updateWindow); disconnect(w, &PlasmaWindow::maximizedChanged, this, &WaylandInterface::updateWindow); disconnect(w, &PlasmaWindow::minimizedChanged, this, &WaylandInterface::updateWindow); disconnect(w, &PlasmaWindow::shadedChanged, this, &WaylandInterface::updateWindow); disconnect(w, &PlasmaWindow::skipTaskbarChanged, this, &WaylandInterface::updateWindow); disconnect(w, &PlasmaWindow::onAllDesktopsChanged, this, &WaylandInterface::updateWindow); disconnect(w, &PlasmaWindow::parentWindowChanged, this, &WaylandInterface::updateWindow); #if KF5_VERSION_MINOR >= 52 disconnect(w, &PlasmaWindow::plasmaVirtualDesktopEntered, this, &WaylandInterface::updateWindow); disconnect(w, &PlasmaWindow::plasmaVirtualDesktopLeft, this, &WaylandInterface::updateWindow); #else disconnect(w, &PlasmaWindow::virtualDesktopChanged, this, &WaylandInterface::updateWindow); #endif disconnect(w, &PlasmaWindow::unmapped, this, &WaylandInterface::windowUnmapped); } void WaylandInterface::windowCreatedProxy(KWayland::Client::PlasmaWindow *w) { if (!isValidWindow(w)) { return; } if ((w->appId() == QLatin1String("org.kde.plasmashell")) && isPlasmaPanel(w)) { registerPlasmaPanel(w->internalId()); } else { trackWindow(w); emit windowAdded(w->internalId()); } if (w->appId() == "latte-dock") { emit latteWindowAdded(); } } } } #include "waylandinterface.moc" diff --git a/app/wm/windowinfowrap.h b/app/wm/windowinfowrap.h index 357fd324..dd3c460a 100644 --- a/app/wm/windowinfowrap.h +++ b/app/wm/windowinfowrap.h @@ -1,514 +1,688 @@ /* * 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 WINDOWINFOWRAP_H #define WINDOWINFOWRAP_H // Qt #include #include #include #include namespace Latte { namespace WindowSystem { using WindowId = QVariant; class WindowInfoWrap { public: WindowInfoWrap() noexcept : m_isValid(false) , m_isActive(false) , m_isMinimized(false) , m_isMaxVert(false) , m_isMaxHoriz(false) , m_isFullscreen(false) , m_isShaded(false) , m_isPlasmaDesktop(false) , m_isKeepAbove(false) , m_isKeepBelow(false) , m_hasSkipTaskbar(false) , m_isOnAllDesktops(false) - , m_isOnAllActivities(false) { + , m_isOnAllActivities(false) + , m_isClosable(false) + , m_isFullScreenable(false) + , m_isGroupable(false) + , m_isMaximizable(false) + , m_isMinimizable(false) + , m_isMovable(false) + , m_isResizable(false) + , m_isShadeable(false) + , m_isVirtualDesktopsChangeable(false) + { } WindowInfoWrap(const WindowInfoWrap &o) noexcept : m_wid(o.m_wid) , m_parentId(o.m_parentId) , m_geometry(o.m_geometry) , m_isValid(o.m_isValid) , m_isActive(o.m_isActive) , m_isMinimized(o.m_isMinimized) , m_isMaxVert(o.m_isMaxVert) , m_isMaxHoriz(o.m_isMaxHoriz) , m_isFullscreen(o.m_isFullscreen) , m_isShaded(o.m_isShaded) , m_isPlasmaDesktop(o.m_isPlasmaDesktop) , m_isKeepAbove(o.m_isKeepAbove) , m_isKeepBelow(o.m_isKeepBelow) , m_hasSkipTaskbar(o.m_hasSkipTaskbar) , m_isOnAllDesktops(o.m_isOnAllDesktops) , m_isOnAllActivities(o.m_isOnAllActivities) + , m_isClosable(o.m_isClosable) + , m_isFullScreenable(o.m_isFullScreenable) + , m_isGroupable(o.m_isGroupable) + , m_isMaximizable(o.m_isMaximizable) + , m_isMinimizable(o.m_isMinimizable) + , m_isMovable(o.m_isMovable) + , m_isResizable(o.m_isResizable) + , m_isShadeable(o.m_isShadeable) + , m_isVirtualDesktopsChangeable(o.m_isVirtualDesktopsChangeable) , m_desktops(o.m_desktops) , m_activities(o.m_activities) , m_display(o.m_display) { } WindowInfoWrap(WindowInfoWrap &&o) noexcept : m_wid(o.m_wid) , m_parentId(o.m_parentId) , m_geometry(o.m_geometry) , m_isValid(o.m_isValid) , m_isActive(o.m_isActive) , m_isMinimized(o.m_isMinimized) , m_isMaxVert(o.m_isMaxVert) , m_isMaxHoriz(o.m_isMaxHoriz) , m_isFullscreen(o.m_isFullscreen) , m_isShaded(o.m_isShaded) , m_isPlasmaDesktop(o.m_isPlasmaDesktop) , m_isKeepAbove(o.m_isKeepAbove) , m_isKeepBelow(o.m_isKeepBelow) , m_hasSkipTaskbar(o.m_hasSkipTaskbar) , m_isOnAllDesktops(o.m_isOnAllDesktops) , m_isOnAllActivities(o.m_isOnAllActivities) + , m_isClosable(o.m_isClosable) + , m_isFullScreenable(o.m_isFullScreenable) + , m_isGroupable(o.m_isGroupable) + , m_isMaximizable(o.m_isMaximizable) + , m_isMinimizable(o.m_isMinimizable) + , m_isMovable(o.m_isMovable) + , m_isResizable(o.m_isResizable) + , m_isShadeable(o.m_isShadeable) + , m_isVirtualDesktopsChangeable(o.m_isVirtualDesktopsChangeable) , m_desktops(o.m_desktops) , m_activities(o.m_activities) , m_display(o.m_display) { } inline WindowInfoWrap &operator=(WindowInfoWrap &&rhs) noexcept; inline WindowInfoWrap &operator=(const WindowInfoWrap &rhs) noexcept; inline bool operator==(const WindowInfoWrap &rhs) const noexcept; inline bool operator<(const WindowInfoWrap &rhs) const noexcept; inline bool operator>(const WindowInfoWrap &rhs) const noexcept; inline bool isValid() const noexcept; inline void setIsValid(bool isValid) noexcept; inline bool isActive() const noexcept; inline void setIsActive(bool isActive) noexcept; inline bool isMinimized() const noexcept; inline void setIsMinimized(bool isMinimized) noexcept; inline bool isMaximized() const noexcept; inline bool isMaxVert() const noexcept; inline void setIsMaxVert(bool isMaxVert) noexcept; inline bool isMaxHoriz() const noexcept; inline void setIsMaxHoriz(bool isMaxHoriz) noexcept; inline bool isFullscreen() const noexcept; inline void setIsFullscreen(bool isFullscreen) noexcept; inline bool isShaded() const noexcept; inline void setIsShaded(bool isShaded) noexcept; inline bool isPlasmaDesktop() const noexcept; inline void setIsPlasmaDesktop(bool isPlasmaDesktop) noexcept; inline bool isKeepAbove() const noexcept; inline void setIsKeepAbove(bool isKeepAbove) noexcept; inline bool isKeepBelow() const noexcept; inline void setIsKeepBelow(bool isKeepBelow) noexcept; inline bool hasSkipTaskbar() const noexcept; inline void setHasSkipTaskbar(bool skipTaskbar) noexcept; inline bool isOnAllDesktops() const noexcept; inline void setIsOnAllDesktops(bool alldesktops) noexcept; inline bool isOnAllActivities() const noexcept; inline void setIsOnAllActivities(bool allactivities) noexcept; + //!BEGIN: Window Abilities + inline bool isCloseable() const noexcept; + inline void setIsClosable(bool closable) noexcept; + + inline bool isFullScreenable() const noexcept; + inline void setIsFullScreenable(bool fullscreenable) noexcept; + + inline bool isGroupable() const noexcept; + inline void setIsGroupable(bool groupable) noexcept; + + inline bool isMaximizable() const noexcept; + inline void setIsMaximizable(bool maximizable) noexcept; + + inline bool isMinimizable() const noexcept; + inline void setIsMinimizable(bool minimizable) noexcept; + + inline bool isMovable() const noexcept; + inline void setIsMovable(bool movable) noexcept; + + inline bool isResizable() const noexcept; + inline void setIsResizable(bool resizable) noexcept; + + inline bool isShadeable() const noexcept; + inline void setIsShadeable(bool shadeble) noexcept; + + inline bool isVirtualDesktopsChangeable() const noexcept; + inline void setIsVirtualDesktopsChangeable(bool virtualdesktopchangeable) noexcept; + //!END: Window Abilities + inline bool isMainWindow() const noexcept; inline bool isChildWindow() const noexcept; inline QRect geometry() const noexcept; inline void setGeometry(const QRect &geometry) noexcept; inline QString appName() const noexcept; inline void setAppName(const QString &appName) noexcept; inline QString display() const noexcept; inline void setDisplay(const QString &display) noexcept; inline QIcon icon() const noexcept; inline void setIcon(const QIcon &icon) noexcept; inline WindowId wid() const noexcept; inline void setWid(const WindowId &wid) noexcept; inline WindowId parentId() const noexcept; inline void setParentId(const WindowId &parentId) noexcept; inline QStringList desktops() const noexcept; inline void setDesktops(const QStringList &desktops) noexcept; inline QStringList activities() const noexcept; inline void setActivities(const QStringList &activities) noexcept; inline bool isOnDesktop(const QString &desktop) const noexcept; inline bool isOnActivity(const QString &activity) const noexcept; private: WindowId m_wid{0}; WindowId m_parentId{0}; QRect m_geometry; bool m_isValid : 1; bool m_isActive : 1; bool m_isMinimized : 1; bool m_isMaxVert : 1; bool m_isMaxHoriz : 1; bool m_isFullscreen : 1; bool m_isShaded : 1; bool m_isPlasmaDesktop : 1; bool m_isKeepAbove: 1; bool m_isKeepBelow: 1; bool m_hasSkipTaskbar: 1; bool m_isOnAllDesktops: 1; bool m_isOnAllActivities: 1; + //!BEGIN: Window Abilities + bool m_isClosable : 1; + bool m_isFullScreenable : 1; + bool m_isGroupable : 1; + bool m_isMaximizable : 1; + bool m_isMinimizable : 1; + bool m_isMovable : 1; + bool m_isResizable : 1; + bool m_isShadeable : 1; + bool m_isVirtualDesktopsChangeable : 1; + //!END: Window Abilities + QString m_appName; QString m_display; QIcon m_icon; QStringList m_desktops; QStringList m_activities; }; // BEGIN: definitions inline WindowInfoWrap &WindowInfoWrap::operator=(WindowInfoWrap &&rhs) noexcept { m_wid = rhs.m_wid; m_parentId = rhs.m_parentId; m_geometry = rhs.m_geometry; m_isValid = rhs.m_isValid; m_isActive = rhs.m_isActive; m_isMinimized = rhs.m_isMinimized; m_isMaxVert = rhs.m_isMaxVert; m_isMaxHoriz = rhs.m_isMaxHoriz; m_isFullscreen = rhs.m_isFullscreen; m_isShaded = rhs.m_isShaded; m_isPlasmaDesktop = rhs.m_isPlasmaDesktop; m_isKeepAbove = rhs.m_isKeepAbove; m_isKeepBelow = rhs.m_isKeepBelow; m_hasSkipTaskbar = rhs.m_hasSkipTaskbar; m_isOnAllDesktops = rhs.m_isOnAllDesktops; m_isOnAllActivities = rhs.m_isOnAllActivities; + m_isClosable = rhs.m_isClosable; + m_isFullScreenable = rhs.m_isFullScreenable; + m_isGroupable = rhs.m_isGroupable; + m_isMaximizable = rhs.m_isMaximizable; + m_isMinimizable = rhs.m_isMinimizable; + m_isMovable = rhs.m_isMovable; + m_isResizable = rhs.m_isResizable; + m_isShadeable = rhs.m_isShadeable; + m_isVirtualDesktopsChangeable = rhs.m_isVirtualDesktopsChangeable; + m_display = rhs.m_display; m_desktops = rhs.m_desktops; m_activities = rhs.m_activities; return *this; } inline WindowInfoWrap &WindowInfoWrap::operator=(const WindowInfoWrap &rhs) noexcept { m_wid = rhs.m_wid; m_parentId = rhs.m_parentId; m_geometry = std::move(rhs.m_geometry); m_isValid = rhs.m_isValid; m_isActive = rhs.m_isActive; m_isMinimized = rhs.m_isMinimized; m_isMaxVert = rhs.m_isMaxVert; m_isMaxHoriz = rhs.m_isMaxHoriz; m_isFullscreen = rhs.m_isFullscreen; m_isShaded = rhs.m_isShaded; m_isPlasmaDesktop = rhs.m_isPlasmaDesktop; m_isKeepAbove = rhs.m_isKeepAbove; m_isKeepBelow = rhs.m_isKeepBelow; m_hasSkipTaskbar = rhs.m_hasSkipTaskbar; m_isOnAllDesktops = rhs.m_isOnAllDesktops; m_isOnAllActivities = rhs.m_isOnAllActivities; + m_isClosable = rhs.m_isClosable; + m_isFullScreenable = rhs.m_isFullScreenable; + m_isGroupable = rhs.m_isGroupable; + m_isMaximizable = rhs.m_isMaximizable; + m_isMinimizable = rhs.m_isMinimizable; + m_isMovable = rhs.m_isMovable; + m_isResizable = rhs.m_isResizable; + m_isShadeable = rhs.m_isShadeable; + m_isVirtualDesktopsChangeable = rhs.m_isVirtualDesktopsChangeable;; + m_display = rhs.m_display; m_desktops = rhs.m_desktops; m_activities = rhs.m_activities; return *this; } inline bool WindowInfoWrap::operator==(const WindowInfoWrap &rhs) const noexcept { return m_wid == rhs.m_wid; } inline bool WindowInfoWrap::operator<(const WindowInfoWrap &rhs) const noexcept { return m_wid < rhs.m_wid; } inline bool WindowInfoWrap::operator>(const WindowInfoWrap &rhs) const noexcept { return m_wid > rhs.m_wid; } inline bool WindowInfoWrap::isValid() const noexcept { return m_isValid; } inline void WindowInfoWrap::setIsValid(bool isValid) noexcept { m_isValid = isValid; } inline bool WindowInfoWrap::isActive() const noexcept { return m_isActive; } inline void WindowInfoWrap::setIsActive(bool isActive) noexcept { m_isActive = isActive; } inline bool WindowInfoWrap::isMinimized() const noexcept { return m_isMinimized; } inline void WindowInfoWrap::setIsMinimized(bool isMinimized) noexcept { m_isMinimized = isMinimized; } inline bool WindowInfoWrap::isMaximized() const noexcept { return m_isMaxVert && m_isMaxHoriz; } inline bool WindowInfoWrap::isMaxVert() const noexcept { return m_isMaxVert; } inline void WindowInfoWrap::setIsMaxVert(bool isMaxVert) noexcept { m_isMaxVert = isMaxVert; } inline bool WindowInfoWrap::isMaxHoriz() const noexcept { return m_isMaxHoriz; } inline void WindowInfoWrap::setIsMaxHoriz(bool isMaxHoriz) noexcept { m_isMaxHoriz = isMaxHoriz; } inline bool WindowInfoWrap::isFullscreen() const noexcept { return m_isFullscreen; } inline void WindowInfoWrap::setIsFullscreen(bool isFullscreen) noexcept { m_isFullscreen = isFullscreen; } inline bool WindowInfoWrap::isShaded() const noexcept { return m_isShaded; } inline void WindowInfoWrap::setIsShaded(bool isShaded) noexcept { m_isShaded = isShaded; } inline bool WindowInfoWrap::isPlasmaDesktop() const noexcept { return m_isPlasmaDesktop; } inline void WindowInfoWrap::setIsPlasmaDesktop(bool isPlasmaDesktop) noexcept { m_isPlasmaDesktop = isPlasmaDesktop; } inline bool WindowInfoWrap::isKeepAbove() const noexcept { return m_isKeepAbove; } inline void WindowInfoWrap::setIsKeepAbove(bool isKeepAbove) noexcept { m_isKeepAbove = isKeepAbove; } inline bool WindowInfoWrap::isKeepBelow() const noexcept { return m_isKeepBelow; } inline void WindowInfoWrap::setIsKeepBelow(bool isKeepBelow) noexcept { m_isKeepBelow = isKeepBelow; } inline bool WindowInfoWrap::hasSkipTaskbar() const noexcept { return m_hasSkipTaskbar; } inline void WindowInfoWrap::setHasSkipTaskbar(bool skipTaskbar) noexcept { m_hasSkipTaskbar = skipTaskbar; } inline bool WindowInfoWrap::isOnAllDesktops() const noexcept { return m_isOnAllDesktops; } inline void WindowInfoWrap::setIsOnAllDesktops(bool alldesktops) noexcept { m_isOnAllDesktops = alldesktops; } inline bool WindowInfoWrap::isOnAllActivities() const noexcept { return m_isOnAllActivities; } inline void WindowInfoWrap::setIsOnAllActivities(bool allactivities) noexcept { m_isOnAllActivities = allactivities; } +//!BEGIN: Window Abilities +inline bool WindowInfoWrap::isCloseable() const noexcept +{ + return m_isClosable; +} +inline void WindowInfoWrap::setIsClosable(bool closable) noexcept +{ + m_isClosable = closable; +} + +inline bool WindowInfoWrap::isFullScreenable() const noexcept +{ + return m_isFullScreenable; +} +inline void WindowInfoWrap::setIsFullScreenable(bool fullscreenable) noexcept +{ + m_isFullScreenable = fullscreenable; +} + +inline bool WindowInfoWrap::isGroupable() const noexcept +{ + return m_isGroupable; +} +inline void WindowInfoWrap::setIsGroupable(bool groupable) noexcept +{ + m_isGroupable = groupable; +} + +inline bool WindowInfoWrap::isMaximizable() const noexcept +{ + return m_isMaximizable; +} +inline void WindowInfoWrap::setIsMaximizable(bool maximizable) noexcept +{ + m_isMaximizable = maximizable; +} + +inline bool WindowInfoWrap::isMinimizable() const noexcept +{ + return m_isMinimizable; +} +inline void WindowInfoWrap::setIsMinimizable(bool minimizable) noexcept +{ + m_isMinimizable = minimizable; +} + +inline bool WindowInfoWrap::isMovable() const noexcept +{ + return m_isMovable; +} +inline void WindowInfoWrap::setIsMovable(bool movable) noexcept +{ + m_isMovable = movable; +} + +inline bool WindowInfoWrap::isResizable() const noexcept +{ + return m_isResizable; +} +inline void WindowInfoWrap::setIsResizable(bool resizable) noexcept +{ + m_isResizable = resizable; +} + +inline bool WindowInfoWrap::isShadeable() const noexcept +{ + return m_isShadeable; +} +inline void WindowInfoWrap::setIsShadeable(bool shadeble) noexcept +{ + m_isShadeable = shadeble; +} + +inline bool WindowInfoWrap::isVirtualDesktopsChangeable() const noexcept +{ + return m_isVirtualDesktopsChangeable; +} +inline void WindowInfoWrap::setIsVirtualDesktopsChangeable(bool virtualdesktopchangeable) noexcept +{ + m_isVirtualDesktopsChangeable = virtualdesktopchangeable; +} +//!END: Window Abilities + + + inline bool WindowInfoWrap::isMainWindow() const noexcept { return (m_parentId.toInt() <= 0); } inline bool WindowInfoWrap::isChildWindow() const noexcept { return (m_parentId.toInt() > 0); } inline QString WindowInfoWrap::appName() const noexcept { return m_appName; } inline void WindowInfoWrap::setAppName(const QString &appName) noexcept { m_appName = appName; } inline QString WindowInfoWrap::display() const noexcept { return m_display; } inline void WindowInfoWrap::setDisplay(const QString &display) noexcept { m_display = display; } inline QIcon WindowInfoWrap::icon() const noexcept { return m_icon; } inline void WindowInfoWrap::setIcon(const QIcon &icon) noexcept { m_icon = icon; } inline QRect WindowInfoWrap::geometry() const noexcept { return m_geometry; } inline void WindowInfoWrap::setGeometry(const QRect &geometry) noexcept { m_geometry = geometry; } inline WindowId WindowInfoWrap::wid() const noexcept { return m_wid; } inline void WindowInfoWrap::setWid(const WindowId &wid) noexcept { m_wid = wid; } inline WindowId WindowInfoWrap::parentId() const noexcept { return m_parentId; } inline void WindowInfoWrap::setParentId(const WindowId &parentId) noexcept { if (m_wid == parentId) { return; } m_parentId = parentId; } inline QStringList WindowInfoWrap::desktops() const noexcept { return m_desktops; } inline void WindowInfoWrap::setDesktops(const QStringList &desktops) noexcept { m_desktops = desktops; } inline QStringList WindowInfoWrap::activities() const noexcept { return m_activities; } inline void WindowInfoWrap::setActivities(const QStringList &activities) noexcept { m_activities = activities; } // END: definitions inline bool WindowInfoWrap::isOnDesktop(const QString &desktop) const noexcept { return m_isOnAllDesktops || m_desktops.contains(desktop); } inline bool WindowInfoWrap::isOnActivity(const QString &activity) const noexcept { return m_isOnAllActivities || m_activities.contains(activity); } } } #endif // WINDOWINFOWRAP_H diff --git a/app/wm/xwindowinterface.cpp b/app/wm/xwindowinterface.cpp index 2fe44ea2..2f9d98ad 100644 --- a/app/wm/xwindowinterface.cpp +++ b/app/wm/xwindowinterface.cpp @@ -1,742 +1,755 @@ /* * 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 "xwindowinterface.h" // local #include "tasktools.h" #include "view/screenedgeghostwindow.h" #include "view/view.h" #include "../../liblatte2/extras.h" #include "../../liblatte2/types.h" // Qt #include #include #include // KDE #include #include #include #include // X11 #include #include namespace Latte { namespace WindowSystem { XWindowInterface::XWindowInterface(QObject *parent) : AbstractWindowInterface(parent) { m_currentDesktop = QString(KWindowSystem::self()->currentDesktop()); connect(KWindowSystem::self(), &KWindowSystem::activeWindowChanged, this, &AbstractWindowInterface::activeWindowChanged); connect(KWindowSystem::self(), &KWindowSystem::windowAdded, this, &AbstractWindowInterface::windowAdded); connect(KWindowSystem::self(), &KWindowSystem::windowRemoved, this, &AbstractWindowInterface::windowRemoved); connect(KWindowSystem::self(), &KWindowSystem::currentDesktopChanged, this, [&](int desktop) { m_currentDesktop = QString(desktop); emit currentDesktopChanged(); }); connect(KWindowSystem::self() , static_cast (&KWindowSystem::windowChanged) , this, &XWindowInterface::windowChangedProxy); for(auto wid : KWindowSystem::self()->windows()) { emit windowAdded(wid); windowChangedProxy(wid,0,0); } } XWindowInterface::~XWindowInterface() { } void XWindowInterface::setViewExtraFlags(QObject *view,bool isPanelWindow, Latte::Types::Visibility mode) { WId winId = -1; QQuickView *quickView = qobject_cast(view); if (quickView) { winId = quickView->winId(); } if (!quickView) { QQuickWindow *quickWindow = qobject_cast(view); if (quickWindow) { winId = quickWindow->winId(); } } if (winId < 0) { return; } NETWinInfo winfo(QX11Info::connection() , static_cast(winId) , static_cast(winId) , 0, 0); winfo.setAllowedActions(NET::ActionChangeDesktop); if (isPanelWindow) { KWindowSystem::setType(winId, NET::Dock); } else { KWindowSystem::setType(winId, NET::Normal); } #if KF5_VERSION_MINOR >= 45 KWindowSystem::setState(winId, NET::SkipTaskbar | NET::SkipPager | NET::SkipSwitcher); #else KWindowSystem::setState(winId, NET::SkipTaskbar | NET::SkipPager); #endif KWindowSystem::setOnAllDesktops(winId, true); //! Layer to be applied if (mode == Latte::Types::WindowsCanCover || mode == Latte::Types::WindowsAlwaysCover) { setKeepBelow(winId, true); } else if (mode == Latte::Types::NormalWindow) { setKeepBelow(winId, false); setKeepAbove(winId, false); } else { setKeepAbove(winId, true); } } void XWindowInterface::setViewStruts(QWindow &view, const QRect &rect , Plasma::Types::Location location) { NETExtendedStrut strut; const auto screen = view.screen(); const QRect currentScreen {screen->geometry()}; const QRect wholeScreen {{0, 0}, screen->virtualSize()}; switch (location) { case Plasma::Types::TopEdge: { const int topOffset {screen->geometry().top()}; strut.top_width = rect.height() + topOffset; strut.top_start = rect.x(); strut.top_end = rect.x() + rect.width() - 1; break; } case Plasma::Types::BottomEdge: { const int bottomOffset {wholeScreen.bottom() - currentScreen.bottom()}; strut.bottom_width = rect.height() + bottomOffset; strut.bottom_start = rect.x(); strut.bottom_end = rect.x() + rect.width() - 1; break; } case Plasma::Types::LeftEdge: { const int leftOffset = {screen->geometry().left()}; strut.left_width = rect.width() + leftOffset; strut.left_start = rect.y(); strut.left_end = rect.y() + rect.height() - 1; break; } case Plasma::Types::RightEdge: { const int rightOffset = {wholeScreen.right() - currentScreen.right()}; strut.right_width = rect.width() + rightOffset; strut.right_start = rect.y(); strut.right_end = rect.y() + rect.height() - 1; break; } default: qWarning() << "wrong location:" << qEnumToStr(location); return; } KWindowSystem::setExtendedStrut(view.winId(), strut.left_width, strut.left_start, strut.left_end, strut.right_width, strut.right_start, strut.right_end, strut.top_width, strut.top_start, strut.top_end, strut.bottom_width, strut.bottom_start, strut.bottom_end ); } void XWindowInterface::switchToNextVirtualDesktop() const { int desktops = KWindowSystem::numberOfDesktops(); if (desktops <= 1) { return; } int curPos = KWindowSystem::currentDesktop(); int nextPos = curPos + 1; if (curPos == desktops) { nextPos = 1; } KWindowSystem::setCurrentDesktop(nextPos); } void XWindowInterface::switchToPreviousVirtualDesktop() const { int desktops = KWindowSystem::numberOfDesktops(); if (desktops <= 1) { return; } int curPos = KWindowSystem::currentDesktop(); int nextPos = curPos - 1; if (curPos == 1) { nextPos = desktops; } KWindowSystem::setCurrentDesktop(nextPos); } void XWindowInterface::setWindowOnActivities(QWindow &window, const QStringList &activities) { KWindowSystem::setOnActivities(window.winId(), activities); } void XWindowInterface::removeViewStruts(QWindow &view) const { KWindowSystem::setStrut(view.winId(), 0, 0, 0, 0); } WindowId XWindowInterface::activeWindow() const { return KWindowSystem::self()->activeWindow(); } void XWindowInterface::skipTaskBar(const QDialog &dialog) const { KWindowSystem::setState(dialog.winId(), NET::SkipTaskbar); } void XWindowInterface::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 XWindowInterface::enableBlurBehind(QWindow &view) const { KWindowEffects::enableBlurBehind(view.winId()); } void XWindowInterface::setActiveEdge(QWindow *view, bool active) const { ViewPart::ScreenEdgeGhostWindow *window = qobject_cast(view); if (!window) { return; } xcb_connection_t *c = QX11Info::connection(); const QByteArray effectName = QByteArrayLiteral("_KDE_NET_WM_SCREEN_EDGE_SHOW"); xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData()); QScopedPointer atom(xcb_intern_atom_reply(c, atomCookie, nullptr)); if (!atom) { return; } if (!active) { xcb_delete_property(c, window->winId(), atom->atom); window->hideWithMask(); return; } window->showWithMask(); uint32_t value = 0; switch (window->location()) { case Plasma::Types::TopEdge: value = 0; break; case Plasma::Types::RightEdge: value = 1; break; case Plasma::Types::BottomEdge: value = 2; break; case Plasma::Types::LeftEdge: value = 3; break; case Plasma::Types::Floating: default: value = 4; break; } int hideType = 0; value |= hideType << 8; xcb_change_property(c, XCB_PROP_MODE_REPLACE, window->winId(), atom->atom, XCB_ATOM_CARDINAL, 32, 1, &value); } WindowInfoWrap XWindowInterface::requestInfoActive() const { return requestInfo(KWindowSystem::activeWindow()); } WindowInfoWrap XWindowInterface::requestInfo(WindowId wid) const { const KWindowInfo winfo{wid.value(), NET::WMFrameExtents | NET::WMWindowType | NET::WMGeometry | NET::WMDesktop | NET::WMState | NET::WMName | NET::WMVisibleName, NET::WM2WindowClass | NET::WM2Activities + | NET::WM2AllowedActions | NET::WM2TransientFor}; //! update desktop id bool isDesktop{false}; if (winfo.windowClassName() == "plasmashell" && isPlasmaDesktop(winfo.geometry())) { isDesktop = true; windowsTracker()->setPlasmaDesktop(wid); } WindowInfoWrap winfoWrap; if (!winfo.valid()) { winfoWrap.setIsValid(false); } else if (isValidWindow(winfo) && !isDesktop) { winfoWrap.setIsValid(true); winfoWrap.setWid(wid); winfoWrap.setParentId(winfo.transientFor()); winfoWrap.setIsActive(KWindowSystem::activeWindow() == wid.value()); winfoWrap.setIsMinimized(winfo.hasState(NET::Hidden)); winfoWrap.setIsMaxVert(winfo.hasState(NET::MaxVert)); winfoWrap.setIsMaxHoriz(winfo.hasState(NET::MaxHoriz)); winfoWrap.setIsFullscreen(winfo.hasState(NET::FullScreen)); winfoWrap.setIsShaded(winfo.hasState(NET::Shaded)); winfoWrap.setIsOnAllDesktops(winfo.onAllDesktops()); winfoWrap.setIsOnAllActivities(winfo.activities().empty()); winfoWrap.setGeometry(winfo.frameGeometry()); winfoWrap.setIsKeepAbove(winfo.hasState(NET::KeepAbove)); winfoWrap.setIsKeepBelow(winfo.hasState(NET::KeepBelow)); winfoWrap.setHasSkipTaskbar(winfo.hasState(NET::SkipTaskbar)); - winfoWrap.setDisplay(winfo.visibleName()); + //! Window Abilities + winfoWrap.setIsClosable(winfo.actionSupported(NET::ActionClose)); + winfoWrap.setIsFullScreenable(winfo.actionSupported(NET::ActionFullScreen)); + winfoWrap.setIsMaximizable(winfo.actionSupported(NET::ActionMax)); + winfoWrap.setIsMinimizable(winfo.actionSupported(NET::ActionMinimize)); + winfoWrap.setIsMovable(winfo.actionSupported(NET::ActionMove)); + winfoWrap.setIsResizable(winfo.actionSupported(NET::ActionResize)); + winfoWrap.setIsShadeable(winfo.actionSupported(NET::ActionShade)); + winfoWrap.setIsVirtualDesktopsChangeable(winfo.actionSupported(NET::ActionChangeDesktop)); + //! Window Abilities + + winfoWrap.setDisplay(winfo.visibleName()); winfoWrap.setDesktops({QString(winfo.desktop())}); winfoWrap.setActivities(winfo.activities()); } else if (m_desktopId == wid) { winfoWrap.setIsValid(true); winfoWrap.setIsPlasmaDesktop(true); winfoWrap.setWid(wid); winfoWrap.setParentId(0); winfoWrap.setHasSkipTaskbar(true); + + //! Window Abilities + winfoWrap.setIsClosable(false); + winfoWrap.setIsFullScreenable(false); + winfoWrap.setIsGroupable(false); + winfoWrap.setIsMaximizable(false); + winfoWrap.setIsMinimizable(false); + winfoWrap.setIsMovable(false); + winfoWrap.setIsResizable(false); + winfoWrap.setIsShadeable(false); + winfoWrap.setIsVirtualDesktopsChangeable(false); + //! Window Abilities } return winfoWrap; } AppData XWindowInterface::appDataFor(WindowId wid) const { return appDataFromUrl(windowUrl(wid)); } QUrl XWindowInterface::windowUrl(WindowId wid) const { const KWindowInfo info(wid.value(), 0, NET::WM2WindowClass | NET::WM2DesktopFileName); QString desktopFile = QString::fromUtf8(info.desktopFileName()); if (!desktopFile.isEmpty()) { KService::Ptr service = KService::serviceByStorageId(desktopFile); if (service) { const QString &menuId = service->menuId(); // applications: URLs are used to refer to applications by their KService::menuId // (i.e. .desktop file name) rather than the absolute path to a .desktop file. if (!menuId.isEmpty()) { return QUrl(QStringLiteral("applications:") + menuId); } return QUrl::fromLocalFile(service->entryPath()); } if (!desktopFile.endsWith(QLatin1String(".desktop"))) { desktopFile.append(QLatin1String(".desktop")); } if (KDesktopFile::isDesktopFile(desktopFile) && QFile::exists(desktopFile)) { return QUrl::fromLocalFile(desktopFile); } } return windowUrlFromMetadata(info.windowClassClass(), NETWinInfo(QX11Info::connection(), wid.value(), QX11Info::appRootWindow(), NET::WMPid, NET::Properties2()).pid(), rulesConfig, info.windowClassName()); } bool XWindowInterface::windowCanBeDragged(WindowId wid) const { - KWindowInfo info(wid.value(), 0, NET::WM2AllowedActions); - - if (info.valid()) { - WindowInfoWrap winfo = requestInfo(wid); - return (winfo.isValid() - && info.actionSupported(NET::ActionMove) - && !winfo.isMinimized() - && inCurrentDesktopActivity(winfo) - && !winfo.isPlasmaDesktop()); - } - - return false; + WindowInfoWrap winfo = requestInfo(wid); + return (winfo.isValid() + && !winfo.isMinimized() + && winfo.isMovable() + && inCurrentDesktopActivity(winfo) + && !winfo.isPlasmaDesktop()); } bool XWindowInterface::windowCanBeMaximized(WindowId wid) const { - KWindowInfo info(wid.value(), 0, NET::WM2AllowedActions); - - if (info.valid()) { - WindowInfoWrap winfo = requestInfo(wid); - return (winfo.isValid() - && !winfo.isMinimized() - && info.actionSupported(NET::ActionMax) - && inCurrentDesktopActivity(winfo) - && !winfo.isPlasmaDesktop()); - } - return false; + WindowInfoWrap winfo = requestInfo(wid); + return (winfo.isValid() + && !winfo.isMinimized() + && winfo.isMaximizable() + && inCurrentDesktopActivity(winfo) + && !winfo.isPlasmaDesktop()); } void XWindowInterface::requestActivate(WindowId wid) const { KWindowSystem::activateWindow(wid.toInt()); } QIcon XWindowInterface::iconFor(WindowId wid) const { QIcon icon; icon.addPixmap(KWindowSystem::icon(wid.value(), KIconLoader::SizeSmall, KIconLoader::SizeSmall, false)); icon.addPixmap(KWindowSystem::icon(wid.value(), KIconLoader::SizeSmallMedium, KIconLoader::SizeSmallMedium, false)); icon.addPixmap(KWindowSystem::icon(wid.value(), KIconLoader::SizeMedium, KIconLoader::SizeMedium, false)); icon.addPixmap(KWindowSystem::icon(wid.value(), KIconLoader::SizeLarge, KIconLoader::SizeLarge, false)); return icon; } WindowId XWindowInterface::winIdFor(QString appId, QRect geometry) const { return activeWindow(); } void XWindowInterface::requestClose(WindowId wid) const { WindowInfoWrap wInfo = requestInfo(wid); if (!wInfo.isValid() || wInfo.isPlasmaDesktop()) { return; } NETRootInfo ri(QX11Info::connection(), NET::CloseWindow); ri.closeWindowRequest(wInfo.wid().toUInt()); } void XWindowInterface::requestMoveWindow(WindowId wid, QPoint from) const { WindowInfoWrap wInfo = requestInfo(wid); if (!wInfo.isValid() || wInfo.isPlasmaDesktop() || !inCurrentDesktopActivity(wInfo)) { return; } int borderX = wInfo.geometry().width() > 120 ? 60 : 10; int borderY{10}; //! find min/max values for x,y based on active window geometry int minX = wInfo.geometry().x() + borderX; int maxX = wInfo.geometry().x() + wInfo.geometry().width() - borderX; int minY = wInfo.geometry().y() + borderY; int maxY = wInfo.geometry().y() + wInfo.geometry().height() - borderY; //! set the point from which this window will be moved, //! make sure that it is in window boundaries int validX = qBound(minX, from.x(), maxX); int validY = qBound(minY, from.y(), maxY); NETRootInfo ri(QX11Info::connection(), NET::WMMoveResize); ri.moveResizeRequest(wInfo.wid().toUInt(), validX, validY, NET::Move); } void XWindowInterface::requestToggleIsOnAllDesktops(WindowId wid) const { WindowInfoWrap wInfo = requestInfo(wid); if (!wInfo.isValid() || wInfo.isPlasmaDesktop()) { return; } if (KWindowSystem::numberOfDesktops() <= 1) { return; } if (wInfo.isOnAllDesktops()) { KWindowSystem::setOnDesktop(wid.toUInt(), KWindowSystem::currentDesktop()); KWindowSystem::forceActiveWindow(wid.toUInt()); } else { KWindowSystem::setOnAllDesktops(wid.toUInt(), true); } } void XWindowInterface::requestToggleKeepAbove(WindowId wid) const { WindowInfoWrap wInfo = requestInfo(wid); if (!wInfo.isValid() || wInfo.isPlasmaDesktop()) { return; } NETWinInfo ni(QX11Info::connection(), wid.toUInt(), QX11Info::appRootWindow(), NET::WMState, NET::Properties2()); if (wInfo.isKeepAbove()) { ni.setState(NET::States(), NET::StaysOnTop); } else { ni.setState(NET::StaysOnTop, NET::StaysOnTop); } } void XWindowInterface::setKeepAbove(WindowId wid, bool active) const { if (wid.toUInt() <= 0) { return; } if (active) { KWindowSystem::setState(wid.toUInt(), NET::KeepAbove); KWindowSystem::clearState(wid.toUInt(), NET::KeepBelow); } else { KWindowSystem::clearState(wid.toUInt(), NET::KeepAbove); } } void XWindowInterface::setKeepBelow(WindowId wid, bool active) const { if (wid.toUInt() <= 0) { return; } if (active) { KWindowSystem::setState(wid.toUInt(), NET::KeepBelow); KWindowSystem::clearState(wid.toUInt(), NET::KeepAbove); } else { KWindowSystem::clearState(wid.toUInt(), NET::KeepBelow); } } void XWindowInterface::requestToggleMinimized(WindowId wid) const { WindowInfoWrap wInfo = requestInfo(wid); if (!wInfo.isValid() || wInfo.isPlasmaDesktop() || !inCurrentDesktopActivity(wInfo)) { return; } if (wInfo.isMinimized()) { bool onCurrent = wInfo.isOnDesktop(m_currentDesktop); KWindowSystem::unminimizeWindow(wid.toUInt()); if (onCurrent) { KWindowSystem::forceActiveWindow(wid.toUInt()); } } else { KWindowSystem::minimizeWindow(wid.toUInt()); } } void XWindowInterface::requestToggleMaximized(WindowId wid) const { WindowInfoWrap wInfo = requestInfo(wid); if (!windowCanBeMaximized(wid) || !inCurrentDesktopActivity(wInfo)) { return; } bool restore = wInfo.isMaxHoriz() && wInfo.isMaxVert(); if (wInfo.isMinimized()) { KWindowSystem::unminimizeWindow(wid.toUInt()); } NETWinInfo ni(QX11Info::connection(), wid.toInt(), QX11Info::appRootWindow(), NET::WMState, NET::Properties2()); if (restore) { ni.setState(NET::States(), NET::Max); } else { ni.setState(NET::Max, NET::Max); } } bool XWindowInterface::isValidWindow(WindowId wid) const { if (windowsTracker()->isValidFor(wid)) { return true; } const KWindowInfo winfo{wid.value(), NET::WMWindowType}; return isValidWindow(winfo); } bool XWindowInterface::isValidWindow(const KWindowInfo &winfo) const { if (windowsTracker()->isValidFor(winfo.win())) { return true; } constexpr auto types = NET::DockMask | NET::MenuMask | NET::SplashMask | NET::PopupMenuMask | NET::NormalMask | NET::DialogMask; NET::WindowType winType = winfo.windowType(types); const auto winClass = KWindowInfo(winfo.win(), 0, NET::WM2WindowClass).windowClassName(); //! ignored windows from tracking if (m_ignoredWindows.contains(winfo.win())) { return false; } if (m_desktopId == winfo.win()) { return false; } if (winType == -1) { // Trying to get more types for verify if the window have any other type winType = winfo.windowType(~types & NET::AllTypesMask); if (winType == -1) { qWarning() << KWindowInfo(winfo.win(), 0, NET::WM2WindowClass).windowClassName() << "doesn't have any WindowType, assuming as NET::Normal"; return true; } } bool isMenu = ((winType & NET::Menu) == true); bool isDock = ((winType & NET::Dock) == true); bool isPopup = ((winType & NET::PopupMenu) == true); bool isSplash = ((winType & NET::Splash) == true); //! GTK2+ dialogs case e.g. inkscape, gimp2, etc... //! are both Popups and Splash types, this is why //! we can not black list them here return !(isMenu || isDock); } void XWindowInterface::windowChangedProxy(WId wid, NET::Properties prop1, NET::Properties2 prop2) { const KWindowInfo info(wid, NET::WMGeometry, NET::WM2WindowClass); const auto winClass = info.windowClassName(); //! ignored windows do not trackd if (m_ignoredWindows.contains(wid)) { return; } if (winClass == "plasmashell") { //! update desktop id if (isPlasmaDesktop(info.geometry())) { m_desktopId = wid; windowsTracker()->setPlasmaDesktop(wid); considerWindowChanged(wid); return; } else if (isPlasmaPanel(info.geometry())) { registerPlasmaPanel(wid); return; } } //! accept only NET::Properties events, //! ignore when the user presses a key, or a window is sending X events etc. //! without needing to (e.g. Firefox, https://bugzilla.mozilla.org/show_bug.cgi?id=1389953) //! NET::WM2UserTime, NET::WM2IconPixmap etc.... if (prop1 == 0 && !(prop2 & (NET::WM2Activities | NET::WM2TransientFor))) { return; } //! accept only the following NET:Properties changed signals //! NET::WMState, NET::WMGeometry, NET::ActiveWindow if ( !(prop1 & NET::WMState) && !(prop1 & NET::WMGeometry) && !(prop1 & NET::ActiveWindow) && !(prop1 & NET::WMDesktop) && !(prop1 & (NET::WMName | NET::WMVisibleName) && !(prop2 & NET::WM2TransientFor) && !(prop2 & NET::WM2Activities)) ) { return; } //! ignore windows that do not respect normal windows types if (!isValidWindow(wid)) { return; } considerWindowChanged(wid); } } }