From ed9eab5beac71ac953178ec982f985ad8c4723dd Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sun, 28 Jan 2024 14:48:18 +0100 Subject: [PATCH 001/165] TaskBar: forward declare --- plugin-taskbar/lxqttaskbar.cpp | 12 ++++++++++-- plugin-taskbar/lxqttaskbar.h | 18 +++++++++++------- plugin-taskbar/lxqttaskbarconfiguration.h | 4 ++-- plugin-taskbar/lxqttaskbarplugin.cpp | 10 +++++++++- plugin-taskbar/lxqttaskbarplugin.h | 10 ++++------ plugin-taskbar/lxqttaskbutton.cpp | 8 ++++---- plugin-taskbar/lxqttaskbutton.h | 2 +- plugin-taskbar/lxqttaskgroup.cpp | 2 ++ plugin-taskbar/lxqttaskgroup.h | 6 ++---- 9 files changed, 45 insertions(+), 27 deletions(-) diff --git a/plugin-taskbar/lxqttaskbar.cpp b/plugin-taskbar/lxqttaskbar.cpp index 5dfa289c9..c4fa01da2 100644 --- a/plugin-taskbar/lxqttaskbar.cpp +++ b/plugin-taskbar/lxqttaskbar.cpp @@ -28,6 +28,8 @@ * * END_COMMON_COPYRIGHT_HEADER */ +#include "lxqttaskbar.h" + #include #include #include @@ -39,11 +41,12 @@ #include #include +#include "ilxqtpanelplugin.h" +#include "pluginsettings.h" + #include #include -#include -#include "lxqttaskbar.h" #include "lxqttaskgroup.h" #include "../panel/pluginsettings.h" @@ -591,6 +594,11 @@ void LXQtTaskBar::realign() emit refreshIconGeometry(); } +ILXQtPanel *LXQtTaskBar::panel() const +{ + return mPlugin->panel(); +} + /************************************************ ************************************************/ diff --git a/plugin-taskbar/lxqttaskbar.h b/plugin-taskbar/lxqttaskbar.h index 03e2bd0d2..d80d1ea13 100644 --- a/plugin-taskbar/lxqttaskbar.h +++ b/plugin-taskbar/lxqttaskbar.h @@ -32,30 +32,33 @@ #ifndef LXQTTASKBAR_H #define LXQTTASKBAR_H -#include "../panel/ilxqtpanel.h" -#include "../panel/ilxqtpanelplugin.h" - #include #include #include -#include + #include "../panel/ilxqtpanel.h" #include #include #include +class ILXQtPanel; +class ILXQtPanelPlugin; + class QSignalMapper; -class LXQtTaskButton; class LXQtTaskGroup; -class ElidedButtonStyle; class LeftAlignedTextStyle; namespace LXQt { class GridLayout; } +namespace GlobalKeyShortcut +{ +class Action; +} + class LXQtTaskBar : public QFrame { Q_OBJECT @@ -80,7 +83,8 @@ class LXQtTaskBar : public QFrame bool isIconByClass() const { return mIconByClass; } int wheelEventsAction() const { return mWheelEventsAction; } int wheelDeltaThreshold() const { return mWheelDeltaThreshold; } - inline ILXQtPanel * panel() const { return mPlugin->panel(); } + + ILXQtPanel * panel() const; inline ILXQtPanelPlugin * plugin() const { return mPlugin; } public slots: diff --git a/plugin-taskbar/lxqttaskbarconfiguration.h b/plugin-taskbar/lxqttaskbarconfiguration.h index e559508b7..930e9f441 100644 --- a/plugin-taskbar/lxqttaskbarconfiguration.h +++ b/plugin-taskbar/lxqttaskbarconfiguration.h @@ -29,8 +29,8 @@ #define LXQTTASKBARCONFIGURATION_H #include "../panel/lxqtpanelpluginconfigdialog.h" -#include "../panel/pluginsettings.h" -#include + +class PluginSettings; namespace Ui { class LXQtTaskbarConfiguration; diff --git a/plugin-taskbar/lxqttaskbarplugin.cpp b/plugin-taskbar/lxqttaskbarplugin.cpp index ae8af1403..759e6db46 100644 --- a/plugin-taskbar/lxqttaskbarplugin.cpp +++ b/plugin-taskbar/lxqttaskbarplugin.cpp @@ -28,6 +28,8 @@ #include "lxqttaskbarplugin.h" +#include "lxqttaskbar.h" + #include "lxqttaskbarconfiguration.h" LXQtTaskBarPlugin::LXQtTaskBarPlugin(const ILXQtPanelPluginStartupInfo &startupInfo): @@ -36,7 +38,6 @@ LXQtTaskBarPlugin::LXQtTaskBarPlugin(const ILXQtPanelPluginStartupInfo &startupI { mTaskBar = new LXQtTaskBar(this); - } @@ -45,11 +46,18 @@ LXQtTaskBarPlugin::~LXQtTaskBarPlugin() delete mTaskBar; } +QWidget *LXQtTaskBarPlugin::widget() { return mTaskBar; } + QDialog *LXQtTaskBarPlugin::configureDialog() { return new LXQtTaskbarConfiguration(settings()); } +void LXQtTaskBarPlugin::settingsChanged() +{ + mTaskBar->settingsChanged(); +} + void LXQtTaskBarPlugin::realign() { mTaskBar->realign(); diff --git a/plugin-taskbar/lxqttaskbarplugin.h b/plugin-taskbar/lxqttaskbarplugin.h index 9c3076990..9bc34cf8e 100644 --- a/plugin-taskbar/lxqttaskbarplugin.h +++ b/plugin-taskbar/lxqttaskbarplugin.h @@ -29,10 +29,8 @@ #ifndef LXQTTASKBARPLUGIN_H #define LXQTTASKBARPLUGIN_H -#include "../panel/ilxqtpanel.h" -#include "../panel/ilxqtpanelplugin.h" -#include "lxqttaskbar.h" -#include +#include "ilxqtpanelplugin.h" + class LXQtTaskBar; class LXQtTaskBarPlugin : public QObject, public ILXQtPanelPlugin @@ -45,10 +43,10 @@ class LXQtTaskBarPlugin : public QObject, public ILXQtPanelPlugin QString themeId() const { return QStringLiteral("TaskBar"); } virtual Flags flags() const { return HaveConfigDialog | NeedsHandle; } - QWidget *widget() { return mTaskBar; } + QWidget *widget(); QDialog *configureDialog(); - void settingsChanged() { mTaskBar->settingsChanged(); } + void settingsChanged(); void realign(); bool isSeparate() const { return true; } diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index bd6df460f..2f1fe3116 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -31,6 +31,8 @@ #include "lxqttaskgroup.h" #include "lxqttaskbar.h" +#include "ilxqtpanelplugin.h" + #include #include @@ -49,11 +51,8 @@ #include #include -#include "lxqttaskbutton.h" -#include "lxqttaskgroup.h" -#include "lxqttaskbar.h" - #include + // Necessary for closeApplication() #include @@ -61,6 +60,7 @@ #include #undef Bool + bool LXQtTaskButton::sDraggging = false; /************************************************ diff --git a/plugin-taskbar/lxqttaskbutton.h b/plugin-taskbar/lxqttaskbutton.h index 69f3b41d8..73d0886d4 100644 --- a/plugin-taskbar/lxqttaskbutton.h +++ b/plugin-taskbar/lxqttaskbutton.h @@ -33,12 +33,12 @@ #include #include + #include "../panel/ilxqtpanel.h" class QPainter; class QPalette; class QMimeData; -class LXQtTaskGroup; class LXQtTaskBar; class LeftAlignedTextStyle : public QProxyStyle diff --git a/plugin-taskbar/lxqttaskgroup.cpp b/plugin-taskbar/lxqttaskgroup.cpp index e192baabf..a8aaa3f2a 100644 --- a/plugin-taskbar/lxqttaskgroup.cpp +++ b/plugin-taskbar/lxqttaskgroup.cpp @@ -32,6 +32,8 @@ #include "lxqttaskbar.h" #include "lxqtgrouppopup.h" +#include "ilxqtpanelplugin.h" + #include #include #include diff --git a/plugin-taskbar/lxqttaskgroup.h b/plugin-taskbar/lxqttaskgroup.h index f1e7e2469..3787f411f 100644 --- a/plugin-taskbar/lxqttaskgroup.h +++ b/plugin-taskbar/lxqttaskgroup.h @@ -31,11 +31,9 @@ #ifndef LXQTTASKGROUP_H #define LXQTTASKGROUP_H -#include "../panel/ilxqtpanel.h" -#include "../panel/ilxqtpanelplugin.h" - #include "lxqttaskbutton.h" -#include + +#include class QVBoxLayout; class ILXQtPanelPlugin; From d683aefddc6edbb90d623363de5241a486a035b1 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 27 Jan 2024 18:37:31 +0100 Subject: [PATCH 002/165] TaskBar: new ILXQtTaskbarAbstractBackend This is an abstract interface to operate windows and workspaces --- plugin-taskbar/CMakeLists.txt | 5 + .../ilxqttaskbarabstractbackend.cpp | 25 +++++ plugin-taskbar/ilxqttaskbarabstractbackend.h | 94 +++++++++++++++++++ plugin-taskbar/lxqttaskbartypes.h | 32 +++++++ 4 files changed, 156 insertions(+) create mode 100644 plugin-taskbar/ilxqttaskbarabstractbackend.cpp create mode 100644 plugin-taskbar/ilxqttaskbarabstractbackend.h create mode 100644 plugin-taskbar/lxqttaskbartypes.h diff --git a/plugin-taskbar/CMakeLists.txt b/plugin-taskbar/CMakeLists.txt index 1d627f23e..9ca6321cc 100644 --- a/plugin-taskbar/CMakeLists.txt +++ b/plugin-taskbar/CMakeLists.txt @@ -7,6 +7,9 @@ set(HEADERS lxqttaskbarplugin.h lxqttaskgroup.h lxqtgrouppopup.h + + ilxqttaskbarabstractbackend.h + lxqttaskbartypes.h ) set(SOURCES @@ -16,6 +19,8 @@ set(SOURCES lxqttaskbarplugin.cpp lxqttaskgroup.cpp lxqtgrouppopup.cpp + + ilxqttaskbarabstractbackend.cpp ) set(UIS diff --git a/plugin-taskbar/ilxqttaskbarabstractbackend.cpp b/plugin-taskbar/ilxqttaskbarabstractbackend.cpp new file mode 100644 index 000000000..d0959fb78 --- /dev/null +++ b/plugin-taskbar/ilxqttaskbarabstractbackend.cpp @@ -0,0 +1,25 @@ +#include "ilxqttaskbarabstractbackend.h" + + +ILXQtTaskbarAbstractBackend::ILXQtTaskbarAbstractBackend(QObject *parent) + : QObject(parent) +{ + +} + +void ILXQtTaskbarAbstractBackend::moveApplicationToPrevNextDesktop(WId windowId, bool next) +{ + int count = getWorkspacesCount(); + if (count <= 1) + return; + + int targetWorkspace = getWindowWorkspace(windowId) + (next ? 1 : -1); + + // Wrap around + if (targetWorkspace > count) + targetWorkspace = 1; //TODO: are X11 desktops 1 based? + else if (targetWorkspace < 1) + targetWorkspace = count; + + setWindowOnWorkspace(windowId, targetWorkspace); +} diff --git a/plugin-taskbar/ilxqttaskbarabstractbackend.h b/plugin-taskbar/ilxqttaskbarabstractbackend.h new file mode 100644 index 000000000..f37a4081b --- /dev/null +++ b/plugin-taskbar/ilxqttaskbarabstractbackend.h @@ -0,0 +1,94 @@ +#ifndef ILXQTTASKBARABSTRACTBACKEND_H +#define ILXQTTASKBARABSTRACTBACKEND_H + +#include + +#include "lxqttaskbartypes.h" + +class QIcon; +class QScreen; + +//FIXME: add something like bool KWindowInfo::actionSupported(...) + +class ILXQtTaskbarAbstractBackend : public QObject +{ + Q_OBJECT + +public: + enum class WindowProperty + { + Title = 0, + Icon, + State, + Urgency, + WindowClass, + Workspace + }; + + explicit ILXQtTaskbarAbstractBackend(QObject *parent = nullptr); + + // Windows + virtual bool reloadWindows() = 0; + + virtual QVector getCurrentWindows() const = 0; + + virtual QString getWindowTitle(WId windowId) const = 0; + + virtual bool applicationDemandsAttention(WId windowId) const = 0; + + virtual QIcon getApplicationIcon(WId windowId, int fallbackDevicePixels) const = 0; + + virtual QString getWindowClass(WId windowId) const = 0; + + virtual LXQtTaskBarWindowLayer getWindowLayer(WId windowId) const = 0; + virtual bool setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) = 0; + + virtual LXQtTaskBarWindowState getWindowState(WId windowId) const = 0; + virtual bool setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set = true) = 0; + + virtual bool isWindowActive(WId windowId) const = 0; + virtual bool raiseWindow(WId windowId, bool onCurrentWorkSpace) = 0; + + virtual bool closeWindow(WId windowId) = 0; + + virtual WId getActiveWindow() const = 0; + + // Workspaces + virtual int getWorkspacesCount() const = 0; + virtual QString getWorkspaceName(int idx) const = 0; + + virtual int getCurrentWorkspace() const = 0; + virtual bool setCurrentWorkspace(int idx) = 0; + + virtual int getWindowWorkspace(WId windowId) const = 0; + virtual bool setWindowOnWorkspace(WId windowId, int idx) = 0; + + virtual void moveApplicationToPrevNextDesktop(WId windowId, bool next); // Default implementation + virtual void moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) = 0; + + virtual bool isWindowOnScreen(QScreen *screen, WId windowId) const = 0; + + // X11 Specific + virtual void moveApplication(WId windowId) = 0; + virtual void resizeApplication(WId windowId) = 0; + + virtual void refreshIconGeometry(WId windowId, const QRect &geom) = 0; + +signals: + void reloaded(); + + // Windows + void windowAdded(WId windowId); + void windowRemoved(WId windowId); + void windowPropertyChanged(WId windowId, int prop); + + // Workspaces + void workspacesCountChanged(); + void workspaceNameChanged(int idx); + void currentWorkspaceChanged(int idx); + + // TODO: needed? + void activeWindowChanged(WId windowId); +}; + +#endif // ILXQTTASKBARABSTRACTBACKEND_H diff --git a/plugin-taskbar/lxqttaskbartypes.h b/plugin-taskbar/lxqttaskbartypes.h new file mode 100644 index 000000000..d841fec74 --- /dev/null +++ b/plugin-taskbar/lxqttaskbartypes.h @@ -0,0 +1,32 @@ +#ifndef LXQTTASKBARTYPES_H +#define LXQTTASKBARTYPES_H + +#include + +typedef quintptr WId; + +enum class LXQtTaskBarWindowState +{ + Hidden = 0, + FullScreen, + Minimized, + Maximized, + MaximizedVertically, + MaximizedHorizontally, + Normal, + RolledUp //Shaded +}; + +enum class LXQtTaskBarWindowLayer +{ + KeepBelow = 0, + Normal, + KeepAbove +}; + +enum class LXQtTaskBarWorkspace +{ + ShowOnAll = -1 +}; + +#endif // LXQTTASKBARTYPES_H From 6fc94c98525ecf8f5e6f997a9aa403d5b7f1f3ff Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sun, 28 Jan 2024 16:37:03 +0100 Subject: [PATCH 003/165] ILXQtTaskbarAbstractBackend: add supportsAction() method - Move WindowProperty enum to lxqttaskbartypes.h --- plugin-taskbar/ilxqttaskbarabstractbackend.h | 15 +++---------- plugin-taskbar/lxqttaskbartypes.h | 22 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/plugin-taskbar/ilxqttaskbarabstractbackend.h b/plugin-taskbar/ilxqttaskbarabstractbackend.h index f37a4081b..932d64fc0 100644 --- a/plugin-taskbar/ilxqttaskbarabstractbackend.h +++ b/plugin-taskbar/ilxqttaskbarabstractbackend.h @@ -8,25 +8,16 @@ class QIcon; class QScreen; -//FIXME: add something like bool KWindowInfo::actionSupported(...) - class ILXQtTaskbarAbstractBackend : public QObject { Q_OBJECT public: - enum class WindowProperty - { - Title = 0, - Icon, - State, - Urgency, - WindowClass, - Workspace - }; - explicit ILXQtTaskbarAbstractBackend(QObject *parent = nullptr); + // Backend + virtual bool supportsAction(WId windowId, LXQtTaskBarBackendAction action) const = 0; + // Windows virtual bool reloadWindows() = 0; diff --git a/plugin-taskbar/lxqttaskbartypes.h b/plugin-taskbar/lxqttaskbartypes.h index d841fec74..ccea42027 100644 --- a/plugin-taskbar/lxqttaskbartypes.h +++ b/plugin-taskbar/lxqttaskbartypes.h @@ -5,6 +5,28 @@ typedef quintptr WId; +enum class LXQtTaskBarBackendAction +{ + Move = 0, + Resize, + Maximize, + MaximizeVertically, + MaximizeHorizontally, + Minimize, + RollUp, + FullScreen +}; + +enum class LXQtTaskBarWindowProperty +{ + Title = 0, + Icon, + State, + Urgency, + WindowClass, + Workspace +}; + enum class LXQtTaskBarWindowState { Hidden = 0, From 2fbcb78f09d1d2d14436f1bd0e5eee4d79fa4e5d Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 27 Jan 2024 18:39:44 +0100 Subject: [PATCH 004/165] TaskBar: add X11 backend --- plugin-taskbar/CMakeLists.txt | 2 + plugin-taskbar/lxqttaskbarbackend_x11.cpp | 582 ++++++++++++++++++++++ plugin-taskbar/lxqttaskbarbackend_x11.h | 80 +++ 3 files changed, 664 insertions(+) create mode 100644 plugin-taskbar/lxqttaskbarbackend_x11.cpp create mode 100644 plugin-taskbar/lxqttaskbarbackend_x11.h diff --git a/plugin-taskbar/CMakeLists.txt b/plugin-taskbar/CMakeLists.txt index 9ca6321cc..053381bef 100644 --- a/plugin-taskbar/CMakeLists.txt +++ b/plugin-taskbar/CMakeLists.txt @@ -10,6 +10,7 @@ set(HEADERS ilxqttaskbarabstractbackend.h lxqttaskbartypes.h + lxqttaskbarbackend_x11.h ) set(SOURCES @@ -21,6 +22,7 @@ set(SOURCES lxqtgrouppopup.cpp ilxqttaskbarabstractbackend.cpp + lxqttaskbarbackend_x11.cpp ) set(UIS diff --git a/plugin-taskbar/lxqttaskbarbackend_x11.cpp b/plugin-taskbar/lxqttaskbarbackend_x11.cpp new file mode 100644 index 000000000..cc7759f4e --- /dev/null +++ b/plugin-taskbar/lxqttaskbarbackend_x11.cpp @@ -0,0 +1,582 @@ +#include "lxqttaskbarbackend_x11.h" + +#include +#include +#include + +// Necessary for closeApplication() +#include + +#include +#include + +#include + +//NOTE: Xlib.h defines Bool which conflicts with QJsonValue::Type enum +#include +#undef Bool + +LXQtTaskbarX11Backend::LXQtTaskbarX11Backend(QObject *parent) + : ILXQtTaskbarAbstractBackend(parent) +{ + auto *x11Application = qGuiApp->nativeInterface(); + Q_ASSERT_X(x11Application, "LXQtTaskbarX11Backend", "Constructed without X11 connection"); + m_X11Display = x11Application->display(); + m_xcbConnection = x11Application->connection(); + + connect(KX11Extras::self(), &KX11Extras::windowChanged, this, &LXQtTaskbarX11Backend::onWindowChanged); + connect(KX11Extras::self(), &KX11Extras::windowAdded, this, &LXQtTaskbarX11Backend::onWindowAdded); + connect(KX11Extras::self(), &KX11Extras::windowRemoved, this, &LXQtTaskbarX11Backend::onWindowRemoved); + + connect(KX11Extras::self(), &KX11Extras::numberOfDesktopsChanged, this, &ILXQtTaskbarAbstractBackend::workspacesCountChanged); + connect(KX11Extras::self(), &KX11Extras::currentDesktopChanged, this, &ILXQtTaskbarAbstractBackend::currentWorkspaceChanged); + + connect(KX11Extras::self(), &KX11Extras::activeWindowChanged, this, &ILXQtTaskbarAbstractBackend::activeWindowChanged); +} + +/************************************************ + * Model slots + ************************************************/ +void LXQtTaskbarX11Backend::onWindowChanged(WId windowId, NET::Properties prop, NET::Properties2 prop2) +{ + if(!m_windows.contains(windowId)) + return; + + if(!acceptWindow(windowId)) + { + onWindowRemoved(windowId); + return; + } + + if (prop2.testFlag(NET::WM2WindowClass)) + { + emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::WindowClass)); + } + + // window changed virtual desktop + if (prop.testFlag(NET::WMDesktop) || prop.testFlag(NET::WMGeometry)) + { + emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::Workspace)); + } + + if (prop.testFlag(NET::WMVisibleName) || prop.testFlag(NET::WMName)) + emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::Title)); + + // XXX: we are setting window icon geometry -> don't need to handle NET::WMIconGeometry + // Icon of the button can be based on windowClass + if (prop.testFlag(NET::WMIcon) || prop2.testFlag(NET::WM2WindowClass)) + emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::Icon)); + + bool update_urgency = false; + if (prop2.testFlag(NET::WM2Urgency)) + { + update_urgency = true; + } + + if (prop.testFlag(NET::WMState)) + { + update_urgency = true; + + emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::State)); + } + + if (update_urgency) + emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::Urgency)); +} + +void LXQtTaskbarX11Backend::onWindowAdded(WId windowId) +{ + if(m_windows.contains(windowId)) + return; + + if (!acceptWindow(windowId)) + return; + + addWindow_internal(windowId); +} + +void LXQtTaskbarX11Backend::onWindowRemoved(WId windowId) +{ + const int row = m_windows.indexOf(windowId); + if(row == -1) + return; + + m_windows.removeAt(row); + + emit windowRemoved(windowId); +} + +/************************************************ + * Model private functions + ************************************************/ +bool LXQtTaskbarX11Backend::acceptWindow(WId windowId) const +{ + QFlags ignoreList; + ignoreList |= NET::DesktopMask; + ignoreList |= NET::DockMask; + ignoreList |= NET::SplashMask; + ignoreList |= NET::ToolbarMask; + ignoreList |= NET::MenuMask; + ignoreList |= NET::PopupMenuMask; + ignoreList |= NET::NotificationMask; + + KWindowInfo info(windowId, NET::WMWindowType | NET::WMState, NET::WM2TransientFor); + if (!info.valid()) + return false; + + if (NET::typeMatchesMask(info.windowType(NET::AllTypesMask), ignoreList)) + return false; + + if (info.state() & NET::SkipTaskbar) + return false; + + // WM_TRANSIENT_FOR hint not set - normal window + WId transFor = info.transientFor(); + + WId appRootWindow = XDefaultRootWindow(m_X11Display); + + if (transFor == 0 || transFor == windowId || transFor == appRootWindow) + return true; + + info = KWindowInfo(transFor, NET::WMWindowType); + + QFlags normalFlag; + normalFlag |= NET::NormalMask; + normalFlag |= NET::DialogMask; + normalFlag |= NET::UtilityMask; + + return !NET::typeMatchesMask(info.windowType(NET::AllTypesMask), normalFlag); +} + +void LXQtTaskbarX11Backend::addWindow_internal(WId windowId, bool emitAdded) +{ + m_windows.append(windowId); + if(emitAdded) + emit windowAdded(windowId); +} + + +/************************************************ + * Windows function + ************************************************/ +bool LXQtTaskbarX11Backend::supportsAction(WId windowId, LXQtTaskBarBackendAction action) const +{ + NET::Action x11Action; + + switch (action) + { + case LXQtTaskBarBackendAction::Move: + x11Action = NET::ActionMove; + break; + + case LXQtTaskBarBackendAction::Resize: + x11Action = NET::ActionResize; + break; + + case LXQtTaskBarBackendAction::Maximize: + x11Action = NET::ActionMax; + break; + + case LXQtTaskBarBackendAction::MaximizeVertically: + x11Action = NET::ActionMaxVert; + break; + + case LXQtTaskBarBackendAction::MaximizeHorizontally: + x11Action = NET::ActionMaxHoriz; + break; + + case LXQtTaskBarBackendAction::Minimize: + x11Action = NET::ActionMinimize; + break; + + case LXQtTaskBarBackendAction::RollUp: + x11Action = NET::ActionShade; + break; + + case LXQtTaskBarBackendAction::FullScreen: + x11Action = NET::ActionFullScreen; + break; + + default: + return false; + } + + KWindowInfo info(windowId, NET::Properties(), NET::WM2AllowedActions); + return info.actionSupported(x11Action); +} + +bool LXQtTaskbarX11Backend::reloadWindows() +{ + QVector oldWindows; + qSwap(oldWindows, m_windows); + + // Just add new windows to groups, deleting is up to the groups + const auto x11windows = KX11Extras::stackingOrder(); + for (auto const windowId: x11windows) + { + if (acceptWindow(windowId)) + { + bool emitAdded = !oldWindows.contains(windowId); + addWindow_internal(windowId, emitAdded); + } + } + + //emulate windowRemoved if known window not reported by KWindowSystem + for (auto i = oldWindows.begin(), i_e = oldWindows.end(); i != i_e; i++) + { + WId windowId = *i; + if (!m_windows.contains(windowId)) + { + //TODO: more efficient method? + emit windowRemoved(windowId); + } + } + + //TODO: refreshPlaceholderVisibility() + emit reloaded(); + + return true; +} + +QVector LXQtTaskbarX11Backend::getCurrentWindows() const +{ + return m_windows; +} + +QString LXQtTaskbarX11Backend::getWindowTitle(WId windowId) const +{ + KWindowInfo info(windowId, NET::WMVisibleName | NET::WMName); + QString title = info.visibleName().isEmpty() ? info.name() : info.visibleName(); + return title; +} + +bool LXQtTaskbarX11Backend::applicationDemandsAttention(WId windowId) const +{ + WId appRootWindow = XDefaultRootWindow(m_X11Display); + return NETWinInfo(m_xcbConnection, windowId, appRootWindow, NET::Properties{}, NET::WM2Urgency).urgency() + || KWindowInfo{windowId, NET::WMState}.hasState(NET::DemandsAttention); +} + +QIcon LXQtTaskbarX11Backend::getApplicationIcon(WId windowId, int devicePixels) const +{ + return KX11Extras::icon(windowId, devicePixels, devicePixels); +} + +QString LXQtTaskbarX11Backend::getWindowClass(WId windowId) const +{ + KWindowInfo info(windowId, NET::Properties(), NET::WM2WindowClass); + return QString::fromUtf8(info.windowClassClass()); +} + +LXQtTaskBarWindowLayer LXQtTaskbarX11Backend::getWindowLayer(WId windowId) const +{ + NET::States state = KWindowInfo(windowId, NET::WMState).state(); + if(state.testFlag(NET::KeepAbove)) + return LXQtTaskBarWindowLayer::KeepAbove; + else if(state.testFlag(NET::KeepBelow)) + return LXQtTaskBarWindowLayer::KeepBelow; + return LXQtTaskBarWindowLayer::Normal; +} + +bool LXQtTaskbarX11Backend::setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) +{ + switch(layer) + { + case LXQtTaskBarWindowLayer::KeepAbove: + KX11Extras::clearState(windowId, NET::KeepBelow); + KX11Extras::setState(windowId, NET::KeepAbove); + break; + + case LXQtTaskBarWindowLayer::KeepBelow: + KX11Extras::clearState(windowId, NET::KeepAbove); + KX11Extras::setState(windowId, NET::KeepBelow); + break; + + default: + KX11Extras::clearState(windowId, NET::KeepBelow); + KX11Extras::clearState(windowId, NET::KeepAbove); + break; + } + + return true; +} + +LXQtTaskBarWindowState LXQtTaskbarX11Backend::getWindowState(WId windowId) const +{ + KWindowInfo info(windowId,NET::WMState | NET::XAWMState); + if(info.isMinimized()) + return LXQtTaskBarWindowState::Minimized; + + NET::States state = info.state(); + if(state.testFlag(NET::Hidden)) + return LXQtTaskBarWindowState::Hidden; + if(state.testFlag(NET::Max)) + return LXQtTaskBarWindowState::Maximized; + if(state.testFlag(NET::MaxHoriz)) + return LXQtTaskBarWindowState::MaximizedHorizontally; + if(state.testFlag(NET::MaxVert)) + return LXQtTaskBarWindowState::MaximizedVertically; + if(state.testFlag(NET::Shaded)) + return LXQtTaskBarWindowState::RolledUp; + if(state.testFlag(NET::FullScreen)) + return LXQtTaskBarWindowState::FullScreen; + + return LXQtTaskBarWindowState::Normal; +} + +bool LXQtTaskbarX11Backend::setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set) +{ + // NOTE: window activation is left to the caller + + NET::State x11State; + + switch (state) + { + case LXQtTaskBarWindowState::Minimized: + { + if(set) + KX11Extras::minimizeWindow(windowId); + else + KX11Extras::unminimizeWindow(windowId); + return true; + } + case LXQtTaskBarWindowState::Maximized: + { + x11State = NET::Max; + break; + } + case LXQtTaskBarWindowState::MaximizedVertically: + { + x11State = NET::MaxVert; + break; + } + case LXQtTaskBarWindowState::MaximizedHorizontally: + { + x11State = NET::MaxHoriz; + break; + } + case LXQtTaskBarWindowState::Normal: + { + x11State = NET::Max; //TODO: correct? + break; + } + case LXQtTaskBarWindowState::RolledUp: + { + x11State = NET::Shaded; + break; + } + default: + return false; + } + + if(set) + KX11Extras::setState(windowId, x11State); + else + KX11Extras::clearState(windowId, x11State); + + return true; +} + +bool LXQtTaskbarX11Backend::isWindowActive(WId windowId) const +{ + return KX11Extras::activeWindow() == windowId; +} + +bool LXQtTaskbarX11Backend::raiseWindow(WId windowId, bool onCurrentWorkSpace) +{ + if (onCurrentWorkSpace && getWindowState(windowId) == LXQtTaskBarWindowState::Minimized) + { + setWindowOnWorkspace(windowId, getCurrentWorkspace()); + } + else + { + setCurrentWorkspace(getWindowWorkspace(windowId)); + } + + // bypass focus stealing prevention + KX11Extras::forceActiveWindow(windowId); + + // Clear urgency flag + emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::Urgency)); + + return true; +} + +bool LXQtTaskbarX11Backend::closeWindow(WId windowId) +{ + // FIXME: Why there is no such thing in KWindowSystem?? + NETRootInfo(m_xcbConnection, NET::CloseWindow).closeWindowRequest(windowId); + return true; +} + +WId LXQtTaskbarX11Backend::getActiveWindow() const +{ + return KX11Extras::activeWindow(); +} + + +/************************************************ + * Workspaces + ************************************************/ +int LXQtTaskbarX11Backend::getWorkspacesCount() const +{ + return KX11Extras::numberOfDesktops(); +} + +QString LXQtTaskbarX11Backend::getWorkspaceName(int idx) const +{ + return KX11Extras::desktopName(idx); +} + +int LXQtTaskbarX11Backend::getCurrentWorkspace() const +{ + return KX11Extras::currentDesktop(); +} + +bool LXQtTaskbarX11Backend::setCurrentWorkspace(int idx) +{ + if(KX11Extras::currentDesktop() == idx) + return true; + + KX11Extras::setCurrentDesktop(idx); + return true; +} + +int LXQtTaskbarX11Backend::getWindowWorkspace(WId windowId) const +{ + KWindowInfo info(windowId, NET::WMDesktop); + return info.desktop(); +} + +bool LXQtTaskbarX11Backend::setWindowOnWorkspace(WId windowId, int idx) +{ + KX11Extras::setOnDesktop(windowId, idx); + return true; +} + +void LXQtTaskbarX11Backend::moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) +{ + KWindowInfo info(windowId, NET::WMDesktop); + if (!info.isOnCurrentDesktop()) + KX11Extras::setCurrentDesktop(info.desktop()); + + if (getWindowState(windowId) == LXQtTaskBarWindowState::Minimized) + KX11Extras::unminimizeWindow(windowId); + + KX11Extras::forceActiveWindow(windowId); + + const QRect& windowGeometry = KWindowInfo(windowId, NET::WMFrameExtents).frameGeometry(); + QList screens = QGuiApplication::screens(); + if (screens.size() > 1) + { + for (int i = 0; i < screens.size(); ++i) + { + QRect screenGeometry = screens[i]->geometry(); + if (screenGeometry.intersects(windowGeometry)) + { + int targetScreen = i + (next ? 1 : -1); + if (targetScreen < 0) + targetScreen += screens.size(); + else if (targetScreen >= screens.size()) + targetScreen -= screens.size(); + + QRect targetScreenGeometry = screens[targetScreen]->geometry(); + int X = windowGeometry.x() - screenGeometry.x() + targetScreenGeometry.x(); + int Y = windowGeometry.y() - screenGeometry.y() + targetScreenGeometry.y(); + NET::States state = KWindowInfo(windowId, NET::WMState).state(); + + // NW geometry | y/x | from panel + const int flags = 1 | (0b011 << 8) | (0b010 << 12); + KX11Extras::clearState(windowId, NET::MaxHoriz | NET::MaxVert | NET::Max | NET::FullScreen); + NETRootInfo(m_xcbConnection, NET::Properties(), NET::WM2MoveResizeWindow).moveResizeWindowRequest(windowId, flags, X, Y, 0, 0); + QTimer::singleShot(200, this, [this, windowId, state, raiseOnCurrentDesktop] + { + KX11Extras::setState(windowId, state); + raiseWindow(windowId, raiseOnCurrentDesktop); + }); + break; + } + } + } +} + +bool LXQtTaskbarX11Backend::isWindowOnScreen(QScreen *screen, WId windowId) const +{ + //TODO: old code was: + //return QApplication::desktop()->screenGeometry(parentTaskBar()).intersects(KWindowInfo(mWindow, NET::WMFrameExtents).frameGeometry()); + + if(!screen) + return true; + + QRect r = KWindowInfo(windowId, NET::WMFrameExtents).frameGeometry(); + return screen->geometry().intersects(r); +} + +/************************************************ + * X11 Specific + ************************************************/ +void LXQtTaskbarX11Backend::moveApplication(WId windowId) +{ + KWindowInfo info(windowId, NET::WMDesktop); + if (!info.isOnCurrentDesktop()) + KX11Extras::setCurrentDesktop(info.desktop()); + + if (getWindowState(windowId) == LXQtTaskBarWindowState::Minimized) + KX11Extras::unminimizeWindow(windowId); + + KX11Extras::forceActiveWindow(windowId); + + const QRect& g = KWindowInfo(windowId, NET::WMGeometry).geometry(); + int X = g.center().x(); + int Y = g.center().y(); + QCursor::setPos(X, Y); + NETRootInfo(m_xcbConnection, NET::WMMoveResize).moveResizeRequest(windowId, X, Y, NET::Move); +} + +void LXQtTaskbarX11Backend::resizeApplication(WId windowId) +{ + KWindowInfo info(windowId, NET::WMDesktop); + if (!info.isOnCurrentDesktop()) + KX11Extras::setCurrentDesktop(info.desktop()); + + if (getWindowState(windowId) == LXQtTaskBarWindowState::Minimized) + KX11Extras::unminimizeWindow(windowId); + + KX11Extras::forceActiveWindow(windowId); + + const QRect& g = KWindowInfo(windowId, NET::WMGeometry).geometry(); + int X = g.bottomRight().x(); + int Y = g.bottomRight().y(); + QCursor::setPos(X, Y); + NETRootInfo(m_xcbConnection, NET::WMMoveResize).moveResizeRequest(windowId, X, Y, NET::BottomRight); +} + +void LXQtTaskbarX11Backend::refreshIconGeometry(WId windowId, QRect const & geom) +{ + // NOTE: This function announces where the task icon is, + // such that X11 WMs can perform their related animations correctly. + + WId appRootWindow = XDefaultRootWindow(m_X11Display); + + NETWinInfo info(m_xcbConnection, + windowId, + appRootWindow, + NET::WMIconGeometry, + NET::Properties2()); + NETRect const curr = info.iconGeometry(); + + // see kwindowsystem -> NETWinInfo::setIconGeometry for the scale factor + const qreal scaleFactor = qApp->devicePixelRatio(); + int xPos = geom.x() * scaleFactor; + int yPos = geom.y() * scaleFactor; + int w = geom.width() * scaleFactor; + int h = geom.height() * scaleFactor; + if (xPos == curr.pos.x && yPos == curr.pos.y && w == curr.size.width && h == curr.size.height) + return; + NETRect nrect; + nrect.pos.x = geom.x(); + nrect.pos.y = geom.y(); + nrect.size.height = geom.height(); + nrect.size.width = geom.width(); + info.setIconGeometry(nrect); +} diff --git a/plugin-taskbar/lxqttaskbarbackend_x11.h b/plugin-taskbar/lxqttaskbarbackend_x11.h new file mode 100644 index 000000000..26c721cf7 --- /dev/null +++ b/plugin-taskbar/lxqttaskbarbackend_x11.h @@ -0,0 +1,80 @@ +#ifndef LXQTTASKBARBACKEND_X11_H +#define LXQTTASKBARBACKEND_X11_H + +#include "ilxqttaskbarabstractbackend.h" + +//TODO: make PIMPL to forward declare NET::Properties, Display, xcb_connection_t +#include + +typedef struct _XDisplay Display; +struct xcb_connection_t; + +class LXQtTaskbarX11Backend : public ILXQtTaskbarAbstractBackend +{ + Q_OBJECT + +public: + explicit LXQtTaskbarX11Backend(QObject *parent = nullptr); + + // Backend + virtual bool supportsAction(WId windowId, LXQtTaskBarBackendAction action) const override; + + // Windows + virtual bool reloadWindows() override; + + virtual QVector getCurrentWindows() const override; + virtual QString getWindowTitle(WId windowId) const override; + virtual bool applicationDemandsAttention(WId windowId) const override; + virtual QIcon getApplicationIcon(WId windowId, int devicePixels) const override; + virtual QString getWindowClass(WId windowId) const override; + + virtual LXQtTaskBarWindowLayer getWindowLayer(WId windowId) const override; + virtual bool setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) override; + + virtual LXQtTaskBarWindowState getWindowState(WId windowId) const override; + virtual bool setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set) override; + + virtual bool isWindowActive(WId windowId) const override; + virtual bool raiseWindow(WId windowId, bool onCurrentWorkSpace) override; + + virtual bool closeWindow(WId windowId) override; + + virtual WId getActiveWindow() const override; + + // Workspaces + virtual int getWorkspacesCount() const override; + virtual QString getWorkspaceName(int idx) const override; + + virtual int getCurrentWorkspace() const override; + virtual bool setCurrentWorkspace(int idx) override; + + virtual int getWindowWorkspace(WId windowId) const override; + virtual bool setWindowOnWorkspace(WId windowId, int idx) override; + + virtual void moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) override; + + virtual bool isWindowOnScreen(QScreen *screen, WId windowId) const override; + + // X11 Specific + virtual void moveApplication(WId windowId) override; + virtual void resizeApplication(WId windowId) override; + + virtual void refreshIconGeometry(WId windowId, const QRect &geom) override; + +private slots: + void onWindowChanged(WId windowId, NET::Properties prop, NET::Properties2 prop2); + void onWindowAdded(WId windowId); + void onWindowRemoved(WId windowId); + +private: + bool acceptWindow(WId windowId) const; + void addWindow_internal(WId windowId, bool emitAdded = true); + +private: + Display *m_X11Display; + xcb_connection_t *m_xcbConnection; + + QVector m_windows; +}; + +#endif // LXQTTASKBARBACKEND_X11_H From 1cf81a633bca6c7a42df6f460e1b2b2d1f8bf0e0 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 27 Jan 2024 18:41:42 +0100 Subject: [PATCH 005/165] TaskBar: new LXQtTaskBarProxyModel This model will manage the tasks shown --- plugin-taskbar/CMakeLists.txt | 2 + plugin-taskbar/lxqttaskbarproxymodel.cpp | 257 +++++++++++++++++++++++ plugin-taskbar/lxqttaskbarproxymodel.h | 100 +++++++++ 3 files changed, 359 insertions(+) create mode 100644 plugin-taskbar/lxqttaskbarproxymodel.cpp create mode 100644 plugin-taskbar/lxqttaskbarproxymodel.h diff --git a/plugin-taskbar/CMakeLists.txt b/plugin-taskbar/CMakeLists.txt index 053381bef..be434f6a6 100644 --- a/plugin-taskbar/CMakeLists.txt +++ b/plugin-taskbar/CMakeLists.txt @@ -11,6 +11,7 @@ set(HEADERS ilxqttaskbarabstractbackend.h lxqttaskbartypes.h lxqttaskbarbackend_x11.h + lxqttaskbarproxymodel.h ) set(SOURCES @@ -23,6 +24,7 @@ set(SOURCES ilxqttaskbarabstractbackend.cpp lxqttaskbarbackend_x11.cpp + lxqttaskbarproxymodel.cpp ) set(UIS diff --git a/plugin-taskbar/lxqttaskbarproxymodel.cpp b/plugin-taskbar/lxqttaskbarproxymodel.cpp new file mode 100644 index 000000000..e2f1d3941 --- /dev/null +++ b/plugin-taskbar/lxqttaskbarproxymodel.cpp @@ -0,0 +1,257 @@ +#include "lxqttaskbarproxymodel.h" + +#include "ilxqttaskbarabstractbackend.h" + +#include + +LXQtTaskBarProxyModel::LXQtTaskBarProxyModel(QObject *parent) + : QAbstractListModel(parent) + , m_backend(nullptr) + , m_groupByWindowClass(false) +{ + +} + +int LXQtTaskBarProxyModel::rowCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : m_items.count(); +} + +QVariant LXQtTaskBarProxyModel::data(const QModelIndex &idx, int role) const +{ + if (!idx.isValid() || idx.row() >= m_items.count()) + return QVariant(); + + const LXQtTaskBarProxyModelItem& item = m_items.at(idx.row()); + + switch (role) + { + case Qt::DisplayRole: + return item.windows.count() == 1 ? item.windows.first().title : item.windowClass; + default: + break; + } + + return QVariant(); +} + +QIcon LXQtTaskBarProxyModel::getWindowIcon(int itemRow, int windowIdxInGroup, int devicePixels) const +{ + if(!m_backend || itemRow < 0 || itemRow >= m_items.size() || windowIdxInGroup < 0) + return QIcon(); + + const LXQtTaskBarProxyModelItem& item = m_items.at(itemRow); + if(windowIdxInGroup >= item.windows.size()) + return QIcon(); + + const LXQtTaskBarProxyModelWindow& window = item.windows.at(windowIdxInGroup); + return m_backend->getApplicationIcon(window.windowId, devicePixels); +} + +void LXQtTaskBarProxyModel::onWindowAdded(WId windowId) +{ + QString windowClass = m_backend->getWindowClass(windowId); + bool willAddRow = !m_groupByWindowClass || !hasWindowClass(windowClass); + + if(willAddRow) + { + const int row = m_items.count(); + beginInsertRows(QModelIndex(), row, row); + } + + addWindow_internal(windowId); + + if(willAddRow) + endInsertRows(); +} + +void LXQtTaskBarProxyModel::onWindowRemoved(WId windowId) +{ + int row = -1; + int windowIdxInGroup = -1; + for(int i = 0; i < m_items.count(); i++) + { + windowIdxInGroup = m_items.at(i).indexOfWindow(windowId); + if(windowIdxInGroup != -1) + { + row = i; + break; + } + } + + if(row == -1) + return; + + LXQtTaskBarProxyModelItem& item = m_items[row]; + item.windows.removeAt(windowIdxInGroup); + + if(item.windows.isEmpty()) + { + // Remove the group + beginRemoveRows(QModelIndex(), row, row); + m_items.removeAt(row); + endRemoveRows(); + } +} + +void LXQtTaskBarProxyModel::onWindowPropertyChanged(WId windowId, int prop) +{ + int row = -1; + int windowIdxInGroup = -1; + for(int i = 0; i < m_items.count(); i++) + { + windowIdxInGroup = m_items.at(i).indexOfWindow(windowId); + if(windowIdxInGroup != -1) + { + row = i; + break; + } + } + + if(row == -1) + return; + + LXQtTaskBarProxyModelItem& item = m_items[row]; + LXQtTaskBarProxyModelWindow& window = item.windows[windowIdxInGroup]; + + switch (LXQtTaskBarWindowProperty(prop)) + { + case LXQtTaskBarWindowProperty::Title: + window.title = m_backend->getWindowTitle(window.windowId); + break; + + case LXQtTaskBarWindowProperty::Urgency: + window.demandsAttention = m_backend->applicationDemandsAttention(window.windowId); + break; + + case LXQtTaskBarWindowProperty::WindowClass: + { + // If window class is changed, window won't be part of same group + //TODO: optimize + onWindowRemoved(windowId); + onWindowAdded(windowId); + } + + default: + break; + } + + const QModelIndex idx = index(row); + emit dataChanged(idx, idx, {Qt::DisplayRole, Qt::DecorationRole}); +} + +void LXQtTaskBarProxyModel::addWindow_internal(WId windowId) +{ + LXQtTaskBarProxyModelWindow window; + window.windowId = windowId; + window.title = m_backend->getWindowTitle(window.windowId); + window.demandsAttention = m_backend->applicationDemandsAttention(window.windowId); + + QString windowClass = m_backend->getWindowClass(window.windowId); + + int row = -1; + if(m_groupByWindowClass) + { + // Find existing group + for(int i = 0; i < m_items.count(); i++) + { + if(m_items.at(i).windowClass == windowClass) + { + row = i; + break; + } + } + } + + if(row == -1) + { + // Create new group + LXQtTaskBarProxyModelItem item; + item.windowClass = windowClass; + m_items.append(item); + row = m_items.size() - 1; + } + + // Add window to group + LXQtTaskBarProxyModelItem& item = m_items[row]; + item.windows.append(window); +} + +bool LXQtTaskBarProxyModel::groupByWindowClass() const +{ + return m_groupByWindowClass; +} + +void LXQtTaskBarProxyModel::setGroupByWindowClass(bool newGroupByWindowClass) +{ + if(m_groupByWindowClass == newGroupByWindowClass) + return; + + m_groupByWindowClass = newGroupByWindowClass; + + if(m_backend && !m_items.isEmpty()) + { + beginResetModel(); + + m_items.clear(); + + // Reload current windows + const QVector windows = m_backend->getCurrentWindows(); + m_items.reserve(windows.size()); + + for(WId windowId : windows) + onWindowAdded(windowId); + + m_items.squeeze(); + + endResetModel(); + } + +} + +ILXQtTaskbarAbstractBackend *LXQtTaskBarProxyModel::backend() const +{ + return m_backend; +} + +void LXQtTaskBarProxyModel::setBackend(ILXQtTaskbarAbstractBackend *newBackend) +{ + beginResetModel(); + + m_items.clear(); + + if(m_backend) + { + disconnect(m_backend, &ILXQtTaskbarAbstractBackend::windowAdded, + this, &LXQtTaskBarProxyModel::onWindowAdded); + disconnect(m_backend, &ILXQtTaskbarAbstractBackend::windowRemoved, + this, &LXQtTaskBarProxyModel::onWindowRemoved); + disconnect(m_backend, &ILXQtTaskbarAbstractBackend::windowPropertyChanged, + this, &LXQtTaskBarProxyModel::onWindowPropertyChanged); + } + + m_backend = newBackend; + + if(m_backend) + { + connect(m_backend, &ILXQtTaskbarAbstractBackend::windowAdded, + this, &LXQtTaskBarProxyModel::onWindowAdded); + connect(m_backend, &ILXQtTaskbarAbstractBackend::windowRemoved, + this, &LXQtTaskBarProxyModel::onWindowRemoved); + connect(m_backend, &ILXQtTaskbarAbstractBackend::windowPropertyChanged, + this, &LXQtTaskBarProxyModel::onWindowPropertyChanged); + + // Reload current windows + const QVector windows = m_backend->getCurrentWindows(); + m_items.reserve(windows.size()); + + for(WId windowId : windows) + onWindowAdded(windowId); + + m_items.squeeze(); + } + + m_items.squeeze(); + + endResetModel(); +} diff --git a/plugin-taskbar/lxqttaskbarproxymodel.h b/plugin-taskbar/lxqttaskbarproxymodel.h new file mode 100644 index 000000000..be5799076 --- /dev/null +++ b/plugin-taskbar/lxqttaskbarproxymodel.h @@ -0,0 +1,100 @@ +#ifndef LXQTTASKBARPROXYMODEL_H +#define LXQTTASKBARPROXYMODEL_H + +#include +#include + +#include "lxqttaskbartypes.h" + +class ILXQtTaskbarAbstractBackend; + +class LXQtTaskBarProxyModelWindow +{ +public: + LXQtTaskBarProxyModelWindow() = default; + + WId windowId; + QString title; + bool demandsAttention = false; +}; + +// Single window or group +class LXQtTaskBarProxyModelItem +{ +public: + LXQtTaskBarProxyModelItem() = default; + + QVector windows; + QString windowClass; + + inline bool demandsAttention() const + { + for(const LXQtTaskBarProxyModelWindow& w : windows) + { + if(w.demandsAttention) + return true; + } + + return false; + } + + int indexOfWindow(WId windowId) const + { + for(int i = 0; i < windows.size(); i++) + { + if(windows.at(i).windowId == windowId) + return i; + } + + return -1; + } +}; + +class LXQtTaskBarProxyModel : public QAbstractListModel +{ + Q_OBJECT + +public: + explicit LXQtTaskBarProxyModel(QObject *parent = nullptr); + + // Basic functionality: + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override; + + QIcon getWindowIcon(int itemRow, int windowIdxInGroup, int devicePixels) const; + + ILXQtTaskbarAbstractBackend *backend() const; + void setBackend(ILXQtTaskbarAbstractBackend *newBackend); + + bool groupByWindowClass() const; + void setGroupByWindowClass(bool newGroupByWindowClass); + +private slots: + void onWindowAdded(WId windowId); + void onWindowRemoved(WId windowId); + void onWindowPropertyChanged(WId windowId, int prop); + +private: + void addWindow_internal(WId windowId); + + inline bool hasWindowClass(const QString& windowClass) const + { + for(const LXQtTaskBarProxyModelItem& item : m_items) + { + if(item.windowClass == windowClass) + return true; + } + + return false; + } + +private: + ILXQtTaskbarAbstractBackend *m_backend; + + QVector m_items; + + bool m_groupByWindowClass; +}; + +#endif // LXQTTASKBARPROXYMODEL_H From 10be8178c4400852ea7baf0426799b0df930d14a Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 27 Jan 2024 19:00:46 +0100 Subject: [PATCH 006/165] TaskBar: use backend in LXQtTaskBar --- plugin-taskbar/lxqttaskbar.cpp | 14 +++++++++++--- plugin-taskbar/lxqttaskbar.h | 6 ++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/plugin-taskbar/lxqttaskbar.cpp b/plugin-taskbar/lxqttaskbar.cpp index c4fa01da2..fc60496a3 100644 --- a/plugin-taskbar/lxqttaskbar.cpp +++ b/plugin-taskbar/lxqttaskbar.cpp @@ -54,6 +54,8 @@ #include #undef Bool +#include "lxqttaskbarbackend_x11.h" + using namespace LXQt; /************************************************ @@ -80,7 +82,8 @@ LXQtTaskBar::LXQtTaskBar(ILXQtPanelPlugin *plugin, QWidget *parent) : mWheelDeltaThreshold(300), mPlugin(plugin), mPlaceHolder(new QWidget(this)), - mStyle(new LeftAlignedTextStyle()) + mStyle(new LeftAlignedTextStyle()), + mBackend(nullptr) { setStyle(mStyle); mLayout = new LXQt::GridLayout(this); @@ -94,6 +97,10 @@ LXQtTaskBar::LXQtTaskBar(ILXQtPanelPlugin *plugin, QWidget *parent) : mPlaceHolder->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); mLayout->addWidget(mPlaceHolder); + // Create model + //TODO: use backend factory + mBackend = new LXQtTaskbarX11Backend(this); + QTimer::singleShot(0, this, &LXQtTaskBar::settingsChanged); setAcceptDrops(true); @@ -282,7 +289,7 @@ void LXQtTaskBar::groupBecomeEmptySlot() void LXQtTaskBar::addWindow(WId window) { // If grouping disabled group behaves like regular button - const QString group_id = mGroupingEnabled ? QString::fromUtf8(KWindowInfo(window, NET::Properties(), NET::WM2WindowClass).windowClassClass()) : QString::number(window); + const QString group_id = mGroupingEnabled ? mBackend->getWindowClass(window) : QString::number(window); LXQtTaskGroup *group = nullptr; auto i_group = mKnownWindows.find(window); @@ -534,7 +541,8 @@ void LXQtTaskBar::settingsChanged() if (iconByClassOld != mIconByClass) emit iconByClassChanged(); - refreshTaskList(); + refreshTaskList(); //TODO: remove + mBackend->reloadWindows(); } /************************************************ diff --git a/plugin-taskbar/lxqttaskbar.h b/plugin-taskbar/lxqttaskbar.h index d80d1ea13..c36bca7e0 100644 --- a/plugin-taskbar/lxqttaskbar.h +++ b/plugin-taskbar/lxqttaskbar.h @@ -50,6 +50,8 @@ class LXQtTaskGroup; class LeftAlignedTextStyle; +class ILXQtTaskbarAbstractBackend; + namespace LXQt { class GridLayout; } @@ -87,6 +89,8 @@ class LXQtTaskBar : public QFrame ILXQtPanel * panel() const; inline ILXQtPanelPlugin * plugin() const { return mPlugin; } + inline ILXQtTaskbarAbstractBackend *getBackend() const { return mBackend; } + public slots: void settingsChanged(); @@ -156,6 +160,8 @@ private slots: ILXQtPanelPlugin *mPlugin; QWidget *mPlaceHolder; LeftAlignedTextStyle *mStyle; + + ILXQtTaskbarAbstractBackend *mBackend; }; #endif // LXQTTASKBAR_H From c9f0cae1efc5daac96bac11f08e6c39df87094b6 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 16:30:07 +0100 Subject: [PATCH 007/165] TaskBar: initial use of backend in LXQtTaskButton --- plugin-taskbar/lxqttaskbutton.cpp | 169 +++--------------------------- plugin-taskbar/lxqttaskbutton.h | 7 +- plugin-taskbar/lxqttaskgroup.cpp | 28 ++--- 3 files changed, 35 insertions(+), 169 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 2f1fe3116..3d2ad0de0 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -51,6 +51,7 @@ #include #include +//TODO: remove #include // Necessary for closeApplication() @@ -61,6 +62,8 @@ #undef Bool +#include "ilxqttaskbarabstractbackend.h" + bool LXQtTaskButton::sDraggging = false; /************************************************ @@ -84,6 +87,7 @@ void LeftAlignedTextStyle::drawItemText(QPainter * painter, const QRect & rect, ************************************************/ LXQtTaskButton::LXQtTaskButton(const WId window, LXQtTaskBar * taskbar, QWidget *parent) : QToolButton(parent), + mBackend(taskbar->getBackend()), mWindow(window), mUrgencyHint(false), mOrigin(Qt::TopLeftCorner), @@ -144,8 +148,7 @@ LXQtTaskButton::~LXQtTaskButton() = default; ************************************************/ void LXQtTaskButton::updateText() { - KWindowInfo info(mWindow, NET::WMVisibleName | NET::WMName); - QString title = info.visibleName().isEmpty() ? info.name() : info.visibleName(); + QString title = mBackend->getWindowTitle(mWindow); setText(title.replace(QStringLiteral("&"), QStringLiteral("&&"))); setToolTip(title); } @@ -168,57 +171,6 @@ void LXQtTaskButton::updateIcon() setIcon(ico.isNull() ? XdgIcon::defaultApplicationIcon() : ico); } -/************************************************ - - ************************************************/ -void LXQtTaskButton::refreshIconGeometry(QRect const & geom) -{ - // NOTE: This function announces where the task icon is, - // such that X11 WMs can perform their related animations correctly. - - WId appRootWindow = 0; - xcb_connection_t* x11conn = nullptr; - - auto *x11Application = qGuiApp->nativeInterface(); - if(x11Application) - { - appRootWindow = XDefaultRootWindow(x11Application->display()); - x11conn = x11Application->connection(); - } - else - { - //qWarning() << "LXQtTaskBar: not implemented on Wayland"; - return; - } - - - if (!x11conn) { - return; - } - - NETWinInfo info(x11conn, - windowId(), - appRootWindow, - NET::WMIconGeometry, - NET::Properties2()); - NETRect const curr = info.iconGeometry(); - - // see kwindowsystem -> NETWinInfo::setIconGeometry for the scale factor - const qreal scaleFactor = qApp->devicePixelRatio(); - int xPos = geom.x() * scaleFactor; - int yPos = geom.y() * scaleFactor; - int w = geom.width() * scaleFactor; - int h = geom.height() * scaleFactor; - if (xPos == curr.pos.x && yPos == curr.pos.y && w == curr.size.width && h == curr.size.height) - return; - NETRect nrect; - nrect.pos.x = geom.x(); - nrect.pos.y = geom.y(); - nrect.size.height = geom.height(); - nrect.size.width = geom.width(); - info.setIconGeometry(nrect); -} - /************************************************ ************************************************/ @@ -434,8 +386,7 @@ void LXQtTaskButton::mouseMoveEvent(QMouseEvent* event) ************************************************/ bool LXQtTaskButton::isApplicationHidden() const { - KWindowInfo info(mWindow, NET::WMState); - return (info.state() & NET::Hidden); + return false; //FIXME: unused } /************************************************ @@ -443,7 +394,7 @@ bool LXQtTaskButton::isApplicationHidden() const ************************************************/ bool LXQtTaskButton::isApplicationActive() const { - return KX11Extras::activeWindow() == mWindow; + return mBackend->isWindowActive(mWindow); } /************************************************ @@ -588,12 +539,12 @@ void LXQtTaskButton::moveApplicationToDesktop() return; bool ok; - int desk = act->data().toInt(&ok); + int idx = act->data().toInt(&ok); if (!ok) return; - KX11Extras::setOnDesktop(mWindow, desk); + mBackend->setWindowOnWorkspace(mWindow, idx); } /************************************************ @@ -601,17 +552,7 @@ void LXQtTaskButton::moveApplicationToDesktop() ************************************************/ void LXQtTaskButton::moveApplicationToPrevNextDesktop(bool next) { - int deskNum = KX11Extras::numberOfDesktops(); - if (deskNum <= 1) - return; - int targetDesk = KWindowInfo(mWindow, NET::WMDesktop).desktop() + (next ? 1 : -1); - // wrap around - if (targetDesk > deskNum) - targetDesk = 1; - else if (targetDesk < 1) - targetDesk = deskNum; - - KX11Extras::setOnDesktop(mWindow, targetDesk); + mBackend->moveApplicationToPrevNextDesktop(mWindow, next); } /************************************************ @@ -619,53 +560,7 @@ void LXQtTaskButton::moveApplicationToPrevNextDesktop(bool next) ************************************************/ void LXQtTaskButton::moveApplicationToPrevNextMonitor(bool next) { - KWindowInfo info(mWindow, NET::WMDesktop); - if (!info.isOnCurrentDesktop()) - KX11Extras::setCurrentDesktop(info.desktop()); - if (isMinimized()) - KX11Extras::unminimizeWindow(mWindow); - KX11Extras::forceActiveWindow(mWindow); - const QRect& windowGeometry = KWindowInfo(mWindow, NET::WMFrameExtents).frameGeometry(); - QList screens = QGuiApplication::screens(); - if (screens.size() > 1){ - for (int i = 0; i < screens.size(); ++i) - { - QRect screenGeometry = screens[i]->geometry(); - if (screenGeometry.intersects(windowGeometry)) - { - int targetScreen = i + (next ? 1 : -1); - if (targetScreen < 0) - targetScreen += screens.size(); - else if (targetScreen >= screens.size()) - targetScreen -= screens.size(); - QRect targetScreenGeometry = screens[targetScreen]->geometry(); - int X = windowGeometry.x() - screenGeometry.x() + targetScreenGeometry.x(); - int Y = windowGeometry.y() - screenGeometry.y() + targetScreenGeometry.y(); - NET::States state = KWindowInfo(mWindow, NET::WMState).state(); - // NW geometry | y/x | from panel - const int flags = 1 | (0b011 << 8) | (0b010 << 12); - KX11Extras::clearState(mWindow, NET::MaxHoriz | NET::MaxVert | NET::Max | NET::FullScreen); - - - auto *x11Application = qGuiApp->nativeInterface(); - if(x11Application) - { - NETRootInfo(x11Application->connection(), NET::Properties(), NET::WM2MoveResizeWindow).moveResizeWindowRequest(mWindow, flags, X, Y, 0, 0); - } - else - { - //qWarning() << "LXQtTaskBar: not implemented on Wayland"; - } - - QTimer::singleShot(200, this, [this, state] - { - KX11Extras::setState(mWindow, state); - raiseApplication(); - }); - break; - } - } - } + mBackend->moveApplicationToPrevNextMonitor(mWindow, next, parentTaskBar()->raiseOnCurrentDesktop()); } /************************************************ @@ -673,26 +568,7 @@ void LXQtTaskButton::moveApplicationToPrevNextMonitor(bool next) ************************************************/ void LXQtTaskButton::moveApplication() { - KWindowInfo info(mWindow, NET::WMDesktop); - if (!info.isOnCurrentDesktop()) - KX11Extras::setCurrentDesktop(info.desktop()); - if (isMinimized()) - KX11Extras::unminimizeWindow(mWindow); - KX11Extras::forceActiveWindow(mWindow); - const QRect& g = KWindowInfo(mWindow, NET::WMGeometry).geometry(); - int X = g.center().x(); - int Y = g.center().y(); - QCursor::setPos(X, Y); - - auto *x11Application = qGuiApp->nativeInterface(); - if(x11Application) - { - NETRootInfo(x11Application->connection(), NET::WMMoveResize).moveResizeRequest(mWindow, X, Y, NET::Move); - } - else - { - //qWarning() << "LXQtTaskBar: not implemented on Wayland"; - } + mBackend->moveApplication(mWindow); } /************************************************ @@ -700,26 +576,7 @@ void LXQtTaskButton::moveApplication() ************************************************/ void LXQtTaskButton::resizeApplication() { - KWindowInfo info(mWindow, NET::WMDesktop); - if (!info.isOnCurrentDesktop()) - KX11Extras::setCurrentDesktop(info.desktop()); - if (isMinimized()) - KX11Extras::unminimizeWindow(mWindow); - KX11Extras::forceActiveWindow(mWindow); - const QRect& g = KWindowInfo(mWindow, NET::WMGeometry).geometry(); - int X = g.bottomRight().x(); - int Y = g.bottomRight().y(); - QCursor::setPos(X, Y); - - auto *x11Application = qGuiApp->nativeInterface(); - if(x11Application) - { - NETRootInfo(x11Application->connection(), NET::WMMoveResize).moveResizeRequest(mWindow, X, Y, NET::BottomRight); - } - else - { - //qWarning() << "LXQtTaskBar: not implemented on Wayland"; - } + mBackend->resizeApplication(mWindow); } /************************************************ diff --git a/plugin-taskbar/lxqttaskbutton.h b/plugin-taskbar/lxqttaskbutton.h index 73d0886d4..9ccca36fa 100644 --- a/plugin-taskbar/lxqttaskbutton.h +++ b/plugin-taskbar/lxqttaskbutton.h @@ -41,6 +41,8 @@ class QPalette; class QMimeData; class LXQtTaskBar; +class ILXQtTaskbarAbstractBackend; + class LeftAlignedTextStyle : public QProxyStyle { using QProxyStyle::QProxyStyle; @@ -79,7 +81,6 @@ class LXQtTaskButton : public QToolButton LXQtTaskBar * parentTaskBar() const {return mParentTaskBar;} - void refreshIconGeometry(QRect const & geom); static QString mimeDataFormat() { return QLatin1String("lxqt/lxqttaskbutton"); } /*! \return true if this button received DragEnter event (and no DragLeave event yet) * */ @@ -121,6 +122,10 @@ public slots: inline ILXQtPanelPlugin * plugin() const { return mPlugin; } +protected: + //TODO: public getter instead? + ILXQtTaskbarAbstractBackend *mBackend; + private: void moveApplicationToPrevNextDesktop(bool next); void moveApplicationToPrevNextMonitor(bool next); diff --git a/plugin-taskbar/lxqttaskgroup.cpp b/plugin-taskbar/lxqttaskgroup.cpp index a8aaa3f2a..0f3a19df8 100644 --- a/plugin-taskbar/lxqttaskgroup.cpp +++ b/plugin-taskbar/lxqttaskgroup.cpp @@ -43,9 +43,13 @@ #include #include +//TODO: remove #include //For nativeInterface() #include #include +#undef Bool + +#include "ilxqttaskbarabstractbackend.h" /************************************************ @@ -62,14 +66,14 @@ LXQtTaskGroup::LXQtTaskGroup(const QString &groupName, WId window, LXQtTaskBar * setObjectName(groupName); setText(groupName); - connect(this, &LXQtTaskGroup::clicked, this, &LXQtTaskGroup::onClicked); - connect(KX11Extras::self(), &KX11Extras::currentDesktopChanged, this, &LXQtTaskGroup::onDesktopChanged); - connect(KX11Extras::self(), &KX11Extras::activeWindowChanged, this, &LXQtTaskGroup::onActiveWindowChanged); - connect(parent, &LXQtTaskBar::buttonRotationRefreshed, this, &LXQtTaskGroup::setAutoRotation); - connect(parent, &LXQtTaskBar::refreshIconGeometry, this, &LXQtTaskGroup::refreshIconsGeometry); - connect(parent, &LXQtTaskBar::buttonStyleRefreshed, this, &LXQtTaskGroup::setToolButtonsStyle); - connect(parent, &LXQtTaskBar::showOnlySettingChanged, this, &LXQtTaskGroup::refreshVisibility); - connect(parent, &LXQtTaskBar::popupShown, this, &LXQtTaskGroup::groupPopupShown); + connect(this, &LXQtTaskGroup::clicked, this, &LXQtTaskGroup::onClicked); + connect(parent, &LXQtTaskBar::buttonRotationRefreshed, this, &LXQtTaskGroup::setAutoRotation); + connect(parent, &LXQtTaskBar::refreshIconGeometry, this, &LXQtTaskGroup::refreshIconsGeometry); + connect(parent, &LXQtTaskBar::buttonStyleRefreshed, this, &LXQtTaskGroup::setToolButtonsStyle); + connect(parent, &LXQtTaskBar::showOnlySettingChanged, this, &LXQtTaskGroup::refreshVisibility); + connect(parent, &LXQtTaskBar::popupShown, this, &LXQtTaskGroup::groupPopupShown); + connect(mBackend, &ILXQtTaskbarAbstractBackend::currentWorkspaceChanged, this, &LXQtTaskGroup::onDesktopChanged); + connect(mBackend, &ILXQtTaskbarAbstractBackend::activeWindowChanged, this, &LXQtTaskGroup::onActiveWindowChanged); } /************************************************ @@ -102,7 +106,7 @@ void LXQtTaskGroup::contextMenuEvent(QContextMenuEvent *event) void LXQtTaskGroup::closeGroup() { for (LXQtTaskButton *button : std::as_const(mButtonHash) ) - if (button->isOnDesktop(KX11Extras::currentDesktop())) + if (button->isOnDesktop(mBackend->getCurrentWorkspace())) button->closeApplication(); } @@ -310,7 +314,7 @@ void LXQtTaskGroup::onClicked(bool) { if (visibleButtonsCount() > 1) { - setChecked(mButtonHash.contains(KX11Extras::activeWindow())); + setChecked(mButtonHash.contains(mBackend->getActiveWindow())); setPopupVisible(true); } } @@ -449,13 +453,13 @@ void LXQtTaskGroup::refreshIconsGeometry() if (mSingleButton) { - refreshIconGeometry(rect); + mBackend->refreshIconGeometry(windowId(), rect); return; } for(LXQtTaskButton *but : std::as_const(mButtonHash)) { - but->refreshIconGeometry(rect); + mBackend->refreshIconGeometry(but->windowId(), rect); but->setIconSize(QSize(plugin()->panel()->iconSize(), plugin()->panel()->iconSize())); } } From 2629ce1888cd4cce0145abd140c5386469097d60 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 16:34:11 +0100 Subject: [PATCH 008/165] TaskBar: use backend for urgency hint --- plugin-taskbar/lxqttaskbutton.cpp | 14 +------------- plugin-taskbar/lxqttaskgroup.cpp | 23 +++++------------------ 2 files changed, 6 insertions(+), 31 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 3d2ad0de0..7febd5638 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -28,7 +28,6 @@ * END_COMMON_COPYRIGHT_HEADER */ #include "lxqttaskbutton.h" -#include "lxqttaskgroup.h" #include "lxqttaskbar.h" #include "ilxqtpanelplugin.h" @@ -121,18 +120,7 @@ LXQtTaskButton::LXQtTaskButton(const WId window, LXQtTaskBar * taskbar, QWidget mWheelDelta = 0; // forget previous wheel deltas }); - auto *x11Application = qGuiApp->nativeInterface(); - if(x11Application) - { - WId appRootWindow = XDefaultRootWindow(x11Application->display()); - setUrgencyHint(NETWinInfo(x11Application->connection(), mWindow, appRootWindow, NET::Properties{}, NET::WM2Urgency).urgency() - || KWindowInfo{mWindow, NET::WMState}.hasState(NET::DemandsAttention)); - } - else - { - qWarning() << "LXQtTaskBar: not implemented on Wayland"; - } - + setUrgencyHint(mBackend->applicationDemandsAttention(mWindow)); connect(LXQt::Settings::globalSettings(), &LXQt::GlobalSettings::iconThemeChanged, this, &LXQtTaskButton::updateIcon); connect(mParentTaskBar, &LXQtTaskBar::iconByClassChanged, this, &LXQtTaskButton::updateIcon); diff --git a/plugin-taskbar/lxqttaskgroup.cpp b/plugin-taskbar/lxqttaskgroup.cpp index 0f3a19df8..e4991c8a4 100644 --- a/plugin-taskbar/lxqttaskgroup.cpp +++ b/plugin-taskbar/lxqttaskgroup.cpp @@ -45,7 +45,6 @@ //TODO: remove #include //For nativeInterface() -#include #include #undef Bool @@ -670,30 +669,18 @@ bool LXQtTaskGroup::onWindowChanged(WId window, NET::Properties prop, NET::Prope if (prop2.testFlag(NET::WM2Urgency)) { set_urgency = true; - if (auto *x11Application = qGuiApp->nativeInterface()) - { - WId appRootWindow = XDefaultRootWindow(x11Application->display()); - urgency = NETWinInfo(x11Application->connection(), window, appRootWindow, NET::Properties{}, NET::WM2Urgency).urgency(); - } + //FIXME: original code here did not consider "demand attention", was it intentional? + urgency = mBackend->applicationDemandsAttention(window); } if (prop.testFlag(NET::WMState)) { KWindowInfo info{window, NET::WMState}; - if(!set_urgency) - { - if (auto *x11Application = qGuiApp->nativeInterface()) - { - WId appRootWindow = XDefaultRootWindow(x11Application->display()); - urgency = NETWinInfo(x11Application->connection(), window, appRootWindow, NET::Properties{}, NET::WM2Urgency).urgency(); - } - } - // Force refresh urgency - //TODO: maybe do it in common place with NET::WM2Urgency - std::for_each(buttons.begin(), buttons.end(), std::bind(&LXQtTaskButton::setUrgencyHint, std::placeholders::_1, urgency || info.hasState(NET::DemandsAttention))); + if (!set_urgency) + urgency = mBackend->applicationDemandsAttention(window); + std::for_each(buttons.begin(), buttons.end(), std::bind(&LXQtTaskButton::setUrgencyHint, std::placeholders::_1, urgency)); set_urgency = false; - if (info.hasState(NET::SkipTaskbar)) onWindowRemoved(window); From a09e65bfb714d0454748b284d1edb1025df8d34a Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 16:36:29 +0100 Subject: [PATCH 009/165] TaskBar: use backend to set window layer --- plugin-taskbar/lxqttaskbutton.cpp | 37 +++++++++---------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 7febd5638..d324009ee 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -498,23 +498,7 @@ void LXQtTaskButton::setApplicationLayer() return; int layer = act->data().toInt(); - switch(layer) - { - case NET::KeepAbove: - KX11Extras::clearState(mWindow, NET::KeepBelow); - KX11Extras::setState(mWindow, NET::KeepAbove); - break; - - case NET::KeepBelow: - KX11Extras::clearState(mWindow, NET::KeepAbove); - KX11Extras::setState(mWindow, NET::KeepBelow); - break; - - default: - KX11Extras::clearState(mWindow, NET::KeepBelow); - KX11Extras::clearState(mWindow, NET::KeepAbove); - break; - } + mBackend->setWindowLayer(mWindow, LXQtTaskBarWindowLayer(layer)); } /************************************************ @@ -707,22 +691,23 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) QMenu* layerMenu = menu->addMenu(tr("&Layer")); - a = layerMenu->addAction(tr("Always on &top")); + LXQtTaskBarWindowLayer currentLayer = mBackend->getWindowLayer(mWindow); + // FIXME: There is no info.actionSupported(NET::ActionKeepAbove) - a->setEnabled(!(state & NET::KeepAbove)); - a->setData(NET::KeepAbove); + a = layerMenu->addAction(tr("Always on &top")); + a->setEnabled(currentLayer != LXQtTaskBarWindowLayer::KeepAbove); + a->setData(int(LXQtTaskBarWindowLayer::KeepAbove)); connect(a, &QAction::triggered, this, &LXQtTaskButton::setApplicationLayer); a = layerMenu->addAction(tr("&Normal")); - a->setEnabled((state & NET::KeepAbove) || (state & NET::KeepBelow)); - // FIXME: There is no NET::KeepNormal, so passing 0 - a->setData(0); + a->setEnabled(currentLayer != LXQtTaskBarWindowLayer::Normal); + a->setData(int(LXQtTaskBarWindowLayer::Normal)); connect(a, &QAction::triggered, this, &LXQtTaskButton::setApplicationLayer); - a = layerMenu->addAction(tr("Always on &bottom")); // FIXME: There is no info.actionSupported(NET::ActionKeepBelow) - a->setEnabled(!(state & NET::KeepBelow)); - a->setData(NET::KeepBelow); + a = layerMenu->addAction(tr("Always on &bottom")); + a->setEnabled(currentLayer != LXQtTaskBarWindowLayer::KeepBelow); + a->setData(int(LXQtTaskBarWindowLayer::KeepBelow)); connect(a, &QAction::triggered, this, &LXQtTaskButton::setApplicationLayer); /********** Kill menu **********/ From 1b013db9fe6f8ae5f368d216d6dd9b4be34658af Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 16:53:02 +0100 Subject: [PATCH 010/165] TaskBar: use backend to close and raise window Also use it to get window icon --- plugin-taskbar/lxqttaskbutton.cpp | 29 +++-------------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index d324009ee..e59219d4c 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -154,7 +154,7 @@ void LXQtTaskButton::updateIcon() if (ico.isNull()) { int devicePixels = mIconSize * devicePixelRatioF(); - ico = KX11Extras::icon(mWindow, devicePixels, devicePixels); + ico = mBackend->getApplicationIcon(mWindow, devicePixels); } setIcon(ico.isNull() ? XdgIcon::defaultApplicationIcon() : ico); } @@ -390,21 +390,7 @@ bool LXQtTaskButton::isApplicationActive() const ************************************************/ void LXQtTaskButton::raiseApplication() { - KWindowInfo info(mWindow, NET::WMDesktop | NET::WMState | NET::XAWMState); - if (parentTaskBar()->raiseOnCurrentDesktop() && info.isMinimized()) - { - KX11Extras::setOnDesktop(mWindow, KX11Extras::currentDesktop()); - } - else - { - int winDesktop = info.desktop(); - if (KX11Extras::currentDesktop() != winDesktop) - KX11Extras::setCurrentDesktop(winDesktop); - } - // bypass focus stealing prevention - KX11Extras::forceActiveWindow(mWindow); - - setUrgencyHint(false); + mBackend->raiseWindow(mWindow, parentTaskBar()->raiseOnCurrentDesktop()); } /************************************************ @@ -476,16 +462,7 @@ void LXQtTaskButton::unShadeApplication() ************************************************/ void LXQtTaskButton::closeApplication() { - auto *x11Application = qGuiApp->nativeInterface(); - if(x11Application) - { - // FIXME: Why there is no such thing in KX11Extras?? - NETRootInfo(x11Application->connection(), NET::CloseWindow).closeWindowRequest(mWindow); - } - else - { - qWarning() << "LXQtTaskBar: not implemented on Wayland"; - } + mBackend->closeWindow(mWindow); } /************************************************ From 17132e09e8f99a07f7ba3640cd8316ef03cbdf3c Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 17:00:58 +0100 Subject: [PATCH 011/165] TaskBar: use backend to roll up (shade) windows --- plugin-taskbar/lxqttaskbutton.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index e59219d4c..3a63999ba 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -446,7 +446,7 @@ void LXQtTaskButton::deMaximizeApplication() ************************************************/ void LXQtTaskButton::shadeApplication() { - KX11Extras::setState(mWindow, NET::Shaded); + mBackend->setWindowState(mWindow, LXQtTaskBarWindowState::RolledUp, true); } /************************************************ @@ -454,7 +454,7 @@ void LXQtTaskButton::shadeApplication() ************************************************/ void LXQtTaskButton::unShadeApplication() { - KX11Extras::clearState(mWindow, NET::Shaded); + mBackend->setWindowState(mWindow, LXQtTaskBarWindowState::RolledUp, false); } /************************************************ From 80d666ee141f11e15e2f04b534f5a9657bc43cab Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 17:09:37 +0100 Subject: [PATCH 012/165] TaskBar: use backend to minimize window --- plugin-taskbar/lxqttaskbutton.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 3a63999ba..a77729bc4 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -398,7 +398,7 @@ void LXQtTaskButton::raiseApplication() ************************************************/ void LXQtTaskButton::minimizeApplication() { - KX11Extras::minimizeWindow(mWindow); + mBackend->setWindowState(mWindow, LXQtTaskBarWindowState::Minimized, true); } /************************************************ From 6d2217cf1d97019fc6d5eb0293cd6047c768bebb Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 17:10:08 +0100 Subject: [PATCH 013/165] TaskBar: use backend to de-maximize window --- plugin-taskbar/lxqttaskbutton.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index a77729bc4..27d430926 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -435,10 +435,10 @@ void LXQtTaskButton::maximizeApplication() ************************************************/ void LXQtTaskButton::deMaximizeApplication() { - KX11Extras::clearState(mWindow, NET::Max); + mBackend->setWindowState(mWindow, LXQtTaskBarWindowState::Maximized, false); - if (!isApplicationActive()) - raiseApplication(); + if(!mBackend->isWindowActive(mWindow)) + mBackend->raiseWindow(mWindow, parentTaskBar()->raiseOnCurrentDesktop()); } /************************************************ From be807cccf0ecca4972950b6277c4d3ba7df232c7 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 17:58:59 +0100 Subject: [PATCH 014/165] TaskBar: use backend to get window state --- plugin-taskbar/lxqttaskbutton.cpp | 35 +++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 27d430926..6f9c1d3d2 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -540,7 +540,7 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) } KWindowInfo info(mWindow, NET::Properties(), NET::WM2AllowedActions); - unsigned long state = KWindowInfo(mWindow, NET::WMState).state(); + NET::States state = KWindowInfo(mWindow, NET::WMState).state(); QMenu * menu = new QMenu(tr("Application")); menu->setAttribute(Qt::WA_DeleteOnClose); @@ -624,42 +624,59 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) /********** State menu **********/ menu->addSeparator(); + LXQtTaskBarWindowState windowState = mBackend->getWindowState(mWindow); + a = menu->addAction(tr("Ma&ximize")); - a->setEnabled(info.actionSupported(NET::ActionMax) && (!(state & NET::Max) || (state & NET::Hidden))); + a->setEnabled(info.actionSupported(NET::ActionMax) + && windowState != LXQtTaskBarWindowState::Maximized + && windowState != LXQtTaskBarWindowState::Hidden); a->setData(NET::Max); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); if (event->modifiers() & Qt::ShiftModifier) { a = menu->addAction(tr("Maximize vertically")); - a->setEnabled(info.actionSupported(NET::ActionMaxVert) && !((state & NET::MaxVert) || (state & NET::Hidden))); + a->setEnabled(info.actionSupported(NET::ActionMaxVert) + && windowState != LXQtTaskBarWindowState::MaximizedVertically + && windowState != LXQtTaskBarWindowState::Hidden); a->setData(NET::MaxVert); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); a = menu->addAction(tr("Maximize horizontally")); - a->setEnabled(info.actionSupported(NET::ActionMaxHoriz) && !((state & NET::MaxHoriz) || (state & NET::Hidden))); + a->setEnabled(info.actionSupported(NET::ActionMaxHoriz) + && windowState != LXQtTaskBarWindowState::MaximizedHorizontally + && windowState != LXQtTaskBarWindowState::Hidden); a->setData(NET::MaxHoriz); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); } a = menu->addAction(tr("&Restore")); - a->setEnabled((state & NET::Hidden) || (state & NET::Max) || (state & NET::MaxHoriz) || (state & NET::MaxVert)); + a->setEnabled(windowState == LXQtTaskBarWindowState::Hidden + || windowState == LXQtTaskBarWindowState::Minimized + || windowState == LXQtTaskBarWindowState::Maximized + || windowState == LXQtTaskBarWindowState::MaximizedVertically + || windowState == LXQtTaskBarWindowState::MaximizedHorizontally); connect(a, &QAction::triggered, this, &LXQtTaskButton::deMaximizeApplication); a = menu->addAction(tr("Mi&nimize")); - a->setEnabled(info.actionSupported(NET::ActionMinimize) && !(state & NET::Hidden)); + a->setEnabled(info.actionSupported(NET::ActionMinimize) + && windowState != LXQtTaskBarWindowState::Hidden + && windowState != LXQtTaskBarWindowState::Minimized); connect(a, &QAction::triggered, this, &LXQtTaskButton::minimizeApplication); - if (state & NET::Shaded) + if (windowState == LXQtTaskBarWindowState::RolledUp) { a = menu->addAction(tr("Roll down")); - a->setEnabled(info.actionSupported(NET::ActionShade) && !(state & NET::Hidden)); + a->setEnabled(info.actionSupported(NET::ActionShade) + && windowState != LXQtTaskBarWindowState::Hidden + && windowState != LXQtTaskBarWindowState::Minimized); connect(a, &QAction::triggered, this, &LXQtTaskButton::unShadeApplication); } else { a = menu->addAction(tr("Roll up")); - a->setEnabled(info.actionSupported(NET::ActionShade) && !(state & NET::Hidden)); + a->setEnabled(info.actionSupported(NET::ActionShade) + && windowState != LXQtTaskBarWindowState::Hidden); connect(a, &QAction::triggered, this, &LXQtTaskButton::shadeApplication); } From d2fc42ef258f7de8a72f8892d69da2150f21a51b Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 18:26:53 +0100 Subject: [PATCH 015/165] TaskBar: use backend to set window state --- plugin-taskbar/lxqttaskbutton.cpp | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 6f9c1d3d2..b63b2aa3f 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -411,23 +411,10 @@ void LXQtTaskButton::maximizeApplication() return; int state = act->data().toInt(); - switch (state) - { - case NET::MaxHoriz: - KX11Extras::setState(mWindow, NET::MaxHoriz); - break; + mBackend->setWindowState(mWindow, LXQtTaskBarWindowState(state), true); - case NET::MaxVert: - KX11Extras::setState(mWindow, NET::MaxVert); - break; - - default: - KX11Extras::setState(mWindow, NET::Max); - break; - } - - if (!isApplicationActive()) - raiseApplication(); + if(!mBackend->isWindowActive(mWindow)) + mBackend->raiseWindow(mWindow, parentTaskBar()->raiseOnCurrentDesktop()); } /************************************************ @@ -630,7 +617,7 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) a->setEnabled(info.actionSupported(NET::ActionMax) && windowState != LXQtTaskBarWindowState::Maximized && windowState != LXQtTaskBarWindowState::Hidden); - a->setData(NET::Max); + a->setData(int(LXQtTaskBarWindowState::Maximized)); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); if (event->modifiers() & Qt::ShiftModifier) @@ -639,14 +626,14 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) a->setEnabled(info.actionSupported(NET::ActionMaxVert) && windowState != LXQtTaskBarWindowState::MaximizedVertically && windowState != LXQtTaskBarWindowState::Hidden); - a->setData(NET::MaxVert); + a->setData(int(LXQtTaskBarWindowState::MaximizedVertically)); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); a = menu->addAction(tr("Maximize horizontally")); a->setEnabled(info.actionSupported(NET::ActionMaxHoriz) && windowState != LXQtTaskBarWindowState::MaximizedHorizontally && windowState != LXQtTaskBarWindowState::Hidden); - a->setData(NET::MaxHoriz); + a->setData(int(LXQtTaskBarWindowState::MaximizedHorizontally)); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); } @@ -747,7 +734,7 @@ bool LXQtTaskButton::isOnCurrentScreen() const bool LXQtTaskButton::isMinimized() const { - return KWindowInfo(mWindow,NET::WMState | NET::XAWMState).isMinimized(); + return mBackend->getWindowState(mWindow) == LXQtTaskBarWindowState::Minimized; } Qt::Corner LXQtTaskButton::origin() const From cc84e943d0ed7cf44890189ac8299fa65db1cdb0 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sun, 28 Jan 2024 13:27:46 +0100 Subject: [PATCH 016/165] TaskBar: port workspace usage to backend --- plugin-taskbar/lxqttaskbutton.cpp | 19 ++++++++----------- plugin-taskbar/lxqttaskgroup.cpp | 4 ++-- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index b63b2aa3f..ec5fca2d4 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -559,21 +559,21 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) */ /********** Desktop menu **********/ - int deskNum = KX11Extras::numberOfDesktops(); + int deskNum = mBackend->getWorkspacesCount(); if (deskNum > 1) { - int winDesk = KWindowInfo(mWindow, NET::WMDesktop).desktop(); + int winDesk = mBackend->getWindowWorkspace(mWindow); QMenu* deskMenu = menu->addMenu(tr("To &Desktop")); a = deskMenu->addAction(tr("&All Desktops")); - a->setData(NET::OnAllDesktops); - a->setEnabled(winDesk != NET::OnAllDesktops); + a->setData(int(LXQtTaskBarWorkspace::ShowOnAll)); + a->setEnabled(winDesk != int(LXQtTaskBarWorkspace::ShowOnAll)); connect(a, &QAction::triggered, this, &LXQtTaskButton::moveApplicationToDesktop); deskMenu->addSeparator(); for (int i = 1; i <= deskNum; ++i) { - auto deskName = KX11Extras::desktopName(i).trimmed(); + auto deskName = mBackend->getWorkspaceName(i).trimmed(); if (deskName.isEmpty()) a = deskMenu->addAction(tr("Desktop &%1").arg(i)); else @@ -584,7 +584,7 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) connect(a, &QAction::triggered, this, &LXQtTaskButton::moveApplicationToDesktop); } - int curDesk = KX11Extras::currentDesktop(); + int curDesk = mBackend->getCurrentWorkspace(); a = menu->addAction(tr("&To Current Desktop")); a->setData(curDesk); a->setEnabled(curDesk != winDesk); @@ -720,16 +720,13 @@ void LXQtTaskButton::setUrgencyHint(bool set) ************************************************/ bool LXQtTaskButton::isOnDesktop(int desktop) const { - return KWindowInfo(mWindow, NET::WMDesktop).isOnDesktop(desktop); + return mBackend->getWindowWorkspace(mWindow) == desktop; } bool LXQtTaskButton::isOnCurrentScreen() const { QScreen *screen = parentTaskBar()->screen(); - QRect screenGeo = screen->geometry(); - QRect windowGeo = KWindowInfo(mWindow, NET::WMFrameExtents).frameGeometry(); - - return screenGeo.intersects(windowGeo); + return mBackend->isWindowOnScreen(screen, mWindow); } bool LXQtTaskButton::isMinimized() const diff --git a/plugin-taskbar/lxqttaskgroup.cpp b/plugin-taskbar/lxqttaskgroup.cpp index e4991c8a4..655e99638 100644 --- a/plugin-taskbar/lxqttaskgroup.cpp +++ b/plugin-taskbar/lxqttaskgroup.cpp @@ -392,7 +392,7 @@ void LXQtTaskGroup::refreshVisibility() const int showDesktop = taskbar->showDesktopNum(); for(LXQtTaskButton * btn : std::as_const(mButtonHash)) { - bool visible = taskbar->isShowOnlyOneDesktopTasks() ? btn->isOnDesktop(0 == showDesktop ? KX11Extras::currentDesktop() : showDesktop) : true; + bool visible = taskbar->isShowOnlyOneDesktopTasks() ? btn->isOnDesktop(0 == showDesktop ? mBackend->getCurrentWorkspace() : showDesktop) : true; visible &= taskbar->isShowOnlyCurrentScreenTasks() ? btn->isOnCurrentScreen() : true; visible &= taskbar->isShowOnlyMinimizedTasks() ? btn->isMinimized() : true; btn->setVisible(visible); @@ -649,7 +649,7 @@ bool LXQtTaskGroup::onWindowChanged(WId window, NET::Properties prop, NET::Prope if (prop.testFlag(NET::WMDesktop) || prop.testFlag(NET::WMGeometry)) { if (parentTaskBar()->isShowOnlyOneDesktopTasks() - || parentTaskBar()->isShowOnlyCurrentScreenTasks()) + || parentTaskBar()->isShowOnlyCurrentScreenTasks()) { needsRefreshVisibility = true; } From a597c927e6a68346eea5f190884189e882228115 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 18:33:09 +0100 Subject: [PATCH 017/165] TaskBar: remove X11 specific includes in lxqttaskbutton.cpp --- plugin-taskbar/lxqttaskbutton.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index ec5fca2d4..2126dca76 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -50,18 +50,9 @@ #include #include -//TODO: remove -#include - -// Necessary for closeApplication() -#include - -//NOTE: Xlib.h defines Bool which conflicts with QJsonValue::Type enum -#include -#undef Bool +#include "ilxqttaskbarabstractbackend.h" -#include "ilxqttaskbarabstractbackend.h" bool LXQtTaskButton::sDraggging = false; From 0f0b4049c13e5e4fef8e4c78b716c1b0a04cb022 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sun, 28 Jan 2024 15:59:25 +0100 Subject: [PATCH 018/165] TaskBar: remove X11 code from LXQtTaskBar and LXQtTaskGroup --- plugin-taskbar/lxqttaskbar.cpp | 99 ++++---------------------------- plugin-taskbar/lxqttaskbar.h | 9 +-- plugin-taskbar/lxqttaskgroup.cpp | 32 ++++------- plugin-taskbar/lxqttaskgroup.h | 5 +- 4 files changed, 28 insertions(+), 117 deletions(-) diff --git a/plugin-taskbar/lxqttaskbar.cpp b/plugin-taskbar/lxqttaskbar.cpp index fc60496a3..1bf7781f7 100644 --- a/plugin-taskbar/lxqttaskbar.cpp +++ b/plugin-taskbar/lxqttaskbar.cpp @@ -50,10 +50,6 @@ #include "lxqttaskgroup.h" #include "../panel/pluginsettings.h" -//NOTE: Xlib.h defines Bool which conflicts with QJsonValue::Type enum -#include -#undef Bool - #include "lxqttaskbarbackend_x11.h" using namespace LXQt; @@ -107,10 +103,9 @@ LXQtTaskBar::LXQtTaskBar(ILXQtPanelPlugin *plugin, QWidget *parent) : connect(mSignalMapper, &QSignalMapper::mappedInt, this, &LXQtTaskBar::activateTask); QTimer::singleShot(0, this, &LXQtTaskBar::registerShortcuts); - connect(KX11Extras::self(), static_cast(&KX11Extras::windowChanged) - , this, &LXQtTaskBar::onWindowChanged); - connect(KX11Extras::self(), &KX11Extras::windowAdded, this, &LXQtTaskBar::onWindowAdded); - connect(KX11Extras::self(), &KX11Extras::windowRemoved, this, &LXQtTaskBar::onWindowRemoved); + connect(mBackend, &ILXQtTaskbarAbstractBackend::windowPropertyChanged, this, &LXQtTaskBar::onWindowChanged); + connect(mBackend, &ILXQtTaskbarAbstractBackend::windowAdded, this, &LXQtTaskBar::onWindowAdded); + connect(mBackend, &ILXQtTaskbarAbstractBackend::windowRemoved, this, &LXQtTaskBar::onWindowRemoved); } /************************************************ @@ -121,49 +116,6 @@ LXQtTaskBar::~LXQtTaskBar() delete mStyle; } -/************************************************ - - ************************************************/ -bool LXQtTaskBar::acceptWindow(WId window) const -{ - QFlags ignoreList; - ignoreList |= NET::DesktopMask; - ignoreList |= NET::DockMask; - ignoreList |= NET::SplashMask; - ignoreList |= NET::ToolbarMask; - ignoreList |= NET::MenuMask; - ignoreList |= NET::PopupMenuMask; - ignoreList |= NET::NotificationMask; - - KWindowInfo info(window, NET::WMWindowType | NET::WMState, NET::WM2TransientFor); - if (!info.valid()) - return false; - - if (NET::typeMatchesMask(info.windowType(NET::AllTypesMask), ignoreList)) - return false; - - if (info.state() & NET::SkipTaskbar) - return false; - - auto *x11Application = qGuiApp->nativeInterface(); - Q_ASSERT_X(x11Application, "DesktopSwitch", "Expected X11 connection"); - WId appRootWindow = XDefaultRootWindow(x11Application->display()); - - // WM_TRANSIENT_FOR hint not set - normal window - WId transFor = info.transientFor(); - if (transFor == 0 || transFor == window || transFor == appRootWindow) - return true; - - info = KWindowInfo(transFor, NET::WMWindowType); - - QFlags normalFlag; - normalFlag |= NET::NormalMask; - normalFlag |= NET::DialogMask; - normalFlag |= NET::UtilityMask; - - return !NET::typeMatchesMask(info.windowType(NET::AllTypesMask), normalFlag); -} - /************************************************ ************************************************/ @@ -328,7 +280,7 @@ void LXQtTaskBar::addWindow(WId window) if (mUngroupedNextToExisting) { - const QString window_class = QString::fromUtf8(KWindowInfo(window, NET::Properties(), NET::WM2WindowClass).windowClassClass()); + const QString window_class = mBackend->getWindowClass(window); int src_index = mLayout->count() - 1; int dst_index = src_index; for (int i = mLayout->count() - 2; 0 <= i; --i) @@ -336,7 +288,7 @@ void LXQtTaskBar::addWindow(WId window) LXQtTaskGroup * current_group = qobject_cast(mLayout->itemAt(i)->widget()); if (nullptr != current_group) { - const QString current_class = QString::fromUtf8(KWindowInfo((current_group->groupName()).toUInt(), NET::Properties(), NET::WM2WindowClass).windowClassClass()); + const QString current_class = mBackend->getWindowClass(current_group->groupName().toUInt()); if(current_class == window_class) { dst_index = i + 1; @@ -370,43 +322,14 @@ auto LXQtTaskBar::removeWindow(windowMap_t::iterator pos) -> windowMap_t::iterat /************************************************ ************************************************/ -void LXQtTaskBar::refreshTaskList() -{ - QList new_list; - // Just add new windows to groups, deleting is up to the groups - const auto wnds = KX11Extras::stackingOrder(); - for (auto const wnd: wnds) - { - if (acceptWindow(wnd)) - { - new_list << wnd; - addWindow(wnd); - } - } - - //emulate windowRemoved if known window not reported by KWindowSystem - for (auto i = mKnownWindows.begin(), i_e = mKnownWindows.end(); i != i_e; ) - { - if (0 > new_list.indexOf(i.key())) - { - i = removeWindow(i); - } else - ++i; - } - - refreshPlaceholderVisibility(); -} - -/************************************************ - - ************************************************/ -void LXQtTaskBar::onWindowChanged(WId window, NET::Properties prop, NET::Properties2 prop2) +void LXQtTaskBar::onWindowChanged(WId window, int prop) { auto i = mKnownWindows.find(window); if (mKnownWindows.end() != i) { - if (!(*i)->onWindowChanged(window, prop, prop2) && acceptWindow(window)) - { // window is removed from a group because of class change, so we should add it again + if (!(*i)->onWindowChanged(window, LXQtTaskBarWindowProperty(prop))) + { + // window is removed from a group because of class change, so we should add it again addWindow(window); } } @@ -415,7 +338,7 @@ void LXQtTaskBar::onWindowChanged(WId window, NET::Properties prop, NET::Propert void LXQtTaskBar::onWindowAdded(WId window) { auto const pos = mKnownWindows.find(window); - if (mKnownWindows.end() == pos && acceptWindow(window)) + if (mKnownWindows.end() == pos) addWindow(window); } @@ -541,8 +464,8 @@ void LXQtTaskBar::settingsChanged() if (iconByClassOld != mIconByClass) emit iconByClassChanged(); - refreshTaskList(); //TODO: remove mBackend->reloadWindows(); + refreshPlaceholderVisibility(); } /************************************************ diff --git a/plugin-taskbar/lxqttaskbar.h b/plugin-taskbar/lxqttaskbar.h index c36bca7e0..9f94a2958 100644 --- a/plugin-taskbar/lxqttaskbar.h +++ b/plugin-taskbar/lxqttaskbar.h @@ -37,9 +37,6 @@ #include #include "../panel/ilxqtpanel.h" -#include -#include -#include class ILXQtPanel; class ILXQtPanelPlugin; @@ -107,13 +104,14 @@ public slots: virtual void dragMoveEvent(QDragMoveEvent * event); private slots: - void refreshTaskList(); void refreshButtonRotation(); void refreshPlaceholderVisibility(); void groupBecomeEmptySlot(); - void onWindowChanged(WId window, NET::Properties prop, NET::Properties2 prop2); + + void onWindowChanged(WId window, int prop); void onWindowAdded(WId window); void onWindowRemoved(WId window); + void registerShortcuts(); void shortcutRegistered(); void activateTask(int pos); @@ -150,7 +148,6 @@ private slots: int mWheelEventsAction; int mWheelDeltaThreshold; - bool acceptWindow(WId window) const; void setButtonStyle(Qt::ToolButtonStyle buttonStyle); void wheelEvent(QWheelEvent* event); diff --git a/plugin-taskbar/lxqttaskgroup.cpp b/plugin-taskbar/lxqttaskgroup.cpp index 655e99638..30babc358 100644 --- a/plugin-taskbar/lxqttaskgroup.cpp +++ b/plugin-taskbar/lxqttaskgroup.cpp @@ -41,12 +41,6 @@ #include #include #include -#include - -//TODO: remove -#include //For nativeInterface() -#include -#undef Bool #include "ilxqttaskbarabstractbackend.h" @@ -622,8 +616,10 @@ void LXQtTaskGroup::wheelEvent(QWheelEvent* event) /************************************************ ************************************************/ -bool LXQtTaskGroup::onWindowChanged(WId window, NET::Properties prop, NET::Properties2 prop2) -{ // returns true if the class is preserved +bool LXQtTaskGroup::onWindowChanged(WId window, LXQtTaskBarWindowProperty prop) +{ + // Returns true if the class is preserved + bool needsRefreshVisibility{false}; QList buttons; if (mButtonHash.contains(window)) @@ -636,17 +632,16 @@ bool LXQtTaskGroup::onWindowChanged(WId window, NET::Properties prop, NET::Prope if (!buttons.isEmpty()) { // if class is changed the window won't belong to our group any more - if (parentTaskBar()->isGroupingEnabled() && prop2.testFlag(NET::WM2WindowClass)) + if (parentTaskBar()->isGroupingEnabled() && prop == LXQtTaskBarWindowProperty::WindowClass) { - KWindowInfo info(window, NET::Properties(), NET::WM2WindowClass); - if (QString::fromUtf8(info.windowClassClass()) != mGroupName) + if (mBackend->getWindowClass(windowId()) != mGroupName) { onWindowRemoved(window); return false; } } // window changed virtual desktop - if (prop.testFlag(NET::WMDesktop) || prop.testFlag(NET::WMGeometry)) + if (prop == LXQtTaskBarWindowProperty::Workspace) { if (parentTaskBar()->isShowOnlyOneDesktopTasks() || parentTaskBar()->isShowOnlyCurrentScreenTasks()) @@ -655,34 +650,29 @@ bool LXQtTaskGroup::onWindowChanged(WId window, NET::Properties prop, NET::Prope } } - if (prop.testFlag(NET::WMVisibleName) || prop.testFlag(NET::WMName)) + if (prop == LXQtTaskBarWindowProperty::Title) std::for_each(buttons.begin(), buttons.end(), std::mem_fn(&LXQtTaskButton::updateText)); // XXX: we are setting window icon geometry -> don't need to handle NET::WMIconGeometry // Icon of the button can be based on windowClass - if (prop.testFlag(NET::WMIcon) || prop2.testFlag(NET::WM2WindowClass)) + if (prop == LXQtTaskBarWindowProperty::Icon) std::for_each(buttons.begin(), buttons.end(), std::mem_fn(&LXQtTaskButton::updateIcon)); bool set_urgency = false; bool urgency = false; - if (prop2.testFlag(NET::WM2Urgency)) + if (prop == LXQtTaskBarWindowProperty::Urgency) { set_urgency = true; //FIXME: original code here did not consider "demand attention", was it intentional? urgency = mBackend->applicationDemandsAttention(window); } - if (prop.testFlag(NET::WMState)) + if (prop == LXQtTaskBarWindowProperty::State) { - KWindowInfo info{window, NET::WMState}; - - // Force refresh urgency if (!set_urgency) urgency = mBackend->applicationDemandsAttention(window); std::for_each(buttons.begin(), buttons.end(), std::bind(&LXQtTaskButton::setUrgencyHint, std::placeholders::_1, urgency)); set_urgency = false; - if (info.hasState(NET::SkipTaskbar)) - onWindowRemoved(window); if (parentTaskBar()->isShowOnlyMinimizedTasks()) { diff --git a/plugin-taskbar/lxqttaskgroup.h b/plugin-taskbar/lxqttaskgroup.h index 3787f411f..d4868c5d2 100644 --- a/plugin-taskbar/lxqttaskgroup.h +++ b/plugin-taskbar/lxqttaskgroup.h @@ -33,7 +33,7 @@ #include "lxqttaskbutton.h" -#include +#include "lxqttaskbartypes.h" class QVBoxLayout; class ILXQtPanelPlugin; @@ -60,7 +60,8 @@ class LXQtTaskGroup: public LXQtTaskButton // if circular is true, then it will go around the list of buttons LXQtTaskButton * getNextPrevChildButton(bool next, bool circular); - bool onWindowChanged(WId window, NET::Properties prop, NET::Properties2 prop2); + bool onWindowChanged(WId window, LXQtTaskBarWindowProperty prop); + void setAutoRotation(bool value, ILXQtPanel::Position position); Qt::ToolButtonStyle popupButtonStyle() const; void setToolButtonsStyle(Qt::ToolButtonStyle style); From ca915440e0ff4cb5d46b0a6db1943e86019ef439 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sun, 28 Jan 2024 16:38:00 +0100 Subject: [PATCH 019/165] TaskBar: LXQtTaskButton remove X11 specific code --- plugin-taskbar/lxqttaskbutton.cpp | 68 ++++++++++++++++--------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 2126dca76..fa588455e 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -140,7 +140,7 @@ void LXQtTaskButton::updateIcon() QIcon ico; if (mParentTaskBar->isIconByClass()) { - ico = XdgIcon::fromTheme(QString::fromUtf8(KWindowInfo{mWindow, NET::Properties(), NET::WM2WindowClass}.windowClassClass()).toLower()); + ico = XdgIcon::fromTheme(mBackend->getWindowClass(mWindow).toLower()); } if (ico.isNull()) { @@ -517,8 +517,7 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) return; } - KWindowInfo info(mWindow, NET::Properties(), NET::WM2AllowedActions); - NET::States state = KWindowInfo(mWindow, NET::WMState).state(); + const LXQtTaskBarWindowState state = mBackend->getWindowState(mWindow); QMenu * menu = new QMenu(tr("Application")); menu->setAttribute(Qt::WA_DeleteOnClose); @@ -587,74 +586,79 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) menu->addSeparator(); a = menu->addAction(tr("Move To N&ext Monitor")); connect(a, &QAction::triggered, this, [this] { moveApplicationToPrevNextMonitor(true); }); - a->setEnabled(info.actionSupported(NET::ActionMove) && (!(state & NET::FullScreen) || ((state & NET::FullScreen) && info.actionSupported(NET::ActionFullScreen)))); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::Move) && + (state != LXQtTaskBarWindowState::FullScreen + || ((state == LXQtTaskBarWindowState::FullScreen) && mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::FullScreen)))); a = menu->addAction(tr("Move To &Previous Monitor")); connect(a, &QAction::triggered, this, [this] { moveApplicationToPrevNextMonitor(false); }); } + menu->addSeparator(); a = menu->addAction(tr("&Move")); - a->setEnabled(info.actionSupported(NET::ActionMove) && !(state & NET::Max) && !(state & NET::FullScreen)); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::Move) + && state != LXQtTaskBarWindowState::Maximized + && state != LXQtTaskBarWindowState::FullScreen); connect(a, &QAction::triggered, this, &LXQtTaskButton::moveApplication); a = menu->addAction(tr("Resi&ze")); - a->setEnabled(info.actionSupported(NET::ActionResize) && !(state & NET::Max) && !(state & NET::FullScreen)); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::Resize) + && state != LXQtTaskBarWindowState::Maximized + && state != LXQtTaskBarWindowState::FullScreen); connect(a, &QAction::triggered, this, &LXQtTaskButton::resizeApplication); /********** State menu **********/ menu->addSeparator(); - LXQtTaskBarWindowState windowState = mBackend->getWindowState(mWindow); - a = menu->addAction(tr("Ma&ximize")); - a->setEnabled(info.actionSupported(NET::ActionMax) - && windowState != LXQtTaskBarWindowState::Maximized - && windowState != LXQtTaskBarWindowState::Hidden); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::Maximize) + && state != LXQtTaskBarWindowState::Maximized + && state != LXQtTaskBarWindowState::Hidden); a->setData(int(LXQtTaskBarWindowState::Maximized)); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); if (event->modifiers() & Qt::ShiftModifier) { a = menu->addAction(tr("Maximize vertically")); - a->setEnabled(info.actionSupported(NET::ActionMaxVert) - && windowState != LXQtTaskBarWindowState::MaximizedVertically - && windowState != LXQtTaskBarWindowState::Hidden); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::MaximizeVertically) + && state != LXQtTaskBarWindowState::MaximizedVertically + && state != LXQtTaskBarWindowState::Hidden); a->setData(int(LXQtTaskBarWindowState::MaximizedVertically)); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); a = menu->addAction(tr("Maximize horizontally")); - a->setEnabled(info.actionSupported(NET::ActionMaxHoriz) - && windowState != LXQtTaskBarWindowState::MaximizedHorizontally - && windowState != LXQtTaskBarWindowState::Hidden); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::MaximizeHorizontally) + && state != LXQtTaskBarWindowState::MaximizedHorizontally + && state != LXQtTaskBarWindowState::Hidden); a->setData(int(LXQtTaskBarWindowState::MaximizedHorizontally)); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); } a = menu->addAction(tr("&Restore")); - a->setEnabled(windowState == LXQtTaskBarWindowState::Hidden - || windowState == LXQtTaskBarWindowState::Minimized - || windowState == LXQtTaskBarWindowState::Maximized - || windowState == LXQtTaskBarWindowState::MaximizedVertically - || windowState == LXQtTaskBarWindowState::MaximizedHorizontally); + a->setEnabled(state == LXQtTaskBarWindowState::Hidden + || state == LXQtTaskBarWindowState::Minimized + || state == LXQtTaskBarWindowState::Maximized + || state == LXQtTaskBarWindowState::MaximizedVertically + || state == LXQtTaskBarWindowState::MaximizedHorizontally); connect(a, &QAction::triggered, this, &LXQtTaskButton::deMaximizeApplication); a = menu->addAction(tr("Mi&nimize")); - a->setEnabled(info.actionSupported(NET::ActionMinimize) - && windowState != LXQtTaskBarWindowState::Hidden - && windowState != LXQtTaskBarWindowState::Minimized); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::Minimize) + && state != LXQtTaskBarWindowState::Hidden + && state != LXQtTaskBarWindowState::Minimized); connect(a, &QAction::triggered, this, &LXQtTaskButton::minimizeApplication); - if (windowState == LXQtTaskBarWindowState::RolledUp) + if (state == LXQtTaskBarWindowState::RolledUp) { a = menu->addAction(tr("Roll down")); - a->setEnabled(info.actionSupported(NET::ActionShade) - && windowState != LXQtTaskBarWindowState::Hidden - && windowState != LXQtTaskBarWindowState::Minimized); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::RollUp) + && state != LXQtTaskBarWindowState::Hidden + && state != LXQtTaskBarWindowState::Minimized); connect(a, &QAction::triggered, this, &LXQtTaskButton::unShadeApplication); } else { a = menu->addAction(tr("Roll up")); - a->setEnabled(info.actionSupported(NET::ActionShade) - && windowState != LXQtTaskBarWindowState::Hidden); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::RollUp) + && state != LXQtTaskBarWindowState::Hidden); connect(a, &QAction::triggered, this, &LXQtTaskButton::shadeApplication); } @@ -665,7 +669,6 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) LXQtTaskBarWindowLayer currentLayer = mBackend->getWindowLayer(mWindow); - // FIXME: There is no info.actionSupported(NET::ActionKeepAbove) a = layerMenu->addAction(tr("Always on &top")); a->setEnabled(currentLayer != LXQtTaskBarWindowLayer::KeepAbove); a->setData(int(LXQtTaskBarWindowLayer::KeepAbove)); @@ -676,7 +679,6 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) a->setData(int(LXQtTaskBarWindowLayer::Normal)); connect(a, &QAction::triggered, this, &LXQtTaskButton::setApplicationLayer); - // FIXME: There is no info.actionSupported(NET::ActionKeepBelow) a = layerMenu->addAction(tr("Always on &bottom")); a->setEnabled(currentLayer != LXQtTaskBarWindowLayer::KeepBelow); a->setData(int(LXQtTaskBarWindowLayer::KeepBelow)); From fb332d733e14d5fe8726de4893ab8ae92bba76a4 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 17 Feb 2024 23:40:21 +0100 Subject: [PATCH 020/165] TaskBar: set context menu parent --- plugin-taskbar/lxqttaskbutton.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index fa588455e..08bc50a1e 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -519,7 +519,7 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) const LXQtTaskBarWindowState state = mBackend->getWindowState(mWindow); - QMenu * menu = new QMenu(tr("Application")); + QMenu * menu = new QMenu(tr("Application"), this); menu->setAttribute(Qt::WA_DeleteOnClose); QAction* a; From 934acf19087c298670d2e7115251560db2fff178 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sun, 18 Feb 2024 00:01:15 +0100 Subject: [PATCH 021/165] LXQtPanel: rework calculatePopupWindowPos() - Don't rely on global screen coordinates - This will be needed for future Wayland port, Where we don't have global screen coordinates - Keep compatible behavior on X11 --- panel/lxqtpanel.cpp | 56 +++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/panel/lxqtpanel.cpp b/panel/lxqtpanel.cpp index 86ba83141..5f3270c41 100644 --- a/panel/lxqtpanel.cpp +++ b/panel/lxqtpanel.cpp @@ -1258,48 +1258,60 @@ Plugin* LXQtPanel::findPlugin(const ILXQtPanelPlugin* iPlugin) const ************************************************/ QRect LXQtPanel::calculatePopupWindowPos(QPoint const & absolutePos, QSize const & windowSize) const { - int x = absolutePos.x(), y = absolutePos.y(); + QPoint localPos = mapFromGlobal(absolutePos); + int x = localPos.x(), y = localPos.y(); switch (position()) { case ILXQtPanel::PositionTop: - y = mGeometry.bottom(); + y = geometry().height(); break; case ILXQtPanel::PositionBottom: - y = mGeometry.top() - windowSize.height(); + y = 0 - windowSize.height(); break; case ILXQtPanel::PositionLeft: - x = mGeometry.right(); + x = geometry().right(); break; case ILXQtPanel::PositionRight: - x = mGeometry.left() - windowSize.width(); + x = geometry().left() - windowSize.width(); break; } QRect res(QPoint(x, y), windowSize); - QRect panelScreen; - const auto screens = QApplication::screens(); - if (mActualScreenNum < screens.size()) - panelScreen = screens.at(mActualScreenNum)->geometry(); - // NOTE: We cannot use AvailableGeometry() which returns the work area here because when in a - // multihead setup with different resolutions. In this case, the size of the work area is limited - // by the smallest monitor and may be much smaller than the current screen and we will place the - // menu at the wrong place. This is very bad for UX. So let's use the full size of the screen. - if (res.right() > panelScreen.right()) - res.moveRight(panelScreen.right()); + // Map to global coordinates + res = QRect(mapToGlobal(res.topLeft()), mapToGlobal(res.bottomRight())); + + if(qGuiApp->nativeInterface()) + { + //On X11 we clamp rects inside screen area. + //NOTE: On Wayland it's done by compositor + + QRect panelScreen; + const auto screens = QApplication::screens(); + if (mActualScreenNum < screens.size()) + panelScreen = screens.at(mActualScreenNum)->geometry(); - if (res.bottom() > panelScreen.bottom()) - res.moveBottom(panelScreen.bottom()); + // NOTE: We cannot use AvailableGeometry() which returns the work area here because when in a + // multihead setup with different resolutions. In this case, the size of the work area is limited + // by the smallest monitor and may be much smaller than the current screen and we will place the + // menu at the wrong place. This is very bad for UX. So let's use the full size of the screen. - if (res.left() < panelScreen.left()) - res.moveLeft(panelScreen.left()); + if (res.right() > panelScreen.right()) + res.moveRight(panelScreen.right()); - if (res.top() < panelScreen.top()) - res.moveTop(panelScreen.top()); + if (res.bottom() > panelScreen.bottom()) + res.moveBottom(panelScreen.bottom() - mGeometry.top()); + + if (res.left() < panelScreen.left()) + res.moveLeft(panelScreen.left()); + + if (res.top() < panelScreen.top()) + res.moveTop(panelScreen.top() - mGeometry.top()); + } return res; } @@ -1321,7 +1333,7 @@ QRect LXQtPanel::calculatePopupWindowPos(const ILXQtPanelPlugin *plugin, const Q } // Note: assuming there are not contentMargins around the "BackgroundWidget" (LXQtPanelWidget) - return calculatePopupWindowPos(mGeometry.topLeft() + panel_plugin->geometry().topLeft(), windowSize); + return calculatePopupWindowPos(mapToGlobal(panel_plugin->geometry().topLeft()), windowSize); } From 616c8de0f9a301bf7a59ac6915ce29f8796975c3 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sun, 18 Feb 2024 13:13:24 +0100 Subject: [PATCH 022/165] LXQtPanel: avoid QCursor::pos() usage --- panel/lxqtpanel.cpp | 6 +++--- panel/lxqtpanel.h | 3 ++- panel/plugin.cpp | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/panel/lxqtpanel.cpp b/panel/lxqtpanel.cpp index 5f3270c41..0835aebb4 100644 --- a/panel/lxqtpanel.cpp +++ b/panel/lxqtpanel.cpp @@ -1106,7 +1106,7 @@ bool LXQtPanel::event(QEvent *event) switch (event->type()) { case QEvent::ContextMenu: - showPopupMenu(); + showPopupMenu(static_cast(event)->globalPos()); break; case QEvent::LayoutRequest: @@ -1171,7 +1171,7 @@ void LXQtPanel::showEvent(QShowEvent *event) /************************************************ ************************************************/ -void LXQtPanel::showPopupMenu(Plugin *plugin) +void LXQtPanel::showPopupMenu(const QPoint& cursorPos, Plugin *plugin) { PopupMenu * menu = new PopupMenu(tr("Panel"), this); menu->setAttribute(Qt::WA_DeleteOnClose); @@ -1239,7 +1239,7 @@ void LXQtPanel::showPopupMenu(Plugin *plugin) * sometimes wrongly (it seems that this bug is somehow connected to misinterpretation * of QDesktopWidget::availableGeometry) */ - menu->setGeometry(calculatePopupWindowPos(QCursor::pos(), menu->sizeHint())); + menu->setGeometry(calculatePopupWindowPos(cursorPos, menu->sizeHint())); willShowWindow(menu); menu->show(); } diff --git a/panel/lxqtpanel.h b/panel/lxqtpanel.h index 7d847dfa6..a3a061a53 100644 --- a/panel/lxqtpanel.h +++ b/panel/lxqtpanel.h @@ -135,10 +135,11 @@ class LXQT_PANEL_API LXQtPanel : public QFrame, public ILXQtPanel * is given as parameter, the menu will be divided in two groups: * plugin-specific options and panel-related options. As these two are * shown together, this menu has to be created by LXQtPanel. + * @param cursorPos The global cursor pos * @param plugin The plugin whose menu options will be included in the * context menu. */ - void showPopupMenu(Plugin *plugin = 0); + void showPopupMenu(const QPoint &cursorPos, Plugin *plugin = nullptr); // ILXQtPanel overrides ........ ILXQtPanel::Position position() const override { return mPosition; } diff --git a/panel/plugin.cpp b/panel/plugin.cpp index eb22ed431..4844bd528 100644 --- a/panel/plugin.cpp +++ b/panel/plugin.cpp @@ -380,9 +380,9 @@ void Plugin::saveSettings() /************************************************ ************************************************/ -void Plugin::contextMenuEvent(QContextMenuEvent * /*event*/) +void Plugin::contextMenuEvent(QContextMenuEvent * event) { - mPanel->showPopupMenu(this); + mPanel->showPopupMenu(event->globalPos(), this); } From 5a36d25c54000db209eae61ba90051bc10dd8e66 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Mon, 19 Feb 2024 23:10:58 +0100 Subject: [PATCH 023/165] ILXQtTaskbarAbstractBackend: new Geometry window property This new window propery flag is needed to notify geometry changes --- plugin-taskbar/lxqttaskbarbackend_x11.cpp | 5 +++++ plugin-taskbar/lxqttaskbartypes.h | 1 + 2 files changed, 6 insertions(+) diff --git a/plugin-taskbar/lxqttaskbarbackend_x11.cpp b/plugin-taskbar/lxqttaskbarbackend_x11.cpp index cc7759f4e..bcb83bdde 100644 --- a/plugin-taskbar/lxqttaskbarbackend_x11.cpp +++ b/plugin-taskbar/lxqttaskbarbackend_x11.cpp @@ -48,6 +48,11 @@ void LXQtTaskbarX11Backend::onWindowChanged(WId windowId, NET::Properties prop, return; } + if (prop.testFlag(NET::WMGeometry)) + { + emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::Geometry)); + } + if (prop2.testFlag(NET::WM2WindowClass)) { emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::WindowClass)); diff --git a/plugin-taskbar/lxqttaskbartypes.h b/plugin-taskbar/lxqttaskbartypes.h index ccea42027..9e12092bb 100644 --- a/plugin-taskbar/lxqttaskbartypes.h +++ b/plugin-taskbar/lxqttaskbartypes.h @@ -22,6 +22,7 @@ enum class LXQtTaskBarWindowProperty Title = 0, Icon, State, + Geometry, Urgency, WindowClass, Workspace From f1d981fe7b344bb9bff2484c588260ddc98d5bce Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 17 Feb 2024 12:34:05 +0100 Subject: [PATCH 024/165] Move ILXQtTaskbarAbstractBackend to panel directory - It is now a global instance --- panel/CMakeLists.txt | 15 +++++++++ panel/backends/CMakeLists.txt | 1 + .../backends}/ilxqttaskbarabstractbackend.cpp | 2 +- .../backends}/ilxqttaskbarabstractbackend.h | 0 .../backends}/lxqttaskbartypes.h | 0 panel/backends/xcb/CMakeLists.txt | 1 + .../backends/xcb}/lxqttaskbarbackend_x11.cpp | 0 .../backends/xcb}/lxqttaskbarbackend_x11.h | 2 +- panel/lxqtpanelapplication.cpp | 31 +++++++++++++++---- panel/lxqtpanelapplication.h | 4 +++ panel/lxqtpanelapplication_p.h | 3 ++ plugin-taskbar/CMakeLists.txt | 5 --- plugin-taskbar/lxqttaskbar.cpp | 9 +++--- plugin-taskbar/lxqttaskbarproxymodel.cpp | 2 +- plugin-taskbar/lxqttaskbarproxymodel.h | 2 +- plugin-taskbar/lxqttaskbutton.cpp | 2 +- plugin-taskbar/lxqttaskgroup.cpp | 2 +- plugin-taskbar/lxqttaskgroup.h | 2 +- 18 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 panel/backends/CMakeLists.txt rename {plugin-taskbar => panel/backends}/ilxqttaskbarabstractbackend.cpp (90%) rename {plugin-taskbar => panel/backends}/ilxqttaskbarabstractbackend.h (100%) rename {plugin-taskbar => panel/backends}/lxqttaskbartypes.h (100%) create mode 100644 panel/backends/xcb/CMakeLists.txt rename {plugin-taskbar => panel/backends/xcb}/lxqttaskbarbackend_x11.cpp (100%) rename {plugin-taskbar => panel/backends/xcb}/lxqttaskbarbackend_x11.h (98%) diff --git a/panel/CMakeLists.txt b/panel/CMakeLists.txt index 916fced2c..c9143e20c 100644 --- a/panel/CMakeLists.txt +++ b/panel/CMakeLists.txt @@ -1,5 +1,8 @@ set(PROJECT lxqt-panel) +# TODO +add_subdirectory(backends) + set(PRIV_HEADERS panelpluginsmodel.h windownotifier.h @@ -18,6 +21,11 @@ set(PRIV_HEADERS config/configstyling.h config/configpluginswidget.h config/addplugindialog.h + + backends/ilxqttaskbarabstractbackend.h + backends/lxqttaskbartypes.h + + backends/xcb/lxqttaskbarbackend_x11.h ) # using LXQt namespace in the public headers. @@ -26,6 +34,9 @@ set(PUB_HEADERS pluginsettings.h ilxqtpanelplugin.h ilxqtpanel.h + + backends/ilxqttaskbarabstractbackend.h + backends/lxqttaskbartypes.h ) set(SOURCES @@ -45,6 +56,10 @@ set(SOURCES config/configstyling.cpp config/configpluginswidget.cpp config/addplugindialog.cpp + + backends/ilxqttaskbarabstractbackend.cpp + + backends/xcb/lxqttaskbarbackend_x11.cpp ) set(UI diff --git a/panel/backends/CMakeLists.txt b/panel/backends/CMakeLists.txt new file mode 100644 index 000000000..8f34a3c67 --- /dev/null +++ b/panel/backends/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(xcb) diff --git a/plugin-taskbar/ilxqttaskbarabstractbackend.cpp b/panel/backends/ilxqttaskbarabstractbackend.cpp similarity index 90% rename from plugin-taskbar/ilxqttaskbarabstractbackend.cpp rename to panel/backends/ilxqttaskbarabstractbackend.cpp index d0959fb78..acf33f464 100644 --- a/plugin-taskbar/ilxqttaskbarabstractbackend.cpp +++ b/panel/backends/ilxqttaskbarabstractbackend.cpp @@ -1,4 +1,4 @@ -#include "ilxqttaskbarabstractbackend.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" ILXQtTaskbarAbstractBackend::ILXQtTaskbarAbstractBackend(QObject *parent) diff --git a/plugin-taskbar/ilxqttaskbarabstractbackend.h b/panel/backends/ilxqttaskbarabstractbackend.h similarity index 100% rename from plugin-taskbar/ilxqttaskbarabstractbackend.h rename to panel/backends/ilxqttaskbarabstractbackend.h diff --git a/plugin-taskbar/lxqttaskbartypes.h b/panel/backends/lxqttaskbartypes.h similarity index 100% rename from plugin-taskbar/lxqttaskbartypes.h rename to panel/backends/lxqttaskbartypes.h diff --git a/panel/backends/xcb/CMakeLists.txt b/panel/backends/xcb/CMakeLists.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/panel/backends/xcb/CMakeLists.txt @@ -0,0 +1 @@ + diff --git a/plugin-taskbar/lxqttaskbarbackend_x11.cpp b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp similarity index 100% rename from plugin-taskbar/lxqttaskbarbackend_x11.cpp rename to panel/backends/xcb/lxqttaskbarbackend_x11.cpp diff --git a/plugin-taskbar/lxqttaskbarbackend_x11.h b/panel/backends/xcb/lxqttaskbarbackend_x11.h similarity index 98% rename from plugin-taskbar/lxqttaskbarbackend_x11.h rename to panel/backends/xcb/lxqttaskbarbackend_x11.h index 26c721cf7..2797a7da8 100644 --- a/plugin-taskbar/lxqttaskbarbackend_x11.h +++ b/panel/backends/xcb/lxqttaskbarbackend_x11.h @@ -1,7 +1,7 @@ #ifndef LXQTTASKBARBACKEND_X11_H #define LXQTTASKBARBACKEND_X11_H -#include "ilxqttaskbarabstractbackend.h" +#include "../ilxqttaskbarabstractbackend.h" //TODO: make PIMPL to forward declare NET::Properties, Display, xcb_connection_t #include diff --git a/panel/lxqtpanelapplication.cpp b/panel/lxqtpanelapplication.cpp index 430ea6f35..2b1980d85 100644 --- a/panel/lxqtpanelapplication.cpp +++ b/panel/lxqtpanelapplication.cpp @@ -25,22 +25,35 @@ * * END_COMMON_COPYRIGHT_HEADER */ - #include "lxqtpanelapplication.h" #include "lxqtpanelapplication_p.h" -#include "lxqtpanel.h" + #include "config/configpaneldialog.h" -#include -#include -#include +#include "lxqtpanel.h" + +#include #include +#include #include -#include +#include +#include + +#include "backends/xcb/lxqttaskbarbackend_x11.h" + +ILXQtTaskbarAbstractBackend *createWMBackend() +{ + if(qGuiApp->nativeInterface()) + return new LXQtTaskbarX11Backend; + + Q_ASSERT_X(false, "createWMBackend()", "Only X11 supported!"); + return nullptr; +} LXQtPanelApplicationPrivate::LXQtPanelApplicationPrivate(LXQtPanelApplication *q) : mSettings(nullptr), q_ptr(q) { + mWMBackend = createWMBackend(); } @@ -290,6 +303,12 @@ bool LXQtPanelApplication::isPluginSingletonAndRunning(QString const & pluginId) return false; } +ILXQtTaskbarAbstractBackend *LXQtPanelApplication::getWMBackend() const +{ + Q_D(const LXQtPanelApplication); + return d->mWMBackend; +} + // See LXQtPanelApplication::LXQtPanelApplication for why this isn't good. void LXQtPanelApplication::setIconTheme(const QString &iconTheme) { diff --git a/panel/lxqtpanelapplication.h b/panel/lxqtpanelapplication.h index a672e7e46..15c912884 100644 --- a/panel/lxqtpanelapplication.h +++ b/panel/lxqtpanelapplication.h @@ -37,6 +37,8 @@ class QScreen; class LXQtPanel; class LXQtPanelApplicationPrivate; +class ILXQtTaskbarAbstractBackend; + /*! * \brief The LXQtPanelApplication class inherits from LXQt::Application and * is therefore the QApplication that we will create and execute in our @@ -89,6 +91,8 @@ class LXQtPanelApplication : public LXQt::Application */ bool isPluginSingletonAndRunning(QString const & pluginId) const; + ILXQtTaskbarAbstractBackend* getWMBackend() const; + public slots: /*! * \brief Adds a new LXQtPanel which consists of the following steps: diff --git a/panel/lxqtpanelapplication_p.h b/panel/lxqtpanelapplication_p.h index 4dd26182c..db924bf62 100644 --- a/panel/lxqtpanelapplication_p.h +++ b/panel/lxqtpanelapplication_p.h @@ -27,6 +27,8 @@ namespace LXQt { class Settings; } +class ILXQtTaskbarAbstractBackend; + class LXQtPanelApplicationPrivate { Q_DECLARE_PUBLIC(LXQtPanelApplication) public: @@ -35,6 +37,7 @@ class LXQtPanelApplicationPrivate { ~LXQtPanelApplicationPrivate() {}; LXQt::Settings *mSettings; + ILXQtTaskbarAbstractBackend *mWMBackend; ILXQtPanel::Position computeNewPanelPosition(const LXQtPanel *p, const int screenNum); diff --git a/plugin-taskbar/CMakeLists.txt b/plugin-taskbar/CMakeLists.txt index be434f6a6..74a00d6e6 100644 --- a/plugin-taskbar/CMakeLists.txt +++ b/plugin-taskbar/CMakeLists.txt @@ -8,9 +8,6 @@ set(HEADERS lxqttaskgroup.h lxqtgrouppopup.h - ilxqttaskbarabstractbackend.h - lxqttaskbartypes.h - lxqttaskbarbackend_x11.h lxqttaskbarproxymodel.h ) @@ -22,8 +19,6 @@ set(SOURCES lxqttaskgroup.cpp lxqtgrouppopup.cpp - ilxqttaskbarabstractbackend.cpp - lxqttaskbarbackend_x11.cpp lxqttaskbarproxymodel.cpp ) diff --git a/plugin-taskbar/lxqttaskbar.cpp b/plugin-taskbar/lxqttaskbar.cpp index 1bf7781f7..c306d796e 100644 --- a/plugin-taskbar/lxqttaskbar.cpp +++ b/plugin-taskbar/lxqttaskbar.cpp @@ -50,7 +50,8 @@ #include "lxqttaskgroup.h" #include "../panel/pluginsettings.h" -#include "lxqttaskbarbackend_x11.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" +#include "../panel/lxqtpanelapplication.h" using namespace LXQt; @@ -93,9 +94,9 @@ LXQtTaskBar::LXQtTaskBar(ILXQtPanelPlugin *plugin, QWidget *parent) : mPlaceHolder->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); mLayout->addWidget(mPlaceHolder); - // Create model - //TODO: use backend factory - mBackend = new LXQtTaskbarX11Backend(this); + // Get backend + LXQtPanelApplication *a = static_cast(qApp); + mBackend = a->getWMBackend(); QTimer::singleShot(0, this, &LXQtTaskBar::settingsChanged); setAcceptDrops(true); diff --git a/plugin-taskbar/lxqttaskbarproxymodel.cpp b/plugin-taskbar/lxqttaskbarproxymodel.cpp index e2f1d3941..fdcdfa4d4 100644 --- a/plugin-taskbar/lxqttaskbarproxymodel.cpp +++ b/plugin-taskbar/lxqttaskbarproxymodel.cpp @@ -1,6 +1,6 @@ #include "lxqttaskbarproxymodel.h" -#include "ilxqttaskbarabstractbackend.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" #include diff --git a/plugin-taskbar/lxqttaskbarproxymodel.h b/plugin-taskbar/lxqttaskbarproxymodel.h index be5799076..8bbb5ec49 100644 --- a/plugin-taskbar/lxqttaskbarproxymodel.h +++ b/plugin-taskbar/lxqttaskbarproxymodel.h @@ -4,7 +4,7 @@ #include #include -#include "lxqttaskbartypes.h" +#include "../panel/backends/lxqttaskbartypes.h" class ILXQtTaskbarAbstractBackend; diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 08bc50a1e..019b45199 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -50,7 +50,7 @@ #include #include -#include "ilxqttaskbarabstractbackend.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" diff --git a/plugin-taskbar/lxqttaskgroup.cpp b/plugin-taskbar/lxqttaskgroup.cpp index 30babc358..c6075df89 100644 --- a/plugin-taskbar/lxqttaskgroup.cpp +++ b/plugin-taskbar/lxqttaskgroup.cpp @@ -42,7 +42,7 @@ #include #include -#include "ilxqttaskbarabstractbackend.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" /************************************************ diff --git a/plugin-taskbar/lxqttaskgroup.h b/plugin-taskbar/lxqttaskgroup.h index d4868c5d2..31ab784fb 100644 --- a/plugin-taskbar/lxqttaskgroup.h +++ b/plugin-taskbar/lxqttaskgroup.h @@ -33,7 +33,7 @@ #include "lxqttaskbutton.h" -#include "lxqttaskbartypes.h" +#include "../panel/backends/lxqttaskbartypes.h" class QVBoxLayout; class ILXQtPanelPlugin; From a891f84d2d9b4c73eb6ca23243b016641382521b Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Mon, 19 Feb 2024 23:17:55 +0100 Subject: [PATCH 025/165] ILXQtTaskbarAbstractBackend: new isAreaOverlapped() method --- panel/backends/ilxqttaskbarabstractbackend.h | 3 ++ panel/backends/xcb/lxqttaskbarbackend_x11.cpp | 32 +++++++++++++++++++ panel/backends/xcb/lxqttaskbarbackend_x11.h | 3 ++ 3 files changed, 38 insertions(+) diff --git a/panel/backends/ilxqttaskbarabstractbackend.h b/panel/backends/ilxqttaskbarabstractbackend.h index 932d64fc0..fb49d7b52 100644 --- a/panel/backends/ilxqttaskbarabstractbackend.h +++ b/panel/backends/ilxqttaskbarabstractbackend.h @@ -65,6 +65,9 @@ class ILXQtTaskbarAbstractBackend : public QObject virtual void refreshIconGeometry(WId windowId, const QRect &geom) = 0; + // Panel internal + virtual bool isAreaOverlapped(const QRect& area) const = 0; + signals: void reloaded(); diff --git a/panel/backends/xcb/lxqttaskbarbackend_x11.cpp b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp index bcb83bdde..1d29148c8 100644 --- a/panel/backends/xcb/lxqttaskbarbackend_x11.cpp +++ b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp @@ -585,3 +585,35 @@ void LXQtTaskbarX11Backend::refreshIconGeometry(WId windowId, QRect const & geom nrect.size.width = geom.width(); info.setIconGeometry(nrect); } + +bool LXQtTaskbarX11Backend::isAreaOverlapped(const QRect &area) const +{ + //TODO: reuse our m_windows cache? + QFlags ignoreList; + ignoreList |= NET::DesktopMask; + ignoreList |= NET::DockMask; + ignoreList |= NET::SplashMask; + ignoreList |= NET::MenuMask; + ignoreList |= NET::PopupMenuMask; + ignoreList |= NET::DropdownMenuMask; + ignoreList |= NET::TopMenuMask; + ignoreList |= NET::NotificationMask; + + const auto wIds = KX11Extras::stackingOrder(); + for (auto const wId : wIds) + { + KWindowInfo info(wId, NET::WMWindowType | NET::WMState | NET::WMFrameExtents | NET::WMDesktop); + if (info.valid() + // skip windows that are on other desktops + && info.isOnCurrentDesktop() + // skip shaded, minimized or hidden windows + && !(info.state() & (NET::Shaded | NET::Hidden)) + // check against the list of ignored types + && !NET::typeMatchesMask(info.windowType(NET::AllTypesMask), ignoreList)) + { + if (info.frameGeometry().intersects(area)) + return true; + } + } + return false; +} diff --git a/panel/backends/xcb/lxqttaskbarbackend_x11.h b/panel/backends/xcb/lxqttaskbarbackend_x11.h index 2797a7da8..42e84b146 100644 --- a/panel/backends/xcb/lxqttaskbarbackend_x11.h +++ b/panel/backends/xcb/lxqttaskbarbackend_x11.h @@ -61,6 +61,9 @@ class LXQtTaskbarX11Backend : public ILXQtTaskbarAbstractBackend virtual void refreshIconGeometry(WId windowId, const QRect &geom) override; + // Panel internal + virtual bool isAreaOverlapped(const QRect& area) const override; + private slots: void onWindowChanged(WId windowId, NET::Properties prop, NET::Properties2 prop2); void onWindowAdded(WId windowId); From 1500200a5afe754a4bf577f18da64214344f47e4 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Mon, 19 Feb 2024 23:11:23 +0100 Subject: [PATCH 026/165] LXQtPanel: use less KX11Extras and more ILXQtTaskbarAbstractBackend --- panel/lxqtpanel.cpp | 83 +++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 48 deletions(-) diff --git a/panel/lxqtpanel.cpp b/panel/lxqtpanel.cpp index 0835aebb4..b704fdd3d 100644 --- a/panel/lxqtpanel.cpp +++ b/panel/lxqtpanel.cpp @@ -51,7 +51,9 @@ #include #include #include -#include + +#include "backends/ilxqttaskbarabstractbackend.h" + // Turn on this to show the time required to load each plugin during startup // #define DEBUG_PLUGIN_LOADTIME @@ -242,18 +244,21 @@ LXQtPanel::LXQtPanel(const QString &configGroup, LXQt::Settings *settings, QWidg QTimer::singleShot(PANEL_HIDE_FIRST_TIME, this, SLOT(hidePanel())); } - connect(KX11Extras::self(), &KX11Extras::windowAdded, this, [this] { + LXQtPanelApplication *a = reinterpret_cast(qApp); + auto wmBackend = a->getWMBackend(); + + connect(wmBackend, &ILXQtTaskbarAbstractBackend::windowAdded, this, [this] { if (mHidable && mHideOnOverlap && !mHidden) { mShowDelayTimer.stop(); hidePanel(); } }); - connect(KX11Extras::self(), &KX11Extras::windowRemoved, this, [this] { + connect(wmBackend, &ILXQtTaskbarAbstractBackend::windowRemoved, this, [this] { if (mHidable && mHideOnOverlap && mHidden && !isPanelOverlapped()) mShowDelayTimer.start(); }); - connect(KX11Extras::self(), &KX11Extras::currentDesktopChanged, this, [this] { + connect(wmBackend, &ILXQtTaskbarAbstractBackend::currentWorkspaceChanged, this, [this] { if (mHidable && mHideOnOverlap) { if (!mHidden) @@ -265,12 +270,12 @@ LXQtPanel::LXQtPanel(const QString &configGroup, LXQt::Settings *settings, QWidg mShowDelayTimer.start(); } }); - connect(KX11Extras::self(), - static_cast(&KX11Extras::windowChanged), - this, [this] (WId /* id */, NET::Properties prop, NET::Properties2) { + connect(wmBackend, &ILXQtTaskbarAbstractBackend::windowPropertyChanged, + this, [this] (WId /* id */, int prop) + { if (mHidable && mHideOnOverlap // when a window is moved, resized, shaded, or minimized - && (prop.testFlag(NET::WMGeometry) || prop.testFlag(NET::WMState))) + && (prop == int(LXQtTaskBarWindowProperty::Geometry) || prop == int(LXQtTaskBarWindowProperty::State))) { if (!mHidden) { @@ -419,7 +424,8 @@ LXQtPanel::~LXQtPanel() void LXQtPanel::show() { QWidget::show(); - KX11Extras::setOnDesktop(effectiveWinId(), NET::OnAllDesktops); + if(qGuiApp->nativeInterface()) //TODO: cache in bool isPlatformX11 + KX11Extras::setOnDesktop(effectiveWinId(), NET::OnAllDesktops); } @@ -1115,22 +1121,25 @@ bool LXQtPanel::event(QEvent *event) case QEvent::WinIdChange: { - // qDebug() << "WinIdChange" << hex << effectiveWinId(); - if(effectiveWinId() == 0) - break; + if(qGuiApp->nativeInterface()) + { + // qDebug() << "WinIdChange" << hex << effectiveWinId(); + if(effectiveWinId() == 0) + break; - // Sometimes Qt needs to re-create the underlying window of the widget and - // the winId() may be changed at runtime. So we need to reset all X11 properties - // when this happens. + // Sometimes Qt needs to re-create the underlying window of the widget and + // the winId() may be changed at runtime. So we need to reset all X11 properties + // when this happens. qDebug() << "WinIdChange" << Qt::hex << effectiveWinId() << "handle" << windowHandle() << windowHandle()->screen(); - // Qt::WA_X11NetWmWindowTypeDock becomes ineffective in Qt 5 - // See QTBUG-39887: https://bugreports.qt-project.org/browse/QTBUG-39887 - // Let's use KWindowSystem for that - KX11Extras::setType(effectiveWinId(), NET::Dock); + // Qt::WA_X11NetWmWindowTypeDock becomes ineffective in Qt 5 + // See QTBUG-39887: https://bugreports.qt-project.org/browse/QTBUG-39887 + // Let's use KWindowSystem for that + KX11Extras::setType(effectiveWinId(), NET::Dock); - updateWmStrut(); // reserve screen space for the panel - KX11Extras::setOnAllDesktops(effectiveWinId(), true); + updateWmStrut(); // reserve screen space for the panel + KX11Extras::setOnAllDesktops(effectiveWinId(), true); + } break; } case QEvent::DragEnter: @@ -1416,33 +1425,11 @@ void LXQtPanel::userRequestForDeletion() bool LXQtPanel::isPanelOverlapped() const { - QFlags ignoreList; - ignoreList |= NET::DesktopMask; - ignoreList |= NET::DockMask; - ignoreList |= NET::SplashMask; - ignoreList |= NET::MenuMask; - ignoreList |= NET::PopupMenuMask; - ignoreList |= NET::DropdownMenuMask; - ignoreList |= NET::TopMenuMask; - ignoreList |= NET::NotificationMask; - - const auto wIds = KX11Extras::stackingOrder(); - for (auto const wId : wIds) - { - KWindowInfo info(wId, NET::WMWindowType | NET::WMState | NET::WMFrameExtents | NET::WMDesktop); - if (info.valid() - // skip windows that are on other desktops - && info.isOnCurrentDesktop() - // skip shaded, minimized or hidden windows - && !(info.state() & (NET::Shaded | NET::Hidden)) - // check against the list of ignored types - && !NET::typeMatchesMask(info.windowType(NET::AllTypesMask), ignoreList)) - { - if (info.frameGeometry().intersects(mGeometry)) - return true; - } - } - return false; + LXQtPanelApplication *a = reinterpret_cast(qApp); + + //TODO: calculate geometry on wayland + QRect area = mGeometry; + return a->getWMBackend()->isAreaOverlapped(area); } void LXQtPanel::showPanel(bool animate) From a7a6e5dc66f41518fbc377a28c8d78147ac121bc Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 20:32:50 +0100 Subject: [PATCH 027/165] ILXQtTaskbarAbstractBackend: add dummy implementation This will be used to avoid crashing panel in case no backend could be created. A warning message will be printed in this case. --- panel/CMakeLists.txt | 2 + panel/backends/lxqttaskbardummybackend.cpp | 156 +++++++++++++++++++++ panel/backends/lxqttaskbardummybackend.h | 82 +++++++++++ panel/lxqtpanelapplication.cpp | 10 +- 4 files changed, 248 insertions(+), 2 deletions(-) create mode 100644 panel/backends/lxqttaskbardummybackend.cpp create mode 100644 panel/backends/lxqttaskbardummybackend.h diff --git a/panel/CMakeLists.txt b/panel/CMakeLists.txt index c9143e20c..79f46497b 100644 --- a/panel/CMakeLists.txt +++ b/panel/CMakeLists.txt @@ -25,6 +25,7 @@ set(PRIV_HEADERS backends/ilxqttaskbarabstractbackend.h backends/lxqttaskbartypes.h + backends/lxqttaskbardummybackend.h backends/xcb/lxqttaskbarbackend_x11.h ) @@ -59,6 +60,7 @@ set(SOURCES backends/ilxqttaskbarabstractbackend.cpp + backends/lxqttaskbardummybackend.cpp backends/xcb/lxqttaskbarbackend_x11.cpp ) diff --git a/panel/backends/lxqttaskbardummybackend.cpp b/panel/backends/lxqttaskbardummybackend.cpp new file mode 100644 index 000000000..f91fa75bf --- /dev/null +++ b/panel/backends/lxqttaskbardummybackend.cpp @@ -0,0 +1,156 @@ +#include "lxqttaskbardummybackend.h" + +#include + +LXQtTaskBarDummyBackend::LXQtTaskBarDummyBackend(QObject *parent) + : ILXQtTaskbarAbstractBackend(parent) +{ + +} + + +/************************************************ + * Windows function + ************************************************/ +bool LXQtTaskBarDummyBackend::supportsAction(WId, LXQtTaskBarBackendAction) const +{ + return false; +} + +bool LXQtTaskBarDummyBackend::reloadWindows() +{ + return false; +} + +QVector LXQtTaskBarDummyBackend::getCurrentWindows() const +{ + return {}; +} + +QString LXQtTaskBarDummyBackend::getWindowTitle(WId) const +{ + return QString(); +} + +bool LXQtTaskBarDummyBackend::applicationDemandsAttention(WId) const +{ + return false; +} + +QIcon LXQtTaskBarDummyBackend::getApplicationIcon(WId, int) const +{ + return QIcon(); +} + +QString LXQtTaskBarDummyBackend::getWindowClass(WId) const +{ + return QString(); +} + +LXQtTaskBarWindowLayer LXQtTaskBarDummyBackend::getWindowLayer(WId) const +{ + return LXQtTaskBarWindowLayer::Normal; +} + +bool LXQtTaskBarDummyBackend::setWindowLayer(WId, LXQtTaskBarWindowLayer) +{ + return false; +} + +LXQtTaskBarWindowState LXQtTaskBarDummyBackend::getWindowState(WId) const +{ + return LXQtTaskBarWindowState::Normal; +} + +bool LXQtTaskBarDummyBackend::setWindowState(WId, LXQtTaskBarWindowState, bool) +{ + return false; +} + +bool LXQtTaskBarDummyBackend::isWindowActive(WId) const +{ + return false; +} + +bool LXQtTaskBarDummyBackend::raiseWindow(WId, bool) +{ + return false; +} + +bool LXQtTaskBarDummyBackend::closeWindow(WId) +{ + return false; +} + +WId LXQtTaskBarDummyBackend::getActiveWindow() const +{ + return 0; +} + + +/************************************************ + * Workspaces + ************************************************/ +int LXQtTaskBarDummyBackend::getWorkspacesCount() const +{ + return 1; // Fake 1 workspace +} + +QString LXQtTaskBarDummyBackend::getWorkspaceName(int) const +{ + return QString(); +} + +int LXQtTaskBarDummyBackend::getCurrentWorkspace() const +{ + return 0; +} + +bool LXQtTaskBarDummyBackend::setCurrentWorkspace(int) +{ + return false; +} + +int LXQtTaskBarDummyBackend::getWindowWorkspace(WId) const +{ + return 0; +} + +bool LXQtTaskBarDummyBackend::setWindowOnWorkspace(WId, int) +{ + return false; +} + +void LXQtTaskBarDummyBackend::moveApplicationToPrevNextMonitor(WId, bool, bool) +{ + //No-op +} + +bool LXQtTaskBarDummyBackend::isWindowOnScreen(QScreen *, WId) const +{ + return false; +} + +/************************************************ + * X11 Specific + ************************************************/ +void LXQtTaskBarDummyBackend::moveApplication(WId) +{ + //No-op +} + +void LXQtTaskBarDummyBackend::resizeApplication(WId) +{ + //No-op +} + +void LXQtTaskBarDummyBackend::refreshIconGeometry(WId, QRect const &) +{ + //No-op +} + +bool LXQtTaskBarDummyBackend::isAreaOverlapped(const QRect &) const +{ + return false; +} + diff --git a/panel/backends/lxqttaskbardummybackend.h b/panel/backends/lxqttaskbardummybackend.h new file mode 100644 index 000000000..535de303b --- /dev/null +++ b/panel/backends/lxqttaskbardummybackend.h @@ -0,0 +1,82 @@ +#ifndef LXQTTASKBARDUMMYBACKEND_H +#define LXQTTASKBARDUMMYBACKEND_H + +#include "ilxqttaskbarabstractbackend.h" + +class LXQtTaskBarDummyBackend : public ILXQtTaskbarAbstractBackend +{ + Q_OBJECT + +public: + explicit LXQtTaskBarDummyBackend(QObject *parent = nullptr); + + // Backend + bool supportsAction(WId windowId, LXQtTaskBarBackendAction action) const override; + + // Windows + bool reloadWindows() override; + + QVector getCurrentWindows() const override; + + QString getWindowTitle(WId windowId) const override; + + bool applicationDemandsAttention(WId windowId) const override; + + QIcon getApplicationIcon(WId windowId, int fallbackDevicePixels) const override; + + QString getWindowClass(WId windowId) const override; + + LXQtTaskBarWindowLayer getWindowLayer(WId windowId) const override; + bool setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) override; + + LXQtTaskBarWindowState getWindowState(WId windowId) const override; + bool setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set = true) override; + + bool isWindowActive(WId windowId) const override; + bool raiseWindow(WId windowId, bool onCurrentWorkSpace) override; + + bool closeWindow(WId windowId) override; + + WId getActiveWindow() const override; + + // Workspaces + int getWorkspacesCount() const override; + QString getWorkspaceName(int idx) const override; + + int getCurrentWorkspace() const override; + bool setCurrentWorkspace(int idx) override; + + int getWindowWorkspace(WId windowId) const override; + bool setWindowOnWorkspace(WId windowId, int idx) override; + + void moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) override; + + bool isWindowOnScreen(QScreen *screen, WId windowId) const override; + + // X11 Specific + void moveApplication(WId windowId) override; + void resizeApplication(WId windowId) override; + + void refreshIconGeometry(WId windowId, const QRect &geom) override; + + // Panel internal + bool isAreaOverlapped(const QRect& area) const override; + +signals: + void reloaded(); + + // Windows + void windowAdded(WId windowId); + void windowRemoved(WId windowId); + void windowPropertyChanged(WId windowId, int prop); + + // Workspaces + void workspacesCountChanged(); + void workspaceNameChanged(int idx); + void currentWorkspaceChanged(int idx); + + // TODO: needed? + void activeWindowChanged(WId windowId); +}; + +#endif // LXQTTASKBARDUMMYBACKEND_H diff --git a/panel/lxqtpanelapplication.cpp b/panel/lxqtpanelapplication.cpp index 2b1980d85..c3b666620 100644 --- a/panel/lxqtpanelapplication.cpp +++ b/panel/lxqtpanelapplication.cpp @@ -38,6 +38,7 @@ #include #include +#include "backends/lxqttaskbardummybackend.h" #include "backends/xcb/lxqttaskbarbackend_x11.h" ILXQtTaskbarAbstractBackend *createWMBackend() @@ -45,8 +46,13 @@ ILXQtTaskbarAbstractBackend *createWMBackend() if(qGuiApp->nativeInterface()) return new LXQtTaskbarX11Backend; - Q_ASSERT_X(false, "createWMBackend()", "Only X11 supported!"); - return nullptr; + qWarning() << "\n" + << "ERROR: Could not create a backend for window managment operations.\n" + << "Only X11 supported!\n" + << "Falling back to dummy backend. Some functions will not be available.\n" + << "\n"; + + return new LXQtTaskBarDummyBackend; } LXQtPanelApplicationPrivate::LXQtPanelApplicationPrivate(LXQtPanelApplication *q) From 0d4190af472761487bce42a7bb6134c424156810 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Wed, 28 Feb 2024 20:32:04 +0100 Subject: [PATCH 028/165] LXQtMainMenu: indent header include --- plugin-mainmenu/lxqtmainmenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-mainmenu/lxqtmainmenu.cpp b/plugin-mainmenu/lxqtmainmenu.cpp index 1db7f911f..0f55cdfbd 100644 --- a/plugin-mainmenu/lxqtmainmenu.cpp +++ b/plugin-mainmenu/lxqtmainmenu.cpp @@ -54,7 +54,7 @@ #include #include #include -#include + #include #endif #define DEFAULT_SHORTCUT "Alt+F1" From 82bcd4be413f76482c675dacf8971f81fad03e4a Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Mon, 26 Feb 2024 13:34:42 +0100 Subject: [PATCH 029/165] SNIProxy: use Qt Private headers to get Xcb timestamp --- plugin-tray/CMakeLists.txt | 1 + plugin-tray/sniproxy.cpp | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/plugin-tray/CMakeLists.txt b/plugin-tray/CMakeLists.txt index a0541e226..fc51b1b50 100644 --- a/plugin-tray/CMakeLists.txt +++ b/plugin-tray/CMakeLists.txt @@ -38,6 +38,7 @@ qt_add_dbus_adaptor(SOURCES org.kde.StatusNotifierItem.xml sniproxy.h SNIProxy) qt_add_dbus_interface(SOURCES org.kde.StatusNotifierWatcher.xml statusnotifierwatcher_interface) set(LIBRARIES + Qt6::GuiPrivate ${XCB_LIBRARIES} ${xtst_LDFLAGS} ) diff --git a/plugin-tray/sniproxy.cpp b/plugin-tray/sniproxy.cpp index 9fbe924b4..4cb05ce97 100644 --- a/plugin-tray/sniproxy.cpp +++ b/plugin-tray/sniproxy.cpp @@ -38,6 +38,7 @@ #include #include +#include //For "gettimestamp" Xcb integration resource #include "kwindowinfo.h" #include "statusnotifieritemadaptor.h" @@ -590,13 +591,17 @@ void SNIProxy::sendClick(uint8_t mouseButton, int x, int y) auto *x11Application = qGuiApp->nativeInterface(); WId appRootWindow = XDefaultRootWindow(x11Application->display()); + // Qt private access + void *ptr = qGuiApp->platformNativeInterface()->nativeResourceForScreen("gettimestamp", qGuiApp->primaryScreen()); + xcb_timestamp_t timeStamp = reinterpret_cast(ptr); + // mouse down if (m_injectMode == Direct) { xcb_button_press_event_t *event = new xcb_button_press_event_t; memset(event, 0x00, sizeof(xcb_button_press_event_t)); event->response_type = XCB_BUTTON_PRESS; event->event = m_windowId; - event->time = XCB_CURRENT_TIME; //NOTE: to get proper timestamp we would need Qt Private APIs + event->time = timeStamp; event->same_screen = 1; event->root = appRootWindow; event->root_x = x; @@ -619,7 +624,7 @@ void SNIProxy::sendClick(uint8_t mouseButton, int x, int y) memset(event, 0x00, sizeof(xcb_button_release_event_t)); event->response_type = XCB_BUTTON_RELEASE; event->event = m_windowId; - event->time = XCB_CURRENT_TIME; //NOTE: to get proper timestamp we would need Qt Private APIs + event->time = timeStamp; event->same_screen = 1; event->root = appRootWindow; event->root_x = x; From 01306fe0e51d562fadfe10bc9695ba5f0e8d3060 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 17:12:03 +0100 Subject: [PATCH 030/165] ILXQtTaskbarAbstractBackend: new showDesktop() function --- panel/backends/ilxqttaskbarabstractbackend.h | 8 ++++++++ panel/backends/lxqttaskbardummybackend.cpp | 10 ++++++++++ panel/backends/lxqttaskbardummybackend.h | 4 ++++ panel/backends/xcb/lxqttaskbarbackend_x11.cpp | 11 +++++++++++ panel/backends/xcb/lxqttaskbarbackend_x11.h | 4 ++++ 5 files changed, 37 insertions(+) diff --git a/panel/backends/ilxqttaskbarabstractbackend.h b/panel/backends/ilxqttaskbarabstractbackend.h index fb49d7b52..7daecfe60 100644 --- a/panel/backends/ilxqttaskbarabstractbackend.h +++ b/panel/backends/ilxqttaskbarabstractbackend.h @@ -68,6 +68,14 @@ class ILXQtTaskbarAbstractBackend : public QObject // Panel internal virtual bool isAreaOverlapped(const QRect& area) const = 0; + // Show Destop TODO: split in multiple interfeces, this is becoming big + // NOTE: KWindowSystem already has these functions + // However on Wayland they are only compatible with KWin + // because internally it uses org_kde_plasma_window_management protocol + // We make this virtual so it can be implemented also for other compositors + virtual bool isShowingDesktop() const = 0; + virtual bool showDesktop(bool value) = 0; + signals: void reloaded(); diff --git a/panel/backends/lxqttaskbardummybackend.cpp b/panel/backends/lxqttaskbardummybackend.cpp index f91fa75bf..ceaf3c334 100644 --- a/panel/backends/lxqttaskbardummybackend.cpp +++ b/panel/backends/lxqttaskbardummybackend.cpp @@ -154,3 +154,13 @@ bool LXQtTaskBarDummyBackend::isAreaOverlapped(const QRect &) const return false; } +bool LXQtTaskBarDummyBackend::isShowingDesktop() const +{ + return false; +} + +bool LXQtTaskBarDummyBackend::showDesktop(bool) +{ + return false; +} + diff --git a/panel/backends/lxqttaskbardummybackend.h b/panel/backends/lxqttaskbardummybackend.h index 535de303b..d4e1ca4bc 100644 --- a/panel/backends/lxqttaskbardummybackend.h +++ b/panel/backends/lxqttaskbardummybackend.h @@ -62,6 +62,10 @@ class LXQtTaskBarDummyBackend : public ILXQtTaskbarAbstractBackend // Panel internal bool isAreaOverlapped(const QRect& area) const override; + // Show Destop + bool isShowingDesktop() const override; + bool showDesktop(bool value) override; + signals: void reloaded(); diff --git a/panel/backends/xcb/lxqttaskbarbackend_x11.cpp b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp index 1d29148c8..822afed51 100644 --- a/panel/backends/xcb/lxqttaskbarbackend_x11.cpp +++ b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp @@ -617,3 +617,14 @@ bool LXQtTaskbarX11Backend::isAreaOverlapped(const QRect &area) const } return false; } + +bool LXQtTaskbarX11Backend::isShowingDesktop() const +{ + return KWindowSystem::showingDesktop(); +} + +bool LXQtTaskbarX11Backend::showDesktop(bool value) +{ + KWindowSystem::setShowingDesktop(value); + return true; +} diff --git a/panel/backends/xcb/lxqttaskbarbackend_x11.h b/panel/backends/xcb/lxqttaskbarbackend_x11.h index 42e84b146..8106cda9f 100644 --- a/panel/backends/xcb/lxqttaskbarbackend_x11.h +++ b/panel/backends/xcb/lxqttaskbarbackend_x11.h @@ -64,6 +64,10 @@ class LXQtTaskbarX11Backend : public ILXQtTaskbarAbstractBackend // Panel internal virtual bool isAreaOverlapped(const QRect& area) const override; + // Show Destop + virtual bool isShowingDesktop() const override; + virtual bool showDesktop(bool value) override; + private slots: void onWindowChanged(WId windowId, NET::Properties prop, NET::Properties2 prop2); void onWindowAdded(WId windowId); From 94d5443db4535ddd887a6bc87fb96d8aa25f9713 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 17:12:28 +0100 Subject: [PATCH 031/165] ShowDesktop: use ILXQtTaskbarAbstractBackend --- plugin-showdesktop/showdesktop.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugin-showdesktop/showdesktop.cpp b/plugin-showdesktop/showdesktop.cpp index 871da62ea..fb69f6067 100644 --- a/plugin-showdesktop/showdesktop.cpp +++ b/plugin-showdesktop/showdesktop.cpp @@ -29,11 +29,12 @@ #include #include #include -#include -#include #include "showdesktop.h" #include "../panel/pluginsettings.h" +#include "../panel/lxqtpanelapplication.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" + #define DEFAULT_SHORTCUT "Control+Alt+D" ShowDesktop::ShowDesktop(const ILXQtPanelPluginStartupInfo &startupInfo) : @@ -69,7 +70,9 @@ void ShowDesktop::shortcutRegistered() void ShowDesktop::toggleShowingDesktop() { - KWindowSystem::setShowingDesktop(!KWindowSystem::showingDesktop()); + LXQtPanelApplication *a = reinterpret_cast(qApp); + auto wmBackend = a->getWMBackend(); + wmBackend->showDesktop(!wmBackend->isShowingDesktop()); } #undef DEFAULT_SHORTCUT From 774cd7a22b87963a5d789b69c7fd8f1e7f175c05 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 23 Feb 2024 12:27:28 +0100 Subject: [PATCH 032/165] DesktopSwitch: port to ILXQtTaskbarAbstractBackend - Clarify desktop index range --- .../backends/ilxqttaskbarabstractbackend.cpp | 2 +- panel/backends/ilxqttaskbarabstractbackend.h | 1 + plugin-desktopswitch/desktopswitch.cpp | 141 ++++++++---------- plugin-desktopswitch/desktopswitch.h | 34 ++--- 4 files changed, 79 insertions(+), 99 deletions(-) diff --git a/panel/backends/ilxqttaskbarabstractbackend.cpp b/panel/backends/ilxqttaskbarabstractbackend.cpp index acf33f464..137728263 100644 --- a/panel/backends/ilxqttaskbarabstractbackend.cpp +++ b/panel/backends/ilxqttaskbarabstractbackend.cpp @@ -17,7 +17,7 @@ void ILXQtTaskbarAbstractBackend::moveApplicationToPrevNextDesktop(WId windowId, // Wrap around if (targetWorkspace > count) - targetWorkspace = 1; //TODO: are X11 desktops 1 based? + targetWorkspace = 1; //Ids are 1-based else if (targetWorkspace < 1) targetWorkspace = count; diff --git a/panel/backends/ilxqttaskbarabstractbackend.h b/panel/backends/ilxqttaskbarabstractbackend.h index 7daecfe60..fe3368363 100644 --- a/panel/backends/ilxqttaskbarabstractbackend.h +++ b/panel/backends/ilxqttaskbarabstractbackend.h @@ -45,6 +45,7 @@ class ILXQtTaskbarAbstractBackend : public QObject virtual WId getActiveWindow() const = 0; // Workspaces + // NOTE: indexes are 1-based, 0 means "Show on All desktops" virtual int getWorkspacesCount() const = 0; virtual QString getWorkspaceName(int idx) const = 0; diff --git a/plugin-desktopswitch/desktopswitch.cpp b/plugin-desktopswitch/desktopswitch.cpp index 4bd05e08f..67f6e56eb 100644 --- a/plugin-desktopswitch/desktopswitch.cpp +++ b/plugin-desktopswitch/desktopswitch.cpp @@ -25,6 +25,7 @@ * * END_COMMON_COPYRIGHT_HEADER */ +#include #include #include #include @@ -33,6 +34,8 @@ #include #include +#include "../panel/lxqtpanelapplication.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" #include @@ -40,29 +43,22 @@ #include "desktopswitchbutton.h" #include "desktopswitchconfiguration.h" -#include -#include -#include - -//NOTE: Xlib.h defines Bool which conflicts with QJsonValue::Type enum -#include -#undef Bool - static const QString DEFAULT_SHORTCUT_TEMPLATE(QStringLiteral("Control+F%1")); DesktopSwitch::DesktopSwitch(const ILXQtPanelPluginStartupInfo &startupInfo) : QObject(), ILXQtPanelPlugin(startupInfo), m_pSignalMapper(new QSignalMapper(this)), - m_desktopCount(KX11Extras::numberOfDesktops()), + m_desktopCount(0), mRows(-1), mShowOnlyActive(false), - mDesktops(nullptr), mLabelType(static_cast(-1)) { - auto *x11Application = qGuiApp->nativeInterface(); - Q_ASSERT_X(x11Application, "DesktopSwitch", "Expected X11 connection"); - mDesktops.reset(new NETRootInfo(x11Application->connection(), NET::NumberOfDesktops | NET::CurrentDesktop | NET::DesktopNames, NET::WM2DesktopLayout)); + LXQtPanelApplication *a = reinterpret_cast(qApp); + mBackend = a->getWMBackend(); + + + m_desktopCount = mBackend->getWorkspacesCount(); m_buttons = new QButtonGroup(this); @@ -74,17 +70,16 @@ DesktopSwitch::DesktopSwitch(const ILXQtPanelPluginStartupInfo &startupInfo) : settingsChanged(); - onCurrentDesktopChanged(KX11Extras::currentDesktop()); + onCurrentDesktopChanged(mBackend->getCurrentWorkspace()); QTimer::singleShot(0, this, SLOT(registerShortcuts())); connect(m_buttons, &QButtonGroup::idClicked, this, &DesktopSwitch::setDesktop); - connect(KX11Extras::self(), &KX11Extras::numberOfDesktopsChanged, this, &DesktopSwitch::onNumberOfDesktopsChanged); - connect(KX11Extras::self(), &KX11Extras::currentDesktopChanged, this, &DesktopSwitch::onCurrentDesktopChanged); - connect(KX11Extras::self(), &KX11Extras::desktopNamesChanged, this, &DesktopSwitch::onDesktopNamesChanged); + connect(mBackend, &ILXQtTaskbarAbstractBackend::workspacesCountChanged, this, &DesktopSwitch::onNumberOfDesktopsChanged); + connect(mBackend, &ILXQtTaskbarAbstractBackend::currentWorkspaceChanged, this, &DesktopSwitch::onCurrentDesktopChanged); + connect(mBackend, &ILXQtTaskbarAbstractBackend::workspaceNameChanged, this, &DesktopSwitch::onDesktopNamesChanged); - connect(KX11Extras::self(), static_cast(&KX11Extras::windowChanged), - this, &DesktopSwitch::onWindowChanged); + connect(mBackend, &ILXQtTaskbarAbstractBackend::windowPropertyChanged, this, &DesktopSwitch::onWindowChanged); } void DesktopSwitch::registerShortcuts() @@ -126,19 +121,18 @@ void DesktopSwitch::shortcutRegistered() } } -void DesktopSwitch::onWindowChanged(WId id, NET::Properties properties, NET::Properties2 /*properties2*/) +void DesktopSwitch::onWindowChanged(WId id, int prop) { - if (properties.testFlag(NET::WMState) && isWindowHighlightable(id)) + if (prop == int(LXQtTaskBarWindowProperty::State) && isWindowHighlightable(id)) { - KWindowInfo info = KWindowInfo(id, NET::WMDesktop | NET::WMState); - int desktop = info.desktop(); - if (!info.valid() || info.onAllDesktops()) + int desktop = mBackend->getWindowWorkspace(id); + if (desktop == int(LXQtTaskBarWorkspace::ShowOnAll)) return; else { DesktopSwitchButton *button = static_cast(m_buttons->button(desktop - 1)); if(button) - button->setUrgencyHint(id, info.hasState(NET::DemandsAttention)); + button->setUrgencyHint(id, mBackend->applicationDemandsAttention(id)); } } } @@ -148,7 +142,7 @@ void DesktopSwitch::refresh() const QList btns = m_buttons->buttons(); int i = 0; - const int current_desktop = KX11Extras::currentDesktop(); + const int current_desktop = mBackend->getCurrentWorkspace(); const int current_cnt = btns.count(); const int border = qMin(btns.count(), m_desktopCount); //update existing buttons @@ -156,9 +150,9 @@ void DesktopSwitch::refresh() { DesktopSwitchButton * button = qobject_cast(btns[i]); button->update(i, mLabelType, - KX11Extras::desktopName(i + 1).isEmpty() ? + mBackend->getWorkspaceName(i + 1).isEmpty() ? tr("Desktop %1").arg(i + 1) : - KX11Extras::desktopName(i + 1)); + mBackend->getWorkspaceName(i + 1)); button->setVisible(!mShowOnlyActive || i + 1 == current_desktop); } @@ -167,9 +161,9 @@ void DesktopSwitch::refresh() for ( ; i < m_desktopCount; ++i) { b = new DesktopSwitchButton(&mWidget, i, mLabelType, - KX11Extras::desktopName(i+1).isEmpty() ? + mBackend->getWorkspaceName(i+1).isEmpty() ? tr("Desktop %1").arg(i+1) : - KX11Extras::desktopName(i+1)); + mBackend->getWorkspaceName(i+1)); mWidget.layout()->addWidget(b); m_buttons->addButton(b, i); b->setVisible(!mShowOnlyActive || i + 1 == current_desktop); @@ -185,56 +179,23 @@ void DesktopSwitch::refresh() } } -bool DesktopSwitch::isWindowHighlightable(WId window) +bool DesktopSwitch::isWindowHighlightable(WId) { - // this method was borrowed from the taskbar plugin - QFlags ignoreList; - ignoreList |= NET::DesktopMask; - ignoreList |= NET::DockMask; - ignoreList |= NET::SplashMask; - ignoreList |= NET::ToolbarMask; - ignoreList |= NET::MenuMask; - ignoreList |= NET::PopupMenuMask; - ignoreList |= NET::NotificationMask; - - KWindowInfo info(window, NET::WMWindowType | NET::WMState, NET::WM2TransientFor); - if (!info.valid()) - return false; - - if (NET::typeMatchesMask(info.windowType(NET::AllTypesMask), ignoreList)) - return false; - - if (info.state() & NET::SkipTaskbar) - return false; - - auto *x11Application = qGuiApp->nativeInterface(); - Q_ASSERT_X(x11Application, "DesktopSwitch", "Expected X11 connection"); - WId appRootWindow = XDefaultRootWindow(x11Application->display()); - - // WM_TRANSIENT_FOR hint not set - normal window - WId transFor = info.transientFor(); - if (transFor == 0 || transFor == window || transFor == appRootWindow) - return true; - - info = KWindowInfo(transFor, NET::WMWindowType); - - QFlags normalFlag; - normalFlag |= NET::NormalMask; - normalFlag |= NET::DialogMask; - normalFlag |= NET::UtilityMask; - - return !NET::typeMatchesMask(info.windowType(NET::AllTypesMask), normalFlag); + // Backend should emit signals only for higlightable windows and ignore others + // TODO: check + return true; } DesktopSwitch::~DesktopSwitch() = default; void DesktopSwitch::setDesktop(int desktop) { - KX11Extras::setCurrentDesktop(desktop + 1); + mBackend->setCurrentWorkspace(desktop + 1); } -void DesktopSwitch::onNumberOfDesktopsChanged(int count) +void DesktopSwitch::onNumberOfDesktopsChanged() { + int count = mBackend->getWorkspacesCount(); qDebug() << "Desktop count changed from" << m_desktopCount << "to" << count; m_desktopCount = count; refresh(); @@ -293,11 +254,11 @@ void DesktopSwitch::settingsChanged() int columns = static_cast(ceil(static_cast(m_desktopCount) / mRows)); if (panel()->isHorizontal()) { - mDesktops->setDesktopLayout(NET::OrientationHorizontal, columns, mRows, mWidget.isRightToLeft() ? NET::DesktopLayoutCornerTopRight : NET::DesktopLayoutCornerTopLeft); + //mDesktops->setDesktopLayout(NET::OrientationHorizontal, columns, mRows, mWidget.isRightToLeft() ? NET::DesktopLayoutCornerTopRight : NET::DesktopLayoutCornerTopLeft); } else { - mDesktops->setDesktopLayout(NET::OrientationHorizontal, mRows, columns, mWidget.isRightToLeft() ? NET::DesktopLayoutCornerTopRight : NET::DesktopLayoutCornerTopLeft); + //mDesktops->setDesktopLayout(NET::OrientationHorizontal, mRows, columns, mWidget.isRightToLeft() ? NET::DesktopLayoutCornerTopRight : NET::DesktopLayoutCornerTopLeft); } realign(); // in case it isn't called when the desktop layout changes } @@ -345,9 +306,12 @@ void DesktopSwitchWidget::wheelEvent(QWheelEvent *e) if(abs(m_mouseWheelThresholdCounter) < 100) return; - int max = KX11Extras::numberOfDesktops(); + LXQtPanelApplication *a = reinterpret_cast(qApp); + auto wmBackend = a->getWMBackend(); + + int max = wmBackend->getWorkspacesCount(); int delta = rotationSteps < 0 ? 1 : -1; - int current = KX11Extras::currentDesktop() + delta; + int current = wmBackend->getCurrentWorkspace() + delta; if (current > max){ current = 1; @@ -356,5 +320,32 @@ void DesktopSwitchWidget::wheelEvent(QWheelEvent *e) current = max; m_mouseWheelThresholdCounter = 0; - KX11Extras::setCurrentDesktop(current); + wmBackend->setCurrentWorkspace(current); +} + +ILXQtPanelPlugin *DesktopSwitchPluginLibrary::instance(const ILXQtPanelPluginStartupInfo &startupInfo) const +{ + return new DesktopSwitch{startupInfo}; + + //TODO: detect dummy backend and show unsupported message? + // Or instead remove it and make just a message box at application start + //return new DesktopSwitchUnsupported{startupInfo}; +} + +DesktopSwitchUnsupported::DesktopSwitchUnsupported(const ILXQtPanelPluginStartupInfo &startupInfo) + : ILXQtPanelPlugin(startupInfo) + , mLabel(new QLabel(tr("n/a"))) +{ + mLabel->setToolTip(tr("DesktopSwitch is unsupported on current platform: %1").arg(QGuiApplication::platformName())); +} + +DesktopSwitchUnsupported::~DesktopSwitchUnsupported() +{ + delete mLabel; + mLabel = nullptr; +} + +QWidget *DesktopSwitchUnsupported::widget() +{ + return mLabel; } diff --git a/plugin-desktopswitch/desktopswitch.h b/plugin-desktopswitch/desktopswitch.h index 93a930dc2..b57b44c32 100644 --- a/plugin-desktopswitch/desktopswitch.h +++ b/plugin-desktopswitch/desktopswitch.h @@ -30,21 +30,19 @@ #define DESKTOPSWITCH_H #include "../panel/ilxqtpanelplugin.h" -#include #include -#include -#include -#include #include "desktopswitchbutton.h" +class QLabel; class QSignalMapper; class QButtonGroup; -class NETRootInfo; namespace LXQt { class GridLayout; } +class ILXQtTaskbarAbstractBackend; + class DesktopSwitchWidget: public QFrame { Q_OBJECT @@ -85,7 +83,7 @@ class DesktopSwitch : public QObject, public ILXQtPanelPlugin LXQt::GridLayout *mLayout; int mRows; bool mShowOnlyActive; - std::unique_ptr mDesktops; + ILXQtTaskbarAbstractBackend *mBackend; DesktopSwitchButton::LabelType mLabelType; void refresh(); @@ -93,31 +91,27 @@ class DesktopSwitch : public QObject, public ILXQtPanelPlugin private slots: void setDesktop(int desktop); - void onNumberOfDesktopsChanged(int); + void onNumberOfDesktopsChanged(); void onCurrentDesktopChanged(int); void onDesktopNamesChanged(); virtual void settingsChanged(); void registerShortcuts(); void shortcutRegistered(); - void onWindowChanged(WId id, NET::Properties properties, NET::Properties2 properties2); + void onWindowChanged(WId id, int prop); }; class DesktopSwitchUnsupported : public QObject, public ILXQtPanelPlugin { Q_OBJECT public: - DesktopSwitchUnsupported(const ILXQtPanelPluginStartupInfo &startupInfo) - : ILXQtPanelPlugin(startupInfo) - , mLabel{tr("n/a")} - { - mLabel.setToolTip(tr("DesktopSwitch is unsupported on current platform: %1").arg(QGuiApplication::platformName())); - } + DesktopSwitchUnsupported(const ILXQtPanelPluginStartupInfo &startupInfo); + ~DesktopSwitchUnsupported(); QString themeId() const { return QStringLiteral("DesktopSwitchUnsupported"); } - QWidget *widget() { return &mLabel; } + QWidget *widget(); bool isSeparate() const { return true; } private: - QLabel mLabel; + QLabel *mLabel; }; class DesktopSwitchPluginLibrary: public QObject, public ILXQtPanelPluginLibrary @@ -126,13 +120,7 @@ class DesktopSwitchPluginLibrary: public QObject, public ILXQtPanelPluginLibrary // Q_PLUGIN_METADATA(IID "lxqt.org/Panel/PluginInterface/3.0") Q_INTERFACES(ILXQtPanelPluginLibrary) public: - ILXQtPanelPlugin *instance(const ILXQtPanelPluginStartupInfo &startupInfo) const - { - if (QGuiApplication::platformName() == QStringLiteral("xcb")) - return new DesktopSwitch{startupInfo}; - else - return new DesktopSwitchUnsupported{startupInfo}; - } + ILXQtPanelPlugin *instance(const ILXQtPanelPluginStartupInfo &startupInfo) const; }; #endif From 2002ec8d8703568f69bc3848a643570708971f56 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 23 Feb 2024 13:39:46 +0100 Subject: [PATCH 033/165] LXQtTaskbarConfiguration: port to ILXQtTaskBarAbstractBackend --- plugin-taskbar/lxqttaskbarconfiguration.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/plugin-taskbar/lxqttaskbarconfiguration.cpp b/plugin-taskbar/lxqttaskbarconfiguration.cpp index b346bb69d..0dd528e51 100644 --- a/plugin-taskbar/lxqttaskbarconfiguration.cpp +++ b/plugin-taskbar/lxqttaskbarconfiguration.cpp @@ -29,7 +29,9 @@ #include "lxqttaskbarconfiguration.h" #include "ui_lxqttaskbarconfiguration.h" -#include + +#include "../panel/lxqtpanelapplication.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" LXQtTaskbarConfiguration::LXQtTaskbarConfiguration(PluginSettings *settings, QWidget *parent): LXQtPanelPluginConfigDialog(settings, parent), @@ -52,11 +54,14 @@ LXQtTaskbarConfiguration::LXQtTaskbarConfiguration(PluginSettings *settings, QWi ui->wheelEventsActionCB->addItem(tr("Scroll up to move to next desktop, down to previous"), 4); ui->wheelEventsActionCB->addItem(tr("Scroll up to move to previous desktop, down to next"), 5); + LXQtPanelApplication *a = reinterpret_cast(qApp); + auto wmBackend = a->getWMBackend(); + ui->showDesktopNumCB->addItem(tr("Current"), 0); //Note: in KWindowSystem desktops are numbered from 1..N - const int desk_cnt = KX11Extras::numberOfDesktops(); + const int desk_cnt = wmBackend->getWorkspacesCount(); for (int i = 1; desk_cnt >= i; ++i) - ui->showDesktopNumCB->addItem(QString(QStringLiteral("%1 - %2")).arg(i).arg(KX11Extras::desktopName(i)), i); + ui->showDesktopNumCB->addItem(QString(QStringLiteral("%1 - %2")).arg(i).arg(wmBackend->getWorkspaceName(i)), i); loadSettings(); ui->ungroupedNextToExistingCB->setEnabled(!(ui->groupingGB->isChecked())); From ac9634e9094f97325979ce07ece560ddbe941a9d Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 23 Feb 2024 14:07:05 +0100 Subject: [PATCH 034/165] DesktopSwitchConfiguration: port to ILXQtTaskBarAbstractBackend TODO TODO: this will disable changing desktop names --- .../desktopswitchconfiguration.cpp | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/plugin-desktopswitch/desktopswitchconfiguration.cpp b/plugin-desktopswitch/desktopswitchconfiguration.cpp index 943dbdd6b..a6edb41a6 100644 --- a/plugin-desktopswitch/desktopswitchconfiguration.cpp +++ b/plugin-desktopswitch/desktopswitchconfiguration.cpp @@ -26,8 +26,9 @@ #include "desktopswitchconfiguration.h" #include "ui_desktopswitchconfiguration.h" -#include -#include + +#include "../panel/lxqtpanelapplication.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" DesktopSwitchConfiguration::DesktopSwitchConfiguration(PluginSettings *settings, QWidget *parent) : LXQtPanelPluginConfigDialog(settings, parent), @@ -64,18 +65,25 @@ void DesktopSwitchConfiguration::loadSettings() void DesktopSwitchConfiguration::loadDesktopsNames() { - int n = KX11Extras::numberOfDesktops(); + LXQtPanelApplication *a = reinterpret_cast(qApp); + auto wmBackend = a->getWMBackend(); + + int n = wmBackend->getWorkspacesCount(); for (int i = 1; i <= n; i++) { - QLineEdit *edit = new QLineEdit(KX11Extras::desktopName(i), this); + QLineEdit *edit = new QLineEdit(wmBackend->getWorkspaceName(i), this); ((QFormLayout *) ui->namesGroupBox->layout())->addRow(tr("Desktop %1:").arg(i), edit); + //TODO: on Wayland we cannot set desktop names in a standart way + // On KWin we could use DBus org.kde.KWin as done by kcm_kwin_virtualdesktops + edit->setReadOnly(true); + // C++11 rocks! - QTimer *timer = new QTimer(this); - timer->setInterval(400); - timer->setSingleShot(true); - connect(timer, &QTimer::timeout, this, [=] { KX11Extras::setDesktopName(i, edit->text()); }); - connect(edit, &QLineEdit::textEdited, this, [=] { timer->start(); }); + //QTimer *timer = new QTimer(this); + //timer->setInterval(400); + //timer->setSingleShot(true); + //connect(timer, &QTimer::timeout, this, [=] { KX11Extras::setDesktopName(i, edit->text()); }); + //connect(edit, &QLineEdit::textEdited, this, [=] { timer->start(); }); } } From e7f314011848bcf798a83fb4c41e8d637b5d3f03 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 17:41:16 +0100 Subject: [PATCH 035/165] TaskBar: consider initial windows --- plugin-taskbar/lxqttaskbar.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugin-taskbar/lxqttaskbar.cpp b/plugin-taskbar/lxqttaskbar.cpp index c306d796e..43090c396 100644 --- a/plugin-taskbar/lxqttaskbar.cpp +++ b/plugin-taskbar/lxqttaskbar.cpp @@ -107,6 +107,13 @@ LXQtTaskBar::LXQtTaskBar(ILXQtPanelPlugin *plugin, QWidget *parent) : connect(mBackend, &ILXQtTaskbarAbstractBackend::windowPropertyChanged, this, &LXQtTaskBar::onWindowChanged); connect(mBackend, &ILXQtTaskbarAbstractBackend::windowAdded, this, &LXQtTaskBar::onWindowAdded); connect(mBackend, &ILXQtTaskbarAbstractBackend::windowRemoved, this, &LXQtTaskBar::onWindowRemoved); + + // Consider already fetched windows + const auto initialWindows = mBackend->getCurrentWindows(); + for(WId windowId : initialWindows) + { + onWindowAdded(windowId); + } } /************************************************ From 757bbf161cc028dcd0650d9b85be812dc49a5dfe Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 17 Feb 2024 19:18:09 +0100 Subject: [PATCH 036/165] TaskBar: add experimental KWin Wayland backend NOTE: works only on KWin - Choose backend at runtime - Windows filter logic is re-evaluated on window property changes --- panel/CMakeLists.txt | 20 + panel/backends/CMakeLists.txt | 1 + panel/backends/wayland/CMakeLists.txt | 1 + .../wayland/lxqttaskbarbackendwayland.cpp | 626 +++++++++ .../wayland/lxqttaskbarbackendwayland.h | 87 ++ .../lxqttaskbarplasmawindowmanagment.cpp | 1146 +++++++++++++++++ .../lxqttaskbarplasmawindowmanagment.h | 154 +++ .../protocols/plasma-window-management.xml | 425 ++++++ panel/lxqtpanelapplication.cpp | 3 + 9 files changed, 2463 insertions(+) create mode 100644 panel/backends/wayland/CMakeLists.txt create mode 100644 panel/backends/wayland/lxqttaskbarbackendwayland.cpp create mode 100644 panel/backends/wayland/lxqttaskbarbackendwayland.h create mode 100644 panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp create mode 100644 panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h create mode 100644 panel/backends/wayland/protocols/plasma-window-management.xml diff --git a/panel/CMakeLists.txt b/panel/CMakeLists.txt index 79f46497b..6fb7c7608 100644 --- a/panel/CMakeLists.txt +++ b/panel/CMakeLists.txt @@ -3,6 +3,10 @@ set(PROJECT lxqt-panel) # TODO add_subdirectory(backends) +# TODO: move to backend folder +find_package(Qt6 ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS WaylandClient Concurrent) + + set(PRIV_HEADERS panelpluginsmodel.h windownotifier.h @@ -26,7 +30,11 @@ set(PRIV_HEADERS backends/lxqttaskbartypes.h backends/lxqttaskbardummybackend.h + backends/xcb/lxqttaskbarbackend_x11.h + + backends/wayland/lxqttaskbarbackendwayland.h + backends/wayland/lxqttaskbarplasmawindowmanagment.h ) # using LXQt namespace in the public headers. @@ -61,7 +69,11 @@ set(SOURCES backends/ilxqttaskbarabstractbackend.cpp backends/lxqttaskbardummybackend.cpp + backends/xcb/lxqttaskbarbackend_x11.cpp + + backends/wayland/lxqttaskbarbackendwayland.cpp + backends/wayland/lxqttaskbarplasmawindowmanagment.cpp ) set(UI @@ -116,10 +128,18 @@ add_executable(${PROJECT} ${UI} ) +# TODO: move to backend folder +qt6_generate_wayland_protocol_client_sources(${PROJECT} +FILES + ${CMAKE_CURRENT_SOURCE_DIR}/backends/wayland/protocols/plasma-window-management.xml +) + target_link_libraries(${PROJECT} ${LIBRARIES} ${QTX_LIBRARIES} KF6::WindowSystem + Qt6::WaylandClient # TODO: Move to backend folder + Qt6::Concurrent ${STATIC_PLUGINS} ) diff --git a/panel/backends/CMakeLists.txt b/panel/backends/CMakeLists.txt index 8f34a3c67..f1915b823 100644 --- a/panel/backends/CMakeLists.txt +++ b/panel/backends/CMakeLists.txt @@ -1 +1,2 @@ +add_subdirectory(wayland) add_subdirectory(xcb) diff --git a/panel/backends/wayland/CMakeLists.txt b/panel/backends/wayland/CMakeLists.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/panel/backends/wayland/CMakeLists.txt @@ -0,0 +1 @@ + diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp new file mode 100644 index 000000000..a2a415504 --- /dev/null +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -0,0 +1,626 @@ +#include "lxqttaskbarbackendwayland.h" + +#include "lxqttaskbarplasmawindowmanagment.h" + +#include +#include +#include + +auto findWindow(const std::vector>& windows, LXQtTaskBarPlasmaWindow *window) +{ + //TODO: use algorithms + auto end = windows.end(); + for(auto it = windows.begin(); it != end; it++) + { + if((*it).get() == window) + { + return it; + } + } + + return windows.end(); +} + +LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) : + ILXQtTaskbarAbstractBackend(parent) +{ + m_managment.reset(new LXQtTaskBarPlasmaWindowManagment); + + connect(m_managment.get(), &LXQtTaskBarPlasmaWindowManagment::windowCreated, this, [this](LXQtTaskBarPlasmaWindow *window) { + connect(window, &LXQtTaskBarPlasmaWindow::initialStateDone, this, [this, window] { + addWindow(window); + }); + }); + + // connect(m_managment.get(), &LXQtTaskBarPlasmaWindowManagment::stackingOrderChanged, + // this, [this](const QString &order) { + // // stackingOrder = order.split(QLatin1Char(';')); + // // for (const auto &window : std::as_const(windows)) { + // // this->dataChanged(window.get(), StackingOrder); + // // } + // }); +} + +bool LXQtTaskbarWaylandBackend::supportsAction(WId windowId, LXQtTaskBarBackendAction action) const +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + LXQtTaskBarPlasmaWindow::state state; + + switch (action) + { + case LXQtTaskBarBackendAction::Move: + state = LXQtTaskBarPlasmaWindow::state::state_movable; + break; + + case LXQtTaskBarBackendAction::Resize: + state = LXQtTaskBarPlasmaWindow::state::state_resizable; + break; + + case LXQtTaskBarBackendAction::Maximize: + state = LXQtTaskBarPlasmaWindow::state::state_maximizable; + break; + + case LXQtTaskBarBackendAction::Minimize: + state = LXQtTaskBarPlasmaWindow::state::state_minimizable; + break; + + case LXQtTaskBarBackendAction::RollUp: + state = LXQtTaskBarPlasmaWindow::state::state_shadeable; + break; + + case LXQtTaskBarBackendAction::FullScreen: + state = LXQtTaskBarPlasmaWindow::state::state_fullscreenable; + break; + + default: + return false; + } + + return window->windowState.testFlag(state); +} + +bool LXQtTaskbarWaylandBackend::reloadWindows() +{ + return false; //TODO +} + +QVector LXQtTaskbarWaylandBackend::getCurrentWindows() const +{ + QVector wids(windows.size()); + for(const auto& window : std::as_const(windows)) + { + wids << window->getWindowId(); + } + return wids; +} + +QString LXQtTaskbarWaylandBackend::getWindowTitle(WId windowId) const +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return QString(); + + return window->title; +} + +bool LXQtTaskbarWaylandBackend::applicationDemandsAttention(WId windowId) const +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + return window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_demands_attention) || transientsDemandingAttention.contains(window); +} + +QIcon LXQtTaskbarWaylandBackend::getApplicationIcon(WId windowId, int devicePixels) const +{ + Q_UNUSED(devicePixels) + + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return QIcon(); + + return window->icon; +} + +QString LXQtTaskbarWaylandBackend::getWindowClass(WId windowId) const +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return QString(); + return window->appId; +} + +LXQtTaskBarWindowLayer LXQtTaskbarWaylandBackend::getWindowLayer(WId windowId) const +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return LXQtTaskBarWindowLayer::Normal; + + if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_keep_above)) + return LXQtTaskBarWindowLayer::KeepAbove; + + if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_keep_below)) + return LXQtTaskBarWindowLayer::KeepBelow; + + return LXQtTaskBarWindowLayer::Normal; +} + +bool LXQtTaskbarWaylandBackend::setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + if(getWindowLayer(windowId) == layer) + return true; //TODO: make more efficient + + LXQtTaskBarPlasmaWindow::state plasmaState = LXQtTaskBarPlasmaWindow::state::state_keep_above; + switch (layer) + { + case LXQtTaskBarWindowLayer::Normal: + case LXQtTaskBarWindowLayer::KeepAbove: + break; + case LXQtTaskBarWindowLayer::KeepBelow: + plasmaState = LXQtTaskBarPlasmaWindow::state::state_keep_below; + break; + default: + return false; + } + + window->set_state(plasmaState, layer == LXQtTaskBarWindowLayer::Normal ? 0 : plasmaState); + return false; +} + +LXQtTaskBarWindowState LXQtTaskbarWaylandBackend::getWindowState(WId windowId) const +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return LXQtTaskBarWindowState::Normal; + + if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_minimized)) + return LXQtTaskBarWindowState::Hidden; + if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_maximizable)) + return LXQtTaskBarWindowState::Maximized; + if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_shaded)) + return LXQtTaskBarWindowState::RolledUp; + if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_fullscreen)) + return LXQtTaskBarWindowState::FullScreen; + + return LXQtTaskBarWindowState::Normal; +} + +bool LXQtTaskbarWaylandBackend::setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set) +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + LXQtTaskBarPlasmaWindow::state plasmaState; + switch (state) + { + case LXQtTaskBarWindowState::Minimized: + { + plasmaState = LXQtTaskBarPlasmaWindow::state::state_minimized; + break; + } + case LXQtTaskBarWindowState::Maximized: + case LXQtTaskBarWindowState::MaximizedVertically: + case LXQtTaskBarWindowState::MaximizedHorizontally: + { + plasmaState = LXQtTaskBarPlasmaWindow::state::state_maximized; + break; + } + case LXQtTaskBarWindowState::Normal: + { + plasmaState = LXQtTaskBarPlasmaWindow::state::state_maximized; + set = !set; //TODO: correct + break; + } + case LXQtTaskBarWindowState::RolledUp: + { + plasmaState = LXQtTaskBarPlasmaWindow::state::state_shaded; + break; + } + default: + return false; + } + + window->set_state(plasmaState, set ? plasmaState : 0); + return true; +} + +bool LXQtTaskbarWaylandBackend::isWindowActive(WId windowId) const +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + return activeWindow == window || window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_active); +} + +bool LXQtTaskbarWaylandBackend::raiseWindow(WId windowId, bool onCurrentWorkSpace) +{ + Q_UNUSED(onCurrentWorkSpace) //TODO + + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + // Pull forward any transient demanding attention. + if (auto *transientDemandingAttention = transientsDemandingAttention.value(window)) + { + window = transientDemandingAttention; + } + else + { + // TODO Shouldn't KWin take care of that? + // Bringing a transient to the front usually brings its parent with it + // but focus is not handled properly. + // TODO take into account d->lastActivation instead + // of just taking the first one. + while (transients.key(window)) + { + window = transients.key(window); + } + } + + window->set_state(LXQtTaskBarPlasmaWindow::state::state_active, LXQtTaskBarPlasmaWindow::state::state_active); + return true; +} + +bool LXQtTaskbarWaylandBackend::closeWindow(WId windowId) +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + window->close(); + return true; +} + +WId LXQtTaskbarWaylandBackend::getActiveWindow() const +{ + if(activeWindow) + return activeWindow->getWindowId(); + return 0; +} + +int LXQtTaskbarWaylandBackend::getWorkspacesCount() const +{ + return 1; //TODO +} + +QString LXQtTaskbarWaylandBackend::getWorkspaceName(int idx) const +{ + return QStringLiteral("TestWorkspace"); +} + +int LXQtTaskbarWaylandBackend::getCurrentWorkspace() const +{ + return 0; //TODO +} + +bool LXQtTaskbarWaylandBackend::setCurrentWorkspace(int idx) +{ + Q_UNUSED(idx) + return false; //TODO +} + +int LXQtTaskbarWaylandBackend::getWindowWorkspace(WId windowId) const +{ + Q_UNUSED(windowId) + return 0; //TODO +} + +bool LXQtTaskbarWaylandBackend::setWindowOnWorkspace(WId windowId, int idx) +{ + Q_UNUSED(windowId) + Q_UNUSED(idx) + return false; //TODO +} + +void LXQtTaskbarWaylandBackend::moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) +{ + +} + +bool LXQtTaskbarWaylandBackend::isWindowOnScreen(QScreen *screen, WId windowId) const +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + return screen->geometry().intersects(window->geometry); +} + +void LXQtTaskbarWaylandBackend::moveApplication(WId windowId) +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return; + + window->set_state(LXQtTaskBarPlasmaWindow::state::state_active, LXQtTaskBarPlasmaWindow::state::state_active); + window->request_move(); +} + +void LXQtTaskbarWaylandBackend::resizeApplication(WId windowId) +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return; + + window->set_state(LXQtTaskBarPlasmaWindow::state::state_active, LXQtTaskBarPlasmaWindow::state::state_active); + window->request_resize(); +} + +void LXQtTaskbarWaylandBackend::refreshIconGeometry(WId windowId, const QRect &geom) +{ + +} + +bool LXQtTaskbarWaylandBackend::isAreaOverlapped(const QRect &area) const +{ + for(auto &window : std::as_const(windows)) + { + if(window->geometry.intersects(area)) + return true; + } + return false; +} + +void LXQtTaskbarWaylandBackend::addWindow(LXQtTaskBarPlasmaWindow *window) +{ + if (findWindow(windows, window) != windows.end() || transients.contains(window)) + { + return; + } + + auto removeWindow = [window, this] + { + auto it = findWindow(windows, window); + if (it != windows.end()) + { + if(window->acceptedInTaskBar) + emit windowRemoved(window->getWindowId()); + windows.erase(it); + transientsDemandingAttention.remove(window); + lastActivated.remove(window); + } + else + { + // Could be a transient. + // Removing a transient might change the demands attention state of the leader. + if (transients.remove(window)) + { + if (LXQtTaskBarPlasmaWindow *leader = transientsDemandingAttention.key(window)) { + transientsDemandingAttention.remove(leader, window); + emit windowPropertyChanged(leader->getWindowId(), int(LXQtTaskBarWindowProperty::Urgency)); + } + } + } + + if (activeWindow == window) + { + activeWindow = nullptr; + emit activeWindowChanged(0); + } + }; + + connect(window, &LXQtTaskBarPlasmaWindow::unmapped, this, removeWindow); + + connect(window, &LXQtTaskBarPlasmaWindow::titleChanged, this, [window, this] { + updateWindowAcceptance(window); + if(window->acceptedInTaskBar) + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Title)); + }); + + connect(window, &LXQtTaskBarPlasmaWindow::iconChanged, this, [window, this] { + updateWindowAcceptance(window); + if(window->acceptedInTaskBar) + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Icon)); + }); + + connect(window, &LXQtTaskBarPlasmaWindow::geometryChanged, this, [window, this] { + updateWindowAcceptance(window); + if(window->acceptedInTaskBar) + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Geometry)); + }); + + connect(window, &LXQtTaskBarPlasmaWindow::appIdChanged, this, [window, this] { + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::WindowClass)); + }); + + if (window->windowState & LXQtTaskBarPlasmaWindow::state::state_active) { + LXQtTaskBarPlasmaWindow *effectiveActive = window; + while (effectiveActive->parentWindow) { + effectiveActive = effectiveActive->parentWindow; + } + + lastActivated[effectiveActive] = QTime::currentTime(); + activeWindow = effectiveActive; + } + + connect(window, &LXQtTaskBarPlasmaWindow::activeChanged, this, [window, this] { + const bool active = window->windowState & LXQtTaskBarPlasmaWindow::state::state_active; + + LXQtTaskBarPlasmaWindow *effectiveWindow = window; + + while (effectiveWindow->parentWindow) + { + effectiveWindow = effectiveWindow->parentWindow; + } + + if (active) + { + lastActivated[effectiveWindow] = QTime::currentTime(); + + if (activeWindow != effectiveWindow) + { + activeWindow = effectiveWindow; + emit activeWindowChanged(activeWindow->getWindowId()); + } + } + else + { + if (activeWindow == effectiveWindow) + { + activeWindow = nullptr; + emit activeWindowChanged(0); + } + } + }); + + connect(window, &LXQtTaskBarPlasmaWindow::parentWindowChanged, this, [window, this] { + LXQtTaskBarPlasmaWindow *leader = window->parentWindow.data(); + + // Migrate demanding attention to new leader. + if (window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_demands_attention)) + { + if (auto *oldLeader = transientsDemandingAttention.key(window)) + { + if (window->parentWindow != oldLeader) + { + transientsDemandingAttention.remove(oldLeader, window); + transientsDemandingAttention.insert(leader, window); + emit windowPropertyChanged(oldLeader->getWindowId(), int(LXQtTaskBarWindowProperty::Urgency)); + emit windowPropertyChanged(leader->getWindowId(), int(LXQtTaskBarWindowProperty::Urgency)); + } + } + } + + if (transients.remove(window)) + { + if (leader) + { + // leader change. + transients.insert(window, leader); + } + else + { + // lost a leader, add to regular windows list. + Q_ASSERT(findWindow(windows, window) == windows.end()); + + windows.emplace_back(window); + } + } + else if (leader) + { + // gained a leader, remove from regular windows list. + auto it = findWindow(windows, window); + Q_ASSERT(it != windows.end()); + + windows.erase(it); + lastActivated.remove(window); + } + }); + + auto stateChanged = [window, this] { + updateWindowAcceptance(window); + if(window->acceptedInTaskBar) + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::State)); + }; + + connect(window, &LXQtTaskBarPlasmaWindow::fullscreenChanged, this, stateChanged); + + connect(window, &LXQtTaskBarPlasmaWindow::maximizedChanged, this, stateChanged); + + connect(window, &LXQtTaskBarPlasmaWindow::minimizedChanged, this, stateChanged); + + connect(window, &LXQtTaskBarPlasmaWindow::shadedChanged, this, stateChanged); + + auto workspaceChanged = [window, this] { + updateWindowAcceptance(window); + if(window->acceptedInTaskBar) + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Workspace)); + }; + + connect(window, &LXQtTaskBarPlasmaWindow::virtualDesktopEntered, this, workspaceChanged); + connect(window, &LXQtTaskBarPlasmaWindow::virtualDesktopLeft, this, workspaceChanged); + + connect(window, &LXQtTaskBarPlasmaWindow::demandsAttentionChanged, this, [window, this] { + // Changes to a transient's state might change demands attention state for leader. + if (auto *leader = transients.value(window)) + { + if (window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_demands_attention)) + { + if (!transientsDemandingAttention.values(leader).contains(window)) + { + transientsDemandingAttention.insert(leader, window); + emit windowPropertyChanged(leader->getWindowId(), int(LXQtTaskBarWindowProperty::Urgency)); + } + } + else if (transientsDemandingAttention.remove(window)) + { + emit windowPropertyChanged(leader->getWindowId(), int(LXQtTaskBarWindowProperty::Urgency)); + } + } + else + { + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Urgency)); + } + }); + + connect(window, &LXQtTaskBarPlasmaWindow::skipTaskbarChanged, this, [window, this] { + updateWindowAcceptance(window); + }); + + // QObject::connect(window, &PlasmaWindow::applicationMenuChanged, q, [window, this] { + // this->dataChanged(window, QList{ApplicationMenuServiceName, ApplicationMenuObjectPath}); + // }); + + // Handle transient. + if (LXQtTaskBarPlasmaWindow *leader = window->parentWindow.data()) + { + transients.insert(window, leader); + + // Update demands attention state for leader. + if (window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_demands_attention)) + { + transientsDemandingAttention.insert(leader, window); + if(leader->acceptedInTaskBar) + emit windowPropertyChanged(leader->getWindowId(), int(LXQtTaskBarWindowProperty::Urgency)); + } + } + else + { + windows.emplace_back(window); + updateWindowAcceptance(window); + } +} + +bool LXQtTaskbarWaylandBackend::acceptWindow(LXQtTaskBarPlasmaWindow *window) const +{ + if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_skiptaskbar)) + return false; + + return true; +} + +void LXQtTaskbarWaylandBackend::updateWindowAcceptance(LXQtTaskBarPlasmaWindow *window) +{ + if(!window->acceptedInTaskBar && acceptWindow(window)) + { + window->acceptedInTaskBar = true; + emit windowAdded(window->getWindowId()); + } + else if(window->acceptedInTaskBar && !acceptWindow(window)) + { + window->acceptedInTaskBar = false; + emit windowRemoved(window->getWindowId()); + } +} + +LXQtTaskBarPlasmaWindow *LXQtTaskbarWaylandBackend::getWindow(WId windowId) const +{ + for(auto &window : std::as_const(windows)) + { + if(window->getWindowId() == windowId) + return window.get(); + } + + return nullptr; +} diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.h b/panel/backends/wayland/lxqttaskbarbackendwayland.h new file mode 100644 index 000000000..553a7e585 --- /dev/null +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.h @@ -0,0 +1,87 @@ +#ifndef LXQTTASKBARBACKENDWAYLAND_H +#define LXQTTASKBARBACKENDWAYLAND_H + +#include "../ilxqttaskbarabstractbackend.h" + +#include +#include + +class LXQtTaskBarPlasmaWindow; +class LXQtTaskBarPlasmaWindowManagment; + + +class LXQtTaskbarWaylandBackend : public ILXQtTaskbarAbstractBackend +{ + Q_OBJECT + +public: + explicit LXQtTaskbarWaylandBackend(QObject *parent = nullptr); + + // Backend + virtual bool supportsAction(WId windowId, LXQtTaskBarBackendAction action) const override; + + // Windows + virtual bool reloadWindows() override; + + virtual QVector getCurrentWindows() const override; + virtual QString getWindowTitle(WId windowId) const override; + virtual bool applicationDemandsAttention(WId windowId) const override; + virtual QIcon getApplicationIcon(WId windowId, int devicePixels) const override; + virtual QString getWindowClass(WId windowId) const override; + + virtual LXQtTaskBarWindowLayer getWindowLayer(WId windowId) const override; + virtual bool setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) override; + + virtual LXQtTaskBarWindowState getWindowState(WId windowId) const override; + virtual bool setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set) override; + + virtual bool isWindowActive(WId windowId) const override; + virtual bool raiseWindow(WId windowId, bool onCurrentWorkSpace) override; + + virtual bool closeWindow(WId windowId) override; + + virtual WId getActiveWindow() const override; + + // Workspaces + virtual int getWorkspacesCount() const override; + virtual QString getWorkspaceName(int idx) const override; + + virtual int getCurrentWorkspace() const override; + virtual bool setCurrentWorkspace(int idx) override; + + virtual int getWindowWorkspace(WId windowId) const override; + virtual bool setWindowOnWorkspace(WId windowId, int idx) override; + + virtual void moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) override; + + virtual bool isWindowOnScreen(QScreen *screen, WId windowId) const override; + + // X11 Specific + virtual void moveApplication(WId windowId) override; + virtual void resizeApplication(WId windowId) override; + + virtual void refreshIconGeometry(WId windowId, const QRect &geom) override; + + // Panel internal + virtual bool isAreaOverlapped(const QRect& area) const override; + +private: + void addWindow(LXQtTaskBarPlasmaWindow *window); + bool acceptWindow(LXQtTaskBarPlasmaWindow *window) const; + void updateWindowAcceptance(LXQtTaskBarPlasmaWindow *window); + +private: + LXQtTaskBarPlasmaWindow *getWindow(WId windowId) const; + + std::unique_ptr m_managment; + + QHash lastActivated; + LXQtTaskBarPlasmaWindow *activeWindow = nullptr; + std::vector> windows; + // key=transient child, value=leader + QHash transients; + // key=leader, values=transient children + QMultiHash transientsDemandingAttention; +}; + +#endif // LXQTTASKBARBACKENDWAYLAND_H diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp new file mode 100644 index 000000000..0a282eadd --- /dev/null +++ b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp @@ -0,0 +1,1146 @@ +#include "lxqttaskbarplasmawindowmanagment.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +// QUuid WaylandTasksModel::Private::uuid = QUuid::createUuid(); + +// WaylandTasksModel::Private::Private(WaylandTasksModel *q) +// : q(q) +// { +// } + +// void WaylandTasksModel::Private::init() +// { +// auto clearCacheAndRefresh = [this] { +// if (windows.empty()) { +// return; +// } + +// appDataCache.clear(); + +// // Emit changes of all roles satisfied from app data cache. +// Q_EMIT q->dataChanged(q->index(0, 0), +// q->index(windows.size() - 1, 0), +// QList{Qt::DecorationRole, +// AbstractTasksModel::AppId, +// AbstractTasksModel::AppName, +// AbstractTasksModel::GenericName, +// AbstractTasksModel::LauncherUrl, +// AbstractTasksModel::LauncherUrlWithoutIcon, +// AbstractTasksModel::CanLaunchNewInstance, +// AbstractTasksModel::SkipTaskbar}); +// }; + +// rulesConfig = KSharedConfig::openConfig(QStringLiteral("taskmanagerrulesrc")); +// configWatcher = new KDirWatch(q); + +// for (const QString &location : QStandardPaths::standardLocations(QStandardPaths::ConfigLocation)) { +// configWatcher->addFile(location + QLatin1String("/taskmanagerrulesrc")); +// } + +// auto rulesConfigChange = [this, clearCacheAndRefresh] { +// rulesConfig->reparseConfiguration(); +// clearCacheAndRefresh(); +// }; + +// QObject::connect(configWatcher, &KDirWatch::dirty, rulesConfigChange); +// QObject::connect(configWatcher, &KDirWatch::created, rulesConfigChange); +// QObject::connect(configWatcher, &KDirWatch::deleted, rulesConfigChange); + +// virtualDesktopInfo = new VirtualDesktopInfo(q); + +// initWayland(); +// } + +// void WaylandTasksModel::Private::initWayland() +// { +// if (!KWindowSystem::isPlatformWayland()) { +// return; +// } + +// windowManagement = std::make_unique(); + +// QObject::connect(windowManagement.get(), &PlasmaWindowManagement::activeChanged, q, [this] { +// q->beginResetModel(); +// windows.clear(); +// q->endResetModel(); +// }); + +// QObject::connect(windowManagement.get(), &PlasmaWindowManagement::windowCreated, q, [this](PlasmaWindow *window) { +// connect(window, &PlasmaWindow::initialStateDone, q, [this, window] { +// addWindow(window); +// }); +// }); + +// QObject::connect(windowManagement.get(), &PlasmaWindowManagement::stackingOrderChanged, q, [this](const QString &order) { +// stackingOrder = order.split(QLatin1Char(';')); +// for (const auto &window : std::as_const(windows)) { +// this->dataChanged(window.get(), StackingOrder); +// } +// }); +// } + +// auto WaylandTasksModel::Private::findWindow(PlasmaWindow *window) const +// { +// return std::find_if(windows.begin(), windows.end(), [window](const std::unique_ptr &candidate) { +// return candidate.get() == window; +// }); +// } + +// void WaylandTasksModel::Private::addWindow(PlasmaWindow *window) +// { +// if (findWindow(window) != windows.end() || transients.contains(window)) { +// return; +// } + +// auto removeWindow = [window, this] { +// auto it = findWindow(window); +// if (it != windows.end()) { +// const int row = it - windows.begin(); +// q->beginRemoveRows(QModelIndex(), row, row); +// windows.erase(it); +// transientsDemandingAttention.remove(window); +// appDataCache.remove(window); +// lastActivated.remove(window); +// q->endRemoveRows(); +// } else { // Could be a transient. +// // Removing a transient might change the demands attention state of the leader. +// if (transients.remove(window)) { +// if (PlasmaWindow *leader = transientsDemandingAttention.key(window)) { +// transientsDemandingAttention.remove(leader, window); +// dataChanged(leader, QVector{IsDemandingAttention}); +// } +// } +// } + +// if (activeWindow == window) { +// activeWindow = nullptr; +// } +// }; + +// QObject::connect(window, &PlasmaWindow::unmapped, q, removeWindow); + +// QObject::connect(window, &PlasmaWindow::titleChanged, q, [window, this] { +// this->dataChanged(window, Qt::DisplayRole); +// }); + +// QObject::connect(window, &PlasmaWindow::iconChanged, q, [window, this] { +// // The icon in the AppData struct might come from PlasmaWindow if it wasn't +// // filled in by windowUrlFromMetadata+appDataFromUrl. +// // TODO: Don't evict the cache unnecessarily if this isn't the case. As icons +// // are currently very static on Wayland, this eviction is unlikely to happen +// // frequently as of now. +// appDataCache.remove(window); +// this->dataChanged(window, Qt::DecorationRole); +// }); + +// QObject::connect(window, &PlasmaWindow::appIdChanged, q, [window, this] { +// // The AppData struct in the cache is derived from this and needs +// // to be evicted in favor of a fresh struct based on the changed +// // window metadata. +// appDataCache.remove(window); + +// // Refresh roles satisfied from the app data cache. +// this->dataChanged(window, +// QList{Qt::DecorationRole, AppId, AppName, GenericName, LauncherUrl, LauncherUrlWithoutIcon, SkipTaskbar, CanLaunchNewInstance}); +// }); + +// if (window->windowState & PlasmaWindow::state::state_active) { +// PlasmaWindow *effectiveActive = window; +// while (effectiveActive->parentWindow) { +// effectiveActive = effectiveActive->parentWindow; +// } + +// lastActivated[effectiveActive] = QTime::currentTime(); +// activeWindow = effectiveActive; +// } + +// QObject::connect(window, &PlasmaWindow::activeChanged, q, [window, this] { +// const bool active = window->windowState & PlasmaWindow::state::state_active; + +// PlasmaWindow *effectiveWindow = window; + +// while (effectiveWindow->parentWindow) { +// effectiveWindow = effectiveWindow->parentWindow; +// } + +// if (active) { +// lastActivated[effectiveWindow] = QTime::currentTime(); + +// if (activeWindow != effectiveWindow) { +// activeWindow = effectiveWindow; +// this->dataChanged(effectiveWindow, IsActive); +// } +// } else { +// if (activeWindow == effectiveWindow) { +// activeWindow = nullptr; +// this->dataChanged(effectiveWindow, IsActive); +// } +// } +// }); + +// QObject::connect(window, &PlasmaWindow::parentWindowChanged, q, [window, this] { +// PlasmaWindow *leader = window->parentWindow.data(); + +// // Migrate demanding attention to new leader. +// if (window->windowState.testFlag(PlasmaWindow::state::state_demands_attention)) { +// if (auto *oldLeader = transientsDemandingAttention.key(window)) { +// if (window->parentWindow != oldLeader) { +// transientsDemandingAttention.remove(oldLeader, window); +// transientsDemandingAttention.insert(leader, window); +// dataChanged(oldLeader, QVector{IsDemandingAttention}); +// dataChanged(leader, QVector{IsDemandingAttention}); +// } +// } +// } + +// if (transients.remove(window)) { +// if (leader) { // leader change. +// transients.insert(window, leader); +// } else { // lost a leader, add to regular windows list. +// Q_ASSERT(findWindow(window) == windows.end()); + +// const int count = windows.size(); +// q->beginInsertRows(QModelIndex(), count, count); +// windows.emplace_back(window); +// q->endInsertRows(); +// } +// } else if (leader) { // gained a leader, remove from regular windows list. +// auto it = findWindow(window); +// Q_ASSERT(it != windows.end()); + +// const int row = it - windows.begin(); +// q->beginRemoveRows(QModelIndex(), row, row); +// windows.erase(it); +// appDataCache.remove(window); +// lastActivated.remove(window); +// q->endRemoveRows(); +// } +// }); + +// QObject::connect(window, &PlasmaWindow::closeableChanged, q, [window, this] { +// this->dataChanged(window, IsClosable); +// }); + +// QObject::connect(window, &PlasmaWindow::movableChanged, q, [window, this] { +// this->dataChanged(window, IsMovable); +// }); + +// QObject::connect(window, &PlasmaWindow::resizableChanged, q, [window, this] { +// this->dataChanged(window, IsResizable); +// }); + +// QObject::connect(window, &PlasmaWindow::fullscreenableChanged, q, [window, this] { +// this->dataChanged(window, IsFullScreenable); +// }); + +// QObject::connect(window, &PlasmaWindow::fullscreenChanged, q, [window, this] { +// this->dataChanged(window, IsFullScreen); +// }); + +// QObject::connect(window, &PlasmaWindow::maximizeableChanged, q, [window, this] { +// this->dataChanged(window, IsMaximizable); +// }); + +// QObject::connect(window, &PlasmaWindow::maximizedChanged, q, [window, this] { +// this->dataChanged(window, IsMaximized); +// }); + +// QObject::connect(window, &PlasmaWindow::minimizeableChanged, q, [window, this] { +// this->dataChanged(window, IsMinimizable); +// }); + +// QObject::connect(window, &PlasmaWindow::minimizedChanged, q, [window, this] { +// this->dataChanged(window, IsMinimized); +// }); + +// QObject::connect(window, &PlasmaWindow::keepAboveChanged, q, [window, this] { +// this->dataChanged(window, IsKeepAbove); +// }); + +// QObject::connect(window, &PlasmaWindow::keepBelowChanged, q, [window, this] { +// this->dataChanged(window, IsKeepBelow); +// }); + +// QObject::connect(window, &PlasmaWindow::shadeableChanged, q, [window, this] { +// this->dataChanged(window, IsShadeable); +// }); + +// QObject::connect(window, &PlasmaWindow::virtualDesktopChangeableChanged, q, [window, this] { +// this->dataChanged(window, IsVirtualDesktopsChangeable); +// }); + +// QObject::connect(window, &PlasmaWindow::virtualDesktopEntered, q, [window, this] { +// this->dataChanged(window, VirtualDesktops); + +// // If the count has changed from 0, the window may no longer be on all virtual +// // desktops. +// if (window->virtualDesktops.count() > 0) { +// this->dataChanged(window, IsOnAllVirtualDesktops); +// } +// }); + +// QObject::connect(window, &PlasmaWindow::virtualDesktopLeft, q, [window, this] { +// this->dataChanged(window, VirtualDesktops); + +// // If the count has changed to 0, the window is now on all virtual desktops. +// if (window->virtualDesktops.count() == 0) { +// this->dataChanged(window, IsOnAllVirtualDesktops); +// } +// }); + +// QObject::connect(window, &PlasmaWindow::geometryChanged, q, [window, this] { +// this->dataChanged(window, QList{Geometry, ScreenGeometry}); +// }); + +// QObject::connect(window, &PlasmaWindow::demandsAttentionChanged, q, [window, this] { +// // Changes to a transient's state might change demands attention state for leader. +// if (auto *leader = transients.value(window)) { +// if (window->windowState.testFlag(PlasmaWindow::state::state_demands_attention)) { +// if (!transientsDemandingAttention.values(leader).contains(window)) { +// transientsDemandingAttention.insert(leader, window); +// this->dataChanged(leader, QVector{IsDemandingAttention}); +// } +// } else if (transientsDemandingAttention.remove(window)) { +// this->dataChanged(leader, QVector{IsDemandingAttention}); +// } +// } else { +// this->dataChanged(window, QVector{IsDemandingAttention}); +// } +// }); + +// QObject::connect(window, &PlasmaWindow::skipTaskbarChanged, q, [window, this] { +// this->dataChanged(window, SkipTaskbar); +// }); + +// QObject::connect(window, &PlasmaWindow::applicationMenuChanged, q, [window, this] { +// this->dataChanged(window, QList{ApplicationMenuServiceName, ApplicationMenuObjectPath}); +// }); + +// QObject::connect(window, &PlasmaWindow::activitiesChanged, q, [window, this] { +// this->dataChanged(window, Activities); +// }); + +// // Handle transient. +// if (PlasmaWindow *leader = window->parentWindow.data()) { +// transients.insert(window, leader); + +// // Update demands attention state for leader. +// if (window->windowState.testFlag(PlasmaWindow::state::state_demands_attention)) { +// transientsDemandingAttention.insert(leader, window); +// dataChanged(leader, QVector{IsDemandingAttention}); +// } +// } else { +// const int count = windows.size(); + +// q->beginInsertRows(QModelIndex(), count, count); + +// windows.emplace_back(window); + +// q->endInsertRows(); +// } +// } + +// const AppData &WaylandTasksModel::Private::appData(PlasmaWindow *window) +// { +// static_assert(!std::is_trivially_copy_assignable_v); +// if (auto it = appDataCache.constFind(window); it != appDataCache.constEnd()) { +// return *it; +// } + +// return *appDataCache.emplace(window, appDataFromUrl(windowUrlFromMetadata(window->appId, window->pid, rulesConfig, window->resourceName))); +// } + +// QIcon WaylandTasksModel::Private::icon(PlasmaWindow *window) +// { +// const AppData &app = appData(window); + +// if (!app.icon.isNull()) { +// return app.icon; +// } + +// appDataCache[window].icon = window->icon; + +// return window->icon; +// } + +// QString WaylandTasksModel::Private::mimeType() +// { +// // Use a unique format id to make this intentionally useless for +// // cross-process DND. +// return QStringLiteral("windowsystem/winid+") + uuid.toString(); +// } + +// QString WaylandTasksModel::Private::groupMimeType() +// { +// // Use a unique format id to make this intentionally useless for +// // cross-process DND. +// return QStringLiteral("windowsystem/multiple-winids+") + uuid.toString(); +// } + +// void WaylandTasksModel::Private::dataChanged(PlasmaWindow *window, int role) +// { +// auto it = findWindow(window); +// if (it == windows.end()) { +// return; +// } +// QModelIndex idx = q->index(it - windows.begin()); +// Q_EMIT q->dataChanged(idx, idx, QList{role}); +// } + +// void WaylandTasksModel::Private::dataChanged(PlasmaWindow *window, const QList &roles) +// { +// auto it = findWindow(window); +// if (it == windows.end()) { +// return; +// } +// QModelIndex idx = q->index(it - windows.begin()); +// Q_EMIT q->dataChanged(idx, idx, roles); +// } + +// WaylandTasksModel::WaylandTasksModel(QObject *parent) +// : AbstractWindowTasksModel(parent) +// , d(new Private(this)) +// { +// d->init(); +// } + +// WaylandTasksModel::~WaylandTasksModel() = default; + +// QVariant WaylandTasksModel::data(const QModelIndex &index, int role) const +// { +// // Note: when index is valid, its row >= 0, so casting to unsigned is safe +// if (!index.isValid() || static_cast(index.row()) >= d->windows.size()) { +// return QVariant(); +// } + +// PlasmaWindow *window = d->windows.at(index.row()).get(); + +// if (role == Qt::DisplayRole) { +// return window->title; +// } else if (role == Qt::DecorationRole) { +// return d->icon(window); +// } else if (role == AppId) { +// const QString &id = d->appData(window).id; + +// if (id.isEmpty()) { +// return window->appId; +// } else { +// return id; +// } +// } else if (role == AppName) { +// return d->appData(window).name; +// } else if (role == GenericName) { +// return d->appData(window).genericName; +// } else if (role == LauncherUrl || role == LauncherUrlWithoutIcon) { +// return d->appData(window).url; +// } else if (role == WinIdList) { +// return QVariantList{window->uuid}; +// } else if (role == MimeType) { +// return d->mimeType(); +// } else if (role == MimeData) { +// return window->uuid; +// } else if (role == IsWindow) { +// return true; +// } else if (role == IsActive) { +// return (window == d->activeWindow); +// } else if (role == IsClosable) { +// return window->windowState.testFlag(PlasmaWindow::state::state_closeable); +// } else if (role == IsMovable) { +// return window->windowState.testFlag(PlasmaWindow::state::state_movable); +// } else if (role == IsResizable) { +// return window->windowState.testFlag(PlasmaWindow::state::state_resizable); +// } else if (role == IsMaximizable) { +// return window->windowState.testFlag(PlasmaWindow::state::state_maximizable); +// } else if (role == IsMaximized) { +// return window->windowState.testFlag(PlasmaWindow::state::state_maximized); +// } else if (role == IsMinimizable) { +// return window->windowState.testFlag(PlasmaWindow::state::state_minimizable); +// } else if (role == IsMinimized || role == IsHidden) { +// return window->windowState.testFlag(PlasmaWindow::state::state_minimized); +// } else if (role == IsKeepAbove) { +// return window->windowState.testFlag(PlasmaWindow::state::state_keep_above); +// } else if (role == IsKeepBelow) { +// return window->windowState.testFlag(PlasmaWindow::state::state_keep_below); +// } else if (role == IsFullScreenable) { +// return window->windowState.testFlag(PlasmaWindow::state::state_fullscreenable); +// } else if (role == IsFullScreen) { +// return window->windowState.testFlag(PlasmaWindow::state::state_fullscreen); +// } else if (role == IsShadeable) { +// return window->windowState.testFlag(PlasmaWindow::state::state_shadeable); +// } else if (role == IsShaded) { +// return window->windowState.testFlag(PlasmaWindow::state::state_shaded); +// } else if (role == IsVirtualDesktopsChangeable) { +// return window->windowState.testFlag(PlasmaWindow::state::state_virtual_desktop_changeable); +// } else if (role == VirtualDesktops) { +// return window->virtualDesktops; +// } else if (role == IsOnAllVirtualDesktops) { +// return window->virtualDesktops.isEmpty(); +// } else if (role == Geometry) { +// return window->geometry; +// } else if (role == ScreenGeometry) { +// return screenGeometry(window->geometry.center()); +// } else if (role == Activities) { +// return window->activities; +// } else if (role == IsDemandingAttention) { +// return window->windowState.testFlag(PlasmaWindow::state::state_demands_attention) || d->transientsDemandingAttention.contains(window); +// } else if (role == SkipTaskbar) { +// return window->windowState.testFlag(PlasmaWindow::state::state_skiptaskbar) || d->appData(window).skipTaskbar; +// } else if (role == SkipPager) { +// // FIXME Implement. +// } else if (role == AppPid) { +// return window->pid; +// } else if (role == StackingOrder) { +// return d->stackingOrder.indexOf(window->uuid); +// } else if (role == LastActivated) { +// if (d->lastActivated.contains(window)) { +// return d->lastActivated.value(window); +// } +// } else if (role == ApplicationMenuObjectPath) { +// return window->applicationMenuObjectPath; +// } else if (role == ApplicationMenuServiceName) { +// return window->applicationMenuService; +// } else if (role == CanLaunchNewInstance) { +// return canLauchNewInstance(d->appData(window)); +// } + +// return AbstractTasksModel::data(index, role); +// } + +// int WaylandTasksModel::rowCount(const QModelIndex &parent) const +// { +// return parent.isValid() ? 0 : d->windows.size(); +// } + +// QModelIndex WaylandTasksModel::index(int row, int column, const QModelIndex &parent) const +// { +// return hasIndex(row, column, parent) ? createIndex(row, column, d->windows.at(row).get()) : QModelIndex(); +// } + +// void WaylandTasksModel::requestActivate(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// PlasmaWindow *window = d->windows.at(index.row()).get(); + +// // Pull forward any transient demanding attention. +// if (auto *transientDemandingAttention = d->transientsDemandingAttention.value(window)) { +// window = transientDemandingAttention; +// } else { +// // TODO Shouldn't KWin take care of that? +// // Bringing a transient to the front usually brings its parent with it +// // but focus is not handled properly. +// // TODO take into account d->lastActivation instead +// // of just taking the first one. +// while (d->transients.key(window)) { +// window = d->transients.key(window); +// } +// } + +// window->set_state(PlasmaWindow::state::state_active, PlasmaWindow::state::state_active); +// } + +// void WaylandTasksModel::requestNewInstance(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// runApp(d->appData(d->windows.at(index.row()).get())); +// } + +// void WaylandTasksModel::requestOpenUrls(const QModelIndex &index, const QList &urls) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent) || urls.isEmpty()) { +// return; +// } + +// runApp(d->appData(d->windows.at(index.row()).get()), urls); +// } + +// void WaylandTasksModel::requestClose(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// d->windows.at(index.row())->close(); +// } + +// void WaylandTasksModel::requestMove(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); + +// window->set_state(PlasmaWindow::state::state_active, PlasmaWindow::state::state_active); +// window->request_move(); +// } + +// void WaylandTasksModel::requestResize(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); + +// window->set_state(PlasmaWindow::state::state_active, PlasmaWindow::state::state_active); +// window->request_resize(); +// } + +// void WaylandTasksModel::requestToggleMinimized(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); + +// if (window->windowState & PlasmaWindow::state::state_minimized) { +// window->set_state(PlasmaWindow::state::state_minimized, 0); +// } else { +// window->set_state(PlasmaWindow::state::state_minimized, PlasmaWindow::state::state_minimized); +// } +// } + +// void WaylandTasksModel::requestToggleMaximized(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); + +// if (window->windowState & PlasmaWindow::state::state_maximized) { +// window->set_state(PlasmaWindow::state::state_maximized | PlasmaWindow::state::state_active, PlasmaWindow::state::state_active); +// } else { +// window->set_state(PlasmaWindow::state::state_maximized | PlasmaWindow::state::state_active, +// PlasmaWindow::state::state_maximized | PlasmaWindow::state::state_active); +// } +// } + +// void WaylandTasksModel::requestToggleKeepAbove(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); + +// if (window->windowState & PlasmaWindow::state::state_keep_above) { +// window->set_state(PlasmaWindow::state::state_keep_above, 0); +// } else { +// window->set_state(PlasmaWindow::state::state_keep_above, PlasmaWindow::state::state_keep_above); +// } +// } + +// void WaylandTasksModel::requestToggleKeepBelow(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } +// auto &window = d->windows.at(index.row()); + +// if (window->windowState & PlasmaWindow::state::state_keep_below) { +// window->set_state(PlasmaWindow::state::state_keep_below, 0); +// } else { +// window->set_state(PlasmaWindow::state::state_keep_below, PlasmaWindow::state::state_keep_below); +// } +// } + +// void WaylandTasksModel::requestToggleFullScreen(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); + +// if (window->windowState & PlasmaWindow::state::state_fullscreen) { +// window->set_state(PlasmaWindow::state::state_fullscreen, 0); +// } else { +// window->set_state(PlasmaWindow::state::state_fullscreen, PlasmaWindow::state::state_fullscreen); +// } +// } + +// void WaylandTasksModel::requestToggleShaded(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); + +// if (window->windowState & PlasmaWindow::state::state_shaded) { +// window->set_state(PlasmaWindow::state::state_shaded, 0); +// } else { +// window->set_state(PlasmaWindow::state::state_shaded, PlasmaWindow::state::state_shaded); +// }; +// } + +// void WaylandTasksModel::requestVirtualDesktops(const QModelIndex &index, const QVariantList &desktops) +// { +// // FIXME TODO: Lacks the "if we've requested the current desktop, force-activate +// // the window" logic from X11 version. This behavior should be in KWin rather than +// // libtm however. + +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); + +// if (desktops.isEmpty()) { +// const QStringList virtualDesktops = window->virtualDesktops; +// for (const QString &desktop : virtualDesktops) { +// window->request_leave_virtual_desktop(desktop); +// } +// } else { +// const QStringList &now = window->virtualDesktops; +// QStringList next; + +// for (const QVariant &desktop : desktops) { +// const QString &desktopId = desktop.toString(); + +// if (!desktopId.isEmpty()) { +// next << desktopId; + +// if (!now.contains(desktopId)) { +// window->request_enter_virtual_desktop(desktopId); +// } +// } +// } + +// for (const QString &desktop : now) { +// if (!next.contains(desktop)) { +// window->request_leave_virtual_desktop(desktop); +// } +// } +// } +// } + +// void WaylandTasksModel::requestNewVirtualDesktop(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// d->windows.at(index.row())->request_enter_new_virtual_desktop(); +// } + +// void WaylandTasksModel::requestActivities(const QModelIndex &index, const QStringList &activities) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); +// const auto newActivities = QSet(activities.begin(), activities.end()); +// const auto plasmaActivities = window->activities; +// const auto oldActivities = QSet(plasmaActivities.begin(), plasmaActivities.end()); + +// const auto activitiesToAdd = newActivities - oldActivities; +// for (const auto &activity : activitiesToAdd) { +// window->request_enter_activity(activity); +// } + +// const auto activitiesToRemove = oldActivities - newActivities; +// for (const auto &activity : activitiesToRemove) { +// window->request_leave_activity(activity); +// } +// } + +// void WaylandTasksModel::requestPublishDelegateGeometry(const QModelIndex &index, const QRect &geometry, QObject *delegate) +// { +// /* +// FIXME: This introduces the dependency on Qt::Quick. I might prefer +// reversing this and publishing the window pointer through the model, +// then calling PlasmaWindow::setMinimizeGeometry in the applet backend, +// rather than hand delegate items into the lib, keeping the lib more UI- +// agnostic. +// */ + +// Q_UNUSED(geometry) + +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// const QQuickItem *item = qobject_cast(delegate); + +// if (!item || !item->parentItem()) { +// return; +// } + +// QWindow *itemWindow = item->window(); + +// if (!itemWindow) { +// return; +// } + +// auto waylandWindow = itemWindow->nativeInterface(); + +// if (!waylandWindow || !waylandWindow->surface()) { +// return; +// } + +// QRect rect(item->x(), item->y(), item->width(), item->height()); +// rect.moveTopLeft(item->parentItem()->mapToScene(rect.topLeft()).toPoint()); + +// auto &window = d->windows.at(index.row()); + +// window->set_minimized_geometry(waylandWindow->surface(), rect.x(), rect.y(), rect.width(), rect.height()); +// } + +// QUuid WaylandTasksModel::winIdFromMimeData(const QMimeData *mimeData, bool *ok) +// { +// Q_ASSERT(mimeData); + +// if (ok) { +// *ok = false; +// } + +// if (!mimeData->hasFormat(Private::mimeType())) { +// return {}; +// } + +// QUuid id(mimeData->data(Private::mimeType())); +// *ok = !id.isNull(); + +// return id; +// } + +// QList WaylandTasksModel::winIdsFromMimeData(const QMimeData *mimeData, bool *ok) +// { +// Q_ASSERT(mimeData); +// QList ids; + +// if (ok) { +// *ok = false; +// } + +// if (!mimeData->hasFormat(Private::groupMimeType())) { +// // Try to extract single window id. +// bool singularOk; +// QUuid id = winIdFromMimeData(mimeData, &singularOk); + +// if (ok) { +// *ok = singularOk; +// } + +// if (singularOk) { +// ids << id; +// } + +// return ids; +// } + +// // FIXME: Extracting multiple winids is still unimplemented; +// // TaskGroupingProxy::data(..., ::MimeData) can't produce +// // a payload with them anyways. + +// return ids; +// } + +LXQtTaskBarPlasmaWindow::LXQtTaskBarPlasmaWindow(const QString &uuid, ::org_kde_plasma_window *id) + : org_kde_plasma_window(id) + , uuid(uuid) +{ +} + +/* + * LXQtTaskBarPlasmaWindow + */ + +LXQtTaskBarPlasmaWindow::~LXQtTaskBarPlasmaWindow() +{ + destroy(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_unmapped() +{ + wasUnmapped = true; + Q_EMIT unmapped(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_title_changed(const QString &title) +{ + if(this->title == title) + return; + this->title = title; + Q_EMIT titleChanged(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_app_id_changed(const QString &app_id) +{ + if(appId == app_id) + return; + appId = app_id; + Q_EMIT appIdChanged(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_icon_changed() +{ + int pipeFds[2]; + if (pipe2(pipeFds, O_CLOEXEC) != 0) { + qWarning() << "TaskManager: failed creating pipe"; + return; + } + get_icon(pipeFds[1]); + ::close(pipeFds[1]); + auto readIcon = [uuid = uuid](int fd) { + auto closeGuard = qScopeGuard([fd]() { + ::close(fd); + }); + pollfd pollFd; + pollFd.fd = fd; + pollFd.events = POLLIN; + QByteArray data; + while (true) { + int ready = poll(&pollFd, 1, 1000); + if (ready < 0 && errno != EINTR) { + qWarning() << "TaskManager: polling for icon of window" << uuid << "failed"; + return QIcon(); + } else if (ready == 0) { + qWarning() << "TaskManager: time out polling for icon of window" << uuid; + return QIcon(); + } else { + char buffer[4096]; + int n = read(fd, buffer, sizeof(buffer)); + if (n < 0) { + qWarning() << "TaskManager: error reading icon of window" << uuid; + return QIcon(); + } else if (n > 0) { + data.append(buffer, n); + } else { + QIcon icon; + QDataStream ds(data); + ds >> icon; + return icon; + } + } + } + }; + QFuture future = QtConcurrent::run(readIcon, pipeFds[0]); + auto watcher = new QFutureWatcher(); + watcher->setFuture(future); + connect(watcher, &QFutureWatcher::finished, this, [this, watcher] { + icon = watcher->future().result(); + Q_EMIT iconChanged(); + }); + connect(watcher, &QFutureWatcher::finished, watcher, &QObject::deleteLater); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_themed_icon_name_changed(const QString &name) +{ + icon = QIcon::fromTheme(name); + Q_EMIT iconChanged(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_state_changed(uint32_t flags) +{ + auto diff = windowState ^ flags; + if (diff & state::state_active) { + windowState.setFlag(state::state_active, flags & state::state_active); + Q_EMIT activeChanged(); + } + if (diff & state::state_minimized) { + windowState.setFlag(state::state_minimized, flags & state::state_minimized); + Q_EMIT minimizedChanged(); + } + if (diff & state::state_maximized) { + windowState.setFlag(state::state_maximized, flags & state::state_maximized); + Q_EMIT maximizedChanged(); + } + if (diff & state::state_fullscreen) { + windowState.setFlag(state::state_fullscreen, flags & state::state_fullscreen); + Q_EMIT fullscreenChanged(); + } + if (diff & state::state_keep_above) { + windowState.setFlag(state::state_keep_above, flags & state::state_keep_above); + Q_EMIT keepAboveChanged(); + } + if (diff & state::state_keep_below) { + windowState.setFlag(state::state_keep_below, flags & state::state_keep_below); + Q_EMIT keepBelowChanged(); + } + if (diff & state::state_on_all_desktops) { + windowState.setFlag(state::state_on_all_desktops, flags & state::state_on_all_desktops); + Q_EMIT onAllDesktopsChanged(); + } + if (diff & state::state_demands_attention) { + windowState.setFlag(state::state_demands_attention, flags & state::state_demands_attention); + Q_EMIT demandsAttentionChanged(); + } + if (diff & state::state_closeable) { + windowState.setFlag(state::state_closeable, flags & state::state_closeable); + Q_EMIT closeableChanged(); + } + if (diff & state::state_minimizable) { + windowState.setFlag(state::state_minimizable, flags & state::state_minimizable); + Q_EMIT minimizeableChanged(); + } + if (diff & state::state_maximizable) { + windowState.setFlag(state::state_maximizable, flags & state::state_maximizable); + Q_EMIT maximizeableChanged(); + } + if (diff & state::state_fullscreenable) { + windowState.setFlag(state::state_fullscreenable, flags & state::state_fullscreenable); + Q_EMIT fullscreenableChanged(); + } + if (diff & state::state_skiptaskbar) { + windowState.setFlag(state::state_skiptaskbar, flags & state::state_skiptaskbar); + Q_EMIT skipTaskbarChanged(); + } + if (diff & state::state_shadeable) { + windowState.setFlag(state::state_shadeable, flags & state::state_shadeable); + Q_EMIT shadeableChanged(); + } + if (diff & state::state_shaded) { + windowState.setFlag(state::state_shaded, flags & state::state_shaded); + Q_EMIT shadedChanged(); + } + if (diff & state::state_movable) { + windowState.setFlag(state::state_movable, flags & state::state_movable); + Q_EMIT movableChanged(); + } + if (diff & state::state_resizable) { + windowState.setFlag(state::state_resizable, flags & state::state_resizable); + Q_EMIT resizableChanged(); + } + if (diff & state::state_virtual_desktop_changeable) { + windowState.setFlag(state::state_virtual_desktop_changeable, flags & state::state_virtual_desktop_changeable); + Q_EMIT virtualDesktopChangeableChanged(); + } + if (diff & state::state_skipswitcher) { + windowState.setFlag(state::state_skipswitcher, flags & state::state_skipswitcher); + Q_EMIT skipSwitcherChanged(); + } +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_virtual_desktop_entered(const QString &id) +{ + virtualDesktops.push_back(id); + Q_EMIT virtualDesktopEntered(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_virtual_desktop_left(const QString &id) +{ + virtualDesktops.removeAll(id); + Q_EMIT virtualDesktopLeft(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_geometry(int32_t x, int32_t y, uint32_t width, uint32_t height) +{ + geometry = QRect(x, y, width, height); + Q_EMIT geometryChanged(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_application_menu(const QString &service_name, const QString &object_path) +{ + applicationMenuService = service_name; + applicationMenuObjectPath = object_path; + Q_EMIT applicationMenuChanged(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_activity_entered(const QString &id) +{ + activities.push_back(id); + Q_EMIT activitiesChanged(); +} +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_activity_left(const QString &id) +{ + activities.removeAll(id); + Q_EMIT activitiesChanged(); +} +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_pid_changed(uint32_t pid) +{ + this->pid = pid; +} +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_resource_name_changed(const QString &resource_name) +{ + resourceName = resource_name; +} +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_parent_window(::org_kde_plasma_window *parent) +{ + LXQtTaskBarPlasmaWindow *parentWindow = nullptr; + if (parent) { + parentWindow = dynamic_cast(LXQtTaskBarPlasmaWindow::fromObject(parent)); + } + setParentWindow(parentWindow); +} +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_initial_state() +{ + Q_EMIT initialStateDone(); +} + +void LXQtTaskBarPlasmaWindow::setParentWindow(LXQtTaskBarPlasmaWindow *parent) +{ + const auto old = parentWindow; + QObject::disconnect(parentWindowUnmappedConnection); + + if (parent && !parent->wasUnmapped) { + parentWindow = QPointer(parent); + parentWindowUnmappedConnection = QObject::connect(parent, &LXQtTaskBarPlasmaWindow::unmapped, this, [this] { + setParentWindow(nullptr); + }); + } else { + parentWindow = QPointer(); + parentWindowUnmappedConnection = QMetaObject::Connection(); + } + + if (parentWindow.data() != old.data()) { + Q_EMIT parentWindowChanged(); + } +} + +/* + * LXQtTaskBarPlasmaWindowManagment + */ + +LXQtTaskBarPlasmaWindowManagment::LXQtTaskBarPlasmaWindowManagment() + : QWaylandClientExtensionTemplate(version) +{ + connect(this, &QWaylandClientExtension::activeChanged, this, [this] { + if (!isActive()) { + wl_proxy_destroy(reinterpret_cast(object())); + } + }); +} + +LXQtTaskBarPlasmaWindowManagment::~LXQtTaskBarPlasmaWindowManagment() +{ + if (isActive()) { + wl_proxy_destroy(reinterpret_cast(object())); + } +} + +void LXQtTaskBarPlasmaWindowManagment::org_kde_plasma_window_management_window_with_uuid(uint32_t id, const QString &uuid) +{ + Q_UNUSED(id) + Q_EMIT windowCreated(new LXQtTaskBarPlasmaWindow(uuid, get_window_by_uuid(uuid))); +} +void LXQtTaskBarPlasmaWindowManagment::org_kde_plasma_window_management_stacking_order_uuid_changed(const QString &uuids) +{ + Q_EMIT stackingOrderChanged(uuids); +} diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h new file mode 100644 index 000000000..78c75693d --- /dev/null +++ b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h @@ -0,0 +1,154 @@ +#ifndef LXQTTASKBARPLASMAWINDOWMANAGMENT_H +#define LXQTTASKBARPLASMAWINDOWMANAGMENT_H + +#include +#include +#include + +#include "qwayland-plasma-window-management.h" + +typedef quintptr WId; + +class LXQtTaskBarPlasmaWindowManagment; + +class LXQtTaskBarPlasmaWindow : public QObject, + public QtWayland::org_kde_plasma_window +{ + Q_OBJECT +public: + LXQtTaskBarPlasmaWindow(const QString &uuid, ::org_kde_plasma_window *id); + ~LXQtTaskBarPlasmaWindow(); + + inline WId getWindowId() const { return reinterpret_cast(this); } + + using state = QtWayland::org_kde_plasma_window_management::state; + const QString uuid; + QString title; + QString appId; + QIcon icon; + QFlags windowState; + QList virtualDesktops; + QRect geometry; + QString applicationMenuService; + QString applicationMenuObjectPath; + QList activities; + quint32 pid; + QString resourceName; + QPointer parentWindow; + bool wasUnmapped = false; + bool acceptedInTaskBar = false; + +Q_SIGNALS: + void unmapped(); + void titleChanged(); + void appIdChanged(); + void iconChanged(); + void activeChanged(); + void minimizedChanged(); + void maximizedChanged(); + void fullscreenChanged(); + void keepAboveChanged(); + void keepBelowChanged(); + void onAllDesktopsChanged(); + void demandsAttentionChanged(); + void closeableChanged(); + void minimizeableChanged(); + void maximizeableChanged(); + void fullscreenableChanged(); + void skiptaskbarChanged(); + void shadeableChanged(); + void shadedChanged(); + void movableChanged(); + void resizableChanged(); + void virtualDesktopChangeableChanged(); + void skipSwitcherChanged(); + void virtualDesktopEntered(); + void virtualDesktopLeft(); + void geometryChanged(); + void skipTaskbarChanged(); + void applicationMenuChanged(); + void activitiesChanged(); + void parentWindowChanged(); + void initialStateDone(); + +protected: + void org_kde_plasma_window_unmapped() override; + void org_kde_plasma_window_title_changed(const QString &title) override; + void org_kde_plasma_window_app_id_changed(const QString &app_id) override; + void org_kde_plasma_window_icon_changed() override; + void org_kde_plasma_window_themed_icon_name_changed(const QString &name) override; + void org_kde_plasma_window_state_changed(uint32_t flags) override; + void org_kde_plasma_window_virtual_desktop_entered(const QString &id) override; + + void org_kde_plasma_window_virtual_desktop_left(const QString &id) override; + void org_kde_plasma_window_geometry(int32_t x, int32_t y, uint32_t width, uint32_t height) override; + void org_kde_plasma_window_application_menu(const QString &service_name, const QString &object_path) override; + void org_kde_plasma_window_activity_entered(const QString &id) override; + void org_kde_plasma_window_activity_left(const QString &id) override; + void org_kde_plasma_window_pid_changed(uint32_t pid) override; + void org_kde_plasma_window_resource_name_changed(const QString &resource_name) override; + void org_kde_plasma_window_parent_window(::org_kde_plasma_window *parent) override; + void org_kde_plasma_window_initial_state() override; + +private: + void setParentWindow(LXQtTaskBarPlasmaWindow *parent); + + QMetaObject::Connection parentWindowUnmappedConnection; +}; + +class LXQtTaskBarPlasmaWindowManagment : public QWaylandClientExtensionTemplate, + public QtWayland::org_kde_plasma_window_management +{ + Q_OBJECT +public: + static constexpr int version = 16; + LXQtTaskBarPlasmaWindowManagment(); + ~LXQtTaskBarPlasmaWindowManagment(); + + void org_kde_plasma_window_management_window_with_uuid(uint32_t id, const QString &uuid) override; + void org_kde_plasma_window_management_stacking_order_uuid_changed(const QString &uuids) override; + +Q_SIGNALS: + void windowCreated(LXQtTaskBarPlasmaWindow *window); + void stackingOrderChanged(const QString &uuids); +}; + +// class Q_DECL_HIDDEN WaylandTasksModel::Private +// { +// public: +// Private(WaylandTasksModel *q); +// QHash appDataCache; +// QHash lastActivated; +// PlasmaWindow *activeWindow = nullptr; +// std::vector> windows; +// // key=transient child, value=leader +// QHash transients; +// // key=leader, values=transient children +// QMultiHash transientsDemandingAttention; +// std::unique_ptr windowManagement; +// KSharedConfig::Ptr rulesConfig; +// KDirWatch *configWatcher = nullptr; +// VirtualDesktopInfo *virtualDesktopInfo = nullptr; +// static QUuid uuid; +// QList stackingOrder; + +// void init(); +// void initWayland(); +// auto findWindow(PlasmaWindow *window) const; +// void addWindow(PlasmaWindow *window); + +// const AppData &appData(PlasmaWindow *window); + +// QIcon icon(PlasmaWindow *window); + +// static QString mimeType(); +// static QString groupMimeType(); + +// void dataChanged(PlasmaWindow *window, int role); +// void dataChanged(PlasmaWindow *window, const QList &roles); + +// private: +// WaylandTasksModel *q; +// }; + +#endif // LXQTTASKBARPLASMAWINDOWMANAGMENT_H diff --git a/panel/backends/wayland/protocols/plasma-window-management.xml b/panel/backends/wayland/protocols/plasma-window-management.xml new file mode 100644 index 000000000..5990ebfda --- /dev/null +++ b/panel/backends/wayland/protocols/plasma-window-management.xml @@ -0,0 +1,425 @@ + + + + + + + This interface manages application windows. + It provides requests to show and hide the desktop and emits + an event every time a window is created so that the client can + use it to manage the window. + + Only one client can bind this interface at a time. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tell the compositor to show/hide the desktop. + + + + + + Deprecated: use get_window_by_uuid + + + + + + + + + + + + This event will be sent whenever the show desktop mode changes. E.g. when it is entered + or left. + + On binding the interface the current state is sent. + + + + + + + This event will be sent immediately after a window is mapped. + + + + + + + This event will be sent when stacking order changed and on bind + + + + + + + This event will be sent when stacking order changed and on bind + + + + + + + This event will be sent immediately after a window is mapped. + + + + + + + + + Manages and control an application window. + + Only one client can bind this interface at a time. + + + + + Set window state. + + Values for state argument are described by org_kde_plasma_window_management.state + and can be used together in a bitfield. The flags bitfield describes which flags are + supposed to be set, the state bitfield the value for the set flags + + + + + + + + Deprecated: use enter_virtual_desktop + Maps the window to a different virtual desktop. + + To show the window on all virtual desktops, call the + org_kde_plasma_window.set_state request and specify a on_all_desktops + state in the bitfield. + + + + + + + Sets the geometry of the taskbar entry for this window. + The geometry is relative to a panel in particular. + + + + + + + + + + + Remove the task geometry information for a particular panel. + + + + + + + + + Close this window. + + + + + + Request an interactive move for this window. + + + + + + Request an interactive resize for this window. + + + + + + Removes the resource bound for this org_kde_plasma_window. + + + + + + The compositor will write the window icon into the provided file descriptor. + The data is a serialized QIcon with QDataStream. + + + + + + + This event will be sent as soon as the window title is changed. + + + + + + + This event will be sent as soon as the application + identifier is changed. + + + + + + + This event will be sent as soon as the window state changes. + + Values for state argument are described by org_kde_plasma_window_management.state. + + + + + + + DEPRECATED: use virtual_desktop_entered and virtual_desktop_left instead + This event will be sent when a window is moved to another + virtual desktop. + + It is not sent if it becomes visible on all virtual desktops though. + + + + + + + This event will be sent whenever the themed icon name changes. May be null. + + + + + + + This event will be sent immediately after the window is closed + and its surface is unmapped. + + + + + + This event will be sent immediately after all initial state been sent to the client. + If the Plasma window is already unmapped, the unmapped event will be sent before the + initial_state event. + + + + + + This event will be sent whenever the parent window of this org_kde_plasma_window changes. + The passed parent is another org_kde_plasma_window and this org_kde_plasma_window is a + transient window to the parent window. If the parent argument is null, this + org_kde_plasma_window does not have a parent window. + + + + + + + This event will be sent whenever the window geometry of this org_kde_plasma_window changes. + The coordinates are in absolute coordinates of the windowing system. + + + + + + + + + + This event will be sent whenever the icon of the window changes, but there is no themed + icon name. Common examples are Xwayland windows which have a pixmap based icon. + + The client can request the icon using get_icon. + + + + + + This event will be sent when the compositor has set the process id this window belongs to. + This should be set once before the initial_state is sent. + + + + + + + + + + Make the window enter a virtual desktop. A window can enter more + than one virtual desktop. if the id is empty or invalid, no action will be performed. + + + + + + RFC: do this with an empty id to request_enter_virtual_desktop? + Make the window enter a new virtual desktop. If the server consents the request, + it will create a new virtual desktop and assign the window to it. + + + + + + Make the window exit a virtual desktop. If it exits all desktops it will be considered on all of them. + + + + + + + This event will be sent when the window has entered a new virtual desktop. The window can be on more than one desktop, or none: then is considered on all of them. + + + + + + + This event will be sent when the window left a virtual desktop. If the window leaves all desktops, it can be considered on all. + If the window gets manually added on all desktops, the server has to send virtual_desktop_left for every previous desktop it was in for the window to be really considered on all desktops. + + + + + + + + + This event will be sent after the application menu + for the window has changed. + + + + + + + + Make the window enter an activity. A window can enter more activity. If the id is empty or invalid, no action will be performed. + + + + + + + Make the window exit a an activity. If it exits all activities it will be considered on all of them. + + + + + + + This event will be sent when the window has entered an activity. The window can be on more than one activity, or none: then is considered on all of them. + + + + + + + This event will be sent when the window left an activity. If the window leaves all activities, it will be considered on all. + If the window gets manually added on all activities, the server has to send activity_left for every previous activity it was in for the window to be really considered on all activities. + + + + + + + Requests this window to be displayed in a specific output. + + + + + + + This event will be sent when the X11 resource name of the window has changed. + This is only set for XWayland windows. + + + + + + + + The activation manager interface provides a way to get notified + when an application is about to be activated. + + + + + Destroy the activation manager object. The activation objects introduced + by this manager object will be unaffected. + + + + + + Will be issued when an app is set to be activated. It offers + an instance of org_kde_plasma_activation that will tell us the app_id + and the extent of the activation. + + + + + + + + + Notify the compositor that the org_kde_plasma_activation object will no + longer be used. + + + + + + + + + + + + + diff --git a/panel/lxqtpanelapplication.cpp b/panel/lxqtpanelapplication.cpp index c3b666620..507163221 100644 --- a/panel/lxqtpanelapplication.cpp +++ b/panel/lxqtpanelapplication.cpp @@ -40,11 +40,14 @@ #include "backends/lxqttaskbardummybackend.h" #include "backends/xcb/lxqttaskbarbackend_x11.h" +#include "backends/wayland/lxqttaskbarbackendwayland.h" ILXQtTaskbarAbstractBackend *createWMBackend() { if(qGuiApp->nativeInterface()) return new LXQtTaskbarX11Backend; + else if(qGuiApp->nativeInterface()) + return new LXQtTaskbarWaylandBackend; qWarning() << "\n" << "ERROR: Could not create a backend for window managment operations.\n" From 6bb3b1649f7895a56c4282f42789e05824a78224 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 17:12:45 +0100 Subject: [PATCH 037/165] LXQtTaskBarPlasmaWindowManagment: implement showDesktop() --- .../wayland/lxqttaskbarbackendwayland.cpp | 17 +++++++++++++++++ .../wayland/lxqttaskbarbackendwayland.h | 4 ++++ .../lxqttaskbarplasmawindowmanagment.cpp | 5 +++++ .../wayland/lxqttaskbarplasmawindowmanagment.h | 7 +++++++ 4 files changed, 33 insertions(+) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index a2a415504..d85ec99d8 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -372,6 +372,23 @@ bool LXQtTaskbarWaylandBackend::isAreaOverlapped(const QRect &area) const return false; } +bool LXQtTaskbarWaylandBackend::isShowingDesktop() const +{ + return m_managment->isShowingDesktop(); +} + +bool LXQtTaskbarWaylandBackend::showDesktop(bool value) +{ + enum LXQtTaskBarPlasmaWindowManagment::show_desktop flag_; + if(value) + flag_ = LXQtTaskBarPlasmaWindowManagment::show_desktop::show_desktop_enabled; + else + flag_ = LXQtTaskBarPlasmaWindowManagment::show_desktop::show_desktop_disabled; + + m_managment->show_desktop(flag_); + return true; +} + void LXQtTaskbarWaylandBackend::addWindow(LXQtTaskBarPlasmaWindow *window) { if (findWindow(windows, window) != windows.end() || transients.contains(window)) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.h b/panel/backends/wayland/lxqttaskbarbackendwayland.h index 553a7e585..77d857eb9 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.h +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.h @@ -65,6 +65,10 @@ class LXQtTaskbarWaylandBackend : public ILXQtTaskbarAbstractBackend // Panel internal virtual bool isAreaOverlapped(const QRect& area) const override; + // Show Destop + virtual bool isShowingDesktop() const override; + virtual bool showDesktop(bool value) override; + private: void addWindow(LXQtTaskBarPlasmaWindow *window); bool acceptWindow(LXQtTaskBarPlasmaWindow *window) const; diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp index 0a282eadd..3b765d3b3 100644 --- a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp +++ b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp @@ -1135,6 +1135,11 @@ LXQtTaskBarPlasmaWindowManagment::~LXQtTaskBarPlasmaWindowManagment() } } +void LXQtTaskBarPlasmaWindowManagment::org_kde_plasma_window_management_show_desktop_changed(uint32_t state) +{ + m_isShowingDesktop = (state == show_desktop::show_desktop_enabled); +} + void LXQtTaskBarPlasmaWindowManagment::org_kde_plasma_window_management_window_with_uuid(uint32_t id, const QString &uuid) { Q_UNUSED(id) diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h index 78c75693d..c21b57b3c 100644 --- a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h +++ b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h @@ -105,12 +105,19 @@ class LXQtTaskBarPlasmaWindowManagment : public QWaylandClientExtensionTemplate< LXQtTaskBarPlasmaWindowManagment(); ~LXQtTaskBarPlasmaWindowManagment(); + inline bool isShowingDesktop() const { return m_isShowingDesktop; } + +protected: + void org_kde_plasma_window_management_show_desktop_changed(uint32_t state) override; void org_kde_plasma_window_management_window_with_uuid(uint32_t id, const QString &uuid) override; void org_kde_plasma_window_management_stacking_order_uuid_changed(const QString &uuids) override; Q_SIGNALS: void windowCreated(LXQtTaskBarPlasmaWindow *window); void stackingOrderChanged(const QString &uuids); + +private: + bool m_isShowingDesktop = false; }; // class Q_DECL_HIDDEN WaylandTasksModel::Private From ec178e3ee0f94548749dee6fe9fca64271de9ad6 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 11:17:24 +0100 Subject: [PATCH 038/165] LXQtTaskbarWaylandBackend: do not show transient windows --- panel/backends/wayland/lxqttaskbarbackendwayland.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index d85ec99d8..5def725ef 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -614,6 +614,9 @@ bool LXQtTaskbarWaylandBackend::acceptWindow(LXQtTaskBarPlasmaWindow *window) co if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_skiptaskbar)) return false; + if(transients.contains(window)) + return false; + return true; } From 70dbc387960c4dc18394574369c7b6cb8ee12251 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Tue, 20 Feb 2024 10:04:23 +0100 Subject: [PATCH 039/165] LXQtTaskBarPlasmaWindowManagment: fix destructor TODO TODO: is this correct? Seems to call wl_proxy_destroy underneath --- panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp | 4 ++-- panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp index 3b765d3b3..f813181ed 100644 --- a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp +++ b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp @@ -1123,7 +1123,7 @@ LXQtTaskBarPlasmaWindowManagment::LXQtTaskBarPlasmaWindowManagment() { connect(this, &QWaylandClientExtension::activeChanged, this, [this] { if (!isActive()) { - wl_proxy_destroy(reinterpret_cast(object())); + org_kde_plasma_window_management_destroy(object()); } }); } @@ -1131,7 +1131,7 @@ LXQtTaskBarPlasmaWindowManagment::LXQtTaskBarPlasmaWindowManagment() LXQtTaskBarPlasmaWindowManagment::~LXQtTaskBarPlasmaWindowManagment() { if (isActive()) { - wl_proxy_destroy(reinterpret_cast(object())); + org_kde_plasma_window_management_destroy(object()); } } diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h index c21b57b3c..976ac53e2 100644 --- a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h +++ b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h @@ -102,6 +102,7 @@ class LXQtTaskBarPlasmaWindowManagment : public QWaylandClientExtensionTemplate< Q_OBJECT public: static constexpr int version = 16; + LXQtTaskBarPlasmaWindowManagment(); ~LXQtTaskBarPlasmaWindowManagment(); From b4b7c13cb5119b344faaaec1c4f0d8086dbe910d Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Wed, 21 Feb 2024 11:52:48 +0100 Subject: [PATCH 040/165] LXQtPanel: basic virtual desktop support on Plasma Wayland --- panel/CMakeLists.txt | 7 + .../wayland/lxqtplasmavirtualdesktop.cpp | 231 ++++++++++++++++++ .../wayland/lxqtplasmavirtualdesktop.h | 87 +++++++ .../wayland/lxqttaskbarbackendwayland.cpp | 18 +- .../wayland/lxqttaskbarbackendwayland.h | 3 + .../org-kde-plasma-virtual-desktop.xml | 110 +++++++++ 6 files changed, 454 insertions(+), 2 deletions(-) create mode 100644 panel/backends/wayland/lxqtplasmavirtualdesktop.cpp create mode 100644 panel/backends/wayland/lxqtplasmavirtualdesktop.h create mode 100644 panel/backends/wayland/protocols/org-kde-plasma-virtual-desktop.xml diff --git a/panel/CMakeLists.txt b/panel/CMakeLists.txt index 6fb7c7608..8c02881fd 100644 --- a/panel/CMakeLists.txt +++ b/panel/CMakeLists.txt @@ -35,6 +35,7 @@ set(PRIV_HEADERS backends/wayland/lxqttaskbarbackendwayland.h backends/wayland/lxqttaskbarplasmawindowmanagment.h + backends/wayland/lxqtplasmavirtualdesktop.h ) # using LXQt namespace in the public headers. @@ -74,6 +75,7 @@ set(SOURCES backends/wayland/lxqttaskbarbackendwayland.cpp backends/wayland/lxqttaskbarplasmawindowmanagment.cpp + backends/wayland/lxqtplasmavirtualdesktop.cpp ) set(UI @@ -134,6 +136,11 @@ FILES ${CMAKE_CURRENT_SOURCE_DIR}/backends/wayland/protocols/plasma-window-management.xml ) +qt6_generate_wayland_protocol_client_sources(${PROJECT} +FILES + ${CMAKE_CURRENT_SOURCE_DIR}/backends/wayland/protocols/org-kde-plasma-virtual-desktop.xml +) + target_link_libraries(${PROJECT} ${LIBRARIES} ${QTX_LIBRARIES} diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp new file mode 100644 index 000000000..a3d987f06 --- /dev/null +++ b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp @@ -0,0 +1,231 @@ +#include "lxqtplasmavirtualdesktop.h" + +#include + +LXQtPlasmaVirtualDesktop::LXQtPlasmaVirtualDesktop(::org_kde_plasma_virtual_desktop *object, const QString &id) + : org_kde_plasma_virtual_desktop(object) + , id(id) +{ +} + +LXQtPlasmaVirtualDesktop::~LXQtPlasmaVirtualDesktop() +{ + wl_proxy_destroy(reinterpret_cast(object())); +} + +void LXQtPlasmaVirtualDesktop::org_kde_plasma_virtual_desktop_name(const QString &name) +{ + this->name = name; +} + +void LXQtPlasmaVirtualDesktop::org_kde_plasma_virtual_desktop_done() +{ + Q_EMIT done(); +} + +void LXQtPlasmaVirtualDesktop::org_kde_plasma_virtual_desktop_activated() +{ + Q_EMIT activated(); +} + +LXQtPlasmaVirtualDesktopManagment::LXQtPlasmaVirtualDesktopManagment() + : QWaylandClientExtensionTemplate(version) +{ + connect(this, &QWaylandClientExtension::activeChanged, this, [this] { + if (!isActive()) { + org_kde_plasma_virtual_desktop_management_destroy(object()); + } + }); +} + +LXQtPlasmaVirtualDesktopManagment::~LXQtPlasmaVirtualDesktopManagment() +{ + if (isActive()) { + org_kde_plasma_virtual_desktop_management_destroy(object()); + } +} + +void LXQtPlasmaVirtualDesktopManagment::org_kde_plasma_virtual_desktop_management_desktop_created(const QString &desktop_id, uint32_t position) +{ + emit desktopCreated(desktop_id, position); +} + +void LXQtPlasmaVirtualDesktopManagment::org_kde_plasma_virtual_desktop_management_desktop_removed(const QString &desktop_id) +{ + emit desktopRemoved(desktop_id); +} + +void LXQtPlasmaVirtualDesktopManagment::org_kde_plasma_virtual_desktop_management_rows(uint32_t rows) +{ + emit rowsChanged(rows); +} + +LXQtPlasmaWaylandWorkspaceInfo::LXQtPlasmaWaylandWorkspaceInfo() +{ + init(); +} + +LXQtPlasmaWaylandWorkspaceInfo::VirtualDesktopsIterator LXQtPlasmaWaylandWorkspaceInfo::findDesktop(const QString &id) const +{ + return std::find_if(virtualDesktops.begin(), virtualDesktops.end(), + [&id](const std::unique_ptr &desktop) { + return desktop->id == id; + }); +} + +void LXQtPlasmaWaylandWorkspaceInfo::init() +{ + virtualDesktopManagement = std::make_unique(); + + connect(virtualDesktopManagement.get(), &LXQtPlasmaVirtualDesktopManagment::activeChanged, this, [this] { + if (!virtualDesktopManagement->isActive()) { + rows = 0; + virtualDesktops.clear(); + currentVirtualDesktop.clear(); + Q_EMIT currentDesktopChanged(); + Q_EMIT numberOfDesktopsChanged(); + Q_EMIT navigationWrappingAroundChanged(); + Q_EMIT desktopIdsChanged(); + Q_EMIT desktopNamesChanged(); + Q_EMIT desktopLayoutRowsChanged(); + } + }); + + connect(virtualDesktopManagement.get(), &LXQtPlasmaVirtualDesktopManagment::desktopCreated, + this, &LXQtPlasmaWaylandWorkspaceInfo::addDesktop); + + connect(virtualDesktopManagement.get(), &LXQtPlasmaVirtualDesktopManagment::desktopRemoved, this, [this](const QString &id) { + + + std::remove_if(virtualDesktops.begin(), virtualDesktops.end(), + [id](const std::unique_ptr &desktop) + { + return desktop->id == id; + }); + + Q_EMIT numberOfDesktopsChanged(); + Q_EMIT desktopIdsChanged(); + Q_EMIT desktopNamesChanged(); + + if (currentVirtualDesktop == id) { + currentVirtualDesktop.clear(); + Q_EMIT currentDesktopChanged(); + } + }); + + connect(virtualDesktopManagement.get(), &LXQtPlasmaVirtualDesktopManagment::rowsChanged, this, [this](quint32 rows) { + this->rows = rows; + Q_EMIT desktopLayoutRowsChanged(); + }); +} + +void LXQtPlasmaWaylandWorkspaceInfo::addDesktop(const QString &id, quint32 position) +{ + if (findDesktop(id) != virtualDesktops.end()) { + return; + } + + auto desktop = std::make_unique(virtualDesktopManagement->get_virtual_desktop(id), id); + + connect(desktop.get(), &LXQtPlasmaVirtualDesktop::activated, this, [id, this]() { + currentVirtualDesktop = id; + Q_EMIT currentDesktopChanged(); + }); + + connect(desktop.get(), &LXQtPlasmaVirtualDesktop::done, this, [this]() { + Q_EMIT desktopNamesChanged(); + }); + + virtualDesktops.insert(std::next(virtualDesktops.begin(), position), std::move(desktop)); + + Q_EMIT numberOfDesktopsChanged(); + Q_EMIT desktopIdsChanged(); + Q_EMIT desktopNamesChanged(); +} + +QVariant LXQtPlasmaWaylandWorkspaceInfo::currentDesktop() const +{ + return currentVirtualDesktop; +} + +int LXQtPlasmaWaylandWorkspaceInfo::numberOfDesktops() const +{ + return virtualDesktops.size(); +} + +quint32 LXQtPlasmaWaylandWorkspaceInfo::position(const QVariant &desktop) const +{ + return std::distance(virtualDesktops.begin(), findDesktop(desktop.toString())); +} + +QVariantList LXQtPlasmaWaylandWorkspaceInfo::desktopIds() const +{ + QVariantList ids; + ids.reserve(virtualDesktops.size()); + + std::transform(virtualDesktops.cbegin(), virtualDesktops.cend(), std::back_inserter(ids), [](const std::unique_ptr &desktop) { + return desktop->id; + }); + return ids; +} + +QStringList LXQtPlasmaWaylandWorkspaceInfo::desktopNames() const +{ + if (!virtualDesktopManagement->isActive()) { + return QStringList(); + } + QStringList names; + names.reserve(virtualDesktops.size()); + + std::transform(virtualDesktops.cbegin(), virtualDesktops.cend(), std::back_inserter(names), [](const std::unique_ptr &desktop) { + return desktop->name; + }); + return names; +} + +int LXQtPlasmaWaylandWorkspaceInfo::desktopLayoutRows() const +{ + if (!virtualDesktopManagement->isActive()) { + return 0; + } + + return rows; +} + +void LXQtPlasmaWaylandWorkspaceInfo::requestActivate(const QVariant &desktop) +{ + if (!virtualDesktopManagement->isActive()) { + return; + } + + if (auto it = findDesktop(desktop.toString()); it != virtualDesktops.end()) { + (*it)->request_activate(); + } +} + +void LXQtPlasmaWaylandWorkspaceInfo::requestCreateDesktop(quint32 position) +{ + if (!virtualDesktopManagement->isActive()) { + return; + } + + //TODO: translatestd + virtualDesktopManagement->request_create_virtual_desktop(QLatin1String("New Desktop"), position); +} + +void LXQtPlasmaWaylandWorkspaceInfo::requestRemoveDesktop(quint32 position) +{ + if (!virtualDesktopManagement->isActive()) { + return; + } + if (virtualDesktops.size() == 1) { + return; + } + + if (position > (virtualDesktops.size() - 1)) { + return; + } + + virtualDesktopManagement->request_remove_virtual_desktop(virtualDesktops.at(position)->id); +} + diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.h b/panel/backends/wayland/lxqtplasmavirtualdesktop.h new file mode 100644 index 000000000..3dabb6242 --- /dev/null +++ b/panel/backends/wayland/lxqtplasmavirtualdesktop.h @@ -0,0 +1,87 @@ +#ifndef LXQTPLASMAVIRTUALDESKTOP_H +#define LXQTPLASMAVIRTUALDESKTOP_H + +#include +#include + +#include + +#include "qwayland-org-kde-plasma-virtual-desktop.h" + +class LXQtPlasmaVirtualDesktop : public QObject, public QtWayland::org_kde_plasma_virtual_desktop +{ + Q_OBJECT +public: + LXQtPlasmaVirtualDesktop(::org_kde_plasma_virtual_desktop *object, const QString &id); + ~LXQtPlasmaVirtualDesktop(); + const QString id; + QString name; +Q_SIGNALS: + void done(); + void activated(); + +protected: + void org_kde_plasma_virtual_desktop_name(const QString &name) override; + void org_kde_plasma_virtual_desktop_done() override; + void org_kde_plasma_virtual_desktop_activated() override; +}; + + +class LXQtPlasmaVirtualDesktopManagment : public QWaylandClientExtensionTemplate, + public QtWayland::org_kde_plasma_virtual_desktop_management +{ + Q_OBJECT +public: + static constexpr int version = 2; + + LXQtPlasmaVirtualDesktopManagment(); + ~LXQtPlasmaVirtualDesktopManagment(); + +signals: + void desktopCreated(const QString &id, quint32 position); + void desktopRemoved(const QString &id); + void rowsChanged(const quint32 rows); + +protected: + virtual void org_kde_plasma_virtual_desktop_management_desktop_created(const QString &desktop_id, uint32_t position) override; + virtual void org_kde_plasma_virtual_desktop_management_desktop_removed(const QString &desktop_id) override; + virtual void org_kde_plasma_virtual_desktop_management_rows(uint32_t rows) override; +}; + +class Q_DECL_HIDDEN LXQtPlasmaWaylandWorkspaceInfo : public QObject +{ + Q_OBJECT +public: + LXQtPlasmaWaylandWorkspaceInfo(); + + QVariant currentVirtualDesktop; + std::vector> virtualDesktops; + std::unique_ptr virtualDesktopManagement; + quint32 rows; + + typedef std::vector>::const_iterator VirtualDesktopsIterator; + + VirtualDesktopsIterator findDesktop(const QString &id) const; + + void init(); + void addDesktop(const QString &id, quint32 position); + QVariant currentDesktop() const; + int numberOfDesktops() const; + QVariantList desktopIds() const; + QStringList desktopNames() const; + quint32 position(const QVariant &desktop) const; + int desktopLayoutRows() const; + void requestActivate(const QVariant &desktop); + void requestCreateDesktop(quint32 position); + void requestRemoveDesktop(quint32 position); + +signals: + void currentDesktopChanged(); + void numberOfDesktopsChanged(); + void navigationWrappingAroundChanged(); + void desktopIdsChanged(); + void desktopNamesChanged(); + void desktopLayoutRowsChanged(); +}; + +#endif // LXQTPLASMAVIRTUALDESKTOP_H diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index 5def725ef..57b77c906 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -1,6 +1,7 @@ #include "lxqttaskbarbackendwayland.h" #include "lxqttaskbarplasmawindowmanagment.h" +#include "lxqtplasmavirtualdesktop.h" #include #include @@ -25,6 +26,7 @@ LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) : ILXQtTaskbarAbstractBackend(parent) { m_managment.reset(new LXQtTaskBarPlasmaWindowManagment); + m_workspaceInfo.reset(new LXQtPlasmaWaylandWorkspaceInfo); connect(m_managment.get(), &LXQtTaskBarPlasmaWindowManagment::windowCreated, this, [this](LXQtTaskBarPlasmaWindow *window) { connect(window, &LXQtTaskBarPlasmaWindow::initialStateDone, this, [this, window] { @@ -39,6 +41,17 @@ LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) : // // this->dataChanged(window.get(), StackingOrder); // // } // }); + + connect(m_workspaceInfo.get(), &LXQtPlasmaWaylandWorkspaceInfo::currentDesktopChanged, this, + [this](){ + int idx = m_workspaceInfo->position(m_workspaceInfo->currentDesktop()); + emit currentWorkspaceChanged(idx); + }); + + connect(m_workspaceInfo.get(), &LXQtPlasmaWaylandWorkspaceInfo::numberOfDesktopsChanged, + this, &ILXQtTaskbarAbstractBackend::workspacesCountChanged); + + //TODO: connect name changed } bool LXQtTaskbarWaylandBackend::supportsAction(WId windowId, LXQtTaskBarBackendAction action) const @@ -291,12 +304,13 @@ WId LXQtTaskbarWaylandBackend::getActiveWindow() const int LXQtTaskbarWaylandBackend::getWorkspacesCount() const { - return 1; //TODO + return m_workspaceInfo->numberOfDesktops(); } QString LXQtTaskbarWaylandBackend::getWorkspaceName(int idx) const { - return QStringLiteral("TestWorkspace"); + //TODO: optimize + return m_workspaceInfo->desktopNames().value(idx, QStringLiteral("ERROR")); } int LXQtTaskbarWaylandBackend::getCurrentWorkspace() const diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.h b/panel/backends/wayland/lxqttaskbarbackendwayland.h index 77d857eb9..0b12ef2fd 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.h +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.h @@ -8,6 +8,7 @@ class LXQtTaskBarPlasmaWindow; class LXQtTaskBarPlasmaWindowManagment; +class LXQtPlasmaWaylandWorkspaceInfo; class LXQtTaskbarWaylandBackend : public ILXQtTaskbarAbstractBackend @@ -77,6 +78,8 @@ class LXQtTaskbarWaylandBackend : public ILXQtTaskbarAbstractBackend private: LXQtTaskBarPlasmaWindow *getWindow(WId windowId) const; + std::unique_ptr m_workspaceInfo; + std::unique_ptr m_managment; QHash lastActivated; diff --git a/panel/backends/wayland/protocols/org-kde-plasma-virtual-desktop.xml b/panel/backends/wayland/protocols/org-kde-plasma-virtual-desktop.xml new file mode 100644 index 000000000..0e0551b0a --- /dev/null +++ b/panel/backends/wayland/protocols/org-kde-plasma-virtual-desktop.xml @@ -0,0 +1,110 @@ + + + + + + + + Given the id of a particular virtual desktop, get the corresponding org_kde_plasma_virtual_desktop which represents only the desktop with that id. + + + + + + + + Ask the server to create a new virtual desktop, and position it at a specified position. If the position is zero or less, it will be positioned at the beginning, if the position is the count or more, it will be positioned at the end. + + + + + + + + Ask the server to get rid of a virtual desktop, the server may or may not acconsent to the request. + + + + + + + + + + + + + + + + + + + This event is sent after all other properties has been + sent after binding to the desktop manager object and after any + other property changes done after that. This allows + changes to the org_kde_plasma_virtual_desktop_management properties to be seen as + atomic, even if they happen via multiple events. + + + + + + + + + + + + + Request the server to set the status of this desktop to active: The server is free to consent or deny the request. This will be the new "current" virtual desktop of the system. + + + + + + The format of the id is decided by the compositor implementation. A desktop id univocally identifies a virtual desktop and must be guaranteed to never exist two desktops with the same id. The format of the string id is up to the server implementation. + + + + + + + + + + + The desktop will be the new "current" desktop of the system. The server may support either one virtual desktop active at a time, or other combinations such as one virtual desktop active per screen. + Windows associated to this virtual desktop will be shown. + + + + + + Windows that were associated only to this desktop will be hidden. + + + + + + This event is sent after all other properties has been + sent after binding to the desktop object and after any + other property changes done after that. This allows + changes to the org_kde_plasma_virtual_desktop properties to be seen as + atomic, even if they happen via multiple events. + + + + + + This virtual desktop has just been removed by the server: + All windows will lose the association to this desktop. + + + + + From fc55f281139c19ebd47dee4ec66a44fecb3f723f Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 17 Feb 2024 23:32:46 +0100 Subject: [PATCH 041/165] LXQtPanel: workaround KAcceleratorManager changing button text FIXME TODO TODO: is this correct approach? --- plugin-taskbar/lxqttaskbutton.cpp | 27 ++++++++++++++++++++++++++- plugin-taskbar/lxqttaskbutton.h | 4 ++++ plugin-taskbar/lxqttaskgroup.cpp | 6 +++--- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 019b45199..45e836893 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -128,7 +128,7 @@ LXQtTaskButton::~LXQtTaskButton() = default; void LXQtTaskButton::updateText() { QString title = mBackend->getWindowTitle(mWindow); - setText(title.replace(QStringLiteral("&"), QStringLiteral("&&"))); + setTextExplicitly(title.replace(QStringLiteral("&"), QStringLiteral("&&"))); setToolTip(title); } @@ -314,6 +314,30 @@ QMimeData * LXQtTaskButton::mimeData() return mimedata; } +/*! + * \brief LXQtTaskButton::setTextExplicitly + * \param str + * + * This is needed to workaround flickering caused by KAcceleratorManager + * This class is hooked by KDE Integration and adds accelerators to button text + * (Adds some '&' characters) + * This triggers widget update but soon after text is reset to original value + * This triggers a KAcceleratorManager update which again adds accelerator + * This happens in loop + * + * TODO: investigate proper solution + */ +void LXQtTaskButton::setTextExplicitly(const QString &str) +{ + if(str == mExplicitlySetText) + { + return; + } + + mExplicitlySetText = str; + setText(mExplicitlySetText); +} + /************************************************ ************************************************/ @@ -688,6 +712,7 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) menu->addSeparator(); a = menu->addAction(XdgIcon::fromTheme(QStringLiteral("process-stop")), tr("&Close")); connect(a, &QAction::triggered, this, &LXQtTaskButton::closeApplication); + menu->setGeometry(mParentTaskBar->panel()->calculatePopupWindowPos(mapToGlobal(event->pos()), menu->sizeHint())); mPlugin->willShowWindow(menu); menu->show(); diff --git a/plugin-taskbar/lxqttaskbutton.h b/plugin-taskbar/lxqttaskbutton.h index 9ccca36fa..6d5841df4 100644 --- a/plugin-taskbar/lxqttaskbutton.h +++ b/plugin-taskbar/lxqttaskbutton.h @@ -122,6 +122,8 @@ public slots: inline ILXQtPanelPlugin * plugin() const { return mPlugin; } + void setTextExplicitly(const QString& str); + protected: //TODO: public getter instead? ILXQtTaskbarAbstractBackend *mBackend; @@ -138,6 +140,8 @@ public slots: int mIconSize; int mWheelDelta; + QString mExplicitlySetText; + // Timer for when draggind something into a button (the button's window // must be activated so that the use can continue dragging to the window QTimer * mDNDTimer; diff --git a/plugin-taskbar/lxqttaskgroup.cpp b/plugin-taskbar/lxqttaskgroup.cpp index c6075df89..f7da5ef21 100644 --- a/plugin-taskbar/lxqttaskgroup.cpp +++ b/plugin-taskbar/lxqttaskgroup.cpp @@ -57,7 +57,7 @@ LXQtTaskGroup::LXQtTaskGroup(const QString &groupName, WId window, LXQtTaskBar * Q_ASSERT(parent); setObjectName(groupName); - setText(groupName); + setTextExplicitly(groupName); connect(this, &LXQtTaskGroup::clicked, this, &LXQtTaskGroup::onClicked); connect(parent, &LXQtTaskBar::buttonRotationRefreshed, this, &LXQtTaskGroup::setAutoRotation); @@ -336,7 +336,7 @@ void LXQtTaskGroup::regroup() if (button) { - setText(button->text()); + setTextExplicitly(button->text()); setToolTip(button->toolTip()); setWindowId(button->windowId()); } @@ -347,7 +347,7 @@ void LXQtTaskGroup::regroup() { mSingleButton = false; QString t = QString(QStringLiteral("%1 - %2 windows")).arg(mGroupName).arg(cont); - setText(t); + setTextExplicitly(t); setToolTip(parentTaskBar()->isShowGroupOnHover() ? QString() : t); } } From 43ee0440249f22e98cee405d13329339cfe8f098 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 17 Feb 2024 23:16:24 +0100 Subject: [PATCH 042/165] LXQtPanel: use LayerShell on Wayland layer_shell protocol allows to aks for placement on Wayland --- CMakeLists.txt | 2 + panel/CMakeLists.txt | 1 + panel/lxqtpanel.cpp | 172 +++++++++++++++++++++++++++++++------------ panel/lxqtpanel.h | 6 ++ 4 files changed, 134 insertions(+), 47 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 25350186d..3cd60ede7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,8 @@ find_package(lxqt ${LXQT_MINIMUM_VERSION} REQUIRED) find_package(lxqt-globalkeys-ui ${LXQT_GLOBALKEYS_MINIMUM_VERSION} REQUIRED) find_package(lxqt-menu-data ${LXQT_MINIMUM_VERSION} REQUIRED) +find_package(LayerShellQt REQUIRED) + # Patch Version set(LXQT_PANEL_PATCH_VERSION 0) set(LXQT_PANEL_VERSION ${LXQT_MAJOR_VERSION}.${LXQT_MINOR_VERSION}.${LXQT_PANEL_PATCH_VERSION}) diff --git a/panel/CMakeLists.txt b/panel/CMakeLists.txt index 8c02881fd..d1008d561 100644 --- a/panel/CMakeLists.txt +++ b/panel/CMakeLists.txt @@ -147,6 +147,7 @@ target_link_libraries(${PROJECT} KF6::WindowSystem Qt6::WaylandClient # TODO: Move to backend folder Qt6::Concurrent + LayerShellQt::Interface ${STATIC_PLUGINS} ) diff --git a/panel/lxqtpanel.cpp b/panel/lxqtpanel.cpp index b704fdd3d..ebadfe0d3 100644 --- a/panel/lxqtpanel.cpp +++ b/panel/lxqtpanel.cpp @@ -54,6 +54,7 @@ #include "backends/ilxqttaskbarabstractbackend.h" +#include // Turn on this to show the time required to load each plugin during startup // #define DEBUG_PLUGIN_LOADTIME @@ -143,6 +144,7 @@ LXQtPanel::LXQtPanel(const QString &configGroup, LXQt::Settings *settings, QWidg mAnimationTime(0), mReserveSpace(true), mAnimation(nullptr), + mLayerWindow(nullptr), mLockPanel(false) { //You can find information about the flags and widget attributes in your @@ -232,6 +234,30 @@ LXQtPanel::LXQtPanel(const QString &configGroup, LXQt::Settings *settings, QWidg loadPlugins(); + if(qGuiApp->nativeInterface()) + { + // Create backing QWindow for LayerShellQt integration + create(); + + // Init Layer Shell (Must be done before showing widget) + mLayerWindow = LayerShellQt::Window::get(windowHandle()); + mLayerWindow->setLayer(LayerShellQt::Window::LayerTop); + + mLayerWindow->setScope(QStringLiteral("dock")); + + LayerShellQt::Window::Anchors anchors; + anchors.setFlag(LayerShellQt::Window::AnchorLeft); + anchors.setFlag(LayerShellQt::Window::AnchorBottom); + anchors.setFlag(LayerShellQt::Window::AnchorRight); + mLayerWindow->setAnchors(anchors); + + mLayerWindow->setKeyboardInteractivity(LayerShellQt::Window::KeyboardInteractivityNone); + mLayerWindow->setCloseOnDismissed(false); + + mLayerWindow->setExclusiveEdge(LayerShellQt::Window::AnchorBottom); + mLayerWindow->setExclusiveZone(height()); + } + // NOTE: Some (X11) WMs may need the geometry to be set before QWidget::show(). setPanelGeometry(); @@ -485,6 +511,7 @@ void LXQtPanel::setPanelGeometry(bool animate) const QRect currentScreen = screens.at(mActualScreenNum)->geometry(); QRect rect; + LayerShellQt::Window::Anchors anchors; if (isHorizontal()) { @@ -521,6 +548,9 @@ void LXQtPanel::setPanelGeometry(bool animate) // Vert ....................... if (mPosition == ILXQtPanel::PositionTop) { + anchors.setFlag(LayerShellQt::Window::AnchorTop); + anchors.setFlag(LayerShellQt::Window::AnchorLeft); + anchors.setFlag(LayerShellQt::Window::AnchorRight); if (mHidden) rect.moveBottom(currentScreen.top() + PANEL_HIDE_SIZE - 1); else @@ -528,6 +558,9 @@ void LXQtPanel::setPanelGeometry(bool animate) } else { + anchors.setFlag(LayerShellQt::Window::AnchorBottom); + anchors.setFlag(LayerShellQt::Window::AnchorLeft); + anchors.setFlag(LayerShellQt::Window::AnchorRight); if (mHidden) rect.moveTop(currentScreen.bottom() - PANEL_HIDE_SIZE + 1); else @@ -569,6 +602,9 @@ void LXQtPanel::setPanelGeometry(bool animate) // Horiz ...................... if (mPosition == ILXQtPanel::PositionLeft) { + anchors.setFlag(LayerShellQt::Window::AnchorTop); + anchors.setFlag(LayerShellQt::Window::AnchorLeft); + anchors.setFlag(LayerShellQt::Window::AnchorBottom); if (mHidden) rect.moveRight(currentScreen.left() + PANEL_HIDE_SIZE - 1); else @@ -576,12 +612,19 @@ void LXQtPanel::setPanelGeometry(bool animate) } else { + anchors.setFlag(LayerShellQt::Window::AnchorTop); + anchors.setFlag(LayerShellQt::Window::AnchorRight); + anchors.setFlag(LayerShellQt::Window::AnchorBottom); if (mHidden) rect.moveLeft(currentScreen.right() - PANEL_HIDE_SIZE + 1); else rect.moveRight(currentScreen.right()); } } + + if(mLayerWindow) + mLayerWindow->setAnchors(anchors); + if (!mHidden || !mGeometry.isValid()) mGeometry = rect; if (rect != geometry()) { @@ -670,62 +713,97 @@ void LXQtPanel::updateWmStrut() if(wid == 0 || !isVisible()) return; - if (mReserveSpace && QApplication::primaryScreen()) + if(qGuiApp->nativeInterface()) { - const QRect wholeScreen = QApplication::primaryScreen()->virtualGeometry(); - const QRect rect = geometry(); - // NOTE: https://standards.freedesktop.org/wm-spec/wm-spec-latest.html - // Quote from the EWMH spec: " Note that the strut is relative to the screen edge, and not the edge of the xinerama monitor." - // So, we use the geometry of the whole screen to calculate the strut rather than using the geometry of individual monitors. - // Though the spec only mention Xinerama and did not mention XRandR, the rule should still be applied. - // At least openbox is implemented like this. - switch (mPosition) + if (mReserveSpace && QApplication::primaryScreen()) { - case LXQtPanel::PositionTop: - KX11Extras::setExtendedStrut(wid, - /* Left */ 0, 0, 0, - /* Right */ 0, 0, 0, - /* Top */ rect.top() + getReserveDimension(), rect.left(), rect.right(), - /* Bottom */ 0, 0, 0 - ); - break; + const QRect wholeScreen = QApplication::primaryScreen()->virtualGeometry(); + const QRect rect = geometry(); + // NOTE: https://standards.freedesktop.org/wm-spec/wm-spec-latest.html + // Quote from the EWMH spec: " Note that the strut is relative to the screen edge, and not the edge of the xinerama monitor." + // So, we use the geometry of the whole screen to calculate the strut rather than using the geometry of individual monitors. + // Though the spec only mention Xinerama and did not mention XRandR, the rule should still be applied. + // At least openbox is implemented like this. + switch (mPosition) + { + case LXQtPanel::PositionTop: + KX11Extras::setExtendedStrut(wid, + /* Left */ 0, 0, 0, + /* Right */ 0, 0, 0, + /* Top */ rect.top() + getReserveDimension(), rect.left(), rect.right(), + /* Bottom */ 0, 0, 0 + ); + break; - case LXQtPanel::PositionBottom: - KX11Extras::setExtendedStrut(wid, - /* Left */ 0, 0, 0, - /* Right */ 0, 0, 0, - /* Top */ 0, 0, 0, - /* Bottom */ wholeScreen.bottom() - rect.bottom() + getReserveDimension(), rect.left(), rect.right() - ); - break; + case LXQtPanel::PositionBottom: + KX11Extras::setExtendedStrut(wid, + /* Left */ 0, 0, 0, + /* Right */ 0, 0, 0, + /* Top */ 0, 0, 0, + /* Bottom */ wholeScreen.bottom() - rect.bottom() + getReserveDimension(), rect.left(), rect.right() + ); + break; - case LXQtPanel::PositionLeft: - KX11Extras::setExtendedStrut(wid, - /* Left */ rect.left() + getReserveDimension(), rect.top(), rect.bottom(), - /* Right */ 0, 0, 0, - /* Top */ 0, 0, 0, - /* Bottom */ 0, 0, 0 - ); + case LXQtPanel::PositionLeft: + KX11Extras::setExtendedStrut(wid, + /* Left */ rect.left() + getReserveDimension(), rect.top(), rect.bottom(), + /* Right */ 0, 0, 0, + /* Top */ 0, 0, 0, + /* Bottom */ 0, 0, 0 + ); - break; + break; - case LXQtPanel::PositionRight: + case LXQtPanel::PositionRight: + KX11Extras::setExtendedStrut(wid, + /* Left */ 0, 0, 0, + /* Right */ wholeScreen.right() - rect.right() + getReserveDimension(), rect.top(), rect.bottom(), + /* Top */ 0, 0, 0, + /* Bottom */ 0, 0, 0 + ); + break; + } + } else + { KX11Extras::setExtendedStrut(wid, - /* Left */ 0, 0, 0, - /* Right */ wholeScreen.right() - rect.right() + getReserveDimension(), rect.top(), rect.bottom(), - /* Top */ 0, 0, 0, - /* Bottom */ 0, 0, 0 - ); - break; + /* Left */ 0, 0, 0, + /* Right */ 0, 0, 0, + /* Top */ 0, 0, 0, + /* Bottom */ 0, 0, 0 + ); + } } - } else + else if(qGuiApp->nativeInterface()) { - KX11Extras::setExtendedStrut(wid, - /* Left */ 0, 0, 0, - /* Right */ 0, 0, 0, - /* Top */ 0, 0, 0, - /* Bottom */ 0, 0, 0 - ); + if (mReserveSpace) + { + switch (mPosition) + { + case LXQtPanel::PositionTop: + mLayerWindow->setExclusiveEdge(LayerShellQt::Window::AnchorTop); + mLayerWindow->setExclusiveZone(height()); + break; + + case LXQtPanel::PositionBottom: + mLayerWindow->setExclusiveEdge(LayerShellQt::Window::AnchorBottom); + mLayerWindow->setExclusiveZone(height()); + break; + + case LXQtPanel::PositionLeft: + mLayerWindow->setExclusiveEdge(LayerShellQt::Window::AnchorLeft); + mLayerWindow->setExclusiveZone(width()); + break; + + case LXQtPanel::PositionRight: + mLayerWindow->setExclusiveEdge(LayerShellQt::Window::AnchorRight); + mLayerWindow->setExclusiveZone(width()); + break; + } + } else + { + mLayerWindow->setExclusiveEdge(LayerShellQt::Window::AnchorNone); + mLayerWindow->setExclusiveZone(0); + } } } diff --git a/panel/lxqtpanel.h b/panel/lxqtpanel.h index a3a061a53..ed952dbdb 100644 --- a/panel/lxqtpanel.h +++ b/panel/lxqtpanel.h @@ -42,6 +42,10 @@ class QMenu; class Plugin; class QAbstractItemModel; +namespace LayerShellQt { +class Window; +} + namespace LXQt { class Settings; class PluginInfo; @@ -689,6 +693,8 @@ private slots: */ QPropertyAnimation *mAnimation; + LayerShellQt::Window *mLayerWindow; + /** * @brief Flag for providing the configuration options in panel's context menu */ From fc293643fe5f162b6e27f4192809d56aec05da73 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 11:44:11 +0100 Subject: [PATCH 043/165] LXQtPanel: fix position not applied immediatly on Wayland --- panel/lxqtpanel.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/panel/lxqtpanel.cpp b/panel/lxqtpanel.cpp index ebadfe0d3..529b74a61 100644 --- a/panel/lxqtpanel.cpp +++ b/panel/lxqtpanel.cpp @@ -652,6 +652,10 @@ void LXQtPanel::setPanelGeometry(bool animate) setGeometry(rect); } } + + // Make LayerShell apply changes immediatly + if(windowHandle()) + windowHandle()->requestUpdate(); } void LXQtPanel::setMargins() From 01266536933bd7195202e444b4c0644589ebb283 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 12:10:02 +0100 Subject: [PATCH 044/165] LXQtPanel: partially fix alignment on Wayland TODO TODO: after changing length to pixels and back to percent alignment is ignored and always kept to right --- panel/lxqtpanel.cpp | 49 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/panel/lxqtpanel.cpp b/panel/lxqtpanel.cpp index 529b74a61..336f1f4cd 100644 --- a/panel/lxqtpanel.cpp +++ b/panel/lxqtpanel.cpp @@ -512,6 +512,7 @@ void LXQtPanel::setPanelGeometry(bool animate) QRect rect; LayerShellQt::Window::Anchors anchors; + LayerShellQt::Window::Anchor edge; if (isHorizontal()) { @@ -533,6 +534,7 @@ void LXQtPanel::setPanelGeometry(bool animate) switch (mAlignment) { case LXQtPanel::AlignmentLeft: + anchors.setFlag(LayerShellQt::Window::AnchorLeft); rect.moveLeft(currentScreen.left()); break; @@ -541,16 +543,24 @@ void LXQtPanel::setPanelGeometry(bool animate) break; case LXQtPanel::AlignmentRight: + anchors.setFlag(LayerShellQt::Window::AnchorRight); rect.moveRight(currentScreen.right()); break; } + if(lengthInPercents() && mLength == 100) + { + //Fill all available width + anchors.setFlag(LayerShellQt::Window::AnchorLeft); + anchors.setFlag(LayerShellQt::Window::AnchorRight); + } + // Vert ....................... if (mPosition == ILXQtPanel::PositionTop) { anchors.setFlag(LayerShellQt::Window::AnchorTop); - anchors.setFlag(LayerShellQt::Window::AnchorLeft); - anchors.setFlag(LayerShellQt::Window::AnchorRight); + edge = LayerShellQt::Window::AnchorTop; + if (mHidden) rect.moveBottom(currentScreen.top() + PANEL_HIDE_SIZE - 1); else @@ -559,8 +569,8 @@ void LXQtPanel::setPanelGeometry(bool animate) else { anchors.setFlag(LayerShellQt::Window::AnchorBottom); - anchors.setFlag(LayerShellQt::Window::AnchorLeft); - anchors.setFlag(LayerShellQt::Window::AnchorRight); + edge = LayerShellQt::Window::AnchorBottom; + if (mHidden) rect.moveTop(currentScreen.bottom() - PANEL_HIDE_SIZE + 1); else @@ -587,6 +597,7 @@ void LXQtPanel::setPanelGeometry(bool animate) switch (mAlignment) { case LXQtPanel::AlignmentLeft: + anchors.setFlag(LayerShellQt::Window::AnchorTop); rect.moveTop(currentScreen.top()); break; @@ -595,16 +606,24 @@ void LXQtPanel::setPanelGeometry(bool animate) break; case LXQtPanel::AlignmentRight: + anchors.setFlag(LayerShellQt::Window::AnchorBottom); rect.moveBottom(currentScreen.bottom()); break; } + if(lengthInPercents() && mLength == 100) + { + //Fill all available width + anchors.setFlag(LayerShellQt::Window::AnchorTop); + anchors.setFlag(LayerShellQt::Window::AnchorBottom); + } + // Horiz ...................... if (mPosition == ILXQtPanel::PositionLeft) { - anchors.setFlag(LayerShellQt::Window::AnchorTop); anchors.setFlag(LayerShellQt::Window::AnchorLeft); - anchors.setFlag(LayerShellQt::Window::AnchorBottom); + edge = LayerShellQt::Window::AnchorLeft; + if (mHidden) rect.moveRight(currentScreen.left() + PANEL_HIDE_SIZE - 1); else @@ -612,9 +631,9 @@ void LXQtPanel::setPanelGeometry(bool animate) } else { - anchors.setFlag(LayerShellQt::Window::AnchorTop); anchors.setFlag(LayerShellQt::Window::AnchorRight); - anchors.setFlag(LayerShellQt::Window::AnchorBottom); + edge = LayerShellQt::Window::AnchorRight; + if (mHidden) rect.moveLeft(currentScreen.right() - PANEL_HIDE_SIZE + 1); else @@ -623,7 +642,13 @@ void LXQtPanel::setPanelGeometry(bool animate) } if(mLayerWindow) + { mLayerWindow->setAnchors(anchors); + mLayerWindow->setExclusiveEdge(edge); + + // Make LayerShell apply changes immediatly + windowHandle()->requestUpdate(); + } if (!mHidden || !mGeometry.isValid()) mGeometry = rect; if (rect != geometry()) @@ -652,10 +677,6 @@ void LXQtPanel::setPanelGeometry(bool animate) setGeometry(rect); } } - - // Make LayerShell apply changes immediatly - if(windowHandle()) - windowHandle()->requestUpdate(); } void LXQtPanel::setMargins() @@ -779,6 +800,8 @@ void LXQtPanel::updateWmStrut() } else if(qGuiApp->nativeInterface()) { + //TODO: duplicated code, also set in setPanelGeometry() + if (mReserveSpace) { switch (mPosition) @@ -808,6 +831,8 @@ void LXQtPanel::updateWmStrut() mLayerWindow->setExclusiveEdge(LayerShellQt::Window::AnchorNone); mLayerWindow->setExclusiveZone(0); } + + windowHandle()->requestUpdate(); } } From cbd9a26743dc9394fbaf23dcaeb0ddfe4b1f4031 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 14:19:31 +0100 Subject: [PATCH 045/165] Add desktop file to be recognized by KWin Wayland NOTE: absolute path is needed inside .desktop file for this to work --- autostart/CMakeLists.txt | 7 +++++++ autostart/lxqt-panel_wayland.desktop | 13 +++++++++++++ panel/lxqtpanelapplication.cpp | 2 ++ 3 files changed, 22 insertions(+) create mode 100644 autostart/lxqt-panel_wayland.desktop diff --git a/autostart/CMakeLists.txt b/autostart/CMakeLists.txt index 098103168..2c9279a93 100644 --- a/autostart/CMakeLists.txt +++ b/autostart/CMakeLists.txt @@ -14,3 +14,10 @@ install(FILES DESTINATION "${LXQT_ETC_XDG_DIR}/autostart" COMPONENT Runtime ) + +install(FILES + lxqt-panel_wayland.desktop + DESTINATION "/usr/share/applications" + RENAME "lxqt-panel.desktop" + COMPONENT Runtime +) diff --git a/autostart/lxqt-panel_wayland.desktop b/autostart/lxqt-panel_wayland.desktop new file mode 100644 index 000000000..5b03e4120 --- /dev/null +++ b/autostart/lxqt-panel_wayland.desktop @@ -0,0 +1,13 @@ +[Desktop Entry] +Type=Application +TryExec=lxqt-panel + +# NOTE: KWin wants absolute path here, make sure it's correct +Exec=/usr/local/bin/lxqt-panel + +# NOTE: adding KDE to make it work under Plasma Wayland session +OnlyShowIn=LXQt;KDE +X-LXQt-Module=true + +# Make KWin recognize us as priviledged client +X-KDE-Wayland-Interfaces=org_kde_plasma_window_management diff --git a/panel/lxqtpanelapplication.cpp b/panel/lxqtpanelapplication.cpp index 507163221..a3d2132f1 100644 --- a/panel/lxqtpanelapplication.cpp +++ b/panel/lxqtpanelapplication.cpp @@ -107,6 +107,8 @@ LXQtPanelApplication::LXQtPanelApplication(int& argc, char** argv) QCoreApplication::setApplicationVersion(VERINFO); + QGuiApplication::setDesktopFileName(QLatin1String("lxqt-panel")); + QCommandLineParser parser; parser.setApplicationDescription(QLatin1String("LXQt Panel")); parser.addHelpOption(); From 81f5e1d51143ae2364e8fe3fe542b45713d4e4af Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 18:01:49 +0100 Subject: [PATCH 046/165] LXQtTaskbarWaylandBackend: return only accepted windows - reloadWindows() force removal and readding of windows This fixes changing windows grouping settings and adding taskbar plugin AFTER panel is started. Both situations resulted in empty taskbar previously --- .../wayland/lxqttaskbarbackendwayland.cpp | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index 57b77c906..27e6b97e5 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -97,15 +97,30 @@ bool LXQtTaskbarWaylandBackend::supportsAction(WId windowId, LXQtTaskBarBackendA bool LXQtTaskbarWaylandBackend::reloadWindows() { - return false; //TODO + const QVector wids = getCurrentWindows(); + + // Force removal and re-adding + for(WId windowId : wids) + { + emit windowRemoved(windowId); + } + for(WId windowId : wids) + { + emit windowAdded(windowId); + } + + return true; } QVector LXQtTaskbarWaylandBackend::getCurrentWindows() const { - QVector wids(windows.size()); - for(const auto& window : std::as_const(windows)) + QVector wids; + wids.reserve(wids.size()); + + for(const std::unique_ptr& window : std::as_const(windows)) { - wids << window->getWindowId(); + if(window->acceptedInTaskBar) + wids << window->getWindowId(); } return wids; } From 076d36a63a33a6bdc598fb50cab309c3bf661ccb Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 22:37:49 +0100 Subject: [PATCH 047/165] LXQtPanel: fix auto-hide on Wayland --- panel/lxqtpanel.cpp | 52 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/panel/lxqtpanel.cpp b/panel/lxqtpanel.cpp index 336f1f4cd..dfb91b3fa 100644 --- a/panel/lxqtpanel.cpp +++ b/panel/lxqtpanel.cpp @@ -677,6 +677,37 @@ void LXQtPanel::setPanelGeometry(bool animate) setGeometry(rect); } } + + if(mLayerWindow) + { + // Emulate auto-hide on Wayland + // NOTE: we cannot move window out of screen so we make it smaller + + // NOTE: a cleaner approach would be to use screen edge protocol + // but it's specific to KWin + + if(mHidden && LXQtPanelWidget->isVisible()) + { + // Make it blank + LXQtPanelWidget->hide(); + + // And make it small + if(isHorizontal()) + resize(rect.width(), PANEL_HIDE_SIZE); + else + resize(PANEL_HIDE_SIZE, rect.height()); + } + else if(!mHidden && !LXQtPanelWidget->isVisible()) + { + // Restore contents + LXQtPanelWidget->show(); + + // And make it big again + resize(rect.size()); + } + + updateWmStrut(); + } } void LXQtPanel::setMargins() @@ -804,34 +835,37 @@ void LXQtPanel::updateWmStrut() if (mReserveSpace) { + LayerShellQt::Window::Anchor edge; + switch (mPosition) { case LXQtPanel::PositionTop: - mLayerWindow->setExclusiveEdge(LayerShellQt::Window::AnchorTop); - mLayerWindow->setExclusiveZone(height()); + edge = LayerShellQt::Window::AnchorTop; break; case LXQtPanel::PositionBottom: - mLayerWindow->setExclusiveEdge(LayerShellQt::Window::AnchorBottom); - mLayerWindow->setExclusiveZone(height()); + edge = LayerShellQt::Window::AnchorBottom; break; case LXQtPanel::PositionLeft: - mLayerWindow->setExclusiveEdge(LayerShellQt::Window::AnchorLeft); - mLayerWindow->setExclusiveZone(width()); + edge = LayerShellQt::Window::AnchorLeft; break; case LXQtPanel::PositionRight: - mLayerWindow->setExclusiveEdge(LayerShellQt::Window::AnchorRight); - mLayerWindow->setExclusiveZone(width()); + edge = LayerShellQt::Window::AnchorRight; break; } - } else + + mLayerWindow->setExclusiveEdge(edge); + mLayerWindow->setExclusiveZone(getReserveDimension()); + } + else { mLayerWindow->setExclusiveEdge(LayerShellQt::Window::AnchorNone); mLayerWindow->setExclusiveZone(0); } + // Make LayerShell apply changes immediatly windowHandle()->requestUpdate(); } } From 5b22d8d6cbf6b16b4e9211ddf02523073eb65a1b Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 23 Feb 2024 12:27:57 +0100 Subject: [PATCH 048/165] LXQtTaskbarWaylandBackend: fix workspace logic --- .../wayland/lxqtplasmavirtualdesktop.cpp | 31 ++++++++++++++----- .../wayland/lxqtplasmavirtualdesktop.h | 8 +++-- .../wayland/lxqttaskbarbackendwayland.cpp | 20 ++++++++---- 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp index a3d987f06..fe3d3a435 100644 --- a/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp +++ b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp @@ -16,6 +16,7 @@ LXQtPlasmaVirtualDesktop::~LXQtPlasmaVirtualDesktop() void LXQtPlasmaVirtualDesktop::org_kde_plasma_virtual_desktop_name(const QString &name) { this->name = name; + Q_EMIT nameChanged(); } void LXQtPlasmaVirtualDesktop::org_kde_plasma_virtual_desktop_done() @@ -73,6 +74,20 @@ LXQtPlasmaWaylandWorkspaceInfo::VirtualDesktopsIterator LXQtPlasmaWaylandWorkspa }); } +QString LXQtPlasmaWaylandWorkspaceInfo::getDesktopName(int pos) const +{ + if(pos < 0 || pos >= virtualDesktops.size()) + return QString(); + return virtualDesktops[pos]->name; +} + +QString LXQtPlasmaWaylandWorkspaceInfo::getDesktopId(int pos) const +{ + if(pos < 0 || pos >= virtualDesktops.size()) + return QString(); + return virtualDesktops[pos]->id; +} + void LXQtPlasmaWaylandWorkspaceInfo::init() { virtualDesktopManagement = std::make_unique(); @@ -86,7 +101,6 @@ void LXQtPlasmaWaylandWorkspaceInfo::init() Q_EMIT numberOfDesktopsChanged(); Q_EMIT navigationWrappingAroundChanged(); Q_EMIT desktopIdsChanged(); - Q_EMIT desktopNamesChanged(); Q_EMIT desktopLayoutRowsChanged(); } }); @@ -105,7 +119,6 @@ void LXQtPlasmaWaylandWorkspaceInfo::init() Q_EMIT numberOfDesktopsChanged(); Q_EMIT desktopIdsChanged(); - Q_EMIT desktopNamesChanged(); if (currentVirtualDesktop == id) { currentVirtualDesktop.clear(); @@ -119,7 +132,7 @@ void LXQtPlasmaWaylandWorkspaceInfo::init() }); } -void LXQtPlasmaWaylandWorkspaceInfo::addDesktop(const QString &id, quint32 position) +void LXQtPlasmaWaylandWorkspaceInfo::addDesktop(const QString &id, quint32 pos) { if (findDesktop(id) != virtualDesktops.end()) { return; @@ -132,15 +145,19 @@ void LXQtPlasmaWaylandWorkspaceInfo::addDesktop(const QString &id, quint32 posit Q_EMIT currentDesktopChanged(); }); - connect(desktop.get(), &LXQtPlasmaVirtualDesktop::done, this, [this]() { - Q_EMIT desktopNamesChanged(); + connect(desktop.get(), &LXQtPlasmaVirtualDesktop::nameChanged, this, [id, this]() { + Q_EMIT desktopNameChanged(position(id)); + }); + + connect(desktop.get(), &LXQtPlasmaVirtualDesktop::done, this, [id, this]() { + Q_EMIT desktopNameChanged(position(id)); }); - virtualDesktops.insert(std::next(virtualDesktops.begin(), position), std::move(desktop)); + virtualDesktops.insert(std::next(virtualDesktops.begin(), pos), std::move(desktop)); Q_EMIT numberOfDesktopsChanged(); Q_EMIT desktopIdsChanged(); - Q_EMIT desktopNamesChanged(); + Q_EMIT desktopNameChanged(position(id)); } QVariant LXQtPlasmaWaylandWorkspaceInfo::currentDesktop() const diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.h b/panel/backends/wayland/lxqtplasmavirtualdesktop.h index 3dabb6242..fff025213 100644 --- a/panel/backends/wayland/lxqtplasmavirtualdesktop.h +++ b/panel/backends/wayland/lxqtplasmavirtualdesktop.h @@ -19,6 +19,7 @@ class LXQtPlasmaVirtualDesktop : public QObject, public QtWayland::org_kde_plasm Q_SIGNALS: void done(); void activated(); + void nameChanged(); protected: void org_kde_plasma_virtual_desktop_name(const QString &name) override; @@ -63,8 +64,11 @@ class Q_DECL_HIDDEN LXQtPlasmaWaylandWorkspaceInfo : public QObject VirtualDesktopsIterator findDesktop(const QString &id) const; + QString getDesktopName(int pos) const; + QString getDesktopId(int pos) const; + void init(); - void addDesktop(const QString &id, quint32 position); + void addDesktop(const QString &id, quint32 pos); QVariant currentDesktop() const; int numberOfDesktops() const; QVariantList desktopIds() const; @@ -80,7 +84,7 @@ class Q_DECL_HIDDEN LXQtPlasmaWaylandWorkspaceInfo : public QObject void numberOfDesktopsChanged(); void navigationWrappingAroundChanged(); void desktopIdsChanged(); - void desktopNamesChanged(); + void desktopNameChanged(quint32 position); void desktopLayoutRowsChanged(); }; diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index 27e6b97e5..8aa214c65 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -45,13 +45,17 @@ LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) : connect(m_workspaceInfo.get(), &LXQtPlasmaWaylandWorkspaceInfo::currentDesktopChanged, this, [this](){ int idx = m_workspaceInfo->position(m_workspaceInfo->currentDesktop()); + idx += 1; // Make 1-based emit currentWorkspaceChanged(idx); }); connect(m_workspaceInfo.get(), &LXQtPlasmaWaylandWorkspaceInfo::numberOfDesktopsChanged, this, &ILXQtTaskbarAbstractBackend::workspacesCountChanged); - //TODO: connect name changed + connect(m_workspaceInfo.get(), &LXQtPlasmaWaylandWorkspaceInfo::desktopNameChanged, + this, [this](int idx) { + emit workspaceNameChanged(idx + 1); // Make 1-based + }); } bool LXQtTaskbarWaylandBackend::supportsAction(WId windowId, LXQtTaskBarBackendAction action) const @@ -324,19 +328,23 @@ int LXQtTaskbarWaylandBackend::getWorkspacesCount() const QString LXQtTaskbarWaylandBackend::getWorkspaceName(int idx) const { - //TODO: optimize - return m_workspaceInfo->desktopNames().value(idx, QStringLiteral("ERROR")); + return m_workspaceInfo->getDesktopName(idx - 1); //Return to 0-based } int LXQtTaskbarWaylandBackend::getCurrentWorkspace() const { - return 0; //TODO + if(!m_workspaceInfo->currentDesktop().isValid()) + return 0; + return m_workspaceInfo->position(m_workspaceInfo->currentDesktop()) + 1; // 1-based } bool LXQtTaskbarWaylandBackend::setCurrentWorkspace(int idx) { - Q_UNUSED(idx) - return false; //TODO + QString id = m_workspaceInfo->getDesktopId(idx - 1); + if(id.isEmpty()) + return false; + m_workspaceInfo->requestActivate(id); + return true; } int LXQtTaskbarWaylandBackend::getWindowWorkspace(WId windowId) const From bc1c267a98bbede280f029562d058dfe469d858c Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 23 Feb 2024 12:58:54 +0100 Subject: [PATCH 049/165] LXQtTaskbarWaylandBackend: fix workspace removal logic --- panel/backends/wayland/lxqtplasmavirtualdesktop.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp index fe3d3a435..b3e49a681 100644 --- a/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp +++ b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp @@ -111,11 +111,12 @@ void LXQtPlasmaWaylandWorkspaceInfo::init() connect(virtualDesktopManagement.get(), &LXQtPlasmaVirtualDesktopManagment::desktopRemoved, this, [this](const QString &id) { - std::remove_if(virtualDesktops.begin(), virtualDesktops.end(), - [id](const std::unique_ptr &desktop) - { - return desktop->id == id; - }); + virtualDesktops.erase(std::remove_if(virtualDesktops.begin(), virtualDesktops.end(), + [id](const std::unique_ptr &desktop) + { + return desktop->id == id; + }), + virtualDesktops.end()); Q_EMIT numberOfDesktopsChanged(); Q_EMIT desktopIdsChanged(); From a28763f4584ac5cf005825f6fbc14aeca50144e0 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 23 Feb 2024 13:33:40 +0100 Subject: [PATCH 050/165] lxqttaskbartypes.h: fix ShowOnAll desktops flag value --- panel/backends/lxqttaskbartypes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panel/backends/lxqttaskbartypes.h b/panel/backends/lxqttaskbartypes.h index 9e12092bb..ae3d5f107 100644 --- a/panel/backends/lxqttaskbartypes.h +++ b/panel/backends/lxqttaskbartypes.h @@ -49,7 +49,7 @@ enum class LXQtTaskBarWindowLayer enum class LXQtTaskBarWorkspace { - ShowOnAll = -1 + ShowOnAll = 0 // Virtual destops have 1-based indexes }; #endif // LXQTTASKBARTYPES_H From 77527ee99453c5292302766cd069c19daf1fcff6 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 23 Feb 2024 13:34:16 +0100 Subject: [PATCH 051/165] LXQtTaskbarWaylandBackend: implement moving window to virtual desktop workspace --- .../wayland/lxqttaskbarbackendwayland.cpp | 51 ++++++++++++++++--- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index 8aa214c65..fb6e940a8 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -340,7 +340,7 @@ int LXQtTaskbarWaylandBackend::getCurrentWorkspace() const bool LXQtTaskbarWaylandBackend::setCurrentWorkspace(int idx) { - QString id = m_workspaceInfo->getDesktopId(idx - 1); + QString id = m_workspaceInfo->getDesktopId(idx - 1); //Return to 0-based if(id.isEmpty()) return false; m_workspaceInfo->requestActivate(id); @@ -349,15 +349,54 @@ bool LXQtTaskbarWaylandBackend::setCurrentWorkspace(int idx) int LXQtTaskbarWaylandBackend::getWindowWorkspace(WId windowId) const { - Q_UNUSED(windowId) - return 0; //TODO + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return 0; + + // TODO: this protocol seems to allow multiple desktop for each window + // We do not support that yet + // Also from KDE Plasma task switch it's not clear how to actually put + // a window on multiple desktops (which is different from "All desktops") + QString id = window->virtualDesktops.value(0, QString()); + if(id.isEmpty()) + return 0; + + return m_workspaceInfo->position(id) + 1; //Make 1-based } bool LXQtTaskbarWaylandBackend::setWindowOnWorkspace(WId windowId, int idx) { - Q_UNUSED(windowId) - Q_UNUSED(idx) - return false; //TODO + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + // Prepare for future multiple virtual desktops per window + QList newDesktops; + + // Fill the list + newDesktops.append(m_workspaceInfo->getDesktopId(idx - 1)); //Return to 0-based + + // Keep only valid IDs + newDesktops.erase(std::remove_if(newDesktops.begin(), newDesktops.end(), + [](const QString& id) { return id.isEmpty(); }), + newDesktops.end()); + + // Add to new requested desktops + for(const QString& id : std::as_const(newDesktops)) + { + if(!window->virtualDesktops.contains(id)) + window->request_enter_virtual_desktop(id); + } + + // Remove from non-requested destops + const QList currentDesktops = window->virtualDesktops; + for(const QString& id : std::as_const(currentDesktops)) + { + if(!newDesktops.contains(id)) + window->request_leave_virtual_desktop(id); + } + + return true; } void LXQtTaskbarWaylandBackend::moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) From a91c1bbe3aba626d7ae60e336d13c24127c96191 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Mon, 26 Feb 2024 09:48:03 +0100 Subject: [PATCH 052/165] ColorPicker: use XDG Desktop Portal on Wayland TODO TODO: show error message when not supported --- plugin-colorpicker/colorpicker.cpp | 156 ++++++++++++++++++++++++++--- plugin-colorpicker/colorpicker.h | 8 ++ 2 files changed, 150 insertions(+), 14 deletions(-) diff --git a/plugin-colorpicker/colorpicker.cpp b/plugin-colorpicker/colorpicker.cpp index 004314557..0827686d5 100644 --- a/plugin-colorpicker/colorpicker.cpp +++ b/plugin-colorpicker/colorpicker.cpp @@ -36,6 +36,9 @@ #include #include +#include +#include + //NOTE: Xlib.h defines Bool which conflicts with QJsonValue::Type enum #include #undef Bool @@ -77,6 +80,33 @@ void ColorPicker::realign() mWidget.update(panel()->lineCount() <= 1 ? panel()->isHorizontal() : !panel()->isHorizontal()); } +void ColorPicker::queryXDGSupport() +{ + if (qEnvironmentVariableIntValue("QT_NO_XDG_DESKTOP_PORTAL") > 0) { + return; + } + QDBusMessage message = QDBusMessage::createMethodCall( + QLatin1String("org.freedesktop.portal.Desktop"), + QLatin1String("/org/freedesktop/portal/desktop"), + QLatin1String("org.freedesktop.DBus.Properties"), + QLatin1String("Get")); + message << QLatin1String("org.freedesktop.portal.Screenshot") + << QLatin1String("version"); + + QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message); + auto watcher = new QDBusPendingCallWatcher(pendingCall); + QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher, + [this](QDBusPendingCallWatcher *watcher) { + watcher->deleteLater(); + QDBusPendingReply reply = *watcher; + if (!reply.isError() && reply.value().toUInt() >= 2) + m_hasScreenshotPortalWithColorPicking = true; + }); + + //TODO: show error tooltip if not supported + //NOTE: on Wayland we cannot pick color without it +} + ColorPickerWidget::ColorPickerWidget(QWidget *parent) : QWidget(parent) { @@ -108,7 +138,8 @@ ColorPickerWidget::ColorPickerWidget(QWidget *parent) : QWidget(parent) layout->addWidget(mColorButton); setLayout(layout); - connect(mPickerButton, &QToolButton::clicked, this, &ColorPickerWidget::captureMouse); + connect(mPickerButton, &QToolButton::clicked, this, &ColorPickerWidget::startCapturingColor); + connect(mColorButton, &QToolButton::clicked, this, [&]() { buildMenu(); @@ -162,29 +193,86 @@ void ColorPickerWidget::mouseReleaseEvent(QMouseEvent *event) qWarning() << "WAYLAND does not support grabbing windows"; } - mColorButton->setColor(col); - paste(col.name()); + setCapturedColor(col); - if (mColorsList.contains(col)) + mCapturing = false; + releaseMouse(); + + if (!mPickerButton->contentsRect().contains(mapFromGlobal(QCursor::pos()))) { - mColorsList.move(mColorsList.indexOf(col), 0); + QApplication::sendEvent(mPickerButton, new QEvent(QEvent::Leave)); } - else +} + +void ColorPickerWidget::startCapturingColor() +{ + //NOTE: see qt6 `src/gui/platform/unix/qgenericunixservices.cpp` + + // Make double sure that we are in a wayland environment. In particular check + // WAYLAND_DISPLAY so also XWayland apps benefit from portal-based color picking. + // Outside wayland we'll rather rely on other means than the XDG desktop portal. + if (!qEnvironmentVariableIsEmpty("WAYLAND_DISPLAY") + || QGuiApplication::platformName().startsWith(QLatin1String("wayland"))) { - mColorsList.prepend(col); + // On Wayland use XDG Desktop Portal + + QString m_parentWindowId; //TODO + + QDBusMessage message = QDBusMessage::createMethodCall( + QLatin1String("org.freedesktop.portal.Desktop"), + QLatin1String("/org/freedesktop/portal/desktop"), + QLatin1String("org.freedesktop.portal.Screenshot"), + QLatin1String("PickColor")); + message << m_parentWindowId << QVariantMap(); + + QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message); + auto watcher = new QDBusPendingCallWatcher(pendingCall, this); + connect(watcher, &QDBusPendingCallWatcher::finished, this, + [this](QDBusPendingCallWatcher *watcher) { + watcher->deleteLater(); + QDBusPendingReply reply = *watcher; + if (reply.isError()) { + qWarning("DBus call to pick color failed: %s", + qPrintable(reply.error().message())); + setCapturedColor({}); + } else { + QDBusConnection::sessionBus().connect( + QLatin1String("org.freedesktop.portal.Desktop"), + reply.value().path(), + QLatin1String("org.freedesktop.portal.Request"), + QLatin1String("Response"), + this, + // clang-format off + SLOT(gotColorResponse(uint,QVariantMap)) + // clang-format on + ); + } + }); } - - if (mColorsList.size() > 10) + else if (qGuiApp->nativeInterface()) { - mColorsList.removeLast(); + // On X11 grab mouse and let `mouseReleaseEvent()` retrieve color + captureMouse(); } +} - mCapturing = false; - releaseMouse(); +void ColorPickerWidget::setCapturedColor(const QColor &color) +{ + mColorButton->setColor(color); + paste(color.name()); - if (!mPickerButton->contentsRect().contains(mapFromGlobal(QCursor::pos()))) + if (mColorsList.contains(color)) { - QApplication::sendEvent(mPickerButton, new QEvent(QEvent::Leave)); + mColorsList.move(mColorsList.indexOf(color), 0); + } + else + { + mColorsList.prepend(color); + } + + if (mColorsList.size() > 10) + { + mColorsList.removeLast(); } } @@ -195,6 +283,46 @@ void ColorPickerWidget::captureMouse() mCapturing = true; } +struct XDGDesktopColor +{ + double r = 0; + double g = 0; + double b = 0; + + QColor toQColor() const + { + constexpr auto rgbMax = 255; + return { static_cast(r * rgbMax), static_cast(g * rgbMax), + static_cast(b * rgbMax) }; + } +}; + +const QDBusArgument &operator>>(const QDBusArgument &argument, XDGDesktopColor &myStruct) +{ + argument.beginStructure(); + argument >> myStruct.r >> myStruct.g >> myStruct.b; + argument.endStructure(); + return argument; +} + +void ColorPickerWidget::gotColorResponse(uint result, const QVariantMap &map) +{ + auto colorProp = QStringLiteral("color"); + + if (result != 0) + return; + if (map.contains(colorProp)) + { + XDGDesktopColor color{}; + map.value(colorProp).value() >> color; + setCapturedColor(color.toQColor()); + } + else + { + setCapturedColor({}); + } +} + QIcon ColorPickerWidget::colorIcon(QColor color) { diff --git a/plugin-colorpicker/colorpicker.h b/plugin-colorpicker/colorpicker.h index 919f42490..ecf2a3b41 100644 --- a/plugin-colorpicker/colorpicker.h +++ b/plugin-colorpicker/colorpicker.h @@ -58,7 +58,10 @@ class ColorPickerWidget : public QWidget void mouseReleaseEvent(QMouseEvent *event); private slots: + void startCapturingColor(); + void setCapturedColor(const QColor& color); void captureMouse(); + void gotColorResponse(uint result, const QVariantMap& map); private: static const QString svgIcon; @@ -91,8 +94,13 @@ class ColorPicker : public QObject, public ILXQtPanelPlugin virtual void realign() override; +private: + void queryXDGSupport(); + private: ColorPickerWidget mWidget; + + bool m_hasScreenshotPortalWithColorPicking = false; }; class ColorPickerLibrary: public QObject, public ILXQtPanelPluginLibrary From c52d6a5989578de215edb7cf0cfc80c8c534d045 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Wed, 28 Feb 2024 20:43:50 +0100 Subject: [PATCH 053/165] LXQtPlasmaWaylandWorkspaceInfo: fix signedness comparison --- panel/backends/wayland/lxqtplasmavirtualdesktop.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp index b3e49a681..01e28fe75 100644 --- a/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp +++ b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp @@ -76,14 +76,14 @@ LXQtPlasmaWaylandWorkspaceInfo::VirtualDesktopsIterator LXQtPlasmaWaylandWorkspa QString LXQtPlasmaWaylandWorkspaceInfo::getDesktopName(int pos) const { - if(pos < 0 || pos >= virtualDesktops.size()) + if(pos < 0 || size_t(pos) >= virtualDesktops.size()) return QString(); return virtualDesktops[pos]->name; } QString LXQtPlasmaWaylandWorkspaceInfo::getDesktopId(int pos) const { - if(pos < 0 || pos >= virtualDesktops.size()) + if(pos < 0 || size_t(pos) >= virtualDesktops.size()) return QString(); return virtualDesktops[pos]->id; } From 94cb67a69e756f03dc8f8b61ec76a470e47b17cc Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 29 Feb 2024 00:09:55 +0100 Subject: [PATCH 054/165] CMake: move panel WM backends to separate libraries - QTime is included to fix build errors --- panel/CMakeLists.txt | 47 +++---------------- panel/backends/CMakeLists.txt | 17 +++++++ .../backends/ilxqttaskbarabstractbackend.cpp | 2 +- panel/backends/wayland/CMakeLists.txt | 31 ++++++++++++ .../wayland/lxqtplasmavirtualdesktop.h | 1 + .../lxqttaskbarplasmawindowmanagment.cpp | 2 - panel/backends/xcb/CMakeLists.txt | 13 +++++ 7 files changed, 70 insertions(+), 43 deletions(-) diff --git a/panel/CMakeLists.txt b/panel/CMakeLists.txt index d1008d561..ef1291af5 100644 --- a/panel/CMakeLists.txt +++ b/panel/CMakeLists.txt @@ -1,10 +1,13 @@ set(PROJECT lxqt-panel) -# TODO +# Window Manager abstraction backend add_subdirectory(backends) -# TODO: move to backend folder -find_package(Qt6 ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS WaylandClient Concurrent) +# TODO: allow compile time selection via CMake variables +set(PANEL_BACKENDS + lxqt-panel-backend-wayland + lxqt-panel-backend-xcb +) set(PRIV_HEADERS @@ -25,17 +28,6 @@ set(PRIV_HEADERS config/configstyling.h config/configpluginswidget.h config/addplugindialog.h - - backends/ilxqttaskbarabstractbackend.h - backends/lxqttaskbartypes.h - - backends/lxqttaskbardummybackend.h - - backends/xcb/lxqttaskbarbackend_x11.h - - backends/wayland/lxqttaskbarbackendwayland.h - backends/wayland/lxqttaskbarplasmawindowmanagment.h - backends/wayland/lxqtplasmavirtualdesktop.h ) # using LXQt namespace in the public headers. @@ -44,9 +36,6 @@ set(PUB_HEADERS pluginsettings.h ilxqtpanelplugin.h ilxqtpanel.h - - backends/ilxqttaskbarabstractbackend.h - backends/lxqttaskbartypes.h ) set(SOURCES @@ -66,16 +55,6 @@ set(SOURCES config/configstyling.cpp config/configpluginswidget.cpp config/addplugindialog.cpp - - backends/ilxqttaskbarabstractbackend.cpp - - backends/lxqttaskbardummybackend.cpp - - backends/xcb/lxqttaskbarbackend_x11.cpp - - backends/wayland/lxqttaskbarbackendwayland.cpp - backends/wayland/lxqttaskbarplasmawindowmanagment.cpp - backends/wayland/lxqtplasmavirtualdesktop.cpp ) set(UI @@ -130,23 +109,11 @@ add_executable(${PROJECT} ${UI} ) -# TODO: move to backend folder -qt6_generate_wayland_protocol_client_sources(${PROJECT} -FILES - ${CMAKE_CURRENT_SOURCE_DIR}/backends/wayland/protocols/plasma-window-management.xml -) - -qt6_generate_wayland_protocol_client_sources(${PROJECT} -FILES - ${CMAKE_CURRENT_SOURCE_DIR}/backends/wayland/protocols/org-kde-plasma-virtual-desktop.xml -) - target_link_libraries(${PROJECT} ${LIBRARIES} ${QTX_LIBRARIES} KF6::WindowSystem - Qt6::WaylandClient # TODO: Move to backend folder - Qt6::Concurrent + ${PANEL_BACKENDS} LayerShellQt::Interface ${STATIC_PLUGINS} ) diff --git a/panel/backends/CMakeLists.txt b/panel/backends/CMakeLists.txt index f1915b823..c369eb81f 100644 --- a/panel/backends/CMakeLists.txt +++ b/panel/backends/CMakeLists.txt @@ -1,2 +1,19 @@ +# Common interface for Window Manager abstraction backend +# This also contains dummy backend + +add_library(lxqt-panel-backend-common STATIC + + lxqttaskbartypes.h + ilxqttaskbarabstractbackend.h + ilxqttaskbarabstractbackend.cpp + + lxqttaskbardummybackend.h + lxqttaskbardummybackend.cpp +) + +target_link_libraries(lxqt-panel-backend-common + Qt6::Gui +) + add_subdirectory(wayland) add_subdirectory(xcb) diff --git a/panel/backends/ilxqttaskbarabstractbackend.cpp b/panel/backends/ilxqttaskbarabstractbackend.cpp index 137728263..dbad943d5 100644 --- a/panel/backends/ilxqttaskbarabstractbackend.cpp +++ b/panel/backends/ilxqttaskbarabstractbackend.cpp @@ -1,4 +1,4 @@ -#include "../panel/backends/ilxqttaskbarabstractbackend.h" +#include "ilxqttaskbarabstractbackend.h" ILXQtTaskbarAbstractBackend::ILXQtTaskbarAbstractBackend(QObject *parent) diff --git a/panel/backends/wayland/CMakeLists.txt b/panel/backends/wayland/CMakeLists.txt index 8b1378917..e0ecab6e9 100644 --- a/panel/backends/wayland/CMakeLists.txt +++ b/panel/backends/wayland/CMakeLists.txt @@ -1 +1,32 @@ +# Wayland WM Backend +project(lxqt-panel-backend-wayland) + +find_package(Qt6 ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS WaylandClient Concurrent) + +add_library(lxqt-panel-backend-wayland STATIC + lxqttaskbarbackendwayland.h + lxqttaskbarplasmawindowmanagment.h + lxqtplasmavirtualdesktop.h + + lxqttaskbarbackendwayland.cpp + lxqttaskbarplasmawindowmanagment.cpp + lxqtplasmavirtualdesktop.cpp +) + +qt6_generate_wayland_protocol_client_sources(lxqt-panel-backend-wayland +FILES + ${CMAKE_CURRENT_SOURCE_DIR}/protocols/plasma-window-management.xml +) + +qt6_generate_wayland_protocol_client_sources(lxqt-panel-backend-wayland +FILES + ${CMAKE_CURRENT_SOURCE_DIR}/protocols/org-kde-plasma-virtual-desktop.xml +) + +target_link_libraries(lxqt-panel-backend-wayland + Qt6::GuiPrivate + Qt6::WaylandClient + Qt6::Concurrent + lxqt-panel-backend-common +) diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.h b/panel/backends/wayland/lxqtplasmavirtualdesktop.h index fff025213..65324e013 100644 --- a/panel/backends/wayland/lxqtplasmavirtualdesktop.h +++ b/panel/backends/wayland/lxqtplasmavirtualdesktop.h @@ -1,6 +1,7 @@ #ifndef LXQTPLASMAVIRTUALDESKTOP_H #define LXQTPLASMAVIRTUALDESKTOP_H +#include // TODO: needed to fix compile errors about QHashPrivate #include #include diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp index f813181ed..b4339dbb0 100644 --- a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp +++ b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp @@ -1,7 +1,5 @@ #include "lxqttaskbarplasmawindowmanagment.h" -#include - #include #include #include diff --git a/panel/backends/xcb/CMakeLists.txt b/panel/backends/xcb/CMakeLists.txt index 8b1378917..ad36ccaa3 100644 --- a/panel/backends/xcb/CMakeLists.txt +++ b/panel/backends/xcb/CMakeLists.txt @@ -1 +1,14 @@ +# XCB WM Backend + +project(lxqt-panel-backend-xcb) + +add_library(lxqt-panel-backend-xcb STATIC + lxqttaskbarbackend_x11.h + lxqttaskbarbackend_x11.cpp +) + +target_link_libraries(lxqt-panel-backend-xcb + KF6::WindowSystem + lxqt-panel-backend-common +) From 35705e3ee00656c00c1b2e8f0bc0ec0a24d67031 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 29 Feb 2024 10:50:31 +0100 Subject: [PATCH 055/165] LXQtTaskbarWaylandBackend: fix QTime inclusion TODO Now it's fixed corretly TODO: merge with previous commit --- panel/backends/wayland/lxqtplasmavirtualdesktop.h | 1 - panel/backends/wayland/lxqttaskbarbackendwayland.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.h b/panel/backends/wayland/lxqtplasmavirtualdesktop.h index 65324e013..fff025213 100644 --- a/panel/backends/wayland/lxqtplasmavirtualdesktop.h +++ b/panel/backends/wayland/lxqtplasmavirtualdesktop.h @@ -1,7 +1,6 @@ #ifndef LXQTPLASMAVIRTUALDESKTOP_H #define LXQTPLASMAVIRTUALDESKTOP_H -#include // TODO: needed to fix compile errors about QHashPrivate #include #include diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.h b/panel/backends/wayland/lxqttaskbarbackendwayland.h index 0b12ef2fd..df202f076 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.h +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.h @@ -3,6 +3,7 @@ #include "../ilxqttaskbarabstractbackend.h" +#include #include #include From e065073856769225ee095e345031a3c8a6cb341c Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 9 Mar 2024 20:06:15 +0100 Subject: [PATCH 056/165] LXQtPanel: set LayerShellQt KeyboardInteractivityOnDemand Set layer shell keyboard interactivity on-demand --- panel/lxqtpanel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panel/lxqtpanel.cpp b/panel/lxqtpanel.cpp index dfb91b3fa..a7a4508ab 100644 --- a/panel/lxqtpanel.cpp +++ b/panel/lxqtpanel.cpp @@ -251,7 +251,7 @@ LXQtPanel::LXQtPanel(const QString &configGroup, LXQt::Settings *settings, QWidg anchors.setFlag(LayerShellQt::Window::AnchorRight); mLayerWindow->setAnchors(anchors); - mLayerWindow->setKeyboardInteractivity(LayerShellQt::Window::KeyboardInteractivityNone); + mLayerWindow->setKeyboardInteractivity(LayerShellQt::Window::KeyboardInteractivityOnDemand); mLayerWindow->setCloseOnDismissed(false); mLayerWindow->setExclusiveEdge(LayerShellQt::Window::AnchorBottom); From dbdbdd7b4b1da674539682bf2dae5896340e994e Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Wed, 27 Mar 2024 00:06:09 +0530 Subject: [PATCH 057/165] Get wlroots backend compiling --- panel/CMakeLists.txt | 3 +- panel/backends/wayland/CMakeLists.txt | 34 +- panel/backends/wayland/plasma/CMakeLists.txt | 32 ++ .../{ => plasma}/lxqtplasmavirtualdesktop.cpp | 0 .../{ => plasma}/lxqtplasmavirtualdesktop.h | 0 .../lxqttaskbarbackendplasma.cpp} | 70 +-- .../lxqttaskbarbackendplasma.h} | 10 +- .../lxqttaskbarplasmawindowmanagment.cpp | 0 .../lxqttaskbarplasmawindowmanagment.h | 0 .../org-kde-plasma-virtual-desktop.xml | 0 .../protocols/plasma-window-management.xml | 0 panel/backends/wayland/wlroots/CMakeLists.txt | 27 + .../wayland/wlroots/lxqttaskbarbackendwlr.cpp | 495 ++++++++++++++++++ .../wayland/wlroots/lxqttaskbarbackendwlr.h | 95 ++++ .../wlroots/lxqttaskbarwlrwindowmanagment.cpp | 127 +++++ .../wlroots/lxqttaskbarwlrwindowmanagment.h | 87 +++ .../wayland/wlroots/lxqtwlrvirtualdesktop.cpp | 104 ++++ .../wayland/wlroots/lxqtwlrvirtualdesktop.h | 73 +++ ...oreign-toplevel-management-unstable-v1.xml | 270 ++++++++++ panel/backends/xcb/lxqttaskbarbackend_x11.cpp | 4 +- panel/lxqtpanelapplication.cpp | 5 +- 21 files changed, 1359 insertions(+), 77 deletions(-) create mode 100644 panel/backends/wayland/plasma/CMakeLists.txt rename panel/backends/wayland/{ => plasma}/lxqtplasmavirtualdesktop.cpp (100%) rename panel/backends/wayland/{ => plasma}/lxqtplasmavirtualdesktop.h (100%) rename panel/backends/wayland/{lxqttaskbarbackendwayland.cpp => plasma/lxqttaskbarbackendplasma.cpp} (88%) rename panel/backends/wayland/{lxqttaskbarbackendwayland.h => plasma/lxqttaskbarbackendplasma.h} (92%) rename panel/backends/wayland/{ => plasma}/lxqttaskbarplasmawindowmanagment.cpp (100%) rename panel/backends/wayland/{ => plasma}/lxqttaskbarplasmawindowmanagment.h (100%) rename panel/backends/wayland/{ => plasma}/protocols/org-kde-plasma-virtual-desktop.xml (100%) rename panel/backends/wayland/{ => plasma}/protocols/plasma-window-management.xml (100%) create mode 100644 panel/backends/wayland/wlroots/CMakeLists.txt create mode 100644 panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp create mode 100644 panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h create mode 100644 panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp create mode 100644 panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h create mode 100644 panel/backends/wayland/wlroots/lxqtwlrvirtualdesktop.cpp create mode 100644 panel/backends/wayland/wlroots/lxqtwlrvirtualdesktop.h create mode 100644 panel/backends/wayland/wlroots/protocols/wlr-foreign-toplevel-management-unstable-v1.xml diff --git a/panel/CMakeLists.txt b/panel/CMakeLists.txt index ef1291af5..f081f2f5f 100644 --- a/panel/CMakeLists.txt +++ b/panel/CMakeLists.txt @@ -5,7 +5,8 @@ add_subdirectory(backends) # TODO: allow compile time selection via CMake variables set(PANEL_BACKENDS - lxqt-panel-backend-wayland + lxqt-panel-backend-plasma + lxqt-panel-backend-wlroots lxqt-panel-backend-xcb ) diff --git a/panel/backends/wayland/CMakeLists.txt b/panel/backends/wayland/CMakeLists.txt index e0ecab6e9..d23f14f1e 100644 --- a/panel/backends/wayland/CMakeLists.txt +++ b/panel/backends/wayland/CMakeLists.txt @@ -1,32 +1,2 @@ -# Wayland WM Backend - -project(lxqt-panel-backend-wayland) - -find_package(Qt6 ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS WaylandClient Concurrent) - -add_library(lxqt-panel-backend-wayland STATIC - lxqttaskbarbackendwayland.h - lxqttaskbarplasmawindowmanagment.h - lxqtplasmavirtualdesktop.h - - lxqttaskbarbackendwayland.cpp - lxqttaskbarplasmawindowmanagment.cpp - lxqtplasmavirtualdesktop.cpp -) - -qt6_generate_wayland_protocol_client_sources(lxqt-panel-backend-wayland -FILES - ${CMAKE_CURRENT_SOURCE_DIR}/protocols/plasma-window-management.xml -) - -qt6_generate_wayland_protocol_client_sources(lxqt-panel-backend-wayland -FILES - ${CMAKE_CURRENT_SOURCE_DIR}/protocols/org-kde-plasma-virtual-desktop.xml -) - -target_link_libraries(lxqt-panel-backend-wayland - Qt6::GuiPrivate - Qt6::WaylandClient - Qt6::Concurrent - lxqt-panel-backend-common -) +add_subdirectory(plasma) +add_subdirectory(wlroots) diff --git a/panel/backends/wayland/plasma/CMakeLists.txt b/panel/backends/wayland/plasma/CMakeLists.txt new file mode 100644 index 000000000..71b2b273b --- /dev/null +++ b/panel/backends/wayland/plasma/CMakeLists.txt @@ -0,0 +1,32 @@ +# Wayland WM Backend + +project(lxqt-panel-backend-plasma) + +find_package(Qt6 ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS WaylandClient Concurrent) + +add_library(lxqt-panel-backend-plasma STATIC + lxqttaskbarbackendplasma.h + lxqttaskbarplasmawindowmanagment.h + lxqtplasmavirtualdesktop.h + + lxqttaskbarbackendplasma.cpp + lxqttaskbarplasmawindowmanagment.cpp + lxqtplasmavirtualdesktop.cpp +) + +qt6_generate_wayland_protocol_client_sources(lxqt-panel-backend-plasma +FILES + ${CMAKE_CURRENT_SOURCE_DIR}/protocols/plasma-window-management.xml +) + +qt6_generate_wayland_protocol_client_sources(lxqt-panel-backend-plasma +FILES + ${CMAKE_CURRENT_SOURCE_DIR}/protocols/org-kde-plasma-virtual-desktop.xml +) + +target_link_libraries(lxqt-panel-backend-plasma + Qt6::GuiPrivate + Qt6::WaylandClient + Qt6::Concurrent + lxqt-panel-backend-common +) diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp b/panel/backends/wayland/plasma/lxqtplasmavirtualdesktop.cpp similarity index 100% rename from panel/backends/wayland/lxqtplasmavirtualdesktop.cpp rename to panel/backends/wayland/plasma/lxqtplasmavirtualdesktop.cpp diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.h b/panel/backends/wayland/plasma/lxqtplasmavirtualdesktop.h similarity index 100% rename from panel/backends/wayland/lxqtplasmavirtualdesktop.h rename to panel/backends/wayland/plasma/lxqtplasmavirtualdesktop.h diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.cpp similarity index 88% rename from panel/backends/wayland/lxqttaskbarbackendwayland.cpp rename to panel/backends/wayland/plasma/lxqttaskbarbackendplasma.cpp index fb6e940a8..ad8b2332f 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.cpp @@ -1,4 +1,4 @@ -#include "lxqttaskbarbackendwayland.h" +#include "lxqttaskbarbackendplasma.h" #include "lxqttaskbarplasmawindowmanagment.h" #include "lxqtplasmavirtualdesktop.h" @@ -22,7 +22,7 @@ auto findWindow(const std::vector>& win return windows.end(); } -LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) : +LXQtTaskbarPlasmaBackend::LXQtTaskbarPlasmaBackend(QObject *parent) : ILXQtTaskbarAbstractBackend(parent) { m_managment.reset(new LXQtTaskBarPlasmaWindowManagment); @@ -58,7 +58,7 @@ LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) : }); } -bool LXQtTaskbarWaylandBackend::supportsAction(WId windowId, LXQtTaskBarBackendAction action) const +bool LXQtTaskbarPlasmaBackend::supportsAction(WId windowId, LXQtTaskBarBackendAction action) const { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -99,7 +99,7 @@ bool LXQtTaskbarWaylandBackend::supportsAction(WId windowId, LXQtTaskBarBackendA return window->windowState.testFlag(state); } -bool LXQtTaskbarWaylandBackend::reloadWindows() +bool LXQtTaskbarPlasmaBackend::reloadWindows() { const QVector wids = getCurrentWindows(); @@ -116,7 +116,7 @@ bool LXQtTaskbarWaylandBackend::reloadWindows() return true; } -QVector LXQtTaskbarWaylandBackend::getCurrentWindows() const +QVector LXQtTaskbarPlasmaBackend::getCurrentWindows() const { QVector wids; wids.reserve(wids.size()); @@ -129,7 +129,7 @@ QVector LXQtTaskbarWaylandBackend::getCurrentWindows() const return wids; } -QString LXQtTaskbarWaylandBackend::getWindowTitle(WId windowId) const +QString LXQtTaskbarPlasmaBackend::getWindowTitle(WId windowId) const { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -138,7 +138,7 @@ QString LXQtTaskbarWaylandBackend::getWindowTitle(WId windowId) const return window->title; } -bool LXQtTaskbarWaylandBackend::applicationDemandsAttention(WId windowId) const +bool LXQtTaskbarPlasmaBackend::applicationDemandsAttention(WId windowId) const { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -147,7 +147,7 @@ bool LXQtTaskbarWaylandBackend::applicationDemandsAttention(WId windowId) const return window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_demands_attention) || transientsDemandingAttention.contains(window); } -QIcon LXQtTaskbarWaylandBackend::getApplicationIcon(WId windowId, int devicePixels) const +QIcon LXQtTaskbarPlasmaBackend::getApplicationIcon(WId windowId, int devicePixels) const { Q_UNUSED(devicePixels) @@ -158,7 +158,7 @@ QIcon LXQtTaskbarWaylandBackend::getApplicationIcon(WId windowId, int devicePixe return window->icon; } -QString LXQtTaskbarWaylandBackend::getWindowClass(WId windowId) const +QString LXQtTaskbarPlasmaBackend::getWindowClass(WId windowId) const { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -166,7 +166,7 @@ QString LXQtTaskbarWaylandBackend::getWindowClass(WId windowId) const return window->appId; } -LXQtTaskBarWindowLayer LXQtTaskbarWaylandBackend::getWindowLayer(WId windowId) const +LXQtTaskBarWindowLayer LXQtTaskbarPlasmaBackend::getWindowLayer(WId windowId) const { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -181,7 +181,7 @@ LXQtTaskBarWindowLayer LXQtTaskbarWaylandBackend::getWindowLayer(WId windowId) c return LXQtTaskBarWindowLayer::Normal; } -bool LXQtTaskbarWaylandBackend::setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) +bool LXQtTaskbarPlasmaBackend::setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -207,7 +207,7 @@ bool LXQtTaskbarWaylandBackend::setWindowLayer(WId windowId, LXQtTaskBarWindowLa return false; } -LXQtTaskBarWindowState LXQtTaskbarWaylandBackend::getWindowState(WId windowId) const +LXQtTaskBarWindowState LXQtTaskbarPlasmaBackend::getWindowState(WId windowId) const { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -225,7 +225,7 @@ LXQtTaskBarWindowState LXQtTaskbarWaylandBackend::getWindowState(WId windowId) c return LXQtTaskBarWindowState::Normal; } -bool LXQtTaskbarWaylandBackend::setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set) +bool LXQtTaskbarPlasmaBackend::setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set) { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -265,7 +265,7 @@ bool LXQtTaskbarWaylandBackend::setWindowState(WId windowId, LXQtTaskBarWindowSt return true; } -bool LXQtTaskbarWaylandBackend::isWindowActive(WId windowId) const +bool LXQtTaskbarPlasmaBackend::isWindowActive(WId windowId) const { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -274,7 +274,7 @@ bool LXQtTaskbarWaylandBackend::isWindowActive(WId windowId) const return activeWindow == window || window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_active); } -bool LXQtTaskbarWaylandBackend::raiseWindow(WId windowId, bool onCurrentWorkSpace) +bool LXQtTaskbarPlasmaBackend::raiseWindow(WId windowId, bool onCurrentWorkSpace) { Q_UNUSED(onCurrentWorkSpace) //TODO @@ -304,7 +304,7 @@ bool LXQtTaskbarWaylandBackend::raiseWindow(WId windowId, bool onCurrentWorkSpac return true; } -bool LXQtTaskbarWaylandBackend::closeWindow(WId windowId) +bool LXQtTaskbarPlasmaBackend::closeWindow(WId windowId) { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -314,31 +314,31 @@ bool LXQtTaskbarWaylandBackend::closeWindow(WId windowId) return true; } -WId LXQtTaskbarWaylandBackend::getActiveWindow() const +WId LXQtTaskbarPlasmaBackend::getActiveWindow() const { if(activeWindow) return activeWindow->getWindowId(); return 0; } -int LXQtTaskbarWaylandBackend::getWorkspacesCount() const +int LXQtTaskbarPlasmaBackend::getWorkspacesCount() const { return m_workspaceInfo->numberOfDesktops(); } -QString LXQtTaskbarWaylandBackend::getWorkspaceName(int idx) const +QString LXQtTaskbarPlasmaBackend::getWorkspaceName(int idx) const { return m_workspaceInfo->getDesktopName(idx - 1); //Return to 0-based } -int LXQtTaskbarWaylandBackend::getCurrentWorkspace() const +int LXQtTaskbarPlasmaBackend::getCurrentWorkspace() const { if(!m_workspaceInfo->currentDesktop().isValid()) return 0; return m_workspaceInfo->position(m_workspaceInfo->currentDesktop()) + 1; // 1-based } -bool LXQtTaskbarWaylandBackend::setCurrentWorkspace(int idx) +bool LXQtTaskbarPlasmaBackend::setCurrentWorkspace(int idx) { QString id = m_workspaceInfo->getDesktopId(idx - 1); //Return to 0-based if(id.isEmpty()) @@ -347,7 +347,7 @@ bool LXQtTaskbarWaylandBackend::setCurrentWorkspace(int idx) return true; } -int LXQtTaskbarWaylandBackend::getWindowWorkspace(WId windowId) const +int LXQtTaskbarPlasmaBackend::getWindowWorkspace(WId windowId) const { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -364,7 +364,7 @@ int LXQtTaskbarWaylandBackend::getWindowWorkspace(WId windowId) const return m_workspaceInfo->position(id) + 1; //Make 1-based } -bool LXQtTaskbarWaylandBackend::setWindowOnWorkspace(WId windowId, int idx) +bool LXQtTaskbarPlasmaBackend::setWindowOnWorkspace(WId windowId, int idx) { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -399,12 +399,12 @@ bool LXQtTaskbarWaylandBackend::setWindowOnWorkspace(WId windowId, int idx) return true; } -void LXQtTaskbarWaylandBackend::moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) +void LXQtTaskbarPlasmaBackend::moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) { } -bool LXQtTaskbarWaylandBackend::isWindowOnScreen(QScreen *screen, WId windowId) const +bool LXQtTaskbarPlasmaBackend::isWindowOnScreen(QScreen *screen, WId windowId) const { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -413,7 +413,7 @@ bool LXQtTaskbarWaylandBackend::isWindowOnScreen(QScreen *screen, WId windowId) return screen->geometry().intersects(window->geometry); } -void LXQtTaskbarWaylandBackend::moveApplication(WId windowId) +void LXQtTaskbarPlasmaBackend::moveApplication(WId windowId) { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -423,7 +423,7 @@ void LXQtTaskbarWaylandBackend::moveApplication(WId windowId) window->request_move(); } -void LXQtTaskbarWaylandBackend::resizeApplication(WId windowId) +void LXQtTaskbarPlasmaBackend::resizeApplication(WId windowId) { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -433,12 +433,12 @@ void LXQtTaskbarWaylandBackend::resizeApplication(WId windowId) window->request_resize(); } -void LXQtTaskbarWaylandBackend::refreshIconGeometry(WId windowId, const QRect &geom) +void LXQtTaskbarPlasmaBackend::refreshIconGeometry(WId windowId, const QRect &geom) { } -bool LXQtTaskbarWaylandBackend::isAreaOverlapped(const QRect &area) const +bool LXQtTaskbarPlasmaBackend::isAreaOverlapped(const QRect &area) const { for(auto &window : std::as_const(windows)) { @@ -448,12 +448,12 @@ bool LXQtTaskbarWaylandBackend::isAreaOverlapped(const QRect &area) const return false; } -bool LXQtTaskbarWaylandBackend::isShowingDesktop() const +bool LXQtTaskbarPlasmaBackend::isShowingDesktop() const { return m_managment->isShowingDesktop(); } -bool LXQtTaskbarWaylandBackend::showDesktop(bool value) +bool LXQtTaskbarPlasmaBackend::showDesktop(bool value) { enum LXQtTaskBarPlasmaWindowManagment::show_desktop flag_; if(value) @@ -465,7 +465,7 @@ bool LXQtTaskbarWaylandBackend::showDesktop(bool value) return true; } -void LXQtTaskbarWaylandBackend::addWindow(LXQtTaskBarPlasmaWindow *window) +void LXQtTaskbarPlasmaBackend::addWindow(LXQtTaskBarPlasmaWindow *window) { if (findWindow(windows, window) != windows.end() || transients.contains(window)) { @@ -685,7 +685,7 @@ void LXQtTaskbarWaylandBackend::addWindow(LXQtTaskBarPlasmaWindow *window) } } -bool LXQtTaskbarWaylandBackend::acceptWindow(LXQtTaskBarPlasmaWindow *window) const +bool LXQtTaskbarPlasmaBackend::acceptWindow(LXQtTaskBarPlasmaWindow *window) const { if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_skiptaskbar)) return false; @@ -696,7 +696,7 @@ bool LXQtTaskbarWaylandBackend::acceptWindow(LXQtTaskBarPlasmaWindow *window) co return true; } -void LXQtTaskbarWaylandBackend::updateWindowAcceptance(LXQtTaskBarPlasmaWindow *window) +void LXQtTaskbarPlasmaBackend::updateWindowAcceptance(LXQtTaskBarPlasmaWindow *window) { if(!window->acceptedInTaskBar && acceptWindow(window)) { @@ -710,7 +710,7 @@ void LXQtTaskbarWaylandBackend::updateWindowAcceptance(LXQtTaskBarPlasmaWindow * } } -LXQtTaskBarPlasmaWindow *LXQtTaskbarWaylandBackend::getWindow(WId windowId) const +LXQtTaskBarPlasmaWindow *LXQtTaskbarPlasmaBackend::getWindow(WId windowId) const { for(auto &window : std::as_const(windows)) { diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.h b/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.h similarity index 92% rename from panel/backends/wayland/lxqttaskbarbackendwayland.h rename to panel/backends/wayland/plasma/lxqttaskbarbackendplasma.h index df202f076..31b78f6c6 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.h +++ b/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.h @@ -1,7 +1,7 @@ -#ifndef LXQTTASKBARBACKENDWAYLAND_H -#define LXQTTASKBARBACKENDWAYLAND_H +#ifndef LXQTTASKBARBACKENDPLASMA_H +#define LXQTTASKBARBACKENDPLASMA_H -#include "../ilxqttaskbarabstractbackend.h" +#include "../../ilxqttaskbarabstractbackend.h" #include #include @@ -12,12 +12,12 @@ class LXQtTaskBarPlasmaWindowManagment; class LXQtPlasmaWaylandWorkspaceInfo; -class LXQtTaskbarWaylandBackend : public ILXQtTaskbarAbstractBackend +class LXQtTaskbarPlasmaBackend : public ILXQtTaskbarAbstractBackend { Q_OBJECT public: - explicit LXQtTaskbarWaylandBackend(QObject *parent = nullptr); + explicit LXQtTaskbarPlasmaBackend(QObject *parent = nullptr); // Backend virtual bool supportsAction(WId windowId, LXQtTaskBarBackendAction action) const override; diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp b/panel/backends/wayland/plasma/lxqttaskbarplasmawindowmanagment.cpp similarity index 100% rename from panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp rename to panel/backends/wayland/plasma/lxqttaskbarplasmawindowmanagment.cpp diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h b/panel/backends/wayland/plasma/lxqttaskbarplasmawindowmanagment.h similarity index 100% rename from panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h rename to panel/backends/wayland/plasma/lxqttaskbarplasmawindowmanagment.h diff --git a/panel/backends/wayland/protocols/org-kde-plasma-virtual-desktop.xml b/panel/backends/wayland/plasma/protocols/org-kde-plasma-virtual-desktop.xml similarity index 100% rename from panel/backends/wayland/protocols/org-kde-plasma-virtual-desktop.xml rename to panel/backends/wayland/plasma/protocols/org-kde-plasma-virtual-desktop.xml diff --git a/panel/backends/wayland/protocols/plasma-window-management.xml b/panel/backends/wayland/plasma/protocols/plasma-window-management.xml similarity index 100% rename from panel/backends/wayland/protocols/plasma-window-management.xml rename to panel/backends/wayland/plasma/protocols/plasma-window-management.xml diff --git a/panel/backends/wayland/wlroots/CMakeLists.txt b/panel/backends/wayland/wlroots/CMakeLists.txt new file mode 100644 index 000000000..8f6f1a59b --- /dev/null +++ b/panel/backends/wayland/wlroots/CMakeLists.txt @@ -0,0 +1,27 @@ +# Wayland WM Backend + +project(lxqt-panel-backend-wlroots) + +find_package(Qt6 ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS WaylandClient Concurrent) + +add_library(lxqt-panel-backend-wlroots STATIC + lxqttaskbarbackendwlr.h + lxqttaskbarwlrwindowmanagment.h + lxqtwlrvirtualdesktop.h + + lxqttaskbarbackendwlr.cpp + lxqttaskbarwlrwindowmanagment.cpp + lxqtwlrvirtualdesktop.cpp +) + +qt6_generate_wayland_protocol_client_sources(lxqt-panel-backend-wlroots +FILES + ${CMAKE_CURRENT_SOURCE_DIR}/protocols/wlr-foreign-toplevel-management-unstable-v1.xml +) + +target_link_libraries(lxqt-panel-backend-wlroots + Qt6::GuiPrivate + Qt6::WaylandClient + Qt6::Concurrent + lxqt-panel-backend-common +) diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp new file mode 100644 index 000000000..7f9590a2c --- /dev/null +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp @@ -0,0 +1,495 @@ +#include "lxqttaskbarbackendwlr.h" + +#include "lxqttaskbarwlrwindowmanagment.h" +#include "lxqtwlrvirtualdesktop.h" + +#include +#include +#include + +auto findWindow(const std::vector>& windows, LXQtTaskBarWlrootsWindow *window) +{ + //TODO: use algorithms + auto end = windows.end(); + for(auto it = windows.begin(); it != end; it++) + { + if((*it).get() == window) + { + return it; + } + } + + return windows.end(); +} + +LXQtTaskbarWlrootsBackend::LXQtTaskbarWlrootsBackend(QObject *parent) : + ILXQtTaskbarAbstractBackend(parent) +{ + m_managment.reset(new LXQtTaskBarWlrootsWindowManagment); + m_workspaceInfo.reset(new LXQtWlrootsWaylandWorkspaceInfo); + + connect(m_managment.get(), &LXQtTaskBarWlrootsWindowManagment::windowCreated, this, [this](LXQtTaskBarWlrootsWindow *window) { + addWindow(window); + }); + + // connect(m_managment.get(), &LXQtTaskBarWlrootsWindowManagment::stackingOrderChanged, + // this, [this](const QString &order) { + // // stackingOrder = order.split(QLatin1Char(';')); + // // for (const auto &window : std::as_const(windows)) { + // // this->dataChanged(window.get(), StackingOrder); + // // } + // }); + + connect(m_workspaceInfo.get(), &LXQtWlrootsWaylandWorkspaceInfo::currentDesktopChanged, this, + [this](){ + int idx = m_workspaceInfo->position(m_workspaceInfo->currentDesktop()); + idx += 1; // Make 1-based + emit currentWorkspaceChanged(idx); + }); + + connect(m_workspaceInfo.get(), &LXQtWlrootsWaylandWorkspaceInfo::numberOfDesktopsChanged, + this, &ILXQtTaskbarAbstractBackend::workspacesCountChanged); + + connect(m_workspaceInfo.get(), &LXQtWlrootsWaylandWorkspaceInfo::desktopNameChanged, + this, [this](int idx) { + emit workspaceNameChanged(idx + 1); // Make 1-based + }); +} + +bool LXQtTaskbarWlrootsBackend::supportsAction(WId, LXQtTaskBarBackendAction action) const +{ + switch (action) + { + case LXQtTaskBarBackendAction::Move: + return false; + + case LXQtTaskBarBackendAction::Resize: + return false; + + case LXQtTaskBarBackendAction::Maximize: + return true; + + case LXQtTaskBarBackendAction::Minimize: + return true; + + case LXQtTaskBarBackendAction::RollUp: + return false; + + case LXQtTaskBarBackendAction::FullScreen: + return true; + + default: + return false; + } + + return false; +} + +bool LXQtTaskbarWlrootsBackend::reloadWindows() +{ + const QVector wids = getCurrentWindows(); + + // Force removal and re-adding + for(WId windowId : wids) + { + emit windowRemoved(windowId); + } + for(WId windowId : wids) + { + emit windowAdded(windowId); + } + + return true; +} + +QVector LXQtTaskbarWlrootsBackend::getCurrentWindows() const +{ + QVector wids; + wids.reserve(wids.size()); + + for(const std::unique_ptr& window : std::as_const(windows)) + { + wids << window->getWindowId(); + } + return wids; +} + +QString LXQtTaskbarWlrootsBackend::getWindowTitle(WId windowId) const +{ + LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + if(!window) + return QString(); + + return window->title; +} + +bool LXQtTaskbarWlrootsBackend::applicationDemandsAttention(WId) const +{ + return false; +} + +QIcon LXQtTaskbarWlrootsBackend::getApplicationIcon(WId windowId, int devicePixels) const +{ + Q_UNUSED(devicePixels) + + LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + if(!window) + return QIcon(); + + return window->icon; +} + +QString LXQtTaskbarWlrootsBackend::getWindowClass(WId windowId) const +{ + LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + if(!window) + return QString(); + return window->appId; +} + +LXQtTaskBarWindowLayer LXQtTaskbarWlrootsBackend::getWindowLayer(WId) const +{ + return LXQtTaskBarWindowLayer::Normal; +} + +bool LXQtTaskbarWlrootsBackend::setWindowLayer(WId, LXQtTaskBarWindowLayer) +{ +} + +LXQtTaskBarWindowState LXQtTaskbarWlrootsBackend::getWindowState(WId windowId) const +{ + LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + if(!window) + return LXQtTaskBarWindowState::Normal; + + if(window->windowState.testFlag(LXQtTaskBarWlrootsWindow::state::state_minimized)) + return LXQtTaskBarWindowState::Hidden; + + if(window->windowState.testFlag(LXQtTaskBarWlrootsWindow::state::state_maximized)) + return LXQtTaskBarWindowState::Maximized; + + if(window->windowState.testFlag(LXQtTaskBarWlrootsWindow::state::state_fullscreen)) + return LXQtTaskBarWindowState::FullScreen; + + return LXQtTaskBarWindowState::Normal; +} + +bool LXQtTaskbarWlrootsBackend::setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set) +{ + LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + if(!window) + return false; + + LXQtTaskBarWlrootsWindow::state WlrootsState; + switch (state) + { + case LXQtTaskBarWindowState::Minimized: + { + WlrootsState = LXQtTaskBarWlrootsWindow::state::state_minimized; + break; + } + case LXQtTaskBarWindowState::Maximized: + case LXQtTaskBarWindowState::MaximizedVertically: + case LXQtTaskBarWindowState::MaximizedHorizontally: + { + WlrootsState = LXQtTaskBarWlrootsWindow::state::state_maximized; + break; + } + case LXQtTaskBarWindowState::Normal: + { + WlrootsState = LXQtTaskBarWlrootsWindow::state::state_maximized; + set = !set; //TODO: correct + break; + } + case LXQtTaskBarWindowState::RolledUp: + { + break; + } + default: + return false; + } + + window->set_state(WlrootsState, set ? WlrootsState : 0); + return true; +} + +bool LXQtTaskbarWlrootsBackend::isWindowActive(WId windowId) const +{ + LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + if(!window) + return false; + + return activeWindow == window || window->windowState.testFlag(LXQtTaskBarWlrootsWindow::state::state_activated); +} + +bool LXQtTaskbarWlrootsBackend::raiseWindow(WId windowId, bool onCurrentWorkSpace) +{ + Q_UNUSED(onCurrentWorkSpace) //TODO + + LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + if(!window) + return false; + + // Pull forward any transient demanding attention. + if (auto *transientDemandingAttention = transientsDemandingAttention.value(window)) + { + window = transientDemandingAttention; + } + else + { + // TODO Shouldn't KWin take care of that? + // Bringing a transient to the front usually brings its parent with it + // but focus is not handled properly. + // TODO take into account d->lastActivation instead + // of just taking the first one. + while (transients.key(window)) + { + window = transients.key(window); + } + } + + window->set_state(LXQtTaskBarWlrootsWindow::state::state_activated, LXQtTaskBarWlrootsWindow::state::state_activated); + return true; +} + +bool LXQtTaskbarWlrootsBackend::closeWindow(WId windowId) +{ + LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + if(!window) + return false; + + window->close(); + return true; +} + +WId LXQtTaskbarWlrootsBackend::getActiveWindow() const +{ + if(activeWindow) + return activeWindow->getWindowId(); + return 0; +} + +int LXQtTaskbarWlrootsBackend::getWorkspacesCount() const +{ + return m_workspaceInfo->numberOfDesktops(); +} + +QString LXQtTaskbarWlrootsBackend::getWorkspaceName(int idx) const +{ + return m_workspaceInfo->getDesktopName(idx - 1); //Return to 0-based +} + +int LXQtTaskbarWlrootsBackend::getCurrentWorkspace() const +{ + return 1; +} + +bool LXQtTaskbarWlrootsBackend::setCurrentWorkspace(int) +{ + return false; +} + +int LXQtTaskbarWlrootsBackend::getWindowWorkspace(WId windowId) const +{ + return 1; +} + +bool LXQtTaskbarWlrootsBackend::setWindowOnWorkspace(WId, int) +{ +} + +void LXQtTaskbarWlrootsBackend::moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) +{ +} + +bool LXQtTaskbarWlrootsBackend::isWindowOnScreen(QScreen *screen, WId windowId) const +{ + // Manage based on output-enter/output-leave + return true; +} + +void LXQtTaskbarWlrootsBackend::moveApplication(WId windowId) +{ +} + +void LXQtTaskbarWlrootsBackend::resizeApplication(WId windowId) +{ +} + +void LXQtTaskbarWlrootsBackend::refreshIconGeometry(WId windowId, const QRect &geom) +{ + +} + +bool LXQtTaskbarWlrootsBackend::isAreaOverlapped(const QRect &area) const +{ +} + +bool LXQtTaskbarWlrootsBackend::isShowingDesktop() const +{ + return m_managment->isShowingDesktop(); +} + +bool LXQtTaskbarWlrootsBackend::showDesktop(bool value) +{ +} + +void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) +{ + if (findWindow(windows, window) != windows.end() || transients.contains(window)) + { + return; + } + + auto removeWindow = [window, this] + { + auto it = findWindow(windows, window); + if (it != windows.end()) + { + windows.erase(it); + transientsDemandingAttention.remove(window); + lastActivated.remove(window); + } + else + { + // Could be a transient. + // Removing a transient might change the demands attention state of the leader. + if (transients.remove(window)) + { + if (LXQtTaskBarWlrootsWindow *leader = transientsDemandingAttention.key(window)) { + transientsDemandingAttention.remove(leader, window); + emit windowPropertyChanged(leader->getWindowId(), int(LXQtTaskBarWindowProperty::Urgency)); + } + } + } + + if (activeWindow == window) + { + activeWindow = nullptr; + emit activeWindowChanged(0); + } + }; + + connect(window, &LXQtTaskBarWlrootsWindow::closed, this, removeWindow); + + connect(window, &LXQtTaskBarWlrootsWindow::titleChanged, this, [window, this] { + updateWindowAcceptance(window); + }); + + connect(window, &LXQtTaskBarWlrootsWindow::appIdChanged, this, [window, this] { + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::WindowClass)); + }); + + if (window->windowState & LXQtTaskBarWlrootsWindow::state::state_activated) { + LXQtTaskBarWlrootsWindow *effectiveActive = window; + while (effectiveActive->parentWindow) { + effectiveActive = effectiveActive->parentWindow; + } + + lastActivated[effectiveActive] = QTime::currentTime(); + activeWindow = effectiveActive; + } + + connect(window, &LXQtTaskBarWlrootsWindow::activeChanged, this, [window, this] { + const bool active = window->windowState & LXQtTaskBarWlrootsWindow::state::state_activated; + + LXQtTaskBarWlrootsWindow *effectiveWindow = window; + + while (effectiveWindow->parentWindow) + { + effectiveWindow = effectiveWindow->parentWindow; + } + + if (active) + { + lastActivated[effectiveWindow] = QTime::currentTime(); + + if (activeWindow != effectiveWindow) + { + activeWindow = effectiveWindow; + emit activeWindowChanged(activeWindow->getWindowId()); + } + } + else + { + if (activeWindow == effectiveWindow) + { + activeWindow = nullptr; + emit activeWindowChanged(0); + } + } + }); + + connect(window, &LXQtTaskBarWlrootsWindow::parentChanged, this, [window, this] { + LXQtTaskBarWlrootsWindow *leader = window->parentWindow.data(); + + if (transients.remove(window)) + { + if (leader) + { + // leader change. + transients.insert(window, leader); + } + else + { + // lost a leader, add to regular windows list. + Q_ASSERT(findWindow(windows, window) == windows.end()); + + windows.emplace_back(window); + } + } + else if (leader) + { + // gained a leader, remove from regular windows list. + auto it = findWindow(windows, window); + Q_ASSERT(it != windows.end()); + + windows.erase(it); + lastActivated.remove(window); + } + }); + + auto stateChanged = [window, this] { + updateWindowAcceptance(window); + }; + + connect(window, &LXQtTaskBarWlrootsWindow::fullscreenChanged, this, stateChanged); + + connect(window, &LXQtTaskBarWlrootsWindow::maximizedChanged, this, stateChanged); + + connect(window, &LXQtTaskBarWlrootsWindow::minimizedChanged, this, stateChanged); + + // Handle transient. + if (LXQtTaskBarWlrootsWindow *leader = window->parentWindow.data()) + { + transients.insert(window, leader); + } + else + { + windows.emplace_back(window); + updateWindowAcceptance(window); + } +} + +bool LXQtTaskbarWlrootsBackend::acceptWindow(LXQtTaskBarWlrootsWindow *window) const +{ + if(transients.contains(window)) + return false; + + return true; +} + +void LXQtTaskbarWlrootsBackend::updateWindowAcceptance(LXQtTaskBarWlrootsWindow *) +{ +} + +LXQtTaskBarWlrootsWindow *LXQtTaskbarWlrootsBackend::getWindow(WId windowId) const +{ + for(auto &window : std::as_const(windows)) + { + if(window->getWindowId() == windowId) + return window.get(); + } + + return nullptr; +} diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h new file mode 100644 index 000000000..9b4348afd --- /dev/null +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h @@ -0,0 +1,95 @@ +#ifndef LXQTTASKBARBACKENDWLROOTS_H +#define LXQTTASKBARBACKENDWLROOTS_H + +#include "../../ilxqttaskbarabstractbackend.h" + +#include +#include +#include + +class LXQtTaskBarWlrootsWindow; +class LXQtTaskBarWlrootsWindowManagment; +class LXQtWlrootsWaylandWorkspaceInfo; + + +class LXQtTaskbarWlrootsBackend : public ILXQtTaskbarAbstractBackend +{ + Q_OBJECT + +public: + explicit LXQtTaskbarWlrootsBackend(QObject *parent = nullptr); + + // Backend + virtual bool supportsAction(WId windowId, LXQtTaskBarBackendAction action) const override; + + // Windows + virtual bool reloadWindows() override; + + virtual QVector getCurrentWindows() const override; + virtual QString getWindowTitle(WId windowId) const override; + virtual bool applicationDemandsAttention(WId windowId) const override; + virtual QIcon getApplicationIcon(WId windowId, int devicePixels) const override; + virtual QString getWindowClass(WId windowId) const override; + + virtual LXQtTaskBarWindowLayer getWindowLayer(WId windowId) const override; + virtual bool setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) override; + + virtual LXQtTaskBarWindowState getWindowState(WId windowId) const override; + virtual bool setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set) override; + + virtual bool isWindowActive(WId windowId) const override; + virtual bool raiseWindow(WId windowId, bool onCurrentWorkSpace) override; + + virtual bool closeWindow(WId windowId) override; + + virtual WId getActiveWindow() const override; + + // Workspaces + virtual int getWorkspacesCount() const override; + virtual QString getWorkspaceName(int idx) const override; + + virtual int getCurrentWorkspace() const override; + virtual bool setCurrentWorkspace(int idx) override; + + virtual int getWindowWorkspace(WId windowId) const override; + virtual bool setWindowOnWorkspace(WId windowId, int idx) override; + + virtual void moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) override; + + virtual bool isWindowOnScreen(QScreen *screen, WId windowId) const override; + + // X11 Specific + virtual void moveApplication(WId windowId) override; + virtual void resizeApplication(WId windowId) override; + + virtual void refreshIconGeometry(WId windowId, const QRect &geom) override; + + // Panel internal + virtual bool isAreaOverlapped(const QRect& area) const override; + + // Show Destop + virtual bool isShowingDesktop() const override; + virtual bool showDesktop(bool value) override; + +private: + void addWindow(LXQtTaskBarWlrootsWindow *window); + bool acceptWindow(LXQtTaskBarWlrootsWindow *window) const; + void updateWindowAcceptance(LXQtTaskBarWlrootsWindow *window); + +private: + LXQtTaskBarWlrootsWindow *getWindow(WId windowId) const; + + std::unique_ptr m_workspaceInfo; + + std::unique_ptr m_managment; + + QHash lastActivated; + LXQtTaskBarWlrootsWindow *activeWindow = nullptr; + std::vector> windows; + // key=transient child, value=leader + QHash transients; + // key=leader, values=transient children + QMultiHash transientsDemandingAttention; +}; + +#endif // LXQTTASKBARBACKENDWAYLAND_H diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp new file mode 100644 index 000000000..214b6f5d1 --- /dev/null +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp @@ -0,0 +1,127 @@ +#include "lxqttaskbarwlrwindowmanagment.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * LXQtTaskBarWlrootsWindowManagment + */ + +LXQtTaskBarWlrootsWindowManagment::LXQtTaskBarWlrootsWindowManagment() + : QWaylandClientExtensionTemplate(version) +{ + connect(this, &QWaylandClientExtension::activeChanged, this, [this] { + if (!isActive()) { + zwlr_foreign_toplevel_manager_v1_destroy(object()); + } + }); +} + +LXQtTaskBarWlrootsWindowManagment::~LXQtTaskBarWlrootsWindowManagment() +{ + if (isActive()) { + zwlr_foreign_toplevel_manager_v1_destroy(object()); + } +} + +void LXQtTaskBarWlrootsWindowManagment::zwlr_foreign_toplevel_manager_v1_toplevel(struct ::zwlr_foreign_toplevel_handle_v1 *toplevel) +{ + emit windowCreated( new LXQtTaskBarWlrootsWindow(toplevel) ); +} + + +/* + * LXQtTaskBarWlrootsWindow + */ + +LXQtTaskBarWlrootsWindow::LXQtTaskBarWlrootsWindow(::zwlr_foreign_toplevel_handle_v1 *id) + : zwlr_foreign_toplevel_handle_v1(id), + uuid(QString::number(0)) +{ +} + +/* + * LXQtTaskBarWlrootsWindow + */ + +LXQtTaskBarWlrootsWindow::~LXQtTaskBarWlrootsWindow() +{ + destroy(); +} + +void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_title(const QString &title) +{ + emit titleChanged(); + this->title = title; +} + +void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_app_id(const QString &app_id) +{ + emit appIdChanged(); + this->appId = app_id; + // Code to get the icon needs to be inserted here +} + +void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_enter(struct ::wl_output *output) +{ + emit outputEnter(); +} + +void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_leave(struct ::wl_output *output) +{ + emit outputLeave(); +} + +void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_state(wl_array *state) +{ + // emit activeChanged(); + // emit maximizedChanged(); + // emit minimizedChanged(); + // emit fullscreenChanged(); +} + +void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_done() +{ + emit done(); +} + +void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_closed() +{ + emit closed(); +} + +void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_parent(struct ::zwlr_foreign_toplevel_handle_v1 *parent) +{ + setParentWindow( new LXQtTaskBarWlrootsWindow(parent)); +} + +void LXQtTaskBarWlrootsWindow::setParentWindow(LXQtTaskBarWlrootsWindow *parent) +{ + const auto old = parentWindow; + QObject::disconnect(parentWindowUnmappedConnection); + + if (parent) { + parentWindow = QPointer(parent); + parentWindowUnmappedConnection = QObject::connect(parent, &LXQtTaskBarWlrootsWindow::closed, this, [this] { + setParentWindow(nullptr); + }); + } else { + parentWindow = QPointer(); + parentWindowUnmappedConnection = QMetaObject::Connection(); + } + + if (parentWindow.data() != old.data()) { + Q_EMIT parentChanged(); + } +} diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h new file mode 100644 index 000000000..d488ce15b --- /dev/null +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h @@ -0,0 +1,87 @@ +#ifndef LXQTTASKBARWlrootsWINDOWMANAGMENT_H +#define LXQTTASKBARWlrootsWINDOWMANAGMENT_H + +#include +#include +#include + +#include "qwayland-wlr-foreign-toplevel-management-unstable-v1.h" +#include "wayland-wlr-foreign-toplevel-management-unstable-v1-client-protocol.h" + +typedef quintptr WId; + +class LXQtTaskBarWlrootsWindow; + +class LXQtTaskBarWlrootsWindowManagment : public QWaylandClientExtensionTemplate, + public QtWayland::zwlr_foreign_toplevel_manager_v1 +{ + Q_OBJECT +public: + static constexpr int version = 16; + + LXQtTaskBarWlrootsWindowManagment(); + ~LXQtTaskBarWlrootsWindowManagment(); + + inline bool isShowingDesktop() const { return m_isShowingDesktop; } + +protected: + void zwlr_foreign_toplevel_manager_v1_toplevel(struct ::zwlr_foreign_toplevel_handle_v1 *toplevel); + void zwlr_foreign_toplevel_manager_v1_finished() {}; + +Q_SIGNALS: + void windowCreated(LXQtTaskBarWlrootsWindow *window); + +private: + bool m_isShowingDesktop = false; +}; + +class LXQtTaskBarWlrootsWindow : public QObject, + public QtWayland::zwlr_foreign_toplevel_handle_v1 +{ + Q_OBJECT +public: + LXQtTaskBarWlrootsWindow(::zwlr_foreign_toplevel_handle_v1 *id); + ~LXQtTaskBarWlrootsWindow(); + + inline WId getWindowId() const { return reinterpret_cast(this); } + + using state = QtWayland::zwlr_foreign_toplevel_handle_v1::state; + const QString uuid; + QString title; + QString appId; + QIcon icon; + QFlags windowState; + QPointer parentWindow; + + void set_state( LXQtTaskBarWlrootsWindow::state, bool ) {}; + +Q_SIGNALS: + void titleChanged(); + void appIdChanged(); + void outputEnter(); + void outputLeave(); + void activeChanged(); + void maximizedChanged(); + void minimizedChanged(); + void fullscreenChanged(); + void done(); + void closed(); + void parentChanged(); + +protected: + void zwlr_foreign_toplevel_handle_v1_title(const QString &title); + void zwlr_foreign_toplevel_handle_v1_app_id(const QString &app_id); + void zwlr_foreign_toplevel_handle_v1_output_enter(struct ::wl_output *output); + void zwlr_foreign_toplevel_handle_v1_output_leave(struct ::wl_output *output); + void zwlr_foreign_toplevel_handle_v1_state(wl_array *state); + void zwlr_foreign_toplevel_handle_v1_done(); + void zwlr_foreign_toplevel_handle_v1_closed(); + void zwlr_foreign_toplevel_handle_v1_parent(struct ::zwlr_foreign_toplevel_handle_v1 *parent); + +private: + void setParentWindow(LXQtTaskBarWlrootsWindow *parent); + + QMetaObject::Connection parentWindowUnmappedConnection; +}; + +#endif // LXQTTASKBARWlrootsWINDOWMANAGMENT_H diff --git a/panel/backends/wayland/wlroots/lxqtwlrvirtualdesktop.cpp b/panel/backends/wayland/wlroots/lxqtwlrvirtualdesktop.cpp new file mode 100644 index 000000000..51e2974ac --- /dev/null +++ b/panel/backends/wayland/wlroots/lxqtwlrvirtualdesktop.cpp @@ -0,0 +1,104 @@ +#include "lxqtwlrvirtualdesktop.h" + +#include + +LXQtWlrootsVirtualDesktop::LXQtWlrootsVirtualDesktop(const QString &id) + : id(id) + , name(QString::fromUtf8("Desktop 1")) +{ +} + +LXQtWlrootsVirtualDesktop::~LXQtWlrootsVirtualDesktop() +{ +} + +LXQtWlrootsVirtualDesktopManagment::LXQtWlrootsVirtualDesktopManagment() : QObject() +{ +} + +LXQtWlrootsVirtualDesktopManagment::~LXQtWlrootsVirtualDesktopManagment() +{ +} + +LXQtWlrootsWaylandWorkspaceInfo::LXQtWlrootsWaylandWorkspaceInfo() +{ + init(); +} + +LXQtWlrootsWaylandWorkspaceInfo::VirtualDesktopsIterator LXQtWlrootsWaylandWorkspaceInfo::findDesktop(const QString &id) const +{ + return std::find_if(virtualDesktops.begin(), virtualDesktops.end(), + [&id](const std::unique_ptr &desktop) { + return desktop->id == id; + }); +} + +QString LXQtWlrootsWaylandWorkspaceInfo::getDesktopName(int pos) const +{ + if(pos < 0 || size_t(pos) >= virtualDesktops.size()) + return QString(); + return virtualDesktops[pos]->name; +} + +QString LXQtWlrootsWaylandWorkspaceInfo::getDesktopId(int pos) const +{ + if(pos < 0 || size_t(pos) >= virtualDesktops.size()) + return QString(); + return virtualDesktops[pos]->id; +} + +void LXQtWlrootsWaylandWorkspaceInfo::init() +{ + virtualDesktopManagement = std::make_unique(); + auto desktopOne = std::make_unique(QString::fromUtf8("desktop-1")); + virtualDesktops.insert(std::next(virtualDesktops.begin(), 0), std::move(desktopOne)); + + currentVirtualDesktop = QString::fromUtf8( "desktop-1" ); +} + +void LXQtWlrootsWaylandWorkspaceInfo::addDesktop(const QString &, quint32) +{ +} + +QVariant LXQtWlrootsWaylandWorkspaceInfo::currentDesktop() const +{ + return currentVirtualDesktop; +} + +int LXQtWlrootsWaylandWorkspaceInfo::numberOfDesktops() const +{ + // will always be 1 (at least until ext-workspace becomes available) + return 1; +} + +quint32 LXQtWlrootsWaylandWorkspaceInfo::position(const QVariant &desktop) const +{ + return 0; +} + +QVariantList LXQtWlrootsWaylandWorkspaceInfo::desktopIds() const +{ + return { virtualDesktops.at( 0 ).get()->id }; +} + +QStringList LXQtWlrootsWaylandWorkspaceInfo::desktopNames() const +{ + return { virtualDesktops.at( 0 ).get()->name }; +} + +int LXQtWlrootsWaylandWorkspaceInfo::desktopLayoutRows() const +{ + return 1; +} + +void LXQtWlrootsWaylandWorkspaceInfo::requestActivate(const QVariant &) +{ +} + +void LXQtWlrootsWaylandWorkspaceInfo::requestCreateDesktop(quint32 position) +{ +} + +void LXQtWlrootsWaylandWorkspaceInfo::requestRemoveDesktop(quint32 position) +{ +} diff --git a/panel/backends/wayland/wlroots/lxqtwlrvirtualdesktop.h b/panel/backends/wayland/wlroots/lxqtwlrvirtualdesktop.h new file mode 100644 index 000000000..f20020d5e --- /dev/null +++ b/panel/backends/wayland/wlroots/lxqtwlrvirtualdesktop.h @@ -0,0 +1,73 @@ +#ifndef LXQTWlrootsVIRTUALDESKTOP_H +#define LXQTWlrootsVIRTUALDESKTOP_H + +#include +#include + +class LXQtWlrootsVirtualDesktop : public QObject +{ + Q_OBJECT +public: + LXQtWlrootsVirtualDesktop(const QString &id); + ~LXQtWlrootsVirtualDesktop(); + const QString id; + QString name; +Q_SIGNALS: + void done(); + void activated(); + void nameChanged(); +}; + +class LXQtWlrootsVirtualDesktopManagment : public QObject +{ + Q_OBJECT +public: + LXQtWlrootsVirtualDesktopManagment(); + ~LXQtWlrootsVirtualDesktopManagment(); + +signals: + void desktopCreated(const QString &id, quint32 position); + void desktopRemoved(const QString &id); + void rowsChanged(const quint32 rows); +}; + +class Q_DECL_HIDDEN LXQtWlrootsWaylandWorkspaceInfo : public QObject +{ + Q_OBJECT +public: + LXQtWlrootsWaylandWorkspaceInfo(); + + QVariant currentVirtualDesktop; + std::vector> virtualDesktops; + std::unique_ptr virtualDesktopManagement; + const quint32 rows = 1; + + typedef std::vector>::const_iterator VirtualDesktopsIterator; + + VirtualDesktopsIterator findDesktop(const QString &id) const; + + QString getDesktopName(int pos) const; + QString getDesktopId(int pos) const; + + void init(); + void addDesktop(const QString &id, quint32 pos); + QVariant currentDesktop() const; + int numberOfDesktops() const; + QVariantList desktopIds() const; + QStringList desktopNames() const; + quint32 position(const QVariant &desktop) const; + int desktopLayoutRows() const; + void requestActivate(const QVariant &desktop); + void requestCreateDesktop(quint32 position); + void requestRemoveDesktop(quint32 position); + +signals: + void currentDesktopChanged(); + void numberOfDesktopsChanged(); + void navigationWrappingAroundChanged(); + void desktopIdsChanged(); + void desktopNameChanged(quint32 position); + void desktopLayoutRowsChanged(); +}; + +#endif // LXQTWlrootsVIRTUALDESKTOP_H diff --git a/panel/backends/wayland/wlroots/protocols/wlr-foreign-toplevel-management-unstable-v1.xml b/panel/backends/wayland/wlroots/protocols/wlr-foreign-toplevel-management-unstable-v1.xml new file mode 100644 index 000000000..108133715 --- /dev/null +++ b/panel/backends/wayland/wlroots/protocols/wlr-foreign-toplevel-management-unstable-v1.xml @@ -0,0 +1,270 @@ + + + + Copyright © 2018 Ilia Bozhinov + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + The purpose of this protocol is to enable the creation of taskbars + and docks by providing them with a list of opened applications and + letting them request certain actions on them, like maximizing, etc. + + After a client binds the zwlr_foreign_toplevel_manager_v1, each opened + toplevel window will be sent via the toplevel event + + + + + This event is emitted whenever a new toplevel window is created. It + is emitted for all toplevels, regardless of the app that has created + them. + + All initial details of the toplevel(title, app_id, states, etc.) will + be sent immediately after this event via the corresponding events in + zwlr_foreign_toplevel_handle_v1. + + + + + + + Indicates the client no longer wishes to receive events for new toplevels. + However the compositor may emit further toplevel_created events, until + the finished event is emitted. + + The client must not send any more requests after this one. + + + + + + This event indicates that the compositor is done sending events to the + zwlr_foreign_toplevel_manager_v1. The server will destroy the object + immediately after sending this request, so it will become invalid and + the client should free any resources associated with it. + + + + + + + A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel + window. Each app may have multiple opened toplevels. + + Each toplevel has a list of outputs it is visible on, conveyed to the + client with the output_enter and output_leave events. + + + + + This event is emitted whenever the title of the toplevel changes. + + + + + + + This event is emitted whenever the app-id of the toplevel changes. + + + + + + + This event is emitted whenever the toplevel becomes visible on + the given output. A toplevel may be visible on multiple outputs. + + + + + + + This event is emitted whenever the toplevel stops being visible on + the given output. It is guaranteed that an entered-output event + with the same output has been emitted before this event. + + + + + + + Requests that the toplevel be maximized. If the maximized state actually + changes, this will be indicated by the state event. + + + + + + Requests that the toplevel be unmaximized. If the maximized state actually + changes, this will be indicated by the state event. + + + + + + Requests that the toplevel be minimized. If the minimized state actually + changes, this will be indicated by the state event. + + + + + + Requests that the toplevel be unminimized. If the minimized state actually + changes, this will be indicated by the state event. + + + + + + Request that this toplevel be activated on the given seat. + There is no guarantee the toplevel will be actually activated. + + + + + + + The different states that a toplevel can have. These have the same meaning + as the states with the same names defined in xdg-toplevel + + + + + + + + + + + This event is emitted immediately after the zlw_foreign_toplevel_handle_v1 + is created and each time the toplevel state changes, either because of a + compositor action or because of a request in this protocol. + + + + + + + + This event is sent after all changes in the toplevel state have been + sent. + + This allows changes to the zwlr_foreign_toplevel_handle_v1 properties + to be seen as atomic, even if they happen via multiple events. + + + + + + Send a request to the toplevel to close itself. The compositor would + typically use a shell-specific method to carry out this request, for + example by sending the xdg_toplevel.close event. However, this gives + no guarantees the toplevel will actually be destroyed. If and when + this happens, the zwlr_foreign_toplevel_handle_v1.closed event will + be emitted. + + + + + + The rectangle of the surface specified in this request corresponds to + the place where the app using this protocol represents the given toplevel. + It can be used by the compositor as a hint for some operations, e.g + minimizing. The client is however not required to set this, in which + case the compositor is free to decide some default value. + + If the client specifies more than one rectangle, only the last one is + considered. + + The dimensions are given in surface-local coordinates. + Setting width=height=0 removes the already-set rectangle. + + + + + + + + + + + + + + + + This event means the toplevel has been destroyed. It is guaranteed there + won't be any more events for this zwlr_foreign_toplevel_handle_v1. The + toplevel itself becomes inert so any requests will be ignored except the + destroy request. + + + + + + Destroys the zwlr_foreign_toplevel_handle_v1 object. + + This request should be called either when the client does not want to + use the toplevel anymore or after the closed event to finalize the + destruction of the object. + + + + + + + + Requests that the toplevel be fullscreened on the given output. If the + fullscreen state and/or the outputs the toplevel is visible on actually + change, this will be indicated by the state and output_enter/leave + events. + + The output parameter is only a hint to the compositor. Also, if output + is NULL, the compositor should decide which output the toplevel will be + fullscreened on, if at all. + + + + + + + Requests that the toplevel be unfullscreened. If the fullscreen state + actually changes, this will be indicated by the state event. + + + + + + + + This event is emitted whenever the parent of the toplevel changes. + + No event is emitted when the parent handle is destroyed by the client. + + + + + diff --git a/panel/backends/xcb/lxqttaskbarbackend_x11.cpp b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp index 822afed51..6037e91d8 100644 --- a/panel/backends/xcb/lxqttaskbarbackend_x11.cpp +++ b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp @@ -535,7 +535,7 @@ void LXQtTaskbarX11Backend::moveApplication(WId windowId) int X = g.center().x(); int Y = g.center().y(); QCursor::setPos(X, Y); - NETRootInfo(m_xcbConnection, NET::WMMoveResize).moveResizeRequest(windowId, X, Y, NET::Move); + // NETRootInfo(m_xcbConnection, NET::WMMoveResize).moveResizeRequest(windowId, X, Y, NET::Move); } void LXQtTaskbarX11Backend::resizeApplication(WId windowId) @@ -553,7 +553,7 @@ void LXQtTaskbarX11Backend::resizeApplication(WId windowId) int X = g.bottomRight().x(); int Y = g.bottomRight().y(); QCursor::setPos(X, Y); - NETRootInfo(m_xcbConnection, NET::WMMoveResize).moveResizeRequest(windowId, X, Y, NET::BottomRight); + // NETRootInfo(m_xcbConnection, NET::WMMoveResize).moveResizeRequest(windowId, X, Y, NET::BottomRight); } void LXQtTaskbarX11Backend::refreshIconGeometry(WId windowId, QRect const & geom) diff --git a/panel/lxqtpanelapplication.cpp b/panel/lxqtpanelapplication.cpp index a3d2132f1..5518e801e 100644 --- a/panel/lxqtpanelapplication.cpp +++ b/panel/lxqtpanelapplication.cpp @@ -40,14 +40,15 @@ #include "backends/lxqttaskbardummybackend.h" #include "backends/xcb/lxqttaskbarbackend_x11.h" -#include "backends/wayland/lxqttaskbarbackendwayland.h" +#include "backends/wayland/plasma/lxqttaskbarbackendplasma.h" +#include "backends/wayland/wlroots/lxqttaskbarbackendwlr.h" ILXQtTaskbarAbstractBackend *createWMBackend() { if(qGuiApp->nativeInterface()) return new LXQtTaskbarX11Backend; else if(qGuiApp->nativeInterface()) - return new LXQtTaskbarWaylandBackend; + return new LXQtTaskbarWlrootsBackend; qWarning() << "\n" << "ERROR: Could not create a backend for window managment operations.\n" From 11cdbaf385742440c187eb5166ba25cbe0b3130f Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Wed, 27 Mar 2024 00:32:23 +0530 Subject: [PATCH 058/165] Fix a few implementation issues --- .../wayland/plasma/lxqtplasmavirtualdesktop.h | 5 +-- .../wayland/plasma/lxqttaskbarbackendplasma.h | 5 +-- .../plasma/lxqttaskbarplasmawindowmanagment.h | 5 +-- .../wayland/wlroots/lxqttaskbarbackendwlr.h | 5 +-- .../wlroots/lxqttaskbarwlrwindowmanagment.cpp | 40 ++++++++++++++----- .../wlroots/lxqttaskbarwlrwindowmanagment.h | 6 +-- .../wayland/wlroots/lxqtwlrvirtualdesktop.h | 5 +-- 7 files changed, 36 insertions(+), 35 deletions(-) diff --git a/panel/backends/wayland/plasma/lxqtplasmavirtualdesktop.h b/panel/backends/wayland/plasma/lxqtplasmavirtualdesktop.h index fff025213..825f0de83 100644 --- a/panel/backends/wayland/plasma/lxqtplasmavirtualdesktop.h +++ b/panel/backends/wayland/plasma/lxqtplasmavirtualdesktop.h @@ -1,5 +1,4 @@ -#ifndef LXQTPLASMAVIRTUALDESKTOP_H -#define LXQTPLASMAVIRTUALDESKTOP_H +#pragma once #include #include @@ -87,5 +86,3 @@ class Q_DECL_HIDDEN LXQtPlasmaWaylandWorkspaceInfo : public QObject void desktopNameChanged(quint32 position); void desktopLayoutRowsChanged(); }; - -#endif // LXQTPLASMAVIRTUALDESKTOP_H diff --git a/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.h b/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.h index 31b78f6c6..f47d724c8 100644 --- a/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.h +++ b/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.h @@ -1,5 +1,4 @@ -#ifndef LXQTTASKBARBACKENDPLASMA_H -#define LXQTTASKBARBACKENDPLASMA_H +#pragma once #include "../../ilxqttaskbarabstractbackend.h" @@ -91,5 +90,3 @@ class LXQtTaskbarPlasmaBackend : public ILXQtTaskbarAbstractBackend // key=leader, values=transient children QMultiHash transientsDemandingAttention; }; - -#endif // LXQTTASKBARBACKENDWAYLAND_H diff --git a/panel/backends/wayland/plasma/lxqttaskbarplasmawindowmanagment.h b/panel/backends/wayland/plasma/lxqttaskbarplasmawindowmanagment.h index 976ac53e2..c05dbc5d5 100644 --- a/panel/backends/wayland/plasma/lxqttaskbarplasmawindowmanagment.h +++ b/panel/backends/wayland/plasma/lxqttaskbarplasmawindowmanagment.h @@ -1,5 +1,4 @@ -#ifndef LXQTTASKBARPLASMAWINDOWMANAGMENT_H -#define LXQTTASKBARPLASMAWINDOWMANAGMENT_H +#pragma once #include #include @@ -158,5 +157,3 @@ class LXQtTaskBarPlasmaWindowManagment : public QWaylandClientExtensionTemplate< // private: // WaylandTasksModel *q; // }; - -#endif // LXQTTASKBARPLASMAWINDOWMANAGMENT_H diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h index 9b4348afd..363ff698b 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h @@ -1,5 +1,4 @@ -#ifndef LXQTTASKBARBACKENDWLROOTS_H -#define LXQTTASKBARBACKENDWLROOTS_H +#pragma once #include "../../ilxqttaskbarabstractbackend.h" @@ -91,5 +90,3 @@ class LXQtTaskbarWlrootsBackend : public ILXQtTaskbarAbstractBackend // key=leader, values=transient children QMultiHash transientsDemandingAttention; }; - -#endif // LXQTTASKBARBACKENDWAYLAND_H diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp index 214b6f5d1..33fb27234 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp @@ -37,6 +37,7 @@ LXQtTaskBarWlrootsWindowManagment::~LXQtTaskBarWlrootsWindowManagment() void LXQtTaskBarWlrootsWindowManagment::zwlr_foreign_toplevel_manager_v1_toplevel(struct ::zwlr_foreign_toplevel_handle_v1 *toplevel) { + qDebug() << "new toplevel created"; emit windowCreated( new LXQtTaskBarWlrootsWindow(toplevel) ); } @@ -46,15 +47,10 @@ void LXQtTaskBarWlrootsWindowManagment::zwlr_foreign_toplevel_manager_v1_topleve */ LXQtTaskBarWlrootsWindow::LXQtTaskBarWlrootsWindow(::zwlr_foreign_toplevel_handle_v1 *id) - : zwlr_foreign_toplevel_handle_v1(id), - uuid(QString::number(0)) + : zwlr_foreign_toplevel_handle_v1(id) { } -/* - * LXQtTaskBarWlrootsWindow - */ - LXQtTaskBarWlrootsWindow::~LXQtTaskBarWlrootsWindow() { destroy(); @@ -85,10 +81,34 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_leave(stru void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_state(wl_array *state) { - // emit activeChanged(); - // emit maximizedChanged(); - // emit minimizedChanged(); - // emit fullscreenChanged(); + QFlags wlrState; + + uint32_t* statePtr = static_cast(state->data); + for (size_t i = 0; i < state->size / sizeof(uint32_t); i++) { + switch (statePtr[i]) { + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED: + wlrState |= state_maximized; + break; + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN: + wlrState |= state_fullscreen; + break; + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED: + wlrState |= state_minimized; + break; + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED: + wlrState |= state_activated; + break; + } + } + + if ( windowState.testFlag( state_maximized ) ^ wlrState.testFlag( state_maximized ) ) + emit maximizedChanged(); + if ( windowState.testFlag( state_minimized ) ^ wlrState.testFlag( state_minimized ) ) + emit minimizedChanged(); + if ( windowState.testFlag( state_fullscreen ) ^ wlrState.testFlag( state_fullscreen ) ) + emit fullscreenChanged(); + if ( windowState.testFlag( state_activated ) ^ wlrState.testFlag( state_activated ) ) + emit activeChanged(); } void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_done() diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h index d488ce15b..767f11f32 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h @@ -1,5 +1,4 @@ -#ifndef LXQTTASKBARWlrootsWINDOWMANAGMENT_H -#define LXQTTASKBARWlrootsWINDOWMANAGMENT_H +#pragma once #include #include @@ -46,7 +45,6 @@ class LXQtTaskBarWlrootsWindow : public QObject, inline WId getWindowId() const { return reinterpret_cast(this); } using state = QtWayland::zwlr_foreign_toplevel_handle_v1::state; - const QString uuid; QString title; QString appId; QIcon icon; @@ -83,5 +81,3 @@ class LXQtTaskBarWlrootsWindow : public QObject, QMetaObject::Connection parentWindowUnmappedConnection; }; - -#endif // LXQTTASKBARWlrootsWINDOWMANAGMENT_H diff --git a/panel/backends/wayland/wlroots/lxqtwlrvirtualdesktop.h b/panel/backends/wayland/wlroots/lxqtwlrvirtualdesktop.h index f20020d5e..4853c43a6 100644 --- a/panel/backends/wayland/wlroots/lxqtwlrvirtualdesktop.h +++ b/panel/backends/wayland/wlroots/lxqtwlrvirtualdesktop.h @@ -1,5 +1,4 @@ -#ifndef LXQTWlrootsVIRTUALDESKTOP_H -#define LXQTWlrootsVIRTUALDESKTOP_H +#pragma once #include #include @@ -69,5 +68,3 @@ class Q_DECL_HIDDEN LXQtWlrootsWaylandWorkspaceInfo : public QObject void desktopNameChanged(quint32 position); void desktopLayoutRowsChanged(); }; - -#endif // LXQTWlrootsVIRTUALDESKTOP_H From dc5a16c58bd653fe2565efd1882b86cdce2ac019 Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Wed, 27 Mar 2024 01:12:17 +0530 Subject: [PATCH 059/165] Updates --- .../wayland/wlroots/lxqttaskbarbackendwlr.cpp | 23 +++---------------- .../wlroots/lxqttaskbarwlrwindowmanagment.cpp | 21 ++++++++++++++++- .../wlroots/lxqttaskbarwlrwindowmanagment.h | 2 ++ 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp index 7f9590a2c..16dd774f9 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp @@ -230,26 +230,7 @@ bool LXQtTaskbarWlrootsBackend::raiseWindow(WId windowId, bool onCurrentWorkSpac if(!window) return false; - // Pull forward any transient demanding attention. - if (auto *transientDemandingAttention = transientsDemandingAttention.value(window)) - { - window = transientDemandingAttention; - } - else - { - // TODO Shouldn't KWin take care of that? - // Bringing a transient to the front usually brings its parent with it - // but focus is not handled properly. - // TODO take into account d->lastActivation instead - // of just taking the first one. - while (transients.key(window)) - { - window = transients.key(window); - } - } - - window->set_state(LXQtTaskBarWlrootsWindow::state::state_activated, LXQtTaskBarWlrootsWindow::state::state_activated); - return true; + window->activate(); } bool LXQtTaskbarWlrootsBackend::closeWindow(WId windowId) @@ -469,6 +450,8 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) windows.emplace_back(window); updateWindowAcceptance(window); } + + emit windowAdded( window->getWindowId() ); } bool LXQtTaskbarWlrootsBackend::acceptWindow(LXQtTaskBarWlrootsWindow *window) const diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp index 33fb27234..7887323ac 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp @@ -10,10 +10,24 @@ #include #include +#include + #include #include #include +wl_seat *get_seat() { + QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface(); + + if ( !native ) { + return nullptr; + } + + struct wl_seat *seat = reinterpret_cast(native->nativeResourceForIntegration( "wl_seat" ) ); + + return seat; +} + /* * LXQtTaskBarWlrootsWindowManagment */ @@ -56,6 +70,11 @@ LXQtTaskBarWlrootsWindow::~LXQtTaskBarWlrootsWindow() destroy(); } +void LXQtTaskBarWlrootsWindow::activate() +{ + zwlr_foreign_toplevel_handle_v1::activate(get_seat()); +} + void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_title(const QString &title) { emit titleChanged(); @@ -123,7 +142,7 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_closed() void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_parent(struct ::zwlr_foreign_toplevel_handle_v1 *parent) { - setParentWindow( new LXQtTaskBarWlrootsWindow(parent)); + // setParentWindow(new LXQtTaskBarWlrootsWindow(parent)); } void LXQtTaskBarWlrootsWindow::setParentWindow(LXQtTaskBarWlrootsWindow *parent) diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h index 767f11f32..f4a2c6285 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h @@ -44,6 +44,8 @@ class LXQtTaskBarWlrootsWindow : public QObject, inline WId getWindowId() const { return reinterpret_cast(this); } + void activate(); + using state = QtWayland::zwlr_foreign_toplevel_handle_v1::state; QString title; QString appId; From 5a0266658896647774ef660baaac337c754f4e62 Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Wed, 27 Mar 2024 01:14:24 +0530 Subject: [PATCH 060/165] Update tasks list on window closed --- panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp index 16dd774f9..b0c43223c 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp @@ -349,6 +349,8 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) activeWindow = nullptr; emit activeWindowChanged(0); } + + emit windowRemoved(window->getWindowId()); }; connect(window, &LXQtTaskBarWlrootsWindow::closed, this, removeWindow); From e247e5b78e7775176bbf9810ab2e572f9e516d91 Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Wed, 27 Mar 2024 01:20:08 +0530 Subject: [PATCH 061/165] Few more updates --- .../wayland/wlroots/lxqttaskbarbackendwlr.cpp | 11 ++++------- .../backends/wayland/wlroots/lxqttaskbarbackendwlr.h | 1 - .../wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp | 4 ++-- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp index b0c43223c..0eda67dfb 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp @@ -231,6 +231,7 @@ bool LXQtTaskbarWlrootsBackend::raiseWindow(WId windowId, bool onCurrentWorkSpac return false; window->activate(); + return true; } bool LXQtTaskbarWlrootsBackend::closeWindow(WId windowId) @@ -356,7 +357,7 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) connect(window, &LXQtTaskBarWlrootsWindow::closed, this, removeWindow); connect(window, &LXQtTaskBarWlrootsWindow::titleChanged, this, [window, this] { - updateWindowAcceptance(window); + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Title)); }); connect(window, &LXQtTaskBarWlrootsWindow::appIdChanged, this, [window, this] { @@ -433,7 +434,7 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) }); auto stateChanged = [window, this] { - updateWindowAcceptance(window); + // updateWindowAcceptance(window); }; connect(window, &LXQtTaskBarWlrootsWindow::fullscreenChanged, this, stateChanged); @@ -450,7 +451,7 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) else { windows.emplace_back(window); - updateWindowAcceptance(window); + // updateWindowAcceptance(window); } emit windowAdded( window->getWindowId() ); @@ -464,10 +465,6 @@ bool LXQtTaskbarWlrootsBackend::acceptWindow(LXQtTaskBarWlrootsWindow *window) c return true; } -void LXQtTaskbarWlrootsBackend::updateWindowAcceptance(LXQtTaskBarWlrootsWindow *) -{ -} - LXQtTaskBarWlrootsWindow *LXQtTaskbarWlrootsBackend::getWindow(WId windowId) const { for(auto &window : std::as_const(windows)) diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h index 363ff698b..61ed5d81a 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h @@ -73,7 +73,6 @@ class LXQtTaskbarWlrootsBackend : public ILXQtTaskbarAbstractBackend private: void addWindow(LXQtTaskBarWlrootsWindow *window); bool acceptWindow(LXQtTaskBarWlrootsWindow *window) const; - void updateWindowAcceptance(LXQtTaskBarWlrootsWindow *window); private: LXQtTaskBarWlrootsWindow *getWindow(WId windowId) const; diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp index 7887323ac..e6ece8834 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp @@ -77,14 +77,14 @@ void LXQtTaskBarWlrootsWindow::activate() void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_title(const QString &title) { - emit titleChanged(); this->title = title; + emit titleChanged(); } void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_app_id(const QString &app_id) { - emit appIdChanged(); this->appId = app_id; + emit appIdChanged(); // Code to get the icon needs to be inserted here } From c6b353fecda69122ecb44d80e0e44244a393b1fb Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Wed, 27 Mar 2024 15:13:47 +0530 Subject: [PATCH 062/165] Allow both wlroots and plasma backends to coexist --- panel/CMakeLists.txt | 1 + panel/backends/wayland/CMakeLists.txt | 12 + .../wayland/lxqttaskbarbackendwayland.cpp | 219 ++++++++++++++++++ .../wayland/lxqttaskbarbackendwayland.h | 86 +++++++ .../plasma/lxqttaskbarbackendplasma.cpp | 1 + panel/lxqtpanelapplication.cpp | 12 +- plugin-taskbar/lxqttaskbarplugin.cpp | 1 + 7 files changed, 329 insertions(+), 3 deletions(-) create mode 100644 panel/backends/wayland/lxqttaskbarbackendwayland.cpp create mode 100644 panel/backends/wayland/lxqttaskbarbackendwayland.h diff --git a/panel/CMakeLists.txt b/panel/CMakeLists.txt index f081f2f5f..a302b030a 100644 --- a/panel/CMakeLists.txt +++ b/panel/CMakeLists.txt @@ -7,6 +7,7 @@ add_subdirectory(backends) set(PANEL_BACKENDS lxqt-panel-backend-plasma lxqt-panel-backend-wlroots + lxqt-panel-backend-wayland lxqt-panel-backend-xcb ) diff --git a/panel/backends/wayland/CMakeLists.txt b/panel/backends/wayland/CMakeLists.txt index d23f14f1e..df8f737fa 100644 --- a/panel/backends/wayland/CMakeLists.txt +++ b/panel/backends/wayland/CMakeLists.txt @@ -1,2 +1,14 @@ add_subdirectory(plasma) add_subdirectory(wlroots) + +add_library(lxqt-panel-backend-wayland STATIC + lxqttaskbarbackendwayland.h + lxqttaskbarbackendwayland.cpp +) + +target_link_libraries(lxqt-panel-backend-wayland + Qt6::GuiPrivate + lxqt-panel-backend-common + lxqt-panel-backend-plasma + lxqt-panel-backend-wlroots +) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp new file mode 100644 index 000000000..3e23d1de5 --- /dev/null +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -0,0 +1,219 @@ +#include "lxqttaskbarbackendwayland.h" +#include "plasma/lxqttaskbarbackendplasma.h" +#include "wlroots/lxqttaskbarbackendwlr.h" + +#include + +LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) + : ILXQtTaskbarAbstractBackend(parent) +{ + /** + * HACK: For now, we will simply concentrate on plasma and wlroots. + * Eventually, we should be dynamically loading different plugins. + */ + + /** + * Let's get our DE: + * If plasma/kde is in desktopsList, we'll use the plasma backend. + * If wayfire/sway/labwc/hyprland/wlroots is in desktopsList, we'll use the wlroots backend. + */ + QList desktopsList = qgetenv("XDG_CURRENT_DESKTOP").toLower().split(':'); + qDebug() << "--------------> Current desktop" << desktopsList; + for( QByteArray desktop: desktopsList ) { + if ( desktop == "plasma" || desktop == "kde" ) + { + qDebug() << "--------------> Using plasma backend"; + m_backend = new LXQtTaskbarPlasmaBackend(); + break; + } + + else if ( desktop == "wayfire" ) + { + qDebug() << "--------------> Using wlroots backend"; + m_backend = new LXQtTaskbarWlrootsBackend(); + break; + } + + else if ( desktop == "sway" ) + { + qDebug() << "--------------> Using wlroots backend"; + m_backend = new LXQtTaskbarWlrootsBackend(); + break; + } + + else if ( desktop == "labwc" ) + { + qDebug() << "--------------> Using wlroots backend"; + m_backend = new LXQtTaskbarWlrootsBackend(); + break; + } + + else if ( desktop == "hyprland" ) + { + qDebug() << "--------------> Using wlroots backend"; + m_backend = new LXQtTaskbarWlrootsBackend(); + break; + } + + else + { + // m_backend = nullptr; + } + } +} + + +/************************************************ + * Windows function + ************************************************/ +bool LXQtTaskbarWaylandBackend::supportsAction(WId id, LXQtTaskBarBackendAction act) const +{ + return ( m_backend == nullptr ? false : m_backend->supportsAction(id, act) ); +} + +bool LXQtTaskbarWaylandBackend::reloadWindows() +{ + return ( m_backend == nullptr ? false : m_backend->reloadWindows() ); +} + +QVector LXQtTaskbarWaylandBackend::getCurrentWindows() const +{ + return ( m_backend == nullptr ? QVector() : m_backend->getCurrentWindows() ); +} + +QString LXQtTaskbarWaylandBackend::getWindowTitle(WId id) const +{ + return ( m_backend == nullptr ? QString() : m_backend->getWindowTitle(id) ); +} + +bool LXQtTaskbarWaylandBackend::applicationDemandsAttention(WId id) const +{ + return ( m_backend == nullptr ? false : m_backend->applicationDemandsAttention(id) ); +} + +QIcon LXQtTaskbarWaylandBackend::getApplicationIcon(WId id, int fallbackDevicePixels) const +{ + return ( m_backend == nullptr ? QIcon() : m_backend->getApplicationIcon(id, fallbackDevicePixels) ); +} + +QString LXQtTaskbarWaylandBackend::getWindowClass(WId id) const +{ + return ( m_backend == nullptr ? QString() : m_backend->getWindowClass(id) ); +} + +LXQtTaskBarWindowLayer LXQtTaskbarWaylandBackend::getWindowLayer(WId id) const +{ + return ( m_backend == nullptr ? LXQtTaskBarWindowLayer::Normal : m_backend->getWindowLayer(id) ); +} + +bool LXQtTaskbarWaylandBackend::setWindowLayer(WId id, LXQtTaskBarWindowLayer lyr) +{ + return ( m_backend == nullptr ? false : m_backend->setWindowLayer(id, lyr) ); +} + +LXQtTaskBarWindowState LXQtTaskbarWaylandBackend::getWindowState(WId id) const +{ + return ( m_backend == nullptr ? LXQtTaskBarWindowState::Normal : m_backend->getWindowState(id) ); +} + +bool LXQtTaskbarWaylandBackend::setWindowState(WId id, LXQtTaskBarWindowState state, bool set) +{ + return ( m_backend == nullptr ? false : m_backend->setWindowState(id, state, set) ); +} + +bool LXQtTaskbarWaylandBackend::isWindowActive(WId id) const +{ + return ( m_backend == nullptr ? false : m_backend->isWindowActive(id) ); +} + +bool LXQtTaskbarWaylandBackend::raiseWindow(WId id, bool yes) +{ + return ( m_backend == nullptr ? false : m_backend->raiseWindow(id, yes) ); +} + +bool LXQtTaskbarWaylandBackend::closeWindow(WId id) +{ + return ( m_backend == nullptr ? false : m_backend->closeWindow(id) ); +} + +WId LXQtTaskbarWaylandBackend::getActiveWindow() const +{ + return ( m_backend == nullptr ? 0 : m_backend->getActiveWindow() ); +} + + +/************************************************ + * Workspaces + ************************************************/ +int LXQtTaskbarWaylandBackend::getWorkspacesCount() const +{ + return ( m_backend == nullptr ? 1 : m_backend->getWorkspacesCount() ); // Fake 1 workspace +} + +QString LXQtTaskbarWaylandBackend::getWorkspaceName(int ws) const +{ + return ( m_backend == nullptr ? QString() : m_backend->getWorkspaceName( ws ) ); +} + +int LXQtTaskbarWaylandBackend::getCurrentWorkspace() const +{ + return ( m_backend == nullptr ? 0 : m_backend->getCurrentWorkspace() ); +} + +bool LXQtTaskbarWaylandBackend::setCurrentWorkspace(int ws) +{ + return ( m_backend == nullptr ? false : m_backend->setCurrentWorkspace(ws) ); +} + +int LXQtTaskbarWaylandBackend::getWindowWorkspace(WId id) const +{ + return ( m_backend == nullptr ? 0 : m_backend->getWindowWorkspace( id ) ); +} + +bool LXQtTaskbarWaylandBackend::setWindowOnWorkspace(WId id, int ws) +{ + return ( m_backend == nullptr ? false : m_backend->setWindowOnWorkspace(id, ws) ); +} + +void LXQtTaskbarWaylandBackend::moveApplicationToPrevNextMonitor(WId id, bool next, bool raise) +{ + ( m_backend == nullptr ? void() : m_backend->moveApplicationToPrevNextMonitor( id, next, raise) ); +} + +bool LXQtTaskbarWaylandBackend::isWindowOnScreen(QScreen *scrn, WId id) const +{ + return ( m_backend == nullptr ? false : m_backend->isWindowOnScreen(scrn, id) ); +} + +/************************************************ + * X11 Specific + ************************************************/ +void LXQtTaskbarWaylandBackend::moveApplication(WId id) +{ + ( m_backend == nullptr ? void() : m_backend->moveApplication(id) ); +} + +void LXQtTaskbarWaylandBackend::resizeApplication(WId id) +{ + ( m_backend == nullptr ? void() : m_backend->resizeApplication(id) ); +} + +void LXQtTaskbarWaylandBackend::refreshIconGeometry(WId id, QRect const &rect) +{ + ( m_backend == nullptr ? void() : m_backend->refreshIconGeometry(id, rect) ); +} + +bool LXQtTaskbarWaylandBackend::isAreaOverlapped(const QRect &rect) const +{ + return ( m_backend == nullptr ? false : m_backend->isAreaOverlapped(rect) ); +} + +bool LXQtTaskbarWaylandBackend::isShowingDesktop() const +{ + return ( m_backend == nullptr ? false : m_backend->isShowingDesktop() ); +} + +bool LXQtTaskbarWaylandBackend::showDesktop(bool yes) +{ + return ( m_backend == nullptr ? false : m_backend->showDesktop(yes) ); +} diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.h b/panel/backends/wayland/lxqttaskbarbackendwayland.h new file mode 100644 index 000000000..25027a567 --- /dev/null +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.h @@ -0,0 +1,86 @@ +#pragma once + +#include "../ilxqttaskbarabstractbackend.h" + +class LXQtTaskbarWaylandBackend : public ILXQtTaskbarAbstractBackend +{ + Q_OBJECT + +public: + explicit LXQtTaskbarWaylandBackend(QObject *parent = nullptr); + + // Backend + bool supportsAction(WId windowId, LXQtTaskBarBackendAction action) const override; + + // Windows + bool reloadWindows() override; + + QVector getCurrentWindows() const override; + + QString getWindowTitle(WId windowId) const override; + + bool applicationDemandsAttention(WId windowId) const override; + + QIcon getApplicationIcon(WId windowId, int fallbackDevicePixels) const override; + + QString getWindowClass(WId windowId) const override; + + LXQtTaskBarWindowLayer getWindowLayer(WId windowId) const override; + bool setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) override; + + LXQtTaskBarWindowState getWindowState(WId windowId) const override; + bool setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set = true) override; + + bool isWindowActive(WId windowId) const override; + bool raiseWindow(WId windowId, bool onCurrentWorkSpace) override; + + bool closeWindow(WId windowId) override; + + WId getActiveWindow() const override; + + // Workspaces + int getWorkspacesCount() const override; + QString getWorkspaceName(int idx) const override; + + int getCurrentWorkspace() const override; + bool setCurrentWorkspace(int idx) override; + + int getWindowWorkspace(WId windowId) const override; + bool setWindowOnWorkspace(WId windowId, int idx) override; + + void moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) override; + + bool isWindowOnScreen(QScreen *screen, WId windowId) const override; + + // X11 Specific + void moveApplication(WId windowId) override; + void resizeApplication(WId windowId) override; + + void refreshIconGeometry(WId windowId, const QRect &geom) override; + + // Panel internal + bool isAreaOverlapped(const QRect& area) const override; + + // Show Destop + bool isShowingDesktop() const override; + bool showDesktop(bool value) override; + +private: + ILXQtTaskbarAbstractBackend *m_backend = nullptr; + +signals: + void reloaded(); + + // Windows + void windowAdded(WId windowId); + void windowRemoved(WId windowId); + void windowPropertyChanged(WId windowId, int prop); + + // Workspaces + void workspacesCountChanged(); + void workspaceNameChanged(int idx); + void currentWorkspaceChanged(int idx); + + // TODO: needed? + void activeWindowChanged(WId windowId); +}; diff --git a/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.cpp b/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.cpp index ad8b2332f..0b7d206de 100644 --- a/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.cpp +++ b/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.cpp @@ -118,6 +118,7 @@ bool LXQtTaskbarPlasmaBackend::reloadWindows() QVector LXQtTaskbarPlasmaBackend::getCurrentWindows() const { + qDebug() << "--------------> Retrieving current window list"; QVector wids; wids.reserve(wids.size()); diff --git a/panel/lxqtpanelapplication.cpp b/panel/lxqtpanelapplication.cpp index 5518e801e..024ba4c05 100644 --- a/panel/lxqtpanelapplication.cpp +++ b/panel/lxqtpanelapplication.cpp @@ -40,15 +40,21 @@ #include "backends/lxqttaskbardummybackend.h" #include "backends/xcb/lxqttaskbarbackend_x11.h" +#include "backends/wayland/lxqttaskbarbackendwayland.h" #include "backends/wayland/plasma/lxqttaskbarbackendplasma.h" #include "backends/wayland/wlroots/lxqttaskbarbackendwlr.h" ILXQtTaskbarAbstractBackend *createWMBackend() { - if(qGuiApp->nativeInterface()) + if(qGuiApp->nativeInterface()){ + qDebug() << "--------------> X11Application"; return new LXQtTaskbarX11Backend; - else if(qGuiApp->nativeInterface()) - return new LXQtTaskbarWlrootsBackend; + } + + else if(qGuiApp->nativeInterface()){ + qDebug() << "--------------> WaylandApplication"; + return new LXQtTaskbarWaylandBackend; + } qWarning() << "\n" << "ERROR: Could not create a backend for window managment operations.\n" diff --git a/plugin-taskbar/lxqttaskbarplugin.cpp b/plugin-taskbar/lxqttaskbarplugin.cpp index 759e6db46..fbdd5b305 100644 --- a/plugin-taskbar/lxqttaskbarplugin.cpp +++ b/plugin-taskbar/lxqttaskbarplugin.cpp @@ -37,6 +37,7 @@ LXQtTaskBarPlugin::LXQtTaskBarPlugin(const ILXQtPanelPluginStartupInfo &startupI ILXQtPanelPlugin(startupInfo) { + qDebug() << "--------------> Init plugin taskbar"; mTaskBar = new LXQtTaskBar(this); } From a01b21515be45dbc850ce701de9ea98c7dcd7f7f Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Wed, 27 Mar 2024 16:31:41 +0530 Subject: [PATCH 063/165] Check for kwin_wayland in XDG_CURRENT_DESKTOP; Add debug messages --- .../wayland/lxqttaskbarbackendwayland.cpp | 14 +++++--- .../wayland/wlroots/lxqttaskbarbackendwlr.cpp | 34 +++++-------------- .../wlroots/lxqttaskbarwlrwindowmanagment.cpp | 18 +++++++++- .../wlroots/lxqttaskbarwlrwindowmanagment.h | 6 ++++ plugin-taskbar/lxqttaskbar.cpp | 3 ++ 5 files changed, 44 insertions(+), 31 deletions(-) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index 3e23d1de5..ad523faeb 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -20,7 +20,7 @@ LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) QList desktopsList = qgetenv("XDG_CURRENT_DESKTOP").toLower().split(':'); qDebug() << "--------------> Current desktop" << desktopsList; for( QByteArray desktop: desktopsList ) { - if ( desktop == "plasma" || desktop == "kde" ) + if ( desktop == "plasma" || desktop == "kde" || desktop == "kwin_wayland" ) { qDebug() << "--------------> Using plasma backend"; m_backend = new LXQtTaskbarPlasmaBackend(); @@ -29,28 +29,28 @@ LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) else if ( desktop == "wayfire" ) { - qDebug() << "--------------> Using wlroots backend"; + qDebug() << "--------------> Using wayfire backend"; m_backend = new LXQtTaskbarWlrootsBackend(); break; } else if ( desktop == "sway" ) { - qDebug() << "--------------> Using wlroots backend"; + qDebug() << "--------------> Using sway backend"; m_backend = new LXQtTaskbarWlrootsBackend(); break; } else if ( desktop == "labwc" ) { - qDebug() << "--------------> Using wlroots backend"; + qDebug() << "--------------> Using labwc backend"; m_backend = new LXQtTaskbarWlrootsBackend(); break; } else if ( desktop == "hyprland" ) { - qDebug() << "--------------> Using wlroots backend"; + qDebug() << "--------------> Using hyprland backend"; m_backend = new LXQtTaskbarWlrootsBackend(); break; } @@ -60,6 +60,10 @@ LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) // m_backend = nullptr; } } + + if ( m_backend == nullptr ) { + qDebug() << "--------------> Using dummy backend. No window management will be done"; + } } diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp index 0eda67dfb..ccabe0c10 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp @@ -29,31 +29,9 @@ LXQtTaskbarWlrootsBackend::LXQtTaskbarWlrootsBackend(QObject *parent) : m_workspaceInfo.reset(new LXQtWlrootsWaylandWorkspaceInfo); connect(m_managment.get(), &LXQtTaskBarWlrootsWindowManagment::windowCreated, this, [this](LXQtTaskBarWlrootsWindow *window) { + qDebug() << "--------------> window created" << window->title << window->appId; addWindow(window); }); - - // connect(m_managment.get(), &LXQtTaskBarWlrootsWindowManagment::stackingOrderChanged, - // this, [this](const QString &order) { - // // stackingOrder = order.split(QLatin1Char(';')); - // // for (const auto &window : std::as_const(windows)) { - // // this->dataChanged(window.get(), StackingOrder); - // // } - // }); - - connect(m_workspaceInfo.get(), &LXQtWlrootsWaylandWorkspaceInfo::currentDesktopChanged, this, - [this](){ - int idx = m_workspaceInfo->position(m_workspaceInfo->currentDesktop()); - idx += 1; // Make 1-based - emit currentWorkspaceChanged(idx); - }); - - connect(m_workspaceInfo.get(), &LXQtWlrootsWaylandWorkspaceInfo::numberOfDesktopsChanged, - this, &ILXQtTaskbarAbstractBackend::workspacesCountChanged); - - connect(m_workspaceInfo.get(), &LXQtWlrootsWaylandWorkspaceInfo::desktopNameChanged, - this, [this](int idx) { - emit workspaceNameChanged(idx + 1); // Make 1-based - }); } bool LXQtTaskbarWlrootsBackend::supportsAction(WId, LXQtTaskBarBackendAction action) const @@ -111,6 +89,8 @@ QVector LXQtTaskbarWlrootsBackend::getCurrentWindows() const { wids << window->getWindowId(); } + + qDebug() << "--------------> current windows" << wids; return wids; } @@ -323,6 +303,12 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) return; } + /** Add the window once it's ready */ + connect(window, &LXQtTaskBarWlrootsWindow::windowReady, this, [window, this] { + qDebug() << "--------------> windowReady; adding window"; + emit windowAdded( window->getWindowId() ); + }); + auto removeWindow = [window, this] { auto it = findWindow(windows, window); @@ -453,8 +439,6 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) windows.emplace_back(window); // updateWindowAcceptance(window); } - - emit windowAdded( window->getWindowId() ); } bool LXQtTaskbarWlrootsBackend::acceptWindow(LXQtTaskBarWlrootsWindow *window) const diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp index e6ece8834..179d823d9 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp @@ -1,5 +1,6 @@ #include "lxqttaskbarwlrwindowmanagment.h" +#include #include #include #include @@ -63,6 +64,8 @@ void LXQtTaskBarWlrootsWindowManagment::zwlr_foreign_toplevel_manager_v1_topleve LXQtTaskBarWlrootsWindow::LXQtTaskBarWlrootsWindow(::zwlr_foreign_toplevel_handle_v1 *id) : zwlr_foreign_toplevel_handle_v1(id) { + title = QString::fromUtf8( "untitled" ); + appId = QString::fromUtf8( "unknown" ); } LXQtTaskBarWlrootsWindow::~LXQtTaskBarWlrootsWindow() @@ -78,14 +81,27 @@ void LXQtTaskBarWlrootsWindow::activate() void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_title(const QString &title) { this->title = title; + titleRecieved = true; emit titleChanged(); + + if ( titleRecieved && appIdRecieved ) + { + qDebug() << "--------------> windowReady!!" << getWindowId() << title << appId; + emit windowReady(); + } } void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_app_id(const QString &app_id) { this->appId = app_id; + appIdRecieved = true; emit appIdChanged(); - // Code to get the icon needs to be inserted here + + if ( appIdRecieved && titleRecieved ) + { + qDebug() << "--------------> windowReady!!" << getWindowId() << title << appId; + emit windowReady(); + } } void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_enter(struct ::wl_output *output) diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h index f4a2c6285..eb470856c 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h @@ -68,6 +68,9 @@ class LXQtTaskBarWlrootsWindow : public QObject, void closed(); void parentChanged(); + /** We wait to get the title and appId to emit this */ + void windowReady(); + protected: void zwlr_foreign_toplevel_handle_v1_title(const QString &title); void zwlr_foreign_toplevel_handle_v1_app_id(const QString &app_id); @@ -82,4 +85,7 @@ class LXQtTaskBarWlrootsWindow : public QObject, void setParentWindow(LXQtTaskBarWlrootsWindow *parent); QMetaObject::Connection parentWindowUnmappedConnection; + + mutable bool titleRecieved = false; + mutable bool appIdRecieved = false; }; diff --git a/plugin-taskbar/lxqttaskbar.cpp b/plugin-taskbar/lxqttaskbar.cpp index 43090c396..94b9a6ab9 100644 --- a/plugin-taskbar/lxqttaskbar.cpp +++ b/plugin-taskbar/lxqttaskbar.cpp @@ -251,6 +251,8 @@ void LXQtTaskBar::addWindow(WId window) // If grouping disabled group behaves like regular button const QString group_id = mGroupingEnabled ? mBackend->getWindowClass(window) : QString::number(window); + qDebug() << "-------------->" << group_id; + LXQtTaskGroup *group = nullptr; auto i_group = mKnownWindows.find(window); if (mKnownWindows.end() != i_group) @@ -345,6 +347,7 @@ void LXQtTaskBar::onWindowChanged(WId window, int prop) void LXQtTaskBar::onWindowAdded(WId window) { + qDebug() << "--------------> onWindowAdded" << window; auto const pos = mKnownWindows.find(window); if (mKnownWindows.end() == pos) addWindow(window); From 6ed1031c2212852fcf43ee73268f9f5d63a8d09c Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Wed, 27 Mar 2024 16:54:01 +0530 Subject: [PATCH 064/165] Forward the signals from m_backend lxqttakcarwaylandbacked.cpp --- panel/backends/wayland/lxqttaskbarbackendwayland.cpp | 11 +++++++++++ plugin-taskbar/lxqttaskbar.cpp | 6 +++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index ad523faeb..05d5941ef 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -64,6 +64,17 @@ LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) if ( m_backend == nullptr ) { qDebug() << "--------------> Using dummy backend. No window management will be done"; } + + else { + connect(m_backend, &ILXQtTaskbarAbstractBackend::reloaded, this, &ILXQtTaskbarAbstractBackend::reloaded ); + connect(m_backend, &ILXQtTaskbarAbstractBackend::windowAdded, this, &ILXQtTaskbarAbstractBackend::windowAdded ); + connect(m_backend, &ILXQtTaskbarAbstractBackend::windowRemoved, this, &ILXQtTaskbarAbstractBackend::windowRemoved ); + connect(m_backend, &ILXQtTaskbarAbstractBackend::windowPropertyChanged, this, &ILXQtTaskbarAbstractBackend::windowPropertyChanged ); + connect(m_backend, &ILXQtTaskbarAbstractBackend::workspacesCountChanged, this, &ILXQtTaskbarAbstractBackend::workspacesCountChanged ); + connect(m_backend, &ILXQtTaskbarAbstractBackend::workspaceNameChanged, this, &ILXQtTaskbarAbstractBackend::workspaceNameChanged ); + connect(m_backend, &ILXQtTaskbarAbstractBackend::currentWorkspaceChanged, this, &ILXQtTaskbarAbstractBackend::currentWorkspaceChanged ); + connect(m_backend, &ILXQtTaskbarAbstractBackend::activeWindowChanged, this, &ILXQtTaskbarAbstractBackend::activeWindowChanged ); + } } diff --git a/plugin-taskbar/lxqttaskbar.cpp b/plugin-taskbar/lxqttaskbar.cpp index 94b9a6ab9..4d8b1bb2d 100644 --- a/plugin-taskbar/lxqttaskbar.cpp +++ b/plugin-taskbar/lxqttaskbar.cpp @@ -105,7 +105,11 @@ LXQtTaskBar::LXQtTaskBar(ILXQtPanelPlugin *plugin, QWidget *parent) : QTimer::singleShot(0, this, &LXQtTaskBar::registerShortcuts); connect(mBackend, &ILXQtTaskbarAbstractBackend::windowPropertyChanged, this, &LXQtTaskBar::onWindowChanged); - connect(mBackend, &ILXQtTaskbarAbstractBackend::windowAdded, this, &LXQtTaskBar::onWindowAdded); + // connect(mBackend, &ILXQtTaskbarAbstractBackend::windowAdded, this, &LXQtTaskBar::onWindowAdded); + qDebug() << connect(mBackend, &ILXQtTaskbarAbstractBackend::windowAdded, [=](WId window){ + qDebug() << "--------------> onWindowAdded" << window; + onWindowAdded(window); + }); connect(mBackend, &ILXQtTaskbarAbstractBackend::windowRemoved, this, &LXQtTaskBar::onWindowRemoved); // Consider already fetched windows From 8ae41f36882aca3d2c17aa1e3adbba1d05b6b0be Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Wed, 27 Mar 2024 17:01:56 +0530 Subject: [PATCH 065/165] Get application icons using XdgDesktopFile --- panel/backends/wayland/wlroots/CMakeLists.txt | 3 + panel/backends/wayland/wlroots/icontools.cpp | 150 ++++++++++++++++++ panel/backends/wayland/wlroots/icontools.hpp | 6 + .../wlroots/lxqttaskbarwlrwindowmanagment.cpp | 5 + plugin-taskbar/lxqttaskbar.cpp | 9 +- 5 files changed, 165 insertions(+), 8 deletions(-) create mode 100644 panel/backends/wayland/wlroots/icontools.cpp create mode 100644 panel/backends/wayland/wlroots/icontools.hpp diff --git a/panel/backends/wayland/wlroots/CMakeLists.txt b/panel/backends/wayland/wlroots/CMakeLists.txt index 8f6f1a59b..1b4a511f8 100644 --- a/panel/backends/wayland/wlroots/CMakeLists.txt +++ b/panel/backends/wayland/wlroots/CMakeLists.txt @@ -8,10 +8,12 @@ add_library(lxqt-panel-backend-wlroots STATIC lxqttaskbarbackendwlr.h lxqttaskbarwlrwindowmanagment.h lxqtwlrvirtualdesktop.h + icontools.hpp lxqttaskbarbackendwlr.cpp lxqttaskbarwlrwindowmanagment.cpp lxqtwlrvirtualdesktop.cpp + icontools.cpp ) qt6_generate_wayland_protocol_client_sources(lxqt-panel-backend-wlroots @@ -23,5 +25,6 @@ target_link_libraries(lxqt-panel-backend-wlroots Qt6::GuiPrivate Qt6::WaylandClient Qt6::Concurrent + Qt6Xdg lxqt-panel-backend-common ) diff --git a/panel/backends/wayland/wlroots/icontools.cpp b/panel/backends/wayland/wlroots/icontools.cpp new file mode 100644 index 000000000..d77165d68 --- /dev/null +++ b/panel/backends/wayland/wlroots/icontools.cpp @@ -0,0 +1,150 @@ +#include + +#include "icontools.hpp" + +static inline QString getPixmapIcon( QString name ) { + QStringList paths{ + QString::fromUtf8( "/usr/local/share/pixmaps/" ), + QString::fromUtf8( "/usr/share/pixmaps/" ), + }; + + QStringList sfxs{ + QString::fromUtf8( ".svg" ), QString::fromUtf8( ".png" ), QString::fromUtf8( ".xpm" ) + }; + + for ( QString path: paths ) { + for ( QString sfx: sfxs ) { + if ( QFile::exists( path + name + sfx ) ) { + return path + name + sfx; + } + } + } + + return QString(); +} + + +QIcon getIconForAppId( QString mAppId ) { + if ( mAppId.isEmpty() or (mAppId == QString::fromUtf8("Unknown")) ) { + return QIcon(); + } + + /** Wine apps */ + if ( mAppId.endsWith( QString::fromUtf8(".exe") ) ) { + return QIcon::fromTheme( QString::fromUtf8("wine") ); + } + + /** Check if a theme icon exists called @mAppId */ + if ( QIcon::hasThemeIcon( mAppId ) ) { + return QIcon::fromTheme( mAppId ); + } + + /** Check if the theme icon is @mAppId, but all lower-case letters */ + else if ( QIcon::hasThemeIcon( mAppId.toLower() ) ) { + return QIcon::fromTheme( mAppId.toLower() ); + } + + QStringList appDirs = { + QDir::home().filePath( QString::fromUtf8( ".local/share/applications/" ) ), + QString::fromUtf8( "/usr/local/share/applications/" ), + QString::fromUtf8( "/usr/share/applications/" ), + QString::fromUtf8( "/usr/local/share/games/" ), + QString::fromUtf8( "/usr/share/games/" ), + }; + + /** + * Assume mAppId == desktop-file-name (ideal situation) + * or mAppId.toLower() == desktop-file-name (cheap fallback) + */ + QString iconName; + + for ( QString path: appDirs ) { + /** Get the icon name from desktop (mAppId: as it is) */ + if ( QFile::exists( path + mAppId + QString::fromUtf8(".desktop") ) ) { + QSettings desktop( path + mAppId + QString::fromUtf8(".desktop"), QSettings::IniFormat ); + iconName = desktop.value( QString::fromUtf8( "Desktop Entry/Icon" ) ).toString(); + } + + /** Get the icon name from desktop (mAppId: all lower-case letters) */ + else if ( QFile::exists( path + mAppId.toLower() + QString::fromUtf8(".desktop") ) ) { + QSettings desktop( path + mAppId.toLower() + QString::fromUtf8(".desktop"), QSettings::IniFormat ); + iconName = desktop.value( QString::fromUtf8("Desktop Entry/Icon") ).toString(); + } + + /** No icon specified: try else-where */ + if ( iconName.isEmpty() ) { + continue; + } + + /** We got an iconName, and it's in the current theme */ + if ( QIcon::hasThemeIcon( iconName ) ) { + return QIcon::fromTheme( iconName ); + } + + /** Not a theme icon, but an absolute path */ + else if ( QFile::exists( iconName ) ) { + return QIcon( iconName ); + } + + /** Not theme icon or absolute path. So check /usr/share/pixmaps/ */ + else { + iconName = getPixmapIcon( iconName ); + + if ( not iconName.isEmpty() ) { + return QIcon( iconName ); + } + } + } + + /* Check all desktop files for @mAppId */ + for ( QString path: appDirs ) { + QStringList desktops = QDir( path ).entryList( { QString::fromUtf8( "*.desktop" ) } ); + for ( QString dskf: desktops ) { + QSettings desktop( path + dskf, QSettings::IniFormat ); + + QString exec = desktop.value( QString::fromUtf8("Desktop Entry/Exec"), QString::fromUtf8("abcd1234/-") ).toString(); + QString name = desktop.value( QString::fromUtf8("Desktop Entry/Name"), QString::fromUtf8("abcd1234/-") ).toString(); + QString cls = desktop.value( QString::fromUtf8("Desktop Entry/StartupWMClass"), QString::fromUtf8("abcd1234/-") ).toString(); + + QString execPath( QString::fromUtf8( std::filesystem::path( exec.toStdString() ).filename().c_str() ) ); + + if ( mAppId.compare( execPath, Qt::CaseInsensitive ) == 0 ) { + iconName = desktop.value( QString::fromUtf8("Desktop Entry/Icon") ).toString(); + } + + else if ( mAppId.compare( name, Qt::CaseInsensitive ) == 0 ) { + iconName = desktop.value( QString::fromUtf8("Desktop Entry/Icon") ).toString(); + } + + else if ( mAppId.compare( cls, Qt::CaseInsensitive ) == 0 ) { + iconName = desktop.value( QString::fromUtf8("Desktop Entry/Icon") ).toString(); + } + + if ( not iconName.isEmpty() ) { + if ( QIcon::hasThemeIcon( iconName ) ) { + return QIcon::fromTheme( iconName ); + } + + else if ( QFile::exists( iconName ) ) { + return QIcon( iconName ); + } + + else { + iconName = getPixmapIcon( iconName ); + + if ( not iconName.isEmpty() ) { + return QIcon( iconName ); + } + } + } + } + } + + iconName = getPixmapIcon( iconName ); + + if ( not iconName.isEmpty() ) { + return QIcon( iconName ); + } + + return QIcon(); +} diff --git a/panel/backends/wayland/wlroots/icontools.hpp b/panel/backends/wayland/wlroots/icontools.hpp new file mode 100644 index 000000000..a119bb14a --- /dev/null +++ b/panel/backends/wayland/wlroots/icontools.hpp @@ -0,0 +1,6 @@ +#pragma once + +#include +#include + +QIcon getIconForAppId( QString ); diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp index 179d823d9..c4c073424 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp @@ -1,4 +1,5 @@ #include "lxqttaskbarwlrwindowmanagment.h" +#include "icontools.hpp" #include #include @@ -11,6 +12,8 @@ #include #include +#include + #include #include @@ -97,6 +100,8 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_app_id(const QStr appIdRecieved = true; emit appIdChanged(); + this->icon = getIconForAppId( app_id ); + if ( appIdRecieved && titleRecieved ) { qDebug() << "--------------> windowReady!!" << getWindowId() << title << appId; diff --git a/plugin-taskbar/lxqttaskbar.cpp b/plugin-taskbar/lxqttaskbar.cpp index 4d8b1bb2d..43090c396 100644 --- a/plugin-taskbar/lxqttaskbar.cpp +++ b/plugin-taskbar/lxqttaskbar.cpp @@ -105,11 +105,7 @@ LXQtTaskBar::LXQtTaskBar(ILXQtPanelPlugin *plugin, QWidget *parent) : QTimer::singleShot(0, this, &LXQtTaskBar::registerShortcuts); connect(mBackend, &ILXQtTaskbarAbstractBackend::windowPropertyChanged, this, &LXQtTaskBar::onWindowChanged); - // connect(mBackend, &ILXQtTaskbarAbstractBackend::windowAdded, this, &LXQtTaskBar::onWindowAdded); - qDebug() << connect(mBackend, &ILXQtTaskbarAbstractBackend::windowAdded, [=](WId window){ - qDebug() << "--------------> onWindowAdded" << window; - onWindowAdded(window); - }); + connect(mBackend, &ILXQtTaskbarAbstractBackend::windowAdded, this, &LXQtTaskBar::onWindowAdded); connect(mBackend, &ILXQtTaskbarAbstractBackend::windowRemoved, this, &LXQtTaskBar::onWindowRemoved); // Consider already fetched windows @@ -255,8 +251,6 @@ void LXQtTaskBar::addWindow(WId window) // If grouping disabled group behaves like regular button const QString group_id = mGroupingEnabled ? mBackend->getWindowClass(window) : QString::number(window); - qDebug() << "-------------->" << group_id; - LXQtTaskGroup *group = nullptr; auto i_group = mKnownWindows.find(window); if (mKnownWindows.end() != i_group) @@ -351,7 +345,6 @@ void LXQtTaskBar::onWindowChanged(WId window, int prop) void LXQtTaskBar::onWindowAdded(WId window) { - qDebug() << "--------------> onWindowAdded" << window; auto const pos = mKnownWindows.find(window); if (mKnownWindows.end() == pos) addWindow(window); From c6b68c02cf635b3d0f9de46975a012e00fad3833 Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Wed, 27 Mar 2024 23:54:14 +0530 Subject: [PATCH 066/165] Correct handling of window states event; add support for setting window states --- .../wayland/wlroots/lxqttaskbarbackendwlr.cpp | 45 +++++++--- .../wlroots/lxqttaskbarwlrwindowmanagment.cpp | 86 ++++++++++++------- .../wlroots/lxqttaskbarwlrwindowmanagment.h | 17 +++- 3 files changed, 105 insertions(+), 43 deletions(-) diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp index ccabe0c10..4c42535f6 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp @@ -29,7 +29,6 @@ LXQtTaskbarWlrootsBackend::LXQtTaskbarWlrootsBackend(QObject *parent) : m_workspaceInfo.reset(new LXQtWlrootsWaylandWorkspaceInfo); connect(m_managment.get(), &LXQtTaskBarWlrootsWindowManagment::windowCreated, this, [this](LXQtTaskBarWlrootsWindow *window) { - qDebug() << "--------------> window created" << window->title << window->appId; addWindow(window); }); } @@ -90,7 +89,6 @@ QVector LXQtTaskbarWlrootsBackend::getCurrentWindows() const wids << window->getWindowId(); } - qDebug() << "--------------> current windows" << wids; return wids; } @@ -165,31 +163,59 @@ bool LXQtTaskbarWlrootsBackend::setWindowState(WId windowId, LXQtTaskBarWindowSt { case LXQtTaskBarWindowState::Minimized: { - WlrootsState = LXQtTaskBarWlrootsWindow::state::state_minimized; + if ( set ) { + window->set_minimized(); + } + + else { + window->unset_minimized(); + } + break; } case LXQtTaskBarWindowState::Maximized: case LXQtTaskBarWindowState::MaximizedVertically: case LXQtTaskBarWindowState::MaximizedHorizontally: { - WlrootsState = LXQtTaskBarWlrootsWindow::state::state_maximized; + if ( set ) { + window->set_maximized(); + } + + else { + window->unset_maximized(); + } + break; } case LXQtTaskBarWindowState::Normal: { - WlrootsState = LXQtTaskBarWlrootsWindow::state::state_maximized; - set = !set; //TODO: correct + /** Restore if maximized/minimized */ + if ( window->windowState.testFlag(LXQtTaskBarWlrootsWindow::state_minimized) ) { + window->unset_minimized(); + } + + if ( window->windowState.testFlag(LXQtTaskBarWlrootsWindow::state_maximized) ) { + window->unset_maximized(); + } break; } - case LXQtTaskBarWindowState::RolledUp: + + case LXQtTaskBarWindowState::FullScreen: { + if ( set ) { + window->set_fullscreen(nullptr); + } + + else { + window->unset_fullscreen(); + } break; } + default: return false; } - window->set_state(WlrootsState, set ? WlrootsState : 0); return true; } @@ -305,7 +331,6 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) /** Add the window once it's ready */ connect(window, &LXQtTaskBarWlrootsWindow::windowReady, this, [window, this] { - qDebug() << "--------------> windowReady; adding window"; emit windowAdded( window->getWindowId() ); }); @@ -361,7 +386,7 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) } connect(window, &LXQtTaskBarWlrootsWindow::activeChanged, this, [window, this] { - const bool active = window->windowState & LXQtTaskBarWlrootsWindow::state::state_activated; + const bool active = window->windowState.testFlag( LXQtTaskBarWlrootsWindow::state::state_activated ); LXQtTaskBarWlrootsWindow *effectiveWindow = window; diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp index c4c073424..ebfc8dc7f 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp @@ -55,7 +55,6 @@ LXQtTaskBarWlrootsWindowManagment::~LXQtTaskBarWlrootsWindowManagment() void LXQtTaskBarWlrootsWindowManagment::zwlr_foreign_toplevel_manager_v1_toplevel(struct ::zwlr_foreign_toplevel_handle_v1 *toplevel) { - qDebug() << "new toplevel created"; emit windowCreated( new LXQtTaskBarWlrootsWindow(toplevel) ); } @@ -89,7 +88,6 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_title(const QStri if ( titleRecieved && appIdRecieved ) { - qDebug() << "--------------> windowReady!!" << getWindowId() << title << appId; emit windowReady(); } } @@ -104,7 +102,6 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_app_id(const QStr if ( appIdRecieved && titleRecieved ) { - qDebug() << "--------------> windowReady!!" << getWindowId() << title << appId; emit windowReady(); } } @@ -121,39 +118,70 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_leave(stru void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_state(wl_array *state) { - QFlags wlrState; - - uint32_t* statePtr = static_cast(state->data); - for (size_t i = 0; i < state->size / sizeof(uint32_t); i++) { - switch (statePtr[i]) { - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED: - wlrState |= state_maximized; - break; - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN: - wlrState |= state_fullscreen; - break; - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED: - wlrState |= state_minimized; - break; - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED: - wlrState |= state_activated; - break; + auto *states = static_cast(state->data); + int numStates = static_cast(state->size / sizeof(uint32_t) ); + + for ( int i = 0; i < numStates; i++ ) { + switch ( (uint32_t)states[ i ] ) { + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED: { + m_pendingState.maximized = true; + break; + } + + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED: { + m_pendingState.minimized = true; + break; + } + + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED: { + m_pendingState.activated = true; + break; + } + + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN: { + m_pendingState.fullscreen = true; + break; + } } } +} - if ( windowState.testFlag( state_maximized ) ^ wlrState.testFlag( state_maximized ) ) +void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_done() +{ + /** + * Update windowState flags before emitting the signals. + * Otherwise, windowState.testFlag(...) will return wrong information!! + */ + windowState = QFlags(); + if ( m_pendingState.maximized ) windowState |= state_maximized; + if ( m_pendingState.minimized ) windowState |= state_minimized; + if ( m_pendingState.activated ) windowState |= state_activated; + if ( m_pendingState.fullscreen ) windowState |= state_fullscreen; + + /** Emit the signals. */ + if ( m_viewState.maximized != m_pendingState.maximized ) emit maximizedChanged(); - if ( windowState.testFlag( state_minimized ) ^ wlrState.testFlag( state_minimized ) ) + + if ( m_viewState.minimized != m_pendingState.minimized ) emit minimizedChanged(); - if ( windowState.testFlag( state_fullscreen ) ^ wlrState.testFlag( state_fullscreen ) ) - emit fullscreenChanged(); - if ( windowState.testFlag( state_activated ) ^ wlrState.testFlag( state_activated ) ) + + if ( m_viewState.activated!= m_pendingState.activated ) emit activeChanged(); -} -void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_done() -{ - emit done(); + if ( m_viewState.fullscreen != m_pendingState.fullscreen ) + emit fullscreenChanged(); + + /** Store m_pendingState into m_viewState for the next run */ + m_viewState.maximized = m_pendingState.maximized; + m_viewState.minimized = m_pendingState.minimized; + m_viewState.activated = m_pendingState.activated; + m_viewState.fullscreen = m_pendingState.fullscreen; + + /** Reset m_pendingState for the next run */ + m_pendingState.maximized = false; + m_pendingState.minimized = false; + m_pendingState.activated = false; + m_pendingState.fullscreen = false; } void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_closed() diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h index eb470856c..bc3c2a0b3 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h @@ -53,8 +53,6 @@ class LXQtTaskBarWlrootsWindow : public QObject, QFlags windowState; QPointer parentWindow; - void set_state( LXQtTaskBarWlrootsWindow::state, bool ) {}; - Q_SIGNALS: void titleChanged(); void appIdChanged(); @@ -86,6 +84,17 @@ class LXQtTaskBarWlrootsWindow : public QObject, QMetaObject::Connection parentWindowUnmappedConnection; - mutable bool titleRecieved = false; - mutable bool appIdRecieved = false; + typedef struct view_state_t + { + bool maximized = false; + bool minimized = false; + bool activated = false; + bool fullscreen = false; + } ViewState; + + ViewState m_viewState; + ViewState m_pendingState; + + bool titleRecieved = false; + bool appIdRecieved = false; }; From 5f8e723974110053c1d55f3ff39498ef1bf85ebd Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Thu, 28 Mar 2024 00:32:03 +0530 Subject: [PATCH 067/165] Fix various warnings --- .../wayland/wlroots/lxqttaskbarbackendwlr.cpp | 32 ++++++++----------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp index 4c42535f6..a40a29ebf 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp @@ -37,21 +37,12 @@ bool LXQtTaskbarWlrootsBackend::supportsAction(WId, LXQtTaskBarBackendAction act { switch (action) { - case LXQtTaskBarBackendAction::Move: - return false; - - case LXQtTaskBarBackendAction::Resize: - return false; - case LXQtTaskBarBackendAction::Maximize: return true; case LXQtTaskBarBackendAction::Minimize: return true; - case LXQtTaskBarBackendAction::RollUp: - return false; - case LXQtTaskBarBackendAction::FullScreen: return true; @@ -132,6 +123,7 @@ LXQtTaskBarWindowLayer LXQtTaskbarWlrootsBackend::getWindowLayer(WId) const bool LXQtTaskbarWlrootsBackend::setWindowLayer(WId, LXQtTaskBarWindowLayer) { + return false; } LXQtTaskBarWindowState LXQtTaskbarWlrootsBackend::getWindowState(WId windowId) const @@ -158,7 +150,6 @@ bool LXQtTaskbarWlrootsBackend::setWindowState(WId windowId, LXQtTaskBarWindowSt if(!window) return false; - LXQtTaskBarWlrootsWindow::state WlrootsState; switch (state) { case LXQtTaskBarWindowState::Minimized: @@ -277,40 +268,42 @@ bool LXQtTaskbarWlrootsBackend::setCurrentWorkspace(int) return false; } -int LXQtTaskbarWlrootsBackend::getWindowWorkspace(WId windowId) const +int LXQtTaskbarWlrootsBackend::getWindowWorkspace(WId) const { return 1; } bool LXQtTaskbarWlrootsBackend::setWindowOnWorkspace(WId, int) { + return false; } -void LXQtTaskbarWlrootsBackend::moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) +void LXQtTaskbarWlrootsBackend::moveApplicationToPrevNextMonitor(WId, bool, bool) { } -bool LXQtTaskbarWlrootsBackend::isWindowOnScreen(QScreen *screen, WId windowId) const +bool LXQtTaskbarWlrootsBackend::isWindowOnScreen(QScreen *, WId) const { - // Manage based on output-enter/output-leave + // TODO: Manage based on output-enter/output-leave return true; } -void LXQtTaskbarWlrootsBackend::moveApplication(WId windowId) +void LXQtTaskbarWlrootsBackend::moveApplication(WId) { } -void LXQtTaskbarWlrootsBackend::resizeApplication(WId windowId) +void LXQtTaskbarWlrootsBackend::resizeApplication(WId) { } -void LXQtTaskbarWlrootsBackend::refreshIconGeometry(WId windowId, const QRect &geom) +void LXQtTaskbarWlrootsBackend::refreshIconGeometry(WId, const QRect &) { } -bool LXQtTaskbarWlrootsBackend::isAreaOverlapped(const QRect &area) const +bool LXQtTaskbarWlrootsBackend::isAreaOverlapped(const QRect &) const { + return false; } bool LXQtTaskbarWlrootsBackend::isShowingDesktop() const @@ -318,8 +311,9 @@ bool LXQtTaskbarWlrootsBackend::isShowingDesktop() const return m_managment->isShowingDesktop(); } -bool LXQtTaskbarWlrootsBackend::showDesktop(bool value) +bool LXQtTaskbarWlrootsBackend::showDesktop(bool) { + return false; } void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) From 565567fa810ad5f57927d6689febc6c3b516fe60 Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Thu, 28 Mar 2024 00:36:57 +0530 Subject: [PATCH 068/165] Add LXQtTaskBarBackendAction::SetLayer to disable 'Layer' in context-menu --- panel/backends/lxqttaskbartypes.h | 3 ++- plugin-taskbar/lxqttaskbutton.cpp | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/panel/backends/lxqttaskbartypes.h b/panel/backends/lxqttaskbartypes.h index ae3d5f107..dff38db0b 100644 --- a/panel/backends/lxqttaskbartypes.h +++ b/panel/backends/lxqttaskbartypes.h @@ -14,7 +14,8 @@ enum class LXQtTaskBarBackendAction MaximizeHorizontally, Minimize, RollUp, - FullScreen + FullScreen, + SetLayer }; enum class LXQtTaskBarWindowProperty diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 45e836893..851f765bc 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -690,6 +690,7 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) menu->addSeparator(); QMenu* layerMenu = menu->addMenu(tr("&Layer")); + layerMenu->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::SetLayer)); LXQtTaskBarWindowLayer currentLayer = mBackend->getWindowLayer(mWindow); From 41ee2b27b9f0fc899af6830c620daafb9eb2025f Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Thu, 28 Mar 2024 12:56:30 +0530 Subject: [PATCH 069/165] Use XdgIcon::fromTheme(...) instead of own implementation --- panel/backends/wayland/wlroots/CMakeLists.txt | 2 - panel/backends/wayland/wlroots/icontools.cpp | 150 ------------------ panel/backends/wayland/wlroots/icontools.hpp | 6 - .../wlroots/lxqttaskbarwlrwindowmanagment.cpp | 9 +- 4 files changed, 6 insertions(+), 161 deletions(-) delete mode 100644 panel/backends/wayland/wlroots/icontools.cpp delete mode 100644 panel/backends/wayland/wlroots/icontools.hpp diff --git a/panel/backends/wayland/wlroots/CMakeLists.txt b/panel/backends/wayland/wlroots/CMakeLists.txt index 1b4a511f8..9fe10d647 100644 --- a/panel/backends/wayland/wlroots/CMakeLists.txt +++ b/panel/backends/wayland/wlroots/CMakeLists.txt @@ -8,12 +8,10 @@ add_library(lxqt-panel-backend-wlroots STATIC lxqttaskbarbackendwlr.h lxqttaskbarwlrwindowmanagment.h lxqtwlrvirtualdesktop.h - icontools.hpp lxqttaskbarbackendwlr.cpp lxqttaskbarwlrwindowmanagment.cpp lxqtwlrvirtualdesktop.cpp - icontools.cpp ) qt6_generate_wayland_protocol_client_sources(lxqt-panel-backend-wlroots diff --git a/panel/backends/wayland/wlroots/icontools.cpp b/panel/backends/wayland/wlroots/icontools.cpp deleted file mode 100644 index d77165d68..000000000 --- a/panel/backends/wayland/wlroots/icontools.cpp +++ /dev/null @@ -1,150 +0,0 @@ -#include - -#include "icontools.hpp" - -static inline QString getPixmapIcon( QString name ) { - QStringList paths{ - QString::fromUtf8( "/usr/local/share/pixmaps/" ), - QString::fromUtf8( "/usr/share/pixmaps/" ), - }; - - QStringList sfxs{ - QString::fromUtf8( ".svg" ), QString::fromUtf8( ".png" ), QString::fromUtf8( ".xpm" ) - }; - - for ( QString path: paths ) { - for ( QString sfx: sfxs ) { - if ( QFile::exists( path + name + sfx ) ) { - return path + name + sfx; - } - } - } - - return QString(); -} - - -QIcon getIconForAppId( QString mAppId ) { - if ( mAppId.isEmpty() or (mAppId == QString::fromUtf8("Unknown")) ) { - return QIcon(); - } - - /** Wine apps */ - if ( mAppId.endsWith( QString::fromUtf8(".exe") ) ) { - return QIcon::fromTheme( QString::fromUtf8("wine") ); - } - - /** Check if a theme icon exists called @mAppId */ - if ( QIcon::hasThemeIcon( mAppId ) ) { - return QIcon::fromTheme( mAppId ); - } - - /** Check if the theme icon is @mAppId, but all lower-case letters */ - else if ( QIcon::hasThemeIcon( mAppId.toLower() ) ) { - return QIcon::fromTheme( mAppId.toLower() ); - } - - QStringList appDirs = { - QDir::home().filePath( QString::fromUtf8( ".local/share/applications/" ) ), - QString::fromUtf8( "/usr/local/share/applications/" ), - QString::fromUtf8( "/usr/share/applications/" ), - QString::fromUtf8( "/usr/local/share/games/" ), - QString::fromUtf8( "/usr/share/games/" ), - }; - - /** - * Assume mAppId == desktop-file-name (ideal situation) - * or mAppId.toLower() == desktop-file-name (cheap fallback) - */ - QString iconName; - - for ( QString path: appDirs ) { - /** Get the icon name from desktop (mAppId: as it is) */ - if ( QFile::exists( path + mAppId + QString::fromUtf8(".desktop") ) ) { - QSettings desktop( path + mAppId + QString::fromUtf8(".desktop"), QSettings::IniFormat ); - iconName = desktop.value( QString::fromUtf8( "Desktop Entry/Icon" ) ).toString(); - } - - /** Get the icon name from desktop (mAppId: all lower-case letters) */ - else if ( QFile::exists( path + mAppId.toLower() + QString::fromUtf8(".desktop") ) ) { - QSettings desktop( path + mAppId.toLower() + QString::fromUtf8(".desktop"), QSettings::IniFormat ); - iconName = desktop.value( QString::fromUtf8("Desktop Entry/Icon") ).toString(); - } - - /** No icon specified: try else-where */ - if ( iconName.isEmpty() ) { - continue; - } - - /** We got an iconName, and it's in the current theme */ - if ( QIcon::hasThemeIcon( iconName ) ) { - return QIcon::fromTheme( iconName ); - } - - /** Not a theme icon, but an absolute path */ - else if ( QFile::exists( iconName ) ) { - return QIcon( iconName ); - } - - /** Not theme icon or absolute path. So check /usr/share/pixmaps/ */ - else { - iconName = getPixmapIcon( iconName ); - - if ( not iconName.isEmpty() ) { - return QIcon( iconName ); - } - } - } - - /* Check all desktop files for @mAppId */ - for ( QString path: appDirs ) { - QStringList desktops = QDir( path ).entryList( { QString::fromUtf8( "*.desktop" ) } ); - for ( QString dskf: desktops ) { - QSettings desktop( path + dskf, QSettings::IniFormat ); - - QString exec = desktop.value( QString::fromUtf8("Desktop Entry/Exec"), QString::fromUtf8("abcd1234/-") ).toString(); - QString name = desktop.value( QString::fromUtf8("Desktop Entry/Name"), QString::fromUtf8("abcd1234/-") ).toString(); - QString cls = desktop.value( QString::fromUtf8("Desktop Entry/StartupWMClass"), QString::fromUtf8("abcd1234/-") ).toString(); - - QString execPath( QString::fromUtf8( std::filesystem::path( exec.toStdString() ).filename().c_str() ) ); - - if ( mAppId.compare( execPath, Qt::CaseInsensitive ) == 0 ) { - iconName = desktop.value( QString::fromUtf8("Desktop Entry/Icon") ).toString(); - } - - else if ( mAppId.compare( name, Qt::CaseInsensitive ) == 0 ) { - iconName = desktop.value( QString::fromUtf8("Desktop Entry/Icon") ).toString(); - } - - else if ( mAppId.compare( cls, Qt::CaseInsensitive ) == 0 ) { - iconName = desktop.value( QString::fromUtf8("Desktop Entry/Icon") ).toString(); - } - - if ( not iconName.isEmpty() ) { - if ( QIcon::hasThemeIcon( iconName ) ) { - return QIcon::fromTheme( iconName ); - } - - else if ( QFile::exists( iconName ) ) { - return QIcon( iconName ); - } - - else { - iconName = getPixmapIcon( iconName ); - - if ( not iconName.isEmpty() ) { - return QIcon( iconName ); - } - } - } - } - } - - iconName = getPixmapIcon( iconName ); - - if ( not iconName.isEmpty() ) { - return QIcon( iconName ); - } - - return QIcon(); -} diff --git a/panel/backends/wayland/wlroots/icontools.hpp b/panel/backends/wayland/wlroots/icontools.hpp deleted file mode 100644 index a119bb14a..000000000 --- a/panel/backends/wayland/wlroots/icontools.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include -#include - -QIcon getIconForAppId( QString ); diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp index ebfc8dc7f..6fdba648e 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp @@ -1,5 +1,4 @@ #include "lxqttaskbarwlrwindowmanagment.h" -#include "icontools.hpp" #include #include @@ -12,7 +11,7 @@ #include #include -#include +#include #include @@ -98,7 +97,11 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_app_id(const QStr appIdRecieved = true; emit appIdChanged(); - this->icon = getIconForAppId( app_id ); + this->icon = XdgIcon::fromTheme(appId); + if ( this->icon.pixmap(64).width() == 0 ) + { + this->icon = XdgIcon::fromTheme(appId.toLower()); + } if ( appIdRecieved && titleRecieved ) { From 09fddbe6864fe7a3ed1c7c4982d0ebc2b0c7a7de Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Fri, 29 Mar 2024 16:35:55 +0530 Subject: [PATCH 070/165] Refactoring of handling of zwlr_foreign_toplevel_handle_v1 events --- .../wayland/wlroots/lxqttaskbarbackendwlr.cpp | 117 +++--- .../wayland/wlroots/lxqttaskbarbackendwlr.h | 2 +- .../wlroots/lxqttaskbarwlrwindowmanagment.cpp | 348 +++++++++++++----- .../wlroots/lxqttaskbarwlrwindowmanagment.h | 86 +++-- 4 files changed, 381 insertions(+), 172 deletions(-) diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp index a40a29ebf..5f26ebb2e 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp @@ -6,20 +6,29 @@ #include #include #include +#include -auto findWindow(const std::vector>& windows, LXQtTaskBarWlrootsWindow *window) -{ - //TODO: use algorithms - auto end = windows.end(); - for(auto it = windows.begin(); it != end; it++) - { - if((*it).get() == window) - { - return it; - } +LXQtTaskBarWlrootsWindow* findWindow(const std::vector windows, LXQtTaskBarWlrootsWindow* window) { + // Use std::find to locate the window pointer within the vector + auto it = std::find(windows.begin(), windows.end(), window); + + // Check if the window was found (iterator != end of vector) + if (it != windows.end()) { + // Return the pointer to the found window + return *it; } + // Window not found, return nullptr + return nullptr; +} - return windows.end(); +void eraseWindow(std::vector& windows, LXQtTaskBarWlrootsWindow* window) { + // Use std::remove to find the element to erase + auto it = std::remove(windows.begin(), windows.end(), window); + + // If the element was found, erase it from the vector + if (it != windows.end()) { + windows.erase(it, windows.end()); + } } LXQtTaskbarWlrootsBackend::LXQtTaskbarWlrootsBackend(QObject *parent) : @@ -75,7 +84,7 @@ QVector LXQtTaskbarWlrootsBackend::getCurrentWindows() const QVector wids; wids.reserve(wids.size()); - for(const std::unique_ptr& window : std::as_const(windows)) + for(LXQtTaskBarWlrootsWindow * window : windows) { wids << window->getWindowId(); } @@ -132,13 +141,13 @@ LXQtTaskBarWindowState LXQtTaskbarWlrootsBackend::getWindowState(WId windowId) c if(!window) return LXQtTaskBarWindowState::Normal; - if(window->windowState.testFlag(LXQtTaskBarWlrootsWindow::state::state_minimized)) - return LXQtTaskBarWindowState::Hidden; + if(window->windowState.minimized) + return LXQtTaskBarWindowState::Minimized; - if(window->windowState.testFlag(LXQtTaskBarWlrootsWindow::state::state_maximized)) + if(window->windowState.maximized) return LXQtTaskBarWindowState::Maximized; - if(window->windowState.testFlag(LXQtTaskBarWlrootsWindow::state::state_fullscreen)) + if(window->windowState.fullscreen) return LXQtTaskBarWindowState::FullScreen; return LXQtTaskBarWindowState::Normal; @@ -181,11 +190,11 @@ bool LXQtTaskbarWlrootsBackend::setWindowState(WId windowId, LXQtTaskBarWindowSt case LXQtTaskBarWindowState::Normal: { /** Restore if maximized/minimized */ - if ( window->windowState.testFlag(LXQtTaskBarWlrootsWindow::state_minimized) ) { + if ( window->windowState.minimized) { window->unset_minimized(); } - if ( window->windowState.testFlag(LXQtTaskBarWlrootsWindow::state_maximized) ) { + if ( window->windowState.maximized) { window->unset_maximized(); } break; @@ -216,7 +225,7 @@ bool LXQtTaskbarWlrootsBackend::isWindowActive(WId windowId) const if(!window) return false; - return activeWindow == window || window->windowState.testFlag(LXQtTaskBarWlrootsWindow::state::state_activated); + return activeWindow == window || window->windowState.activated; } bool LXQtTaskbarWlrootsBackend::raiseWindow(WId windowId, bool onCurrentWorkSpace) @@ -318,37 +327,15 @@ bool LXQtTaskbarWlrootsBackend::showDesktop(bool) void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) { - if (findWindow(windows, window) != windows.end() || transients.contains(window)) + if (findWindow(windows, window) != nullptr || transients.contains(window)) { return; } - /** Add the window once it's ready */ - connect(window, &LXQtTaskBarWlrootsWindow::windowReady, this, [window, this] { - emit windowAdded( window->getWindowId() ); - }); - auto removeWindow = [window, this] { - auto it = findWindow(windows, window); - if (it != windows.end()) - { - windows.erase(it); - transientsDemandingAttention.remove(window); - lastActivated.remove(window); - } - else - { - // Could be a transient. - // Removing a transient might change the demands attention state of the leader. - if (transients.remove(window)) - { - if (LXQtTaskBarWlrootsWindow *leader = transientsDemandingAttention.key(window)) { - transientsDemandingAttention.remove(leader, window); - emit windowPropertyChanged(leader->getWindowId(), int(LXQtTaskBarWindowProperty::Urgency)); - } - } - } + eraseWindow(windows, window); + lastActivated.remove(window); if (activeWindow == window) { @@ -369,7 +356,7 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::WindowClass)); }); - if (window->windowState & LXQtTaskBarWlrootsWindow::state::state_activated) { + if (window->windowState.activated) { LXQtTaskBarWlrootsWindow *effectiveActive = window; while (effectiveActive->parentWindow) { effectiveActive = effectiveActive->parentWindow; @@ -379,9 +366,7 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) activeWindow = effectiveActive; } - connect(window, &LXQtTaskBarWlrootsWindow::activeChanged, this, [window, this] { - const bool active = window->windowState.testFlag( LXQtTaskBarWlrootsWindow::state::state_activated ); - + connect(window, &LXQtTaskBarWlrootsWindow::activatedChanged, this, [window, this] { LXQtTaskBarWlrootsWindow *effectiveWindow = window; while (effectiveWindow->parentWindow) @@ -389,7 +374,7 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) effectiveWindow = effectiveWindow->parentWindow; } - if (active) + if (window->windowState.activated) { lastActivated[effectiveWindow] = QTime::currentTime(); @@ -422,24 +407,20 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) else { // lost a leader, add to regular windows list. - Q_ASSERT(findWindow(windows, window) == windows.end()); + Q_ASSERT(findWindow(windows, window) == nullptr); windows.emplace_back(window); } } else if (leader) { - // gained a leader, remove from regular windows list. - auto it = findWindow(windows, window); - Q_ASSERT(it != windows.end()); - - windows.erase(it); + eraseWindow(windows, window); lastActivated.remove(window); } }); auto stateChanged = [window, this] { - // updateWindowAcceptance(window); + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::State)); }; connect(window, &LXQtTaskBarWlrootsWindow::fullscreenChanged, this, stateChanged); @@ -455,9 +436,16 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) } else { - windows.emplace_back(window); - // updateWindowAcceptance(window); + windows.push_back(window); } + + qDebug() << window << window->getWindowId(); + emit windowAdded( window->getWindowId() ); + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Title)); + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Icon)); + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::State)); + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Geometry)); + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::WindowClass)); } bool LXQtTaskbarWlrootsBackend::acceptWindow(LXQtTaskBarWlrootsWindow *window) const @@ -470,10 +458,19 @@ bool LXQtTaskbarWlrootsBackend::acceptWindow(LXQtTaskBarWlrootsWindow *window) c LXQtTaskBarWlrootsWindow *LXQtTaskbarWlrootsBackend::getWindow(WId windowId) const { + /** Easiest way is to convert the quintptr to the actual pointer */ + LXQtTaskBarWlrootsWindow *win = reinterpret_cast( windowId ); + if ( win ) { + qDebug() << "get-window-windowId"; + return win; + } + + /** Fallback attempt */ for(auto &window : std::as_const(windows)) { - if(window->getWindowId() == windowId) - return window.get(); + if(window->getWindowId() == windowId) { + return window; + } } return nullptr; diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h index 61ed5d81a..ef0c67afa 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h @@ -83,7 +83,7 @@ class LXQtTaskbarWlrootsBackend : public ILXQtTaskbarAbstractBackend QHash lastActivated; LXQtTaskBarWlrootsWindow *activeWindow = nullptr; - std::vector> windows; + std::vector windows; // key=transient child, value=leader QHash transients; // key=leader, values=transient children diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp index 6fdba648e..c9fbb6941 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp @@ -19,42 +19,61 @@ #include #include -wl_seat *get_seat() { +static inline wl_seat *get_seat() +{ QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface(); - if ( !native ) { + if (!native) + { return nullptr; } - struct wl_seat *seat = reinterpret_cast(native->nativeResourceForIntegration( "wl_seat" ) ); + struct wl_seat *seat = reinterpret_cast(native->nativeResourceForIntegration("wl_seat")); return seat; } + /* * LXQtTaskBarWlrootsWindowManagment */ -LXQtTaskBarWlrootsWindowManagment::LXQtTaskBarWlrootsWindowManagment() - : QWaylandClientExtensionTemplate(version) +LXQtTaskBarWlrootsWindowManagment::LXQtTaskBarWlrootsWindowManagment() : QWaylandClientExtensionTemplate(version) { - connect(this, &QWaylandClientExtension::activeChanged, this, [this] { - if (!isActive()) { + /** Automatically destroy thie object */ + connect( + this, &QWaylandClientExtension::activeChanged, this, [ this ] { + if (!isActive()) + { zwlr_foreign_toplevel_manager_v1_destroy(object()); } }); } + LXQtTaskBarWlrootsWindowManagment::~LXQtTaskBarWlrootsWindowManagment() { - if (isActive()) { + if (isActive()) + { zwlr_foreign_toplevel_manager_v1_destroy(object()); } } + void LXQtTaskBarWlrootsWindowManagment::zwlr_foreign_toplevel_manager_v1_toplevel(struct ::zwlr_foreign_toplevel_handle_v1 *toplevel) { - emit windowCreated( new LXQtTaskBarWlrootsWindow(toplevel) ); + /** + * A window was created. + * Wait for the window to become ready, i.e. wait for done() event to be sent by the compositor. + * Once we recieve done(), emit the windowReady() signal. + */ + + auto w = new LXQtTaskBarWlrootsWindow(toplevel); + + connect(w, &LXQtTaskBarWlrootsWindow::windowReady, [w, this] () { + qDebug() << "------------> a window was created" << w << w->getWindowId() << w->appId << w->title; + emit windowCreated(w); + }); } @@ -62,157 +81,312 @@ void LXQtTaskBarWlrootsWindowManagment::zwlr_foreign_toplevel_manager_v1_topleve * LXQtTaskBarWlrootsWindow */ -LXQtTaskBarWlrootsWindow::LXQtTaskBarWlrootsWindow(::zwlr_foreign_toplevel_handle_v1 *id) - : zwlr_foreign_toplevel_handle_v1(id) +LXQtTaskBarWlrootsWindow::LXQtTaskBarWlrootsWindow(::zwlr_foreign_toplevel_handle_v1 *id) : zwlr_foreign_toplevel_handle_v1(id) { - title = QString::fromUtf8( "untitled" ); - appId = QString::fromUtf8( "unknown" ); + /** Set a default non-null title and appId */ + title = QString::fromUtf8("untitled"); + appId = QString::fromUtf8("unknown"); } + LXQtTaskBarWlrootsWindow::~LXQtTaskBarWlrootsWindow() { destroy(); } + void LXQtTaskBarWlrootsWindow::activate() { + /** + * Activate on default seat. + * TODO: Worry about multi-seat setups, when we have no other worries :P + */ zwlr_foreign_toplevel_handle_v1::activate(get_seat()); } -void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_title(const QString &title) -{ - this->title = title; - titleRecieved = true; - emit titleChanged(); - if ( titleRecieved && appIdRecieved ) - { - emit windowReady(); - } +void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_title(const QString& title) +{ + /** Store the incoming title in pending */ + m_pendingState.title = title; + m_pendingState.titleChanged = true; } -void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_app_id(const QString &app_id) + +void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_app_id(const QString& app_id) { - this->appId = app_id; - appIdRecieved = true; - emit appIdChanged(); + /** Store the incoming appId in pending */ + m_pendingState.appId = app_id; + m_pendingState.appIdChanged = true; + /** Update the icon */ this->icon = XdgIcon::fromTheme(appId); - if ( this->icon.pixmap(64).width() == 0 ) + + /** Sometimes, appId can be capitalized, for example, Pulsar. So try lower-case. */ + if (this->icon.pixmap(64).width() == 0) { this->icon = XdgIcon::fromTheme(appId.toLower()); } - if ( appIdRecieved && titleRecieved ) + /** We did not get any icon from app-id. Let's use application-x-executable */ + if (this->icon.pixmap(64).width() == 0) { - emit windowReady(); + this->icon = XdgIcon::fromTheme(QString::fromUtf8("application-x-executable")); } } + void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_enter(struct ::wl_output *output) { - emit outputEnter(); + /** This view was added to an output */ + m_pendingState.outputs << output; + m_pendingState.outputsChanged = true; } + void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_leave(struct ::wl_output *output) { - emit outputLeave(); + /** This view was removed from an output; store it in pending. */ + m_pendingState.outputsLeft << output; + + if (m_pendingState.outputs.contains(output)) + { + m_pendingState.outputs.removeAll(output); + } + + m_pendingState.outputsChanged = true; } + void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_state(wl_array *state) { - auto *states = static_cast(state->data); - int numStates = static_cast(state->size / sizeof(uint32_t) ); + /** State of this window was changed; store it in pending. */ + auto *states = static_cast(state->data); + int numStates = static_cast(state->size / sizeof(uint32_t)); - for ( int i = 0; i < numStates; i++ ) { - switch ( (uint32_t)states[ i ] ) { - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED: { - m_pendingState.maximized = true; - break; - } + for (int i = 0; i < numStates; i++) + { + switch ((uint32_t)states[ i ]) + { + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED: { + m_pendingState.maximized = true; + m_pendingState.maximizedChanged = true; + break; + } - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED: { - m_pendingState.minimized = true; - break; - } + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED: { + m_pendingState.minimized = true; + m_pendingState.minimizedChanged = true; + break; + } - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED: { - m_pendingState.activated = true; - break; - } + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED: { + m_pendingState.activated = true; + m_pendingState.activatedChanged = true; + break; + } - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN: { - m_pendingState.fullscreen = true; - break; - } + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN: { + m_pendingState.fullscreen = true; + m_pendingState.fullscreenChanged = true; + break; + } } } } + void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_done() { /** - * Update windowState flags before emitting the signals. - * Otherwise, windowState.testFlag(...) will return wrong information!! + * All the states/properties have been sent. + * We can now emit the signals and clear the pending state: + * 1. Update all the variables first. + * 2. Then clear the m_pendingState. + * 3. Emit the changed signals. + * 4. Finally, cleanr the m_pendingState.Changed flags. */ - windowState = QFlags(); - if ( m_pendingState.maximized ) windowState |= state_maximized; - if ( m_pendingState.minimized ) windowState |= state_minimized; - if ( m_pendingState.activated ) windowState |= state_activated; - if ( m_pendingState.fullscreen ) windowState |= state_fullscreen; - - /** Emit the signals. */ - if ( m_viewState.maximized != m_pendingState.maximized ) - emit maximizedChanged(); - - if ( m_viewState.minimized != m_pendingState.minimized ) - emit minimizedChanged(); - - if ( m_viewState.activated!= m_pendingState.activated ) - emit activeChanged(); - - if ( m_viewState.fullscreen != m_pendingState.fullscreen ) - emit fullscreenChanged(); - - /** Store m_pendingState into m_viewState for the next run */ - m_viewState.maximized = m_pendingState.maximized; - m_viewState.minimized = m_pendingState.minimized; - m_viewState.activated = m_pendingState.activated; - m_viewState.fullscreen = m_pendingState.fullscreen; - - /** Reset m_pendingState for the next run */ - m_pendingState.maximized = false; - m_pendingState.minimized = false; - m_pendingState.activated = false; + + // (1) title, if it changed + if (m_pendingState.titleChanged) + { + windowState.title = m_pendingState.title; + } + + // (2) appId, if it changed + if (m_pendingState.appIdChanged) + { + windowState.appId = m_pendingState.appId; + } + + // (3) outputs, if they changed + if (m_pendingState.outputsChanged) + { + for (::wl_output *op: m_pendingState.outputsLeft) + { + if (windowState.outputs.contains(op)) + { + windowState.outputs.removeAll(op); + } + } + + for (::wl_output *op: m_pendingState.outputs) + { + if (!windowState.outputs.contains(op)) + { + windowState.outputs << op; + } + } + } + + // (4) states, if they changed. Don't trust the changed flag. + if (m_pendingState.maximized != windowState.maximized) + { + windowState.maximized = m_pendingState.maximized; + m_pendingState.maximizedChanged = true; + } + + if (m_pendingState.minimized != windowState.minimized) + { + windowState.minimized = m_pendingState.minimized; + m_pendingState.minimizedChanged = true; + } + + if (m_pendingState.activated != windowState.activated) + { + windowState.activated = m_pendingState.activated; + m_pendingState.activatedChanged = true; + } + + if (m_pendingState.fullscreen != windowState.fullscreen) + { + windowState.fullscreen = m_pendingState.fullscreen; + m_pendingState.fullscreenChanged = true; + } + + // (5) parent, if it changed. + if (m_pendingState.parentChanged) + { + if (m_pendingState.parent) + { + setParentWindow(new LXQtTaskBarWlrootsWindow(m_pendingState.parent)); + } + + else + { + setParentWindow(nullptr); + } + } + + /** 2. Clear all m_pendingState. for next run */ + m_pendingState.title = QString(); + m_pendingState.appId = QString(); + m_pendingState.outputs.clear(); + m_pendingState.maximized = false; + m_pendingState.minimized = false; + m_pendingState.activated = false; m_pendingState.fullscreen = false; + m_pendingState.parent = nullptr; + + /** + * 3. Emit signals + * (a) First time done was emitted after the window was created. + * (b) Other times. + */ + + /** (a) First time done was emitted */ + if (initDone == false) + { + qDebug() << " " << this << getWindowId(); + qDebug() << " titleChanged" << m_pendingState.titleChanged << windowState.title; + qDebug() << " appIdChanged" << m_pendingState.appIdChanged << windowState.appId; + qDebug() << " outputsChanged" << m_pendingState.outputsChanged << windowState.outputs; + qDebug() << " maximizedChanged" << m_pendingState.maximizedChanged << windowState.maximized; + qDebug() << " minimizedChanged" << m_pendingState.minimizedChanged << windowState.minimized; + qDebug() << " activatedChanged" << m_pendingState.activatedChanged << windowState.activated; + qDebug() << " fullscreenChanged" << m_pendingState.fullscreenChanged << windowState.fullscreen; + qDebug() << " parentChanged" << m_pendingState.parentChanged << windowState.parent; + + /** + * All the states/properties are already set. + * Any query will give valid results. + */ + initDone = true; + emit windowReady(); + } + + /** (b) All the subsequent times */ + else + { + if (m_pendingState.titleChanged) + emit titleChanged(); + if (m_pendingState.appIdChanged) + emit appIdChanged(); + if (m_pendingState.outputsChanged) + emit outputsChanged(); + if (m_pendingState.maximizedChanged) + emit maximizedChanged(); + if (m_pendingState.minimizedChanged) + emit minimizedChanged(); + if (m_pendingState.activatedChanged) + emit activatedChanged(); + if (m_pendingState.fullscreenChanged) + emit fullscreenChanged(); + if (m_pendingState.parentChanged) + emit parentChanged(); + + emit stateChanged(); + } + + /** 4. Clear m+m_pendingState.Changed flags */ + m_pendingState.titleChanged = false; + m_pendingState.appIdChanged = false; + m_pendingState.outputsChanged = false; + m_pendingState.maximizedChanged = false; + m_pendingState.minimizedChanged = false; + m_pendingState.activatedChanged = false; + m_pendingState.fullscreenChanged = false; + m_pendingState.parentChanged = false; } + void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_closed() { + /** This window was closed */ emit closed(); } + void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_parent(struct ::zwlr_foreign_toplevel_handle_v1 *parent) { - // setParentWindow(new LXQtTaskBarWlrootsWindow(parent)); + /** Parent of this window changed; store it in pending. */ + m_pendingState.parent = parent; + m_pendingState.parentChanged = true; } + void LXQtTaskBarWlrootsWindow::setParentWindow(LXQtTaskBarWlrootsWindow *parent) { const auto old = parentWindow; + QObject::disconnect(parentWindowUnmappedConnection); - if (parent) { + if (parent) + { parentWindow = QPointer(parent); - parentWindowUnmappedConnection = QObject::connect(parent, &LXQtTaskBarWlrootsWindow::closed, this, [this] { + parentWindowUnmappedConnection = QObject::connect( + parent, &LXQtTaskBarWlrootsWindow::closed, this, [ this ] { setParentWindow(nullptr); }); - } else { + } + else + { parentWindow = QPointer(); parentWindowUnmappedConnection = QMetaObject::Connection(); } - if (parentWindow.data() != old.data()) { + if (parentWindow.data() != old.data()) + { Q_EMIT parentChanged(); } } diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h index bc3c2a0b3..705107312 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h @@ -34,6 +34,46 @@ class LXQtTaskBarWlrootsWindowManagment : public QWaylandClientExtensionTemplate bool m_isShowingDesktop = false; }; +using WindowState = QtWayland::zwlr_foreign_toplevel_handle_v1::state; + +class WindowProperties { + public: + /** Title of the window */ + QString title = QString::fromUtf8( "untitled" ); + bool titleChanged = false; + + /** appId of the window */ + QString appId = QString::fromUtf8( "unidentified" ); + bool appIdChanged = false; + + /** List of outputs which the window is currently on */ + QList<::wl_output *> outputs; + bool outputsChanged = false; + + /** Is maximized */ + bool maximized = false; + bool maximizedChanged = false; + + /** Is minimized */ + bool minimized = false; + bool minimizedChanged = false; + + /** Is active */ + bool activated = false; + bool activatedChanged = false; + + /** Is fullscreen */ + bool fullscreen = false; + bool fullscreenChanged = false; + + /** Parent of this view, can be null */ + ::zwlr_foreign_toplevel_handle_v1 * parent = nullptr; + bool parentChanged = false; + + /** List of outputs from which window has left */ + QList<::wl_output *> outputsLeft; +}; + class LXQtTaskBarWlrootsWindow : public QObject, public QtWayland::zwlr_foreign_toplevel_handle_v1 { @@ -46,29 +86,37 @@ class LXQtTaskBarWlrootsWindow : public QObject, void activate(); - using state = QtWayland::zwlr_foreign_toplevel_handle_v1::state; - QString title; - QString appId; - QIcon icon; - QFlags windowState; - QPointer parentWindow; + mutable QString title; + mutable QString appId; + mutable QIcon icon; + mutable WindowProperties windowState; + mutable QPointer parentWindow; Q_SIGNALS: void titleChanged(); void appIdChanged(); - void outputEnter(); - void outputLeave(); - void activeChanged(); + void outputsChanged(); + + /** Individual state change signals */ void maximizedChanged(); void minimizedChanged(); + void activatedChanged(); void fullscreenChanged(); - void done(); - void closed(); + void parentChanged(); - /** We wait to get the title and appId to emit this */ + /** Bulk state change signal */ + void stateChanged(); + + /** First state change signal: Before this, the window did not have a valid state */ void windowReady(); + /** All state changes have been sent. */ + void done(); + + /** Window closed signal */ + void closed(); + protected: void zwlr_foreign_toplevel_handle_v1_title(const QString &title); void zwlr_foreign_toplevel_handle_v1_app_id(const QString &app_id); @@ -84,17 +132,7 @@ class LXQtTaskBarWlrootsWindow : public QObject, QMetaObject::Connection parentWindowUnmappedConnection; - typedef struct view_state_t - { - bool maximized = false; - bool minimized = false; - bool activated = false; - bool fullscreen = false; - } ViewState; - - ViewState m_viewState; - ViewState m_pendingState; + WindowProperties m_pendingState; - bool titleRecieved = false; - bool appIdRecieved = false; + bool initDone = false; }; From e53254e37ae902b7f4f98541b0c79cf33345777f Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Sat, 30 Mar 2024 00:29:10 +0530 Subject: [PATCH 071/165] Refactor LXQtTaskbarWlrootsBackend Instead of using LXQtTaskbarWlrootsWindow* pointers, it's easier to store and use WId a.k.a quintptr which are derived from the address of the LXQtTaskbarWlrootsWindow pointer. This way, it's trivial to retrieve the underlying pointer with a reinterpret_cast<...>(). Also, switch to using std::find(...) to search a given WId in windows. Use a dedicated function to erase a WId from windows: eraseWindow(...). --- .../wayland/wlroots/lxqttaskbarbackendwlr.cpp | 175 +++++++++--------- .../wayland/wlroots/lxqttaskbarbackendwlr.h | 24 +-- .../wlroots/lxqttaskbarwlrwindowmanagment.cpp | 73 +++----- .../wlroots/lxqttaskbarwlrwindowmanagment.h | 26 ++- 4 files changed, 135 insertions(+), 163 deletions(-) diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp index 5f26ebb2e..3c5754244 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp @@ -8,38 +8,39 @@ #include #include -LXQtTaskBarWlrootsWindow* findWindow(const std::vector windows, LXQtTaskBarWlrootsWindow* window) { - // Use std::find to locate the window pointer within the vector - auto it = std::find(windows.begin(), windows.end(), window); +// Function to search for a window in the vector +WId findWindow(const std::vector& windows, WId tgt) { + // Use std::find to locate the target window + auto it = std::find(windows.begin(), windows.end(), tgt); - // Check if the window was found (iterator != end of vector) + // Check if the window was found (iterator points to windows.end() if not found) if (it != windows.end()) { - // Return the pointer to the found window + // If found, return the window ID by dereferencing the iterator return *it; } - // Window not found, return nullptr - return nullptr; + + return 0; } -void eraseWindow(std::vector& windows, LXQtTaskBarWlrootsWindow* window) { - // Use std::remove to find the element to erase - auto it = std::remove(windows.begin(), windows.end(), window); +// Function to erase a window from the vector +void eraseWindow(std::vector& windows, WId tgt) { + // Use std::vector::iterator to find the window + auto it = std::find(windows.begin(), windows.end(), tgt); - // If the element was found, erase it from the vector + // Check if the window was found if (it != windows.end()) { - windows.erase(it, windows.end()); + // If found, erase the element pointed to by the iterator + windows.erase(it); } } LXQtTaskbarWlrootsBackend::LXQtTaskbarWlrootsBackend(QObject *parent) : ILXQtTaskbarAbstractBackend(parent) { - m_managment.reset(new LXQtTaskBarWlrootsWindowManagment); + m_managment.reset(new LXQtTaskbarWlrootsWindowManagment); m_workspaceInfo.reset(new LXQtWlrootsWaylandWorkspaceInfo); - connect(m_managment.get(), &LXQtTaskBarWlrootsWindowManagment::windowCreated, this, [this](LXQtTaskBarWlrootsWindow *window) { - addWindow(window); - }); + connect(m_managment.get(), &LXQtTaskbarWlrootsWindowManagment::windowCreated, this, &LXQtTaskbarWlrootsBackend::addWindow); } bool LXQtTaskbarWlrootsBackend::supportsAction(WId, LXQtTaskBarBackendAction action) const @@ -82,11 +83,9 @@ bool LXQtTaskbarWlrootsBackend::reloadWindows() QVector LXQtTaskbarWlrootsBackend::getCurrentWindows() const { QVector wids; - wids.reserve(wids.size()); - for(LXQtTaskBarWlrootsWindow * window : windows) - { - wids << window->getWindowId(); + for( WId wid: windows ){ + wids << wid; } return wids; @@ -94,11 +93,11 @@ QVector LXQtTaskbarWlrootsBackend::getCurrentWindows() const QString LXQtTaskbarWlrootsBackend::getWindowTitle(WId windowId) const { - LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + LXQtTaskbarWlrootsWindow *window = getWindow(windowId); if(!window) return QString(); - return window->title; + return window->windowState.title; } bool LXQtTaskbarWlrootsBackend::applicationDemandsAttention(WId) const @@ -110,7 +109,7 @@ QIcon LXQtTaskbarWlrootsBackend::getApplicationIcon(WId windowId, int devicePixe { Q_UNUSED(devicePixels) - LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + LXQtTaskbarWlrootsWindow *window = getWindow(windowId); if(!window) return QIcon(); @@ -119,10 +118,10 @@ QIcon LXQtTaskbarWlrootsBackend::getApplicationIcon(WId windowId, int devicePixe QString LXQtTaskbarWlrootsBackend::getWindowClass(WId windowId) const { - LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + LXQtTaskbarWlrootsWindow *window = getWindow(windowId); if(!window) return QString(); - return window->appId; + return window->windowState.appId; } LXQtTaskBarWindowLayer LXQtTaskbarWlrootsBackend::getWindowLayer(WId) const @@ -137,7 +136,7 @@ bool LXQtTaskbarWlrootsBackend::setWindowLayer(WId, LXQtTaskBarWindowLayer) LXQtTaskBarWindowState LXQtTaskbarWlrootsBackend::getWindowState(WId windowId) const { - LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + LXQtTaskbarWlrootsWindow *window = getWindow(windowId); if(!window) return LXQtTaskBarWindowState::Normal; @@ -155,7 +154,7 @@ LXQtTaskBarWindowState LXQtTaskbarWlrootsBackend::getWindowState(WId windowId) c bool LXQtTaskbarWlrootsBackend::setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set) { - LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + LXQtTaskbarWlrootsWindow *window = getWindow(windowId); if(!window) return false; @@ -221,18 +220,18 @@ bool LXQtTaskbarWlrootsBackend::setWindowState(WId windowId, LXQtTaskBarWindowSt bool LXQtTaskbarWlrootsBackend::isWindowActive(WId windowId) const { - LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + LXQtTaskbarWlrootsWindow *window = getWindow(windowId); if(!window) return false; - return activeWindow == window || window->windowState.activated; + return activeWindow == windowId || window->windowState.activated; } bool LXQtTaskbarWlrootsBackend::raiseWindow(WId windowId, bool onCurrentWorkSpace) { Q_UNUSED(onCurrentWorkSpace) //TODO - LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + LXQtTaskbarWlrootsWindow *window = getWindow(windowId); if(!window) return false; @@ -242,7 +241,7 @@ bool LXQtTaskbarWlrootsBackend::raiseWindow(WId windowId, bool onCurrentWorkSpac bool LXQtTaskbarWlrootsBackend::closeWindow(WId windowId) { - LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + LXQtTaskbarWlrootsWindow *window = getWindow(windowId); if(!window) return false; @@ -252,9 +251,7 @@ bool LXQtTaskbarWlrootsBackend::closeWindow(WId windowId) WId LXQtTaskbarWlrootsBackend::getActiveWindow() const { - if(activeWindow) - return activeWindow->getWindowId(); - return 0; + return activeWindow; } int LXQtTaskbarWlrootsBackend::getWorkspacesCount() const @@ -325,53 +322,60 @@ bool LXQtTaskbarWlrootsBackend::showDesktop(bool) return false; } -void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) +void LXQtTaskbarWlrootsBackend::addWindow(WId winId) { - if (findWindow(windows, window) != nullptr || transients.contains(window)) + if (findWindow(windows, winId) != 0 || transients.contains(winId)) { return; } - auto removeWindow = [window, this] + auto removeWindow = [winId, this] { - eraseWindow(windows, window); - lastActivated.remove(window); + eraseWindow(windows, winId); + lastActivated.remove(winId); - if (activeWindow == window) + if (activeWindow == winId) { - activeWindow = nullptr; + activeWindow = 0; emit activeWindowChanged(0); } - emit windowRemoved(window->getWindowId()); + emit windowRemoved(winId); }; - connect(window, &LXQtTaskBarWlrootsWindow::closed, this, removeWindow); + LXQtTaskbarWlrootsWindow *window = getWindow( winId ); + if ( window == nullptr ) { + return; + } + + /** The window was closed. Remove from our lists */ + connect(window, &LXQtTaskbarWlrootsWindow::closed, this, removeWindow); - connect(window, &LXQtTaskBarWlrootsWindow::titleChanged, this, [window, this] { - emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Title)); + /** */ + connect(window, &LXQtTaskbarWlrootsWindow::titleChanged, this, [winId, this] { + emit windowPropertyChanged(winId, int(LXQtTaskBarWindowProperty::Title)); }); - connect(window, &LXQtTaskBarWlrootsWindow::appIdChanged, this, [window, this] { - emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::WindowClass)); + connect(window, &LXQtTaskbarWlrootsWindow::appIdChanged, this, [winId, this] { + emit windowPropertyChanged(winId, int(LXQtTaskBarWindowProperty::WindowClass)); }); if (window->windowState.activated) { - LXQtTaskBarWlrootsWindow *effectiveActive = window; + LXQtTaskbarWlrootsWindow *effectiveActive = window; while (effectiveActive->parentWindow) { - effectiveActive = effectiveActive->parentWindow; + effectiveActive = getWindow(effectiveActive->parentWindow); } - lastActivated[effectiveActive] = QTime::currentTime(); - activeWindow = effectiveActive; + lastActivated[effectiveActive->getWindowId()] = QTime::currentTime(); + activeWindow = effectiveActive->getWindowId(); } - connect(window, &LXQtTaskBarWlrootsWindow::activatedChanged, this, [window, this] { - LXQtTaskBarWlrootsWindow *effectiveWindow = window; + connect(window, &LXQtTaskbarWlrootsWindow::activatedChanged, this, [window, this] { + WId effectiveWindow = window->getWindowId(); - while (effectiveWindow->parentWindow) + while (getWindow(effectiveWindow)->parentWindow) { - effectiveWindow = effectiveWindow->parentWindow; + effectiveWindow = getWindow(effectiveWindow)->parentWindow; } if (window->windowState.activated) @@ -381,41 +385,43 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) if (activeWindow != effectiveWindow) { activeWindow = effectiveWindow; - emit activeWindowChanged(activeWindow->getWindowId()); + emit activeWindowChanged(activeWindow); } } else { if (activeWindow == effectiveWindow) { - activeWindow = nullptr; + activeWindow = 0; emit activeWindowChanged(0); } } }); - connect(window, &LXQtTaskBarWlrootsWindow::parentChanged, this, [window, this] { - LXQtTaskBarWlrootsWindow *leader = window->parentWindow.data(); + connect(window, &LXQtTaskbarWlrootsWindow::parentChanged, this, [window, this] { + WId leader = window->parentWindow; - if (transients.remove(window)) + /** Basically, check if this window is a transient */ + if (transients.remove(leader)) { if (leader) { // leader change. - transients.insert(window, leader); + transients.insert(window->getWindowId(), leader); } else { // lost a leader, add to regular windows list. - Q_ASSERT(findWindow(windows, window) == nullptr); + Q_ASSERT(findWindow(windows, leader) == 0); - windows.emplace_back(window); + windows.push_back(leader); } } + else if (leader) { - eraseWindow(windows, window); - lastActivated.remove(window); + eraseWindow(windows, window->getWindowId()); + lastActivated.remove(window->getWindowId()); } }); @@ -423,32 +429,30 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::State)); }; - connect(window, &LXQtTaskBarWlrootsWindow::fullscreenChanged, this, stateChanged); + connect(window, &LXQtTaskbarWlrootsWindow::fullscreenChanged, this, stateChanged); - connect(window, &LXQtTaskBarWlrootsWindow::maximizedChanged, this, stateChanged); + connect(window, &LXQtTaskbarWlrootsWindow::maximizedChanged, this, stateChanged); - connect(window, &LXQtTaskBarWlrootsWindow::minimizedChanged, this, stateChanged); + connect(window, &LXQtTaskbarWlrootsWindow::minimizedChanged, this, stateChanged); // Handle transient. - if (LXQtTaskBarWlrootsWindow *leader = window->parentWindow.data()) + if (WId leader = window->parentWindow) { - transients.insert(window, leader); + transients.insert(winId, leader); } else { - windows.push_back(window); + windows.push_back(winId); } - qDebug() << window << window->getWindowId(); - emit windowAdded( window->getWindowId() ); - emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Title)); - emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Icon)); - emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::State)); - emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Geometry)); - emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::WindowClass)); + emit windowAdded( winId ); + emit windowPropertyChanged(winId, int(LXQtTaskBarWindowProperty::WindowClass)); + emit windowPropertyChanged(winId, int(LXQtTaskBarWindowProperty::Title)); + emit windowPropertyChanged(winId, int(LXQtTaskBarWindowProperty::Icon)); + emit windowPropertyChanged(winId, int(LXQtTaskBarWindowProperty::State)); } -bool LXQtTaskbarWlrootsBackend::acceptWindow(LXQtTaskBarWlrootsWindow *window) const +bool LXQtTaskbarWlrootsBackend::acceptWindow(WId window) const { if(transients.contains(window)) return false; @@ -456,22 +460,13 @@ bool LXQtTaskbarWlrootsBackend::acceptWindow(LXQtTaskBarWlrootsWindow *window) c return true; } -LXQtTaskBarWlrootsWindow *LXQtTaskbarWlrootsBackend::getWindow(WId windowId) const +LXQtTaskbarWlrootsWindow *LXQtTaskbarWlrootsBackend::getWindow(WId windowId) const { /** Easiest way is to convert the quintptr to the actual pointer */ - LXQtTaskBarWlrootsWindow *win = reinterpret_cast( windowId ); + LXQtTaskbarWlrootsWindow *win = reinterpret_cast( windowId ); if ( win ) { - qDebug() << "get-window-windowId"; return win; } - /** Fallback attempt */ - for(auto &window : std::as_const(windows)) - { - if(window->getWindowId() == windowId) { - return window; - } - } - return nullptr; } diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h index ef0c67afa..b2ae9a925 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h @@ -6,8 +6,8 @@ #include #include -class LXQtTaskBarWlrootsWindow; -class LXQtTaskBarWlrootsWindowManagment; +class LXQtTaskbarWlrootsWindow; +class LXQtTaskbarWlrootsWindowManagment; class LXQtWlrootsWaylandWorkspaceInfo; @@ -71,21 +71,21 @@ class LXQtTaskbarWlrootsBackend : public ILXQtTaskbarAbstractBackend virtual bool showDesktop(bool value) override; private: - void addWindow(LXQtTaskBarWlrootsWindow *window); - bool acceptWindow(LXQtTaskBarWlrootsWindow *window) const; + void addWindow(WId wid); + bool acceptWindow(WId wid) const; private: - LXQtTaskBarWlrootsWindow *getWindow(WId windowId) const; + /** Convert WId (i.e. quintptr into LXQtTaskbarWlrootsWindow*) */ + LXQtTaskbarWlrootsWindow *getWindow(WId windowId) const; std::unique_ptr m_workspaceInfo; - std::unique_ptr m_managment; + std::unique_ptr m_managment; + + QHash lastActivated; + WId activeWindow = 0; + std::vector windows; - QHash lastActivated; - LXQtTaskBarWlrootsWindow *activeWindow = nullptr; - std::vector windows; // key=transient child, value=leader - QHash transients; - // key=leader, values=transient children - QMultiHash transientsDemandingAttention; + QHash transients; }; diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp index c9fbb6941..80c0892fa 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp @@ -35,10 +35,10 @@ static inline wl_seat *get_seat() /* - * LXQtTaskBarWlrootsWindowManagment + * LXQtTaskbarWlrootsWindowManagment */ -LXQtTaskBarWlrootsWindowManagment::LXQtTaskBarWlrootsWindowManagment() : QWaylandClientExtensionTemplate(version) +LXQtTaskbarWlrootsWindowManagment::LXQtTaskbarWlrootsWindowManagment() : QWaylandClientExtensionTemplate(version) { /** Automatically destroy thie object */ connect( @@ -51,7 +51,7 @@ LXQtTaskBarWlrootsWindowManagment::LXQtTaskBarWlrootsWindowManagment() : QWaylan } -LXQtTaskBarWlrootsWindowManagment::~LXQtTaskBarWlrootsWindowManagment() +LXQtTaskbarWlrootsWindowManagment::~LXQtTaskbarWlrootsWindowManagment() { if (isActive()) { @@ -60,7 +60,7 @@ LXQtTaskBarWlrootsWindowManagment::~LXQtTaskBarWlrootsWindowManagment() } -void LXQtTaskBarWlrootsWindowManagment::zwlr_foreign_toplevel_manager_v1_toplevel(struct ::zwlr_foreign_toplevel_handle_v1 *toplevel) +void LXQtTaskbarWlrootsWindowManagment::zwlr_foreign_toplevel_manager_v1_toplevel(struct ::zwlr_foreign_toplevel_handle_v1 *toplevel) { /** * A window was created. @@ -68,34 +68,30 @@ void LXQtTaskBarWlrootsWindowManagment::zwlr_foreign_toplevel_manager_v1_topleve * Once we recieve done(), emit the windowReady() signal. */ - auto w = new LXQtTaskBarWlrootsWindow(toplevel); + auto w = new LXQtTaskbarWlrootsWindow(toplevel); - connect(w, &LXQtTaskBarWlrootsWindow::windowReady, [w, this] () { - qDebug() << "------------> a window was created" << w << w->getWindowId() << w->appId << w->title; - emit windowCreated(w); + connect(w, &LXQtTaskbarWlrootsWindow::windowReady, [w, this] () { + emit windowCreated(w->getWindowId()); }); } /* - * LXQtTaskBarWlrootsWindow + * LXQtTaskbarWlrootsWindow */ -LXQtTaskBarWlrootsWindow::LXQtTaskBarWlrootsWindow(::zwlr_foreign_toplevel_handle_v1 *id) : zwlr_foreign_toplevel_handle_v1(id) +LXQtTaskbarWlrootsWindow::LXQtTaskbarWlrootsWindow(::zwlr_foreign_toplevel_handle_v1 *id) : zwlr_foreign_toplevel_handle_v1(id) { - /** Set a default non-null title and appId */ - title = QString::fromUtf8("untitled"); - appId = QString::fromUtf8("unknown"); } -LXQtTaskBarWlrootsWindow::~LXQtTaskBarWlrootsWindow() +LXQtTaskbarWlrootsWindow::~LXQtTaskbarWlrootsWindow() { destroy(); } -void LXQtTaskBarWlrootsWindow::activate() +void LXQtTaskbarWlrootsWindow::activate() { /** * Activate on default seat. @@ -105,7 +101,7 @@ void LXQtTaskBarWlrootsWindow::activate() } -void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_title(const QString& title) +void LXQtTaskbarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_title(const QString& title) { /** Store the incoming title in pending */ m_pendingState.title = title; @@ -113,19 +109,19 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_title(const QStri } -void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_app_id(const QString& app_id) +void LXQtTaskbarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_app_id(const QString& app_id) { /** Store the incoming appId in pending */ m_pendingState.appId = app_id; m_pendingState.appIdChanged = true; /** Update the icon */ - this->icon = XdgIcon::fromTheme(appId); + this->icon = XdgIcon::fromTheme(app_id); /** Sometimes, appId can be capitalized, for example, Pulsar. So try lower-case. */ if (this->icon.pixmap(64).width() == 0) { - this->icon = XdgIcon::fromTheme(appId.toLower()); + this->icon = XdgIcon::fromTheme(app_id.toLower()); } /** We did not get any icon from app-id. Let's use application-x-executable */ @@ -136,7 +132,7 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_app_id(const QStr } -void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_enter(struct ::wl_output *output) +void LXQtTaskbarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_enter(struct ::wl_output *output) { /** This view was added to an output */ m_pendingState.outputs << output; @@ -144,7 +140,7 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_enter(stru } -void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_leave(struct ::wl_output *output) +void LXQtTaskbarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_leave(struct ::wl_output *output) { /** This view was removed from an output; store it in pending. */ m_pendingState.outputsLeft << output; @@ -158,7 +154,7 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_leave(stru } -void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_state(wl_array *state) +void LXQtTaskbarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_state(wl_array *state) { /** State of this window was changed; store it in pending. */ auto *states = static_cast(state->data); @@ -196,7 +192,7 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_state(wl_array *s } -void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_done() +void LXQtTaskbarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_done() { /** * All the states/properties have been sent. @@ -269,7 +265,7 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_done() { if (m_pendingState.parent) { - setParentWindow(new LXQtTaskBarWlrootsWindow(m_pendingState.parent)); + setParentWindow(new LXQtTaskbarWlrootsWindow(m_pendingState.parent)); } else @@ -297,16 +293,6 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_done() /** (a) First time done was emitted */ if (initDone == false) { - qDebug() << " " << this << getWindowId(); - qDebug() << " titleChanged" << m_pendingState.titleChanged << windowState.title; - qDebug() << " appIdChanged" << m_pendingState.appIdChanged << windowState.appId; - qDebug() << " outputsChanged" << m_pendingState.outputsChanged << windowState.outputs; - qDebug() << " maximizedChanged" << m_pendingState.maximizedChanged << windowState.maximized; - qDebug() << " minimizedChanged" << m_pendingState.minimizedChanged << windowState.minimized; - qDebug() << " activatedChanged" << m_pendingState.activatedChanged << windowState.activated; - qDebug() << " fullscreenChanged" << m_pendingState.fullscreenChanged << windowState.fullscreen; - qDebug() << " parentChanged" << m_pendingState.parentChanged << windowState.parent; - /** * All the states/properties are already set. * Any query will give valid results. @@ -350,14 +336,14 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_done() } -void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_closed() +void LXQtTaskbarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_closed() { /** This window was closed */ emit closed(); } -void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_parent(struct ::zwlr_foreign_toplevel_handle_v1 *parent) +void LXQtTaskbarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_parent(struct ::zwlr_foreign_toplevel_handle_v1 *parent) { /** Parent of this window changed; store it in pending. */ m_pendingState.parent = parent; @@ -365,28 +351,21 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_parent(struct ::z } -void LXQtTaskBarWlrootsWindow::setParentWindow(LXQtTaskBarWlrootsWindow *parent) +void LXQtTaskbarWlrootsWindow::setParentWindow(LXQtTaskbarWlrootsWindow *parent) { - const auto old = parentWindow; - QObject::disconnect(parentWindowUnmappedConnection); if (parent) { - parentWindow = QPointer(parent); + parentWindow = parent->getWindowId(); parentWindowUnmappedConnection = QObject::connect( - parent, &LXQtTaskBarWlrootsWindow::closed, this, [ this ] { + parent, &LXQtTaskbarWlrootsWindow::closed, this, [ this ] { setParentWindow(nullptr); }); } else { - parentWindow = QPointer(); + parentWindow = 0; parentWindowUnmappedConnection = QMetaObject::Connection(); } - - if (parentWindow.data() != old.data()) - { - Q_EMIT parentChanged(); - } } diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h index 705107312..9e4dbb6e6 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h @@ -9,17 +9,17 @@ typedef quintptr WId; -class LXQtTaskBarWlrootsWindow; +class LXQtTaskbarWlrootsWindow; -class LXQtTaskBarWlrootsWindowManagment : public QWaylandClientExtensionTemplate, +class LXQtTaskbarWlrootsWindowManagment : public QWaylandClientExtensionTemplate, public QtWayland::zwlr_foreign_toplevel_manager_v1 { Q_OBJECT public: static constexpr int version = 16; - LXQtTaskBarWlrootsWindowManagment(); - ~LXQtTaskBarWlrootsWindowManagment(); + LXQtTaskbarWlrootsWindowManagment(); + ~LXQtTaskbarWlrootsWindowManagment(); inline bool isShowingDesktop() const { return m_isShowingDesktop; } @@ -28,7 +28,7 @@ class LXQtTaskBarWlrootsWindowManagment : public QWaylandClientExtensionTemplate void zwlr_foreign_toplevel_manager_v1_finished() {}; Q_SIGNALS: - void windowCreated(LXQtTaskBarWlrootsWindow *window); + void windowCreated(WId wid); private: bool m_isShowingDesktop = false; @@ -74,23 +74,21 @@ class WindowProperties { QList<::wl_output *> outputsLeft; }; -class LXQtTaskBarWlrootsWindow : public QObject, +class LXQtTaskbarWlrootsWindow : public QObject, public QtWayland::zwlr_foreign_toplevel_handle_v1 { Q_OBJECT public: - LXQtTaskBarWlrootsWindow(::zwlr_foreign_toplevel_handle_v1 *id); - ~LXQtTaskBarWlrootsWindow(); + LXQtTaskbarWlrootsWindow(::zwlr_foreign_toplevel_handle_v1 *id); + ~LXQtTaskbarWlrootsWindow(); inline WId getWindowId() const { return reinterpret_cast(this); } void activate(); - mutable QString title; - mutable QString appId; - mutable QIcon icon; - mutable WindowProperties windowState; - mutable QPointer parentWindow; + QIcon icon; + WindowProperties windowState; + WId parentWindow; Q_SIGNALS: void titleChanged(); @@ -128,7 +126,7 @@ class LXQtTaskBarWlrootsWindow : public QObject, void zwlr_foreign_toplevel_handle_v1_parent(struct ::zwlr_foreign_toplevel_handle_v1 *parent); private: - void setParentWindow(LXQtTaskBarWlrootsWindow *parent); + void setParentWindow(LXQtTaskbarWlrootsWindow *parent); QMetaObject::Connection parentWindowUnmappedConnection; From fa1defa874d007c2ef971e63eacca5dcc164d124 Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Sat, 30 Mar 2024 00:43:02 +0530 Subject: [PATCH 072/165] Add a generic wlroots fallback in lxqttaskbarbackendwayland.cpp --- panel/backends/wayland/lxqttaskbarbackendwayland.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index 05d5941ef..c1b3f28af 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -55,6 +55,13 @@ LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) break; } + else if ( desktop == "wlroots" ) + { + qDebug() << "--------------> Using generic wlroots backend"; + m_backend = new LXQtTaskbarWlrootsBackend(); + break; + } + else { // m_backend = nullptr; From 5b25762e388eaca107aee00608ab8b8bc341798a Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Sun, 31 Mar 2024 13:40:45 +0530 Subject: [PATCH 073/165] Fix issue with restore (from task context menu. Remove debug messages. --- .../backends/wayland/lxqttaskbarbackendwayland.cpp | 8 -------- .../wayland/wlroots/lxqttaskbarbackendwlr.cpp | 13 ++++++------- panel/lxqtpanelapplication.cpp | 2 -- plugin-taskbar/lxqttaskbarplugin.cpp | 1 - plugin-taskbar/lxqttaskbutton.cpp | 3 ++- 5 files changed, 8 insertions(+), 19 deletions(-) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index c1b3f28af..4ded124d3 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -18,46 +18,39 @@ LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) * If wayfire/sway/labwc/hyprland/wlroots is in desktopsList, we'll use the wlroots backend. */ QList desktopsList = qgetenv("XDG_CURRENT_DESKTOP").toLower().split(':'); - qDebug() << "--------------> Current desktop" << desktopsList; for( QByteArray desktop: desktopsList ) { if ( desktop == "plasma" || desktop == "kde" || desktop == "kwin_wayland" ) { - qDebug() << "--------------> Using plasma backend"; m_backend = new LXQtTaskbarPlasmaBackend(); break; } else if ( desktop == "wayfire" ) { - qDebug() << "--------------> Using wayfire backend"; m_backend = new LXQtTaskbarWlrootsBackend(); break; } else if ( desktop == "sway" ) { - qDebug() << "--------------> Using sway backend"; m_backend = new LXQtTaskbarWlrootsBackend(); break; } else if ( desktop == "labwc" ) { - qDebug() << "--------------> Using labwc backend"; m_backend = new LXQtTaskbarWlrootsBackend(); break; } else if ( desktop == "hyprland" ) { - qDebug() << "--------------> Using hyprland backend"; m_backend = new LXQtTaskbarWlrootsBackend(); break; } else if ( desktop == "wlroots" ) { - qDebug() << "--------------> Using generic wlroots backend"; m_backend = new LXQtTaskbarWlrootsBackend(); break; } @@ -69,7 +62,6 @@ LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) } if ( m_backend == nullptr ) { - qDebug() << "--------------> Using dummy backend. No window management will be done"; } else { diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp index 3c5754244..f9af04fa3 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp @@ -188,14 +188,13 @@ bool LXQtTaskbarWlrootsBackend::setWindowState(WId windowId, LXQtTaskBarWindowSt } case LXQtTaskBarWindowState::Normal: { - /** Restore if maximized/minimized */ - if ( window->windowState.minimized) { - window->unset_minimized(); + if (set) + { + if ( window->windowState.maximized) { + window->unset_maximized(); + } } - if ( window->windowState.maximized) { - window->unset_maximized(); - } break; } @@ -229,7 +228,7 @@ bool LXQtTaskbarWlrootsBackend::isWindowActive(WId windowId) const bool LXQtTaskbarWlrootsBackend::raiseWindow(WId windowId, bool onCurrentWorkSpace) { - Q_UNUSED(onCurrentWorkSpace) //TODO + Q_UNUSED(onCurrentWorkSpace) // Cannot be done on a generic wlroots-based compositor! LXQtTaskbarWlrootsWindow *window = getWindow(windowId); if(!window) diff --git a/panel/lxqtpanelapplication.cpp b/panel/lxqtpanelapplication.cpp index 024ba4c05..2a3f08c36 100644 --- a/panel/lxqtpanelapplication.cpp +++ b/panel/lxqtpanelapplication.cpp @@ -47,12 +47,10 @@ ILXQtTaskbarAbstractBackend *createWMBackend() { if(qGuiApp->nativeInterface()){ - qDebug() << "--------------> X11Application"; return new LXQtTaskbarX11Backend; } else if(qGuiApp->nativeInterface()){ - qDebug() << "--------------> WaylandApplication"; return new LXQtTaskbarWaylandBackend; } diff --git a/plugin-taskbar/lxqttaskbarplugin.cpp b/plugin-taskbar/lxqttaskbarplugin.cpp index fbdd5b305..759e6db46 100644 --- a/plugin-taskbar/lxqttaskbarplugin.cpp +++ b/plugin-taskbar/lxqttaskbarplugin.cpp @@ -37,7 +37,6 @@ LXQtTaskBarPlugin::LXQtTaskBarPlugin(const ILXQtPanelPluginStartupInfo &startupI ILXQtPanelPlugin(startupInfo) { - qDebug() << "--------------> Init plugin taskbar"; mTaskBar = new LXQtTaskBar(this); } diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 851f765bc..c20c0bd92 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -439,7 +439,8 @@ void LXQtTaskButton::deMaximizeApplication() { mBackend->setWindowState(mWindow, LXQtTaskBarWindowState::Maximized, false); - if(!mBackend->isWindowActive(mWindow)) + /** This guard is unnecessary */ + // if(!mBackend->isWindowActive(mWindow)) mBackend->raiseWindow(mWindow, parentTaskBar()->raiseOnCurrentDesktop()); } From c3d0531a769ada252dce690bd43b28208392b729 Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Thu, 4 Apr 2024 14:58:54 +0530 Subject: [PATCH 074/165] Remove qdebug message from lxqttaskbarbackendplasma.cpp --- panel/backends/wayland/plasma/lxqttaskbarbackendplasma.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.cpp b/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.cpp index 0b7d206de..ad8b2332f 100644 --- a/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.cpp +++ b/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.cpp @@ -118,7 +118,6 @@ bool LXQtTaskbarPlasmaBackend::reloadWindows() QVector LXQtTaskbarPlasmaBackend::getCurrentWindows() const { - qDebug() << "--------------> Retrieving current window list"; QVector wids; wids.reserve(wids.size()); From d274b54277ae4cfc7b46978530e93498b20fcba7 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sun, 28 Jan 2024 14:48:18 +0100 Subject: [PATCH 075/165] TaskBar: forward declare --- plugin-taskbar/lxqttaskbar.cpp | 12 ++++++++++-- plugin-taskbar/lxqttaskbar.h | 18 +++++++++++------- plugin-taskbar/lxqttaskbarconfiguration.h | 4 ++-- plugin-taskbar/lxqttaskbarplugin.cpp | 10 +++++++++- plugin-taskbar/lxqttaskbarplugin.h | 10 ++++------ plugin-taskbar/lxqttaskbutton.cpp | 8 ++++---- plugin-taskbar/lxqttaskbutton.h | 2 +- plugin-taskbar/lxqttaskgroup.cpp | 2 ++ plugin-taskbar/lxqttaskgroup.h | 6 ++---- 9 files changed, 45 insertions(+), 27 deletions(-) diff --git a/plugin-taskbar/lxqttaskbar.cpp b/plugin-taskbar/lxqttaskbar.cpp index 5dfa289c9..c4fa01da2 100644 --- a/plugin-taskbar/lxqttaskbar.cpp +++ b/plugin-taskbar/lxqttaskbar.cpp @@ -28,6 +28,8 @@ * * END_COMMON_COPYRIGHT_HEADER */ +#include "lxqttaskbar.h" + #include #include #include @@ -39,11 +41,12 @@ #include #include +#include "ilxqtpanelplugin.h" +#include "pluginsettings.h" + #include #include -#include -#include "lxqttaskbar.h" #include "lxqttaskgroup.h" #include "../panel/pluginsettings.h" @@ -591,6 +594,11 @@ void LXQtTaskBar::realign() emit refreshIconGeometry(); } +ILXQtPanel *LXQtTaskBar::panel() const +{ + return mPlugin->panel(); +} + /************************************************ ************************************************/ diff --git a/plugin-taskbar/lxqttaskbar.h b/plugin-taskbar/lxqttaskbar.h index 03e2bd0d2..d80d1ea13 100644 --- a/plugin-taskbar/lxqttaskbar.h +++ b/plugin-taskbar/lxqttaskbar.h @@ -32,30 +32,33 @@ #ifndef LXQTTASKBAR_H #define LXQTTASKBAR_H -#include "../panel/ilxqtpanel.h" -#include "../panel/ilxqtpanelplugin.h" - #include #include #include -#include + #include "../panel/ilxqtpanel.h" #include #include #include +class ILXQtPanel; +class ILXQtPanelPlugin; + class QSignalMapper; -class LXQtTaskButton; class LXQtTaskGroup; -class ElidedButtonStyle; class LeftAlignedTextStyle; namespace LXQt { class GridLayout; } +namespace GlobalKeyShortcut +{ +class Action; +} + class LXQtTaskBar : public QFrame { Q_OBJECT @@ -80,7 +83,8 @@ class LXQtTaskBar : public QFrame bool isIconByClass() const { return mIconByClass; } int wheelEventsAction() const { return mWheelEventsAction; } int wheelDeltaThreshold() const { return mWheelDeltaThreshold; } - inline ILXQtPanel * panel() const { return mPlugin->panel(); } + + ILXQtPanel * panel() const; inline ILXQtPanelPlugin * plugin() const { return mPlugin; } public slots: diff --git a/plugin-taskbar/lxqttaskbarconfiguration.h b/plugin-taskbar/lxqttaskbarconfiguration.h index e559508b7..930e9f441 100644 --- a/plugin-taskbar/lxqttaskbarconfiguration.h +++ b/plugin-taskbar/lxqttaskbarconfiguration.h @@ -29,8 +29,8 @@ #define LXQTTASKBARCONFIGURATION_H #include "../panel/lxqtpanelpluginconfigdialog.h" -#include "../panel/pluginsettings.h" -#include + +class PluginSettings; namespace Ui { class LXQtTaskbarConfiguration; diff --git a/plugin-taskbar/lxqttaskbarplugin.cpp b/plugin-taskbar/lxqttaskbarplugin.cpp index ae8af1403..759e6db46 100644 --- a/plugin-taskbar/lxqttaskbarplugin.cpp +++ b/plugin-taskbar/lxqttaskbarplugin.cpp @@ -28,6 +28,8 @@ #include "lxqttaskbarplugin.h" +#include "lxqttaskbar.h" + #include "lxqttaskbarconfiguration.h" LXQtTaskBarPlugin::LXQtTaskBarPlugin(const ILXQtPanelPluginStartupInfo &startupInfo): @@ -36,7 +38,6 @@ LXQtTaskBarPlugin::LXQtTaskBarPlugin(const ILXQtPanelPluginStartupInfo &startupI { mTaskBar = new LXQtTaskBar(this); - } @@ -45,11 +46,18 @@ LXQtTaskBarPlugin::~LXQtTaskBarPlugin() delete mTaskBar; } +QWidget *LXQtTaskBarPlugin::widget() { return mTaskBar; } + QDialog *LXQtTaskBarPlugin::configureDialog() { return new LXQtTaskbarConfiguration(settings()); } +void LXQtTaskBarPlugin::settingsChanged() +{ + mTaskBar->settingsChanged(); +} + void LXQtTaskBarPlugin::realign() { mTaskBar->realign(); diff --git a/plugin-taskbar/lxqttaskbarplugin.h b/plugin-taskbar/lxqttaskbarplugin.h index 9c3076990..9bc34cf8e 100644 --- a/plugin-taskbar/lxqttaskbarplugin.h +++ b/plugin-taskbar/lxqttaskbarplugin.h @@ -29,10 +29,8 @@ #ifndef LXQTTASKBARPLUGIN_H #define LXQTTASKBARPLUGIN_H -#include "../panel/ilxqtpanel.h" -#include "../panel/ilxqtpanelplugin.h" -#include "lxqttaskbar.h" -#include +#include "ilxqtpanelplugin.h" + class LXQtTaskBar; class LXQtTaskBarPlugin : public QObject, public ILXQtPanelPlugin @@ -45,10 +43,10 @@ class LXQtTaskBarPlugin : public QObject, public ILXQtPanelPlugin QString themeId() const { return QStringLiteral("TaskBar"); } virtual Flags flags() const { return HaveConfigDialog | NeedsHandle; } - QWidget *widget() { return mTaskBar; } + QWidget *widget(); QDialog *configureDialog(); - void settingsChanged() { mTaskBar->settingsChanged(); } + void settingsChanged(); void realign(); bool isSeparate() const { return true; } diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index bd6df460f..2f1fe3116 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -31,6 +31,8 @@ #include "lxqttaskgroup.h" #include "lxqttaskbar.h" +#include "ilxqtpanelplugin.h" + #include #include @@ -49,11 +51,8 @@ #include #include -#include "lxqttaskbutton.h" -#include "lxqttaskgroup.h" -#include "lxqttaskbar.h" - #include + // Necessary for closeApplication() #include @@ -61,6 +60,7 @@ #include #undef Bool + bool LXQtTaskButton::sDraggging = false; /************************************************ diff --git a/plugin-taskbar/lxqttaskbutton.h b/plugin-taskbar/lxqttaskbutton.h index 69f3b41d8..73d0886d4 100644 --- a/plugin-taskbar/lxqttaskbutton.h +++ b/plugin-taskbar/lxqttaskbutton.h @@ -33,12 +33,12 @@ #include #include + #include "../panel/ilxqtpanel.h" class QPainter; class QPalette; class QMimeData; -class LXQtTaskGroup; class LXQtTaskBar; class LeftAlignedTextStyle : public QProxyStyle diff --git a/plugin-taskbar/lxqttaskgroup.cpp b/plugin-taskbar/lxqttaskgroup.cpp index e192baabf..a8aaa3f2a 100644 --- a/plugin-taskbar/lxqttaskgroup.cpp +++ b/plugin-taskbar/lxqttaskgroup.cpp @@ -32,6 +32,8 @@ #include "lxqttaskbar.h" #include "lxqtgrouppopup.h" +#include "ilxqtpanelplugin.h" + #include #include #include diff --git a/plugin-taskbar/lxqttaskgroup.h b/plugin-taskbar/lxqttaskgroup.h index f1e7e2469..3787f411f 100644 --- a/plugin-taskbar/lxqttaskgroup.h +++ b/plugin-taskbar/lxqttaskgroup.h @@ -31,11 +31,9 @@ #ifndef LXQTTASKGROUP_H #define LXQTTASKGROUP_H -#include "../panel/ilxqtpanel.h" -#include "../panel/ilxqtpanelplugin.h" - #include "lxqttaskbutton.h" -#include + +#include class QVBoxLayout; class ILXQtPanelPlugin; From 43ddc7cf20b1541258f25ba3a75b71cb7f80f2e0 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 27 Jan 2024 18:37:31 +0100 Subject: [PATCH 076/165] TaskBar: new ILXQtTaskbarAbstractBackend This is an abstract interface to operate windows and workspaces --- plugin-taskbar/CMakeLists.txt | 5 + .../ilxqttaskbarabstractbackend.cpp | 25 +++++ plugin-taskbar/ilxqttaskbarabstractbackend.h | 94 +++++++++++++++++++ plugin-taskbar/lxqttaskbartypes.h | 32 +++++++ 4 files changed, 156 insertions(+) create mode 100644 plugin-taskbar/ilxqttaskbarabstractbackend.cpp create mode 100644 plugin-taskbar/ilxqttaskbarabstractbackend.h create mode 100644 plugin-taskbar/lxqttaskbartypes.h diff --git a/plugin-taskbar/CMakeLists.txt b/plugin-taskbar/CMakeLists.txt index 1d627f23e..9ca6321cc 100644 --- a/plugin-taskbar/CMakeLists.txt +++ b/plugin-taskbar/CMakeLists.txt @@ -7,6 +7,9 @@ set(HEADERS lxqttaskbarplugin.h lxqttaskgroup.h lxqtgrouppopup.h + + ilxqttaskbarabstractbackend.h + lxqttaskbartypes.h ) set(SOURCES @@ -16,6 +19,8 @@ set(SOURCES lxqttaskbarplugin.cpp lxqttaskgroup.cpp lxqtgrouppopup.cpp + + ilxqttaskbarabstractbackend.cpp ) set(UIS diff --git a/plugin-taskbar/ilxqttaskbarabstractbackend.cpp b/plugin-taskbar/ilxqttaskbarabstractbackend.cpp new file mode 100644 index 000000000..d0959fb78 --- /dev/null +++ b/plugin-taskbar/ilxqttaskbarabstractbackend.cpp @@ -0,0 +1,25 @@ +#include "ilxqttaskbarabstractbackend.h" + + +ILXQtTaskbarAbstractBackend::ILXQtTaskbarAbstractBackend(QObject *parent) + : QObject(parent) +{ + +} + +void ILXQtTaskbarAbstractBackend::moveApplicationToPrevNextDesktop(WId windowId, bool next) +{ + int count = getWorkspacesCount(); + if (count <= 1) + return; + + int targetWorkspace = getWindowWorkspace(windowId) + (next ? 1 : -1); + + // Wrap around + if (targetWorkspace > count) + targetWorkspace = 1; //TODO: are X11 desktops 1 based? + else if (targetWorkspace < 1) + targetWorkspace = count; + + setWindowOnWorkspace(windowId, targetWorkspace); +} diff --git a/plugin-taskbar/ilxqttaskbarabstractbackend.h b/plugin-taskbar/ilxqttaskbarabstractbackend.h new file mode 100644 index 000000000..f37a4081b --- /dev/null +++ b/plugin-taskbar/ilxqttaskbarabstractbackend.h @@ -0,0 +1,94 @@ +#ifndef ILXQTTASKBARABSTRACTBACKEND_H +#define ILXQTTASKBARABSTRACTBACKEND_H + +#include + +#include "lxqttaskbartypes.h" + +class QIcon; +class QScreen; + +//FIXME: add something like bool KWindowInfo::actionSupported(...) + +class ILXQtTaskbarAbstractBackend : public QObject +{ + Q_OBJECT + +public: + enum class WindowProperty + { + Title = 0, + Icon, + State, + Urgency, + WindowClass, + Workspace + }; + + explicit ILXQtTaskbarAbstractBackend(QObject *parent = nullptr); + + // Windows + virtual bool reloadWindows() = 0; + + virtual QVector getCurrentWindows() const = 0; + + virtual QString getWindowTitle(WId windowId) const = 0; + + virtual bool applicationDemandsAttention(WId windowId) const = 0; + + virtual QIcon getApplicationIcon(WId windowId, int fallbackDevicePixels) const = 0; + + virtual QString getWindowClass(WId windowId) const = 0; + + virtual LXQtTaskBarWindowLayer getWindowLayer(WId windowId) const = 0; + virtual bool setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) = 0; + + virtual LXQtTaskBarWindowState getWindowState(WId windowId) const = 0; + virtual bool setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set = true) = 0; + + virtual bool isWindowActive(WId windowId) const = 0; + virtual bool raiseWindow(WId windowId, bool onCurrentWorkSpace) = 0; + + virtual bool closeWindow(WId windowId) = 0; + + virtual WId getActiveWindow() const = 0; + + // Workspaces + virtual int getWorkspacesCount() const = 0; + virtual QString getWorkspaceName(int idx) const = 0; + + virtual int getCurrentWorkspace() const = 0; + virtual bool setCurrentWorkspace(int idx) = 0; + + virtual int getWindowWorkspace(WId windowId) const = 0; + virtual bool setWindowOnWorkspace(WId windowId, int idx) = 0; + + virtual void moveApplicationToPrevNextDesktop(WId windowId, bool next); // Default implementation + virtual void moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) = 0; + + virtual bool isWindowOnScreen(QScreen *screen, WId windowId) const = 0; + + // X11 Specific + virtual void moveApplication(WId windowId) = 0; + virtual void resizeApplication(WId windowId) = 0; + + virtual void refreshIconGeometry(WId windowId, const QRect &geom) = 0; + +signals: + void reloaded(); + + // Windows + void windowAdded(WId windowId); + void windowRemoved(WId windowId); + void windowPropertyChanged(WId windowId, int prop); + + // Workspaces + void workspacesCountChanged(); + void workspaceNameChanged(int idx); + void currentWorkspaceChanged(int idx); + + // TODO: needed? + void activeWindowChanged(WId windowId); +}; + +#endif // ILXQTTASKBARABSTRACTBACKEND_H diff --git a/plugin-taskbar/lxqttaskbartypes.h b/plugin-taskbar/lxqttaskbartypes.h new file mode 100644 index 000000000..d841fec74 --- /dev/null +++ b/plugin-taskbar/lxqttaskbartypes.h @@ -0,0 +1,32 @@ +#ifndef LXQTTASKBARTYPES_H +#define LXQTTASKBARTYPES_H + +#include + +typedef quintptr WId; + +enum class LXQtTaskBarWindowState +{ + Hidden = 0, + FullScreen, + Minimized, + Maximized, + MaximizedVertically, + MaximizedHorizontally, + Normal, + RolledUp //Shaded +}; + +enum class LXQtTaskBarWindowLayer +{ + KeepBelow = 0, + Normal, + KeepAbove +}; + +enum class LXQtTaskBarWorkspace +{ + ShowOnAll = -1 +}; + +#endif // LXQTTASKBARTYPES_H From 223e8fc82dc882df69e9e85f61a4ce2b31051dce Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sun, 28 Jan 2024 16:37:03 +0100 Subject: [PATCH 077/165] ILXQtTaskbarAbstractBackend: add supportsAction() method - Move WindowProperty enum to lxqttaskbartypes.h --- plugin-taskbar/ilxqttaskbarabstractbackend.h | 15 +++---------- plugin-taskbar/lxqttaskbartypes.h | 22 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/plugin-taskbar/ilxqttaskbarabstractbackend.h b/plugin-taskbar/ilxqttaskbarabstractbackend.h index f37a4081b..932d64fc0 100644 --- a/plugin-taskbar/ilxqttaskbarabstractbackend.h +++ b/plugin-taskbar/ilxqttaskbarabstractbackend.h @@ -8,25 +8,16 @@ class QIcon; class QScreen; -//FIXME: add something like bool KWindowInfo::actionSupported(...) - class ILXQtTaskbarAbstractBackend : public QObject { Q_OBJECT public: - enum class WindowProperty - { - Title = 0, - Icon, - State, - Urgency, - WindowClass, - Workspace - }; - explicit ILXQtTaskbarAbstractBackend(QObject *parent = nullptr); + // Backend + virtual bool supportsAction(WId windowId, LXQtTaskBarBackendAction action) const = 0; + // Windows virtual bool reloadWindows() = 0; diff --git a/plugin-taskbar/lxqttaskbartypes.h b/plugin-taskbar/lxqttaskbartypes.h index d841fec74..ccea42027 100644 --- a/plugin-taskbar/lxqttaskbartypes.h +++ b/plugin-taskbar/lxqttaskbartypes.h @@ -5,6 +5,28 @@ typedef quintptr WId; +enum class LXQtTaskBarBackendAction +{ + Move = 0, + Resize, + Maximize, + MaximizeVertically, + MaximizeHorizontally, + Minimize, + RollUp, + FullScreen +}; + +enum class LXQtTaskBarWindowProperty +{ + Title = 0, + Icon, + State, + Urgency, + WindowClass, + Workspace +}; + enum class LXQtTaskBarWindowState { Hidden = 0, From f1bf02ab6f474083b4ab81e908dd7a1099886aeb Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 27 Jan 2024 18:39:44 +0100 Subject: [PATCH 078/165] TaskBar: add X11 backend --- plugin-taskbar/CMakeLists.txt | 2 + plugin-taskbar/lxqttaskbarbackend_x11.cpp | 582 ++++++++++++++++++++++ plugin-taskbar/lxqttaskbarbackend_x11.h | 80 +++ 3 files changed, 664 insertions(+) create mode 100644 plugin-taskbar/lxqttaskbarbackend_x11.cpp create mode 100644 plugin-taskbar/lxqttaskbarbackend_x11.h diff --git a/plugin-taskbar/CMakeLists.txt b/plugin-taskbar/CMakeLists.txt index 9ca6321cc..053381bef 100644 --- a/plugin-taskbar/CMakeLists.txt +++ b/plugin-taskbar/CMakeLists.txt @@ -10,6 +10,7 @@ set(HEADERS ilxqttaskbarabstractbackend.h lxqttaskbartypes.h + lxqttaskbarbackend_x11.h ) set(SOURCES @@ -21,6 +22,7 @@ set(SOURCES lxqtgrouppopup.cpp ilxqttaskbarabstractbackend.cpp + lxqttaskbarbackend_x11.cpp ) set(UIS diff --git a/plugin-taskbar/lxqttaskbarbackend_x11.cpp b/plugin-taskbar/lxqttaskbarbackend_x11.cpp new file mode 100644 index 000000000..cc7759f4e --- /dev/null +++ b/plugin-taskbar/lxqttaskbarbackend_x11.cpp @@ -0,0 +1,582 @@ +#include "lxqttaskbarbackend_x11.h" + +#include +#include +#include + +// Necessary for closeApplication() +#include + +#include +#include + +#include + +//NOTE: Xlib.h defines Bool which conflicts with QJsonValue::Type enum +#include +#undef Bool + +LXQtTaskbarX11Backend::LXQtTaskbarX11Backend(QObject *parent) + : ILXQtTaskbarAbstractBackend(parent) +{ + auto *x11Application = qGuiApp->nativeInterface(); + Q_ASSERT_X(x11Application, "LXQtTaskbarX11Backend", "Constructed without X11 connection"); + m_X11Display = x11Application->display(); + m_xcbConnection = x11Application->connection(); + + connect(KX11Extras::self(), &KX11Extras::windowChanged, this, &LXQtTaskbarX11Backend::onWindowChanged); + connect(KX11Extras::self(), &KX11Extras::windowAdded, this, &LXQtTaskbarX11Backend::onWindowAdded); + connect(KX11Extras::self(), &KX11Extras::windowRemoved, this, &LXQtTaskbarX11Backend::onWindowRemoved); + + connect(KX11Extras::self(), &KX11Extras::numberOfDesktopsChanged, this, &ILXQtTaskbarAbstractBackend::workspacesCountChanged); + connect(KX11Extras::self(), &KX11Extras::currentDesktopChanged, this, &ILXQtTaskbarAbstractBackend::currentWorkspaceChanged); + + connect(KX11Extras::self(), &KX11Extras::activeWindowChanged, this, &ILXQtTaskbarAbstractBackend::activeWindowChanged); +} + +/************************************************ + * Model slots + ************************************************/ +void LXQtTaskbarX11Backend::onWindowChanged(WId windowId, NET::Properties prop, NET::Properties2 prop2) +{ + if(!m_windows.contains(windowId)) + return; + + if(!acceptWindow(windowId)) + { + onWindowRemoved(windowId); + return; + } + + if (prop2.testFlag(NET::WM2WindowClass)) + { + emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::WindowClass)); + } + + // window changed virtual desktop + if (prop.testFlag(NET::WMDesktop) || prop.testFlag(NET::WMGeometry)) + { + emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::Workspace)); + } + + if (prop.testFlag(NET::WMVisibleName) || prop.testFlag(NET::WMName)) + emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::Title)); + + // XXX: we are setting window icon geometry -> don't need to handle NET::WMIconGeometry + // Icon of the button can be based on windowClass + if (prop.testFlag(NET::WMIcon) || prop2.testFlag(NET::WM2WindowClass)) + emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::Icon)); + + bool update_urgency = false; + if (prop2.testFlag(NET::WM2Urgency)) + { + update_urgency = true; + } + + if (prop.testFlag(NET::WMState)) + { + update_urgency = true; + + emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::State)); + } + + if (update_urgency) + emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::Urgency)); +} + +void LXQtTaskbarX11Backend::onWindowAdded(WId windowId) +{ + if(m_windows.contains(windowId)) + return; + + if (!acceptWindow(windowId)) + return; + + addWindow_internal(windowId); +} + +void LXQtTaskbarX11Backend::onWindowRemoved(WId windowId) +{ + const int row = m_windows.indexOf(windowId); + if(row == -1) + return; + + m_windows.removeAt(row); + + emit windowRemoved(windowId); +} + +/************************************************ + * Model private functions + ************************************************/ +bool LXQtTaskbarX11Backend::acceptWindow(WId windowId) const +{ + QFlags ignoreList; + ignoreList |= NET::DesktopMask; + ignoreList |= NET::DockMask; + ignoreList |= NET::SplashMask; + ignoreList |= NET::ToolbarMask; + ignoreList |= NET::MenuMask; + ignoreList |= NET::PopupMenuMask; + ignoreList |= NET::NotificationMask; + + KWindowInfo info(windowId, NET::WMWindowType | NET::WMState, NET::WM2TransientFor); + if (!info.valid()) + return false; + + if (NET::typeMatchesMask(info.windowType(NET::AllTypesMask), ignoreList)) + return false; + + if (info.state() & NET::SkipTaskbar) + return false; + + // WM_TRANSIENT_FOR hint not set - normal window + WId transFor = info.transientFor(); + + WId appRootWindow = XDefaultRootWindow(m_X11Display); + + if (transFor == 0 || transFor == windowId || transFor == appRootWindow) + return true; + + info = KWindowInfo(transFor, NET::WMWindowType); + + QFlags normalFlag; + normalFlag |= NET::NormalMask; + normalFlag |= NET::DialogMask; + normalFlag |= NET::UtilityMask; + + return !NET::typeMatchesMask(info.windowType(NET::AllTypesMask), normalFlag); +} + +void LXQtTaskbarX11Backend::addWindow_internal(WId windowId, bool emitAdded) +{ + m_windows.append(windowId); + if(emitAdded) + emit windowAdded(windowId); +} + + +/************************************************ + * Windows function + ************************************************/ +bool LXQtTaskbarX11Backend::supportsAction(WId windowId, LXQtTaskBarBackendAction action) const +{ + NET::Action x11Action; + + switch (action) + { + case LXQtTaskBarBackendAction::Move: + x11Action = NET::ActionMove; + break; + + case LXQtTaskBarBackendAction::Resize: + x11Action = NET::ActionResize; + break; + + case LXQtTaskBarBackendAction::Maximize: + x11Action = NET::ActionMax; + break; + + case LXQtTaskBarBackendAction::MaximizeVertically: + x11Action = NET::ActionMaxVert; + break; + + case LXQtTaskBarBackendAction::MaximizeHorizontally: + x11Action = NET::ActionMaxHoriz; + break; + + case LXQtTaskBarBackendAction::Minimize: + x11Action = NET::ActionMinimize; + break; + + case LXQtTaskBarBackendAction::RollUp: + x11Action = NET::ActionShade; + break; + + case LXQtTaskBarBackendAction::FullScreen: + x11Action = NET::ActionFullScreen; + break; + + default: + return false; + } + + KWindowInfo info(windowId, NET::Properties(), NET::WM2AllowedActions); + return info.actionSupported(x11Action); +} + +bool LXQtTaskbarX11Backend::reloadWindows() +{ + QVector oldWindows; + qSwap(oldWindows, m_windows); + + // Just add new windows to groups, deleting is up to the groups + const auto x11windows = KX11Extras::stackingOrder(); + for (auto const windowId: x11windows) + { + if (acceptWindow(windowId)) + { + bool emitAdded = !oldWindows.contains(windowId); + addWindow_internal(windowId, emitAdded); + } + } + + //emulate windowRemoved if known window not reported by KWindowSystem + for (auto i = oldWindows.begin(), i_e = oldWindows.end(); i != i_e; i++) + { + WId windowId = *i; + if (!m_windows.contains(windowId)) + { + //TODO: more efficient method? + emit windowRemoved(windowId); + } + } + + //TODO: refreshPlaceholderVisibility() + emit reloaded(); + + return true; +} + +QVector LXQtTaskbarX11Backend::getCurrentWindows() const +{ + return m_windows; +} + +QString LXQtTaskbarX11Backend::getWindowTitle(WId windowId) const +{ + KWindowInfo info(windowId, NET::WMVisibleName | NET::WMName); + QString title = info.visibleName().isEmpty() ? info.name() : info.visibleName(); + return title; +} + +bool LXQtTaskbarX11Backend::applicationDemandsAttention(WId windowId) const +{ + WId appRootWindow = XDefaultRootWindow(m_X11Display); + return NETWinInfo(m_xcbConnection, windowId, appRootWindow, NET::Properties{}, NET::WM2Urgency).urgency() + || KWindowInfo{windowId, NET::WMState}.hasState(NET::DemandsAttention); +} + +QIcon LXQtTaskbarX11Backend::getApplicationIcon(WId windowId, int devicePixels) const +{ + return KX11Extras::icon(windowId, devicePixels, devicePixels); +} + +QString LXQtTaskbarX11Backend::getWindowClass(WId windowId) const +{ + KWindowInfo info(windowId, NET::Properties(), NET::WM2WindowClass); + return QString::fromUtf8(info.windowClassClass()); +} + +LXQtTaskBarWindowLayer LXQtTaskbarX11Backend::getWindowLayer(WId windowId) const +{ + NET::States state = KWindowInfo(windowId, NET::WMState).state(); + if(state.testFlag(NET::KeepAbove)) + return LXQtTaskBarWindowLayer::KeepAbove; + else if(state.testFlag(NET::KeepBelow)) + return LXQtTaskBarWindowLayer::KeepBelow; + return LXQtTaskBarWindowLayer::Normal; +} + +bool LXQtTaskbarX11Backend::setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) +{ + switch(layer) + { + case LXQtTaskBarWindowLayer::KeepAbove: + KX11Extras::clearState(windowId, NET::KeepBelow); + KX11Extras::setState(windowId, NET::KeepAbove); + break; + + case LXQtTaskBarWindowLayer::KeepBelow: + KX11Extras::clearState(windowId, NET::KeepAbove); + KX11Extras::setState(windowId, NET::KeepBelow); + break; + + default: + KX11Extras::clearState(windowId, NET::KeepBelow); + KX11Extras::clearState(windowId, NET::KeepAbove); + break; + } + + return true; +} + +LXQtTaskBarWindowState LXQtTaskbarX11Backend::getWindowState(WId windowId) const +{ + KWindowInfo info(windowId,NET::WMState | NET::XAWMState); + if(info.isMinimized()) + return LXQtTaskBarWindowState::Minimized; + + NET::States state = info.state(); + if(state.testFlag(NET::Hidden)) + return LXQtTaskBarWindowState::Hidden; + if(state.testFlag(NET::Max)) + return LXQtTaskBarWindowState::Maximized; + if(state.testFlag(NET::MaxHoriz)) + return LXQtTaskBarWindowState::MaximizedHorizontally; + if(state.testFlag(NET::MaxVert)) + return LXQtTaskBarWindowState::MaximizedVertically; + if(state.testFlag(NET::Shaded)) + return LXQtTaskBarWindowState::RolledUp; + if(state.testFlag(NET::FullScreen)) + return LXQtTaskBarWindowState::FullScreen; + + return LXQtTaskBarWindowState::Normal; +} + +bool LXQtTaskbarX11Backend::setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set) +{ + // NOTE: window activation is left to the caller + + NET::State x11State; + + switch (state) + { + case LXQtTaskBarWindowState::Minimized: + { + if(set) + KX11Extras::minimizeWindow(windowId); + else + KX11Extras::unminimizeWindow(windowId); + return true; + } + case LXQtTaskBarWindowState::Maximized: + { + x11State = NET::Max; + break; + } + case LXQtTaskBarWindowState::MaximizedVertically: + { + x11State = NET::MaxVert; + break; + } + case LXQtTaskBarWindowState::MaximizedHorizontally: + { + x11State = NET::MaxHoriz; + break; + } + case LXQtTaskBarWindowState::Normal: + { + x11State = NET::Max; //TODO: correct? + break; + } + case LXQtTaskBarWindowState::RolledUp: + { + x11State = NET::Shaded; + break; + } + default: + return false; + } + + if(set) + KX11Extras::setState(windowId, x11State); + else + KX11Extras::clearState(windowId, x11State); + + return true; +} + +bool LXQtTaskbarX11Backend::isWindowActive(WId windowId) const +{ + return KX11Extras::activeWindow() == windowId; +} + +bool LXQtTaskbarX11Backend::raiseWindow(WId windowId, bool onCurrentWorkSpace) +{ + if (onCurrentWorkSpace && getWindowState(windowId) == LXQtTaskBarWindowState::Minimized) + { + setWindowOnWorkspace(windowId, getCurrentWorkspace()); + } + else + { + setCurrentWorkspace(getWindowWorkspace(windowId)); + } + + // bypass focus stealing prevention + KX11Extras::forceActiveWindow(windowId); + + // Clear urgency flag + emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::Urgency)); + + return true; +} + +bool LXQtTaskbarX11Backend::closeWindow(WId windowId) +{ + // FIXME: Why there is no such thing in KWindowSystem?? + NETRootInfo(m_xcbConnection, NET::CloseWindow).closeWindowRequest(windowId); + return true; +} + +WId LXQtTaskbarX11Backend::getActiveWindow() const +{ + return KX11Extras::activeWindow(); +} + + +/************************************************ + * Workspaces + ************************************************/ +int LXQtTaskbarX11Backend::getWorkspacesCount() const +{ + return KX11Extras::numberOfDesktops(); +} + +QString LXQtTaskbarX11Backend::getWorkspaceName(int idx) const +{ + return KX11Extras::desktopName(idx); +} + +int LXQtTaskbarX11Backend::getCurrentWorkspace() const +{ + return KX11Extras::currentDesktop(); +} + +bool LXQtTaskbarX11Backend::setCurrentWorkspace(int idx) +{ + if(KX11Extras::currentDesktop() == idx) + return true; + + KX11Extras::setCurrentDesktop(idx); + return true; +} + +int LXQtTaskbarX11Backend::getWindowWorkspace(WId windowId) const +{ + KWindowInfo info(windowId, NET::WMDesktop); + return info.desktop(); +} + +bool LXQtTaskbarX11Backend::setWindowOnWorkspace(WId windowId, int idx) +{ + KX11Extras::setOnDesktop(windowId, idx); + return true; +} + +void LXQtTaskbarX11Backend::moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) +{ + KWindowInfo info(windowId, NET::WMDesktop); + if (!info.isOnCurrentDesktop()) + KX11Extras::setCurrentDesktop(info.desktop()); + + if (getWindowState(windowId) == LXQtTaskBarWindowState::Minimized) + KX11Extras::unminimizeWindow(windowId); + + KX11Extras::forceActiveWindow(windowId); + + const QRect& windowGeometry = KWindowInfo(windowId, NET::WMFrameExtents).frameGeometry(); + QList screens = QGuiApplication::screens(); + if (screens.size() > 1) + { + for (int i = 0; i < screens.size(); ++i) + { + QRect screenGeometry = screens[i]->geometry(); + if (screenGeometry.intersects(windowGeometry)) + { + int targetScreen = i + (next ? 1 : -1); + if (targetScreen < 0) + targetScreen += screens.size(); + else if (targetScreen >= screens.size()) + targetScreen -= screens.size(); + + QRect targetScreenGeometry = screens[targetScreen]->geometry(); + int X = windowGeometry.x() - screenGeometry.x() + targetScreenGeometry.x(); + int Y = windowGeometry.y() - screenGeometry.y() + targetScreenGeometry.y(); + NET::States state = KWindowInfo(windowId, NET::WMState).state(); + + // NW geometry | y/x | from panel + const int flags = 1 | (0b011 << 8) | (0b010 << 12); + KX11Extras::clearState(windowId, NET::MaxHoriz | NET::MaxVert | NET::Max | NET::FullScreen); + NETRootInfo(m_xcbConnection, NET::Properties(), NET::WM2MoveResizeWindow).moveResizeWindowRequest(windowId, flags, X, Y, 0, 0); + QTimer::singleShot(200, this, [this, windowId, state, raiseOnCurrentDesktop] + { + KX11Extras::setState(windowId, state); + raiseWindow(windowId, raiseOnCurrentDesktop); + }); + break; + } + } + } +} + +bool LXQtTaskbarX11Backend::isWindowOnScreen(QScreen *screen, WId windowId) const +{ + //TODO: old code was: + //return QApplication::desktop()->screenGeometry(parentTaskBar()).intersects(KWindowInfo(mWindow, NET::WMFrameExtents).frameGeometry()); + + if(!screen) + return true; + + QRect r = KWindowInfo(windowId, NET::WMFrameExtents).frameGeometry(); + return screen->geometry().intersects(r); +} + +/************************************************ + * X11 Specific + ************************************************/ +void LXQtTaskbarX11Backend::moveApplication(WId windowId) +{ + KWindowInfo info(windowId, NET::WMDesktop); + if (!info.isOnCurrentDesktop()) + KX11Extras::setCurrentDesktop(info.desktop()); + + if (getWindowState(windowId) == LXQtTaskBarWindowState::Minimized) + KX11Extras::unminimizeWindow(windowId); + + KX11Extras::forceActiveWindow(windowId); + + const QRect& g = KWindowInfo(windowId, NET::WMGeometry).geometry(); + int X = g.center().x(); + int Y = g.center().y(); + QCursor::setPos(X, Y); + NETRootInfo(m_xcbConnection, NET::WMMoveResize).moveResizeRequest(windowId, X, Y, NET::Move); +} + +void LXQtTaskbarX11Backend::resizeApplication(WId windowId) +{ + KWindowInfo info(windowId, NET::WMDesktop); + if (!info.isOnCurrentDesktop()) + KX11Extras::setCurrentDesktop(info.desktop()); + + if (getWindowState(windowId) == LXQtTaskBarWindowState::Minimized) + KX11Extras::unminimizeWindow(windowId); + + KX11Extras::forceActiveWindow(windowId); + + const QRect& g = KWindowInfo(windowId, NET::WMGeometry).geometry(); + int X = g.bottomRight().x(); + int Y = g.bottomRight().y(); + QCursor::setPos(X, Y); + NETRootInfo(m_xcbConnection, NET::WMMoveResize).moveResizeRequest(windowId, X, Y, NET::BottomRight); +} + +void LXQtTaskbarX11Backend::refreshIconGeometry(WId windowId, QRect const & geom) +{ + // NOTE: This function announces where the task icon is, + // such that X11 WMs can perform their related animations correctly. + + WId appRootWindow = XDefaultRootWindow(m_X11Display); + + NETWinInfo info(m_xcbConnection, + windowId, + appRootWindow, + NET::WMIconGeometry, + NET::Properties2()); + NETRect const curr = info.iconGeometry(); + + // see kwindowsystem -> NETWinInfo::setIconGeometry for the scale factor + const qreal scaleFactor = qApp->devicePixelRatio(); + int xPos = geom.x() * scaleFactor; + int yPos = geom.y() * scaleFactor; + int w = geom.width() * scaleFactor; + int h = geom.height() * scaleFactor; + if (xPos == curr.pos.x && yPos == curr.pos.y && w == curr.size.width && h == curr.size.height) + return; + NETRect nrect; + nrect.pos.x = geom.x(); + nrect.pos.y = geom.y(); + nrect.size.height = geom.height(); + nrect.size.width = geom.width(); + info.setIconGeometry(nrect); +} diff --git a/plugin-taskbar/lxqttaskbarbackend_x11.h b/plugin-taskbar/lxqttaskbarbackend_x11.h new file mode 100644 index 000000000..26c721cf7 --- /dev/null +++ b/plugin-taskbar/lxqttaskbarbackend_x11.h @@ -0,0 +1,80 @@ +#ifndef LXQTTASKBARBACKEND_X11_H +#define LXQTTASKBARBACKEND_X11_H + +#include "ilxqttaskbarabstractbackend.h" + +//TODO: make PIMPL to forward declare NET::Properties, Display, xcb_connection_t +#include + +typedef struct _XDisplay Display; +struct xcb_connection_t; + +class LXQtTaskbarX11Backend : public ILXQtTaskbarAbstractBackend +{ + Q_OBJECT + +public: + explicit LXQtTaskbarX11Backend(QObject *parent = nullptr); + + // Backend + virtual bool supportsAction(WId windowId, LXQtTaskBarBackendAction action) const override; + + // Windows + virtual bool reloadWindows() override; + + virtual QVector getCurrentWindows() const override; + virtual QString getWindowTitle(WId windowId) const override; + virtual bool applicationDemandsAttention(WId windowId) const override; + virtual QIcon getApplicationIcon(WId windowId, int devicePixels) const override; + virtual QString getWindowClass(WId windowId) const override; + + virtual LXQtTaskBarWindowLayer getWindowLayer(WId windowId) const override; + virtual bool setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) override; + + virtual LXQtTaskBarWindowState getWindowState(WId windowId) const override; + virtual bool setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set) override; + + virtual bool isWindowActive(WId windowId) const override; + virtual bool raiseWindow(WId windowId, bool onCurrentWorkSpace) override; + + virtual bool closeWindow(WId windowId) override; + + virtual WId getActiveWindow() const override; + + // Workspaces + virtual int getWorkspacesCount() const override; + virtual QString getWorkspaceName(int idx) const override; + + virtual int getCurrentWorkspace() const override; + virtual bool setCurrentWorkspace(int idx) override; + + virtual int getWindowWorkspace(WId windowId) const override; + virtual bool setWindowOnWorkspace(WId windowId, int idx) override; + + virtual void moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) override; + + virtual bool isWindowOnScreen(QScreen *screen, WId windowId) const override; + + // X11 Specific + virtual void moveApplication(WId windowId) override; + virtual void resizeApplication(WId windowId) override; + + virtual void refreshIconGeometry(WId windowId, const QRect &geom) override; + +private slots: + void onWindowChanged(WId windowId, NET::Properties prop, NET::Properties2 prop2); + void onWindowAdded(WId windowId); + void onWindowRemoved(WId windowId); + +private: + bool acceptWindow(WId windowId) const; + void addWindow_internal(WId windowId, bool emitAdded = true); + +private: + Display *m_X11Display; + xcb_connection_t *m_xcbConnection; + + QVector m_windows; +}; + +#endif // LXQTTASKBARBACKEND_X11_H From d0fe4b40b2c370640836e739aec2dae977b466d3 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 27 Jan 2024 18:41:42 +0100 Subject: [PATCH 079/165] TaskBar: new LXQtTaskBarProxyModel This model will manage the tasks shown --- plugin-taskbar/CMakeLists.txt | 2 + plugin-taskbar/lxqttaskbarproxymodel.cpp | 257 +++++++++++++++++++++++ plugin-taskbar/lxqttaskbarproxymodel.h | 100 +++++++++ 3 files changed, 359 insertions(+) create mode 100644 plugin-taskbar/lxqttaskbarproxymodel.cpp create mode 100644 plugin-taskbar/lxqttaskbarproxymodel.h diff --git a/plugin-taskbar/CMakeLists.txt b/plugin-taskbar/CMakeLists.txt index 053381bef..be434f6a6 100644 --- a/plugin-taskbar/CMakeLists.txt +++ b/plugin-taskbar/CMakeLists.txt @@ -11,6 +11,7 @@ set(HEADERS ilxqttaskbarabstractbackend.h lxqttaskbartypes.h lxqttaskbarbackend_x11.h + lxqttaskbarproxymodel.h ) set(SOURCES @@ -23,6 +24,7 @@ set(SOURCES ilxqttaskbarabstractbackend.cpp lxqttaskbarbackend_x11.cpp + lxqttaskbarproxymodel.cpp ) set(UIS diff --git a/plugin-taskbar/lxqttaskbarproxymodel.cpp b/plugin-taskbar/lxqttaskbarproxymodel.cpp new file mode 100644 index 000000000..e2f1d3941 --- /dev/null +++ b/plugin-taskbar/lxqttaskbarproxymodel.cpp @@ -0,0 +1,257 @@ +#include "lxqttaskbarproxymodel.h" + +#include "ilxqttaskbarabstractbackend.h" + +#include + +LXQtTaskBarProxyModel::LXQtTaskBarProxyModel(QObject *parent) + : QAbstractListModel(parent) + , m_backend(nullptr) + , m_groupByWindowClass(false) +{ + +} + +int LXQtTaskBarProxyModel::rowCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : m_items.count(); +} + +QVariant LXQtTaskBarProxyModel::data(const QModelIndex &idx, int role) const +{ + if (!idx.isValid() || idx.row() >= m_items.count()) + return QVariant(); + + const LXQtTaskBarProxyModelItem& item = m_items.at(idx.row()); + + switch (role) + { + case Qt::DisplayRole: + return item.windows.count() == 1 ? item.windows.first().title : item.windowClass; + default: + break; + } + + return QVariant(); +} + +QIcon LXQtTaskBarProxyModel::getWindowIcon(int itemRow, int windowIdxInGroup, int devicePixels) const +{ + if(!m_backend || itemRow < 0 || itemRow >= m_items.size() || windowIdxInGroup < 0) + return QIcon(); + + const LXQtTaskBarProxyModelItem& item = m_items.at(itemRow); + if(windowIdxInGroup >= item.windows.size()) + return QIcon(); + + const LXQtTaskBarProxyModelWindow& window = item.windows.at(windowIdxInGroup); + return m_backend->getApplicationIcon(window.windowId, devicePixels); +} + +void LXQtTaskBarProxyModel::onWindowAdded(WId windowId) +{ + QString windowClass = m_backend->getWindowClass(windowId); + bool willAddRow = !m_groupByWindowClass || !hasWindowClass(windowClass); + + if(willAddRow) + { + const int row = m_items.count(); + beginInsertRows(QModelIndex(), row, row); + } + + addWindow_internal(windowId); + + if(willAddRow) + endInsertRows(); +} + +void LXQtTaskBarProxyModel::onWindowRemoved(WId windowId) +{ + int row = -1; + int windowIdxInGroup = -1; + for(int i = 0; i < m_items.count(); i++) + { + windowIdxInGroup = m_items.at(i).indexOfWindow(windowId); + if(windowIdxInGroup != -1) + { + row = i; + break; + } + } + + if(row == -1) + return; + + LXQtTaskBarProxyModelItem& item = m_items[row]; + item.windows.removeAt(windowIdxInGroup); + + if(item.windows.isEmpty()) + { + // Remove the group + beginRemoveRows(QModelIndex(), row, row); + m_items.removeAt(row); + endRemoveRows(); + } +} + +void LXQtTaskBarProxyModel::onWindowPropertyChanged(WId windowId, int prop) +{ + int row = -1; + int windowIdxInGroup = -1; + for(int i = 0; i < m_items.count(); i++) + { + windowIdxInGroup = m_items.at(i).indexOfWindow(windowId); + if(windowIdxInGroup != -1) + { + row = i; + break; + } + } + + if(row == -1) + return; + + LXQtTaskBarProxyModelItem& item = m_items[row]; + LXQtTaskBarProxyModelWindow& window = item.windows[windowIdxInGroup]; + + switch (LXQtTaskBarWindowProperty(prop)) + { + case LXQtTaskBarWindowProperty::Title: + window.title = m_backend->getWindowTitle(window.windowId); + break; + + case LXQtTaskBarWindowProperty::Urgency: + window.demandsAttention = m_backend->applicationDemandsAttention(window.windowId); + break; + + case LXQtTaskBarWindowProperty::WindowClass: + { + // If window class is changed, window won't be part of same group + //TODO: optimize + onWindowRemoved(windowId); + onWindowAdded(windowId); + } + + default: + break; + } + + const QModelIndex idx = index(row); + emit dataChanged(idx, idx, {Qt::DisplayRole, Qt::DecorationRole}); +} + +void LXQtTaskBarProxyModel::addWindow_internal(WId windowId) +{ + LXQtTaskBarProxyModelWindow window; + window.windowId = windowId; + window.title = m_backend->getWindowTitle(window.windowId); + window.demandsAttention = m_backend->applicationDemandsAttention(window.windowId); + + QString windowClass = m_backend->getWindowClass(window.windowId); + + int row = -1; + if(m_groupByWindowClass) + { + // Find existing group + for(int i = 0; i < m_items.count(); i++) + { + if(m_items.at(i).windowClass == windowClass) + { + row = i; + break; + } + } + } + + if(row == -1) + { + // Create new group + LXQtTaskBarProxyModelItem item; + item.windowClass = windowClass; + m_items.append(item); + row = m_items.size() - 1; + } + + // Add window to group + LXQtTaskBarProxyModelItem& item = m_items[row]; + item.windows.append(window); +} + +bool LXQtTaskBarProxyModel::groupByWindowClass() const +{ + return m_groupByWindowClass; +} + +void LXQtTaskBarProxyModel::setGroupByWindowClass(bool newGroupByWindowClass) +{ + if(m_groupByWindowClass == newGroupByWindowClass) + return; + + m_groupByWindowClass = newGroupByWindowClass; + + if(m_backend && !m_items.isEmpty()) + { + beginResetModel(); + + m_items.clear(); + + // Reload current windows + const QVector windows = m_backend->getCurrentWindows(); + m_items.reserve(windows.size()); + + for(WId windowId : windows) + onWindowAdded(windowId); + + m_items.squeeze(); + + endResetModel(); + } + +} + +ILXQtTaskbarAbstractBackend *LXQtTaskBarProxyModel::backend() const +{ + return m_backend; +} + +void LXQtTaskBarProxyModel::setBackend(ILXQtTaskbarAbstractBackend *newBackend) +{ + beginResetModel(); + + m_items.clear(); + + if(m_backend) + { + disconnect(m_backend, &ILXQtTaskbarAbstractBackend::windowAdded, + this, &LXQtTaskBarProxyModel::onWindowAdded); + disconnect(m_backend, &ILXQtTaskbarAbstractBackend::windowRemoved, + this, &LXQtTaskBarProxyModel::onWindowRemoved); + disconnect(m_backend, &ILXQtTaskbarAbstractBackend::windowPropertyChanged, + this, &LXQtTaskBarProxyModel::onWindowPropertyChanged); + } + + m_backend = newBackend; + + if(m_backend) + { + connect(m_backend, &ILXQtTaskbarAbstractBackend::windowAdded, + this, &LXQtTaskBarProxyModel::onWindowAdded); + connect(m_backend, &ILXQtTaskbarAbstractBackend::windowRemoved, + this, &LXQtTaskBarProxyModel::onWindowRemoved); + connect(m_backend, &ILXQtTaskbarAbstractBackend::windowPropertyChanged, + this, &LXQtTaskBarProxyModel::onWindowPropertyChanged); + + // Reload current windows + const QVector windows = m_backend->getCurrentWindows(); + m_items.reserve(windows.size()); + + for(WId windowId : windows) + onWindowAdded(windowId); + + m_items.squeeze(); + } + + m_items.squeeze(); + + endResetModel(); +} diff --git a/plugin-taskbar/lxqttaskbarproxymodel.h b/plugin-taskbar/lxqttaskbarproxymodel.h new file mode 100644 index 000000000..be5799076 --- /dev/null +++ b/plugin-taskbar/lxqttaskbarproxymodel.h @@ -0,0 +1,100 @@ +#ifndef LXQTTASKBARPROXYMODEL_H +#define LXQTTASKBARPROXYMODEL_H + +#include +#include + +#include "lxqttaskbartypes.h" + +class ILXQtTaskbarAbstractBackend; + +class LXQtTaskBarProxyModelWindow +{ +public: + LXQtTaskBarProxyModelWindow() = default; + + WId windowId; + QString title; + bool demandsAttention = false; +}; + +// Single window or group +class LXQtTaskBarProxyModelItem +{ +public: + LXQtTaskBarProxyModelItem() = default; + + QVector windows; + QString windowClass; + + inline bool demandsAttention() const + { + for(const LXQtTaskBarProxyModelWindow& w : windows) + { + if(w.demandsAttention) + return true; + } + + return false; + } + + int indexOfWindow(WId windowId) const + { + for(int i = 0; i < windows.size(); i++) + { + if(windows.at(i).windowId == windowId) + return i; + } + + return -1; + } +}; + +class LXQtTaskBarProxyModel : public QAbstractListModel +{ + Q_OBJECT + +public: + explicit LXQtTaskBarProxyModel(QObject *parent = nullptr); + + // Basic functionality: + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override; + + QIcon getWindowIcon(int itemRow, int windowIdxInGroup, int devicePixels) const; + + ILXQtTaskbarAbstractBackend *backend() const; + void setBackend(ILXQtTaskbarAbstractBackend *newBackend); + + bool groupByWindowClass() const; + void setGroupByWindowClass(bool newGroupByWindowClass); + +private slots: + void onWindowAdded(WId windowId); + void onWindowRemoved(WId windowId); + void onWindowPropertyChanged(WId windowId, int prop); + +private: + void addWindow_internal(WId windowId); + + inline bool hasWindowClass(const QString& windowClass) const + { + for(const LXQtTaskBarProxyModelItem& item : m_items) + { + if(item.windowClass == windowClass) + return true; + } + + return false; + } + +private: + ILXQtTaskbarAbstractBackend *m_backend; + + QVector m_items; + + bool m_groupByWindowClass; +}; + +#endif // LXQTTASKBARPROXYMODEL_H From 3a842e7f7e3bc8cc7e26d7c463cd360a66780e47 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 27 Jan 2024 19:00:46 +0100 Subject: [PATCH 080/165] TaskBar: use backend in LXQtTaskBar --- plugin-taskbar/lxqttaskbar.cpp | 14 +++++++++++--- plugin-taskbar/lxqttaskbar.h | 6 ++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/plugin-taskbar/lxqttaskbar.cpp b/plugin-taskbar/lxqttaskbar.cpp index c4fa01da2..fc60496a3 100644 --- a/plugin-taskbar/lxqttaskbar.cpp +++ b/plugin-taskbar/lxqttaskbar.cpp @@ -54,6 +54,8 @@ #include #undef Bool +#include "lxqttaskbarbackend_x11.h" + using namespace LXQt; /************************************************ @@ -80,7 +82,8 @@ LXQtTaskBar::LXQtTaskBar(ILXQtPanelPlugin *plugin, QWidget *parent) : mWheelDeltaThreshold(300), mPlugin(plugin), mPlaceHolder(new QWidget(this)), - mStyle(new LeftAlignedTextStyle()) + mStyle(new LeftAlignedTextStyle()), + mBackend(nullptr) { setStyle(mStyle); mLayout = new LXQt::GridLayout(this); @@ -94,6 +97,10 @@ LXQtTaskBar::LXQtTaskBar(ILXQtPanelPlugin *plugin, QWidget *parent) : mPlaceHolder->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); mLayout->addWidget(mPlaceHolder); + // Create model + //TODO: use backend factory + mBackend = new LXQtTaskbarX11Backend(this); + QTimer::singleShot(0, this, &LXQtTaskBar::settingsChanged); setAcceptDrops(true); @@ -282,7 +289,7 @@ void LXQtTaskBar::groupBecomeEmptySlot() void LXQtTaskBar::addWindow(WId window) { // If grouping disabled group behaves like regular button - const QString group_id = mGroupingEnabled ? QString::fromUtf8(KWindowInfo(window, NET::Properties(), NET::WM2WindowClass).windowClassClass()) : QString::number(window); + const QString group_id = mGroupingEnabled ? mBackend->getWindowClass(window) : QString::number(window); LXQtTaskGroup *group = nullptr; auto i_group = mKnownWindows.find(window); @@ -534,7 +541,8 @@ void LXQtTaskBar::settingsChanged() if (iconByClassOld != mIconByClass) emit iconByClassChanged(); - refreshTaskList(); + refreshTaskList(); //TODO: remove + mBackend->reloadWindows(); } /************************************************ diff --git a/plugin-taskbar/lxqttaskbar.h b/plugin-taskbar/lxqttaskbar.h index d80d1ea13..c36bca7e0 100644 --- a/plugin-taskbar/lxqttaskbar.h +++ b/plugin-taskbar/lxqttaskbar.h @@ -50,6 +50,8 @@ class LXQtTaskGroup; class LeftAlignedTextStyle; +class ILXQtTaskbarAbstractBackend; + namespace LXQt { class GridLayout; } @@ -87,6 +89,8 @@ class LXQtTaskBar : public QFrame ILXQtPanel * panel() const; inline ILXQtPanelPlugin * plugin() const { return mPlugin; } + inline ILXQtTaskbarAbstractBackend *getBackend() const { return mBackend; } + public slots: void settingsChanged(); @@ -156,6 +160,8 @@ private slots: ILXQtPanelPlugin *mPlugin; QWidget *mPlaceHolder; LeftAlignedTextStyle *mStyle; + + ILXQtTaskbarAbstractBackend *mBackend; }; #endif // LXQTTASKBAR_H From b81220db79dead9005dd482c2e46b95d9115158e Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 16:30:07 +0100 Subject: [PATCH 081/165] TaskBar: initial use of backend in LXQtTaskButton --- plugin-taskbar/lxqttaskbutton.cpp | 169 +++--------------------------- plugin-taskbar/lxqttaskbutton.h | 7 +- plugin-taskbar/lxqttaskgroup.cpp | 28 ++--- 3 files changed, 35 insertions(+), 169 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 2f1fe3116..3d2ad0de0 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -51,6 +51,7 @@ #include #include +//TODO: remove #include // Necessary for closeApplication() @@ -61,6 +62,8 @@ #undef Bool +#include "ilxqttaskbarabstractbackend.h" + bool LXQtTaskButton::sDraggging = false; /************************************************ @@ -84,6 +87,7 @@ void LeftAlignedTextStyle::drawItemText(QPainter * painter, const QRect & rect, ************************************************/ LXQtTaskButton::LXQtTaskButton(const WId window, LXQtTaskBar * taskbar, QWidget *parent) : QToolButton(parent), + mBackend(taskbar->getBackend()), mWindow(window), mUrgencyHint(false), mOrigin(Qt::TopLeftCorner), @@ -144,8 +148,7 @@ LXQtTaskButton::~LXQtTaskButton() = default; ************************************************/ void LXQtTaskButton::updateText() { - KWindowInfo info(mWindow, NET::WMVisibleName | NET::WMName); - QString title = info.visibleName().isEmpty() ? info.name() : info.visibleName(); + QString title = mBackend->getWindowTitle(mWindow); setText(title.replace(QStringLiteral("&"), QStringLiteral("&&"))); setToolTip(title); } @@ -168,57 +171,6 @@ void LXQtTaskButton::updateIcon() setIcon(ico.isNull() ? XdgIcon::defaultApplicationIcon() : ico); } -/************************************************ - - ************************************************/ -void LXQtTaskButton::refreshIconGeometry(QRect const & geom) -{ - // NOTE: This function announces where the task icon is, - // such that X11 WMs can perform their related animations correctly. - - WId appRootWindow = 0; - xcb_connection_t* x11conn = nullptr; - - auto *x11Application = qGuiApp->nativeInterface(); - if(x11Application) - { - appRootWindow = XDefaultRootWindow(x11Application->display()); - x11conn = x11Application->connection(); - } - else - { - //qWarning() << "LXQtTaskBar: not implemented on Wayland"; - return; - } - - - if (!x11conn) { - return; - } - - NETWinInfo info(x11conn, - windowId(), - appRootWindow, - NET::WMIconGeometry, - NET::Properties2()); - NETRect const curr = info.iconGeometry(); - - // see kwindowsystem -> NETWinInfo::setIconGeometry for the scale factor - const qreal scaleFactor = qApp->devicePixelRatio(); - int xPos = geom.x() * scaleFactor; - int yPos = geom.y() * scaleFactor; - int w = geom.width() * scaleFactor; - int h = geom.height() * scaleFactor; - if (xPos == curr.pos.x && yPos == curr.pos.y && w == curr.size.width && h == curr.size.height) - return; - NETRect nrect; - nrect.pos.x = geom.x(); - nrect.pos.y = geom.y(); - nrect.size.height = geom.height(); - nrect.size.width = geom.width(); - info.setIconGeometry(nrect); -} - /************************************************ ************************************************/ @@ -434,8 +386,7 @@ void LXQtTaskButton::mouseMoveEvent(QMouseEvent* event) ************************************************/ bool LXQtTaskButton::isApplicationHidden() const { - KWindowInfo info(mWindow, NET::WMState); - return (info.state() & NET::Hidden); + return false; //FIXME: unused } /************************************************ @@ -443,7 +394,7 @@ bool LXQtTaskButton::isApplicationHidden() const ************************************************/ bool LXQtTaskButton::isApplicationActive() const { - return KX11Extras::activeWindow() == mWindow; + return mBackend->isWindowActive(mWindow); } /************************************************ @@ -588,12 +539,12 @@ void LXQtTaskButton::moveApplicationToDesktop() return; bool ok; - int desk = act->data().toInt(&ok); + int idx = act->data().toInt(&ok); if (!ok) return; - KX11Extras::setOnDesktop(mWindow, desk); + mBackend->setWindowOnWorkspace(mWindow, idx); } /************************************************ @@ -601,17 +552,7 @@ void LXQtTaskButton::moveApplicationToDesktop() ************************************************/ void LXQtTaskButton::moveApplicationToPrevNextDesktop(bool next) { - int deskNum = KX11Extras::numberOfDesktops(); - if (deskNum <= 1) - return; - int targetDesk = KWindowInfo(mWindow, NET::WMDesktop).desktop() + (next ? 1 : -1); - // wrap around - if (targetDesk > deskNum) - targetDesk = 1; - else if (targetDesk < 1) - targetDesk = deskNum; - - KX11Extras::setOnDesktop(mWindow, targetDesk); + mBackend->moveApplicationToPrevNextDesktop(mWindow, next); } /************************************************ @@ -619,53 +560,7 @@ void LXQtTaskButton::moveApplicationToPrevNextDesktop(bool next) ************************************************/ void LXQtTaskButton::moveApplicationToPrevNextMonitor(bool next) { - KWindowInfo info(mWindow, NET::WMDesktop); - if (!info.isOnCurrentDesktop()) - KX11Extras::setCurrentDesktop(info.desktop()); - if (isMinimized()) - KX11Extras::unminimizeWindow(mWindow); - KX11Extras::forceActiveWindow(mWindow); - const QRect& windowGeometry = KWindowInfo(mWindow, NET::WMFrameExtents).frameGeometry(); - QList screens = QGuiApplication::screens(); - if (screens.size() > 1){ - for (int i = 0; i < screens.size(); ++i) - { - QRect screenGeometry = screens[i]->geometry(); - if (screenGeometry.intersects(windowGeometry)) - { - int targetScreen = i + (next ? 1 : -1); - if (targetScreen < 0) - targetScreen += screens.size(); - else if (targetScreen >= screens.size()) - targetScreen -= screens.size(); - QRect targetScreenGeometry = screens[targetScreen]->geometry(); - int X = windowGeometry.x() - screenGeometry.x() + targetScreenGeometry.x(); - int Y = windowGeometry.y() - screenGeometry.y() + targetScreenGeometry.y(); - NET::States state = KWindowInfo(mWindow, NET::WMState).state(); - // NW geometry | y/x | from panel - const int flags = 1 | (0b011 << 8) | (0b010 << 12); - KX11Extras::clearState(mWindow, NET::MaxHoriz | NET::MaxVert | NET::Max | NET::FullScreen); - - - auto *x11Application = qGuiApp->nativeInterface(); - if(x11Application) - { - NETRootInfo(x11Application->connection(), NET::Properties(), NET::WM2MoveResizeWindow).moveResizeWindowRequest(mWindow, flags, X, Y, 0, 0); - } - else - { - //qWarning() << "LXQtTaskBar: not implemented on Wayland"; - } - - QTimer::singleShot(200, this, [this, state] - { - KX11Extras::setState(mWindow, state); - raiseApplication(); - }); - break; - } - } - } + mBackend->moveApplicationToPrevNextMonitor(mWindow, next, parentTaskBar()->raiseOnCurrentDesktop()); } /************************************************ @@ -673,26 +568,7 @@ void LXQtTaskButton::moveApplicationToPrevNextMonitor(bool next) ************************************************/ void LXQtTaskButton::moveApplication() { - KWindowInfo info(mWindow, NET::WMDesktop); - if (!info.isOnCurrentDesktop()) - KX11Extras::setCurrentDesktop(info.desktop()); - if (isMinimized()) - KX11Extras::unminimizeWindow(mWindow); - KX11Extras::forceActiveWindow(mWindow); - const QRect& g = KWindowInfo(mWindow, NET::WMGeometry).geometry(); - int X = g.center().x(); - int Y = g.center().y(); - QCursor::setPos(X, Y); - - auto *x11Application = qGuiApp->nativeInterface(); - if(x11Application) - { - NETRootInfo(x11Application->connection(), NET::WMMoveResize).moveResizeRequest(mWindow, X, Y, NET::Move); - } - else - { - //qWarning() << "LXQtTaskBar: not implemented on Wayland"; - } + mBackend->moveApplication(mWindow); } /************************************************ @@ -700,26 +576,7 @@ void LXQtTaskButton::moveApplication() ************************************************/ void LXQtTaskButton::resizeApplication() { - KWindowInfo info(mWindow, NET::WMDesktop); - if (!info.isOnCurrentDesktop()) - KX11Extras::setCurrentDesktop(info.desktop()); - if (isMinimized()) - KX11Extras::unminimizeWindow(mWindow); - KX11Extras::forceActiveWindow(mWindow); - const QRect& g = KWindowInfo(mWindow, NET::WMGeometry).geometry(); - int X = g.bottomRight().x(); - int Y = g.bottomRight().y(); - QCursor::setPos(X, Y); - - auto *x11Application = qGuiApp->nativeInterface(); - if(x11Application) - { - NETRootInfo(x11Application->connection(), NET::WMMoveResize).moveResizeRequest(mWindow, X, Y, NET::BottomRight); - } - else - { - //qWarning() << "LXQtTaskBar: not implemented on Wayland"; - } + mBackend->resizeApplication(mWindow); } /************************************************ diff --git a/plugin-taskbar/lxqttaskbutton.h b/plugin-taskbar/lxqttaskbutton.h index 73d0886d4..9ccca36fa 100644 --- a/plugin-taskbar/lxqttaskbutton.h +++ b/plugin-taskbar/lxqttaskbutton.h @@ -41,6 +41,8 @@ class QPalette; class QMimeData; class LXQtTaskBar; +class ILXQtTaskbarAbstractBackend; + class LeftAlignedTextStyle : public QProxyStyle { using QProxyStyle::QProxyStyle; @@ -79,7 +81,6 @@ class LXQtTaskButton : public QToolButton LXQtTaskBar * parentTaskBar() const {return mParentTaskBar;} - void refreshIconGeometry(QRect const & geom); static QString mimeDataFormat() { return QLatin1String("lxqt/lxqttaskbutton"); } /*! \return true if this button received DragEnter event (and no DragLeave event yet) * */ @@ -121,6 +122,10 @@ public slots: inline ILXQtPanelPlugin * plugin() const { return mPlugin; } +protected: + //TODO: public getter instead? + ILXQtTaskbarAbstractBackend *mBackend; + private: void moveApplicationToPrevNextDesktop(bool next); void moveApplicationToPrevNextMonitor(bool next); diff --git a/plugin-taskbar/lxqttaskgroup.cpp b/plugin-taskbar/lxqttaskgroup.cpp index a8aaa3f2a..0f3a19df8 100644 --- a/plugin-taskbar/lxqttaskgroup.cpp +++ b/plugin-taskbar/lxqttaskgroup.cpp @@ -43,9 +43,13 @@ #include #include +//TODO: remove #include //For nativeInterface() #include #include +#undef Bool + +#include "ilxqttaskbarabstractbackend.h" /************************************************ @@ -62,14 +66,14 @@ LXQtTaskGroup::LXQtTaskGroup(const QString &groupName, WId window, LXQtTaskBar * setObjectName(groupName); setText(groupName); - connect(this, &LXQtTaskGroup::clicked, this, &LXQtTaskGroup::onClicked); - connect(KX11Extras::self(), &KX11Extras::currentDesktopChanged, this, &LXQtTaskGroup::onDesktopChanged); - connect(KX11Extras::self(), &KX11Extras::activeWindowChanged, this, &LXQtTaskGroup::onActiveWindowChanged); - connect(parent, &LXQtTaskBar::buttonRotationRefreshed, this, &LXQtTaskGroup::setAutoRotation); - connect(parent, &LXQtTaskBar::refreshIconGeometry, this, &LXQtTaskGroup::refreshIconsGeometry); - connect(parent, &LXQtTaskBar::buttonStyleRefreshed, this, &LXQtTaskGroup::setToolButtonsStyle); - connect(parent, &LXQtTaskBar::showOnlySettingChanged, this, &LXQtTaskGroup::refreshVisibility); - connect(parent, &LXQtTaskBar::popupShown, this, &LXQtTaskGroup::groupPopupShown); + connect(this, &LXQtTaskGroup::clicked, this, &LXQtTaskGroup::onClicked); + connect(parent, &LXQtTaskBar::buttonRotationRefreshed, this, &LXQtTaskGroup::setAutoRotation); + connect(parent, &LXQtTaskBar::refreshIconGeometry, this, &LXQtTaskGroup::refreshIconsGeometry); + connect(parent, &LXQtTaskBar::buttonStyleRefreshed, this, &LXQtTaskGroup::setToolButtonsStyle); + connect(parent, &LXQtTaskBar::showOnlySettingChanged, this, &LXQtTaskGroup::refreshVisibility); + connect(parent, &LXQtTaskBar::popupShown, this, &LXQtTaskGroup::groupPopupShown); + connect(mBackend, &ILXQtTaskbarAbstractBackend::currentWorkspaceChanged, this, &LXQtTaskGroup::onDesktopChanged); + connect(mBackend, &ILXQtTaskbarAbstractBackend::activeWindowChanged, this, &LXQtTaskGroup::onActiveWindowChanged); } /************************************************ @@ -102,7 +106,7 @@ void LXQtTaskGroup::contextMenuEvent(QContextMenuEvent *event) void LXQtTaskGroup::closeGroup() { for (LXQtTaskButton *button : std::as_const(mButtonHash) ) - if (button->isOnDesktop(KX11Extras::currentDesktop())) + if (button->isOnDesktop(mBackend->getCurrentWorkspace())) button->closeApplication(); } @@ -310,7 +314,7 @@ void LXQtTaskGroup::onClicked(bool) { if (visibleButtonsCount() > 1) { - setChecked(mButtonHash.contains(KX11Extras::activeWindow())); + setChecked(mButtonHash.contains(mBackend->getActiveWindow())); setPopupVisible(true); } } @@ -449,13 +453,13 @@ void LXQtTaskGroup::refreshIconsGeometry() if (mSingleButton) { - refreshIconGeometry(rect); + mBackend->refreshIconGeometry(windowId(), rect); return; } for(LXQtTaskButton *but : std::as_const(mButtonHash)) { - but->refreshIconGeometry(rect); + mBackend->refreshIconGeometry(but->windowId(), rect); but->setIconSize(QSize(plugin()->panel()->iconSize(), plugin()->panel()->iconSize())); } } From 9cd7198bae8a9abf4844d0bef8c788a5279f85ad Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 16:34:11 +0100 Subject: [PATCH 082/165] TaskBar: use backend for urgency hint --- plugin-taskbar/lxqttaskbutton.cpp | 14 +------------- plugin-taskbar/lxqttaskgroup.cpp | 23 +++++------------------ 2 files changed, 6 insertions(+), 31 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 3d2ad0de0..7febd5638 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -28,7 +28,6 @@ * END_COMMON_COPYRIGHT_HEADER */ #include "lxqttaskbutton.h" -#include "lxqttaskgroup.h" #include "lxqttaskbar.h" #include "ilxqtpanelplugin.h" @@ -121,18 +120,7 @@ LXQtTaskButton::LXQtTaskButton(const WId window, LXQtTaskBar * taskbar, QWidget mWheelDelta = 0; // forget previous wheel deltas }); - auto *x11Application = qGuiApp->nativeInterface(); - if(x11Application) - { - WId appRootWindow = XDefaultRootWindow(x11Application->display()); - setUrgencyHint(NETWinInfo(x11Application->connection(), mWindow, appRootWindow, NET::Properties{}, NET::WM2Urgency).urgency() - || KWindowInfo{mWindow, NET::WMState}.hasState(NET::DemandsAttention)); - } - else - { - qWarning() << "LXQtTaskBar: not implemented on Wayland"; - } - + setUrgencyHint(mBackend->applicationDemandsAttention(mWindow)); connect(LXQt::Settings::globalSettings(), &LXQt::GlobalSettings::iconThemeChanged, this, &LXQtTaskButton::updateIcon); connect(mParentTaskBar, &LXQtTaskBar::iconByClassChanged, this, &LXQtTaskButton::updateIcon); diff --git a/plugin-taskbar/lxqttaskgroup.cpp b/plugin-taskbar/lxqttaskgroup.cpp index 0f3a19df8..e4991c8a4 100644 --- a/plugin-taskbar/lxqttaskgroup.cpp +++ b/plugin-taskbar/lxqttaskgroup.cpp @@ -45,7 +45,6 @@ //TODO: remove #include //For nativeInterface() -#include #include #undef Bool @@ -670,30 +669,18 @@ bool LXQtTaskGroup::onWindowChanged(WId window, NET::Properties prop, NET::Prope if (prop2.testFlag(NET::WM2Urgency)) { set_urgency = true; - if (auto *x11Application = qGuiApp->nativeInterface()) - { - WId appRootWindow = XDefaultRootWindow(x11Application->display()); - urgency = NETWinInfo(x11Application->connection(), window, appRootWindow, NET::Properties{}, NET::WM2Urgency).urgency(); - } + //FIXME: original code here did not consider "demand attention", was it intentional? + urgency = mBackend->applicationDemandsAttention(window); } if (prop.testFlag(NET::WMState)) { KWindowInfo info{window, NET::WMState}; - if(!set_urgency) - { - if (auto *x11Application = qGuiApp->nativeInterface()) - { - WId appRootWindow = XDefaultRootWindow(x11Application->display()); - urgency = NETWinInfo(x11Application->connection(), window, appRootWindow, NET::Properties{}, NET::WM2Urgency).urgency(); - } - } - // Force refresh urgency - //TODO: maybe do it in common place with NET::WM2Urgency - std::for_each(buttons.begin(), buttons.end(), std::bind(&LXQtTaskButton::setUrgencyHint, std::placeholders::_1, urgency || info.hasState(NET::DemandsAttention))); + if (!set_urgency) + urgency = mBackend->applicationDemandsAttention(window); + std::for_each(buttons.begin(), buttons.end(), std::bind(&LXQtTaskButton::setUrgencyHint, std::placeholders::_1, urgency)); set_urgency = false; - if (info.hasState(NET::SkipTaskbar)) onWindowRemoved(window); From 354e027f21eb37d876dfbebebb8eb7e0685cb7d4 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 16:36:29 +0100 Subject: [PATCH 083/165] TaskBar: use backend to set window layer --- plugin-taskbar/lxqttaskbutton.cpp | 37 +++++++++---------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 7febd5638..d324009ee 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -498,23 +498,7 @@ void LXQtTaskButton::setApplicationLayer() return; int layer = act->data().toInt(); - switch(layer) - { - case NET::KeepAbove: - KX11Extras::clearState(mWindow, NET::KeepBelow); - KX11Extras::setState(mWindow, NET::KeepAbove); - break; - - case NET::KeepBelow: - KX11Extras::clearState(mWindow, NET::KeepAbove); - KX11Extras::setState(mWindow, NET::KeepBelow); - break; - - default: - KX11Extras::clearState(mWindow, NET::KeepBelow); - KX11Extras::clearState(mWindow, NET::KeepAbove); - break; - } + mBackend->setWindowLayer(mWindow, LXQtTaskBarWindowLayer(layer)); } /************************************************ @@ -707,22 +691,23 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) QMenu* layerMenu = menu->addMenu(tr("&Layer")); - a = layerMenu->addAction(tr("Always on &top")); + LXQtTaskBarWindowLayer currentLayer = mBackend->getWindowLayer(mWindow); + // FIXME: There is no info.actionSupported(NET::ActionKeepAbove) - a->setEnabled(!(state & NET::KeepAbove)); - a->setData(NET::KeepAbove); + a = layerMenu->addAction(tr("Always on &top")); + a->setEnabled(currentLayer != LXQtTaskBarWindowLayer::KeepAbove); + a->setData(int(LXQtTaskBarWindowLayer::KeepAbove)); connect(a, &QAction::triggered, this, &LXQtTaskButton::setApplicationLayer); a = layerMenu->addAction(tr("&Normal")); - a->setEnabled((state & NET::KeepAbove) || (state & NET::KeepBelow)); - // FIXME: There is no NET::KeepNormal, so passing 0 - a->setData(0); + a->setEnabled(currentLayer != LXQtTaskBarWindowLayer::Normal); + a->setData(int(LXQtTaskBarWindowLayer::Normal)); connect(a, &QAction::triggered, this, &LXQtTaskButton::setApplicationLayer); - a = layerMenu->addAction(tr("Always on &bottom")); // FIXME: There is no info.actionSupported(NET::ActionKeepBelow) - a->setEnabled(!(state & NET::KeepBelow)); - a->setData(NET::KeepBelow); + a = layerMenu->addAction(tr("Always on &bottom")); + a->setEnabled(currentLayer != LXQtTaskBarWindowLayer::KeepBelow); + a->setData(int(LXQtTaskBarWindowLayer::KeepBelow)); connect(a, &QAction::triggered, this, &LXQtTaskButton::setApplicationLayer); /********** Kill menu **********/ From 290178eed8905b7eb1b9c0995f4dcf25c28a0507 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 16:53:02 +0100 Subject: [PATCH 084/165] TaskBar: use backend to close and raise window Also use it to get window icon --- plugin-taskbar/lxqttaskbutton.cpp | 29 +++-------------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index d324009ee..e59219d4c 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -154,7 +154,7 @@ void LXQtTaskButton::updateIcon() if (ico.isNull()) { int devicePixels = mIconSize * devicePixelRatioF(); - ico = KX11Extras::icon(mWindow, devicePixels, devicePixels); + ico = mBackend->getApplicationIcon(mWindow, devicePixels); } setIcon(ico.isNull() ? XdgIcon::defaultApplicationIcon() : ico); } @@ -390,21 +390,7 @@ bool LXQtTaskButton::isApplicationActive() const ************************************************/ void LXQtTaskButton::raiseApplication() { - KWindowInfo info(mWindow, NET::WMDesktop | NET::WMState | NET::XAWMState); - if (parentTaskBar()->raiseOnCurrentDesktop() && info.isMinimized()) - { - KX11Extras::setOnDesktop(mWindow, KX11Extras::currentDesktop()); - } - else - { - int winDesktop = info.desktop(); - if (KX11Extras::currentDesktop() != winDesktop) - KX11Extras::setCurrentDesktop(winDesktop); - } - // bypass focus stealing prevention - KX11Extras::forceActiveWindow(mWindow); - - setUrgencyHint(false); + mBackend->raiseWindow(mWindow, parentTaskBar()->raiseOnCurrentDesktop()); } /************************************************ @@ -476,16 +462,7 @@ void LXQtTaskButton::unShadeApplication() ************************************************/ void LXQtTaskButton::closeApplication() { - auto *x11Application = qGuiApp->nativeInterface(); - if(x11Application) - { - // FIXME: Why there is no such thing in KX11Extras?? - NETRootInfo(x11Application->connection(), NET::CloseWindow).closeWindowRequest(mWindow); - } - else - { - qWarning() << "LXQtTaskBar: not implemented on Wayland"; - } + mBackend->closeWindow(mWindow); } /************************************************ From 78b5f7e0c65e3c3c27b99f948e9c9ed05f8d7f15 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 17:00:58 +0100 Subject: [PATCH 085/165] TaskBar: use backend to roll up (shade) windows --- plugin-taskbar/lxqttaskbutton.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index e59219d4c..3a63999ba 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -446,7 +446,7 @@ void LXQtTaskButton::deMaximizeApplication() ************************************************/ void LXQtTaskButton::shadeApplication() { - KX11Extras::setState(mWindow, NET::Shaded); + mBackend->setWindowState(mWindow, LXQtTaskBarWindowState::RolledUp, true); } /************************************************ @@ -454,7 +454,7 @@ void LXQtTaskButton::shadeApplication() ************************************************/ void LXQtTaskButton::unShadeApplication() { - KX11Extras::clearState(mWindow, NET::Shaded); + mBackend->setWindowState(mWindow, LXQtTaskBarWindowState::RolledUp, false); } /************************************************ From 74bcae45dba4fed2c419a4bb71b766815e2bed8d Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 17:09:37 +0100 Subject: [PATCH 086/165] TaskBar: use backend to minimize window --- plugin-taskbar/lxqttaskbutton.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 3a63999ba..a77729bc4 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -398,7 +398,7 @@ void LXQtTaskButton::raiseApplication() ************************************************/ void LXQtTaskButton::minimizeApplication() { - KX11Extras::minimizeWindow(mWindow); + mBackend->setWindowState(mWindow, LXQtTaskBarWindowState::Minimized, true); } /************************************************ From a14a0fe553f329ff96e4951901a6f76a894d4811 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 17:10:08 +0100 Subject: [PATCH 087/165] TaskBar: use backend to de-maximize window --- plugin-taskbar/lxqttaskbutton.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index a77729bc4..27d430926 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -435,10 +435,10 @@ void LXQtTaskButton::maximizeApplication() ************************************************/ void LXQtTaskButton::deMaximizeApplication() { - KX11Extras::clearState(mWindow, NET::Max); + mBackend->setWindowState(mWindow, LXQtTaskBarWindowState::Maximized, false); - if (!isApplicationActive()) - raiseApplication(); + if(!mBackend->isWindowActive(mWindow)) + mBackend->raiseWindow(mWindow, parentTaskBar()->raiseOnCurrentDesktop()); } /************************************************ From d0d674e4b83a32ca1f5f39d92b574078728782c1 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 17:58:59 +0100 Subject: [PATCH 088/165] TaskBar: use backend to get window state --- plugin-taskbar/lxqttaskbutton.cpp | 35 +++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 27d430926..6f9c1d3d2 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -540,7 +540,7 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) } KWindowInfo info(mWindow, NET::Properties(), NET::WM2AllowedActions); - unsigned long state = KWindowInfo(mWindow, NET::WMState).state(); + NET::States state = KWindowInfo(mWindow, NET::WMState).state(); QMenu * menu = new QMenu(tr("Application")); menu->setAttribute(Qt::WA_DeleteOnClose); @@ -624,42 +624,59 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) /********** State menu **********/ menu->addSeparator(); + LXQtTaskBarWindowState windowState = mBackend->getWindowState(mWindow); + a = menu->addAction(tr("Ma&ximize")); - a->setEnabled(info.actionSupported(NET::ActionMax) && (!(state & NET::Max) || (state & NET::Hidden))); + a->setEnabled(info.actionSupported(NET::ActionMax) + && windowState != LXQtTaskBarWindowState::Maximized + && windowState != LXQtTaskBarWindowState::Hidden); a->setData(NET::Max); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); if (event->modifiers() & Qt::ShiftModifier) { a = menu->addAction(tr("Maximize vertically")); - a->setEnabled(info.actionSupported(NET::ActionMaxVert) && !((state & NET::MaxVert) || (state & NET::Hidden))); + a->setEnabled(info.actionSupported(NET::ActionMaxVert) + && windowState != LXQtTaskBarWindowState::MaximizedVertically + && windowState != LXQtTaskBarWindowState::Hidden); a->setData(NET::MaxVert); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); a = menu->addAction(tr("Maximize horizontally")); - a->setEnabled(info.actionSupported(NET::ActionMaxHoriz) && !((state & NET::MaxHoriz) || (state & NET::Hidden))); + a->setEnabled(info.actionSupported(NET::ActionMaxHoriz) + && windowState != LXQtTaskBarWindowState::MaximizedHorizontally + && windowState != LXQtTaskBarWindowState::Hidden); a->setData(NET::MaxHoriz); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); } a = menu->addAction(tr("&Restore")); - a->setEnabled((state & NET::Hidden) || (state & NET::Max) || (state & NET::MaxHoriz) || (state & NET::MaxVert)); + a->setEnabled(windowState == LXQtTaskBarWindowState::Hidden + || windowState == LXQtTaskBarWindowState::Minimized + || windowState == LXQtTaskBarWindowState::Maximized + || windowState == LXQtTaskBarWindowState::MaximizedVertically + || windowState == LXQtTaskBarWindowState::MaximizedHorizontally); connect(a, &QAction::triggered, this, &LXQtTaskButton::deMaximizeApplication); a = menu->addAction(tr("Mi&nimize")); - a->setEnabled(info.actionSupported(NET::ActionMinimize) && !(state & NET::Hidden)); + a->setEnabled(info.actionSupported(NET::ActionMinimize) + && windowState != LXQtTaskBarWindowState::Hidden + && windowState != LXQtTaskBarWindowState::Minimized); connect(a, &QAction::triggered, this, &LXQtTaskButton::minimizeApplication); - if (state & NET::Shaded) + if (windowState == LXQtTaskBarWindowState::RolledUp) { a = menu->addAction(tr("Roll down")); - a->setEnabled(info.actionSupported(NET::ActionShade) && !(state & NET::Hidden)); + a->setEnabled(info.actionSupported(NET::ActionShade) + && windowState != LXQtTaskBarWindowState::Hidden + && windowState != LXQtTaskBarWindowState::Minimized); connect(a, &QAction::triggered, this, &LXQtTaskButton::unShadeApplication); } else { a = menu->addAction(tr("Roll up")); - a->setEnabled(info.actionSupported(NET::ActionShade) && !(state & NET::Hidden)); + a->setEnabled(info.actionSupported(NET::ActionShade) + && windowState != LXQtTaskBarWindowState::Hidden); connect(a, &QAction::triggered, this, &LXQtTaskButton::shadeApplication); } From b4af0dfd683219b328942431e7574fe196136a4c Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 18:26:53 +0100 Subject: [PATCH 089/165] TaskBar: use backend to set window state --- plugin-taskbar/lxqttaskbutton.cpp | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 6f9c1d3d2..b63b2aa3f 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -411,23 +411,10 @@ void LXQtTaskButton::maximizeApplication() return; int state = act->data().toInt(); - switch (state) - { - case NET::MaxHoriz: - KX11Extras::setState(mWindow, NET::MaxHoriz); - break; + mBackend->setWindowState(mWindow, LXQtTaskBarWindowState(state), true); - case NET::MaxVert: - KX11Extras::setState(mWindow, NET::MaxVert); - break; - - default: - KX11Extras::setState(mWindow, NET::Max); - break; - } - - if (!isApplicationActive()) - raiseApplication(); + if(!mBackend->isWindowActive(mWindow)) + mBackend->raiseWindow(mWindow, parentTaskBar()->raiseOnCurrentDesktop()); } /************************************************ @@ -630,7 +617,7 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) a->setEnabled(info.actionSupported(NET::ActionMax) && windowState != LXQtTaskBarWindowState::Maximized && windowState != LXQtTaskBarWindowState::Hidden); - a->setData(NET::Max); + a->setData(int(LXQtTaskBarWindowState::Maximized)); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); if (event->modifiers() & Qt::ShiftModifier) @@ -639,14 +626,14 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) a->setEnabled(info.actionSupported(NET::ActionMaxVert) && windowState != LXQtTaskBarWindowState::MaximizedVertically && windowState != LXQtTaskBarWindowState::Hidden); - a->setData(NET::MaxVert); + a->setData(int(LXQtTaskBarWindowState::MaximizedVertically)); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); a = menu->addAction(tr("Maximize horizontally")); a->setEnabled(info.actionSupported(NET::ActionMaxHoriz) && windowState != LXQtTaskBarWindowState::MaximizedHorizontally && windowState != LXQtTaskBarWindowState::Hidden); - a->setData(NET::MaxHoriz); + a->setData(int(LXQtTaskBarWindowState::MaximizedHorizontally)); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); } @@ -747,7 +734,7 @@ bool LXQtTaskButton::isOnCurrentScreen() const bool LXQtTaskButton::isMinimized() const { - return KWindowInfo(mWindow,NET::WMState | NET::XAWMState).isMinimized(); + return mBackend->getWindowState(mWindow) == LXQtTaskBarWindowState::Minimized; } Qt::Corner LXQtTaskButton::origin() const From 6a28f7a88d71301cf36a11bab1b57a3e672923c8 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sun, 28 Jan 2024 13:27:46 +0100 Subject: [PATCH 090/165] TaskBar: port workspace usage to backend --- plugin-taskbar/lxqttaskbutton.cpp | 19 ++++++++----------- plugin-taskbar/lxqttaskgroup.cpp | 4 ++-- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index b63b2aa3f..ec5fca2d4 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -559,21 +559,21 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) */ /********** Desktop menu **********/ - int deskNum = KX11Extras::numberOfDesktops(); + int deskNum = mBackend->getWorkspacesCount(); if (deskNum > 1) { - int winDesk = KWindowInfo(mWindow, NET::WMDesktop).desktop(); + int winDesk = mBackend->getWindowWorkspace(mWindow); QMenu* deskMenu = menu->addMenu(tr("To &Desktop")); a = deskMenu->addAction(tr("&All Desktops")); - a->setData(NET::OnAllDesktops); - a->setEnabled(winDesk != NET::OnAllDesktops); + a->setData(int(LXQtTaskBarWorkspace::ShowOnAll)); + a->setEnabled(winDesk != int(LXQtTaskBarWorkspace::ShowOnAll)); connect(a, &QAction::triggered, this, &LXQtTaskButton::moveApplicationToDesktop); deskMenu->addSeparator(); for (int i = 1; i <= deskNum; ++i) { - auto deskName = KX11Extras::desktopName(i).trimmed(); + auto deskName = mBackend->getWorkspaceName(i).trimmed(); if (deskName.isEmpty()) a = deskMenu->addAction(tr("Desktop &%1").arg(i)); else @@ -584,7 +584,7 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) connect(a, &QAction::triggered, this, &LXQtTaskButton::moveApplicationToDesktop); } - int curDesk = KX11Extras::currentDesktop(); + int curDesk = mBackend->getCurrentWorkspace(); a = menu->addAction(tr("&To Current Desktop")); a->setData(curDesk); a->setEnabled(curDesk != winDesk); @@ -720,16 +720,13 @@ void LXQtTaskButton::setUrgencyHint(bool set) ************************************************/ bool LXQtTaskButton::isOnDesktop(int desktop) const { - return KWindowInfo(mWindow, NET::WMDesktop).isOnDesktop(desktop); + return mBackend->getWindowWorkspace(mWindow) == desktop; } bool LXQtTaskButton::isOnCurrentScreen() const { QScreen *screen = parentTaskBar()->screen(); - QRect screenGeo = screen->geometry(); - QRect windowGeo = KWindowInfo(mWindow, NET::WMFrameExtents).frameGeometry(); - - return screenGeo.intersects(windowGeo); + return mBackend->isWindowOnScreen(screen, mWindow); } bool LXQtTaskButton::isMinimized() const diff --git a/plugin-taskbar/lxqttaskgroup.cpp b/plugin-taskbar/lxqttaskgroup.cpp index e4991c8a4..655e99638 100644 --- a/plugin-taskbar/lxqttaskgroup.cpp +++ b/plugin-taskbar/lxqttaskgroup.cpp @@ -392,7 +392,7 @@ void LXQtTaskGroup::refreshVisibility() const int showDesktop = taskbar->showDesktopNum(); for(LXQtTaskButton * btn : std::as_const(mButtonHash)) { - bool visible = taskbar->isShowOnlyOneDesktopTasks() ? btn->isOnDesktop(0 == showDesktop ? KX11Extras::currentDesktop() : showDesktop) : true; + bool visible = taskbar->isShowOnlyOneDesktopTasks() ? btn->isOnDesktop(0 == showDesktop ? mBackend->getCurrentWorkspace() : showDesktop) : true; visible &= taskbar->isShowOnlyCurrentScreenTasks() ? btn->isOnCurrentScreen() : true; visible &= taskbar->isShowOnlyMinimizedTasks() ? btn->isMinimized() : true; btn->setVisible(visible); @@ -649,7 +649,7 @@ bool LXQtTaskGroup::onWindowChanged(WId window, NET::Properties prop, NET::Prope if (prop.testFlag(NET::WMDesktop) || prop.testFlag(NET::WMGeometry)) { if (parentTaskBar()->isShowOnlyOneDesktopTasks() - || parentTaskBar()->isShowOnlyCurrentScreenTasks()) + || parentTaskBar()->isShowOnlyCurrentScreenTasks()) { needsRefreshVisibility = true; } From 7fad2a1819a3b6a67a060e02f5bc78b36d5cecb6 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 3 Feb 2024 18:33:09 +0100 Subject: [PATCH 091/165] TaskBar: remove X11 specific includes in lxqttaskbutton.cpp --- plugin-taskbar/lxqttaskbutton.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index ec5fca2d4..2126dca76 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -50,18 +50,9 @@ #include #include -//TODO: remove -#include - -// Necessary for closeApplication() -#include - -//NOTE: Xlib.h defines Bool which conflicts with QJsonValue::Type enum -#include -#undef Bool +#include "ilxqttaskbarabstractbackend.h" -#include "ilxqttaskbarabstractbackend.h" bool LXQtTaskButton::sDraggging = false; From d5a4751721cd22f491b48d54ae2692e22bd80be0 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sun, 28 Jan 2024 15:59:25 +0100 Subject: [PATCH 092/165] TaskBar: remove X11 code from LXQtTaskBar and LXQtTaskGroup --- plugin-taskbar/lxqttaskbar.cpp | 99 ++++---------------------------- plugin-taskbar/lxqttaskbar.h | 9 +-- plugin-taskbar/lxqttaskgroup.cpp | 32 ++++------- plugin-taskbar/lxqttaskgroup.h | 5 +- 4 files changed, 28 insertions(+), 117 deletions(-) diff --git a/plugin-taskbar/lxqttaskbar.cpp b/plugin-taskbar/lxqttaskbar.cpp index fc60496a3..1bf7781f7 100644 --- a/plugin-taskbar/lxqttaskbar.cpp +++ b/plugin-taskbar/lxqttaskbar.cpp @@ -50,10 +50,6 @@ #include "lxqttaskgroup.h" #include "../panel/pluginsettings.h" -//NOTE: Xlib.h defines Bool which conflicts with QJsonValue::Type enum -#include -#undef Bool - #include "lxqttaskbarbackend_x11.h" using namespace LXQt; @@ -107,10 +103,9 @@ LXQtTaskBar::LXQtTaskBar(ILXQtPanelPlugin *plugin, QWidget *parent) : connect(mSignalMapper, &QSignalMapper::mappedInt, this, &LXQtTaskBar::activateTask); QTimer::singleShot(0, this, &LXQtTaskBar::registerShortcuts); - connect(KX11Extras::self(), static_cast(&KX11Extras::windowChanged) - , this, &LXQtTaskBar::onWindowChanged); - connect(KX11Extras::self(), &KX11Extras::windowAdded, this, &LXQtTaskBar::onWindowAdded); - connect(KX11Extras::self(), &KX11Extras::windowRemoved, this, &LXQtTaskBar::onWindowRemoved); + connect(mBackend, &ILXQtTaskbarAbstractBackend::windowPropertyChanged, this, &LXQtTaskBar::onWindowChanged); + connect(mBackend, &ILXQtTaskbarAbstractBackend::windowAdded, this, &LXQtTaskBar::onWindowAdded); + connect(mBackend, &ILXQtTaskbarAbstractBackend::windowRemoved, this, &LXQtTaskBar::onWindowRemoved); } /************************************************ @@ -121,49 +116,6 @@ LXQtTaskBar::~LXQtTaskBar() delete mStyle; } -/************************************************ - - ************************************************/ -bool LXQtTaskBar::acceptWindow(WId window) const -{ - QFlags ignoreList; - ignoreList |= NET::DesktopMask; - ignoreList |= NET::DockMask; - ignoreList |= NET::SplashMask; - ignoreList |= NET::ToolbarMask; - ignoreList |= NET::MenuMask; - ignoreList |= NET::PopupMenuMask; - ignoreList |= NET::NotificationMask; - - KWindowInfo info(window, NET::WMWindowType | NET::WMState, NET::WM2TransientFor); - if (!info.valid()) - return false; - - if (NET::typeMatchesMask(info.windowType(NET::AllTypesMask), ignoreList)) - return false; - - if (info.state() & NET::SkipTaskbar) - return false; - - auto *x11Application = qGuiApp->nativeInterface(); - Q_ASSERT_X(x11Application, "DesktopSwitch", "Expected X11 connection"); - WId appRootWindow = XDefaultRootWindow(x11Application->display()); - - // WM_TRANSIENT_FOR hint not set - normal window - WId transFor = info.transientFor(); - if (transFor == 0 || transFor == window || transFor == appRootWindow) - return true; - - info = KWindowInfo(transFor, NET::WMWindowType); - - QFlags normalFlag; - normalFlag |= NET::NormalMask; - normalFlag |= NET::DialogMask; - normalFlag |= NET::UtilityMask; - - return !NET::typeMatchesMask(info.windowType(NET::AllTypesMask), normalFlag); -} - /************************************************ ************************************************/ @@ -328,7 +280,7 @@ void LXQtTaskBar::addWindow(WId window) if (mUngroupedNextToExisting) { - const QString window_class = QString::fromUtf8(KWindowInfo(window, NET::Properties(), NET::WM2WindowClass).windowClassClass()); + const QString window_class = mBackend->getWindowClass(window); int src_index = mLayout->count() - 1; int dst_index = src_index; for (int i = mLayout->count() - 2; 0 <= i; --i) @@ -336,7 +288,7 @@ void LXQtTaskBar::addWindow(WId window) LXQtTaskGroup * current_group = qobject_cast(mLayout->itemAt(i)->widget()); if (nullptr != current_group) { - const QString current_class = QString::fromUtf8(KWindowInfo((current_group->groupName()).toUInt(), NET::Properties(), NET::WM2WindowClass).windowClassClass()); + const QString current_class = mBackend->getWindowClass(current_group->groupName().toUInt()); if(current_class == window_class) { dst_index = i + 1; @@ -370,43 +322,14 @@ auto LXQtTaskBar::removeWindow(windowMap_t::iterator pos) -> windowMap_t::iterat /************************************************ ************************************************/ -void LXQtTaskBar::refreshTaskList() -{ - QList new_list; - // Just add new windows to groups, deleting is up to the groups - const auto wnds = KX11Extras::stackingOrder(); - for (auto const wnd: wnds) - { - if (acceptWindow(wnd)) - { - new_list << wnd; - addWindow(wnd); - } - } - - //emulate windowRemoved if known window not reported by KWindowSystem - for (auto i = mKnownWindows.begin(), i_e = mKnownWindows.end(); i != i_e; ) - { - if (0 > new_list.indexOf(i.key())) - { - i = removeWindow(i); - } else - ++i; - } - - refreshPlaceholderVisibility(); -} - -/************************************************ - - ************************************************/ -void LXQtTaskBar::onWindowChanged(WId window, NET::Properties prop, NET::Properties2 prop2) +void LXQtTaskBar::onWindowChanged(WId window, int prop) { auto i = mKnownWindows.find(window); if (mKnownWindows.end() != i) { - if (!(*i)->onWindowChanged(window, prop, prop2) && acceptWindow(window)) - { // window is removed from a group because of class change, so we should add it again + if (!(*i)->onWindowChanged(window, LXQtTaskBarWindowProperty(prop))) + { + // window is removed from a group because of class change, so we should add it again addWindow(window); } } @@ -415,7 +338,7 @@ void LXQtTaskBar::onWindowChanged(WId window, NET::Properties prop, NET::Propert void LXQtTaskBar::onWindowAdded(WId window) { auto const pos = mKnownWindows.find(window); - if (mKnownWindows.end() == pos && acceptWindow(window)) + if (mKnownWindows.end() == pos) addWindow(window); } @@ -541,8 +464,8 @@ void LXQtTaskBar::settingsChanged() if (iconByClassOld != mIconByClass) emit iconByClassChanged(); - refreshTaskList(); //TODO: remove mBackend->reloadWindows(); + refreshPlaceholderVisibility(); } /************************************************ diff --git a/plugin-taskbar/lxqttaskbar.h b/plugin-taskbar/lxqttaskbar.h index c36bca7e0..9f94a2958 100644 --- a/plugin-taskbar/lxqttaskbar.h +++ b/plugin-taskbar/lxqttaskbar.h @@ -37,9 +37,6 @@ #include #include "../panel/ilxqtpanel.h" -#include -#include -#include class ILXQtPanel; class ILXQtPanelPlugin; @@ -107,13 +104,14 @@ public slots: virtual void dragMoveEvent(QDragMoveEvent * event); private slots: - void refreshTaskList(); void refreshButtonRotation(); void refreshPlaceholderVisibility(); void groupBecomeEmptySlot(); - void onWindowChanged(WId window, NET::Properties prop, NET::Properties2 prop2); + + void onWindowChanged(WId window, int prop); void onWindowAdded(WId window); void onWindowRemoved(WId window); + void registerShortcuts(); void shortcutRegistered(); void activateTask(int pos); @@ -150,7 +148,6 @@ private slots: int mWheelEventsAction; int mWheelDeltaThreshold; - bool acceptWindow(WId window) const; void setButtonStyle(Qt::ToolButtonStyle buttonStyle); void wheelEvent(QWheelEvent* event); diff --git a/plugin-taskbar/lxqttaskgroup.cpp b/plugin-taskbar/lxqttaskgroup.cpp index 655e99638..30babc358 100644 --- a/plugin-taskbar/lxqttaskgroup.cpp +++ b/plugin-taskbar/lxqttaskgroup.cpp @@ -41,12 +41,6 @@ #include #include #include -#include - -//TODO: remove -#include //For nativeInterface() -#include -#undef Bool #include "ilxqttaskbarabstractbackend.h" @@ -622,8 +616,10 @@ void LXQtTaskGroup::wheelEvent(QWheelEvent* event) /************************************************ ************************************************/ -bool LXQtTaskGroup::onWindowChanged(WId window, NET::Properties prop, NET::Properties2 prop2) -{ // returns true if the class is preserved +bool LXQtTaskGroup::onWindowChanged(WId window, LXQtTaskBarWindowProperty prop) +{ + // Returns true if the class is preserved + bool needsRefreshVisibility{false}; QList buttons; if (mButtonHash.contains(window)) @@ -636,17 +632,16 @@ bool LXQtTaskGroup::onWindowChanged(WId window, NET::Properties prop, NET::Prope if (!buttons.isEmpty()) { // if class is changed the window won't belong to our group any more - if (parentTaskBar()->isGroupingEnabled() && prop2.testFlag(NET::WM2WindowClass)) + if (parentTaskBar()->isGroupingEnabled() && prop == LXQtTaskBarWindowProperty::WindowClass) { - KWindowInfo info(window, NET::Properties(), NET::WM2WindowClass); - if (QString::fromUtf8(info.windowClassClass()) != mGroupName) + if (mBackend->getWindowClass(windowId()) != mGroupName) { onWindowRemoved(window); return false; } } // window changed virtual desktop - if (prop.testFlag(NET::WMDesktop) || prop.testFlag(NET::WMGeometry)) + if (prop == LXQtTaskBarWindowProperty::Workspace) { if (parentTaskBar()->isShowOnlyOneDesktopTasks() || parentTaskBar()->isShowOnlyCurrentScreenTasks()) @@ -655,34 +650,29 @@ bool LXQtTaskGroup::onWindowChanged(WId window, NET::Properties prop, NET::Prope } } - if (prop.testFlag(NET::WMVisibleName) || prop.testFlag(NET::WMName)) + if (prop == LXQtTaskBarWindowProperty::Title) std::for_each(buttons.begin(), buttons.end(), std::mem_fn(&LXQtTaskButton::updateText)); // XXX: we are setting window icon geometry -> don't need to handle NET::WMIconGeometry // Icon of the button can be based on windowClass - if (prop.testFlag(NET::WMIcon) || prop2.testFlag(NET::WM2WindowClass)) + if (prop == LXQtTaskBarWindowProperty::Icon) std::for_each(buttons.begin(), buttons.end(), std::mem_fn(&LXQtTaskButton::updateIcon)); bool set_urgency = false; bool urgency = false; - if (prop2.testFlag(NET::WM2Urgency)) + if (prop == LXQtTaskBarWindowProperty::Urgency) { set_urgency = true; //FIXME: original code here did not consider "demand attention", was it intentional? urgency = mBackend->applicationDemandsAttention(window); } - if (prop.testFlag(NET::WMState)) + if (prop == LXQtTaskBarWindowProperty::State) { - KWindowInfo info{window, NET::WMState}; - - // Force refresh urgency if (!set_urgency) urgency = mBackend->applicationDemandsAttention(window); std::for_each(buttons.begin(), buttons.end(), std::bind(&LXQtTaskButton::setUrgencyHint, std::placeholders::_1, urgency)); set_urgency = false; - if (info.hasState(NET::SkipTaskbar)) - onWindowRemoved(window); if (parentTaskBar()->isShowOnlyMinimizedTasks()) { diff --git a/plugin-taskbar/lxqttaskgroup.h b/plugin-taskbar/lxqttaskgroup.h index 3787f411f..d4868c5d2 100644 --- a/plugin-taskbar/lxqttaskgroup.h +++ b/plugin-taskbar/lxqttaskgroup.h @@ -33,7 +33,7 @@ #include "lxqttaskbutton.h" -#include +#include "lxqttaskbartypes.h" class QVBoxLayout; class ILXQtPanelPlugin; @@ -60,7 +60,8 @@ class LXQtTaskGroup: public LXQtTaskButton // if circular is true, then it will go around the list of buttons LXQtTaskButton * getNextPrevChildButton(bool next, bool circular); - bool onWindowChanged(WId window, NET::Properties prop, NET::Properties2 prop2); + bool onWindowChanged(WId window, LXQtTaskBarWindowProperty prop); + void setAutoRotation(bool value, ILXQtPanel::Position position); Qt::ToolButtonStyle popupButtonStyle() const; void setToolButtonsStyle(Qt::ToolButtonStyle style); From 649e85adaf62159a504eb9345fc576721b3cfe43 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sun, 28 Jan 2024 16:38:00 +0100 Subject: [PATCH 093/165] TaskBar: LXQtTaskButton remove X11 specific code --- plugin-taskbar/lxqttaskbutton.cpp | 68 ++++++++++++++++--------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 2126dca76..fa588455e 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -140,7 +140,7 @@ void LXQtTaskButton::updateIcon() QIcon ico; if (mParentTaskBar->isIconByClass()) { - ico = XdgIcon::fromTheme(QString::fromUtf8(KWindowInfo{mWindow, NET::Properties(), NET::WM2WindowClass}.windowClassClass()).toLower()); + ico = XdgIcon::fromTheme(mBackend->getWindowClass(mWindow).toLower()); } if (ico.isNull()) { @@ -517,8 +517,7 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) return; } - KWindowInfo info(mWindow, NET::Properties(), NET::WM2AllowedActions); - NET::States state = KWindowInfo(mWindow, NET::WMState).state(); + const LXQtTaskBarWindowState state = mBackend->getWindowState(mWindow); QMenu * menu = new QMenu(tr("Application")); menu->setAttribute(Qt::WA_DeleteOnClose); @@ -587,74 +586,79 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) menu->addSeparator(); a = menu->addAction(tr("Move To N&ext Monitor")); connect(a, &QAction::triggered, this, [this] { moveApplicationToPrevNextMonitor(true); }); - a->setEnabled(info.actionSupported(NET::ActionMove) && (!(state & NET::FullScreen) || ((state & NET::FullScreen) && info.actionSupported(NET::ActionFullScreen)))); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::Move) && + (state != LXQtTaskBarWindowState::FullScreen + || ((state == LXQtTaskBarWindowState::FullScreen) && mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::FullScreen)))); a = menu->addAction(tr("Move To &Previous Monitor")); connect(a, &QAction::triggered, this, [this] { moveApplicationToPrevNextMonitor(false); }); } + menu->addSeparator(); a = menu->addAction(tr("&Move")); - a->setEnabled(info.actionSupported(NET::ActionMove) && !(state & NET::Max) && !(state & NET::FullScreen)); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::Move) + && state != LXQtTaskBarWindowState::Maximized + && state != LXQtTaskBarWindowState::FullScreen); connect(a, &QAction::triggered, this, &LXQtTaskButton::moveApplication); a = menu->addAction(tr("Resi&ze")); - a->setEnabled(info.actionSupported(NET::ActionResize) && !(state & NET::Max) && !(state & NET::FullScreen)); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::Resize) + && state != LXQtTaskBarWindowState::Maximized + && state != LXQtTaskBarWindowState::FullScreen); connect(a, &QAction::triggered, this, &LXQtTaskButton::resizeApplication); /********** State menu **********/ menu->addSeparator(); - LXQtTaskBarWindowState windowState = mBackend->getWindowState(mWindow); - a = menu->addAction(tr("Ma&ximize")); - a->setEnabled(info.actionSupported(NET::ActionMax) - && windowState != LXQtTaskBarWindowState::Maximized - && windowState != LXQtTaskBarWindowState::Hidden); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::Maximize) + && state != LXQtTaskBarWindowState::Maximized + && state != LXQtTaskBarWindowState::Hidden); a->setData(int(LXQtTaskBarWindowState::Maximized)); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); if (event->modifiers() & Qt::ShiftModifier) { a = menu->addAction(tr("Maximize vertically")); - a->setEnabled(info.actionSupported(NET::ActionMaxVert) - && windowState != LXQtTaskBarWindowState::MaximizedVertically - && windowState != LXQtTaskBarWindowState::Hidden); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::MaximizeVertically) + && state != LXQtTaskBarWindowState::MaximizedVertically + && state != LXQtTaskBarWindowState::Hidden); a->setData(int(LXQtTaskBarWindowState::MaximizedVertically)); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); a = menu->addAction(tr("Maximize horizontally")); - a->setEnabled(info.actionSupported(NET::ActionMaxHoriz) - && windowState != LXQtTaskBarWindowState::MaximizedHorizontally - && windowState != LXQtTaskBarWindowState::Hidden); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::MaximizeHorizontally) + && state != LXQtTaskBarWindowState::MaximizedHorizontally + && state != LXQtTaskBarWindowState::Hidden); a->setData(int(LXQtTaskBarWindowState::MaximizedHorizontally)); connect(a, &QAction::triggered, this, &LXQtTaskButton::maximizeApplication); } a = menu->addAction(tr("&Restore")); - a->setEnabled(windowState == LXQtTaskBarWindowState::Hidden - || windowState == LXQtTaskBarWindowState::Minimized - || windowState == LXQtTaskBarWindowState::Maximized - || windowState == LXQtTaskBarWindowState::MaximizedVertically - || windowState == LXQtTaskBarWindowState::MaximizedHorizontally); + a->setEnabled(state == LXQtTaskBarWindowState::Hidden + || state == LXQtTaskBarWindowState::Minimized + || state == LXQtTaskBarWindowState::Maximized + || state == LXQtTaskBarWindowState::MaximizedVertically + || state == LXQtTaskBarWindowState::MaximizedHorizontally); connect(a, &QAction::triggered, this, &LXQtTaskButton::deMaximizeApplication); a = menu->addAction(tr("Mi&nimize")); - a->setEnabled(info.actionSupported(NET::ActionMinimize) - && windowState != LXQtTaskBarWindowState::Hidden - && windowState != LXQtTaskBarWindowState::Minimized); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::Minimize) + && state != LXQtTaskBarWindowState::Hidden + && state != LXQtTaskBarWindowState::Minimized); connect(a, &QAction::triggered, this, &LXQtTaskButton::minimizeApplication); - if (windowState == LXQtTaskBarWindowState::RolledUp) + if (state == LXQtTaskBarWindowState::RolledUp) { a = menu->addAction(tr("Roll down")); - a->setEnabled(info.actionSupported(NET::ActionShade) - && windowState != LXQtTaskBarWindowState::Hidden - && windowState != LXQtTaskBarWindowState::Minimized); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::RollUp) + && state != LXQtTaskBarWindowState::Hidden + && state != LXQtTaskBarWindowState::Minimized); connect(a, &QAction::triggered, this, &LXQtTaskButton::unShadeApplication); } else { a = menu->addAction(tr("Roll up")); - a->setEnabled(info.actionSupported(NET::ActionShade) - && windowState != LXQtTaskBarWindowState::Hidden); + a->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::RollUp) + && state != LXQtTaskBarWindowState::Hidden); connect(a, &QAction::triggered, this, &LXQtTaskButton::shadeApplication); } @@ -665,7 +669,6 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) LXQtTaskBarWindowLayer currentLayer = mBackend->getWindowLayer(mWindow); - // FIXME: There is no info.actionSupported(NET::ActionKeepAbove) a = layerMenu->addAction(tr("Always on &top")); a->setEnabled(currentLayer != LXQtTaskBarWindowLayer::KeepAbove); a->setData(int(LXQtTaskBarWindowLayer::KeepAbove)); @@ -676,7 +679,6 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) a->setData(int(LXQtTaskBarWindowLayer::Normal)); connect(a, &QAction::triggered, this, &LXQtTaskButton::setApplicationLayer); - // FIXME: There is no info.actionSupported(NET::ActionKeepBelow) a = layerMenu->addAction(tr("Always on &bottom")); a->setEnabled(currentLayer != LXQtTaskBarWindowLayer::KeepBelow); a->setData(int(LXQtTaskBarWindowLayer::KeepBelow)); From 99747610a3ba5d49f9991c4ccb34f537e953ac91 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 17 Feb 2024 23:40:21 +0100 Subject: [PATCH 094/165] TaskBar: set context menu parent --- plugin-taskbar/lxqttaskbutton.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index fa588455e..08bc50a1e 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -519,7 +519,7 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) const LXQtTaskBarWindowState state = mBackend->getWindowState(mWindow); - QMenu * menu = new QMenu(tr("Application")); + QMenu * menu = new QMenu(tr("Application"), this); menu->setAttribute(Qt::WA_DeleteOnClose); QAction* a; From b0e54b87c7a9800cf9786b97916b48e9eb0c4c23 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sun, 18 Feb 2024 00:01:15 +0100 Subject: [PATCH 095/165] LXQtPanel: rework calculatePopupWindowPos() - Don't rely on global screen coordinates - This will be needed for future Wayland port, Where we don't have global screen coordinates - Keep compatible behavior on X11 --- panel/lxqtpanel.cpp | 56 +++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/panel/lxqtpanel.cpp b/panel/lxqtpanel.cpp index a1fb68f35..ead1b276b 100644 --- a/panel/lxqtpanel.cpp +++ b/panel/lxqtpanel.cpp @@ -1407,48 +1407,60 @@ Plugin* LXQtPanel::findPlugin(const ILXQtPanelPlugin* iPlugin) const ************************************************/ QRect LXQtPanel::calculatePopupWindowPos(QPoint const & absolutePos, QSize const & windowSize) const { - int x = absolutePos.x(), y = absolutePos.y(); + QPoint localPos = mapFromGlobal(absolutePos); + int x = localPos.x(), y = localPos.y(); switch (position()) { case ILXQtPanel::PositionTop: - y = mGeometry.bottom(); + y = geometry().height(); break; case ILXQtPanel::PositionBottom: - y = mGeometry.top() - windowSize.height(); + y = 0 - windowSize.height(); break; case ILXQtPanel::PositionLeft: - x = mGeometry.right(); + x = geometry().right(); break; case ILXQtPanel::PositionRight: - x = mGeometry.left() - windowSize.width(); + x = geometry().left() - windowSize.width(); break; } QRect res(QPoint(x, y), windowSize); - QRect panelScreen; - const auto screens = QApplication::screens(); - if (mActualScreenNum < screens.size()) - panelScreen = screens.at(mActualScreenNum)->geometry(); - // NOTE: We cannot use AvailableGeometry() which returns the work area here because when in a - // multihead setup with different resolutions. In this case, the size of the work area is limited - // by the smallest monitor and may be much smaller than the current screen and we will place the - // menu at the wrong place. This is very bad for UX. So let's use the full size of the screen. - if (res.right() > panelScreen.right()) - res.moveRight(panelScreen.right()); + // Map to global coordinates + res = QRect(mapToGlobal(res.topLeft()), mapToGlobal(res.bottomRight())); + + if(qGuiApp->nativeInterface()) + { + //On X11 we clamp rects inside screen area. + //NOTE: On Wayland it's done by compositor - if (res.bottom() > panelScreen.bottom()) - res.moveBottom(panelScreen.bottom()); + QRect panelScreen; + const auto screens = QApplication::screens(); + if (mActualScreenNum < screens.size()) + panelScreen = screens.at(mActualScreenNum)->geometry(); - if (res.left() < panelScreen.left()) - res.moveLeft(panelScreen.left()); + // NOTE: We cannot use AvailableGeometry() which returns the work area here because when in a + // multihead setup with different resolutions. In this case, the size of the work area is limited + // by the smallest monitor and may be much smaller than the current screen and we will place the + // menu at the wrong place. This is very bad for UX. So let's use the full size of the screen. - if (res.top() < panelScreen.top()) - res.moveTop(panelScreen.top()); + if (res.right() > panelScreen.right()) + res.moveRight(panelScreen.right()); + + if (res.bottom() > panelScreen.bottom()) + res.moveBottom(panelScreen.bottom() - mGeometry.top()); + + if (res.left() < panelScreen.left()) + res.moveLeft(panelScreen.left()); + + if (res.top() < panelScreen.top()) + res.moveTop(panelScreen.top() - mGeometry.top()); + } return res; } @@ -1470,7 +1482,7 @@ QRect LXQtPanel::calculatePopupWindowPos(const ILXQtPanelPlugin *plugin, const Q } // Note: assuming there are not contentMargins around the "BackgroundWidget" (LXQtPanelWidget) - return calculatePopupWindowPos(mGeometry.topLeft() + panel_plugin->geometry().topLeft(), windowSize); + return calculatePopupWindowPos(mapToGlobal(panel_plugin->geometry().topLeft()), windowSize); } From 8fd51efdfd11bbdc11d13a92dddf689ea50f8631 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sun, 18 Feb 2024 13:13:24 +0100 Subject: [PATCH 096/165] LXQtPanel: avoid QCursor::pos() usage --- panel/lxqtpanel.cpp | 6 +++--- panel/lxqtpanel.h | 3 ++- panel/plugin.cpp | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/panel/lxqtpanel.cpp b/panel/lxqtpanel.cpp index ead1b276b..f0897fede 100644 --- a/panel/lxqtpanel.cpp +++ b/panel/lxqtpanel.cpp @@ -1255,7 +1255,7 @@ bool LXQtPanel::event(QEvent *event) switch (event->type()) { case QEvent::ContextMenu: - showPopupMenu(); + showPopupMenu(static_cast(event)->globalPos()); break; case QEvent::LayoutRequest: @@ -1320,7 +1320,7 @@ void LXQtPanel::showEvent(QShowEvent *event) /************************************************ ************************************************/ -void LXQtPanel::showPopupMenu(Plugin *plugin) +void LXQtPanel::showPopupMenu(const QPoint& cursorPos, Plugin *plugin) { PopupMenu * menu = new PopupMenu(tr("Panel"), this); menu->setAttribute(Qt::WA_DeleteOnClose); @@ -1388,7 +1388,7 @@ void LXQtPanel::showPopupMenu(Plugin *plugin) * sometimes wrongly (it seems that this bug is somehow connected to misinterpretation * of QDesktopWidget::availableGeometry) */ - menu->setGeometry(calculatePopupWindowPos(QCursor::pos(), menu->sizeHint())); + menu->setGeometry(calculatePopupWindowPos(cursorPos, menu->sizeHint())); willShowWindow(menu); menu->show(); } diff --git a/panel/lxqtpanel.h b/panel/lxqtpanel.h index c1930c1bf..ed952dbdb 100644 --- a/panel/lxqtpanel.h +++ b/panel/lxqtpanel.h @@ -139,10 +139,11 @@ class LXQT_PANEL_API LXQtPanel : public QFrame, public ILXQtPanel * is given as parameter, the menu will be divided in two groups: * plugin-specific options and panel-related options. As these two are * shown together, this menu has to be created by LXQtPanel. + * @param cursorPos The global cursor pos * @param plugin The plugin whose menu options will be included in the * context menu. */ - void showPopupMenu(Plugin *plugin = 0); + void showPopupMenu(const QPoint &cursorPos, Plugin *plugin = nullptr); // ILXQtPanel overrides ........ ILXQtPanel::Position position() const override { return mPosition; } diff --git a/panel/plugin.cpp b/panel/plugin.cpp index eb22ed431..4844bd528 100644 --- a/panel/plugin.cpp +++ b/panel/plugin.cpp @@ -380,9 +380,9 @@ void Plugin::saveSettings() /************************************************ ************************************************/ -void Plugin::contextMenuEvent(QContextMenuEvent * /*event*/) +void Plugin::contextMenuEvent(QContextMenuEvent * event) { - mPanel->showPopupMenu(this); + mPanel->showPopupMenu(event->globalPos(), this); } From be23ae5cb184388d65d7973bde8320723e8c7887 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Mon, 19 Feb 2024 23:10:58 +0100 Subject: [PATCH 097/165] ILXQtTaskbarAbstractBackend: new Geometry window property This new window propery flag is needed to notify geometry changes --- plugin-taskbar/lxqttaskbarbackend_x11.cpp | 5 +++++ plugin-taskbar/lxqttaskbartypes.h | 1 + 2 files changed, 6 insertions(+) diff --git a/plugin-taskbar/lxqttaskbarbackend_x11.cpp b/plugin-taskbar/lxqttaskbarbackend_x11.cpp index cc7759f4e..bcb83bdde 100644 --- a/plugin-taskbar/lxqttaskbarbackend_x11.cpp +++ b/plugin-taskbar/lxqttaskbarbackend_x11.cpp @@ -48,6 +48,11 @@ void LXQtTaskbarX11Backend::onWindowChanged(WId windowId, NET::Properties prop, return; } + if (prop.testFlag(NET::WMGeometry)) + { + emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::Geometry)); + } + if (prop2.testFlag(NET::WM2WindowClass)) { emit windowPropertyChanged(windowId, int(LXQtTaskBarWindowProperty::WindowClass)); diff --git a/plugin-taskbar/lxqttaskbartypes.h b/plugin-taskbar/lxqttaskbartypes.h index ccea42027..9e12092bb 100644 --- a/plugin-taskbar/lxqttaskbartypes.h +++ b/plugin-taskbar/lxqttaskbartypes.h @@ -22,6 +22,7 @@ enum class LXQtTaskBarWindowProperty Title = 0, Icon, State, + Geometry, Urgency, WindowClass, Workspace From e334ec065350a9914dd5d9f97ca8a56f770df729 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 17 Feb 2024 12:34:05 +0100 Subject: [PATCH 098/165] Move ILXQtTaskbarAbstractBackend to panel directory - It is now a global instance --- panel/CMakeLists.txt | 15 +++++++++ panel/backends/CMakeLists.txt | 1 + .../backends}/ilxqttaskbarabstractbackend.cpp | 2 +- .../backends}/ilxqttaskbarabstractbackend.h | 0 .../backends}/lxqttaskbartypes.h | 0 panel/backends/xcb/CMakeLists.txt | 1 + .../backends/xcb}/lxqttaskbarbackend_x11.cpp | 0 .../backends/xcb}/lxqttaskbarbackend_x11.h | 2 +- panel/lxqtpanelapplication.cpp | 31 +++++++++++++++---- panel/lxqtpanelapplication.h | 4 +++ panel/lxqtpanelapplication_p.h | 3 ++ plugin-taskbar/CMakeLists.txt | 5 --- plugin-taskbar/lxqttaskbar.cpp | 9 +++--- plugin-taskbar/lxqttaskbarproxymodel.cpp | 2 +- plugin-taskbar/lxqttaskbarproxymodel.h | 2 +- plugin-taskbar/lxqttaskbutton.cpp | 2 +- plugin-taskbar/lxqttaskgroup.cpp | 2 +- plugin-taskbar/lxqttaskgroup.h | 2 +- 18 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 panel/backends/CMakeLists.txt rename {plugin-taskbar => panel/backends}/ilxqttaskbarabstractbackend.cpp (90%) rename {plugin-taskbar => panel/backends}/ilxqttaskbarabstractbackend.h (100%) rename {plugin-taskbar => panel/backends}/lxqttaskbartypes.h (100%) create mode 100644 panel/backends/xcb/CMakeLists.txt rename {plugin-taskbar => panel/backends/xcb}/lxqttaskbarbackend_x11.cpp (100%) rename {plugin-taskbar => panel/backends/xcb}/lxqttaskbarbackend_x11.h (98%) diff --git a/panel/CMakeLists.txt b/panel/CMakeLists.txt index 556d2212f..83d813772 100644 --- a/panel/CMakeLists.txt +++ b/panel/CMakeLists.txt @@ -1,5 +1,8 @@ set(PROJECT lxqt-panel) +# TODO +add_subdirectory(backends) + set(PRIV_HEADERS panelpluginsmodel.h windownotifier.h @@ -18,6 +21,11 @@ set(PRIV_HEADERS config/configstyling.h config/configpluginswidget.h config/addplugindialog.h + + backends/ilxqttaskbarabstractbackend.h + backends/lxqttaskbartypes.h + + backends/xcb/lxqttaskbarbackend_x11.h ) # using LXQt namespace in the public headers. @@ -26,6 +34,9 @@ set(PUB_HEADERS pluginsettings.h ilxqtpanelplugin.h ilxqtpanel.h + + backends/ilxqttaskbarabstractbackend.h + backends/lxqttaskbartypes.h ) set(SOURCES @@ -45,6 +56,10 @@ set(SOURCES config/configstyling.cpp config/configpluginswidget.cpp config/addplugindialog.cpp + + backends/ilxqttaskbarabstractbackend.cpp + + backends/xcb/lxqttaskbarbackend_x11.cpp ) set(UI diff --git a/panel/backends/CMakeLists.txt b/panel/backends/CMakeLists.txt new file mode 100644 index 000000000..8f34a3c67 --- /dev/null +++ b/panel/backends/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(xcb) diff --git a/plugin-taskbar/ilxqttaskbarabstractbackend.cpp b/panel/backends/ilxqttaskbarabstractbackend.cpp similarity index 90% rename from plugin-taskbar/ilxqttaskbarabstractbackend.cpp rename to panel/backends/ilxqttaskbarabstractbackend.cpp index d0959fb78..acf33f464 100644 --- a/plugin-taskbar/ilxqttaskbarabstractbackend.cpp +++ b/panel/backends/ilxqttaskbarabstractbackend.cpp @@ -1,4 +1,4 @@ -#include "ilxqttaskbarabstractbackend.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" ILXQtTaskbarAbstractBackend::ILXQtTaskbarAbstractBackend(QObject *parent) diff --git a/plugin-taskbar/ilxqttaskbarabstractbackend.h b/panel/backends/ilxqttaskbarabstractbackend.h similarity index 100% rename from plugin-taskbar/ilxqttaskbarabstractbackend.h rename to panel/backends/ilxqttaskbarabstractbackend.h diff --git a/plugin-taskbar/lxqttaskbartypes.h b/panel/backends/lxqttaskbartypes.h similarity index 100% rename from plugin-taskbar/lxqttaskbartypes.h rename to panel/backends/lxqttaskbartypes.h diff --git a/panel/backends/xcb/CMakeLists.txt b/panel/backends/xcb/CMakeLists.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/panel/backends/xcb/CMakeLists.txt @@ -0,0 +1 @@ + diff --git a/plugin-taskbar/lxqttaskbarbackend_x11.cpp b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp similarity index 100% rename from plugin-taskbar/lxqttaskbarbackend_x11.cpp rename to panel/backends/xcb/lxqttaskbarbackend_x11.cpp diff --git a/plugin-taskbar/lxqttaskbarbackend_x11.h b/panel/backends/xcb/lxqttaskbarbackend_x11.h similarity index 98% rename from plugin-taskbar/lxqttaskbarbackend_x11.h rename to panel/backends/xcb/lxqttaskbarbackend_x11.h index 26c721cf7..2797a7da8 100644 --- a/plugin-taskbar/lxqttaskbarbackend_x11.h +++ b/panel/backends/xcb/lxqttaskbarbackend_x11.h @@ -1,7 +1,7 @@ #ifndef LXQTTASKBARBACKEND_X11_H #define LXQTTASKBARBACKEND_X11_H -#include "ilxqttaskbarabstractbackend.h" +#include "../ilxqttaskbarabstractbackend.h" //TODO: make PIMPL to forward declare NET::Properties, Display, xcb_connection_t #include diff --git a/panel/lxqtpanelapplication.cpp b/panel/lxqtpanelapplication.cpp index 430ea6f35..2b1980d85 100644 --- a/panel/lxqtpanelapplication.cpp +++ b/panel/lxqtpanelapplication.cpp @@ -25,22 +25,35 @@ * * END_COMMON_COPYRIGHT_HEADER */ - #include "lxqtpanelapplication.h" #include "lxqtpanelapplication_p.h" -#include "lxqtpanel.h" + #include "config/configpaneldialog.h" -#include -#include -#include +#include "lxqtpanel.h" + +#include #include +#include #include -#include +#include +#include + +#include "backends/xcb/lxqttaskbarbackend_x11.h" + +ILXQtTaskbarAbstractBackend *createWMBackend() +{ + if(qGuiApp->nativeInterface()) + return new LXQtTaskbarX11Backend; + + Q_ASSERT_X(false, "createWMBackend()", "Only X11 supported!"); + return nullptr; +} LXQtPanelApplicationPrivate::LXQtPanelApplicationPrivate(LXQtPanelApplication *q) : mSettings(nullptr), q_ptr(q) { + mWMBackend = createWMBackend(); } @@ -290,6 +303,12 @@ bool LXQtPanelApplication::isPluginSingletonAndRunning(QString const & pluginId) return false; } +ILXQtTaskbarAbstractBackend *LXQtPanelApplication::getWMBackend() const +{ + Q_D(const LXQtPanelApplication); + return d->mWMBackend; +} + // See LXQtPanelApplication::LXQtPanelApplication for why this isn't good. void LXQtPanelApplication::setIconTheme(const QString &iconTheme) { diff --git a/panel/lxqtpanelapplication.h b/panel/lxqtpanelapplication.h index a672e7e46..15c912884 100644 --- a/panel/lxqtpanelapplication.h +++ b/panel/lxqtpanelapplication.h @@ -37,6 +37,8 @@ class QScreen; class LXQtPanel; class LXQtPanelApplicationPrivate; +class ILXQtTaskbarAbstractBackend; + /*! * \brief The LXQtPanelApplication class inherits from LXQt::Application and * is therefore the QApplication that we will create and execute in our @@ -89,6 +91,8 @@ class LXQtPanelApplication : public LXQt::Application */ bool isPluginSingletonAndRunning(QString const & pluginId) const; + ILXQtTaskbarAbstractBackend* getWMBackend() const; + public slots: /*! * \brief Adds a new LXQtPanel which consists of the following steps: diff --git a/panel/lxqtpanelapplication_p.h b/panel/lxqtpanelapplication_p.h index 4dd26182c..db924bf62 100644 --- a/panel/lxqtpanelapplication_p.h +++ b/panel/lxqtpanelapplication_p.h @@ -27,6 +27,8 @@ namespace LXQt { class Settings; } +class ILXQtTaskbarAbstractBackend; + class LXQtPanelApplicationPrivate { Q_DECLARE_PUBLIC(LXQtPanelApplication) public: @@ -35,6 +37,7 @@ class LXQtPanelApplicationPrivate { ~LXQtPanelApplicationPrivate() {}; LXQt::Settings *mSettings; + ILXQtTaskbarAbstractBackend *mWMBackend; ILXQtPanel::Position computeNewPanelPosition(const LXQtPanel *p, const int screenNum); diff --git a/plugin-taskbar/CMakeLists.txt b/plugin-taskbar/CMakeLists.txt index be434f6a6..74a00d6e6 100644 --- a/plugin-taskbar/CMakeLists.txt +++ b/plugin-taskbar/CMakeLists.txt @@ -8,9 +8,6 @@ set(HEADERS lxqttaskgroup.h lxqtgrouppopup.h - ilxqttaskbarabstractbackend.h - lxqttaskbartypes.h - lxqttaskbarbackend_x11.h lxqttaskbarproxymodel.h ) @@ -22,8 +19,6 @@ set(SOURCES lxqttaskgroup.cpp lxqtgrouppopup.cpp - ilxqttaskbarabstractbackend.cpp - lxqttaskbarbackend_x11.cpp lxqttaskbarproxymodel.cpp ) diff --git a/plugin-taskbar/lxqttaskbar.cpp b/plugin-taskbar/lxqttaskbar.cpp index 1bf7781f7..c306d796e 100644 --- a/plugin-taskbar/lxqttaskbar.cpp +++ b/plugin-taskbar/lxqttaskbar.cpp @@ -50,7 +50,8 @@ #include "lxqttaskgroup.h" #include "../panel/pluginsettings.h" -#include "lxqttaskbarbackend_x11.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" +#include "../panel/lxqtpanelapplication.h" using namespace LXQt; @@ -93,9 +94,9 @@ LXQtTaskBar::LXQtTaskBar(ILXQtPanelPlugin *plugin, QWidget *parent) : mPlaceHolder->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); mLayout->addWidget(mPlaceHolder); - // Create model - //TODO: use backend factory - mBackend = new LXQtTaskbarX11Backend(this); + // Get backend + LXQtPanelApplication *a = static_cast(qApp); + mBackend = a->getWMBackend(); QTimer::singleShot(0, this, &LXQtTaskBar::settingsChanged); setAcceptDrops(true); diff --git a/plugin-taskbar/lxqttaskbarproxymodel.cpp b/plugin-taskbar/lxqttaskbarproxymodel.cpp index e2f1d3941..fdcdfa4d4 100644 --- a/plugin-taskbar/lxqttaskbarproxymodel.cpp +++ b/plugin-taskbar/lxqttaskbarproxymodel.cpp @@ -1,6 +1,6 @@ #include "lxqttaskbarproxymodel.h" -#include "ilxqttaskbarabstractbackend.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" #include diff --git a/plugin-taskbar/lxqttaskbarproxymodel.h b/plugin-taskbar/lxqttaskbarproxymodel.h index be5799076..8bbb5ec49 100644 --- a/plugin-taskbar/lxqttaskbarproxymodel.h +++ b/plugin-taskbar/lxqttaskbarproxymodel.h @@ -4,7 +4,7 @@ #include #include -#include "lxqttaskbartypes.h" +#include "../panel/backends/lxqttaskbartypes.h" class ILXQtTaskbarAbstractBackend; diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 08bc50a1e..019b45199 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -50,7 +50,7 @@ #include #include -#include "ilxqttaskbarabstractbackend.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" diff --git a/plugin-taskbar/lxqttaskgroup.cpp b/plugin-taskbar/lxqttaskgroup.cpp index 30babc358..c6075df89 100644 --- a/plugin-taskbar/lxqttaskgroup.cpp +++ b/plugin-taskbar/lxqttaskgroup.cpp @@ -42,7 +42,7 @@ #include #include -#include "ilxqttaskbarabstractbackend.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" /************************************************ diff --git a/plugin-taskbar/lxqttaskgroup.h b/plugin-taskbar/lxqttaskgroup.h index d4868c5d2..31ab784fb 100644 --- a/plugin-taskbar/lxqttaskgroup.h +++ b/plugin-taskbar/lxqttaskgroup.h @@ -33,7 +33,7 @@ #include "lxqttaskbutton.h" -#include "lxqttaskbartypes.h" +#include "../panel/backends/lxqttaskbartypes.h" class QVBoxLayout; class ILXQtPanelPlugin; From 9bbf4fa3d1e95bff52105038574a1ff2550a1bc3 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Mon, 19 Feb 2024 23:17:55 +0100 Subject: [PATCH 099/165] ILXQtTaskbarAbstractBackend: new isAreaOverlapped() method --- panel/backends/ilxqttaskbarabstractbackend.h | 3 ++ panel/backends/xcb/lxqttaskbarbackend_x11.cpp | 32 +++++++++++++++++++ panel/backends/xcb/lxqttaskbarbackend_x11.h | 3 ++ 3 files changed, 38 insertions(+) diff --git a/panel/backends/ilxqttaskbarabstractbackend.h b/panel/backends/ilxqttaskbarabstractbackend.h index 932d64fc0..fb49d7b52 100644 --- a/panel/backends/ilxqttaskbarabstractbackend.h +++ b/panel/backends/ilxqttaskbarabstractbackend.h @@ -65,6 +65,9 @@ class ILXQtTaskbarAbstractBackend : public QObject virtual void refreshIconGeometry(WId windowId, const QRect &geom) = 0; + // Panel internal + virtual bool isAreaOverlapped(const QRect& area) const = 0; + signals: void reloaded(); diff --git a/panel/backends/xcb/lxqttaskbarbackend_x11.cpp b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp index bcb83bdde..1d29148c8 100644 --- a/panel/backends/xcb/lxqttaskbarbackend_x11.cpp +++ b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp @@ -585,3 +585,35 @@ void LXQtTaskbarX11Backend::refreshIconGeometry(WId windowId, QRect const & geom nrect.size.width = geom.width(); info.setIconGeometry(nrect); } + +bool LXQtTaskbarX11Backend::isAreaOverlapped(const QRect &area) const +{ + //TODO: reuse our m_windows cache? + QFlags ignoreList; + ignoreList |= NET::DesktopMask; + ignoreList |= NET::DockMask; + ignoreList |= NET::SplashMask; + ignoreList |= NET::MenuMask; + ignoreList |= NET::PopupMenuMask; + ignoreList |= NET::DropdownMenuMask; + ignoreList |= NET::TopMenuMask; + ignoreList |= NET::NotificationMask; + + const auto wIds = KX11Extras::stackingOrder(); + for (auto const wId : wIds) + { + KWindowInfo info(wId, NET::WMWindowType | NET::WMState | NET::WMFrameExtents | NET::WMDesktop); + if (info.valid() + // skip windows that are on other desktops + && info.isOnCurrentDesktop() + // skip shaded, minimized or hidden windows + && !(info.state() & (NET::Shaded | NET::Hidden)) + // check against the list of ignored types + && !NET::typeMatchesMask(info.windowType(NET::AllTypesMask), ignoreList)) + { + if (info.frameGeometry().intersects(area)) + return true; + } + } + return false; +} diff --git a/panel/backends/xcb/lxqttaskbarbackend_x11.h b/panel/backends/xcb/lxqttaskbarbackend_x11.h index 2797a7da8..42e84b146 100644 --- a/panel/backends/xcb/lxqttaskbarbackend_x11.h +++ b/panel/backends/xcb/lxqttaskbarbackend_x11.h @@ -61,6 +61,9 @@ class LXQtTaskbarX11Backend : public ILXQtTaskbarAbstractBackend virtual void refreshIconGeometry(WId windowId, const QRect &geom) override; + // Panel internal + virtual bool isAreaOverlapped(const QRect& area) const override; + private slots: void onWindowChanged(WId windowId, NET::Properties prop, NET::Properties2 prop2); void onWindowAdded(WId windowId); From b9c8dfd7838d3b0c118b2c89d74023449575ebe2 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Mon, 19 Feb 2024 23:11:23 +0100 Subject: [PATCH 100/165] LXQtPanel: use less KX11Extras and more ILXQtTaskbarAbstractBackend --- panel/lxqtpanel.cpp | 83 +++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 48 deletions(-) diff --git a/panel/lxqtpanel.cpp b/panel/lxqtpanel.cpp index f0897fede..3735bd068 100644 --- a/panel/lxqtpanel.cpp +++ b/panel/lxqtpanel.cpp @@ -51,7 +51,9 @@ #include #include #include -#include + +#include "backends/ilxqttaskbarabstractbackend.h" + #include @@ -276,18 +278,21 @@ LXQtPanel::LXQtPanel(const QString &configGroup, LXQt::Settings *settings, QWidg QTimer::singleShot(PANEL_HIDE_FIRST_TIME, this, SLOT(hidePanel())); } - connect(KX11Extras::self(), &KX11Extras::windowAdded, this, [this] { + LXQtPanelApplication *a = reinterpret_cast(qApp); + auto wmBackend = a->getWMBackend(); + + connect(wmBackend, &ILXQtTaskbarAbstractBackend::windowAdded, this, [this] { if (mHidable && mHideOnOverlap && !mHidden) { mShowDelayTimer.stop(); hidePanel(); } }); - connect(KX11Extras::self(), &KX11Extras::windowRemoved, this, [this] { + connect(wmBackend, &ILXQtTaskbarAbstractBackend::windowRemoved, this, [this] { if (mHidable && mHideOnOverlap && mHidden && !isPanelOverlapped()) mShowDelayTimer.start(); }); - connect(KX11Extras::self(), &KX11Extras::currentDesktopChanged, this, [this] { + connect(wmBackend, &ILXQtTaskbarAbstractBackend::currentWorkspaceChanged, this, [this] { if (mHidable && mHideOnOverlap) { if (!mHidden) @@ -299,12 +304,12 @@ LXQtPanel::LXQtPanel(const QString &configGroup, LXQt::Settings *settings, QWidg mShowDelayTimer.start(); } }); - connect(KX11Extras::self(), - static_cast(&KX11Extras::windowChanged), - this, [this] (WId /* id */, NET::Properties prop, NET::Properties2) { + connect(wmBackend, &ILXQtTaskbarAbstractBackend::windowPropertyChanged, + this, [this] (WId /* id */, int prop) + { if (mHidable && mHideOnOverlap // when a window is moved, resized, shaded, or minimized - && (prop.testFlag(NET::WMGeometry) || prop.testFlag(NET::WMState))) + && (prop == int(LXQtTaskBarWindowProperty::Geometry) || prop == int(LXQtTaskBarWindowProperty::State))) { if (!mHidden) { @@ -453,7 +458,8 @@ LXQtPanel::~LXQtPanel() void LXQtPanel::show() { QWidget::show(); - KX11Extras::setOnDesktop(effectiveWinId(), NET::OnAllDesktops); + if(qGuiApp->nativeInterface()) //TODO: cache in bool isPlatformX11 + KX11Extras::setOnDesktop(effectiveWinId(), NET::OnAllDesktops); } @@ -1264,22 +1270,25 @@ bool LXQtPanel::event(QEvent *event) case QEvent::WinIdChange: { - // qDebug() << "WinIdChange" << hex << effectiveWinId(); - if(effectiveWinId() == 0) - break; + if(qGuiApp->nativeInterface()) + { + // qDebug() << "WinIdChange" << hex << effectiveWinId(); + if(effectiveWinId() == 0) + break; - // Sometimes Qt needs to re-create the underlying window of the widget and - // the winId() may be changed at runtime. So we need to reset all X11 properties - // when this happens. + // Sometimes Qt needs to re-create the underlying window of the widget and + // the winId() may be changed at runtime. So we need to reset all X11 properties + // when this happens. qDebug() << "WinIdChange" << Qt::hex << effectiveWinId() << "handle" << windowHandle() << windowHandle()->screen(); - // Qt::WA_X11NetWmWindowTypeDock becomes ineffective in Qt 5 - // See QTBUG-39887: https://bugreports.qt-project.org/browse/QTBUG-39887 - // Let's use KWindowSystem for that - KX11Extras::setType(effectiveWinId(), NET::Dock); + // Qt::WA_X11NetWmWindowTypeDock becomes ineffective in Qt 5 + // See QTBUG-39887: https://bugreports.qt-project.org/browse/QTBUG-39887 + // Let's use KWindowSystem for that + KX11Extras::setType(effectiveWinId(), NET::Dock); - updateWmStrut(); // reserve screen space for the panel - KX11Extras::setOnAllDesktops(effectiveWinId(), true); + updateWmStrut(); // reserve screen space for the panel + KX11Extras::setOnAllDesktops(effectiveWinId(), true); + } break; } case QEvent::DragEnter: @@ -1565,33 +1574,11 @@ void LXQtPanel::userRequestForDeletion() bool LXQtPanel::isPanelOverlapped() const { - QFlags ignoreList; - ignoreList |= NET::DesktopMask; - ignoreList |= NET::DockMask; - ignoreList |= NET::SplashMask; - ignoreList |= NET::MenuMask; - ignoreList |= NET::PopupMenuMask; - ignoreList |= NET::DropdownMenuMask; - ignoreList |= NET::TopMenuMask; - ignoreList |= NET::NotificationMask; - - const auto wIds = KX11Extras::stackingOrder(); - for (auto const wId : wIds) - { - KWindowInfo info(wId, NET::WMWindowType | NET::WMState | NET::WMFrameExtents | NET::WMDesktop); - if (info.valid() - // skip windows that are on other desktops - && info.isOnCurrentDesktop() - // skip shaded, minimized or hidden windows - && !(info.state() & (NET::Shaded | NET::Hidden)) - // check against the list of ignored types - && !NET::typeMatchesMask(info.windowType(NET::AllTypesMask), ignoreList)) - { - if (info.frameGeometry().intersects(mGeometry)) - return true; - } - } - return false; + LXQtPanelApplication *a = reinterpret_cast(qApp); + + //TODO: calculate geometry on wayland + QRect area = mGeometry; + return a->getWMBackend()->isAreaOverlapped(area); } void LXQtPanel::showPanel(bool animate) From 145d59806280feaa82d301ea1c6b7146f49ea18a Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 20:32:50 +0100 Subject: [PATCH 101/165] ILXQtTaskbarAbstractBackend: add dummy implementation This will be used to avoid crashing panel in case no backend could be created. A warning message will be printed in this case. --- panel/CMakeLists.txt | 2 + panel/backends/lxqttaskbardummybackend.cpp | 156 +++++++++++++++++++++ panel/backends/lxqttaskbardummybackend.h | 82 +++++++++++ panel/lxqtpanelapplication.cpp | 10 +- 4 files changed, 248 insertions(+), 2 deletions(-) create mode 100644 panel/backends/lxqttaskbardummybackend.cpp create mode 100644 panel/backends/lxqttaskbardummybackend.h diff --git a/panel/CMakeLists.txt b/panel/CMakeLists.txt index 83d813772..f088fe33a 100644 --- a/panel/CMakeLists.txt +++ b/panel/CMakeLists.txt @@ -25,6 +25,7 @@ set(PRIV_HEADERS backends/ilxqttaskbarabstractbackend.h backends/lxqttaskbartypes.h + backends/lxqttaskbardummybackend.h backends/xcb/lxqttaskbarbackend_x11.h ) @@ -59,6 +60,7 @@ set(SOURCES backends/ilxqttaskbarabstractbackend.cpp + backends/lxqttaskbardummybackend.cpp backends/xcb/lxqttaskbarbackend_x11.cpp ) diff --git a/panel/backends/lxqttaskbardummybackend.cpp b/panel/backends/lxqttaskbardummybackend.cpp new file mode 100644 index 000000000..f91fa75bf --- /dev/null +++ b/panel/backends/lxqttaskbardummybackend.cpp @@ -0,0 +1,156 @@ +#include "lxqttaskbardummybackend.h" + +#include + +LXQtTaskBarDummyBackend::LXQtTaskBarDummyBackend(QObject *parent) + : ILXQtTaskbarAbstractBackend(parent) +{ + +} + + +/************************************************ + * Windows function + ************************************************/ +bool LXQtTaskBarDummyBackend::supportsAction(WId, LXQtTaskBarBackendAction) const +{ + return false; +} + +bool LXQtTaskBarDummyBackend::reloadWindows() +{ + return false; +} + +QVector LXQtTaskBarDummyBackend::getCurrentWindows() const +{ + return {}; +} + +QString LXQtTaskBarDummyBackend::getWindowTitle(WId) const +{ + return QString(); +} + +bool LXQtTaskBarDummyBackend::applicationDemandsAttention(WId) const +{ + return false; +} + +QIcon LXQtTaskBarDummyBackend::getApplicationIcon(WId, int) const +{ + return QIcon(); +} + +QString LXQtTaskBarDummyBackend::getWindowClass(WId) const +{ + return QString(); +} + +LXQtTaskBarWindowLayer LXQtTaskBarDummyBackend::getWindowLayer(WId) const +{ + return LXQtTaskBarWindowLayer::Normal; +} + +bool LXQtTaskBarDummyBackend::setWindowLayer(WId, LXQtTaskBarWindowLayer) +{ + return false; +} + +LXQtTaskBarWindowState LXQtTaskBarDummyBackend::getWindowState(WId) const +{ + return LXQtTaskBarWindowState::Normal; +} + +bool LXQtTaskBarDummyBackend::setWindowState(WId, LXQtTaskBarWindowState, bool) +{ + return false; +} + +bool LXQtTaskBarDummyBackend::isWindowActive(WId) const +{ + return false; +} + +bool LXQtTaskBarDummyBackend::raiseWindow(WId, bool) +{ + return false; +} + +bool LXQtTaskBarDummyBackend::closeWindow(WId) +{ + return false; +} + +WId LXQtTaskBarDummyBackend::getActiveWindow() const +{ + return 0; +} + + +/************************************************ + * Workspaces + ************************************************/ +int LXQtTaskBarDummyBackend::getWorkspacesCount() const +{ + return 1; // Fake 1 workspace +} + +QString LXQtTaskBarDummyBackend::getWorkspaceName(int) const +{ + return QString(); +} + +int LXQtTaskBarDummyBackend::getCurrentWorkspace() const +{ + return 0; +} + +bool LXQtTaskBarDummyBackend::setCurrentWorkspace(int) +{ + return false; +} + +int LXQtTaskBarDummyBackend::getWindowWorkspace(WId) const +{ + return 0; +} + +bool LXQtTaskBarDummyBackend::setWindowOnWorkspace(WId, int) +{ + return false; +} + +void LXQtTaskBarDummyBackend::moveApplicationToPrevNextMonitor(WId, bool, bool) +{ + //No-op +} + +bool LXQtTaskBarDummyBackend::isWindowOnScreen(QScreen *, WId) const +{ + return false; +} + +/************************************************ + * X11 Specific + ************************************************/ +void LXQtTaskBarDummyBackend::moveApplication(WId) +{ + //No-op +} + +void LXQtTaskBarDummyBackend::resizeApplication(WId) +{ + //No-op +} + +void LXQtTaskBarDummyBackend::refreshIconGeometry(WId, QRect const &) +{ + //No-op +} + +bool LXQtTaskBarDummyBackend::isAreaOverlapped(const QRect &) const +{ + return false; +} + diff --git a/panel/backends/lxqttaskbardummybackend.h b/panel/backends/lxqttaskbardummybackend.h new file mode 100644 index 000000000..535de303b --- /dev/null +++ b/panel/backends/lxqttaskbardummybackend.h @@ -0,0 +1,82 @@ +#ifndef LXQTTASKBARDUMMYBACKEND_H +#define LXQTTASKBARDUMMYBACKEND_H + +#include "ilxqttaskbarabstractbackend.h" + +class LXQtTaskBarDummyBackend : public ILXQtTaskbarAbstractBackend +{ + Q_OBJECT + +public: + explicit LXQtTaskBarDummyBackend(QObject *parent = nullptr); + + // Backend + bool supportsAction(WId windowId, LXQtTaskBarBackendAction action) const override; + + // Windows + bool reloadWindows() override; + + QVector getCurrentWindows() const override; + + QString getWindowTitle(WId windowId) const override; + + bool applicationDemandsAttention(WId windowId) const override; + + QIcon getApplicationIcon(WId windowId, int fallbackDevicePixels) const override; + + QString getWindowClass(WId windowId) const override; + + LXQtTaskBarWindowLayer getWindowLayer(WId windowId) const override; + bool setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) override; + + LXQtTaskBarWindowState getWindowState(WId windowId) const override; + bool setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set = true) override; + + bool isWindowActive(WId windowId) const override; + bool raiseWindow(WId windowId, bool onCurrentWorkSpace) override; + + bool closeWindow(WId windowId) override; + + WId getActiveWindow() const override; + + // Workspaces + int getWorkspacesCount() const override; + QString getWorkspaceName(int idx) const override; + + int getCurrentWorkspace() const override; + bool setCurrentWorkspace(int idx) override; + + int getWindowWorkspace(WId windowId) const override; + bool setWindowOnWorkspace(WId windowId, int idx) override; + + void moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) override; + + bool isWindowOnScreen(QScreen *screen, WId windowId) const override; + + // X11 Specific + void moveApplication(WId windowId) override; + void resizeApplication(WId windowId) override; + + void refreshIconGeometry(WId windowId, const QRect &geom) override; + + // Panel internal + bool isAreaOverlapped(const QRect& area) const override; + +signals: + void reloaded(); + + // Windows + void windowAdded(WId windowId); + void windowRemoved(WId windowId); + void windowPropertyChanged(WId windowId, int prop); + + // Workspaces + void workspacesCountChanged(); + void workspaceNameChanged(int idx); + void currentWorkspaceChanged(int idx); + + // TODO: needed? + void activeWindowChanged(WId windowId); +}; + +#endif // LXQTTASKBARDUMMYBACKEND_H diff --git a/panel/lxqtpanelapplication.cpp b/panel/lxqtpanelapplication.cpp index 2b1980d85..c3b666620 100644 --- a/panel/lxqtpanelapplication.cpp +++ b/panel/lxqtpanelapplication.cpp @@ -38,6 +38,7 @@ #include #include +#include "backends/lxqttaskbardummybackend.h" #include "backends/xcb/lxqttaskbarbackend_x11.h" ILXQtTaskbarAbstractBackend *createWMBackend() @@ -45,8 +46,13 @@ ILXQtTaskbarAbstractBackend *createWMBackend() if(qGuiApp->nativeInterface()) return new LXQtTaskbarX11Backend; - Q_ASSERT_X(false, "createWMBackend()", "Only X11 supported!"); - return nullptr; + qWarning() << "\n" + << "ERROR: Could not create a backend for window managment operations.\n" + << "Only X11 supported!\n" + << "Falling back to dummy backend. Some functions will not be available.\n" + << "\n"; + + return new LXQtTaskBarDummyBackend; } LXQtPanelApplicationPrivate::LXQtPanelApplicationPrivate(LXQtPanelApplication *q) From a3b7c022e8808102f05cd49ec582d0c96b50fe1e Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Wed, 28 Feb 2024 20:32:04 +0100 Subject: [PATCH 102/165] LXQtMainMenu: indent header include --- plugin-mainmenu/lxqtmainmenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-mainmenu/lxqtmainmenu.cpp b/plugin-mainmenu/lxqtmainmenu.cpp index 1db7f911f..0f55cdfbd 100644 --- a/plugin-mainmenu/lxqtmainmenu.cpp +++ b/plugin-mainmenu/lxqtmainmenu.cpp @@ -54,7 +54,7 @@ #include #include #include -#include + #include #endif #define DEFAULT_SHORTCUT "Alt+F1" From b199b7716178bf8fc076905c44d8fe44507ecdb9 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 17:12:03 +0100 Subject: [PATCH 103/165] ILXQtTaskbarAbstractBackend: new showDesktop() function --- panel/backends/ilxqttaskbarabstractbackend.h | 8 ++++++++ panel/backends/lxqttaskbardummybackend.cpp | 10 ++++++++++ panel/backends/lxqttaskbardummybackend.h | 4 ++++ panel/backends/xcb/lxqttaskbarbackend_x11.cpp | 11 +++++++++++ panel/backends/xcb/lxqttaskbarbackend_x11.h | 4 ++++ 5 files changed, 37 insertions(+) diff --git a/panel/backends/ilxqttaskbarabstractbackend.h b/panel/backends/ilxqttaskbarabstractbackend.h index fb49d7b52..7daecfe60 100644 --- a/panel/backends/ilxqttaskbarabstractbackend.h +++ b/panel/backends/ilxqttaskbarabstractbackend.h @@ -68,6 +68,14 @@ class ILXQtTaskbarAbstractBackend : public QObject // Panel internal virtual bool isAreaOverlapped(const QRect& area) const = 0; + // Show Destop TODO: split in multiple interfeces, this is becoming big + // NOTE: KWindowSystem already has these functions + // However on Wayland they are only compatible with KWin + // because internally it uses org_kde_plasma_window_management protocol + // We make this virtual so it can be implemented also for other compositors + virtual bool isShowingDesktop() const = 0; + virtual bool showDesktop(bool value) = 0; + signals: void reloaded(); diff --git a/panel/backends/lxqttaskbardummybackend.cpp b/panel/backends/lxqttaskbardummybackend.cpp index f91fa75bf..ceaf3c334 100644 --- a/panel/backends/lxqttaskbardummybackend.cpp +++ b/panel/backends/lxqttaskbardummybackend.cpp @@ -154,3 +154,13 @@ bool LXQtTaskBarDummyBackend::isAreaOverlapped(const QRect &) const return false; } +bool LXQtTaskBarDummyBackend::isShowingDesktop() const +{ + return false; +} + +bool LXQtTaskBarDummyBackend::showDesktop(bool) +{ + return false; +} + diff --git a/panel/backends/lxqttaskbardummybackend.h b/panel/backends/lxqttaskbardummybackend.h index 535de303b..d4e1ca4bc 100644 --- a/panel/backends/lxqttaskbardummybackend.h +++ b/panel/backends/lxqttaskbardummybackend.h @@ -62,6 +62,10 @@ class LXQtTaskBarDummyBackend : public ILXQtTaskbarAbstractBackend // Panel internal bool isAreaOverlapped(const QRect& area) const override; + // Show Destop + bool isShowingDesktop() const override; + bool showDesktop(bool value) override; + signals: void reloaded(); diff --git a/panel/backends/xcb/lxqttaskbarbackend_x11.cpp b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp index 1d29148c8..822afed51 100644 --- a/panel/backends/xcb/lxqttaskbarbackend_x11.cpp +++ b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp @@ -617,3 +617,14 @@ bool LXQtTaskbarX11Backend::isAreaOverlapped(const QRect &area) const } return false; } + +bool LXQtTaskbarX11Backend::isShowingDesktop() const +{ + return KWindowSystem::showingDesktop(); +} + +bool LXQtTaskbarX11Backend::showDesktop(bool value) +{ + KWindowSystem::setShowingDesktop(value); + return true; +} diff --git a/panel/backends/xcb/lxqttaskbarbackend_x11.h b/panel/backends/xcb/lxqttaskbarbackend_x11.h index 42e84b146..8106cda9f 100644 --- a/panel/backends/xcb/lxqttaskbarbackend_x11.h +++ b/panel/backends/xcb/lxqttaskbarbackend_x11.h @@ -64,6 +64,10 @@ class LXQtTaskbarX11Backend : public ILXQtTaskbarAbstractBackend // Panel internal virtual bool isAreaOverlapped(const QRect& area) const override; + // Show Destop + virtual bool isShowingDesktop() const override; + virtual bool showDesktop(bool value) override; + private slots: void onWindowChanged(WId windowId, NET::Properties prop, NET::Properties2 prop2); void onWindowAdded(WId windowId); From f59269ce897466ed23cd65237d466f8f9f6e0899 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 17:12:28 +0100 Subject: [PATCH 104/165] ShowDesktop: use ILXQtTaskbarAbstractBackend --- plugin-showdesktop/showdesktop.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugin-showdesktop/showdesktop.cpp b/plugin-showdesktop/showdesktop.cpp index 871da62ea..fb69f6067 100644 --- a/plugin-showdesktop/showdesktop.cpp +++ b/plugin-showdesktop/showdesktop.cpp @@ -29,11 +29,12 @@ #include #include #include -#include -#include #include "showdesktop.h" #include "../panel/pluginsettings.h" +#include "../panel/lxqtpanelapplication.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" + #define DEFAULT_SHORTCUT "Control+Alt+D" ShowDesktop::ShowDesktop(const ILXQtPanelPluginStartupInfo &startupInfo) : @@ -69,7 +70,9 @@ void ShowDesktop::shortcutRegistered() void ShowDesktop::toggleShowingDesktop() { - KWindowSystem::setShowingDesktop(!KWindowSystem::showingDesktop()); + LXQtPanelApplication *a = reinterpret_cast(qApp); + auto wmBackend = a->getWMBackend(); + wmBackend->showDesktop(!wmBackend->isShowingDesktop()); } #undef DEFAULT_SHORTCUT From f1a26d4138398a841ee526e13cae825b6bb280f8 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 23 Feb 2024 12:27:28 +0100 Subject: [PATCH 105/165] DesktopSwitch: port to ILXQtTaskbarAbstractBackend - Clarify desktop index range --- .../backends/ilxqttaskbarabstractbackend.cpp | 2 +- panel/backends/ilxqttaskbarabstractbackend.h | 1 + plugin-desktopswitch/desktopswitch.cpp | 141 ++++++++---------- plugin-desktopswitch/desktopswitch.h | 34 ++--- 4 files changed, 79 insertions(+), 99 deletions(-) diff --git a/panel/backends/ilxqttaskbarabstractbackend.cpp b/panel/backends/ilxqttaskbarabstractbackend.cpp index acf33f464..137728263 100644 --- a/panel/backends/ilxqttaskbarabstractbackend.cpp +++ b/panel/backends/ilxqttaskbarabstractbackend.cpp @@ -17,7 +17,7 @@ void ILXQtTaskbarAbstractBackend::moveApplicationToPrevNextDesktop(WId windowId, // Wrap around if (targetWorkspace > count) - targetWorkspace = 1; //TODO: are X11 desktops 1 based? + targetWorkspace = 1; //Ids are 1-based else if (targetWorkspace < 1) targetWorkspace = count; diff --git a/panel/backends/ilxqttaskbarabstractbackend.h b/panel/backends/ilxqttaskbarabstractbackend.h index 7daecfe60..fe3368363 100644 --- a/panel/backends/ilxqttaskbarabstractbackend.h +++ b/panel/backends/ilxqttaskbarabstractbackend.h @@ -45,6 +45,7 @@ class ILXQtTaskbarAbstractBackend : public QObject virtual WId getActiveWindow() const = 0; // Workspaces + // NOTE: indexes are 1-based, 0 means "Show on All desktops" virtual int getWorkspacesCount() const = 0; virtual QString getWorkspaceName(int idx) const = 0; diff --git a/plugin-desktopswitch/desktopswitch.cpp b/plugin-desktopswitch/desktopswitch.cpp index 4bd05e08f..67f6e56eb 100644 --- a/plugin-desktopswitch/desktopswitch.cpp +++ b/plugin-desktopswitch/desktopswitch.cpp @@ -25,6 +25,7 @@ * * END_COMMON_COPYRIGHT_HEADER */ +#include #include #include #include @@ -33,6 +34,8 @@ #include #include +#include "../panel/lxqtpanelapplication.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" #include @@ -40,29 +43,22 @@ #include "desktopswitchbutton.h" #include "desktopswitchconfiguration.h" -#include -#include -#include - -//NOTE: Xlib.h defines Bool which conflicts with QJsonValue::Type enum -#include -#undef Bool - static const QString DEFAULT_SHORTCUT_TEMPLATE(QStringLiteral("Control+F%1")); DesktopSwitch::DesktopSwitch(const ILXQtPanelPluginStartupInfo &startupInfo) : QObject(), ILXQtPanelPlugin(startupInfo), m_pSignalMapper(new QSignalMapper(this)), - m_desktopCount(KX11Extras::numberOfDesktops()), + m_desktopCount(0), mRows(-1), mShowOnlyActive(false), - mDesktops(nullptr), mLabelType(static_cast(-1)) { - auto *x11Application = qGuiApp->nativeInterface(); - Q_ASSERT_X(x11Application, "DesktopSwitch", "Expected X11 connection"); - mDesktops.reset(new NETRootInfo(x11Application->connection(), NET::NumberOfDesktops | NET::CurrentDesktop | NET::DesktopNames, NET::WM2DesktopLayout)); + LXQtPanelApplication *a = reinterpret_cast(qApp); + mBackend = a->getWMBackend(); + + + m_desktopCount = mBackend->getWorkspacesCount(); m_buttons = new QButtonGroup(this); @@ -74,17 +70,16 @@ DesktopSwitch::DesktopSwitch(const ILXQtPanelPluginStartupInfo &startupInfo) : settingsChanged(); - onCurrentDesktopChanged(KX11Extras::currentDesktop()); + onCurrentDesktopChanged(mBackend->getCurrentWorkspace()); QTimer::singleShot(0, this, SLOT(registerShortcuts())); connect(m_buttons, &QButtonGroup::idClicked, this, &DesktopSwitch::setDesktop); - connect(KX11Extras::self(), &KX11Extras::numberOfDesktopsChanged, this, &DesktopSwitch::onNumberOfDesktopsChanged); - connect(KX11Extras::self(), &KX11Extras::currentDesktopChanged, this, &DesktopSwitch::onCurrentDesktopChanged); - connect(KX11Extras::self(), &KX11Extras::desktopNamesChanged, this, &DesktopSwitch::onDesktopNamesChanged); + connect(mBackend, &ILXQtTaskbarAbstractBackend::workspacesCountChanged, this, &DesktopSwitch::onNumberOfDesktopsChanged); + connect(mBackend, &ILXQtTaskbarAbstractBackend::currentWorkspaceChanged, this, &DesktopSwitch::onCurrentDesktopChanged); + connect(mBackend, &ILXQtTaskbarAbstractBackend::workspaceNameChanged, this, &DesktopSwitch::onDesktopNamesChanged); - connect(KX11Extras::self(), static_cast(&KX11Extras::windowChanged), - this, &DesktopSwitch::onWindowChanged); + connect(mBackend, &ILXQtTaskbarAbstractBackend::windowPropertyChanged, this, &DesktopSwitch::onWindowChanged); } void DesktopSwitch::registerShortcuts() @@ -126,19 +121,18 @@ void DesktopSwitch::shortcutRegistered() } } -void DesktopSwitch::onWindowChanged(WId id, NET::Properties properties, NET::Properties2 /*properties2*/) +void DesktopSwitch::onWindowChanged(WId id, int prop) { - if (properties.testFlag(NET::WMState) && isWindowHighlightable(id)) + if (prop == int(LXQtTaskBarWindowProperty::State) && isWindowHighlightable(id)) { - KWindowInfo info = KWindowInfo(id, NET::WMDesktop | NET::WMState); - int desktop = info.desktop(); - if (!info.valid() || info.onAllDesktops()) + int desktop = mBackend->getWindowWorkspace(id); + if (desktop == int(LXQtTaskBarWorkspace::ShowOnAll)) return; else { DesktopSwitchButton *button = static_cast(m_buttons->button(desktop - 1)); if(button) - button->setUrgencyHint(id, info.hasState(NET::DemandsAttention)); + button->setUrgencyHint(id, mBackend->applicationDemandsAttention(id)); } } } @@ -148,7 +142,7 @@ void DesktopSwitch::refresh() const QList btns = m_buttons->buttons(); int i = 0; - const int current_desktop = KX11Extras::currentDesktop(); + const int current_desktop = mBackend->getCurrentWorkspace(); const int current_cnt = btns.count(); const int border = qMin(btns.count(), m_desktopCount); //update existing buttons @@ -156,9 +150,9 @@ void DesktopSwitch::refresh() { DesktopSwitchButton * button = qobject_cast(btns[i]); button->update(i, mLabelType, - KX11Extras::desktopName(i + 1).isEmpty() ? + mBackend->getWorkspaceName(i + 1).isEmpty() ? tr("Desktop %1").arg(i + 1) : - KX11Extras::desktopName(i + 1)); + mBackend->getWorkspaceName(i + 1)); button->setVisible(!mShowOnlyActive || i + 1 == current_desktop); } @@ -167,9 +161,9 @@ void DesktopSwitch::refresh() for ( ; i < m_desktopCount; ++i) { b = new DesktopSwitchButton(&mWidget, i, mLabelType, - KX11Extras::desktopName(i+1).isEmpty() ? + mBackend->getWorkspaceName(i+1).isEmpty() ? tr("Desktop %1").arg(i+1) : - KX11Extras::desktopName(i+1)); + mBackend->getWorkspaceName(i+1)); mWidget.layout()->addWidget(b); m_buttons->addButton(b, i); b->setVisible(!mShowOnlyActive || i + 1 == current_desktop); @@ -185,56 +179,23 @@ void DesktopSwitch::refresh() } } -bool DesktopSwitch::isWindowHighlightable(WId window) +bool DesktopSwitch::isWindowHighlightable(WId) { - // this method was borrowed from the taskbar plugin - QFlags ignoreList; - ignoreList |= NET::DesktopMask; - ignoreList |= NET::DockMask; - ignoreList |= NET::SplashMask; - ignoreList |= NET::ToolbarMask; - ignoreList |= NET::MenuMask; - ignoreList |= NET::PopupMenuMask; - ignoreList |= NET::NotificationMask; - - KWindowInfo info(window, NET::WMWindowType | NET::WMState, NET::WM2TransientFor); - if (!info.valid()) - return false; - - if (NET::typeMatchesMask(info.windowType(NET::AllTypesMask), ignoreList)) - return false; - - if (info.state() & NET::SkipTaskbar) - return false; - - auto *x11Application = qGuiApp->nativeInterface(); - Q_ASSERT_X(x11Application, "DesktopSwitch", "Expected X11 connection"); - WId appRootWindow = XDefaultRootWindow(x11Application->display()); - - // WM_TRANSIENT_FOR hint not set - normal window - WId transFor = info.transientFor(); - if (transFor == 0 || transFor == window || transFor == appRootWindow) - return true; - - info = KWindowInfo(transFor, NET::WMWindowType); - - QFlags normalFlag; - normalFlag |= NET::NormalMask; - normalFlag |= NET::DialogMask; - normalFlag |= NET::UtilityMask; - - return !NET::typeMatchesMask(info.windowType(NET::AllTypesMask), normalFlag); + // Backend should emit signals only for higlightable windows and ignore others + // TODO: check + return true; } DesktopSwitch::~DesktopSwitch() = default; void DesktopSwitch::setDesktop(int desktop) { - KX11Extras::setCurrentDesktop(desktop + 1); + mBackend->setCurrentWorkspace(desktop + 1); } -void DesktopSwitch::onNumberOfDesktopsChanged(int count) +void DesktopSwitch::onNumberOfDesktopsChanged() { + int count = mBackend->getWorkspacesCount(); qDebug() << "Desktop count changed from" << m_desktopCount << "to" << count; m_desktopCount = count; refresh(); @@ -293,11 +254,11 @@ void DesktopSwitch::settingsChanged() int columns = static_cast(ceil(static_cast(m_desktopCount) / mRows)); if (panel()->isHorizontal()) { - mDesktops->setDesktopLayout(NET::OrientationHorizontal, columns, mRows, mWidget.isRightToLeft() ? NET::DesktopLayoutCornerTopRight : NET::DesktopLayoutCornerTopLeft); + //mDesktops->setDesktopLayout(NET::OrientationHorizontal, columns, mRows, mWidget.isRightToLeft() ? NET::DesktopLayoutCornerTopRight : NET::DesktopLayoutCornerTopLeft); } else { - mDesktops->setDesktopLayout(NET::OrientationHorizontal, mRows, columns, mWidget.isRightToLeft() ? NET::DesktopLayoutCornerTopRight : NET::DesktopLayoutCornerTopLeft); + //mDesktops->setDesktopLayout(NET::OrientationHorizontal, mRows, columns, mWidget.isRightToLeft() ? NET::DesktopLayoutCornerTopRight : NET::DesktopLayoutCornerTopLeft); } realign(); // in case it isn't called when the desktop layout changes } @@ -345,9 +306,12 @@ void DesktopSwitchWidget::wheelEvent(QWheelEvent *e) if(abs(m_mouseWheelThresholdCounter) < 100) return; - int max = KX11Extras::numberOfDesktops(); + LXQtPanelApplication *a = reinterpret_cast(qApp); + auto wmBackend = a->getWMBackend(); + + int max = wmBackend->getWorkspacesCount(); int delta = rotationSteps < 0 ? 1 : -1; - int current = KX11Extras::currentDesktop() + delta; + int current = wmBackend->getCurrentWorkspace() + delta; if (current > max){ current = 1; @@ -356,5 +320,32 @@ void DesktopSwitchWidget::wheelEvent(QWheelEvent *e) current = max; m_mouseWheelThresholdCounter = 0; - KX11Extras::setCurrentDesktop(current); + wmBackend->setCurrentWorkspace(current); +} + +ILXQtPanelPlugin *DesktopSwitchPluginLibrary::instance(const ILXQtPanelPluginStartupInfo &startupInfo) const +{ + return new DesktopSwitch{startupInfo}; + + //TODO: detect dummy backend and show unsupported message? + // Or instead remove it and make just a message box at application start + //return new DesktopSwitchUnsupported{startupInfo}; +} + +DesktopSwitchUnsupported::DesktopSwitchUnsupported(const ILXQtPanelPluginStartupInfo &startupInfo) + : ILXQtPanelPlugin(startupInfo) + , mLabel(new QLabel(tr("n/a"))) +{ + mLabel->setToolTip(tr("DesktopSwitch is unsupported on current platform: %1").arg(QGuiApplication::platformName())); +} + +DesktopSwitchUnsupported::~DesktopSwitchUnsupported() +{ + delete mLabel; + mLabel = nullptr; +} + +QWidget *DesktopSwitchUnsupported::widget() +{ + return mLabel; } diff --git a/plugin-desktopswitch/desktopswitch.h b/plugin-desktopswitch/desktopswitch.h index 93a930dc2..b57b44c32 100644 --- a/plugin-desktopswitch/desktopswitch.h +++ b/plugin-desktopswitch/desktopswitch.h @@ -30,21 +30,19 @@ #define DESKTOPSWITCH_H #include "../panel/ilxqtpanelplugin.h" -#include #include -#include -#include -#include #include "desktopswitchbutton.h" +class QLabel; class QSignalMapper; class QButtonGroup; -class NETRootInfo; namespace LXQt { class GridLayout; } +class ILXQtTaskbarAbstractBackend; + class DesktopSwitchWidget: public QFrame { Q_OBJECT @@ -85,7 +83,7 @@ class DesktopSwitch : public QObject, public ILXQtPanelPlugin LXQt::GridLayout *mLayout; int mRows; bool mShowOnlyActive; - std::unique_ptr mDesktops; + ILXQtTaskbarAbstractBackend *mBackend; DesktopSwitchButton::LabelType mLabelType; void refresh(); @@ -93,31 +91,27 @@ class DesktopSwitch : public QObject, public ILXQtPanelPlugin private slots: void setDesktop(int desktop); - void onNumberOfDesktopsChanged(int); + void onNumberOfDesktopsChanged(); void onCurrentDesktopChanged(int); void onDesktopNamesChanged(); virtual void settingsChanged(); void registerShortcuts(); void shortcutRegistered(); - void onWindowChanged(WId id, NET::Properties properties, NET::Properties2 properties2); + void onWindowChanged(WId id, int prop); }; class DesktopSwitchUnsupported : public QObject, public ILXQtPanelPlugin { Q_OBJECT public: - DesktopSwitchUnsupported(const ILXQtPanelPluginStartupInfo &startupInfo) - : ILXQtPanelPlugin(startupInfo) - , mLabel{tr("n/a")} - { - mLabel.setToolTip(tr("DesktopSwitch is unsupported on current platform: %1").arg(QGuiApplication::platformName())); - } + DesktopSwitchUnsupported(const ILXQtPanelPluginStartupInfo &startupInfo); + ~DesktopSwitchUnsupported(); QString themeId() const { return QStringLiteral("DesktopSwitchUnsupported"); } - QWidget *widget() { return &mLabel; } + QWidget *widget(); bool isSeparate() const { return true; } private: - QLabel mLabel; + QLabel *mLabel; }; class DesktopSwitchPluginLibrary: public QObject, public ILXQtPanelPluginLibrary @@ -126,13 +120,7 @@ class DesktopSwitchPluginLibrary: public QObject, public ILXQtPanelPluginLibrary // Q_PLUGIN_METADATA(IID "lxqt.org/Panel/PluginInterface/3.0") Q_INTERFACES(ILXQtPanelPluginLibrary) public: - ILXQtPanelPlugin *instance(const ILXQtPanelPluginStartupInfo &startupInfo) const - { - if (QGuiApplication::platformName() == QStringLiteral("xcb")) - return new DesktopSwitch{startupInfo}; - else - return new DesktopSwitchUnsupported{startupInfo}; - } + ILXQtPanelPlugin *instance(const ILXQtPanelPluginStartupInfo &startupInfo) const; }; #endif From 72a75e04c0d821ed076ab93b9fd394498b1baecf Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 23 Feb 2024 13:39:46 +0100 Subject: [PATCH 106/165] LXQtTaskbarConfiguration: port to ILXQtTaskBarAbstractBackend --- plugin-taskbar/lxqttaskbarconfiguration.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/plugin-taskbar/lxqttaskbarconfiguration.cpp b/plugin-taskbar/lxqttaskbarconfiguration.cpp index b346bb69d..0dd528e51 100644 --- a/plugin-taskbar/lxqttaskbarconfiguration.cpp +++ b/plugin-taskbar/lxqttaskbarconfiguration.cpp @@ -29,7 +29,9 @@ #include "lxqttaskbarconfiguration.h" #include "ui_lxqttaskbarconfiguration.h" -#include + +#include "../panel/lxqtpanelapplication.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" LXQtTaskbarConfiguration::LXQtTaskbarConfiguration(PluginSettings *settings, QWidget *parent): LXQtPanelPluginConfigDialog(settings, parent), @@ -52,11 +54,14 @@ LXQtTaskbarConfiguration::LXQtTaskbarConfiguration(PluginSettings *settings, QWi ui->wheelEventsActionCB->addItem(tr("Scroll up to move to next desktop, down to previous"), 4); ui->wheelEventsActionCB->addItem(tr("Scroll up to move to previous desktop, down to next"), 5); + LXQtPanelApplication *a = reinterpret_cast(qApp); + auto wmBackend = a->getWMBackend(); + ui->showDesktopNumCB->addItem(tr("Current"), 0); //Note: in KWindowSystem desktops are numbered from 1..N - const int desk_cnt = KX11Extras::numberOfDesktops(); + const int desk_cnt = wmBackend->getWorkspacesCount(); for (int i = 1; desk_cnt >= i; ++i) - ui->showDesktopNumCB->addItem(QString(QStringLiteral("%1 - %2")).arg(i).arg(KX11Extras::desktopName(i)), i); + ui->showDesktopNumCB->addItem(QString(QStringLiteral("%1 - %2")).arg(i).arg(wmBackend->getWorkspaceName(i)), i); loadSettings(); ui->ungroupedNextToExistingCB->setEnabled(!(ui->groupingGB->isChecked())); From 05fc7422203a2d12c5869390bb645627132d6cd5 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 23 Feb 2024 14:07:05 +0100 Subject: [PATCH 107/165] DesktopSwitchConfiguration: port to ILXQtTaskBarAbstractBackend TODO TODO: this will disable changing desktop names --- .../desktopswitchconfiguration.cpp | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/plugin-desktopswitch/desktopswitchconfiguration.cpp b/plugin-desktopswitch/desktopswitchconfiguration.cpp index 943dbdd6b..a6edb41a6 100644 --- a/plugin-desktopswitch/desktopswitchconfiguration.cpp +++ b/plugin-desktopswitch/desktopswitchconfiguration.cpp @@ -26,8 +26,9 @@ #include "desktopswitchconfiguration.h" #include "ui_desktopswitchconfiguration.h" -#include -#include + +#include "../panel/lxqtpanelapplication.h" +#include "../panel/backends/ilxqttaskbarabstractbackend.h" DesktopSwitchConfiguration::DesktopSwitchConfiguration(PluginSettings *settings, QWidget *parent) : LXQtPanelPluginConfigDialog(settings, parent), @@ -64,18 +65,25 @@ void DesktopSwitchConfiguration::loadSettings() void DesktopSwitchConfiguration::loadDesktopsNames() { - int n = KX11Extras::numberOfDesktops(); + LXQtPanelApplication *a = reinterpret_cast(qApp); + auto wmBackend = a->getWMBackend(); + + int n = wmBackend->getWorkspacesCount(); for (int i = 1; i <= n; i++) { - QLineEdit *edit = new QLineEdit(KX11Extras::desktopName(i), this); + QLineEdit *edit = new QLineEdit(wmBackend->getWorkspaceName(i), this); ((QFormLayout *) ui->namesGroupBox->layout())->addRow(tr("Desktop %1:").arg(i), edit); + //TODO: on Wayland we cannot set desktop names in a standart way + // On KWin we could use DBus org.kde.KWin as done by kcm_kwin_virtualdesktops + edit->setReadOnly(true); + // C++11 rocks! - QTimer *timer = new QTimer(this); - timer->setInterval(400); - timer->setSingleShot(true); - connect(timer, &QTimer::timeout, this, [=] { KX11Extras::setDesktopName(i, edit->text()); }); - connect(edit, &QLineEdit::textEdited, this, [=] { timer->start(); }); + //QTimer *timer = new QTimer(this); + //timer->setInterval(400); + //timer->setSingleShot(true); + //connect(timer, &QTimer::timeout, this, [=] { KX11Extras::setDesktopName(i, edit->text()); }); + //connect(edit, &QLineEdit::textEdited, this, [=] { timer->start(); }); } } From 8b78adda8784fe52e3d5a4154cf71aef71f018ab Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 17:41:16 +0100 Subject: [PATCH 108/165] TaskBar: consider initial windows --- plugin-taskbar/lxqttaskbar.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugin-taskbar/lxqttaskbar.cpp b/plugin-taskbar/lxqttaskbar.cpp index c306d796e..43090c396 100644 --- a/plugin-taskbar/lxqttaskbar.cpp +++ b/plugin-taskbar/lxqttaskbar.cpp @@ -107,6 +107,13 @@ LXQtTaskBar::LXQtTaskBar(ILXQtPanelPlugin *plugin, QWidget *parent) : connect(mBackend, &ILXQtTaskbarAbstractBackend::windowPropertyChanged, this, &LXQtTaskBar::onWindowChanged); connect(mBackend, &ILXQtTaskbarAbstractBackend::windowAdded, this, &LXQtTaskBar::onWindowAdded); connect(mBackend, &ILXQtTaskbarAbstractBackend::windowRemoved, this, &LXQtTaskBar::onWindowRemoved); + + // Consider already fetched windows + const auto initialWindows = mBackend->getCurrentWindows(); + for(WId windowId : initialWindows) + { + onWindowAdded(windowId); + } } /************************************************ From 84366c2fd9854e039e33caf15a0467098376e0e9 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 17 Feb 2024 19:18:09 +0100 Subject: [PATCH 109/165] TaskBar: add experimental KWin Wayland backend NOTE: works only on KWin - Choose backend at runtime - Windows filter logic is re-evaluated on window property changes --- panel/CMakeLists.txt | 20 + panel/backends/CMakeLists.txt | 1 + panel/backends/wayland/CMakeLists.txt | 1 + .../wayland/lxqttaskbarbackendwayland.cpp | 626 +++++++++ .../wayland/lxqttaskbarbackendwayland.h | 87 ++ .../lxqttaskbarplasmawindowmanagment.cpp | 1146 +++++++++++++++++ .../lxqttaskbarplasmawindowmanagment.h | 154 +++ .../protocols/plasma-window-management.xml | 425 ++++++ panel/lxqtpanelapplication.cpp | 3 + 9 files changed, 2463 insertions(+) create mode 100644 panel/backends/wayland/CMakeLists.txt create mode 100644 panel/backends/wayland/lxqttaskbarbackendwayland.cpp create mode 100644 panel/backends/wayland/lxqttaskbarbackendwayland.h create mode 100644 panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp create mode 100644 panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h create mode 100644 panel/backends/wayland/protocols/plasma-window-management.xml diff --git a/panel/CMakeLists.txt b/panel/CMakeLists.txt index f088fe33a..315b34dc3 100644 --- a/panel/CMakeLists.txt +++ b/panel/CMakeLists.txt @@ -3,6 +3,10 @@ set(PROJECT lxqt-panel) # TODO add_subdirectory(backends) +# TODO: move to backend folder +find_package(Qt6 ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS WaylandClient Concurrent) + + set(PRIV_HEADERS panelpluginsmodel.h windownotifier.h @@ -26,7 +30,11 @@ set(PRIV_HEADERS backends/lxqttaskbartypes.h backends/lxqttaskbardummybackend.h + backends/xcb/lxqttaskbarbackend_x11.h + + backends/wayland/lxqttaskbarbackendwayland.h + backends/wayland/lxqttaskbarplasmawindowmanagment.h ) # using LXQt namespace in the public headers. @@ -61,7 +69,11 @@ set(SOURCES backends/ilxqttaskbarabstractbackend.cpp backends/lxqttaskbardummybackend.cpp + backends/xcb/lxqttaskbarbackend_x11.cpp + + backends/wayland/lxqttaskbarbackendwayland.cpp + backends/wayland/lxqttaskbarplasmawindowmanagment.cpp ) set(UI @@ -116,11 +128,19 @@ add_executable(${PROJECT} ${UI} ) +# TODO: move to backend folder +qt6_generate_wayland_protocol_client_sources(${PROJECT} +FILES + ${CMAKE_CURRENT_SOURCE_DIR}/backends/wayland/protocols/plasma-window-management.xml +) + target_link_libraries(${PROJECT} ${LIBRARIES} ${QTX_LIBRARIES} KF6::WindowSystem LayerShellQt::Interface + Qt6::WaylandClient # TODO: Move to backend folder + Qt6::Concurrent ${STATIC_PLUGINS} ) diff --git a/panel/backends/CMakeLists.txt b/panel/backends/CMakeLists.txt index 8f34a3c67..f1915b823 100644 --- a/panel/backends/CMakeLists.txt +++ b/panel/backends/CMakeLists.txt @@ -1 +1,2 @@ +add_subdirectory(wayland) add_subdirectory(xcb) diff --git a/panel/backends/wayland/CMakeLists.txt b/panel/backends/wayland/CMakeLists.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/panel/backends/wayland/CMakeLists.txt @@ -0,0 +1 @@ + diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp new file mode 100644 index 000000000..a2a415504 --- /dev/null +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -0,0 +1,626 @@ +#include "lxqttaskbarbackendwayland.h" + +#include "lxqttaskbarplasmawindowmanagment.h" + +#include +#include +#include + +auto findWindow(const std::vector>& windows, LXQtTaskBarPlasmaWindow *window) +{ + //TODO: use algorithms + auto end = windows.end(); + for(auto it = windows.begin(); it != end; it++) + { + if((*it).get() == window) + { + return it; + } + } + + return windows.end(); +} + +LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) : + ILXQtTaskbarAbstractBackend(parent) +{ + m_managment.reset(new LXQtTaskBarPlasmaWindowManagment); + + connect(m_managment.get(), &LXQtTaskBarPlasmaWindowManagment::windowCreated, this, [this](LXQtTaskBarPlasmaWindow *window) { + connect(window, &LXQtTaskBarPlasmaWindow::initialStateDone, this, [this, window] { + addWindow(window); + }); + }); + + // connect(m_managment.get(), &LXQtTaskBarPlasmaWindowManagment::stackingOrderChanged, + // this, [this](const QString &order) { + // // stackingOrder = order.split(QLatin1Char(';')); + // // for (const auto &window : std::as_const(windows)) { + // // this->dataChanged(window.get(), StackingOrder); + // // } + // }); +} + +bool LXQtTaskbarWaylandBackend::supportsAction(WId windowId, LXQtTaskBarBackendAction action) const +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + LXQtTaskBarPlasmaWindow::state state; + + switch (action) + { + case LXQtTaskBarBackendAction::Move: + state = LXQtTaskBarPlasmaWindow::state::state_movable; + break; + + case LXQtTaskBarBackendAction::Resize: + state = LXQtTaskBarPlasmaWindow::state::state_resizable; + break; + + case LXQtTaskBarBackendAction::Maximize: + state = LXQtTaskBarPlasmaWindow::state::state_maximizable; + break; + + case LXQtTaskBarBackendAction::Minimize: + state = LXQtTaskBarPlasmaWindow::state::state_minimizable; + break; + + case LXQtTaskBarBackendAction::RollUp: + state = LXQtTaskBarPlasmaWindow::state::state_shadeable; + break; + + case LXQtTaskBarBackendAction::FullScreen: + state = LXQtTaskBarPlasmaWindow::state::state_fullscreenable; + break; + + default: + return false; + } + + return window->windowState.testFlag(state); +} + +bool LXQtTaskbarWaylandBackend::reloadWindows() +{ + return false; //TODO +} + +QVector LXQtTaskbarWaylandBackend::getCurrentWindows() const +{ + QVector wids(windows.size()); + for(const auto& window : std::as_const(windows)) + { + wids << window->getWindowId(); + } + return wids; +} + +QString LXQtTaskbarWaylandBackend::getWindowTitle(WId windowId) const +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return QString(); + + return window->title; +} + +bool LXQtTaskbarWaylandBackend::applicationDemandsAttention(WId windowId) const +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + return window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_demands_attention) || transientsDemandingAttention.contains(window); +} + +QIcon LXQtTaskbarWaylandBackend::getApplicationIcon(WId windowId, int devicePixels) const +{ + Q_UNUSED(devicePixels) + + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return QIcon(); + + return window->icon; +} + +QString LXQtTaskbarWaylandBackend::getWindowClass(WId windowId) const +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return QString(); + return window->appId; +} + +LXQtTaskBarWindowLayer LXQtTaskbarWaylandBackend::getWindowLayer(WId windowId) const +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return LXQtTaskBarWindowLayer::Normal; + + if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_keep_above)) + return LXQtTaskBarWindowLayer::KeepAbove; + + if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_keep_below)) + return LXQtTaskBarWindowLayer::KeepBelow; + + return LXQtTaskBarWindowLayer::Normal; +} + +bool LXQtTaskbarWaylandBackend::setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + if(getWindowLayer(windowId) == layer) + return true; //TODO: make more efficient + + LXQtTaskBarPlasmaWindow::state plasmaState = LXQtTaskBarPlasmaWindow::state::state_keep_above; + switch (layer) + { + case LXQtTaskBarWindowLayer::Normal: + case LXQtTaskBarWindowLayer::KeepAbove: + break; + case LXQtTaskBarWindowLayer::KeepBelow: + plasmaState = LXQtTaskBarPlasmaWindow::state::state_keep_below; + break; + default: + return false; + } + + window->set_state(plasmaState, layer == LXQtTaskBarWindowLayer::Normal ? 0 : plasmaState); + return false; +} + +LXQtTaskBarWindowState LXQtTaskbarWaylandBackend::getWindowState(WId windowId) const +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return LXQtTaskBarWindowState::Normal; + + if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_minimized)) + return LXQtTaskBarWindowState::Hidden; + if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_maximizable)) + return LXQtTaskBarWindowState::Maximized; + if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_shaded)) + return LXQtTaskBarWindowState::RolledUp; + if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_fullscreen)) + return LXQtTaskBarWindowState::FullScreen; + + return LXQtTaskBarWindowState::Normal; +} + +bool LXQtTaskbarWaylandBackend::setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set) +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + LXQtTaskBarPlasmaWindow::state plasmaState; + switch (state) + { + case LXQtTaskBarWindowState::Minimized: + { + plasmaState = LXQtTaskBarPlasmaWindow::state::state_minimized; + break; + } + case LXQtTaskBarWindowState::Maximized: + case LXQtTaskBarWindowState::MaximizedVertically: + case LXQtTaskBarWindowState::MaximizedHorizontally: + { + plasmaState = LXQtTaskBarPlasmaWindow::state::state_maximized; + break; + } + case LXQtTaskBarWindowState::Normal: + { + plasmaState = LXQtTaskBarPlasmaWindow::state::state_maximized; + set = !set; //TODO: correct + break; + } + case LXQtTaskBarWindowState::RolledUp: + { + plasmaState = LXQtTaskBarPlasmaWindow::state::state_shaded; + break; + } + default: + return false; + } + + window->set_state(plasmaState, set ? plasmaState : 0); + return true; +} + +bool LXQtTaskbarWaylandBackend::isWindowActive(WId windowId) const +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + return activeWindow == window || window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_active); +} + +bool LXQtTaskbarWaylandBackend::raiseWindow(WId windowId, bool onCurrentWorkSpace) +{ + Q_UNUSED(onCurrentWorkSpace) //TODO + + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + // Pull forward any transient demanding attention. + if (auto *transientDemandingAttention = transientsDemandingAttention.value(window)) + { + window = transientDemandingAttention; + } + else + { + // TODO Shouldn't KWin take care of that? + // Bringing a transient to the front usually brings its parent with it + // but focus is not handled properly. + // TODO take into account d->lastActivation instead + // of just taking the first one. + while (transients.key(window)) + { + window = transients.key(window); + } + } + + window->set_state(LXQtTaskBarPlasmaWindow::state::state_active, LXQtTaskBarPlasmaWindow::state::state_active); + return true; +} + +bool LXQtTaskbarWaylandBackend::closeWindow(WId windowId) +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + window->close(); + return true; +} + +WId LXQtTaskbarWaylandBackend::getActiveWindow() const +{ + if(activeWindow) + return activeWindow->getWindowId(); + return 0; +} + +int LXQtTaskbarWaylandBackend::getWorkspacesCount() const +{ + return 1; //TODO +} + +QString LXQtTaskbarWaylandBackend::getWorkspaceName(int idx) const +{ + return QStringLiteral("TestWorkspace"); +} + +int LXQtTaskbarWaylandBackend::getCurrentWorkspace() const +{ + return 0; //TODO +} + +bool LXQtTaskbarWaylandBackend::setCurrentWorkspace(int idx) +{ + Q_UNUSED(idx) + return false; //TODO +} + +int LXQtTaskbarWaylandBackend::getWindowWorkspace(WId windowId) const +{ + Q_UNUSED(windowId) + return 0; //TODO +} + +bool LXQtTaskbarWaylandBackend::setWindowOnWorkspace(WId windowId, int idx) +{ + Q_UNUSED(windowId) + Q_UNUSED(idx) + return false; //TODO +} + +void LXQtTaskbarWaylandBackend::moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) +{ + +} + +bool LXQtTaskbarWaylandBackend::isWindowOnScreen(QScreen *screen, WId windowId) const +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + return screen->geometry().intersects(window->geometry); +} + +void LXQtTaskbarWaylandBackend::moveApplication(WId windowId) +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return; + + window->set_state(LXQtTaskBarPlasmaWindow::state::state_active, LXQtTaskBarPlasmaWindow::state::state_active); + window->request_move(); +} + +void LXQtTaskbarWaylandBackend::resizeApplication(WId windowId) +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return; + + window->set_state(LXQtTaskBarPlasmaWindow::state::state_active, LXQtTaskBarPlasmaWindow::state::state_active); + window->request_resize(); +} + +void LXQtTaskbarWaylandBackend::refreshIconGeometry(WId windowId, const QRect &geom) +{ + +} + +bool LXQtTaskbarWaylandBackend::isAreaOverlapped(const QRect &area) const +{ + for(auto &window : std::as_const(windows)) + { + if(window->geometry.intersects(area)) + return true; + } + return false; +} + +void LXQtTaskbarWaylandBackend::addWindow(LXQtTaskBarPlasmaWindow *window) +{ + if (findWindow(windows, window) != windows.end() || transients.contains(window)) + { + return; + } + + auto removeWindow = [window, this] + { + auto it = findWindow(windows, window); + if (it != windows.end()) + { + if(window->acceptedInTaskBar) + emit windowRemoved(window->getWindowId()); + windows.erase(it); + transientsDemandingAttention.remove(window); + lastActivated.remove(window); + } + else + { + // Could be a transient. + // Removing a transient might change the demands attention state of the leader. + if (transients.remove(window)) + { + if (LXQtTaskBarPlasmaWindow *leader = transientsDemandingAttention.key(window)) { + transientsDemandingAttention.remove(leader, window); + emit windowPropertyChanged(leader->getWindowId(), int(LXQtTaskBarWindowProperty::Urgency)); + } + } + } + + if (activeWindow == window) + { + activeWindow = nullptr; + emit activeWindowChanged(0); + } + }; + + connect(window, &LXQtTaskBarPlasmaWindow::unmapped, this, removeWindow); + + connect(window, &LXQtTaskBarPlasmaWindow::titleChanged, this, [window, this] { + updateWindowAcceptance(window); + if(window->acceptedInTaskBar) + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Title)); + }); + + connect(window, &LXQtTaskBarPlasmaWindow::iconChanged, this, [window, this] { + updateWindowAcceptance(window); + if(window->acceptedInTaskBar) + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Icon)); + }); + + connect(window, &LXQtTaskBarPlasmaWindow::geometryChanged, this, [window, this] { + updateWindowAcceptance(window); + if(window->acceptedInTaskBar) + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Geometry)); + }); + + connect(window, &LXQtTaskBarPlasmaWindow::appIdChanged, this, [window, this] { + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::WindowClass)); + }); + + if (window->windowState & LXQtTaskBarPlasmaWindow::state::state_active) { + LXQtTaskBarPlasmaWindow *effectiveActive = window; + while (effectiveActive->parentWindow) { + effectiveActive = effectiveActive->parentWindow; + } + + lastActivated[effectiveActive] = QTime::currentTime(); + activeWindow = effectiveActive; + } + + connect(window, &LXQtTaskBarPlasmaWindow::activeChanged, this, [window, this] { + const bool active = window->windowState & LXQtTaskBarPlasmaWindow::state::state_active; + + LXQtTaskBarPlasmaWindow *effectiveWindow = window; + + while (effectiveWindow->parentWindow) + { + effectiveWindow = effectiveWindow->parentWindow; + } + + if (active) + { + lastActivated[effectiveWindow] = QTime::currentTime(); + + if (activeWindow != effectiveWindow) + { + activeWindow = effectiveWindow; + emit activeWindowChanged(activeWindow->getWindowId()); + } + } + else + { + if (activeWindow == effectiveWindow) + { + activeWindow = nullptr; + emit activeWindowChanged(0); + } + } + }); + + connect(window, &LXQtTaskBarPlasmaWindow::parentWindowChanged, this, [window, this] { + LXQtTaskBarPlasmaWindow *leader = window->parentWindow.data(); + + // Migrate demanding attention to new leader. + if (window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_demands_attention)) + { + if (auto *oldLeader = transientsDemandingAttention.key(window)) + { + if (window->parentWindow != oldLeader) + { + transientsDemandingAttention.remove(oldLeader, window); + transientsDemandingAttention.insert(leader, window); + emit windowPropertyChanged(oldLeader->getWindowId(), int(LXQtTaskBarWindowProperty::Urgency)); + emit windowPropertyChanged(leader->getWindowId(), int(LXQtTaskBarWindowProperty::Urgency)); + } + } + } + + if (transients.remove(window)) + { + if (leader) + { + // leader change. + transients.insert(window, leader); + } + else + { + // lost a leader, add to regular windows list. + Q_ASSERT(findWindow(windows, window) == windows.end()); + + windows.emplace_back(window); + } + } + else if (leader) + { + // gained a leader, remove from regular windows list. + auto it = findWindow(windows, window); + Q_ASSERT(it != windows.end()); + + windows.erase(it); + lastActivated.remove(window); + } + }); + + auto stateChanged = [window, this] { + updateWindowAcceptance(window); + if(window->acceptedInTaskBar) + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::State)); + }; + + connect(window, &LXQtTaskBarPlasmaWindow::fullscreenChanged, this, stateChanged); + + connect(window, &LXQtTaskBarPlasmaWindow::maximizedChanged, this, stateChanged); + + connect(window, &LXQtTaskBarPlasmaWindow::minimizedChanged, this, stateChanged); + + connect(window, &LXQtTaskBarPlasmaWindow::shadedChanged, this, stateChanged); + + auto workspaceChanged = [window, this] { + updateWindowAcceptance(window); + if(window->acceptedInTaskBar) + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Workspace)); + }; + + connect(window, &LXQtTaskBarPlasmaWindow::virtualDesktopEntered, this, workspaceChanged); + connect(window, &LXQtTaskBarPlasmaWindow::virtualDesktopLeft, this, workspaceChanged); + + connect(window, &LXQtTaskBarPlasmaWindow::demandsAttentionChanged, this, [window, this] { + // Changes to a transient's state might change demands attention state for leader. + if (auto *leader = transients.value(window)) + { + if (window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_demands_attention)) + { + if (!transientsDemandingAttention.values(leader).contains(window)) + { + transientsDemandingAttention.insert(leader, window); + emit windowPropertyChanged(leader->getWindowId(), int(LXQtTaskBarWindowProperty::Urgency)); + } + } + else if (transientsDemandingAttention.remove(window)) + { + emit windowPropertyChanged(leader->getWindowId(), int(LXQtTaskBarWindowProperty::Urgency)); + } + } + else + { + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Urgency)); + } + }); + + connect(window, &LXQtTaskBarPlasmaWindow::skipTaskbarChanged, this, [window, this] { + updateWindowAcceptance(window); + }); + + // QObject::connect(window, &PlasmaWindow::applicationMenuChanged, q, [window, this] { + // this->dataChanged(window, QList{ApplicationMenuServiceName, ApplicationMenuObjectPath}); + // }); + + // Handle transient. + if (LXQtTaskBarPlasmaWindow *leader = window->parentWindow.data()) + { + transients.insert(window, leader); + + // Update demands attention state for leader. + if (window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_demands_attention)) + { + transientsDemandingAttention.insert(leader, window); + if(leader->acceptedInTaskBar) + emit windowPropertyChanged(leader->getWindowId(), int(LXQtTaskBarWindowProperty::Urgency)); + } + } + else + { + windows.emplace_back(window); + updateWindowAcceptance(window); + } +} + +bool LXQtTaskbarWaylandBackend::acceptWindow(LXQtTaskBarPlasmaWindow *window) const +{ + if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_skiptaskbar)) + return false; + + return true; +} + +void LXQtTaskbarWaylandBackend::updateWindowAcceptance(LXQtTaskBarPlasmaWindow *window) +{ + if(!window->acceptedInTaskBar && acceptWindow(window)) + { + window->acceptedInTaskBar = true; + emit windowAdded(window->getWindowId()); + } + else if(window->acceptedInTaskBar && !acceptWindow(window)) + { + window->acceptedInTaskBar = false; + emit windowRemoved(window->getWindowId()); + } +} + +LXQtTaskBarPlasmaWindow *LXQtTaskbarWaylandBackend::getWindow(WId windowId) const +{ + for(auto &window : std::as_const(windows)) + { + if(window->getWindowId() == windowId) + return window.get(); + } + + return nullptr; +} diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.h b/panel/backends/wayland/lxqttaskbarbackendwayland.h new file mode 100644 index 000000000..553a7e585 --- /dev/null +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.h @@ -0,0 +1,87 @@ +#ifndef LXQTTASKBARBACKENDWAYLAND_H +#define LXQTTASKBARBACKENDWAYLAND_H + +#include "../ilxqttaskbarabstractbackend.h" + +#include +#include + +class LXQtTaskBarPlasmaWindow; +class LXQtTaskBarPlasmaWindowManagment; + + +class LXQtTaskbarWaylandBackend : public ILXQtTaskbarAbstractBackend +{ + Q_OBJECT + +public: + explicit LXQtTaskbarWaylandBackend(QObject *parent = nullptr); + + // Backend + virtual bool supportsAction(WId windowId, LXQtTaskBarBackendAction action) const override; + + // Windows + virtual bool reloadWindows() override; + + virtual QVector getCurrentWindows() const override; + virtual QString getWindowTitle(WId windowId) const override; + virtual bool applicationDemandsAttention(WId windowId) const override; + virtual QIcon getApplicationIcon(WId windowId, int devicePixels) const override; + virtual QString getWindowClass(WId windowId) const override; + + virtual LXQtTaskBarWindowLayer getWindowLayer(WId windowId) const override; + virtual bool setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) override; + + virtual LXQtTaskBarWindowState getWindowState(WId windowId) const override; + virtual bool setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set) override; + + virtual bool isWindowActive(WId windowId) const override; + virtual bool raiseWindow(WId windowId, bool onCurrentWorkSpace) override; + + virtual bool closeWindow(WId windowId) override; + + virtual WId getActiveWindow() const override; + + // Workspaces + virtual int getWorkspacesCount() const override; + virtual QString getWorkspaceName(int idx) const override; + + virtual int getCurrentWorkspace() const override; + virtual bool setCurrentWorkspace(int idx) override; + + virtual int getWindowWorkspace(WId windowId) const override; + virtual bool setWindowOnWorkspace(WId windowId, int idx) override; + + virtual void moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) override; + + virtual bool isWindowOnScreen(QScreen *screen, WId windowId) const override; + + // X11 Specific + virtual void moveApplication(WId windowId) override; + virtual void resizeApplication(WId windowId) override; + + virtual void refreshIconGeometry(WId windowId, const QRect &geom) override; + + // Panel internal + virtual bool isAreaOverlapped(const QRect& area) const override; + +private: + void addWindow(LXQtTaskBarPlasmaWindow *window); + bool acceptWindow(LXQtTaskBarPlasmaWindow *window) const; + void updateWindowAcceptance(LXQtTaskBarPlasmaWindow *window); + +private: + LXQtTaskBarPlasmaWindow *getWindow(WId windowId) const; + + std::unique_ptr m_managment; + + QHash lastActivated; + LXQtTaskBarPlasmaWindow *activeWindow = nullptr; + std::vector> windows; + // key=transient child, value=leader + QHash transients; + // key=leader, values=transient children + QMultiHash transientsDemandingAttention; +}; + +#endif // LXQTTASKBARBACKENDWAYLAND_H diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp new file mode 100644 index 000000000..0a282eadd --- /dev/null +++ b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp @@ -0,0 +1,1146 @@ +#include "lxqttaskbarplasmawindowmanagment.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +// QUuid WaylandTasksModel::Private::uuid = QUuid::createUuid(); + +// WaylandTasksModel::Private::Private(WaylandTasksModel *q) +// : q(q) +// { +// } + +// void WaylandTasksModel::Private::init() +// { +// auto clearCacheAndRefresh = [this] { +// if (windows.empty()) { +// return; +// } + +// appDataCache.clear(); + +// // Emit changes of all roles satisfied from app data cache. +// Q_EMIT q->dataChanged(q->index(0, 0), +// q->index(windows.size() - 1, 0), +// QList{Qt::DecorationRole, +// AbstractTasksModel::AppId, +// AbstractTasksModel::AppName, +// AbstractTasksModel::GenericName, +// AbstractTasksModel::LauncherUrl, +// AbstractTasksModel::LauncherUrlWithoutIcon, +// AbstractTasksModel::CanLaunchNewInstance, +// AbstractTasksModel::SkipTaskbar}); +// }; + +// rulesConfig = KSharedConfig::openConfig(QStringLiteral("taskmanagerrulesrc")); +// configWatcher = new KDirWatch(q); + +// for (const QString &location : QStandardPaths::standardLocations(QStandardPaths::ConfigLocation)) { +// configWatcher->addFile(location + QLatin1String("/taskmanagerrulesrc")); +// } + +// auto rulesConfigChange = [this, clearCacheAndRefresh] { +// rulesConfig->reparseConfiguration(); +// clearCacheAndRefresh(); +// }; + +// QObject::connect(configWatcher, &KDirWatch::dirty, rulesConfigChange); +// QObject::connect(configWatcher, &KDirWatch::created, rulesConfigChange); +// QObject::connect(configWatcher, &KDirWatch::deleted, rulesConfigChange); + +// virtualDesktopInfo = new VirtualDesktopInfo(q); + +// initWayland(); +// } + +// void WaylandTasksModel::Private::initWayland() +// { +// if (!KWindowSystem::isPlatformWayland()) { +// return; +// } + +// windowManagement = std::make_unique(); + +// QObject::connect(windowManagement.get(), &PlasmaWindowManagement::activeChanged, q, [this] { +// q->beginResetModel(); +// windows.clear(); +// q->endResetModel(); +// }); + +// QObject::connect(windowManagement.get(), &PlasmaWindowManagement::windowCreated, q, [this](PlasmaWindow *window) { +// connect(window, &PlasmaWindow::initialStateDone, q, [this, window] { +// addWindow(window); +// }); +// }); + +// QObject::connect(windowManagement.get(), &PlasmaWindowManagement::stackingOrderChanged, q, [this](const QString &order) { +// stackingOrder = order.split(QLatin1Char(';')); +// for (const auto &window : std::as_const(windows)) { +// this->dataChanged(window.get(), StackingOrder); +// } +// }); +// } + +// auto WaylandTasksModel::Private::findWindow(PlasmaWindow *window) const +// { +// return std::find_if(windows.begin(), windows.end(), [window](const std::unique_ptr &candidate) { +// return candidate.get() == window; +// }); +// } + +// void WaylandTasksModel::Private::addWindow(PlasmaWindow *window) +// { +// if (findWindow(window) != windows.end() || transients.contains(window)) { +// return; +// } + +// auto removeWindow = [window, this] { +// auto it = findWindow(window); +// if (it != windows.end()) { +// const int row = it - windows.begin(); +// q->beginRemoveRows(QModelIndex(), row, row); +// windows.erase(it); +// transientsDemandingAttention.remove(window); +// appDataCache.remove(window); +// lastActivated.remove(window); +// q->endRemoveRows(); +// } else { // Could be a transient. +// // Removing a transient might change the demands attention state of the leader. +// if (transients.remove(window)) { +// if (PlasmaWindow *leader = transientsDemandingAttention.key(window)) { +// transientsDemandingAttention.remove(leader, window); +// dataChanged(leader, QVector{IsDemandingAttention}); +// } +// } +// } + +// if (activeWindow == window) { +// activeWindow = nullptr; +// } +// }; + +// QObject::connect(window, &PlasmaWindow::unmapped, q, removeWindow); + +// QObject::connect(window, &PlasmaWindow::titleChanged, q, [window, this] { +// this->dataChanged(window, Qt::DisplayRole); +// }); + +// QObject::connect(window, &PlasmaWindow::iconChanged, q, [window, this] { +// // The icon in the AppData struct might come from PlasmaWindow if it wasn't +// // filled in by windowUrlFromMetadata+appDataFromUrl. +// // TODO: Don't evict the cache unnecessarily if this isn't the case. As icons +// // are currently very static on Wayland, this eviction is unlikely to happen +// // frequently as of now. +// appDataCache.remove(window); +// this->dataChanged(window, Qt::DecorationRole); +// }); + +// QObject::connect(window, &PlasmaWindow::appIdChanged, q, [window, this] { +// // The AppData struct in the cache is derived from this and needs +// // to be evicted in favor of a fresh struct based on the changed +// // window metadata. +// appDataCache.remove(window); + +// // Refresh roles satisfied from the app data cache. +// this->dataChanged(window, +// QList{Qt::DecorationRole, AppId, AppName, GenericName, LauncherUrl, LauncherUrlWithoutIcon, SkipTaskbar, CanLaunchNewInstance}); +// }); + +// if (window->windowState & PlasmaWindow::state::state_active) { +// PlasmaWindow *effectiveActive = window; +// while (effectiveActive->parentWindow) { +// effectiveActive = effectiveActive->parentWindow; +// } + +// lastActivated[effectiveActive] = QTime::currentTime(); +// activeWindow = effectiveActive; +// } + +// QObject::connect(window, &PlasmaWindow::activeChanged, q, [window, this] { +// const bool active = window->windowState & PlasmaWindow::state::state_active; + +// PlasmaWindow *effectiveWindow = window; + +// while (effectiveWindow->parentWindow) { +// effectiveWindow = effectiveWindow->parentWindow; +// } + +// if (active) { +// lastActivated[effectiveWindow] = QTime::currentTime(); + +// if (activeWindow != effectiveWindow) { +// activeWindow = effectiveWindow; +// this->dataChanged(effectiveWindow, IsActive); +// } +// } else { +// if (activeWindow == effectiveWindow) { +// activeWindow = nullptr; +// this->dataChanged(effectiveWindow, IsActive); +// } +// } +// }); + +// QObject::connect(window, &PlasmaWindow::parentWindowChanged, q, [window, this] { +// PlasmaWindow *leader = window->parentWindow.data(); + +// // Migrate demanding attention to new leader. +// if (window->windowState.testFlag(PlasmaWindow::state::state_demands_attention)) { +// if (auto *oldLeader = transientsDemandingAttention.key(window)) { +// if (window->parentWindow != oldLeader) { +// transientsDemandingAttention.remove(oldLeader, window); +// transientsDemandingAttention.insert(leader, window); +// dataChanged(oldLeader, QVector{IsDemandingAttention}); +// dataChanged(leader, QVector{IsDemandingAttention}); +// } +// } +// } + +// if (transients.remove(window)) { +// if (leader) { // leader change. +// transients.insert(window, leader); +// } else { // lost a leader, add to regular windows list. +// Q_ASSERT(findWindow(window) == windows.end()); + +// const int count = windows.size(); +// q->beginInsertRows(QModelIndex(), count, count); +// windows.emplace_back(window); +// q->endInsertRows(); +// } +// } else if (leader) { // gained a leader, remove from regular windows list. +// auto it = findWindow(window); +// Q_ASSERT(it != windows.end()); + +// const int row = it - windows.begin(); +// q->beginRemoveRows(QModelIndex(), row, row); +// windows.erase(it); +// appDataCache.remove(window); +// lastActivated.remove(window); +// q->endRemoveRows(); +// } +// }); + +// QObject::connect(window, &PlasmaWindow::closeableChanged, q, [window, this] { +// this->dataChanged(window, IsClosable); +// }); + +// QObject::connect(window, &PlasmaWindow::movableChanged, q, [window, this] { +// this->dataChanged(window, IsMovable); +// }); + +// QObject::connect(window, &PlasmaWindow::resizableChanged, q, [window, this] { +// this->dataChanged(window, IsResizable); +// }); + +// QObject::connect(window, &PlasmaWindow::fullscreenableChanged, q, [window, this] { +// this->dataChanged(window, IsFullScreenable); +// }); + +// QObject::connect(window, &PlasmaWindow::fullscreenChanged, q, [window, this] { +// this->dataChanged(window, IsFullScreen); +// }); + +// QObject::connect(window, &PlasmaWindow::maximizeableChanged, q, [window, this] { +// this->dataChanged(window, IsMaximizable); +// }); + +// QObject::connect(window, &PlasmaWindow::maximizedChanged, q, [window, this] { +// this->dataChanged(window, IsMaximized); +// }); + +// QObject::connect(window, &PlasmaWindow::minimizeableChanged, q, [window, this] { +// this->dataChanged(window, IsMinimizable); +// }); + +// QObject::connect(window, &PlasmaWindow::minimizedChanged, q, [window, this] { +// this->dataChanged(window, IsMinimized); +// }); + +// QObject::connect(window, &PlasmaWindow::keepAboveChanged, q, [window, this] { +// this->dataChanged(window, IsKeepAbove); +// }); + +// QObject::connect(window, &PlasmaWindow::keepBelowChanged, q, [window, this] { +// this->dataChanged(window, IsKeepBelow); +// }); + +// QObject::connect(window, &PlasmaWindow::shadeableChanged, q, [window, this] { +// this->dataChanged(window, IsShadeable); +// }); + +// QObject::connect(window, &PlasmaWindow::virtualDesktopChangeableChanged, q, [window, this] { +// this->dataChanged(window, IsVirtualDesktopsChangeable); +// }); + +// QObject::connect(window, &PlasmaWindow::virtualDesktopEntered, q, [window, this] { +// this->dataChanged(window, VirtualDesktops); + +// // If the count has changed from 0, the window may no longer be on all virtual +// // desktops. +// if (window->virtualDesktops.count() > 0) { +// this->dataChanged(window, IsOnAllVirtualDesktops); +// } +// }); + +// QObject::connect(window, &PlasmaWindow::virtualDesktopLeft, q, [window, this] { +// this->dataChanged(window, VirtualDesktops); + +// // If the count has changed to 0, the window is now on all virtual desktops. +// if (window->virtualDesktops.count() == 0) { +// this->dataChanged(window, IsOnAllVirtualDesktops); +// } +// }); + +// QObject::connect(window, &PlasmaWindow::geometryChanged, q, [window, this] { +// this->dataChanged(window, QList{Geometry, ScreenGeometry}); +// }); + +// QObject::connect(window, &PlasmaWindow::demandsAttentionChanged, q, [window, this] { +// // Changes to a transient's state might change demands attention state for leader. +// if (auto *leader = transients.value(window)) { +// if (window->windowState.testFlag(PlasmaWindow::state::state_demands_attention)) { +// if (!transientsDemandingAttention.values(leader).contains(window)) { +// transientsDemandingAttention.insert(leader, window); +// this->dataChanged(leader, QVector{IsDemandingAttention}); +// } +// } else if (transientsDemandingAttention.remove(window)) { +// this->dataChanged(leader, QVector{IsDemandingAttention}); +// } +// } else { +// this->dataChanged(window, QVector{IsDemandingAttention}); +// } +// }); + +// QObject::connect(window, &PlasmaWindow::skipTaskbarChanged, q, [window, this] { +// this->dataChanged(window, SkipTaskbar); +// }); + +// QObject::connect(window, &PlasmaWindow::applicationMenuChanged, q, [window, this] { +// this->dataChanged(window, QList{ApplicationMenuServiceName, ApplicationMenuObjectPath}); +// }); + +// QObject::connect(window, &PlasmaWindow::activitiesChanged, q, [window, this] { +// this->dataChanged(window, Activities); +// }); + +// // Handle transient. +// if (PlasmaWindow *leader = window->parentWindow.data()) { +// transients.insert(window, leader); + +// // Update demands attention state for leader. +// if (window->windowState.testFlag(PlasmaWindow::state::state_demands_attention)) { +// transientsDemandingAttention.insert(leader, window); +// dataChanged(leader, QVector{IsDemandingAttention}); +// } +// } else { +// const int count = windows.size(); + +// q->beginInsertRows(QModelIndex(), count, count); + +// windows.emplace_back(window); + +// q->endInsertRows(); +// } +// } + +// const AppData &WaylandTasksModel::Private::appData(PlasmaWindow *window) +// { +// static_assert(!std::is_trivially_copy_assignable_v); +// if (auto it = appDataCache.constFind(window); it != appDataCache.constEnd()) { +// return *it; +// } + +// return *appDataCache.emplace(window, appDataFromUrl(windowUrlFromMetadata(window->appId, window->pid, rulesConfig, window->resourceName))); +// } + +// QIcon WaylandTasksModel::Private::icon(PlasmaWindow *window) +// { +// const AppData &app = appData(window); + +// if (!app.icon.isNull()) { +// return app.icon; +// } + +// appDataCache[window].icon = window->icon; + +// return window->icon; +// } + +// QString WaylandTasksModel::Private::mimeType() +// { +// // Use a unique format id to make this intentionally useless for +// // cross-process DND. +// return QStringLiteral("windowsystem/winid+") + uuid.toString(); +// } + +// QString WaylandTasksModel::Private::groupMimeType() +// { +// // Use a unique format id to make this intentionally useless for +// // cross-process DND. +// return QStringLiteral("windowsystem/multiple-winids+") + uuid.toString(); +// } + +// void WaylandTasksModel::Private::dataChanged(PlasmaWindow *window, int role) +// { +// auto it = findWindow(window); +// if (it == windows.end()) { +// return; +// } +// QModelIndex idx = q->index(it - windows.begin()); +// Q_EMIT q->dataChanged(idx, idx, QList{role}); +// } + +// void WaylandTasksModel::Private::dataChanged(PlasmaWindow *window, const QList &roles) +// { +// auto it = findWindow(window); +// if (it == windows.end()) { +// return; +// } +// QModelIndex idx = q->index(it - windows.begin()); +// Q_EMIT q->dataChanged(idx, idx, roles); +// } + +// WaylandTasksModel::WaylandTasksModel(QObject *parent) +// : AbstractWindowTasksModel(parent) +// , d(new Private(this)) +// { +// d->init(); +// } + +// WaylandTasksModel::~WaylandTasksModel() = default; + +// QVariant WaylandTasksModel::data(const QModelIndex &index, int role) const +// { +// // Note: when index is valid, its row >= 0, so casting to unsigned is safe +// if (!index.isValid() || static_cast(index.row()) >= d->windows.size()) { +// return QVariant(); +// } + +// PlasmaWindow *window = d->windows.at(index.row()).get(); + +// if (role == Qt::DisplayRole) { +// return window->title; +// } else if (role == Qt::DecorationRole) { +// return d->icon(window); +// } else if (role == AppId) { +// const QString &id = d->appData(window).id; + +// if (id.isEmpty()) { +// return window->appId; +// } else { +// return id; +// } +// } else if (role == AppName) { +// return d->appData(window).name; +// } else if (role == GenericName) { +// return d->appData(window).genericName; +// } else if (role == LauncherUrl || role == LauncherUrlWithoutIcon) { +// return d->appData(window).url; +// } else if (role == WinIdList) { +// return QVariantList{window->uuid}; +// } else if (role == MimeType) { +// return d->mimeType(); +// } else if (role == MimeData) { +// return window->uuid; +// } else if (role == IsWindow) { +// return true; +// } else if (role == IsActive) { +// return (window == d->activeWindow); +// } else if (role == IsClosable) { +// return window->windowState.testFlag(PlasmaWindow::state::state_closeable); +// } else if (role == IsMovable) { +// return window->windowState.testFlag(PlasmaWindow::state::state_movable); +// } else if (role == IsResizable) { +// return window->windowState.testFlag(PlasmaWindow::state::state_resizable); +// } else if (role == IsMaximizable) { +// return window->windowState.testFlag(PlasmaWindow::state::state_maximizable); +// } else if (role == IsMaximized) { +// return window->windowState.testFlag(PlasmaWindow::state::state_maximized); +// } else if (role == IsMinimizable) { +// return window->windowState.testFlag(PlasmaWindow::state::state_minimizable); +// } else if (role == IsMinimized || role == IsHidden) { +// return window->windowState.testFlag(PlasmaWindow::state::state_minimized); +// } else if (role == IsKeepAbove) { +// return window->windowState.testFlag(PlasmaWindow::state::state_keep_above); +// } else if (role == IsKeepBelow) { +// return window->windowState.testFlag(PlasmaWindow::state::state_keep_below); +// } else if (role == IsFullScreenable) { +// return window->windowState.testFlag(PlasmaWindow::state::state_fullscreenable); +// } else if (role == IsFullScreen) { +// return window->windowState.testFlag(PlasmaWindow::state::state_fullscreen); +// } else if (role == IsShadeable) { +// return window->windowState.testFlag(PlasmaWindow::state::state_shadeable); +// } else if (role == IsShaded) { +// return window->windowState.testFlag(PlasmaWindow::state::state_shaded); +// } else if (role == IsVirtualDesktopsChangeable) { +// return window->windowState.testFlag(PlasmaWindow::state::state_virtual_desktop_changeable); +// } else if (role == VirtualDesktops) { +// return window->virtualDesktops; +// } else if (role == IsOnAllVirtualDesktops) { +// return window->virtualDesktops.isEmpty(); +// } else if (role == Geometry) { +// return window->geometry; +// } else if (role == ScreenGeometry) { +// return screenGeometry(window->geometry.center()); +// } else if (role == Activities) { +// return window->activities; +// } else if (role == IsDemandingAttention) { +// return window->windowState.testFlag(PlasmaWindow::state::state_demands_attention) || d->transientsDemandingAttention.contains(window); +// } else if (role == SkipTaskbar) { +// return window->windowState.testFlag(PlasmaWindow::state::state_skiptaskbar) || d->appData(window).skipTaskbar; +// } else if (role == SkipPager) { +// // FIXME Implement. +// } else if (role == AppPid) { +// return window->pid; +// } else if (role == StackingOrder) { +// return d->stackingOrder.indexOf(window->uuid); +// } else if (role == LastActivated) { +// if (d->lastActivated.contains(window)) { +// return d->lastActivated.value(window); +// } +// } else if (role == ApplicationMenuObjectPath) { +// return window->applicationMenuObjectPath; +// } else if (role == ApplicationMenuServiceName) { +// return window->applicationMenuService; +// } else if (role == CanLaunchNewInstance) { +// return canLauchNewInstance(d->appData(window)); +// } + +// return AbstractTasksModel::data(index, role); +// } + +// int WaylandTasksModel::rowCount(const QModelIndex &parent) const +// { +// return parent.isValid() ? 0 : d->windows.size(); +// } + +// QModelIndex WaylandTasksModel::index(int row, int column, const QModelIndex &parent) const +// { +// return hasIndex(row, column, parent) ? createIndex(row, column, d->windows.at(row).get()) : QModelIndex(); +// } + +// void WaylandTasksModel::requestActivate(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// PlasmaWindow *window = d->windows.at(index.row()).get(); + +// // Pull forward any transient demanding attention. +// if (auto *transientDemandingAttention = d->transientsDemandingAttention.value(window)) { +// window = transientDemandingAttention; +// } else { +// // TODO Shouldn't KWin take care of that? +// // Bringing a transient to the front usually brings its parent with it +// // but focus is not handled properly. +// // TODO take into account d->lastActivation instead +// // of just taking the first one. +// while (d->transients.key(window)) { +// window = d->transients.key(window); +// } +// } + +// window->set_state(PlasmaWindow::state::state_active, PlasmaWindow::state::state_active); +// } + +// void WaylandTasksModel::requestNewInstance(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// runApp(d->appData(d->windows.at(index.row()).get())); +// } + +// void WaylandTasksModel::requestOpenUrls(const QModelIndex &index, const QList &urls) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent) || urls.isEmpty()) { +// return; +// } + +// runApp(d->appData(d->windows.at(index.row()).get()), urls); +// } + +// void WaylandTasksModel::requestClose(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// d->windows.at(index.row())->close(); +// } + +// void WaylandTasksModel::requestMove(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); + +// window->set_state(PlasmaWindow::state::state_active, PlasmaWindow::state::state_active); +// window->request_move(); +// } + +// void WaylandTasksModel::requestResize(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); + +// window->set_state(PlasmaWindow::state::state_active, PlasmaWindow::state::state_active); +// window->request_resize(); +// } + +// void WaylandTasksModel::requestToggleMinimized(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); + +// if (window->windowState & PlasmaWindow::state::state_minimized) { +// window->set_state(PlasmaWindow::state::state_minimized, 0); +// } else { +// window->set_state(PlasmaWindow::state::state_minimized, PlasmaWindow::state::state_minimized); +// } +// } + +// void WaylandTasksModel::requestToggleMaximized(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); + +// if (window->windowState & PlasmaWindow::state::state_maximized) { +// window->set_state(PlasmaWindow::state::state_maximized | PlasmaWindow::state::state_active, PlasmaWindow::state::state_active); +// } else { +// window->set_state(PlasmaWindow::state::state_maximized | PlasmaWindow::state::state_active, +// PlasmaWindow::state::state_maximized | PlasmaWindow::state::state_active); +// } +// } + +// void WaylandTasksModel::requestToggleKeepAbove(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); + +// if (window->windowState & PlasmaWindow::state::state_keep_above) { +// window->set_state(PlasmaWindow::state::state_keep_above, 0); +// } else { +// window->set_state(PlasmaWindow::state::state_keep_above, PlasmaWindow::state::state_keep_above); +// } +// } + +// void WaylandTasksModel::requestToggleKeepBelow(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } +// auto &window = d->windows.at(index.row()); + +// if (window->windowState & PlasmaWindow::state::state_keep_below) { +// window->set_state(PlasmaWindow::state::state_keep_below, 0); +// } else { +// window->set_state(PlasmaWindow::state::state_keep_below, PlasmaWindow::state::state_keep_below); +// } +// } + +// void WaylandTasksModel::requestToggleFullScreen(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); + +// if (window->windowState & PlasmaWindow::state::state_fullscreen) { +// window->set_state(PlasmaWindow::state::state_fullscreen, 0); +// } else { +// window->set_state(PlasmaWindow::state::state_fullscreen, PlasmaWindow::state::state_fullscreen); +// } +// } + +// void WaylandTasksModel::requestToggleShaded(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); + +// if (window->windowState & PlasmaWindow::state::state_shaded) { +// window->set_state(PlasmaWindow::state::state_shaded, 0); +// } else { +// window->set_state(PlasmaWindow::state::state_shaded, PlasmaWindow::state::state_shaded); +// }; +// } + +// void WaylandTasksModel::requestVirtualDesktops(const QModelIndex &index, const QVariantList &desktops) +// { +// // FIXME TODO: Lacks the "if we've requested the current desktop, force-activate +// // the window" logic from X11 version. This behavior should be in KWin rather than +// // libtm however. + +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); + +// if (desktops.isEmpty()) { +// const QStringList virtualDesktops = window->virtualDesktops; +// for (const QString &desktop : virtualDesktops) { +// window->request_leave_virtual_desktop(desktop); +// } +// } else { +// const QStringList &now = window->virtualDesktops; +// QStringList next; + +// for (const QVariant &desktop : desktops) { +// const QString &desktopId = desktop.toString(); + +// if (!desktopId.isEmpty()) { +// next << desktopId; + +// if (!now.contains(desktopId)) { +// window->request_enter_virtual_desktop(desktopId); +// } +// } +// } + +// for (const QString &desktop : now) { +// if (!next.contains(desktop)) { +// window->request_leave_virtual_desktop(desktop); +// } +// } +// } +// } + +// void WaylandTasksModel::requestNewVirtualDesktop(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// d->windows.at(index.row())->request_enter_new_virtual_desktop(); +// } + +// void WaylandTasksModel::requestActivities(const QModelIndex &index, const QStringList &activities) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); +// const auto newActivities = QSet(activities.begin(), activities.end()); +// const auto plasmaActivities = window->activities; +// const auto oldActivities = QSet(plasmaActivities.begin(), plasmaActivities.end()); + +// const auto activitiesToAdd = newActivities - oldActivities; +// for (const auto &activity : activitiesToAdd) { +// window->request_enter_activity(activity); +// } + +// const auto activitiesToRemove = oldActivities - newActivities; +// for (const auto &activity : activitiesToRemove) { +// window->request_leave_activity(activity); +// } +// } + +// void WaylandTasksModel::requestPublishDelegateGeometry(const QModelIndex &index, const QRect &geometry, QObject *delegate) +// { +// /* +// FIXME: This introduces the dependency on Qt::Quick. I might prefer +// reversing this and publishing the window pointer through the model, +// then calling PlasmaWindow::setMinimizeGeometry in the applet backend, +// rather than hand delegate items into the lib, keeping the lib more UI- +// agnostic. +// */ + +// Q_UNUSED(geometry) + +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// const QQuickItem *item = qobject_cast(delegate); + +// if (!item || !item->parentItem()) { +// return; +// } + +// QWindow *itemWindow = item->window(); + +// if (!itemWindow) { +// return; +// } + +// auto waylandWindow = itemWindow->nativeInterface(); + +// if (!waylandWindow || !waylandWindow->surface()) { +// return; +// } + +// QRect rect(item->x(), item->y(), item->width(), item->height()); +// rect.moveTopLeft(item->parentItem()->mapToScene(rect.topLeft()).toPoint()); + +// auto &window = d->windows.at(index.row()); + +// window->set_minimized_geometry(waylandWindow->surface(), rect.x(), rect.y(), rect.width(), rect.height()); +// } + +// QUuid WaylandTasksModel::winIdFromMimeData(const QMimeData *mimeData, bool *ok) +// { +// Q_ASSERT(mimeData); + +// if (ok) { +// *ok = false; +// } + +// if (!mimeData->hasFormat(Private::mimeType())) { +// return {}; +// } + +// QUuid id(mimeData->data(Private::mimeType())); +// *ok = !id.isNull(); + +// return id; +// } + +// QList WaylandTasksModel::winIdsFromMimeData(const QMimeData *mimeData, bool *ok) +// { +// Q_ASSERT(mimeData); +// QList ids; + +// if (ok) { +// *ok = false; +// } + +// if (!mimeData->hasFormat(Private::groupMimeType())) { +// // Try to extract single window id. +// bool singularOk; +// QUuid id = winIdFromMimeData(mimeData, &singularOk); + +// if (ok) { +// *ok = singularOk; +// } + +// if (singularOk) { +// ids << id; +// } + +// return ids; +// } + +// // FIXME: Extracting multiple winids is still unimplemented; +// // TaskGroupingProxy::data(..., ::MimeData) can't produce +// // a payload with them anyways. + +// return ids; +// } + +LXQtTaskBarPlasmaWindow::LXQtTaskBarPlasmaWindow(const QString &uuid, ::org_kde_plasma_window *id) + : org_kde_plasma_window(id) + , uuid(uuid) +{ +} + +/* + * LXQtTaskBarPlasmaWindow + */ + +LXQtTaskBarPlasmaWindow::~LXQtTaskBarPlasmaWindow() +{ + destroy(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_unmapped() +{ + wasUnmapped = true; + Q_EMIT unmapped(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_title_changed(const QString &title) +{ + if(this->title == title) + return; + this->title = title; + Q_EMIT titleChanged(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_app_id_changed(const QString &app_id) +{ + if(appId == app_id) + return; + appId = app_id; + Q_EMIT appIdChanged(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_icon_changed() +{ + int pipeFds[2]; + if (pipe2(pipeFds, O_CLOEXEC) != 0) { + qWarning() << "TaskManager: failed creating pipe"; + return; + } + get_icon(pipeFds[1]); + ::close(pipeFds[1]); + auto readIcon = [uuid = uuid](int fd) { + auto closeGuard = qScopeGuard([fd]() { + ::close(fd); + }); + pollfd pollFd; + pollFd.fd = fd; + pollFd.events = POLLIN; + QByteArray data; + while (true) { + int ready = poll(&pollFd, 1, 1000); + if (ready < 0 && errno != EINTR) { + qWarning() << "TaskManager: polling for icon of window" << uuid << "failed"; + return QIcon(); + } else if (ready == 0) { + qWarning() << "TaskManager: time out polling for icon of window" << uuid; + return QIcon(); + } else { + char buffer[4096]; + int n = read(fd, buffer, sizeof(buffer)); + if (n < 0) { + qWarning() << "TaskManager: error reading icon of window" << uuid; + return QIcon(); + } else if (n > 0) { + data.append(buffer, n); + } else { + QIcon icon; + QDataStream ds(data); + ds >> icon; + return icon; + } + } + } + }; + QFuture future = QtConcurrent::run(readIcon, pipeFds[0]); + auto watcher = new QFutureWatcher(); + watcher->setFuture(future); + connect(watcher, &QFutureWatcher::finished, this, [this, watcher] { + icon = watcher->future().result(); + Q_EMIT iconChanged(); + }); + connect(watcher, &QFutureWatcher::finished, watcher, &QObject::deleteLater); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_themed_icon_name_changed(const QString &name) +{ + icon = QIcon::fromTheme(name); + Q_EMIT iconChanged(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_state_changed(uint32_t flags) +{ + auto diff = windowState ^ flags; + if (diff & state::state_active) { + windowState.setFlag(state::state_active, flags & state::state_active); + Q_EMIT activeChanged(); + } + if (diff & state::state_minimized) { + windowState.setFlag(state::state_minimized, flags & state::state_minimized); + Q_EMIT minimizedChanged(); + } + if (diff & state::state_maximized) { + windowState.setFlag(state::state_maximized, flags & state::state_maximized); + Q_EMIT maximizedChanged(); + } + if (diff & state::state_fullscreen) { + windowState.setFlag(state::state_fullscreen, flags & state::state_fullscreen); + Q_EMIT fullscreenChanged(); + } + if (diff & state::state_keep_above) { + windowState.setFlag(state::state_keep_above, flags & state::state_keep_above); + Q_EMIT keepAboveChanged(); + } + if (diff & state::state_keep_below) { + windowState.setFlag(state::state_keep_below, flags & state::state_keep_below); + Q_EMIT keepBelowChanged(); + } + if (diff & state::state_on_all_desktops) { + windowState.setFlag(state::state_on_all_desktops, flags & state::state_on_all_desktops); + Q_EMIT onAllDesktopsChanged(); + } + if (diff & state::state_demands_attention) { + windowState.setFlag(state::state_demands_attention, flags & state::state_demands_attention); + Q_EMIT demandsAttentionChanged(); + } + if (diff & state::state_closeable) { + windowState.setFlag(state::state_closeable, flags & state::state_closeable); + Q_EMIT closeableChanged(); + } + if (diff & state::state_minimizable) { + windowState.setFlag(state::state_minimizable, flags & state::state_minimizable); + Q_EMIT minimizeableChanged(); + } + if (diff & state::state_maximizable) { + windowState.setFlag(state::state_maximizable, flags & state::state_maximizable); + Q_EMIT maximizeableChanged(); + } + if (diff & state::state_fullscreenable) { + windowState.setFlag(state::state_fullscreenable, flags & state::state_fullscreenable); + Q_EMIT fullscreenableChanged(); + } + if (diff & state::state_skiptaskbar) { + windowState.setFlag(state::state_skiptaskbar, flags & state::state_skiptaskbar); + Q_EMIT skipTaskbarChanged(); + } + if (diff & state::state_shadeable) { + windowState.setFlag(state::state_shadeable, flags & state::state_shadeable); + Q_EMIT shadeableChanged(); + } + if (diff & state::state_shaded) { + windowState.setFlag(state::state_shaded, flags & state::state_shaded); + Q_EMIT shadedChanged(); + } + if (diff & state::state_movable) { + windowState.setFlag(state::state_movable, flags & state::state_movable); + Q_EMIT movableChanged(); + } + if (diff & state::state_resizable) { + windowState.setFlag(state::state_resizable, flags & state::state_resizable); + Q_EMIT resizableChanged(); + } + if (diff & state::state_virtual_desktop_changeable) { + windowState.setFlag(state::state_virtual_desktop_changeable, flags & state::state_virtual_desktop_changeable); + Q_EMIT virtualDesktopChangeableChanged(); + } + if (diff & state::state_skipswitcher) { + windowState.setFlag(state::state_skipswitcher, flags & state::state_skipswitcher); + Q_EMIT skipSwitcherChanged(); + } +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_virtual_desktop_entered(const QString &id) +{ + virtualDesktops.push_back(id); + Q_EMIT virtualDesktopEntered(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_virtual_desktop_left(const QString &id) +{ + virtualDesktops.removeAll(id); + Q_EMIT virtualDesktopLeft(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_geometry(int32_t x, int32_t y, uint32_t width, uint32_t height) +{ + geometry = QRect(x, y, width, height); + Q_EMIT geometryChanged(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_application_menu(const QString &service_name, const QString &object_path) +{ + applicationMenuService = service_name; + applicationMenuObjectPath = object_path; + Q_EMIT applicationMenuChanged(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_activity_entered(const QString &id) +{ + activities.push_back(id); + Q_EMIT activitiesChanged(); +} +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_activity_left(const QString &id) +{ + activities.removeAll(id); + Q_EMIT activitiesChanged(); +} +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_pid_changed(uint32_t pid) +{ + this->pid = pid; +} +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_resource_name_changed(const QString &resource_name) +{ + resourceName = resource_name; +} +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_parent_window(::org_kde_plasma_window *parent) +{ + LXQtTaskBarPlasmaWindow *parentWindow = nullptr; + if (parent) { + parentWindow = dynamic_cast(LXQtTaskBarPlasmaWindow::fromObject(parent)); + } + setParentWindow(parentWindow); +} +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_initial_state() +{ + Q_EMIT initialStateDone(); +} + +void LXQtTaskBarPlasmaWindow::setParentWindow(LXQtTaskBarPlasmaWindow *parent) +{ + const auto old = parentWindow; + QObject::disconnect(parentWindowUnmappedConnection); + + if (parent && !parent->wasUnmapped) { + parentWindow = QPointer(parent); + parentWindowUnmappedConnection = QObject::connect(parent, &LXQtTaskBarPlasmaWindow::unmapped, this, [this] { + setParentWindow(nullptr); + }); + } else { + parentWindow = QPointer(); + parentWindowUnmappedConnection = QMetaObject::Connection(); + } + + if (parentWindow.data() != old.data()) { + Q_EMIT parentWindowChanged(); + } +} + +/* + * LXQtTaskBarPlasmaWindowManagment + */ + +LXQtTaskBarPlasmaWindowManagment::LXQtTaskBarPlasmaWindowManagment() + : QWaylandClientExtensionTemplate(version) +{ + connect(this, &QWaylandClientExtension::activeChanged, this, [this] { + if (!isActive()) { + wl_proxy_destroy(reinterpret_cast(object())); + } + }); +} + +LXQtTaskBarPlasmaWindowManagment::~LXQtTaskBarPlasmaWindowManagment() +{ + if (isActive()) { + wl_proxy_destroy(reinterpret_cast(object())); + } +} + +void LXQtTaskBarPlasmaWindowManagment::org_kde_plasma_window_management_window_with_uuid(uint32_t id, const QString &uuid) +{ + Q_UNUSED(id) + Q_EMIT windowCreated(new LXQtTaskBarPlasmaWindow(uuid, get_window_by_uuid(uuid))); +} +void LXQtTaskBarPlasmaWindowManagment::org_kde_plasma_window_management_stacking_order_uuid_changed(const QString &uuids) +{ + Q_EMIT stackingOrderChanged(uuids); +} diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h new file mode 100644 index 000000000..78c75693d --- /dev/null +++ b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h @@ -0,0 +1,154 @@ +#ifndef LXQTTASKBARPLASMAWINDOWMANAGMENT_H +#define LXQTTASKBARPLASMAWINDOWMANAGMENT_H + +#include +#include +#include + +#include "qwayland-plasma-window-management.h" + +typedef quintptr WId; + +class LXQtTaskBarPlasmaWindowManagment; + +class LXQtTaskBarPlasmaWindow : public QObject, + public QtWayland::org_kde_plasma_window +{ + Q_OBJECT +public: + LXQtTaskBarPlasmaWindow(const QString &uuid, ::org_kde_plasma_window *id); + ~LXQtTaskBarPlasmaWindow(); + + inline WId getWindowId() const { return reinterpret_cast(this); } + + using state = QtWayland::org_kde_plasma_window_management::state; + const QString uuid; + QString title; + QString appId; + QIcon icon; + QFlags windowState; + QList virtualDesktops; + QRect geometry; + QString applicationMenuService; + QString applicationMenuObjectPath; + QList activities; + quint32 pid; + QString resourceName; + QPointer parentWindow; + bool wasUnmapped = false; + bool acceptedInTaskBar = false; + +Q_SIGNALS: + void unmapped(); + void titleChanged(); + void appIdChanged(); + void iconChanged(); + void activeChanged(); + void minimizedChanged(); + void maximizedChanged(); + void fullscreenChanged(); + void keepAboveChanged(); + void keepBelowChanged(); + void onAllDesktopsChanged(); + void demandsAttentionChanged(); + void closeableChanged(); + void minimizeableChanged(); + void maximizeableChanged(); + void fullscreenableChanged(); + void skiptaskbarChanged(); + void shadeableChanged(); + void shadedChanged(); + void movableChanged(); + void resizableChanged(); + void virtualDesktopChangeableChanged(); + void skipSwitcherChanged(); + void virtualDesktopEntered(); + void virtualDesktopLeft(); + void geometryChanged(); + void skipTaskbarChanged(); + void applicationMenuChanged(); + void activitiesChanged(); + void parentWindowChanged(); + void initialStateDone(); + +protected: + void org_kde_plasma_window_unmapped() override; + void org_kde_plasma_window_title_changed(const QString &title) override; + void org_kde_plasma_window_app_id_changed(const QString &app_id) override; + void org_kde_plasma_window_icon_changed() override; + void org_kde_plasma_window_themed_icon_name_changed(const QString &name) override; + void org_kde_plasma_window_state_changed(uint32_t flags) override; + void org_kde_plasma_window_virtual_desktop_entered(const QString &id) override; + + void org_kde_plasma_window_virtual_desktop_left(const QString &id) override; + void org_kde_plasma_window_geometry(int32_t x, int32_t y, uint32_t width, uint32_t height) override; + void org_kde_plasma_window_application_menu(const QString &service_name, const QString &object_path) override; + void org_kde_plasma_window_activity_entered(const QString &id) override; + void org_kde_plasma_window_activity_left(const QString &id) override; + void org_kde_plasma_window_pid_changed(uint32_t pid) override; + void org_kde_plasma_window_resource_name_changed(const QString &resource_name) override; + void org_kde_plasma_window_parent_window(::org_kde_plasma_window *parent) override; + void org_kde_plasma_window_initial_state() override; + +private: + void setParentWindow(LXQtTaskBarPlasmaWindow *parent); + + QMetaObject::Connection parentWindowUnmappedConnection; +}; + +class LXQtTaskBarPlasmaWindowManagment : public QWaylandClientExtensionTemplate, + public QtWayland::org_kde_plasma_window_management +{ + Q_OBJECT +public: + static constexpr int version = 16; + LXQtTaskBarPlasmaWindowManagment(); + ~LXQtTaskBarPlasmaWindowManagment(); + + void org_kde_plasma_window_management_window_with_uuid(uint32_t id, const QString &uuid) override; + void org_kde_plasma_window_management_stacking_order_uuid_changed(const QString &uuids) override; + +Q_SIGNALS: + void windowCreated(LXQtTaskBarPlasmaWindow *window); + void stackingOrderChanged(const QString &uuids); +}; + +// class Q_DECL_HIDDEN WaylandTasksModel::Private +// { +// public: +// Private(WaylandTasksModel *q); +// QHash appDataCache; +// QHash lastActivated; +// PlasmaWindow *activeWindow = nullptr; +// std::vector> windows; +// // key=transient child, value=leader +// QHash transients; +// // key=leader, values=transient children +// QMultiHash transientsDemandingAttention; +// std::unique_ptr windowManagement; +// KSharedConfig::Ptr rulesConfig; +// KDirWatch *configWatcher = nullptr; +// VirtualDesktopInfo *virtualDesktopInfo = nullptr; +// static QUuid uuid; +// QList stackingOrder; + +// void init(); +// void initWayland(); +// auto findWindow(PlasmaWindow *window) const; +// void addWindow(PlasmaWindow *window); + +// const AppData &appData(PlasmaWindow *window); + +// QIcon icon(PlasmaWindow *window); + +// static QString mimeType(); +// static QString groupMimeType(); + +// void dataChanged(PlasmaWindow *window, int role); +// void dataChanged(PlasmaWindow *window, const QList &roles); + +// private: +// WaylandTasksModel *q; +// }; + +#endif // LXQTTASKBARPLASMAWINDOWMANAGMENT_H diff --git a/panel/backends/wayland/protocols/plasma-window-management.xml b/panel/backends/wayland/protocols/plasma-window-management.xml new file mode 100644 index 000000000..5990ebfda --- /dev/null +++ b/panel/backends/wayland/protocols/plasma-window-management.xml @@ -0,0 +1,425 @@ + + + + + + + This interface manages application windows. + It provides requests to show and hide the desktop and emits + an event every time a window is created so that the client can + use it to manage the window. + + Only one client can bind this interface at a time. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tell the compositor to show/hide the desktop. + + + + + + Deprecated: use get_window_by_uuid + + + + + + + + + + + + This event will be sent whenever the show desktop mode changes. E.g. when it is entered + or left. + + On binding the interface the current state is sent. + + + + + + + This event will be sent immediately after a window is mapped. + + + + + + + This event will be sent when stacking order changed and on bind + + + + + + + This event will be sent when stacking order changed and on bind + + + + + + + This event will be sent immediately after a window is mapped. + + + + + + + + + Manages and control an application window. + + Only one client can bind this interface at a time. + + + + + Set window state. + + Values for state argument are described by org_kde_plasma_window_management.state + and can be used together in a bitfield. The flags bitfield describes which flags are + supposed to be set, the state bitfield the value for the set flags + + + + + + + + Deprecated: use enter_virtual_desktop + Maps the window to a different virtual desktop. + + To show the window on all virtual desktops, call the + org_kde_plasma_window.set_state request and specify a on_all_desktops + state in the bitfield. + + + + + + + Sets the geometry of the taskbar entry for this window. + The geometry is relative to a panel in particular. + + + + + + + + + + + Remove the task geometry information for a particular panel. + + + + + + + + + Close this window. + + + + + + Request an interactive move for this window. + + + + + + Request an interactive resize for this window. + + + + + + Removes the resource bound for this org_kde_plasma_window. + + + + + + The compositor will write the window icon into the provided file descriptor. + The data is a serialized QIcon with QDataStream. + + + + + + + This event will be sent as soon as the window title is changed. + + + + + + + This event will be sent as soon as the application + identifier is changed. + + + + + + + This event will be sent as soon as the window state changes. + + Values for state argument are described by org_kde_plasma_window_management.state. + + + + + + + DEPRECATED: use virtual_desktop_entered and virtual_desktop_left instead + This event will be sent when a window is moved to another + virtual desktop. + + It is not sent if it becomes visible on all virtual desktops though. + + + + + + + This event will be sent whenever the themed icon name changes. May be null. + + + + + + + This event will be sent immediately after the window is closed + and its surface is unmapped. + + + + + + This event will be sent immediately after all initial state been sent to the client. + If the Plasma window is already unmapped, the unmapped event will be sent before the + initial_state event. + + + + + + This event will be sent whenever the parent window of this org_kde_plasma_window changes. + The passed parent is another org_kde_plasma_window and this org_kde_plasma_window is a + transient window to the parent window. If the parent argument is null, this + org_kde_plasma_window does not have a parent window. + + + + + + + This event will be sent whenever the window geometry of this org_kde_plasma_window changes. + The coordinates are in absolute coordinates of the windowing system. + + + + + + + + + + This event will be sent whenever the icon of the window changes, but there is no themed + icon name. Common examples are Xwayland windows which have a pixmap based icon. + + The client can request the icon using get_icon. + + + + + + This event will be sent when the compositor has set the process id this window belongs to. + This should be set once before the initial_state is sent. + + + + + + + + + + Make the window enter a virtual desktop. A window can enter more + than one virtual desktop. if the id is empty or invalid, no action will be performed. + + + + + + RFC: do this with an empty id to request_enter_virtual_desktop? + Make the window enter a new virtual desktop. If the server consents the request, + it will create a new virtual desktop and assign the window to it. + + + + + + Make the window exit a virtual desktop. If it exits all desktops it will be considered on all of them. + + + + + + + This event will be sent when the window has entered a new virtual desktop. The window can be on more than one desktop, or none: then is considered on all of them. + + + + + + + This event will be sent when the window left a virtual desktop. If the window leaves all desktops, it can be considered on all. + If the window gets manually added on all desktops, the server has to send virtual_desktop_left for every previous desktop it was in for the window to be really considered on all desktops. + + + + + + + + + This event will be sent after the application menu + for the window has changed. + + + + + + + + Make the window enter an activity. A window can enter more activity. If the id is empty or invalid, no action will be performed. + + + + + + + Make the window exit a an activity. If it exits all activities it will be considered on all of them. + + + + + + + This event will be sent when the window has entered an activity. The window can be on more than one activity, or none: then is considered on all of them. + + + + + + + This event will be sent when the window left an activity. If the window leaves all activities, it will be considered on all. + If the window gets manually added on all activities, the server has to send activity_left for every previous activity it was in for the window to be really considered on all activities. + + + + + + + Requests this window to be displayed in a specific output. + + + + + + + This event will be sent when the X11 resource name of the window has changed. + This is only set for XWayland windows. + + + + + + + + The activation manager interface provides a way to get notified + when an application is about to be activated. + + + + + Destroy the activation manager object. The activation objects introduced + by this manager object will be unaffected. + + + + + + Will be issued when an app is set to be activated. It offers + an instance of org_kde_plasma_activation that will tell us the app_id + and the extent of the activation. + + + + + + + + + Notify the compositor that the org_kde_plasma_activation object will no + longer be used. + + + + + + + + + + + + + diff --git a/panel/lxqtpanelapplication.cpp b/panel/lxqtpanelapplication.cpp index c3b666620..507163221 100644 --- a/panel/lxqtpanelapplication.cpp +++ b/panel/lxqtpanelapplication.cpp @@ -40,11 +40,14 @@ #include "backends/lxqttaskbardummybackend.h" #include "backends/xcb/lxqttaskbarbackend_x11.h" +#include "backends/wayland/lxqttaskbarbackendwayland.h" ILXQtTaskbarAbstractBackend *createWMBackend() { if(qGuiApp->nativeInterface()) return new LXQtTaskbarX11Backend; + else if(qGuiApp->nativeInterface()) + return new LXQtTaskbarWaylandBackend; qWarning() << "\n" << "ERROR: Could not create a backend for window managment operations.\n" From df73c3f260acb821bc864f34f6f472c599a72c21 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 17:12:45 +0100 Subject: [PATCH 110/165] LXQtTaskBarPlasmaWindowManagment: implement showDesktop() --- .../wayland/lxqttaskbarbackendwayland.cpp | 17 +++++++++++++++++ .../wayland/lxqttaskbarbackendwayland.h | 4 ++++ .../lxqttaskbarplasmawindowmanagment.cpp | 5 +++++ .../wayland/lxqttaskbarplasmawindowmanagment.h | 7 +++++++ 4 files changed, 33 insertions(+) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index a2a415504..d85ec99d8 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -372,6 +372,23 @@ bool LXQtTaskbarWaylandBackend::isAreaOverlapped(const QRect &area) const return false; } +bool LXQtTaskbarWaylandBackend::isShowingDesktop() const +{ + return m_managment->isShowingDesktop(); +} + +bool LXQtTaskbarWaylandBackend::showDesktop(bool value) +{ + enum LXQtTaskBarPlasmaWindowManagment::show_desktop flag_; + if(value) + flag_ = LXQtTaskBarPlasmaWindowManagment::show_desktop::show_desktop_enabled; + else + flag_ = LXQtTaskBarPlasmaWindowManagment::show_desktop::show_desktop_disabled; + + m_managment->show_desktop(flag_); + return true; +} + void LXQtTaskbarWaylandBackend::addWindow(LXQtTaskBarPlasmaWindow *window) { if (findWindow(windows, window) != windows.end() || transients.contains(window)) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.h b/panel/backends/wayland/lxqttaskbarbackendwayland.h index 553a7e585..77d857eb9 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.h +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.h @@ -65,6 +65,10 @@ class LXQtTaskbarWaylandBackend : public ILXQtTaskbarAbstractBackend // Panel internal virtual bool isAreaOverlapped(const QRect& area) const override; + // Show Destop + virtual bool isShowingDesktop() const override; + virtual bool showDesktop(bool value) override; + private: void addWindow(LXQtTaskBarPlasmaWindow *window); bool acceptWindow(LXQtTaskBarPlasmaWindow *window) const; diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp index 0a282eadd..3b765d3b3 100644 --- a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp +++ b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp @@ -1135,6 +1135,11 @@ LXQtTaskBarPlasmaWindowManagment::~LXQtTaskBarPlasmaWindowManagment() } } +void LXQtTaskBarPlasmaWindowManagment::org_kde_plasma_window_management_show_desktop_changed(uint32_t state) +{ + m_isShowingDesktop = (state == show_desktop::show_desktop_enabled); +} + void LXQtTaskBarPlasmaWindowManagment::org_kde_plasma_window_management_window_with_uuid(uint32_t id, const QString &uuid) { Q_UNUSED(id) diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h index 78c75693d..c21b57b3c 100644 --- a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h +++ b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h @@ -105,12 +105,19 @@ class LXQtTaskBarPlasmaWindowManagment : public QWaylandClientExtensionTemplate< LXQtTaskBarPlasmaWindowManagment(); ~LXQtTaskBarPlasmaWindowManagment(); + inline bool isShowingDesktop() const { return m_isShowingDesktop; } + +protected: + void org_kde_plasma_window_management_show_desktop_changed(uint32_t state) override; void org_kde_plasma_window_management_window_with_uuid(uint32_t id, const QString &uuid) override; void org_kde_plasma_window_management_stacking_order_uuid_changed(const QString &uuids) override; Q_SIGNALS: void windowCreated(LXQtTaskBarPlasmaWindow *window); void stackingOrderChanged(const QString &uuids); + +private: + bool m_isShowingDesktop = false; }; // class Q_DECL_HIDDEN WaylandTasksModel::Private From 9bfe823fa76a093aee447d4e10e2c8bff4c7ed9b Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 11:17:24 +0100 Subject: [PATCH 111/165] LXQtTaskbarWaylandBackend: do not show transient windows --- panel/backends/wayland/lxqttaskbarbackendwayland.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index d85ec99d8..5def725ef 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -614,6 +614,9 @@ bool LXQtTaskbarWaylandBackend::acceptWindow(LXQtTaskBarPlasmaWindow *window) co if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_skiptaskbar)) return false; + if(transients.contains(window)) + return false; + return true; } From af9887f5455390b27920de9a13ed1af5161e9567 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Tue, 20 Feb 2024 10:04:23 +0100 Subject: [PATCH 112/165] LXQtTaskBarPlasmaWindowManagment: fix destructor TODO TODO: is this correct? Seems to call wl_proxy_destroy underneath --- panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp | 4 ++-- panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp index 3b765d3b3..f813181ed 100644 --- a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp +++ b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp @@ -1123,7 +1123,7 @@ LXQtTaskBarPlasmaWindowManagment::LXQtTaskBarPlasmaWindowManagment() { connect(this, &QWaylandClientExtension::activeChanged, this, [this] { if (!isActive()) { - wl_proxy_destroy(reinterpret_cast(object())); + org_kde_plasma_window_management_destroy(object()); } }); } @@ -1131,7 +1131,7 @@ LXQtTaskBarPlasmaWindowManagment::LXQtTaskBarPlasmaWindowManagment() LXQtTaskBarPlasmaWindowManagment::~LXQtTaskBarPlasmaWindowManagment() { if (isActive()) { - wl_proxy_destroy(reinterpret_cast(object())); + org_kde_plasma_window_management_destroy(object()); } } diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h index c21b57b3c..976ac53e2 100644 --- a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h +++ b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h @@ -102,6 +102,7 @@ class LXQtTaskBarPlasmaWindowManagment : public QWaylandClientExtensionTemplate< Q_OBJECT public: static constexpr int version = 16; + LXQtTaskBarPlasmaWindowManagment(); ~LXQtTaskBarPlasmaWindowManagment(); From 71ec35c1a5118ee79fe0fc69f7a69ec4d8e6dbc7 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Wed, 21 Feb 2024 11:52:48 +0100 Subject: [PATCH 113/165] LXQtPanel: basic virtual desktop support on Plasma Wayland --- panel/CMakeLists.txt | 7 + .../wayland/lxqtplasmavirtualdesktop.cpp | 231 ++++++++++++++++++ .../wayland/lxqtplasmavirtualdesktop.h | 87 +++++++ .../wayland/lxqttaskbarbackendwayland.cpp | 18 +- .../wayland/lxqttaskbarbackendwayland.h | 3 + .../org-kde-plasma-virtual-desktop.xml | 110 +++++++++ 6 files changed, 454 insertions(+), 2 deletions(-) create mode 100644 panel/backends/wayland/lxqtplasmavirtualdesktop.cpp create mode 100644 panel/backends/wayland/lxqtplasmavirtualdesktop.h create mode 100644 panel/backends/wayland/protocols/org-kde-plasma-virtual-desktop.xml diff --git a/panel/CMakeLists.txt b/panel/CMakeLists.txt index 315b34dc3..b11ca5512 100644 --- a/panel/CMakeLists.txt +++ b/panel/CMakeLists.txt @@ -35,6 +35,7 @@ set(PRIV_HEADERS backends/wayland/lxqttaskbarbackendwayland.h backends/wayland/lxqttaskbarplasmawindowmanagment.h + backends/wayland/lxqtplasmavirtualdesktop.h ) # using LXQt namespace in the public headers. @@ -74,6 +75,7 @@ set(SOURCES backends/wayland/lxqttaskbarbackendwayland.cpp backends/wayland/lxqttaskbarplasmawindowmanagment.cpp + backends/wayland/lxqtplasmavirtualdesktop.cpp ) set(UI @@ -134,6 +136,11 @@ FILES ${CMAKE_CURRENT_SOURCE_DIR}/backends/wayland/protocols/plasma-window-management.xml ) +qt6_generate_wayland_protocol_client_sources(${PROJECT} +FILES + ${CMAKE_CURRENT_SOURCE_DIR}/backends/wayland/protocols/org-kde-plasma-virtual-desktop.xml +) + target_link_libraries(${PROJECT} ${LIBRARIES} ${QTX_LIBRARIES} diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp new file mode 100644 index 000000000..a3d987f06 --- /dev/null +++ b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp @@ -0,0 +1,231 @@ +#include "lxqtplasmavirtualdesktop.h" + +#include + +LXQtPlasmaVirtualDesktop::LXQtPlasmaVirtualDesktop(::org_kde_plasma_virtual_desktop *object, const QString &id) + : org_kde_plasma_virtual_desktop(object) + , id(id) +{ +} + +LXQtPlasmaVirtualDesktop::~LXQtPlasmaVirtualDesktop() +{ + wl_proxy_destroy(reinterpret_cast(object())); +} + +void LXQtPlasmaVirtualDesktop::org_kde_plasma_virtual_desktop_name(const QString &name) +{ + this->name = name; +} + +void LXQtPlasmaVirtualDesktop::org_kde_plasma_virtual_desktop_done() +{ + Q_EMIT done(); +} + +void LXQtPlasmaVirtualDesktop::org_kde_plasma_virtual_desktop_activated() +{ + Q_EMIT activated(); +} + +LXQtPlasmaVirtualDesktopManagment::LXQtPlasmaVirtualDesktopManagment() + : QWaylandClientExtensionTemplate(version) +{ + connect(this, &QWaylandClientExtension::activeChanged, this, [this] { + if (!isActive()) { + org_kde_plasma_virtual_desktop_management_destroy(object()); + } + }); +} + +LXQtPlasmaVirtualDesktopManagment::~LXQtPlasmaVirtualDesktopManagment() +{ + if (isActive()) { + org_kde_plasma_virtual_desktop_management_destroy(object()); + } +} + +void LXQtPlasmaVirtualDesktopManagment::org_kde_plasma_virtual_desktop_management_desktop_created(const QString &desktop_id, uint32_t position) +{ + emit desktopCreated(desktop_id, position); +} + +void LXQtPlasmaVirtualDesktopManagment::org_kde_plasma_virtual_desktop_management_desktop_removed(const QString &desktop_id) +{ + emit desktopRemoved(desktop_id); +} + +void LXQtPlasmaVirtualDesktopManagment::org_kde_plasma_virtual_desktop_management_rows(uint32_t rows) +{ + emit rowsChanged(rows); +} + +LXQtPlasmaWaylandWorkspaceInfo::LXQtPlasmaWaylandWorkspaceInfo() +{ + init(); +} + +LXQtPlasmaWaylandWorkspaceInfo::VirtualDesktopsIterator LXQtPlasmaWaylandWorkspaceInfo::findDesktop(const QString &id) const +{ + return std::find_if(virtualDesktops.begin(), virtualDesktops.end(), + [&id](const std::unique_ptr &desktop) { + return desktop->id == id; + }); +} + +void LXQtPlasmaWaylandWorkspaceInfo::init() +{ + virtualDesktopManagement = std::make_unique(); + + connect(virtualDesktopManagement.get(), &LXQtPlasmaVirtualDesktopManagment::activeChanged, this, [this] { + if (!virtualDesktopManagement->isActive()) { + rows = 0; + virtualDesktops.clear(); + currentVirtualDesktop.clear(); + Q_EMIT currentDesktopChanged(); + Q_EMIT numberOfDesktopsChanged(); + Q_EMIT navigationWrappingAroundChanged(); + Q_EMIT desktopIdsChanged(); + Q_EMIT desktopNamesChanged(); + Q_EMIT desktopLayoutRowsChanged(); + } + }); + + connect(virtualDesktopManagement.get(), &LXQtPlasmaVirtualDesktopManagment::desktopCreated, + this, &LXQtPlasmaWaylandWorkspaceInfo::addDesktop); + + connect(virtualDesktopManagement.get(), &LXQtPlasmaVirtualDesktopManagment::desktopRemoved, this, [this](const QString &id) { + + + std::remove_if(virtualDesktops.begin(), virtualDesktops.end(), + [id](const std::unique_ptr &desktop) + { + return desktop->id == id; + }); + + Q_EMIT numberOfDesktopsChanged(); + Q_EMIT desktopIdsChanged(); + Q_EMIT desktopNamesChanged(); + + if (currentVirtualDesktop == id) { + currentVirtualDesktop.clear(); + Q_EMIT currentDesktopChanged(); + } + }); + + connect(virtualDesktopManagement.get(), &LXQtPlasmaVirtualDesktopManagment::rowsChanged, this, [this](quint32 rows) { + this->rows = rows; + Q_EMIT desktopLayoutRowsChanged(); + }); +} + +void LXQtPlasmaWaylandWorkspaceInfo::addDesktop(const QString &id, quint32 position) +{ + if (findDesktop(id) != virtualDesktops.end()) { + return; + } + + auto desktop = std::make_unique(virtualDesktopManagement->get_virtual_desktop(id), id); + + connect(desktop.get(), &LXQtPlasmaVirtualDesktop::activated, this, [id, this]() { + currentVirtualDesktop = id; + Q_EMIT currentDesktopChanged(); + }); + + connect(desktop.get(), &LXQtPlasmaVirtualDesktop::done, this, [this]() { + Q_EMIT desktopNamesChanged(); + }); + + virtualDesktops.insert(std::next(virtualDesktops.begin(), position), std::move(desktop)); + + Q_EMIT numberOfDesktopsChanged(); + Q_EMIT desktopIdsChanged(); + Q_EMIT desktopNamesChanged(); +} + +QVariant LXQtPlasmaWaylandWorkspaceInfo::currentDesktop() const +{ + return currentVirtualDesktop; +} + +int LXQtPlasmaWaylandWorkspaceInfo::numberOfDesktops() const +{ + return virtualDesktops.size(); +} + +quint32 LXQtPlasmaWaylandWorkspaceInfo::position(const QVariant &desktop) const +{ + return std::distance(virtualDesktops.begin(), findDesktop(desktop.toString())); +} + +QVariantList LXQtPlasmaWaylandWorkspaceInfo::desktopIds() const +{ + QVariantList ids; + ids.reserve(virtualDesktops.size()); + + std::transform(virtualDesktops.cbegin(), virtualDesktops.cend(), std::back_inserter(ids), [](const std::unique_ptr &desktop) { + return desktop->id; + }); + return ids; +} + +QStringList LXQtPlasmaWaylandWorkspaceInfo::desktopNames() const +{ + if (!virtualDesktopManagement->isActive()) { + return QStringList(); + } + QStringList names; + names.reserve(virtualDesktops.size()); + + std::transform(virtualDesktops.cbegin(), virtualDesktops.cend(), std::back_inserter(names), [](const std::unique_ptr &desktop) { + return desktop->name; + }); + return names; +} + +int LXQtPlasmaWaylandWorkspaceInfo::desktopLayoutRows() const +{ + if (!virtualDesktopManagement->isActive()) { + return 0; + } + + return rows; +} + +void LXQtPlasmaWaylandWorkspaceInfo::requestActivate(const QVariant &desktop) +{ + if (!virtualDesktopManagement->isActive()) { + return; + } + + if (auto it = findDesktop(desktop.toString()); it != virtualDesktops.end()) { + (*it)->request_activate(); + } +} + +void LXQtPlasmaWaylandWorkspaceInfo::requestCreateDesktop(quint32 position) +{ + if (!virtualDesktopManagement->isActive()) { + return; + } + + //TODO: translatestd + virtualDesktopManagement->request_create_virtual_desktop(QLatin1String("New Desktop"), position); +} + +void LXQtPlasmaWaylandWorkspaceInfo::requestRemoveDesktop(quint32 position) +{ + if (!virtualDesktopManagement->isActive()) { + return; + } + if (virtualDesktops.size() == 1) { + return; + } + + if (position > (virtualDesktops.size() - 1)) { + return; + } + + virtualDesktopManagement->request_remove_virtual_desktop(virtualDesktops.at(position)->id); +} + diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.h b/panel/backends/wayland/lxqtplasmavirtualdesktop.h new file mode 100644 index 000000000..3dabb6242 --- /dev/null +++ b/panel/backends/wayland/lxqtplasmavirtualdesktop.h @@ -0,0 +1,87 @@ +#ifndef LXQTPLASMAVIRTUALDESKTOP_H +#define LXQTPLASMAVIRTUALDESKTOP_H + +#include +#include + +#include + +#include "qwayland-org-kde-plasma-virtual-desktop.h" + +class LXQtPlasmaVirtualDesktop : public QObject, public QtWayland::org_kde_plasma_virtual_desktop +{ + Q_OBJECT +public: + LXQtPlasmaVirtualDesktop(::org_kde_plasma_virtual_desktop *object, const QString &id); + ~LXQtPlasmaVirtualDesktop(); + const QString id; + QString name; +Q_SIGNALS: + void done(); + void activated(); + +protected: + void org_kde_plasma_virtual_desktop_name(const QString &name) override; + void org_kde_plasma_virtual_desktop_done() override; + void org_kde_plasma_virtual_desktop_activated() override; +}; + + +class LXQtPlasmaVirtualDesktopManagment : public QWaylandClientExtensionTemplate, + public QtWayland::org_kde_plasma_virtual_desktop_management +{ + Q_OBJECT +public: + static constexpr int version = 2; + + LXQtPlasmaVirtualDesktopManagment(); + ~LXQtPlasmaVirtualDesktopManagment(); + +signals: + void desktopCreated(const QString &id, quint32 position); + void desktopRemoved(const QString &id); + void rowsChanged(const quint32 rows); + +protected: + virtual void org_kde_plasma_virtual_desktop_management_desktop_created(const QString &desktop_id, uint32_t position) override; + virtual void org_kde_plasma_virtual_desktop_management_desktop_removed(const QString &desktop_id) override; + virtual void org_kde_plasma_virtual_desktop_management_rows(uint32_t rows) override; +}; + +class Q_DECL_HIDDEN LXQtPlasmaWaylandWorkspaceInfo : public QObject +{ + Q_OBJECT +public: + LXQtPlasmaWaylandWorkspaceInfo(); + + QVariant currentVirtualDesktop; + std::vector> virtualDesktops; + std::unique_ptr virtualDesktopManagement; + quint32 rows; + + typedef std::vector>::const_iterator VirtualDesktopsIterator; + + VirtualDesktopsIterator findDesktop(const QString &id) const; + + void init(); + void addDesktop(const QString &id, quint32 position); + QVariant currentDesktop() const; + int numberOfDesktops() const; + QVariantList desktopIds() const; + QStringList desktopNames() const; + quint32 position(const QVariant &desktop) const; + int desktopLayoutRows() const; + void requestActivate(const QVariant &desktop); + void requestCreateDesktop(quint32 position); + void requestRemoveDesktop(quint32 position); + +signals: + void currentDesktopChanged(); + void numberOfDesktopsChanged(); + void navigationWrappingAroundChanged(); + void desktopIdsChanged(); + void desktopNamesChanged(); + void desktopLayoutRowsChanged(); +}; + +#endif // LXQTPLASMAVIRTUALDESKTOP_H diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index 5def725ef..57b77c906 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -1,6 +1,7 @@ #include "lxqttaskbarbackendwayland.h" #include "lxqttaskbarplasmawindowmanagment.h" +#include "lxqtplasmavirtualdesktop.h" #include #include @@ -25,6 +26,7 @@ LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) : ILXQtTaskbarAbstractBackend(parent) { m_managment.reset(new LXQtTaskBarPlasmaWindowManagment); + m_workspaceInfo.reset(new LXQtPlasmaWaylandWorkspaceInfo); connect(m_managment.get(), &LXQtTaskBarPlasmaWindowManagment::windowCreated, this, [this](LXQtTaskBarPlasmaWindow *window) { connect(window, &LXQtTaskBarPlasmaWindow::initialStateDone, this, [this, window] { @@ -39,6 +41,17 @@ LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) : // // this->dataChanged(window.get(), StackingOrder); // // } // }); + + connect(m_workspaceInfo.get(), &LXQtPlasmaWaylandWorkspaceInfo::currentDesktopChanged, this, + [this](){ + int idx = m_workspaceInfo->position(m_workspaceInfo->currentDesktop()); + emit currentWorkspaceChanged(idx); + }); + + connect(m_workspaceInfo.get(), &LXQtPlasmaWaylandWorkspaceInfo::numberOfDesktopsChanged, + this, &ILXQtTaskbarAbstractBackend::workspacesCountChanged); + + //TODO: connect name changed } bool LXQtTaskbarWaylandBackend::supportsAction(WId windowId, LXQtTaskBarBackendAction action) const @@ -291,12 +304,13 @@ WId LXQtTaskbarWaylandBackend::getActiveWindow() const int LXQtTaskbarWaylandBackend::getWorkspacesCount() const { - return 1; //TODO + return m_workspaceInfo->numberOfDesktops(); } QString LXQtTaskbarWaylandBackend::getWorkspaceName(int idx) const { - return QStringLiteral("TestWorkspace"); + //TODO: optimize + return m_workspaceInfo->desktopNames().value(idx, QStringLiteral("ERROR")); } int LXQtTaskbarWaylandBackend::getCurrentWorkspace() const diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.h b/panel/backends/wayland/lxqttaskbarbackendwayland.h index 77d857eb9..0b12ef2fd 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.h +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.h @@ -8,6 +8,7 @@ class LXQtTaskBarPlasmaWindow; class LXQtTaskBarPlasmaWindowManagment; +class LXQtPlasmaWaylandWorkspaceInfo; class LXQtTaskbarWaylandBackend : public ILXQtTaskbarAbstractBackend @@ -77,6 +78,8 @@ class LXQtTaskbarWaylandBackend : public ILXQtTaskbarAbstractBackend private: LXQtTaskBarPlasmaWindow *getWindow(WId windowId) const; + std::unique_ptr m_workspaceInfo; + std::unique_ptr m_managment; QHash lastActivated; diff --git a/panel/backends/wayland/protocols/org-kde-plasma-virtual-desktop.xml b/panel/backends/wayland/protocols/org-kde-plasma-virtual-desktop.xml new file mode 100644 index 000000000..0e0551b0a --- /dev/null +++ b/panel/backends/wayland/protocols/org-kde-plasma-virtual-desktop.xml @@ -0,0 +1,110 @@ + + + + + + + + Given the id of a particular virtual desktop, get the corresponding org_kde_plasma_virtual_desktop which represents only the desktop with that id. + + + + + + + + Ask the server to create a new virtual desktop, and position it at a specified position. If the position is zero or less, it will be positioned at the beginning, if the position is the count or more, it will be positioned at the end. + + + + + + + + Ask the server to get rid of a virtual desktop, the server may or may not acconsent to the request. + + + + + + + + + + + + + + + + + + + This event is sent after all other properties has been + sent after binding to the desktop manager object and after any + other property changes done after that. This allows + changes to the org_kde_plasma_virtual_desktop_management properties to be seen as + atomic, even if they happen via multiple events. + + + + + + + + + + + + + Request the server to set the status of this desktop to active: The server is free to consent or deny the request. This will be the new "current" virtual desktop of the system. + + + + + + The format of the id is decided by the compositor implementation. A desktop id univocally identifies a virtual desktop and must be guaranteed to never exist two desktops with the same id. The format of the string id is up to the server implementation. + + + + + + + + + + + The desktop will be the new "current" desktop of the system. The server may support either one virtual desktop active at a time, or other combinations such as one virtual desktop active per screen. + Windows associated to this virtual desktop will be shown. + + + + + + Windows that were associated only to this desktop will be hidden. + + + + + + This event is sent after all other properties has been + sent after binding to the desktop object and after any + other property changes done after that. This allows + changes to the org_kde_plasma_virtual_desktop properties to be seen as + atomic, even if they happen via multiple events. + + + + + + This virtual desktop has just been removed by the server: + All windows will lose the association to this desktop. + + + + + From c9689508c07775a66b4f11488d041dee720ecb95 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 17 Feb 2024 23:32:46 +0100 Subject: [PATCH 114/165] LXQtPanel: workaround KAcceleratorManager changing button text FIXME TODO TODO: is this correct approach? --- plugin-taskbar/lxqttaskbutton.cpp | 27 ++++++++++++++++++++++++++- plugin-taskbar/lxqttaskbutton.h | 4 ++++ plugin-taskbar/lxqttaskgroup.cpp | 6 +++--- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 019b45199..45e836893 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -128,7 +128,7 @@ LXQtTaskButton::~LXQtTaskButton() = default; void LXQtTaskButton::updateText() { QString title = mBackend->getWindowTitle(mWindow); - setText(title.replace(QStringLiteral("&"), QStringLiteral("&&"))); + setTextExplicitly(title.replace(QStringLiteral("&"), QStringLiteral("&&"))); setToolTip(title); } @@ -314,6 +314,30 @@ QMimeData * LXQtTaskButton::mimeData() return mimedata; } +/*! + * \brief LXQtTaskButton::setTextExplicitly + * \param str + * + * This is needed to workaround flickering caused by KAcceleratorManager + * This class is hooked by KDE Integration and adds accelerators to button text + * (Adds some '&' characters) + * This triggers widget update but soon after text is reset to original value + * This triggers a KAcceleratorManager update which again adds accelerator + * This happens in loop + * + * TODO: investigate proper solution + */ +void LXQtTaskButton::setTextExplicitly(const QString &str) +{ + if(str == mExplicitlySetText) + { + return; + } + + mExplicitlySetText = str; + setText(mExplicitlySetText); +} + /************************************************ ************************************************/ @@ -688,6 +712,7 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) menu->addSeparator(); a = menu->addAction(XdgIcon::fromTheme(QStringLiteral("process-stop")), tr("&Close")); connect(a, &QAction::triggered, this, &LXQtTaskButton::closeApplication); + menu->setGeometry(mParentTaskBar->panel()->calculatePopupWindowPos(mapToGlobal(event->pos()), menu->sizeHint())); mPlugin->willShowWindow(menu); menu->show(); diff --git a/plugin-taskbar/lxqttaskbutton.h b/plugin-taskbar/lxqttaskbutton.h index 9ccca36fa..6d5841df4 100644 --- a/plugin-taskbar/lxqttaskbutton.h +++ b/plugin-taskbar/lxqttaskbutton.h @@ -122,6 +122,8 @@ public slots: inline ILXQtPanelPlugin * plugin() const { return mPlugin; } + void setTextExplicitly(const QString& str); + protected: //TODO: public getter instead? ILXQtTaskbarAbstractBackend *mBackend; @@ -138,6 +140,8 @@ public slots: int mIconSize; int mWheelDelta; + QString mExplicitlySetText; + // Timer for when draggind something into a button (the button's window // must be activated so that the use can continue dragging to the window QTimer * mDNDTimer; diff --git a/plugin-taskbar/lxqttaskgroup.cpp b/plugin-taskbar/lxqttaskgroup.cpp index c6075df89..f7da5ef21 100644 --- a/plugin-taskbar/lxqttaskgroup.cpp +++ b/plugin-taskbar/lxqttaskgroup.cpp @@ -57,7 +57,7 @@ LXQtTaskGroup::LXQtTaskGroup(const QString &groupName, WId window, LXQtTaskBar * Q_ASSERT(parent); setObjectName(groupName); - setText(groupName); + setTextExplicitly(groupName); connect(this, &LXQtTaskGroup::clicked, this, &LXQtTaskGroup::onClicked); connect(parent, &LXQtTaskBar::buttonRotationRefreshed, this, &LXQtTaskGroup::setAutoRotation); @@ -336,7 +336,7 @@ void LXQtTaskGroup::regroup() if (button) { - setText(button->text()); + setTextExplicitly(button->text()); setToolTip(button->toolTip()); setWindowId(button->windowId()); } @@ -347,7 +347,7 @@ void LXQtTaskGroup::regroup() { mSingleButton = false; QString t = QString(QStringLiteral("%1 - %2 windows")).arg(mGroupName).arg(cont); - setText(t); + setTextExplicitly(t); setToolTip(parentTaskBar()->isShowGroupOnHover() ? QString() : t); } } From 0008660915f4e765bc20e505a2b0d61fc5e31504 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 14:19:31 +0100 Subject: [PATCH 115/165] Add desktop file to be recognized by KWin Wayland NOTE: absolute path is needed inside .desktop file for this to work use CMake to get it. - Prevent double dekstop file installed in autostart --- autostart/CMakeLists.txt | 11 ++++++++++- autostart/lxqt-panel_wayland.desktop.in | 13 +++++++++++++ panel/lxqtpanelapplication.cpp | 2 ++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 autostart/lxqt-panel_wayland.desktop.in diff --git a/autostart/CMakeLists.txt b/autostart/CMakeLists.txt index 098103168..6d044738b 100644 --- a/autostart/CMakeLists.txt +++ b/autostart/CMakeLists.txt @@ -1,4 +1,4 @@ -file(GLOB DESKTOP_FILES_IN *.desktop.in) +set(DESKTOP_FILES lxqt-panel.desktop.in) # Translations ********************************** lxqt_translate_desktop(DESKTOP_FILES @@ -14,3 +14,12 @@ install(FILES DESTINATION "${LXQT_ETC_XDG_DIR}/autostart" COMPONENT Runtime ) + +configure_file(lxqt-panel_wayland.desktop.in lxqt-panel_wayland.desktop @ONLY) + +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/lxqt-panel_wayland.desktop" + DESTINATION "/usr/share/applications" + RENAME "lxqt-panel.desktop" + COMPONENT Runtime +) diff --git a/autostart/lxqt-panel_wayland.desktop.in b/autostart/lxqt-panel_wayland.desktop.in new file mode 100644 index 000000000..089082aea --- /dev/null +++ b/autostart/lxqt-panel_wayland.desktop.in @@ -0,0 +1,13 @@ +[Desktop Entry] +Type=Application +TryExec=lxqt-panel + +# NOTE: KWin wants absolute path here, get it from CMake install path +Exec=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@/lxqt-panel + +# NOTE: adding KDE to make it work under Plasma Wayland session +OnlyShowIn=LXQt;KDE +X-LXQt-Module=true + +# Make KWin recognize us as priviledged client +X-KDE-Wayland-Interfaces=org_kde_plasma_window_management diff --git a/panel/lxqtpanelapplication.cpp b/panel/lxqtpanelapplication.cpp index 507163221..a3d2132f1 100644 --- a/panel/lxqtpanelapplication.cpp +++ b/panel/lxqtpanelapplication.cpp @@ -107,6 +107,8 @@ LXQtPanelApplication::LXQtPanelApplication(int& argc, char** argv) QCoreApplication::setApplicationVersion(VERINFO); + QGuiApplication::setDesktopFileName(QLatin1String("lxqt-panel")); + QCommandLineParser parser; parser.setApplicationDescription(QLatin1String("LXQt Panel")); parser.addHelpOption(); From 4a75306eccdf259f7c98b3f24ed5a0c214e944b0 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 18:01:49 +0100 Subject: [PATCH 116/165] LXQtTaskbarWaylandBackend: return only accepted windows - reloadWindows() force removal and readding of windows This fixes changing windows grouping settings and adding taskbar plugin AFTER panel is started. Both situations resulted in empty taskbar previously --- .../wayland/lxqttaskbarbackendwayland.cpp | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index 57b77c906..27e6b97e5 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -97,15 +97,30 @@ bool LXQtTaskbarWaylandBackend::supportsAction(WId windowId, LXQtTaskBarBackendA bool LXQtTaskbarWaylandBackend::reloadWindows() { - return false; //TODO + const QVector wids = getCurrentWindows(); + + // Force removal and re-adding + for(WId windowId : wids) + { + emit windowRemoved(windowId); + } + for(WId windowId : wids) + { + emit windowAdded(windowId); + } + + return true; } QVector LXQtTaskbarWaylandBackend::getCurrentWindows() const { - QVector wids(windows.size()); - for(const auto& window : std::as_const(windows)) + QVector wids; + wids.reserve(wids.size()); + + for(const std::unique_ptr& window : std::as_const(windows)) { - wids << window->getWindowId(); + if(window->acceptedInTaskBar) + wids << window->getWindowId(); } return wids; } From 41579e26d6f6ff9ad2e48093746b897f664f53ef Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 23 Feb 2024 12:27:57 +0100 Subject: [PATCH 117/165] LXQtTaskbarWaylandBackend: fix workspace logic --- .../wayland/lxqtplasmavirtualdesktop.cpp | 31 ++++++++++++++----- .../wayland/lxqtplasmavirtualdesktop.h | 8 +++-- .../wayland/lxqttaskbarbackendwayland.cpp | 20 ++++++++---- 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp index a3d987f06..fe3d3a435 100644 --- a/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp +++ b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp @@ -16,6 +16,7 @@ LXQtPlasmaVirtualDesktop::~LXQtPlasmaVirtualDesktop() void LXQtPlasmaVirtualDesktop::org_kde_plasma_virtual_desktop_name(const QString &name) { this->name = name; + Q_EMIT nameChanged(); } void LXQtPlasmaVirtualDesktop::org_kde_plasma_virtual_desktop_done() @@ -73,6 +74,20 @@ LXQtPlasmaWaylandWorkspaceInfo::VirtualDesktopsIterator LXQtPlasmaWaylandWorkspa }); } +QString LXQtPlasmaWaylandWorkspaceInfo::getDesktopName(int pos) const +{ + if(pos < 0 || pos >= virtualDesktops.size()) + return QString(); + return virtualDesktops[pos]->name; +} + +QString LXQtPlasmaWaylandWorkspaceInfo::getDesktopId(int pos) const +{ + if(pos < 0 || pos >= virtualDesktops.size()) + return QString(); + return virtualDesktops[pos]->id; +} + void LXQtPlasmaWaylandWorkspaceInfo::init() { virtualDesktopManagement = std::make_unique(); @@ -86,7 +101,6 @@ void LXQtPlasmaWaylandWorkspaceInfo::init() Q_EMIT numberOfDesktopsChanged(); Q_EMIT navigationWrappingAroundChanged(); Q_EMIT desktopIdsChanged(); - Q_EMIT desktopNamesChanged(); Q_EMIT desktopLayoutRowsChanged(); } }); @@ -105,7 +119,6 @@ void LXQtPlasmaWaylandWorkspaceInfo::init() Q_EMIT numberOfDesktopsChanged(); Q_EMIT desktopIdsChanged(); - Q_EMIT desktopNamesChanged(); if (currentVirtualDesktop == id) { currentVirtualDesktop.clear(); @@ -119,7 +132,7 @@ void LXQtPlasmaWaylandWorkspaceInfo::init() }); } -void LXQtPlasmaWaylandWorkspaceInfo::addDesktop(const QString &id, quint32 position) +void LXQtPlasmaWaylandWorkspaceInfo::addDesktop(const QString &id, quint32 pos) { if (findDesktop(id) != virtualDesktops.end()) { return; @@ -132,15 +145,19 @@ void LXQtPlasmaWaylandWorkspaceInfo::addDesktop(const QString &id, quint32 posit Q_EMIT currentDesktopChanged(); }); - connect(desktop.get(), &LXQtPlasmaVirtualDesktop::done, this, [this]() { - Q_EMIT desktopNamesChanged(); + connect(desktop.get(), &LXQtPlasmaVirtualDesktop::nameChanged, this, [id, this]() { + Q_EMIT desktopNameChanged(position(id)); + }); + + connect(desktop.get(), &LXQtPlasmaVirtualDesktop::done, this, [id, this]() { + Q_EMIT desktopNameChanged(position(id)); }); - virtualDesktops.insert(std::next(virtualDesktops.begin(), position), std::move(desktop)); + virtualDesktops.insert(std::next(virtualDesktops.begin(), pos), std::move(desktop)); Q_EMIT numberOfDesktopsChanged(); Q_EMIT desktopIdsChanged(); - Q_EMIT desktopNamesChanged(); + Q_EMIT desktopNameChanged(position(id)); } QVariant LXQtPlasmaWaylandWorkspaceInfo::currentDesktop() const diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.h b/panel/backends/wayland/lxqtplasmavirtualdesktop.h index 3dabb6242..fff025213 100644 --- a/panel/backends/wayland/lxqtplasmavirtualdesktop.h +++ b/panel/backends/wayland/lxqtplasmavirtualdesktop.h @@ -19,6 +19,7 @@ class LXQtPlasmaVirtualDesktop : public QObject, public QtWayland::org_kde_plasm Q_SIGNALS: void done(); void activated(); + void nameChanged(); protected: void org_kde_plasma_virtual_desktop_name(const QString &name) override; @@ -63,8 +64,11 @@ class Q_DECL_HIDDEN LXQtPlasmaWaylandWorkspaceInfo : public QObject VirtualDesktopsIterator findDesktop(const QString &id) const; + QString getDesktopName(int pos) const; + QString getDesktopId(int pos) const; + void init(); - void addDesktop(const QString &id, quint32 position); + void addDesktop(const QString &id, quint32 pos); QVariant currentDesktop() const; int numberOfDesktops() const; QVariantList desktopIds() const; @@ -80,7 +84,7 @@ class Q_DECL_HIDDEN LXQtPlasmaWaylandWorkspaceInfo : public QObject void numberOfDesktopsChanged(); void navigationWrappingAroundChanged(); void desktopIdsChanged(); - void desktopNamesChanged(); + void desktopNameChanged(quint32 position); void desktopLayoutRowsChanged(); }; diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index 27e6b97e5..8aa214c65 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -45,13 +45,17 @@ LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) : connect(m_workspaceInfo.get(), &LXQtPlasmaWaylandWorkspaceInfo::currentDesktopChanged, this, [this](){ int idx = m_workspaceInfo->position(m_workspaceInfo->currentDesktop()); + idx += 1; // Make 1-based emit currentWorkspaceChanged(idx); }); connect(m_workspaceInfo.get(), &LXQtPlasmaWaylandWorkspaceInfo::numberOfDesktopsChanged, this, &ILXQtTaskbarAbstractBackend::workspacesCountChanged); - //TODO: connect name changed + connect(m_workspaceInfo.get(), &LXQtPlasmaWaylandWorkspaceInfo::desktopNameChanged, + this, [this](int idx) { + emit workspaceNameChanged(idx + 1); // Make 1-based + }); } bool LXQtTaskbarWaylandBackend::supportsAction(WId windowId, LXQtTaskBarBackendAction action) const @@ -324,19 +328,23 @@ int LXQtTaskbarWaylandBackend::getWorkspacesCount() const QString LXQtTaskbarWaylandBackend::getWorkspaceName(int idx) const { - //TODO: optimize - return m_workspaceInfo->desktopNames().value(idx, QStringLiteral("ERROR")); + return m_workspaceInfo->getDesktopName(idx - 1); //Return to 0-based } int LXQtTaskbarWaylandBackend::getCurrentWorkspace() const { - return 0; //TODO + if(!m_workspaceInfo->currentDesktop().isValid()) + return 0; + return m_workspaceInfo->position(m_workspaceInfo->currentDesktop()) + 1; // 1-based } bool LXQtTaskbarWaylandBackend::setCurrentWorkspace(int idx) { - Q_UNUSED(idx) - return false; //TODO + QString id = m_workspaceInfo->getDesktopId(idx - 1); + if(id.isEmpty()) + return false; + m_workspaceInfo->requestActivate(id); + return true; } int LXQtTaskbarWaylandBackend::getWindowWorkspace(WId windowId) const From 8d2c37a3de0328737f2d74b7a75e8405d746da7f Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 23 Feb 2024 12:58:54 +0100 Subject: [PATCH 118/165] LXQtTaskbarWaylandBackend: fix workspace removal logic --- panel/backends/wayland/lxqtplasmavirtualdesktop.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp index fe3d3a435..b3e49a681 100644 --- a/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp +++ b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp @@ -111,11 +111,12 @@ void LXQtPlasmaWaylandWorkspaceInfo::init() connect(virtualDesktopManagement.get(), &LXQtPlasmaVirtualDesktopManagment::desktopRemoved, this, [this](const QString &id) { - std::remove_if(virtualDesktops.begin(), virtualDesktops.end(), - [id](const std::unique_ptr &desktop) - { - return desktop->id == id; - }); + virtualDesktops.erase(std::remove_if(virtualDesktops.begin(), virtualDesktops.end(), + [id](const std::unique_ptr &desktop) + { + return desktop->id == id; + }), + virtualDesktops.end()); Q_EMIT numberOfDesktopsChanged(); Q_EMIT desktopIdsChanged(); From 3bd87e9b84f958a0b97a5198f056506929989b5f Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 23 Feb 2024 13:33:40 +0100 Subject: [PATCH 119/165] lxqttaskbartypes.h: fix ShowOnAll desktops flag value --- panel/backends/lxqttaskbartypes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panel/backends/lxqttaskbartypes.h b/panel/backends/lxqttaskbartypes.h index 9e12092bb..ae3d5f107 100644 --- a/panel/backends/lxqttaskbartypes.h +++ b/panel/backends/lxqttaskbartypes.h @@ -49,7 +49,7 @@ enum class LXQtTaskBarWindowLayer enum class LXQtTaskBarWorkspace { - ShowOnAll = -1 + ShowOnAll = 0 // Virtual destops have 1-based indexes }; #endif // LXQTTASKBARTYPES_H From 8b6375d3542ee4d18fbab4ade274dcf02d4ee866 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 23 Feb 2024 13:34:16 +0100 Subject: [PATCH 120/165] LXQtTaskbarWaylandBackend: implement moving window to virtual desktop workspace --- .../wayland/lxqttaskbarbackendwayland.cpp | 51 ++++++++++++++++--- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index 8aa214c65..fb6e940a8 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -340,7 +340,7 @@ int LXQtTaskbarWaylandBackend::getCurrentWorkspace() const bool LXQtTaskbarWaylandBackend::setCurrentWorkspace(int idx) { - QString id = m_workspaceInfo->getDesktopId(idx - 1); + QString id = m_workspaceInfo->getDesktopId(idx - 1); //Return to 0-based if(id.isEmpty()) return false; m_workspaceInfo->requestActivate(id); @@ -349,15 +349,54 @@ bool LXQtTaskbarWaylandBackend::setCurrentWorkspace(int idx) int LXQtTaskbarWaylandBackend::getWindowWorkspace(WId windowId) const { - Q_UNUSED(windowId) - return 0; //TODO + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return 0; + + // TODO: this protocol seems to allow multiple desktop for each window + // We do not support that yet + // Also from KDE Plasma task switch it's not clear how to actually put + // a window on multiple desktops (which is different from "All desktops") + QString id = window->virtualDesktops.value(0, QString()); + if(id.isEmpty()) + return 0; + + return m_workspaceInfo->position(id) + 1; //Make 1-based } bool LXQtTaskbarWaylandBackend::setWindowOnWorkspace(WId windowId, int idx) { - Q_UNUSED(windowId) - Q_UNUSED(idx) - return false; //TODO + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + // Prepare for future multiple virtual desktops per window + QList newDesktops; + + // Fill the list + newDesktops.append(m_workspaceInfo->getDesktopId(idx - 1)); //Return to 0-based + + // Keep only valid IDs + newDesktops.erase(std::remove_if(newDesktops.begin(), newDesktops.end(), + [](const QString& id) { return id.isEmpty(); }), + newDesktops.end()); + + // Add to new requested desktops + for(const QString& id : std::as_const(newDesktops)) + { + if(!window->virtualDesktops.contains(id)) + window->request_enter_virtual_desktop(id); + } + + // Remove from non-requested destops + const QList currentDesktops = window->virtualDesktops; + for(const QString& id : std::as_const(currentDesktops)) + { + if(!newDesktops.contains(id)) + window->request_leave_virtual_desktop(id); + } + + return true; } void LXQtTaskbarWaylandBackend::moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) From 02364071b3ea5a541185f4de731b756dd167d32c Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Mon, 26 Feb 2024 09:48:03 +0100 Subject: [PATCH 121/165] ColorPicker: use XDG Desktop Portal on Wayland TODO TODO: show error message when not supported --- plugin-colorpicker/colorpicker.cpp | 156 ++++++++++++++++++++++++++--- plugin-colorpicker/colorpicker.h | 8 ++ 2 files changed, 150 insertions(+), 14 deletions(-) diff --git a/plugin-colorpicker/colorpicker.cpp b/plugin-colorpicker/colorpicker.cpp index 004314557..0827686d5 100644 --- a/plugin-colorpicker/colorpicker.cpp +++ b/plugin-colorpicker/colorpicker.cpp @@ -36,6 +36,9 @@ #include #include +#include +#include + //NOTE: Xlib.h defines Bool which conflicts with QJsonValue::Type enum #include #undef Bool @@ -77,6 +80,33 @@ void ColorPicker::realign() mWidget.update(panel()->lineCount() <= 1 ? panel()->isHorizontal() : !panel()->isHorizontal()); } +void ColorPicker::queryXDGSupport() +{ + if (qEnvironmentVariableIntValue("QT_NO_XDG_DESKTOP_PORTAL") > 0) { + return; + } + QDBusMessage message = QDBusMessage::createMethodCall( + QLatin1String("org.freedesktop.portal.Desktop"), + QLatin1String("/org/freedesktop/portal/desktop"), + QLatin1String("org.freedesktop.DBus.Properties"), + QLatin1String("Get")); + message << QLatin1String("org.freedesktop.portal.Screenshot") + << QLatin1String("version"); + + QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message); + auto watcher = new QDBusPendingCallWatcher(pendingCall); + QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher, + [this](QDBusPendingCallWatcher *watcher) { + watcher->deleteLater(); + QDBusPendingReply reply = *watcher; + if (!reply.isError() && reply.value().toUInt() >= 2) + m_hasScreenshotPortalWithColorPicking = true; + }); + + //TODO: show error tooltip if not supported + //NOTE: on Wayland we cannot pick color without it +} + ColorPickerWidget::ColorPickerWidget(QWidget *parent) : QWidget(parent) { @@ -108,7 +138,8 @@ ColorPickerWidget::ColorPickerWidget(QWidget *parent) : QWidget(parent) layout->addWidget(mColorButton); setLayout(layout); - connect(mPickerButton, &QToolButton::clicked, this, &ColorPickerWidget::captureMouse); + connect(mPickerButton, &QToolButton::clicked, this, &ColorPickerWidget::startCapturingColor); + connect(mColorButton, &QToolButton::clicked, this, [&]() { buildMenu(); @@ -162,29 +193,86 @@ void ColorPickerWidget::mouseReleaseEvent(QMouseEvent *event) qWarning() << "WAYLAND does not support grabbing windows"; } - mColorButton->setColor(col); - paste(col.name()); + setCapturedColor(col); - if (mColorsList.contains(col)) + mCapturing = false; + releaseMouse(); + + if (!mPickerButton->contentsRect().contains(mapFromGlobal(QCursor::pos()))) { - mColorsList.move(mColorsList.indexOf(col), 0); + QApplication::sendEvent(mPickerButton, new QEvent(QEvent::Leave)); } - else +} + +void ColorPickerWidget::startCapturingColor() +{ + //NOTE: see qt6 `src/gui/platform/unix/qgenericunixservices.cpp` + + // Make double sure that we are in a wayland environment. In particular check + // WAYLAND_DISPLAY so also XWayland apps benefit from portal-based color picking. + // Outside wayland we'll rather rely on other means than the XDG desktop portal. + if (!qEnvironmentVariableIsEmpty("WAYLAND_DISPLAY") + || QGuiApplication::platformName().startsWith(QLatin1String("wayland"))) { - mColorsList.prepend(col); + // On Wayland use XDG Desktop Portal + + QString m_parentWindowId; //TODO + + QDBusMessage message = QDBusMessage::createMethodCall( + QLatin1String("org.freedesktop.portal.Desktop"), + QLatin1String("/org/freedesktop/portal/desktop"), + QLatin1String("org.freedesktop.portal.Screenshot"), + QLatin1String("PickColor")); + message << m_parentWindowId << QVariantMap(); + + QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message); + auto watcher = new QDBusPendingCallWatcher(pendingCall, this); + connect(watcher, &QDBusPendingCallWatcher::finished, this, + [this](QDBusPendingCallWatcher *watcher) { + watcher->deleteLater(); + QDBusPendingReply reply = *watcher; + if (reply.isError()) { + qWarning("DBus call to pick color failed: %s", + qPrintable(reply.error().message())); + setCapturedColor({}); + } else { + QDBusConnection::sessionBus().connect( + QLatin1String("org.freedesktop.portal.Desktop"), + reply.value().path(), + QLatin1String("org.freedesktop.portal.Request"), + QLatin1String("Response"), + this, + // clang-format off + SLOT(gotColorResponse(uint,QVariantMap)) + // clang-format on + ); + } + }); } - - if (mColorsList.size() > 10) + else if (qGuiApp->nativeInterface()) { - mColorsList.removeLast(); + // On X11 grab mouse and let `mouseReleaseEvent()` retrieve color + captureMouse(); } +} - mCapturing = false; - releaseMouse(); +void ColorPickerWidget::setCapturedColor(const QColor &color) +{ + mColorButton->setColor(color); + paste(color.name()); - if (!mPickerButton->contentsRect().contains(mapFromGlobal(QCursor::pos()))) + if (mColorsList.contains(color)) { - QApplication::sendEvent(mPickerButton, new QEvent(QEvent::Leave)); + mColorsList.move(mColorsList.indexOf(color), 0); + } + else + { + mColorsList.prepend(color); + } + + if (mColorsList.size() > 10) + { + mColorsList.removeLast(); } } @@ -195,6 +283,46 @@ void ColorPickerWidget::captureMouse() mCapturing = true; } +struct XDGDesktopColor +{ + double r = 0; + double g = 0; + double b = 0; + + QColor toQColor() const + { + constexpr auto rgbMax = 255; + return { static_cast(r * rgbMax), static_cast(g * rgbMax), + static_cast(b * rgbMax) }; + } +}; + +const QDBusArgument &operator>>(const QDBusArgument &argument, XDGDesktopColor &myStruct) +{ + argument.beginStructure(); + argument >> myStruct.r >> myStruct.g >> myStruct.b; + argument.endStructure(); + return argument; +} + +void ColorPickerWidget::gotColorResponse(uint result, const QVariantMap &map) +{ + auto colorProp = QStringLiteral("color"); + + if (result != 0) + return; + if (map.contains(colorProp)) + { + XDGDesktopColor color{}; + map.value(colorProp).value() >> color; + setCapturedColor(color.toQColor()); + } + else + { + setCapturedColor({}); + } +} + QIcon ColorPickerWidget::colorIcon(QColor color) { diff --git a/plugin-colorpicker/colorpicker.h b/plugin-colorpicker/colorpicker.h index 919f42490..ecf2a3b41 100644 --- a/plugin-colorpicker/colorpicker.h +++ b/plugin-colorpicker/colorpicker.h @@ -58,7 +58,10 @@ class ColorPickerWidget : public QWidget void mouseReleaseEvent(QMouseEvent *event); private slots: + void startCapturingColor(); + void setCapturedColor(const QColor& color); void captureMouse(); + void gotColorResponse(uint result, const QVariantMap& map); private: static const QString svgIcon; @@ -91,8 +94,13 @@ class ColorPicker : public QObject, public ILXQtPanelPlugin virtual void realign() override; +private: + void queryXDGSupport(); + private: ColorPickerWidget mWidget; + + bool m_hasScreenshotPortalWithColorPicking = false; }; class ColorPickerLibrary: public QObject, public ILXQtPanelPluginLibrary From 4970e5082ad9518284c6f89366768429273080b0 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Wed, 28 Feb 2024 20:43:50 +0100 Subject: [PATCH 122/165] LXQtPlasmaWaylandWorkspaceInfo: fix signedness comparison --- panel/backends/wayland/lxqtplasmavirtualdesktop.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp index b3e49a681..01e28fe75 100644 --- a/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp +++ b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp @@ -76,14 +76,14 @@ LXQtPlasmaWaylandWorkspaceInfo::VirtualDesktopsIterator LXQtPlasmaWaylandWorkspa QString LXQtPlasmaWaylandWorkspaceInfo::getDesktopName(int pos) const { - if(pos < 0 || pos >= virtualDesktops.size()) + if(pos < 0 || size_t(pos) >= virtualDesktops.size()) return QString(); return virtualDesktops[pos]->name; } QString LXQtPlasmaWaylandWorkspaceInfo::getDesktopId(int pos) const { - if(pos < 0 || pos >= virtualDesktops.size()) + if(pos < 0 || size_t(pos) >= virtualDesktops.size()) return QString(); return virtualDesktops[pos]->id; } From 1d52a4e527cc2e5bf34c4e95a433007e07e4ca3f Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 29 Feb 2024 00:09:55 +0100 Subject: [PATCH 123/165] CMake: move panel WM backends to separate libraries --- panel/CMakeLists.txt | 47 +++---------------- panel/backends/CMakeLists.txt | 17 +++++++ .../backends/ilxqttaskbarabstractbackend.cpp | 2 +- panel/backends/wayland/CMakeLists.txt | 31 ++++++++++++ .../wayland/lxqttaskbarbackendwayland.h | 1 + .../lxqttaskbarplasmawindowmanagment.cpp | 2 - panel/backends/xcb/CMakeLists.txt | 13 +++++ 7 files changed, 70 insertions(+), 43 deletions(-) diff --git a/panel/CMakeLists.txt b/panel/CMakeLists.txt index b11ca5512..ef1291af5 100644 --- a/panel/CMakeLists.txt +++ b/panel/CMakeLists.txt @@ -1,10 +1,13 @@ set(PROJECT lxqt-panel) -# TODO +# Window Manager abstraction backend add_subdirectory(backends) -# TODO: move to backend folder -find_package(Qt6 ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS WaylandClient Concurrent) +# TODO: allow compile time selection via CMake variables +set(PANEL_BACKENDS + lxqt-panel-backend-wayland + lxqt-panel-backend-xcb +) set(PRIV_HEADERS @@ -25,17 +28,6 @@ set(PRIV_HEADERS config/configstyling.h config/configpluginswidget.h config/addplugindialog.h - - backends/ilxqttaskbarabstractbackend.h - backends/lxqttaskbartypes.h - - backends/lxqttaskbardummybackend.h - - backends/xcb/lxqttaskbarbackend_x11.h - - backends/wayland/lxqttaskbarbackendwayland.h - backends/wayland/lxqttaskbarplasmawindowmanagment.h - backends/wayland/lxqtplasmavirtualdesktop.h ) # using LXQt namespace in the public headers. @@ -44,9 +36,6 @@ set(PUB_HEADERS pluginsettings.h ilxqtpanelplugin.h ilxqtpanel.h - - backends/ilxqttaskbarabstractbackend.h - backends/lxqttaskbartypes.h ) set(SOURCES @@ -66,16 +55,6 @@ set(SOURCES config/configstyling.cpp config/configpluginswidget.cpp config/addplugindialog.cpp - - backends/ilxqttaskbarabstractbackend.cpp - - backends/lxqttaskbardummybackend.cpp - - backends/xcb/lxqttaskbarbackend_x11.cpp - - backends/wayland/lxqttaskbarbackendwayland.cpp - backends/wayland/lxqttaskbarplasmawindowmanagment.cpp - backends/wayland/lxqtplasmavirtualdesktop.cpp ) set(UI @@ -130,24 +109,12 @@ add_executable(${PROJECT} ${UI} ) -# TODO: move to backend folder -qt6_generate_wayland_protocol_client_sources(${PROJECT} -FILES - ${CMAKE_CURRENT_SOURCE_DIR}/backends/wayland/protocols/plasma-window-management.xml -) - -qt6_generate_wayland_protocol_client_sources(${PROJECT} -FILES - ${CMAKE_CURRENT_SOURCE_DIR}/backends/wayland/protocols/org-kde-plasma-virtual-desktop.xml -) - target_link_libraries(${PROJECT} ${LIBRARIES} ${QTX_LIBRARIES} KF6::WindowSystem + ${PANEL_BACKENDS} LayerShellQt::Interface - Qt6::WaylandClient # TODO: Move to backend folder - Qt6::Concurrent ${STATIC_PLUGINS} ) diff --git a/panel/backends/CMakeLists.txt b/panel/backends/CMakeLists.txt index f1915b823..c369eb81f 100644 --- a/panel/backends/CMakeLists.txt +++ b/panel/backends/CMakeLists.txt @@ -1,2 +1,19 @@ +# Common interface for Window Manager abstraction backend +# This also contains dummy backend + +add_library(lxqt-panel-backend-common STATIC + + lxqttaskbartypes.h + ilxqttaskbarabstractbackend.h + ilxqttaskbarabstractbackend.cpp + + lxqttaskbardummybackend.h + lxqttaskbardummybackend.cpp +) + +target_link_libraries(lxqt-panel-backend-common + Qt6::Gui +) + add_subdirectory(wayland) add_subdirectory(xcb) diff --git a/panel/backends/ilxqttaskbarabstractbackend.cpp b/panel/backends/ilxqttaskbarabstractbackend.cpp index 137728263..dbad943d5 100644 --- a/panel/backends/ilxqttaskbarabstractbackend.cpp +++ b/panel/backends/ilxqttaskbarabstractbackend.cpp @@ -1,4 +1,4 @@ -#include "../panel/backends/ilxqttaskbarabstractbackend.h" +#include "ilxqttaskbarabstractbackend.h" ILXQtTaskbarAbstractBackend::ILXQtTaskbarAbstractBackend(QObject *parent) diff --git a/panel/backends/wayland/CMakeLists.txt b/panel/backends/wayland/CMakeLists.txt index 8b1378917..e0ecab6e9 100644 --- a/panel/backends/wayland/CMakeLists.txt +++ b/panel/backends/wayland/CMakeLists.txt @@ -1 +1,32 @@ +# Wayland WM Backend +project(lxqt-panel-backend-wayland) + +find_package(Qt6 ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS WaylandClient Concurrent) + +add_library(lxqt-panel-backend-wayland STATIC + lxqttaskbarbackendwayland.h + lxqttaskbarplasmawindowmanagment.h + lxqtplasmavirtualdesktop.h + + lxqttaskbarbackendwayland.cpp + lxqttaskbarplasmawindowmanagment.cpp + lxqtplasmavirtualdesktop.cpp +) + +qt6_generate_wayland_protocol_client_sources(lxqt-panel-backend-wayland +FILES + ${CMAKE_CURRENT_SOURCE_DIR}/protocols/plasma-window-management.xml +) + +qt6_generate_wayland_protocol_client_sources(lxqt-panel-backend-wayland +FILES + ${CMAKE_CURRENT_SOURCE_DIR}/protocols/org-kde-plasma-virtual-desktop.xml +) + +target_link_libraries(lxqt-panel-backend-wayland + Qt6::GuiPrivate + Qt6::WaylandClient + Qt6::Concurrent + lxqt-panel-backend-common +) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.h b/panel/backends/wayland/lxqttaskbarbackendwayland.h index 0b12ef2fd..df202f076 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.h +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.h @@ -3,6 +3,7 @@ #include "../ilxqttaskbarabstractbackend.h" +#include #include #include diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp index f813181ed..b4339dbb0 100644 --- a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp +++ b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp @@ -1,7 +1,5 @@ #include "lxqttaskbarplasmawindowmanagment.h" -#include - #include #include #include diff --git a/panel/backends/xcb/CMakeLists.txt b/panel/backends/xcb/CMakeLists.txt index 8b1378917..ad36ccaa3 100644 --- a/panel/backends/xcb/CMakeLists.txt +++ b/panel/backends/xcb/CMakeLists.txt @@ -1 +1,14 @@ +# XCB WM Backend + +project(lxqt-panel-backend-xcb) + +add_library(lxqt-panel-backend-xcb STATIC + lxqttaskbarbackend_x11.h + lxqttaskbarbackend_x11.cpp +) + +target_link_libraries(lxqt-panel-backend-xcb + KF6::WindowSystem + lxqt-panel-backend-common +) From 01c925f22841695e00a974c04dcd8803ea8a0dd7 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 28 Mar 2024 11:21:38 +0100 Subject: [PATCH 124/165] LXQtTaskbarWaylandBackend: possibly fix crash on showDesktop for non- KWin --- panel/backends/wayland/lxqttaskbarbackendwayland.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index fb6e940a8..37b9817b7 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -450,11 +450,14 @@ bool LXQtTaskbarWaylandBackend::isAreaOverlapped(const QRect &area) const bool LXQtTaskbarWaylandBackend::isShowingDesktop() const { - return m_managment->isShowingDesktop(); + return m_managment->isActive() ? m_managment->isShowingDesktop() : false; } bool LXQtTaskbarWaylandBackend::showDesktop(bool value) { + if(!m_managment->isActive()) + return false; + enum LXQtTaskBarPlasmaWindowManagment::show_desktop flag_; if(value) flag_ = LXQtTaskBarPlasmaWindowManagment::show_desktop::show_desktop_enabled; From 414e7d02b855656d43aa82747e5dbd2ce0f5750c Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Tue, 9 Apr 2024 11:52:46 +0200 Subject: [PATCH 125/165] Hide lxqt-panel application from applications menu - Add NoDisplay=true to .desktop file --- autostart/lxqt-panel_wayland.desktop.in | 1 + 1 file changed, 1 insertion(+) diff --git a/autostart/lxqt-panel_wayland.desktop.in b/autostart/lxqt-panel_wayland.desktop.in index 089082aea..540955e18 100644 --- a/autostart/lxqt-panel_wayland.desktop.in +++ b/autostart/lxqt-panel_wayland.desktop.in @@ -1,6 +1,7 @@ [Desktop Entry] Type=Application TryExec=lxqt-panel +NoDisplay=true # NOTE: KWin wants absolute path here, get it from CMake install path Exec=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@/lxqt-panel From 87c73271b1b8c415c19f18baccc0c90ecfd0cf46 Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Wed, 27 Mar 2024 00:06:09 +0530 Subject: [PATCH 126/165] Get wlroots backend compiling --- panel/CMakeLists.txt | 3 +- panel/backends/wayland/CMakeLists.txt | 34 +- panel/backends/wayland/plasma/CMakeLists.txt | 32 ++ .../{ => plasma}/lxqtplasmavirtualdesktop.cpp | 0 .../{ => plasma}/lxqtplasmavirtualdesktop.h | 0 .../lxqttaskbarbackendplasma.cpp} | 70 +-- .../lxqttaskbarbackendplasma.h} | 10 +- .../lxqttaskbarplasmawindowmanagment.cpp | 0 .../lxqttaskbarplasmawindowmanagment.h | 0 .../org-kde-plasma-virtual-desktop.xml | 0 .../protocols/plasma-window-management.xml | 0 panel/backends/wayland/wlroots/CMakeLists.txt | 27 + .../wayland/wlroots/lxqttaskbarbackendwlr.cpp | 495 ++++++++++++++++++ .../wayland/wlroots/lxqttaskbarbackendwlr.h | 95 ++++ .../wlroots/lxqttaskbarwlrwindowmanagment.cpp | 127 +++++ .../wlroots/lxqttaskbarwlrwindowmanagment.h | 87 +++ .../wayland/wlroots/lxqtwlrvirtualdesktop.cpp | 104 ++++ .../wayland/wlroots/lxqtwlrvirtualdesktop.h | 73 +++ ...oreign-toplevel-management-unstable-v1.xml | 270 ++++++++++ panel/backends/xcb/lxqttaskbarbackend_x11.cpp | 4 +- panel/lxqtpanelapplication.cpp | 5 +- 21 files changed, 1359 insertions(+), 77 deletions(-) create mode 100644 panel/backends/wayland/plasma/CMakeLists.txt rename panel/backends/wayland/{ => plasma}/lxqtplasmavirtualdesktop.cpp (100%) rename panel/backends/wayland/{ => plasma}/lxqtplasmavirtualdesktop.h (100%) rename panel/backends/wayland/{lxqttaskbarbackendwayland.cpp => plasma/lxqttaskbarbackendplasma.cpp} (88%) rename panel/backends/wayland/{lxqttaskbarbackendwayland.h => plasma/lxqttaskbarbackendplasma.h} (92%) rename panel/backends/wayland/{ => plasma}/lxqttaskbarplasmawindowmanagment.cpp (100%) rename panel/backends/wayland/{ => plasma}/lxqttaskbarplasmawindowmanagment.h (100%) rename panel/backends/wayland/{ => plasma}/protocols/org-kde-plasma-virtual-desktop.xml (100%) rename panel/backends/wayland/{ => plasma}/protocols/plasma-window-management.xml (100%) create mode 100644 panel/backends/wayland/wlroots/CMakeLists.txt create mode 100644 panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp create mode 100644 panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h create mode 100644 panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp create mode 100644 panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h create mode 100644 panel/backends/wayland/wlroots/lxqtwlrvirtualdesktop.cpp create mode 100644 panel/backends/wayland/wlroots/lxqtwlrvirtualdesktop.h create mode 100644 panel/backends/wayland/wlroots/protocols/wlr-foreign-toplevel-management-unstable-v1.xml diff --git a/panel/CMakeLists.txt b/panel/CMakeLists.txt index ef1291af5..f081f2f5f 100644 --- a/panel/CMakeLists.txt +++ b/panel/CMakeLists.txt @@ -5,7 +5,8 @@ add_subdirectory(backends) # TODO: allow compile time selection via CMake variables set(PANEL_BACKENDS - lxqt-panel-backend-wayland + lxqt-panel-backend-plasma + lxqt-panel-backend-wlroots lxqt-panel-backend-xcb ) diff --git a/panel/backends/wayland/CMakeLists.txt b/panel/backends/wayland/CMakeLists.txt index e0ecab6e9..d23f14f1e 100644 --- a/panel/backends/wayland/CMakeLists.txt +++ b/panel/backends/wayland/CMakeLists.txt @@ -1,32 +1,2 @@ -# Wayland WM Backend - -project(lxqt-panel-backend-wayland) - -find_package(Qt6 ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS WaylandClient Concurrent) - -add_library(lxqt-panel-backend-wayland STATIC - lxqttaskbarbackendwayland.h - lxqttaskbarplasmawindowmanagment.h - lxqtplasmavirtualdesktop.h - - lxqttaskbarbackendwayland.cpp - lxqttaskbarplasmawindowmanagment.cpp - lxqtplasmavirtualdesktop.cpp -) - -qt6_generate_wayland_protocol_client_sources(lxqt-panel-backend-wayland -FILES - ${CMAKE_CURRENT_SOURCE_DIR}/protocols/plasma-window-management.xml -) - -qt6_generate_wayland_protocol_client_sources(lxqt-panel-backend-wayland -FILES - ${CMAKE_CURRENT_SOURCE_DIR}/protocols/org-kde-plasma-virtual-desktop.xml -) - -target_link_libraries(lxqt-panel-backend-wayland - Qt6::GuiPrivate - Qt6::WaylandClient - Qt6::Concurrent - lxqt-panel-backend-common -) +add_subdirectory(plasma) +add_subdirectory(wlroots) diff --git a/panel/backends/wayland/plasma/CMakeLists.txt b/panel/backends/wayland/plasma/CMakeLists.txt new file mode 100644 index 000000000..71b2b273b --- /dev/null +++ b/panel/backends/wayland/plasma/CMakeLists.txt @@ -0,0 +1,32 @@ +# Wayland WM Backend + +project(lxqt-panel-backend-plasma) + +find_package(Qt6 ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS WaylandClient Concurrent) + +add_library(lxqt-panel-backend-plasma STATIC + lxqttaskbarbackendplasma.h + lxqttaskbarplasmawindowmanagment.h + lxqtplasmavirtualdesktop.h + + lxqttaskbarbackendplasma.cpp + lxqttaskbarplasmawindowmanagment.cpp + lxqtplasmavirtualdesktop.cpp +) + +qt6_generate_wayland_protocol_client_sources(lxqt-panel-backend-plasma +FILES + ${CMAKE_CURRENT_SOURCE_DIR}/protocols/plasma-window-management.xml +) + +qt6_generate_wayland_protocol_client_sources(lxqt-panel-backend-plasma +FILES + ${CMAKE_CURRENT_SOURCE_DIR}/protocols/org-kde-plasma-virtual-desktop.xml +) + +target_link_libraries(lxqt-panel-backend-plasma + Qt6::GuiPrivate + Qt6::WaylandClient + Qt6::Concurrent + lxqt-panel-backend-common +) diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp b/panel/backends/wayland/plasma/lxqtplasmavirtualdesktop.cpp similarity index 100% rename from panel/backends/wayland/lxqtplasmavirtualdesktop.cpp rename to panel/backends/wayland/plasma/lxqtplasmavirtualdesktop.cpp diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.h b/panel/backends/wayland/plasma/lxqtplasmavirtualdesktop.h similarity index 100% rename from panel/backends/wayland/lxqtplasmavirtualdesktop.h rename to panel/backends/wayland/plasma/lxqtplasmavirtualdesktop.h diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.cpp similarity index 88% rename from panel/backends/wayland/lxqttaskbarbackendwayland.cpp rename to panel/backends/wayland/plasma/lxqttaskbarbackendplasma.cpp index 37b9817b7..4631c03d3 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.cpp @@ -1,4 +1,4 @@ -#include "lxqttaskbarbackendwayland.h" +#include "lxqttaskbarbackendplasma.h" #include "lxqttaskbarplasmawindowmanagment.h" #include "lxqtplasmavirtualdesktop.h" @@ -22,7 +22,7 @@ auto findWindow(const std::vector>& win return windows.end(); } -LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) : +LXQtTaskbarPlasmaBackend::LXQtTaskbarPlasmaBackend(QObject *parent) : ILXQtTaskbarAbstractBackend(parent) { m_managment.reset(new LXQtTaskBarPlasmaWindowManagment); @@ -58,7 +58,7 @@ LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) : }); } -bool LXQtTaskbarWaylandBackend::supportsAction(WId windowId, LXQtTaskBarBackendAction action) const +bool LXQtTaskbarPlasmaBackend::supportsAction(WId windowId, LXQtTaskBarBackendAction action) const { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -99,7 +99,7 @@ bool LXQtTaskbarWaylandBackend::supportsAction(WId windowId, LXQtTaskBarBackendA return window->windowState.testFlag(state); } -bool LXQtTaskbarWaylandBackend::reloadWindows() +bool LXQtTaskbarPlasmaBackend::reloadWindows() { const QVector wids = getCurrentWindows(); @@ -116,7 +116,7 @@ bool LXQtTaskbarWaylandBackend::reloadWindows() return true; } -QVector LXQtTaskbarWaylandBackend::getCurrentWindows() const +QVector LXQtTaskbarPlasmaBackend::getCurrentWindows() const { QVector wids; wids.reserve(wids.size()); @@ -129,7 +129,7 @@ QVector LXQtTaskbarWaylandBackend::getCurrentWindows() const return wids; } -QString LXQtTaskbarWaylandBackend::getWindowTitle(WId windowId) const +QString LXQtTaskbarPlasmaBackend::getWindowTitle(WId windowId) const { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -138,7 +138,7 @@ QString LXQtTaskbarWaylandBackend::getWindowTitle(WId windowId) const return window->title; } -bool LXQtTaskbarWaylandBackend::applicationDemandsAttention(WId windowId) const +bool LXQtTaskbarPlasmaBackend::applicationDemandsAttention(WId windowId) const { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -147,7 +147,7 @@ bool LXQtTaskbarWaylandBackend::applicationDemandsAttention(WId windowId) const return window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_demands_attention) || transientsDemandingAttention.contains(window); } -QIcon LXQtTaskbarWaylandBackend::getApplicationIcon(WId windowId, int devicePixels) const +QIcon LXQtTaskbarPlasmaBackend::getApplicationIcon(WId windowId, int devicePixels) const { Q_UNUSED(devicePixels) @@ -158,7 +158,7 @@ QIcon LXQtTaskbarWaylandBackend::getApplicationIcon(WId windowId, int devicePixe return window->icon; } -QString LXQtTaskbarWaylandBackend::getWindowClass(WId windowId) const +QString LXQtTaskbarPlasmaBackend::getWindowClass(WId windowId) const { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -166,7 +166,7 @@ QString LXQtTaskbarWaylandBackend::getWindowClass(WId windowId) const return window->appId; } -LXQtTaskBarWindowLayer LXQtTaskbarWaylandBackend::getWindowLayer(WId windowId) const +LXQtTaskBarWindowLayer LXQtTaskbarPlasmaBackend::getWindowLayer(WId windowId) const { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -181,7 +181,7 @@ LXQtTaskBarWindowLayer LXQtTaskbarWaylandBackend::getWindowLayer(WId windowId) c return LXQtTaskBarWindowLayer::Normal; } -bool LXQtTaskbarWaylandBackend::setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) +bool LXQtTaskbarPlasmaBackend::setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -207,7 +207,7 @@ bool LXQtTaskbarWaylandBackend::setWindowLayer(WId windowId, LXQtTaskBarWindowLa return false; } -LXQtTaskBarWindowState LXQtTaskbarWaylandBackend::getWindowState(WId windowId) const +LXQtTaskBarWindowState LXQtTaskbarPlasmaBackend::getWindowState(WId windowId) const { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -225,7 +225,7 @@ LXQtTaskBarWindowState LXQtTaskbarWaylandBackend::getWindowState(WId windowId) c return LXQtTaskBarWindowState::Normal; } -bool LXQtTaskbarWaylandBackend::setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set) +bool LXQtTaskbarPlasmaBackend::setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set) { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -265,7 +265,7 @@ bool LXQtTaskbarWaylandBackend::setWindowState(WId windowId, LXQtTaskBarWindowSt return true; } -bool LXQtTaskbarWaylandBackend::isWindowActive(WId windowId) const +bool LXQtTaskbarPlasmaBackend::isWindowActive(WId windowId) const { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -274,7 +274,7 @@ bool LXQtTaskbarWaylandBackend::isWindowActive(WId windowId) const return activeWindow == window || window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_active); } -bool LXQtTaskbarWaylandBackend::raiseWindow(WId windowId, bool onCurrentWorkSpace) +bool LXQtTaskbarPlasmaBackend::raiseWindow(WId windowId, bool onCurrentWorkSpace) { Q_UNUSED(onCurrentWorkSpace) //TODO @@ -304,7 +304,7 @@ bool LXQtTaskbarWaylandBackend::raiseWindow(WId windowId, bool onCurrentWorkSpac return true; } -bool LXQtTaskbarWaylandBackend::closeWindow(WId windowId) +bool LXQtTaskbarPlasmaBackend::closeWindow(WId windowId) { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -314,31 +314,31 @@ bool LXQtTaskbarWaylandBackend::closeWindow(WId windowId) return true; } -WId LXQtTaskbarWaylandBackend::getActiveWindow() const +WId LXQtTaskbarPlasmaBackend::getActiveWindow() const { if(activeWindow) return activeWindow->getWindowId(); return 0; } -int LXQtTaskbarWaylandBackend::getWorkspacesCount() const +int LXQtTaskbarPlasmaBackend::getWorkspacesCount() const { return m_workspaceInfo->numberOfDesktops(); } -QString LXQtTaskbarWaylandBackend::getWorkspaceName(int idx) const +QString LXQtTaskbarPlasmaBackend::getWorkspaceName(int idx) const { return m_workspaceInfo->getDesktopName(idx - 1); //Return to 0-based } -int LXQtTaskbarWaylandBackend::getCurrentWorkspace() const +int LXQtTaskbarPlasmaBackend::getCurrentWorkspace() const { if(!m_workspaceInfo->currentDesktop().isValid()) return 0; return m_workspaceInfo->position(m_workspaceInfo->currentDesktop()) + 1; // 1-based } -bool LXQtTaskbarWaylandBackend::setCurrentWorkspace(int idx) +bool LXQtTaskbarPlasmaBackend::setCurrentWorkspace(int idx) { QString id = m_workspaceInfo->getDesktopId(idx - 1); //Return to 0-based if(id.isEmpty()) @@ -347,7 +347,7 @@ bool LXQtTaskbarWaylandBackend::setCurrentWorkspace(int idx) return true; } -int LXQtTaskbarWaylandBackend::getWindowWorkspace(WId windowId) const +int LXQtTaskbarPlasmaBackend::getWindowWorkspace(WId windowId) const { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -364,7 +364,7 @@ int LXQtTaskbarWaylandBackend::getWindowWorkspace(WId windowId) const return m_workspaceInfo->position(id) + 1; //Make 1-based } -bool LXQtTaskbarWaylandBackend::setWindowOnWorkspace(WId windowId, int idx) +bool LXQtTaskbarPlasmaBackend::setWindowOnWorkspace(WId windowId, int idx) { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -399,12 +399,12 @@ bool LXQtTaskbarWaylandBackend::setWindowOnWorkspace(WId windowId, int idx) return true; } -void LXQtTaskbarWaylandBackend::moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) +void LXQtTaskbarPlasmaBackend::moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) { } -bool LXQtTaskbarWaylandBackend::isWindowOnScreen(QScreen *screen, WId windowId) const +bool LXQtTaskbarPlasmaBackend::isWindowOnScreen(QScreen *screen, WId windowId) const { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -413,7 +413,7 @@ bool LXQtTaskbarWaylandBackend::isWindowOnScreen(QScreen *screen, WId windowId) return screen->geometry().intersects(window->geometry); } -void LXQtTaskbarWaylandBackend::moveApplication(WId windowId) +void LXQtTaskbarPlasmaBackend::moveApplication(WId windowId) { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -423,7 +423,7 @@ void LXQtTaskbarWaylandBackend::moveApplication(WId windowId) window->request_move(); } -void LXQtTaskbarWaylandBackend::resizeApplication(WId windowId) +void LXQtTaskbarPlasmaBackend::resizeApplication(WId windowId) { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); if(!window) @@ -433,12 +433,12 @@ void LXQtTaskbarWaylandBackend::resizeApplication(WId windowId) window->request_resize(); } -void LXQtTaskbarWaylandBackend::refreshIconGeometry(WId windowId, const QRect &geom) +void LXQtTaskbarPlasmaBackend::refreshIconGeometry(WId windowId, const QRect &geom) { } -bool LXQtTaskbarWaylandBackend::isAreaOverlapped(const QRect &area) const +bool LXQtTaskbarPlasmaBackend::isAreaOverlapped(const QRect &area) const { for(auto &window : std::as_const(windows)) { @@ -448,12 +448,12 @@ bool LXQtTaskbarWaylandBackend::isAreaOverlapped(const QRect &area) const return false; } -bool LXQtTaskbarWaylandBackend::isShowingDesktop() const +bool LXQtTaskbarPlasmaBackend::isShowingDesktop() const { return m_managment->isActive() ? m_managment->isShowingDesktop() : false; } -bool LXQtTaskbarWaylandBackend::showDesktop(bool value) +bool LXQtTaskbarPlasmaBackend::showDesktop(bool value) { if(!m_managment->isActive()) return false; @@ -468,7 +468,7 @@ bool LXQtTaskbarWaylandBackend::showDesktop(bool value) return true; } -void LXQtTaskbarWaylandBackend::addWindow(LXQtTaskBarPlasmaWindow *window) +void LXQtTaskbarPlasmaBackend::addWindow(LXQtTaskBarPlasmaWindow *window) { if (findWindow(windows, window) != windows.end() || transients.contains(window)) { @@ -688,7 +688,7 @@ void LXQtTaskbarWaylandBackend::addWindow(LXQtTaskBarPlasmaWindow *window) } } -bool LXQtTaskbarWaylandBackend::acceptWindow(LXQtTaskBarPlasmaWindow *window) const +bool LXQtTaskbarPlasmaBackend::acceptWindow(LXQtTaskBarPlasmaWindow *window) const { if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_skiptaskbar)) return false; @@ -699,7 +699,7 @@ bool LXQtTaskbarWaylandBackend::acceptWindow(LXQtTaskBarPlasmaWindow *window) co return true; } -void LXQtTaskbarWaylandBackend::updateWindowAcceptance(LXQtTaskBarPlasmaWindow *window) +void LXQtTaskbarPlasmaBackend::updateWindowAcceptance(LXQtTaskBarPlasmaWindow *window) { if(!window->acceptedInTaskBar && acceptWindow(window)) { @@ -713,7 +713,7 @@ void LXQtTaskbarWaylandBackend::updateWindowAcceptance(LXQtTaskBarPlasmaWindow * } } -LXQtTaskBarPlasmaWindow *LXQtTaskbarWaylandBackend::getWindow(WId windowId) const +LXQtTaskBarPlasmaWindow *LXQtTaskbarPlasmaBackend::getWindow(WId windowId) const { for(auto &window : std::as_const(windows)) { diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.h b/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.h similarity index 92% rename from panel/backends/wayland/lxqttaskbarbackendwayland.h rename to panel/backends/wayland/plasma/lxqttaskbarbackendplasma.h index df202f076..31b78f6c6 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.h +++ b/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.h @@ -1,7 +1,7 @@ -#ifndef LXQTTASKBARBACKENDWAYLAND_H -#define LXQTTASKBARBACKENDWAYLAND_H +#ifndef LXQTTASKBARBACKENDPLASMA_H +#define LXQTTASKBARBACKENDPLASMA_H -#include "../ilxqttaskbarabstractbackend.h" +#include "../../ilxqttaskbarabstractbackend.h" #include #include @@ -12,12 +12,12 @@ class LXQtTaskBarPlasmaWindowManagment; class LXQtPlasmaWaylandWorkspaceInfo; -class LXQtTaskbarWaylandBackend : public ILXQtTaskbarAbstractBackend +class LXQtTaskbarPlasmaBackend : public ILXQtTaskbarAbstractBackend { Q_OBJECT public: - explicit LXQtTaskbarWaylandBackend(QObject *parent = nullptr); + explicit LXQtTaskbarPlasmaBackend(QObject *parent = nullptr); // Backend virtual bool supportsAction(WId windowId, LXQtTaskBarBackendAction action) const override; diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp b/panel/backends/wayland/plasma/lxqttaskbarplasmawindowmanagment.cpp similarity index 100% rename from panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp rename to panel/backends/wayland/plasma/lxqttaskbarplasmawindowmanagment.cpp diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h b/panel/backends/wayland/plasma/lxqttaskbarplasmawindowmanagment.h similarity index 100% rename from panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h rename to panel/backends/wayland/plasma/lxqttaskbarplasmawindowmanagment.h diff --git a/panel/backends/wayland/protocols/org-kde-plasma-virtual-desktop.xml b/panel/backends/wayland/plasma/protocols/org-kde-plasma-virtual-desktop.xml similarity index 100% rename from panel/backends/wayland/protocols/org-kde-plasma-virtual-desktop.xml rename to panel/backends/wayland/plasma/protocols/org-kde-plasma-virtual-desktop.xml diff --git a/panel/backends/wayland/protocols/plasma-window-management.xml b/panel/backends/wayland/plasma/protocols/plasma-window-management.xml similarity index 100% rename from panel/backends/wayland/protocols/plasma-window-management.xml rename to panel/backends/wayland/plasma/protocols/plasma-window-management.xml diff --git a/panel/backends/wayland/wlroots/CMakeLists.txt b/panel/backends/wayland/wlroots/CMakeLists.txt new file mode 100644 index 000000000..8f6f1a59b --- /dev/null +++ b/panel/backends/wayland/wlroots/CMakeLists.txt @@ -0,0 +1,27 @@ +# Wayland WM Backend + +project(lxqt-panel-backend-wlroots) + +find_package(Qt6 ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS WaylandClient Concurrent) + +add_library(lxqt-panel-backend-wlroots STATIC + lxqttaskbarbackendwlr.h + lxqttaskbarwlrwindowmanagment.h + lxqtwlrvirtualdesktop.h + + lxqttaskbarbackendwlr.cpp + lxqttaskbarwlrwindowmanagment.cpp + lxqtwlrvirtualdesktop.cpp +) + +qt6_generate_wayland_protocol_client_sources(lxqt-panel-backend-wlroots +FILES + ${CMAKE_CURRENT_SOURCE_DIR}/protocols/wlr-foreign-toplevel-management-unstable-v1.xml +) + +target_link_libraries(lxqt-panel-backend-wlroots + Qt6::GuiPrivate + Qt6::WaylandClient + Qt6::Concurrent + lxqt-panel-backend-common +) diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp new file mode 100644 index 000000000..7f9590a2c --- /dev/null +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp @@ -0,0 +1,495 @@ +#include "lxqttaskbarbackendwlr.h" + +#include "lxqttaskbarwlrwindowmanagment.h" +#include "lxqtwlrvirtualdesktop.h" + +#include +#include +#include + +auto findWindow(const std::vector>& windows, LXQtTaskBarWlrootsWindow *window) +{ + //TODO: use algorithms + auto end = windows.end(); + for(auto it = windows.begin(); it != end; it++) + { + if((*it).get() == window) + { + return it; + } + } + + return windows.end(); +} + +LXQtTaskbarWlrootsBackend::LXQtTaskbarWlrootsBackend(QObject *parent) : + ILXQtTaskbarAbstractBackend(parent) +{ + m_managment.reset(new LXQtTaskBarWlrootsWindowManagment); + m_workspaceInfo.reset(new LXQtWlrootsWaylandWorkspaceInfo); + + connect(m_managment.get(), &LXQtTaskBarWlrootsWindowManagment::windowCreated, this, [this](LXQtTaskBarWlrootsWindow *window) { + addWindow(window); + }); + + // connect(m_managment.get(), &LXQtTaskBarWlrootsWindowManagment::stackingOrderChanged, + // this, [this](const QString &order) { + // // stackingOrder = order.split(QLatin1Char(';')); + // // for (const auto &window : std::as_const(windows)) { + // // this->dataChanged(window.get(), StackingOrder); + // // } + // }); + + connect(m_workspaceInfo.get(), &LXQtWlrootsWaylandWorkspaceInfo::currentDesktopChanged, this, + [this](){ + int idx = m_workspaceInfo->position(m_workspaceInfo->currentDesktop()); + idx += 1; // Make 1-based + emit currentWorkspaceChanged(idx); + }); + + connect(m_workspaceInfo.get(), &LXQtWlrootsWaylandWorkspaceInfo::numberOfDesktopsChanged, + this, &ILXQtTaskbarAbstractBackend::workspacesCountChanged); + + connect(m_workspaceInfo.get(), &LXQtWlrootsWaylandWorkspaceInfo::desktopNameChanged, + this, [this](int idx) { + emit workspaceNameChanged(idx + 1); // Make 1-based + }); +} + +bool LXQtTaskbarWlrootsBackend::supportsAction(WId, LXQtTaskBarBackendAction action) const +{ + switch (action) + { + case LXQtTaskBarBackendAction::Move: + return false; + + case LXQtTaskBarBackendAction::Resize: + return false; + + case LXQtTaskBarBackendAction::Maximize: + return true; + + case LXQtTaskBarBackendAction::Minimize: + return true; + + case LXQtTaskBarBackendAction::RollUp: + return false; + + case LXQtTaskBarBackendAction::FullScreen: + return true; + + default: + return false; + } + + return false; +} + +bool LXQtTaskbarWlrootsBackend::reloadWindows() +{ + const QVector wids = getCurrentWindows(); + + // Force removal and re-adding + for(WId windowId : wids) + { + emit windowRemoved(windowId); + } + for(WId windowId : wids) + { + emit windowAdded(windowId); + } + + return true; +} + +QVector LXQtTaskbarWlrootsBackend::getCurrentWindows() const +{ + QVector wids; + wids.reserve(wids.size()); + + for(const std::unique_ptr& window : std::as_const(windows)) + { + wids << window->getWindowId(); + } + return wids; +} + +QString LXQtTaskbarWlrootsBackend::getWindowTitle(WId windowId) const +{ + LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + if(!window) + return QString(); + + return window->title; +} + +bool LXQtTaskbarWlrootsBackend::applicationDemandsAttention(WId) const +{ + return false; +} + +QIcon LXQtTaskbarWlrootsBackend::getApplicationIcon(WId windowId, int devicePixels) const +{ + Q_UNUSED(devicePixels) + + LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + if(!window) + return QIcon(); + + return window->icon; +} + +QString LXQtTaskbarWlrootsBackend::getWindowClass(WId windowId) const +{ + LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + if(!window) + return QString(); + return window->appId; +} + +LXQtTaskBarWindowLayer LXQtTaskbarWlrootsBackend::getWindowLayer(WId) const +{ + return LXQtTaskBarWindowLayer::Normal; +} + +bool LXQtTaskbarWlrootsBackend::setWindowLayer(WId, LXQtTaskBarWindowLayer) +{ +} + +LXQtTaskBarWindowState LXQtTaskbarWlrootsBackend::getWindowState(WId windowId) const +{ + LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + if(!window) + return LXQtTaskBarWindowState::Normal; + + if(window->windowState.testFlag(LXQtTaskBarWlrootsWindow::state::state_minimized)) + return LXQtTaskBarWindowState::Hidden; + + if(window->windowState.testFlag(LXQtTaskBarWlrootsWindow::state::state_maximized)) + return LXQtTaskBarWindowState::Maximized; + + if(window->windowState.testFlag(LXQtTaskBarWlrootsWindow::state::state_fullscreen)) + return LXQtTaskBarWindowState::FullScreen; + + return LXQtTaskBarWindowState::Normal; +} + +bool LXQtTaskbarWlrootsBackend::setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set) +{ + LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + if(!window) + return false; + + LXQtTaskBarWlrootsWindow::state WlrootsState; + switch (state) + { + case LXQtTaskBarWindowState::Minimized: + { + WlrootsState = LXQtTaskBarWlrootsWindow::state::state_minimized; + break; + } + case LXQtTaskBarWindowState::Maximized: + case LXQtTaskBarWindowState::MaximizedVertically: + case LXQtTaskBarWindowState::MaximizedHorizontally: + { + WlrootsState = LXQtTaskBarWlrootsWindow::state::state_maximized; + break; + } + case LXQtTaskBarWindowState::Normal: + { + WlrootsState = LXQtTaskBarWlrootsWindow::state::state_maximized; + set = !set; //TODO: correct + break; + } + case LXQtTaskBarWindowState::RolledUp: + { + break; + } + default: + return false; + } + + window->set_state(WlrootsState, set ? WlrootsState : 0); + return true; +} + +bool LXQtTaskbarWlrootsBackend::isWindowActive(WId windowId) const +{ + LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + if(!window) + return false; + + return activeWindow == window || window->windowState.testFlag(LXQtTaskBarWlrootsWindow::state::state_activated); +} + +bool LXQtTaskbarWlrootsBackend::raiseWindow(WId windowId, bool onCurrentWorkSpace) +{ + Q_UNUSED(onCurrentWorkSpace) //TODO + + LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + if(!window) + return false; + + // Pull forward any transient demanding attention. + if (auto *transientDemandingAttention = transientsDemandingAttention.value(window)) + { + window = transientDemandingAttention; + } + else + { + // TODO Shouldn't KWin take care of that? + // Bringing a transient to the front usually brings its parent with it + // but focus is not handled properly. + // TODO take into account d->lastActivation instead + // of just taking the first one. + while (transients.key(window)) + { + window = transients.key(window); + } + } + + window->set_state(LXQtTaskBarWlrootsWindow::state::state_activated, LXQtTaskBarWlrootsWindow::state::state_activated); + return true; +} + +bool LXQtTaskbarWlrootsBackend::closeWindow(WId windowId) +{ + LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + if(!window) + return false; + + window->close(); + return true; +} + +WId LXQtTaskbarWlrootsBackend::getActiveWindow() const +{ + if(activeWindow) + return activeWindow->getWindowId(); + return 0; +} + +int LXQtTaskbarWlrootsBackend::getWorkspacesCount() const +{ + return m_workspaceInfo->numberOfDesktops(); +} + +QString LXQtTaskbarWlrootsBackend::getWorkspaceName(int idx) const +{ + return m_workspaceInfo->getDesktopName(idx - 1); //Return to 0-based +} + +int LXQtTaskbarWlrootsBackend::getCurrentWorkspace() const +{ + return 1; +} + +bool LXQtTaskbarWlrootsBackend::setCurrentWorkspace(int) +{ + return false; +} + +int LXQtTaskbarWlrootsBackend::getWindowWorkspace(WId windowId) const +{ + return 1; +} + +bool LXQtTaskbarWlrootsBackend::setWindowOnWorkspace(WId, int) +{ +} + +void LXQtTaskbarWlrootsBackend::moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) +{ +} + +bool LXQtTaskbarWlrootsBackend::isWindowOnScreen(QScreen *screen, WId windowId) const +{ + // Manage based on output-enter/output-leave + return true; +} + +void LXQtTaskbarWlrootsBackend::moveApplication(WId windowId) +{ +} + +void LXQtTaskbarWlrootsBackend::resizeApplication(WId windowId) +{ +} + +void LXQtTaskbarWlrootsBackend::refreshIconGeometry(WId windowId, const QRect &geom) +{ + +} + +bool LXQtTaskbarWlrootsBackend::isAreaOverlapped(const QRect &area) const +{ +} + +bool LXQtTaskbarWlrootsBackend::isShowingDesktop() const +{ + return m_managment->isShowingDesktop(); +} + +bool LXQtTaskbarWlrootsBackend::showDesktop(bool value) +{ +} + +void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) +{ + if (findWindow(windows, window) != windows.end() || transients.contains(window)) + { + return; + } + + auto removeWindow = [window, this] + { + auto it = findWindow(windows, window); + if (it != windows.end()) + { + windows.erase(it); + transientsDemandingAttention.remove(window); + lastActivated.remove(window); + } + else + { + // Could be a transient. + // Removing a transient might change the demands attention state of the leader. + if (transients.remove(window)) + { + if (LXQtTaskBarWlrootsWindow *leader = transientsDemandingAttention.key(window)) { + transientsDemandingAttention.remove(leader, window); + emit windowPropertyChanged(leader->getWindowId(), int(LXQtTaskBarWindowProperty::Urgency)); + } + } + } + + if (activeWindow == window) + { + activeWindow = nullptr; + emit activeWindowChanged(0); + } + }; + + connect(window, &LXQtTaskBarWlrootsWindow::closed, this, removeWindow); + + connect(window, &LXQtTaskBarWlrootsWindow::titleChanged, this, [window, this] { + updateWindowAcceptance(window); + }); + + connect(window, &LXQtTaskBarWlrootsWindow::appIdChanged, this, [window, this] { + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::WindowClass)); + }); + + if (window->windowState & LXQtTaskBarWlrootsWindow::state::state_activated) { + LXQtTaskBarWlrootsWindow *effectiveActive = window; + while (effectiveActive->parentWindow) { + effectiveActive = effectiveActive->parentWindow; + } + + lastActivated[effectiveActive] = QTime::currentTime(); + activeWindow = effectiveActive; + } + + connect(window, &LXQtTaskBarWlrootsWindow::activeChanged, this, [window, this] { + const bool active = window->windowState & LXQtTaskBarWlrootsWindow::state::state_activated; + + LXQtTaskBarWlrootsWindow *effectiveWindow = window; + + while (effectiveWindow->parentWindow) + { + effectiveWindow = effectiveWindow->parentWindow; + } + + if (active) + { + lastActivated[effectiveWindow] = QTime::currentTime(); + + if (activeWindow != effectiveWindow) + { + activeWindow = effectiveWindow; + emit activeWindowChanged(activeWindow->getWindowId()); + } + } + else + { + if (activeWindow == effectiveWindow) + { + activeWindow = nullptr; + emit activeWindowChanged(0); + } + } + }); + + connect(window, &LXQtTaskBarWlrootsWindow::parentChanged, this, [window, this] { + LXQtTaskBarWlrootsWindow *leader = window->parentWindow.data(); + + if (transients.remove(window)) + { + if (leader) + { + // leader change. + transients.insert(window, leader); + } + else + { + // lost a leader, add to regular windows list. + Q_ASSERT(findWindow(windows, window) == windows.end()); + + windows.emplace_back(window); + } + } + else if (leader) + { + // gained a leader, remove from regular windows list. + auto it = findWindow(windows, window); + Q_ASSERT(it != windows.end()); + + windows.erase(it); + lastActivated.remove(window); + } + }); + + auto stateChanged = [window, this] { + updateWindowAcceptance(window); + }; + + connect(window, &LXQtTaskBarWlrootsWindow::fullscreenChanged, this, stateChanged); + + connect(window, &LXQtTaskBarWlrootsWindow::maximizedChanged, this, stateChanged); + + connect(window, &LXQtTaskBarWlrootsWindow::minimizedChanged, this, stateChanged); + + // Handle transient. + if (LXQtTaskBarWlrootsWindow *leader = window->parentWindow.data()) + { + transients.insert(window, leader); + } + else + { + windows.emplace_back(window); + updateWindowAcceptance(window); + } +} + +bool LXQtTaskbarWlrootsBackend::acceptWindow(LXQtTaskBarWlrootsWindow *window) const +{ + if(transients.contains(window)) + return false; + + return true; +} + +void LXQtTaskbarWlrootsBackend::updateWindowAcceptance(LXQtTaskBarWlrootsWindow *) +{ +} + +LXQtTaskBarWlrootsWindow *LXQtTaskbarWlrootsBackend::getWindow(WId windowId) const +{ + for(auto &window : std::as_const(windows)) + { + if(window->getWindowId() == windowId) + return window.get(); + } + + return nullptr; +} diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h new file mode 100644 index 000000000..9b4348afd --- /dev/null +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h @@ -0,0 +1,95 @@ +#ifndef LXQTTASKBARBACKENDWLROOTS_H +#define LXQTTASKBARBACKENDWLROOTS_H + +#include "../../ilxqttaskbarabstractbackend.h" + +#include +#include +#include + +class LXQtTaskBarWlrootsWindow; +class LXQtTaskBarWlrootsWindowManagment; +class LXQtWlrootsWaylandWorkspaceInfo; + + +class LXQtTaskbarWlrootsBackend : public ILXQtTaskbarAbstractBackend +{ + Q_OBJECT + +public: + explicit LXQtTaskbarWlrootsBackend(QObject *parent = nullptr); + + // Backend + virtual bool supportsAction(WId windowId, LXQtTaskBarBackendAction action) const override; + + // Windows + virtual bool reloadWindows() override; + + virtual QVector getCurrentWindows() const override; + virtual QString getWindowTitle(WId windowId) const override; + virtual bool applicationDemandsAttention(WId windowId) const override; + virtual QIcon getApplicationIcon(WId windowId, int devicePixels) const override; + virtual QString getWindowClass(WId windowId) const override; + + virtual LXQtTaskBarWindowLayer getWindowLayer(WId windowId) const override; + virtual bool setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) override; + + virtual LXQtTaskBarWindowState getWindowState(WId windowId) const override; + virtual bool setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set) override; + + virtual bool isWindowActive(WId windowId) const override; + virtual bool raiseWindow(WId windowId, bool onCurrentWorkSpace) override; + + virtual bool closeWindow(WId windowId) override; + + virtual WId getActiveWindow() const override; + + // Workspaces + virtual int getWorkspacesCount() const override; + virtual QString getWorkspaceName(int idx) const override; + + virtual int getCurrentWorkspace() const override; + virtual bool setCurrentWorkspace(int idx) override; + + virtual int getWindowWorkspace(WId windowId) const override; + virtual bool setWindowOnWorkspace(WId windowId, int idx) override; + + virtual void moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) override; + + virtual bool isWindowOnScreen(QScreen *screen, WId windowId) const override; + + // X11 Specific + virtual void moveApplication(WId windowId) override; + virtual void resizeApplication(WId windowId) override; + + virtual void refreshIconGeometry(WId windowId, const QRect &geom) override; + + // Panel internal + virtual bool isAreaOverlapped(const QRect& area) const override; + + // Show Destop + virtual bool isShowingDesktop() const override; + virtual bool showDesktop(bool value) override; + +private: + void addWindow(LXQtTaskBarWlrootsWindow *window); + bool acceptWindow(LXQtTaskBarWlrootsWindow *window) const; + void updateWindowAcceptance(LXQtTaskBarWlrootsWindow *window); + +private: + LXQtTaskBarWlrootsWindow *getWindow(WId windowId) const; + + std::unique_ptr m_workspaceInfo; + + std::unique_ptr m_managment; + + QHash lastActivated; + LXQtTaskBarWlrootsWindow *activeWindow = nullptr; + std::vector> windows; + // key=transient child, value=leader + QHash transients; + // key=leader, values=transient children + QMultiHash transientsDemandingAttention; +}; + +#endif // LXQTTASKBARBACKENDWAYLAND_H diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp new file mode 100644 index 000000000..214b6f5d1 --- /dev/null +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp @@ -0,0 +1,127 @@ +#include "lxqttaskbarwlrwindowmanagment.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * LXQtTaskBarWlrootsWindowManagment + */ + +LXQtTaskBarWlrootsWindowManagment::LXQtTaskBarWlrootsWindowManagment() + : QWaylandClientExtensionTemplate(version) +{ + connect(this, &QWaylandClientExtension::activeChanged, this, [this] { + if (!isActive()) { + zwlr_foreign_toplevel_manager_v1_destroy(object()); + } + }); +} + +LXQtTaskBarWlrootsWindowManagment::~LXQtTaskBarWlrootsWindowManagment() +{ + if (isActive()) { + zwlr_foreign_toplevel_manager_v1_destroy(object()); + } +} + +void LXQtTaskBarWlrootsWindowManagment::zwlr_foreign_toplevel_manager_v1_toplevel(struct ::zwlr_foreign_toplevel_handle_v1 *toplevel) +{ + emit windowCreated( new LXQtTaskBarWlrootsWindow(toplevel) ); +} + + +/* + * LXQtTaskBarWlrootsWindow + */ + +LXQtTaskBarWlrootsWindow::LXQtTaskBarWlrootsWindow(::zwlr_foreign_toplevel_handle_v1 *id) + : zwlr_foreign_toplevel_handle_v1(id), + uuid(QString::number(0)) +{ +} + +/* + * LXQtTaskBarWlrootsWindow + */ + +LXQtTaskBarWlrootsWindow::~LXQtTaskBarWlrootsWindow() +{ + destroy(); +} + +void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_title(const QString &title) +{ + emit titleChanged(); + this->title = title; +} + +void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_app_id(const QString &app_id) +{ + emit appIdChanged(); + this->appId = app_id; + // Code to get the icon needs to be inserted here +} + +void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_enter(struct ::wl_output *output) +{ + emit outputEnter(); +} + +void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_leave(struct ::wl_output *output) +{ + emit outputLeave(); +} + +void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_state(wl_array *state) +{ + // emit activeChanged(); + // emit maximizedChanged(); + // emit minimizedChanged(); + // emit fullscreenChanged(); +} + +void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_done() +{ + emit done(); +} + +void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_closed() +{ + emit closed(); +} + +void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_parent(struct ::zwlr_foreign_toplevel_handle_v1 *parent) +{ + setParentWindow( new LXQtTaskBarWlrootsWindow(parent)); +} + +void LXQtTaskBarWlrootsWindow::setParentWindow(LXQtTaskBarWlrootsWindow *parent) +{ + const auto old = parentWindow; + QObject::disconnect(parentWindowUnmappedConnection); + + if (parent) { + parentWindow = QPointer(parent); + parentWindowUnmappedConnection = QObject::connect(parent, &LXQtTaskBarWlrootsWindow::closed, this, [this] { + setParentWindow(nullptr); + }); + } else { + parentWindow = QPointer(); + parentWindowUnmappedConnection = QMetaObject::Connection(); + } + + if (parentWindow.data() != old.data()) { + Q_EMIT parentChanged(); + } +} diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h new file mode 100644 index 000000000..d488ce15b --- /dev/null +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h @@ -0,0 +1,87 @@ +#ifndef LXQTTASKBARWlrootsWINDOWMANAGMENT_H +#define LXQTTASKBARWlrootsWINDOWMANAGMENT_H + +#include +#include +#include + +#include "qwayland-wlr-foreign-toplevel-management-unstable-v1.h" +#include "wayland-wlr-foreign-toplevel-management-unstable-v1-client-protocol.h" + +typedef quintptr WId; + +class LXQtTaskBarWlrootsWindow; + +class LXQtTaskBarWlrootsWindowManagment : public QWaylandClientExtensionTemplate, + public QtWayland::zwlr_foreign_toplevel_manager_v1 +{ + Q_OBJECT +public: + static constexpr int version = 16; + + LXQtTaskBarWlrootsWindowManagment(); + ~LXQtTaskBarWlrootsWindowManagment(); + + inline bool isShowingDesktop() const { return m_isShowingDesktop; } + +protected: + void zwlr_foreign_toplevel_manager_v1_toplevel(struct ::zwlr_foreign_toplevel_handle_v1 *toplevel); + void zwlr_foreign_toplevel_manager_v1_finished() {}; + +Q_SIGNALS: + void windowCreated(LXQtTaskBarWlrootsWindow *window); + +private: + bool m_isShowingDesktop = false; +}; + +class LXQtTaskBarWlrootsWindow : public QObject, + public QtWayland::zwlr_foreign_toplevel_handle_v1 +{ + Q_OBJECT +public: + LXQtTaskBarWlrootsWindow(::zwlr_foreign_toplevel_handle_v1 *id); + ~LXQtTaskBarWlrootsWindow(); + + inline WId getWindowId() const { return reinterpret_cast(this); } + + using state = QtWayland::zwlr_foreign_toplevel_handle_v1::state; + const QString uuid; + QString title; + QString appId; + QIcon icon; + QFlags windowState; + QPointer parentWindow; + + void set_state( LXQtTaskBarWlrootsWindow::state, bool ) {}; + +Q_SIGNALS: + void titleChanged(); + void appIdChanged(); + void outputEnter(); + void outputLeave(); + void activeChanged(); + void maximizedChanged(); + void minimizedChanged(); + void fullscreenChanged(); + void done(); + void closed(); + void parentChanged(); + +protected: + void zwlr_foreign_toplevel_handle_v1_title(const QString &title); + void zwlr_foreign_toplevel_handle_v1_app_id(const QString &app_id); + void zwlr_foreign_toplevel_handle_v1_output_enter(struct ::wl_output *output); + void zwlr_foreign_toplevel_handle_v1_output_leave(struct ::wl_output *output); + void zwlr_foreign_toplevel_handle_v1_state(wl_array *state); + void zwlr_foreign_toplevel_handle_v1_done(); + void zwlr_foreign_toplevel_handle_v1_closed(); + void zwlr_foreign_toplevel_handle_v1_parent(struct ::zwlr_foreign_toplevel_handle_v1 *parent); + +private: + void setParentWindow(LXQtTaskBarWlrootsWindow *parent); + + QMetaObject::Connection parentWindowUnmappedConnection; +}; + +#endif // LXQTTASKBARWlrootsWINDOWMANAGMENT_H diff --git a/panel/backends/wayland/wlroots/lxqtwlrvirtualdesktop.cpp b/panel/backends/wayland/wlroots/lxqtwlrvirtualdesktop.cpp new file mode 100644 index 000000000..51e2974ac --- /dev/null +++ b/panel/backends/wayland/wlroots/lxqtwlrvirtualdesktop.cpp @@ -0,0 +1,104 @@ +#include "lxqtwlrvirtualdesktop.h" + +#include + +LXQtWlrootsVirtualDesktop::LXQtWlrootsVirtualDesktop(const QString &id) + : id(id) + , name(QString::fromUtf8("Desktop 1")) +{ +} + +LXQtWlrootsVirtualDesktop::~LXQtWlrootsVirtualDesktop() +{ +} + +LXQtWlrootsVirtualDesktopManagment::LXQtWlrootsVirtualDesktopManagment() : QObject() +{ +} + +LXQtWlrootsVirtualDesktopManagment::~LXQtWlrootsVirtualDesktopManagment() +{ +} + +LXQtWlrootsWaylandWorkspaceInfo::LXQtWlrootsWaylandWorkspaceInfo() +{ + init(); +} + +LXQtWlrootsWaylandWorkspaceInfo::VirtualDesktopsIterator LXQtWlrootsWaylandWorkspaceInfo::findDesktop(const QString &id) const +{ + return std::find_if(virtualDesktops.begin(), virtualDesktops.end(), + [&id](const std::unique_ptr &desktop) { + return desktop->id == id; + }); +} + +QString LXQtWlrootsWaylandWorkspaceInfo::getDesktopName(int pos) const +{ + if(pos < 0 || size_t(pos) >= virtualDesktops.size()) + return QString(); + return virtualDesktops[pos]->name; +} + +QString LXQtWlrootsWaylandWorkspaceInfo::getDesktopId(int pos) const +{ + if(pos < 0 || size_t(pos) >= virtualDesktops.size()) + return QString(); + return virtualDesktops[pos]->id; +} + +void LXQtWlrootsWaylandWorkspaceInfo::init() +{ + virtualDesktopManagement = std::make_unique(); + auto desktopOne = std::make_unique(QString::fromUtf8("desktop-1")); + virtualDesktops.insert(std::next(virtualDesktops.begin(), 0), std::move(desktopOne)); + + currentVirtualDesktop = QString::fromUtf8( "desktop-1" ); +} + +void LXQtWlrootsWaylandWorkspaceInfo::addDesktop(const QString &, quint32) +{ +} + +QVariant LXQtWlrootsWaylandWorkspaceInfo::currentDesktop() const +{ + return currentVirtualDesktop; +} + +int LXQtWlrootsWaylandWorkspaceInfo::numberOfDesktops() const +{ + // will always be 1 (at least until ext-workspace becomes available) + return 1; +} + +quint32 LXQtWlrootsWaylandWorkspaceInfo::position(const QVariant &desktop) const +{ + return 0; +} + +QVariantList LXQtWlrootsWaylandWorkspaceInfo::desktopIds() const +{ + return { virtualDesktops.at( 0 ).get()->id }; +} + +QStringList LXQtWlrootsWaylandWorkspaceInfo::desktopNames() const +{ + return { virtualDesktops.at( 0 ).get()->name }; +} + +int LXQtWlrootsWaylandWorkspaceInfo::desktopLayoutRows() const +{ + return 1; +} + +void LXQtWlrootsWaylandWorkspaceInfo::requestActivate(const QVariant &) +{ +} + +void LXQtWlrootsWaylandWorkspaceInfo::requestCreateDesktop(quint32 position) +{ +} + +void LXQtWlrootsWaylandWorkspaceInfo::requestRemoveDesktop(quint32 position) +{ +} diff --git a/panel/backends/wayland/wlroots/lxqtwlrvirtualdesktop.h b/panel/backends/wayland/wlroots/lxqtwlrvirtualdesktop.h new file mode 100644 index 000000000..f20020d5e --- /dev/null +++ b/panel/backends/wayland/wlroots/lxqtwlrvirtualdesktop.h @@ -0,0 +1,73 @@ +#ifndef LXQTWlrootsVIRTUALDESKTOP_H +#define LXQTWlrootsVIRTUALDESKTOP_H + +#include +#include + +class LXQtWlrootsVirtualDesktop : public QObject +{ + Q_OBJECT +public: + LXQtWlrootsVirtualDesktop(const QString &id); + ~LXQtWlrootsVirtualDesktop(); + const QString id; + QString name; +Q_SIGNALS: + void done(); + void activated(); + void nameChanged(); +}; + +class LXQtWlrootsVirtualDesktopManagment : public QObject +{ + Q_OBJECT +public: + LXQtWlrootsVirtualDesktopManagment(); + ~LXQtWlrootsVirtualDesktopManagment(); + +signals: + void desktopCreated(const QString &id, quint32 position); + void desktopRemoved(const QString &id); + void rowsChanged(const quint32 rows); +}; + +class Q_DECL_HIDDEN LXQtWlrootsWaylandWorkspaceInfo : public QObject +{ + Q_OBJECT +public: + LXQtWlrootsWaylandWorkspaceInfo(); + + QVariant currentVirtualDesktop; + std::vector> virtualDesktops; + std::unique_ptr virtualDesktopManagement; + const quint32 rows = 1; + + typedef std::vector>::const_iterator VirtualDesktopsIterator; + + VirtualDesktopsIterator findDesktop(const QString &id) const; + + QString getDesktopName(int pos) const; + QString getDesktopId(int pos) const; + + void init(); + void addDesktop(const QString &id, quint32 pos); + QVariant currentDesktop() const; + int numberOfDesktops() const; + QVariantList desktopIds() const; + QStringList desktopNames() const; + quint32 position(const QVariant &desktop) const; + int desktopLayoutRows() const; + void requestActivate(const QVariant &desktop); + void requestCreateDesktop(quint32 position); + void requestRemoveDesktop(quint32 position); + +signals: + void currentDesktopChanged(); + void numberOfDesktopsChanged(); + void navigationWrappingAroundChanged(); + void desktopIdsChanged(); + void desktopNameChanged(quint32 position); + void desktopLayoutRowsChanged(); +}; + +#endif // LXQTWlrootsVIRTUALDESKTOP_H diff --git a/panel/backends/wayland/wlroots/protocols/wlr-foreign-toplevel-management-unstable-v1.xml b/panel/backends/wayland/wlroots/protocols/wlr-foreign-toplevel-management-unstable-v1.xml new file mode 100644 index 000000000..108133715 --- /dev/null +++ b/panel/backends/wayland/wlroots/protocols/wlr-foreign-toplevel-management-unstable-v1.xml @@ -0,0 +1,270 @@ + + + + Copyright © 2018 Ilia Bozhinov + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + The purpose of this protocol is to enable the creation of taskbars + and docks by providing them with a list of opened applications and + letting them request certain actions on them, like maximizing, etc. + + After a client binds the zwlr_foreign_toplevel_manager_v1, each opened + toplevel window will be sent via the toplevel event + + + + + This event is emitted whenever a new toplevel window is created. It + is emitted for all toplevels, regardless of the app that has created + them. + + All initial details of the toplevel(title, app_id, states, etc.) will + be sent immediately after this event via the corresponding events in + zwlr_foreign_toplevel_handle_v1. + + + + + + + Indicates the client no longer wishes to receive events for new toplevels. + However the compositor may emit further toplevel_created events, until + the finished event is emitted. + + The client must not send any more requests after this one. + + + + + + This event indicates that the compositor is done sending events to the + zwlr_foreign_toplevel_manager_v1. The server will destroy the object + immediately after sending this request, so it will become invalid and + the client should free any resources associated with it. + + + + + + + A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel + window. Each app may have multiple opened toplevels. + + Each toplevel has a list of outputs it is visible on, conveyed to the + client with the output_enter and output_leave events. + + + + + This event is emitted whenever the title of the toplevel changes. + + + + + + + This event is emitted whenever the app-id of the toplevel changes. + + + + + + + This event is emitted whenever the toplevel becomes visible on + the given output. A toplevel may be visible on multiple outputs. + + + + + + + This event is emitted whenever the toplevel stops being visible on + the given output. It is guaranteed that an entered-output event + with the same output has been emitted before this event. + + + + + + + Requests that the toplevel be maximized. If the maximized state actually + changes, this will be indicated by the state event. + + + + + + Requests that the toplevel be unmaximized. If the maximized state actually + changes, this will be indicated by the state event. + + + + + + Requests that the toplevel be minimized. If the minimized state actually + changes, this will be indicated by the state event. + + + + + + Requests that the toplevel be unminimized. If the minimized state actually + changes, this will be indicated by the state event. + + + + + + Request that this toplevel be activated on the given seat. + There is no guarantee the toplevel will be actually activated. + + + + + + + The different states that a toplevel can have. These have the same meaning + as the states with the same names defined in xdg-toplevel + + + + + + + + + + + This event is emitted immediately after the zlw_foreign_toplevel_handle_v1 + is created and each time the toplevel state changes, either because of a + compositor action or because of a request in this protocol. + + + + + + + + This event is sent after all changes in the toplevel state have been + sent. + + This allows changes to the zwlr_foreign_toplevel_handle_v1 properties + to be seen as atomic, even if they happen via multiple events. + + + + + + Send a request to the toplevel to close itself. The compositor would + typically use a shell-specific method to carry out this request, for + example by sending the xdg_toplevel.close event. However, this gives + no guarantees the toplevel will actually be destroyed. If and when + this happens, the zwlr_foreign_toplevel_handle_v1.closed event will + be emitted. + + + + + + The rectangle of the surface specified in this request corresponds to + the place where the app using this protocol represents the given toplevel. + It can be used by the compositor as a hint for some operations, e.g + minimizing. The client is however not required to set this, in which + case the compositor is free to decide some default value. + + If the client specifies more than one rectangle, only the last one is + considered. + + The dimensions are given in surface-local coordinates. + Setting width=height=0 removes the already-set rectangle. + + + + + + + + + + + + + + + + This event means the toplevel has been destroyed. It is guaranteed there + won't be any more events for this zwlr_foreign_toplevel_handle_v1. The + toplevel itself becomes inert so any requests will be ignored except the + destroy request. + + + + + + Destroys the zwlr_foreign_toplevel_handle_v1 object. + + This request should be called either when the client does not want to + use the toplevel anymore or after the closed event to finalize the + destruction of the object. + + + + + + + + Requests that the toplevel be fullscreened on the given output. If the + fullscreen state and/or the outputs the toplevel is visible on actually + change, this will be indicated by the state and output_enter/leave + events. + + The output parameter is only a hint to the compositor. Also, if output + is NULL, the compositor should decide which output the toplevel will be + fullscreened on, if at all. + + + + + + + Requests that the toplevel be unfullscreened. If the fullscreen state + actually changes, this will be indicated by the state event. + + + + + + + + This event is emitted whenever the parent of the toplevel changes. + + No event is emitted when the parent handle is destroyed by the client. + + + + + diff --git a/panel/backends/xcb/lxqttaskbarbackend_x11.cpp b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp index 822afed51..6037e91d8 100644 --- a/panel/backends/xcb/lxqttaskbarbackend_x11.cpp +++ b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp @@ -535,7 +535,7 @@ void LXQtTaskbarX11Backend::moveApplication(WId windowId) int X = g.center().x(); int Y = g.center().y(); QCursor::setPos(X, Y); - NETRootInfo(m_xcbConnection, NET::WMMoveResize).moveResizeRequest(windowId, X, Y, NET::Move); + // NETRootInfo(m_xcbConnection, NET::WMMoveResize).moveResizeRequest(windowId, X, Y, NET::Move); } void LXQtTaskbarX11Backend::resizeApplication(WId windowId) @@ -553,7 +553,7 @@ void LXQtTaskbarX11Backend::resizeApplication(WId windowId) int X = g.bottomRight().x(); int Y = g.bottomRight().y(); QCursor::setPos(X, Y); - NETRootInfo(m_xcbConnection, NET::WMMoveResize).moveResizeRequest(windowId, X, Y, NET::BottomRight); + // NETRootInfo(m_xcbConnection, NET::WMMoveResize).moveResizeRequest(windowId, X, Y, NET::BottomRight); } void LXQtTaskbarX11Backend::refreshIconGeometry(WId windowId, QRect const & geom) diff --git a/panel/lxqtpanelapplication.cpp b/panel/lxqtpanelapplication.cpp index a3d2132f1..5518e801e 100644 --- a/panel/lxqtpanelapplication.cpp +++ b/panel/lxqtpanelapplication.cpp @@ -40,14 +40,15 @@ #include "backends/lxqttaskbardummybackend.h" #include "backends/xcb/lxqttaskbarbackend_x11.h" -#include "backends/wayland/lxqttaskbarbackendwayland.h" +#include "backends/wayland/plasma/lxqttaskbarbackendplasma.h" +#include "backends/wayland/wlroots/lxqttaskbarbackendwlr.h" ILXQtTaskbarAbstractBackend *createWMBackend() { if(qGuiApp->nativeInterface()) return new LXQtTaskbarX11Backend; else if(qGuiApp->nativeInterface()) - return new LXQtTaskbarWaylandBackend; + return new LXQtTaskbarWlrootsBackend; qWarning() << "\n" << "ERROR: Could not create a backend for window managment operations.\n" From e68edc8398a7318be51b075404d7d2da3cd0170f Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Wed, 27 Mar 2024 00:32:23 +0530 Subject: [PATCH 127/165] Fix a few implementation issues --- .../wayland/plasma/lxqtplasmavirtualdesktop.h | 5 +-- .../wayland/plasma/lxqttaskbarbackendplasma.h | 5 +-- .../plasma/lxqttaskbarplasmawindowmanagment.h | 5 +-- .../wayland/wlroots/lxqttaskbarbackendwlr.h | 5 +-- .../wlroots/lxqttaskbarwlrwindowmanagment.cpp | 40 ++++++++++++++----- .../wlroots/lxqttaskbarwlrwindowmanagment.h | 6 +-- .../wayland/wlroots/lxqtwlrvirtualdesktop.h | 5 +-- 7 files changed, 36 insertions(+), 35 deletions(-) diff --git a/panel/backends/wayland/plasma/lxqtplasmavirtualdesktop.h b/panel/backends/wayland/plasma/lxqtplasmavirtualdesktop.h index fff025213..825f0de83 100644 --- a/panel/backends/wayland/plasma/lxqtplasmavirtualdesktop.h +++ b/panel/backends/wayland/plasma/lxqtplasmavirtualdesktop.h @@ -1,5 +1,4 @@ -#ifndef LXQTPLASMAVIRTUALDESKTOP_H -#define LXQTPLASMAVIRTUALDESKTOP_H +#pragma once #include #include @@ -87,5 +86,3 @@ class Q_DECL_HIDDEN LXQtPlasmaWaylandWorkspaceInfo : public QObject void desktopNameChanged(quint32 position); void desktopLayoutRowsChanged(); }; - -#endif // LXQTPLASMAVIRTUALDESKTOP_H diff --git a/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.h b/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.h index 31b78f6c6..f47d724c8 100644 --- a/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.h +++ b/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.h @@ -1,5 +1,4 @@ -#ifndef LXQTTASKBARBACKENDPLASMA_H -#define LXQTTASKBARBACKENDPLASMA_H +#pragma once #include "../../ilxqttaskbarabstractbackend.h" @@ -91,5 +90,3 @@ class LXQtTaskbarPlasmaBackend : public ILXQtTaskbarAbstractBackend // key=leader, values=transient children QMultiHash transientsDemandingAttention; }; - -#endif // LXQTTASKBARBACKENDWAYLAND_H diff --git a/panel/backends/wayland/plasma/lxqttaskbarplasmawindowmanagment.h b/panel/backends/wayland/plasma/lxqttaskbarplasmawindowmanagment.h index 976ac53e2..c05dbc5d5 100644 --- a/panel/backends/wayland/plasma/lxqttaskbarplasmawindowmanagment.h +++ b/panel/backends/wayland/plasma/lxqttaskbarplasmawindowmanagment.h @@ -1,5 +1,4 @@ -#ifndef LXQTTASKBARPLASMAWINDOWMANAGMENT_H -#define LXQTTASKBARPLASMAWINDOWMANAGMENT_H +#pragma once #include #include @@ -158,5 +157,3 @@ class LXQtTaskBarPlasmaWindowManagment : public QWaylandClientExtensionTemplate< // private: // WaylandTasksModel *q; // }; - -#endif // LXQTTASKBARPLASMAWINDOWMANAGMENT_H diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h index 9b4348afd..363ff698b 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h @@ -1,5 +1,4 @@ -#ifndef LXQTTASKBARBACKENDWLROOTS_H -#define LXQTTASKBARBACKENDWLROOTS_H +#pragma once #include "../../ilxqttaskbarabstractbackend.h" @@ -91,5 +90,3 @@ class LXQtTaskbarWlrootsBackend : public ILXQtTaskbarAbstractBackend // key=leader, values=transient children QMultiHash transientsDemandingAttention; }; - -#endif // LXQTTASKBARBACKENDWAYLAND_H diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp index 214b6f5d1..33fb27234 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp @@ -37,6 +37,7 @@ LXQtTaskBarWlrootsWindowManagment::~LXQtTaskBarWlrootsWindowManagment() void LXQtTaskBarWlrootsWindowManagment::zwlr_foreign_toplevel_manager_v1_toplevel(struct ::zwlr_foreign_toplevel_handle_v1 *toplevel) { + qDebug() << "new toplevel created"; emit windowCreated( new LXQtTaskBarWlrootsWindow(toplevel) ); } @@ -46,15 +47,10 @@ void LXQtTaskBarWlrootsWindowManagment::zwlr_foreign_toplevel_manager_v1_topleve */ LXQtTaskBarWlrootsWindow::LXQtTaskBarWlrootsWindow(::zwlr_foreign_toplevel_handle_v1 *id) - : zwlr_foreign_toplevel_handle_v1(id), - uuid(QString::number(0)) + : zwlr_foreign_toplevel_handle_v1(id) { } -/* - * LXQtTaskBarWlrootsWindow - */ - LXQtTaskBarWlrootsWindow::~LXQtTaskBarWlrootsWindow() { destroy(); @@ -85,10 +81,34 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_leave(stru void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_state(wl_array *state) { - // emit activeChanged(); - // emit maximizedChanged(); - // emit minimizedChanged(); - // emit fullscreenChanged(); + QFlags wlrState; + + uint32_t* statePtr = static_cast(state->data); + for (size_t i = 0; i < state->size / sizeof(uint32_t); i++) { + switch (statePtr[i]) { + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED: + wlrState |= state_maximized; + break; + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN: + wlrState |= state_fullscreen; + break; + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED: + wlrState |= state_minimized; + break; + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED: + wlrState |= state_activated; + break; + } + } + + if ( windowState.testFlag( state_maximized ) ^ wlrState.testFlag( state_maximized ) ) + emit maximizedChanged(); + if ( windowState.testFlag( state_minimized ) ^ wlrState.testFlag( state_minimized ) ) + emit minimizedChanged(); + if ( windowState.testFlag( state_fullscreen ) ^ wlrState.testFlag( state_fullscreen ) ) + emit fullscreenChanged(); + if ( windowState.testFlag( state_activated ) ^ wlrState.testFlag( state_activated ) ) + emit activeChanged(); } void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_done() diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h index d488ce15b..767f11f32 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h @@ -1,5 +1,4 @@ -#ifndef LXQTTASKBARWlrootsWINDOWMANAGMENT_H -#define LXQTTASKBARWlrootsWINDOWMANAGMENT_H +#pragma once #include #include @@ -46,7 +45,6 @@ class LXQtTaskBarWlrootsWindow : public QObject, inline WId getWindowId() const { return reinterpret_cast(this); } using state = QtWayland::zwlr_foreign_toplevel_handle_v1::state; - const QString uuid; QString title; QString appId; QIcon icon; @@ -83,5 +81,3 @@ class LXQtTaskBarWlrootsWindow : public QObject, QMetaObject::Connection parentWindowUnmappedConnection; }; - -#endif // LXQTTASKBARWlrootsWINDOWMANAGMENT_H diff --git a/panel/backends/wayland/wlroots/lxqtwlrvirtualdesktop.h b/panel/backends/wayland/wlroots/lxqtwlrvirtualdesktop.h index f20020d5e..4853c43a6 100644 --- a/panel/backends/wayland/wlroots/lxqtwlrvirtualdesktop.h +++ b/panel/backends/wayland/wlroots/lxqtwlrvirtualdesktop.h @@ -1,5 +1,4 @@ -#ifndef LXQTWlrootsVIRTUALDESKTOP_H -#define LXQTWlrootsVIRTUALDESKTOP_H +#pragma once #include #include @@ -69,5 +68,3 @@ class Q_DECL_HIDDEN LXQtWlrootsWaylandWorkspaceInfo : public QObject void desktopNameChanged(quint32 position); void desktopLayoutRowsChanged(); }; - -#endif // LXQTWlrootsVIRTUALDESKTOP_H From 141c3ed27ffb5f42ca28d8e9665b4fd6c083e02e Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Wed, 27 Mar 2024 01:12:17 +0530 Subject: [PATCH 128/165] Updates --- .../wayland/wlroots/lxqttaskbarbackendwlr.cpp | 23 +++---------------- .../wlroots/lxqttaskbarwlrwindowmanagment.cpp | 21 ++++++++++++++++- .../wlroots/lxqttaskbarwlrwindowmanagment.h | 2 ++ 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp index 7f9590a2c..16dd774f9 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp @@ -230,26 +230,7 @@ bool LXQtTaskbarWlrootsBackend::raiseWindow(WId windowId, bool onCurrentWorkSpac if(!window) return false; - // Pull forward any transient demanding attention. - if (auto *transientDemandingAttention = transientsDemandingAttention.value(window)) - { - window = transientDemandingAttention; - } - else - { - // TODO Shouldn't KWin take care of that? - // Bringing a transient to the front usually brings its parent with it - // but focus is not handled properly. - // TODO take into account d->lastActivation instead - // of just taking the first one. - while (transients.key(window)) - { - window = transients.key(window); - } - } - - window->set_state(LXQtTaskBarWlrootsWindow::state::state_activated, LXQtTaskBarWlrootsWindow::state::state_activated); - return true; + window->activate(); } bool LXQtTaskbarWlrootsBackend::closeWindow(WId windowId) @@ -469,6 +450,8 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) windows.emplace_back(window); updateWindowAcceptance(window); } + + emit windowAdded( window->getWindowId() ); } bool LXQtTaskbarWlrootsBackend::acceptWindow(LXQtTaskBarWlrootsWindow *window) const diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp index 33fb27234..7887323ac 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp @@ -10,10 +10,24 @@ #include #include +#include + #include #include #include +wl_seat *get_seat() { + QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface(); + + if ( !native ) { + return nullptr; + } + + struct wl_seat *seat = reinterpret_cast(native->nativeResourceForIntegration( "wl_seat" ) ); + + return seat; +} + /* * LXQtTaskBarWlrootsWindowManagment */ @@ -56,6 +70,11 @@ LXQtTaskBarWlrootsWindow::~LXQtTaskBarWlrootsWindow() destroy(); } +void LXQtTaskBarWlrootsWindow::activate() +{ + zwlr_foreign_toplevel_handle_v1::activate(get_seat()); +} + void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_title(const QString &title) { emit titleChanged(); @@ -123,7 +142,7 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_closed() void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_parent(struct ::zwlr_foreign_toplevel_handle_v1 *parent) { - setParentWindow( new LXQtTaskBarWlrootsWindow(parent)); + // setParentWindow(new LXQtTaskBarWlrootsWindow(parent)); } void LXQtTaskBarWlrootsWindow::setParentWindow(LXQtTaskBarWlrootsWindow *parent) diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h index 767f11f32..f4a2c6285 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h @@ -44,6 +44,8 @@ class LXQtTaskBarWlrootsWindow : public QObject, inline WId getWindowId() const { return reinterpret_cast(this); } + void activate(); + using state = QtWayland::zwlr_foreign_toplevel_handle_v1::state; QString title; QString appId; From f0c55a672827d86f76cfb4479f33eb07df03fecb Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Wed, 27 Mar 2024 01:14:24 +0530 Subject: [PATCH 129/165] Update tasks list on window closed --- panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp index 16dd774f9..b0c43223c 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp @@ -349,6 +349,8 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) activeWindow = nullptr; emit activeWindowChanged(0); } + + emit windowRemoved(window->getWindowId()); }; connect(window, &LXQtTaskBarWlrootsWindow::closed, this, removeWindow); From a69f61cdd134d2ee8aca150c67de670420ac469d Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Wed, 27 Mar 2024 01:20:08 +0530 Subject: [PATCH 130/165] Few more updates --- .../wayland/wlroots/lxqttaskbarbackendwlr.cpp | 11 ++++------- .../backends/wayland/wlroots/lxqttaskbarbackendwlr.h | 1 - .../wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp | 4 ++-- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp index b0c43223c..0eda67dfb 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp @@ -231,6 +231,7 @@ bool LXQtTaskbarWlrootsBackend::raiseWindow(WId windowId, bool onCurrentWorkSpac return false; window->activate(); + return true; } bool LXQtTaskbarWlrootsBackend::closeWindow(WId windowId) @@ -356,7 +357,7 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) connect(window, &LXQtTaskBarWlrootsWindow::closed, this, removeWindow); connect(window, &LXQtTaskBarWlrootsWindow::titleChanged, this, [window, this] { - updateWindowAcceptance(window); + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Title)); }); connect(window, &LXQtTaskBarWlrootsWindow::appIdChanged, this, [window, this] { @@ -433,7 +434,7 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) }); auto stateChanged = [window, this] { - updateWindowAcceptance(window); + // updateWindowAcceptance(window); }; connect(window, &LXQtTaskBarWlrootsWindow::fullscreenChanged, this, stateChanged); @@ -450,7 +451,7 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) else { windows.emplace_back(window); - updateWindowAcceptance(window); + // updateWindowAcceptance(window); } emit windowAdded( window->getWindowId() ); @@ -464,10 +465,6 @@ bool LXQtTaskbarWlrootsBackend::acceptWindow(LXQtTaskBarWlrootsWindow *window) c return true; } -void LXQtTaskbarWlrootsBackend::updateWindowAcceptance(LXQtTaskBarWlrootsWindow *) -{ -} - LXQtTaskBarWlrootsWindow *LXQtTaskbarWlrootsBackend::getWindow(WId windowId) const { for(auto &window : std::as_const(windows)) diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h index 363ff698b..61ed5d81a 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h @@ -73,7 +73,6 @@ class LXQtTaskbarWlrootsBackend : public ILXQtTaskbarAbstractBackend private: void addWindow(LXQtTaskBarWlrootsWindow *window); bool acceptWindow(LXQtTaskBarWlrootsWindow *window) const; - void updateWindowAcceptance(LXQtTaskBarWlrootsWindow *window); private: LXQtTaskBarWlrootsWindow *getWindow(WId windowId) const; diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp index 7887323ac..e6ece8834 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp @@ -77,14 +77,14 @@ void LXQtTaskBarWlrootsWindow::activate() void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_title(const QString &title) { - emit titleChanged(); this->title = title; + emit titleChanged(); } void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_app_id(const QString &app_id) { - emit appIdChanged(); this->appId = app_id; + emit appIdChanged(); // Code to get the icon needs to be inserted here } From 9f72a3b0b317345e703d98b7d9be7b99eeff90a0 Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Wed, 27 Mar 2024 15:13:47 +0530 Subject: [PATCH 131/165] Allow both wlroots and plasma backends to coexist --- panel/CMakeLists.txt | 1 + panel/backends/wayland/CMakeLists.txt | 12 + .../wayland/lxqttaskbarbackendwayland.cpp | 219 ++++++++++++++++++ .../wayland/lxqttaskbarbackendwayland.h | 86 +++++++ .../plasma/lxqttaskbarbackendplasma.cpp | 1 + panel/lxqtpanelapplication.cpp | 12 +- plugin-taskbar/lxqttaskbarplugin.cpp | 1 + 7 files changed, 329 insertions(+), 3 deletions(-) create mode 100644 panel/backends/wayland/lxqttaskbarbackendwayland.cpp create mode 100644 panel/backends/wayland/lxqttaskbarbackendwayland.h diff --git a/panel/CMakeLists.txt b/panel/CMakeLists.txt index f081f2f5f..a302b030a 100644 --- a/panel/CMakeLists.txt +++ b/panel/CMakeLists.txt @@ -7,6 +7,7 @@ add_subdirectory(backends) set(PANEL_BACKENDS lxqt-panel-backend-plasma lxqt-panel-backend-wlroots + lxqt-panel-backend-wayland lxqt-panel-backend-xcb ) diff --git a/panel/backends/wayland/CMakeLists.txt b/panel/backends/wayland/CMakeLists.txt index d23f14f1e..df8f737fa 100644 --- a/panel/backends/wayland/CMakeLists.txt +++ b/panel/backends/wayland/CMakeLists.txt @@ -1,2 +1,14 @@ add_subdirectory(plasma) add_subdirectory(wlroots) + +add_library(lxqt-panel-backend-wayland STATIC + lxqttaskbarbackendwayland.h + lxqttaskbarbackendwayland.cpp +) + +target_link_libraries(lxqt-panel-backend-wayland + Qt6::GuiPrivate + lxqt-panel-backend-common + lxqt-panel-backend-plasma + lxqt-panel-backend-wlroots +) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp new file mode 100644 index 000000000..3e23d1de5 --- /dev/null +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -0,0 +1,219 @@ +#include "lxqttaskbarbackendwayland.h" +#include "plasma/lxqttaskbarbackendplasma.h" +#include "wlroots/lxqttaskbarbackendwlr.h" + +#include + +LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) + : ILXQtTaskbarAbstractBackend(parent) +{ + /** + * HACK: For now, we will simply concentrate on plasma and wlroots. + * Eventually, we should be dynamically loading different plugins. + */ + + /** + * Let's get our DE: + * If plasma/kde is in desktopsList, we'll use the plasma backend. + * If wayfire/sway/labwc/hyprland/wlroots is in desktopsList, we'll use the wlroots backend. + */ + QList desktopsList = qgetenv("XDG_CURRENT_DESKTOP").toLower().split(':'); + qDebug() << "--------------> Current desktop" << desktopsList; + for( QByteArray desktop: desktopsList ) { + if ( desktop == "plasma" || desktop == "kde" ) + { + qDebug() << "--------------> Using plasma backend"; + m_backend = new LXQtTaskbarPlasmaBackend(); + break; + } + + else if ( desktop == "wayfire" ) + { + qDebug() << "--------------> Using wlroots backend"; + m_backend = new LXQtTaskbarWlrootsBackend(); + break; + } + + else if ( desktop == "sway" ) + { + qDebug() << "--------------> Using wlroots backend"; + m_backend = new LXQtTaskbarWlrootsBackend(); + break; + } + + else if ( desktop == "labwc" ) + { + qDebug() << "--------------> Using wlroots backend"; + m_backend = new LXQtTaskbarWlrootsBackend(); + break; + } + + else if ( desktop == "hyprland" ) + { + qDebug() << "--------------> Using wlroots backend"; + m_backend = new LXQtTaskbarWlrootsBackend(); + break; + } + + else + { + // m_backend = nullptr; + } + } +} + + +/************************************************ + * Windows function + ************************************************/ +bool LXQtTaskbarWaylandBackend::supportsAction(WId id, LXQtTaskBarBackendAction act) const +{ + return ( m_backend == nullptr ? false : m_backend->supportsAction(id, act) ); +} + +bool LXQtTaskbarWaylandBackend::reloadWindows() +{ + return ( m_backend == nullptr ? false : m_backend->reloadWindows() ); +} + +QVector LXQtTaskbarWaylandBackend::getCurrentWindows() const +{ + return ( m_backend == nullptr ? QVector() : m_backend->getCurrentWindows() ); +} + +QString LXQtTaskbarWaylandBackend::getWindowTitle(WId id) const +{ + return ( m_backend == nullptr ? QString() : m_backend->getWindowTitle(id) ); +} + +bool LXQtTaskbarWaylandBackend::applicationDemandsAttention(WId id) const +{ + return ( m_backend == nullptr ? false : m_backend->applicationDemandsAttention(id) ); +} + +QIcon LXQtTaskbarWaylandBackend::getApplicationIcon(WId id, int fallbackDevicePixels) const +{ + return ( m_backend == nullptr ? QIcon() : m_backend->getApplicationIcon(id, fallbackDevicePixels) ); +} + +QString LXQtTaskbarWaylandBackend::getWindowClass(WId id) const +{ + return ( m_backend == nullptr ? QString() : m_backend->getWindowClass(id) ); +} + +LXQtTaskBarWindowLayer LXQtTaskbarWaylandBackend::getWindowLayer(WId id) const +{ + return ( m_backend == nullptr ? LXQtTaskBarWindowLayer::Normal : m_backend->getWindowLayer(id) ); +} + +bool LXQtTaskbarWaylandBackend::setWindowLayer(WId id, LXQtTaskBarWindowLayer lyr) +{ + return ( m_backend == nullptr ? false : m_backend->setWindowLayer(id, lyr) ); +} + +LXQtTaskBarWindowState LXQtTaskbarWaylandBackend::getWindowState(WId id) const +{ + return ( m_backend == nullptr ? LXQtTaskBarWindowState::Normal : m_backend->getWindowState(id) ); +} + +bool LXQtTaskbarWaylandBackend::setWindowState(WId id, LXQtTaskBarWindowState state, bool set) +{ + return ( m_backend == nullptr ? false : m_backend->setWindowState(id, state, set) ); +} + +bool LXQtTaskbarWaylandBackend::isWindowActive(WId id) const +{ + return ( m_backend == nullptr ? false : m_backend->isWindowActive(id) ); +} + +bool LXQtTaskbarWaylandBackend::raiseWindow(WId id, bool yes) +{ + return ( m_backend == nullptr ? false : m_backend->raiseWindow(id, yes) ); +} + +bool LXQtTaskbarWaylandBackend::closeWindow(WId id) +{ + return ( m_backend == nullptr ? false : m_backend->closeWindow(id) ); +} + +WId LXQtTaskbarWaylandBackend::getActiveWindow() const +{ + return ( m_backend == nullptr ? 0 : m_backend->getActiveWindow() ); +} + + +/************************************************ + * Workspaces + ************************************************/ +int LXQtTaskbarWaylandBackend::getWorkspacesCount() const +{ + return ( m_backend == nullptr ? 1 : m_backend->getWorkspacesCount() ); // Fake 1 workspace +} + +QString LXQtTaskbarWaylandBackend::getWorkspaceName(int ws) const +{ + return ( m_backend == nullptr ? QString() : m_backend->getWorkspaceName( ws ) ); +} + +int LXQtTaskbarWaylandBackend::getCurrentWorkspace() const +{ + return ( m_backend == nullptr ? 0 : m_backend->getCurrentWorkspace() ); +} + +bool LXQtTaskbarWaylandBackend::setCurrentWorkspace(int ws) +{ + return ( m_backend == nullptr ? false : m_backend->setCurrentWorkspace(ws) ); +} + +int LXQtTaskbarWaylandBackend::getWindowWorkspace(WId id) const +{ + return ( m_backend == nullptr ? 0 : m_backend->getWindowWorkspace( id ) ); +} + +bool LXQtTaskbarWaylandBackend::setWindowOnWorkspace(WId id, int ws) +{ + return ( m_backend == nullptr ? false : m_backend->setWindowOnWorkspace(id, ws) ); +} + +void LXQtTaskbarWaylandBackend::moveApplicationToPrevNextMonitor(WId id, bool next, bool raise) +{ + ( m_backend == nullptr ? void() : m_backend->moveApplicationToPrevNextMonitor( id, next, raise) ); +} + +bool LXQtTaskbarWaylandBackend::isWindowOnScreen(QScreen *scrn, WId id) const +{ + return ( m_backend == nullptr ? false : m_backend->isWindowOnScreen(scrn, id) ); +} + +/************************************************ + * X11 Specific + ************************************************/ +void LXQtTaskbarWaylandBackend::moveApplication(WId id) +{ + ( m_backend == nullptr ? void() : m_backend->moveApplication(id) ); +} + +void LXQtTaskbarWaylandBackend::resizeApplication(WId id) +{ + ( m_backend == nullptr ? void() : m_backend->resizeApplication(id) ); +} + +void LXQtTaskbarWaylandBackend::refreshIconGeometry(WId id, QRect const &rect) +{ + ( m_backend == nullptr ? void() : m_backend->refreshIconGeometry(id, rect) ); +} + +bool LXQtTaskbarWaylandBackend::isAreaOverlapped(const QRect &rect) const +{ + return ( m_backend == nullptr ? false : m_backend->isAreaOverlapped(rect) ); +} + +bool LXQtTaskbarWaylandBackend::isShowingDesktop() const +{ + return ( m_backend == nullptr ? false : m_backend->isShowingDesktop() ); +} + +bool LXQtTaskbarWaylandBackend::showDesktop(bool yes) +{ + return ( m_backend == nullptr ? false : m_backend->showDesktop(yes) ); +} diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.h b/panel/backends/wayland/lxqttaskbarbackendwayland.h new file mode 100644 index 000000000..25027a567 --- /dev/null +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.h @@ -0,0 +1,86 @@ +#pragma once + +#include "../ilxqttaskbarabstractbackend.h" + +class LXQtTaskbarWaylandBackend : public ILXQtTaskbarAbstractBackend +{ + Q_OBJECT + +public: + explicit LXQtTaskbarWaylandBackend(QObject *parent = nullptr); + + // Backend + bool supportsAction(WId windowId, LXQtTaskBarBackendAction action) const override; + + // Windows + bool reloadWindows() override; + + QVector getCurrentWindows() const override; + + QString getWindowTitle(WId windowId) const override; + + bool applicationDemandsAttention(WId windowId) const override; + + QIcon getApplicationIcon(WId windowId, int fallbackDevicePixels) const override; + + QString getWindowClass(WId windowId) const override; + + LXQtTaskBarWindowLayer getWindowLayer(WId windowId) const override; + bool setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) override; + + LXQtTaskBarWindowState getWindowState(WId windowId) const override; + bool setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set = true) override; + + bool isWindowActive(WId windowId) const override; + bool raiseWindow(WId windowId, bool onCurrentWorkSpace) override; + + bool closeWindow(WId windowId) override; + + WId getActiveWindow() const override; + + // Workspaces + int getWorkspacesCount() const override; + QString getWorkspaceName(int idx) const override; + + int getCurrentWorkspace() const override; + bool setCurrentWorkspace(int idx) override; + + int getWindowWorkspace(WId windowId) const override; + bool setWindowOnWorkspace(WId windowId, int idx) override; + + void moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) override; + + bool isWindowOnScreen(QScreen *screen, WId windowId) const override; + + // X11 Specific + void moveApplication(WId windowId) override; + void resizeApplication(WId windowId) override; + + void refreshIconGeometry(WId windowId, const QRect &geom) override; + + // Panel internal + bool isAreaOverlapped(const QRect& area) const override; + + // Show Destop + bool isShowingDesktop() const override; + bool showDesktop(bool value) override; + +private: + ILXQtTaskbarAbstractBackend *m_backend = nullptr; + +signals: + void reloaded(); + + // Windows + void windowAdded(WId windowId); + void windowRemoved(WId windowId); + void windowPropertyChanged(WId windowId, int prop); + + // Workspaces + void workspacesCountChanged(); + void workspaceNameChanged(int idx); + void currentWorkspaceChanged(int idx); + + // TODO: needed? + void activeWindowChanged(WId windowId); +}; diff --git a/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.cpp b/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.cpp index 4631c03d3..84bcc0ca3 100644 --- a/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.cpp +++ b/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.cpp @@ -118,6 +118,7 @@ bool LXQtTaskbarPlasmaBackend::reloadWindows() QVector LXQtTaskbarPlasmaBackend::getCurrentWindows() const { + qDebug() << "--------------> Retrieving current window list"; QVector wids; wids.reserve(wids.size()); diff --git a/panel/lxqtpanelapplication.cpp b/panel/lxqtpanelapplication.cpp index 5518e801e..024ba4c05 100644 --- a/panel/lxqtpanelapplication.cpp +++ b/panel/lxqtpanelapplication.cpp @@ -40,15 +40,21 @@ #include "backends/lxqttaskbardummybackend.h" #include "backends/xcb/lxqttaskbarbackend_x11.h" +#include "backends/wayland/lxqttaskbarbackendwayland.h" #include "backends/wayland/plasma/lxqttaskbarbackendplasma.h" #include "backends/wayland/wlroots/lxqttaskbarbackendwlr.h" ILXQtTaskbarAbstractBackend *createWMBackend() { - if(qGuiApp->nativeInterface()) + if(qGuiApp->nativeInterface()){ + qDebug() << "--------------> X11Application"; return new LXQtTaskbarX11Backend; - else if(qGuiApp->nativeInterface()) - return new LXQtTaskbarWlrootsBackend; + } + + else if(qGuiApp->nativeInterface()){ + qDebug() << "--------------> WaylandApplication"; + return new LXQtTaskbarWaylandBackend; + } qWarning() << "\n" << "ERROR: Could not create a backend for window managment operations.\n" diff --git a/plugin-taskbar/lxqttaskbarplugin.cpp b/plugin-taskbar/lxqttaskbarplugin.cpp index 759e6db46..fbdd5b305 100644 --- a/plugin-taskbar/lxqttaskbarplugin.cpp +++ b/plugin-taskbar/lxqttaskbarplugin.cpp @@ -37,6 +37,7 @@ LXQtTaskBarPlugin::LXQtTaskBarPlugin(const ILXQtPanelPluginStartupInfo &startupI ILXQtPanelPlugin(startupInfo) { + qDebug() << "--------------> Init plugin taskbar"; mTaskBar = new LXQtTaskBar(this); } From a581c9e59290d52183cc03497cf34d2df0d18303 Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Wed, 27 Mar 2024 16:31:41 +0530 Subject: [PATCH 132/165] Check for kwin_wayland in XDG_CURRENT_DESKTOP; Add debug messages --- .../wayland/lxqttaskbarbackendwayland.cpp | 14 +++++--- .../wayland/wlroots/lxqttaskbarbackendwlr.cpp | 34 +++++-------------- .../wlroots/lxqttaskbarwlrwindowmanagment.cpp | 18 +++++++++- .../wlroots/lxqttaskbarwlrwindowmanagment.h | 6 ++++ plugin-taskbar/lxqttaskbar.cpp | 3 ++ 5 files changed, 44 insertions(+), 31 deletions(-) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index 3e23d1de5..ad523faeb 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -20,7 +20,7 @@ LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) QList desktopsList = qgetenv("XDG_CURRENT_DESKTOP").toLower().split(':'); qDebug() << "--------------> Current desktop" << desktopsList; for( QByteArray desktop: desktopsList ) { - if ( desktop == "plasma" || desktop == "kde" ) + if ( desktop == "plasma" || desktop == "kde" || desktop == "kwin_wayland" ) { qDebug() << "--------------> Using plasma backend"; m_backend = new LXQtTaskbarPlasmaBackend(); @@ -29,28 +29,28 @@ LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) else if ( desktop == "wayfire" ) { - qDebug() << "--------------> Using wlroots backend"; + qDebug() << "--------------> Using wayfire backend"; m_backend = new LXQtTaskbarWlrootsBackend(); break; } else if ( desktop == "sway" ) { - qDebug() << "--------------> Using wlroots backend"; + qDebug() << "--------------> Using sway backend"; m_backend = new LXQtTaskbarWlrootsBackend(); break; } else if ( desktop == "labwc" ) { - qDebug() << "--------------> Using wlroots backend"; + qDebug() << "--------------> Using labwc backend"; m_backend = new LXQtTaskbarWlrootsBackend(); break; } else if ( desktop == "hyprland" ) { - qDebug() << "--------------> Using wlroots backend"; + qDebug() << "--------------> Using hyprland backend"; m_backend = new LXQtTaskbarWlrootsBackend(); break; } @@ -60,6 +60,10 @@ LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) // m_backend = nullptr; } } + + if ( m_backend == nullptr ) { + qDebug() << "--------------> Using dummy backend. No window management will be done"; + } } diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp index 0eda67dfb..ccabe0c10 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp @@ -29,31 +29,9 @@ LXQtTaskbarWlrootsBackend::LXQtTaskbarWlrootsBackend(QObject *parent) : m_workspaceInfo.reset(new LXQtWlrootsWaylandWorkspaceInfo); connect(m_managment.get(), &LXQtTaskBarWlrootsWindowManagment::windowCreated, this, [this](LXQtTaskBarWlrootsWindow *window) { + qDebug() << "--------------> window created" << window->title << window->appId; addWindow(window); }); - - // connect(m_managment.get(), &LXQtTaskBarWlrootsWindowManagment::stackingOrderChanged, - // this, [this](const QString &order) { - // // stackingOrder = order.split(QLatin1Char(';')); - // // for (const auto &window : std::as_const(windows)) { - // // this->dataChanged(window.get(), StackingOrder); - // // } - // }); - - connect(m_workspaceInfo.get(), &LXQtWlrootsWaylandWorkspaceInfo::currentDesktopChanged, this, - [this](){ - int idx = m_workspaceInfo->position(m_workspaceInfo->currentDesktop()); - idx += 1; // Make 1-based - emit currentWorkspaceChanged(idx); - }); - - connect(m_workspaceInfo.get(), &LXQtWlrootsWaylandWorkspaceInfo::numberOfDesktopsChanged, - this, &ILXQtTaskbarAbstractBackend::workspacesCountChanged); - - connect(m_workspaceInfo.get(), &LXQtWlrootsWaylandWorkspaceInfo::desktopNameChanged, - this, [this](int idx) { - emit workspaceNameChanged(idx + 1); // Make 1-based - }); } bool LXQtTaskbarWlrootsBackend::supportsAction(WId, LXQtTaskBarBackendAction action) const @@ -111,6 +89,8 @@ QVector LXQtTaskbarWlrootsBackend::getCurrentWindows() const { wids << window->getWindowId(); } + + qDebug() << "--------------> current windows" << wids; return wids; } @@ -323,6 +303,12 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) return; } + /** Add the window once it's ready */ + connect(window, &LXQtTaskBarWlrootsWindow::windowReady, this, [window, this] { + qDebug() << "--------------> windowReady; adding window"; + emit windowAdded( window->getWindowId() ); + }); + auto removeWindow = [window, this] { auto it = findWindow(windows, window); @@ -453,8 +439,6 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) windows.emplace_back(window); // updateWindowAcceptance(window); } - - emit windowAdded( window->getWindowId() ); } bool LXQtTaskbarWlrootsBackend::acceptWindow(LXQtTaskBarWlrootsWindow *window) const diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp index e6ece8834..179d823d9 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp @@ -1,5 +1,6 @@ #include "lxqttaskbarwlrwindowmanagment.h" +#include #include #include #include @@ -63,6 +64,8 @@ void LXQtTaskBarWlrootsWindowManagment::zwlr_foreign_toplevel_manager_v1_topleve LXQtTaskBarWlrootsWindow::LXQtTaskBarWlrootsWindow(::zwlr_foreign_toplevel_handle_v1 *id) : zwlr_foreign_toplevel_handle_v1(id) { + title = QString::fromUtf8( "untitled" ); + appId = QString::fromUtf8( "unknown" ); } LXQtTaskBarWlrootsWindow::~LXQtTaskBarWlrootsWindow() @@ -78,14 +81,27 @@ void LXQtTaskBarWlrootsWindow::activate() void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_title(const QString &title) { this->title = title; + titleRecieved = true; emit titleChanged(); + + if ( titleRecieved && appIdRecieved ) + { + qDebug() << "--------------> windowReady!!" << getWindowId() << title << appId; + emit windowReady(); + } } void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_app_id(const QString &app_id) { this->appId = app_id; + appIdRecieved = true; emit appIdChanged(); - // Code to get the icon needs to be inserted here + + if ( appIdRecieved && titleRecieved ) + { + qDebug() << "--------------> windowReady!!" << getWindowId() << title << appId; + emit windowReady(); + } } void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_enter(struct ::wl_output *output) diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h index f4a2c6285..eb470856c 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h @@ -68,6 +68,9 @@ class LXQtTaskBarWlrootsWindow : public QObject, void closed(); void parentChanged(); + /** We wait to get the title and appId to emit this */ + void windowReady(); + protected: void zwlr_foreign_toplevel_handle_v1_title(const QString &title); void zwlr_foreign_toplevel_handle_v1_app_id(const QString &app_id); @@ -82,4 +85,7 @@ class LXQtTaskBarWlrootsWindow : public QObject, void setParentWindow(LXQtTaskBarWlrootsWindow *parent); QMetaObject::Connection parentWindowUnmappedConnection; + + mutable bool titleRecieved = false; + mutable bool appIdRecieved = false; }; diff --git a/plugin-taskbar/lxqttaskbar.cpp b/plugin-taskbar/lxqttaskbar.cpp index 43090c396..94b9a6ab9 100644 --- a/plugin-taskbar/lxqttaskbar.cpp +++ b/plugin-taskbar/lxqttaskbar.cpp @@ -251,6 +251,8 @@ void LXQtTaskBar::addWindow(WId window) // If grouping disabled group behaves like regular button const QString group_id = mGroupingEnabled ? mBackend->getWindowClass(window) : QString::number(window); + qDebug() << "-------------->" << group_id; + LXQtTaskGroup *group = nullptr; auto i_group = mKnownWindows.find(window); if (mKnownWindows.end() != i_group) @@ -345,6 +347,7 @@ void LXQtTaskBar::onWindowChanged(WId window, int prop) void LXQtTaskBar::onWindowAdded(WId window) { + qDebug() << "--------------> onWindowAdded" << window; auto const pos = mKnownWindows.find(window); if (mKnownWindows.end() == pos) addWindow(window); From 0d1463590382f691434f0aebbc0ee51b7171f3d2 Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Wed, 27 Mar 2024 16:54:01 +0530 Subject: [PATCH 133/165] Forward the signals from m_backend lxqttakcarwaylandbacked.cpp --- panel/backends/wayland/lxqttaskbarbackendwayland.cpp | 11 +++++++++++ plugin-taskbar/lxqttaskbar.cpp | 6 +++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index ad523faeb..05d5941ef 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -64,6 +64,17 @@ LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) if ( m_backend == nullptr ) { qDebug() << "--------------> Using dummy backend. No window management will be done"; } + + else { + connect(m_backend, &ILXQtTaskbarAbstractBackend::reloaded, this, &ILXQtTaskbarAbstractBackend::reloaded ); + connect(m_backend, &ILXQtTaskbarAbstractBackend::windowAdded, this, &ILXQtTaskbarAbstractBackend::windowAdded ); + connect(m_backend, &ILXQtTaskbarAbstractBackend::windowRemoved, this, &ILXQtTaskbarAbstractBackend::windowRemoved ); + connect(m_backend, &ILXQtTaskbarAbstractBackend::windowPropertyChanged, this, &ILXQtTaskbarAbstractBackend::windowPropertyChanged ); + connect(m_backend, &ILXQtTaskbarAbstractBackend::workspacesCountChanged, this, &ILXQtTaskbarAbstractBackend::workspacesCountChanged ); + connect(m_backend, &ILXQtTaskbarAbstractBackend::workspaceNameChanged, this, &ILXQtTaskbarAbstractBackend::workspaceNameChanged ); + connect(m_backend, &ILXQtTaskbarAbstractBackend::currentWorkspaceChanged, this, &ILXQtTaskbarAbstractBackend::currentWorkspaceChanged ); + connect(m_backend, &ILXQtTaskbarAbstractBackend::activeWindowChanged, this, &ILXQtTaskbarAbstractBackend::activeWindowChanged ); + } } diff --git a/plugin-taskbar/lxqttaskbar.cpp b/plugin-taskbar/lxqttaskbar.cpp index 94b9a6ab9..4d8b1bb2d 100644 --- a/plugin-taskbar/lxqttaskbar.cpp +++ b/plugin-taskbar/lxqttaskbar.cpp @@ -105,7 +105,11 @@ LXQtTaskBar::LXQtTaskBar(ILXQtPanelPlugin *plugin, QWidget *parent) : QTimer::singleShot(0, this, &LXQtTaskBar::registerShortcuts); connect(mBackend, &ILXQtTaskbarAbstractBackend::windowPropertyChanged, this, &LXQtTaskBar::onWindowChanged); - connect(mBackend, &ILXQtTaskbarAbstractBackend::windowAdded, this, &LXQtTaskBar::onWindowAdded); + // connect(mBackend, &ILXQtTaskbarAbstractBackend::windowAdded, this, &LXQtTaskBar::onWindowAdded); + qDebug() << connect(mBackend, &ILXQtTaskbarAbstractBackend::windowAdded, [=](WId window){ + qDebug() << "--------------> onWindowAdded" << window; + onWindowAdded(window); + }); connect(mBackend, &ILXQtTaskbarAbstractBackend::windowRemoved, this, &LXQtTaskBar::onWindowRemoved); // Consider already fetched windows From d517f9bbf4a05b29b8224bb4878b8ea84ecc5352 Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Wed, 27 Mar 2024 17:01:56 +0530 Subject: [PATCH 134/165] Get application icons using XdgDesktopFile --- panel/backends/wayland/wlroots/CMakeLists.txt | 3 + panel/backends/wayland/wlroots/icontools.cpp | 150 ++++++++++++++++++ panel/backends/wayland/wlroots/icontools.hpp | 6 + .../wlroots/lxqttaskbarwlrwindowmanagment.cpp | 5 + plugin-taskbar/lxqttaskbar.cpp | 9 +- 5 files changed, 165 insertions(+), 8 deletions(-) create mode 100644 panel/backends/wayland/wlroots/icontools.cpp create mode 100644 panel/backends/wayland/wlroots/icontools.hpp diff --git a/panel/backends/wayland/wlroots/CMakeLists.txt b/panel/backends/wayland/wlroots/CMakeLists.txt index 8f6f1a59b..1b4a511f8 100644 --- a/panel/backends/wayland/wlroots/CMakeLists.txt +++ b/panel/backends/wayland/wlroots/CMakeLists.txt @@ -8,10 +8,12 @@ add_library(lxqt-panel-backend-wlroots STATIC lxqttaskbarbackendwlr.h lxqttaskbarwlrwindowmanagment.h lxqtwlrvirtualdesktop.h + icontools.hpp lxqttaskbarbackendwlr.cpp lxqttaskbarwlrwindowmanagment.cpp lxqtwlrvirtualdesktop.cpp + icontools.cpp ) qt6_generate_wayland_protocol_client_sources(lxqt-panel-backend-wlroots @@ -23,5 +25,6 @@ target_link_libraries(lxqt-panel-backend-wlroots Qt6::GuiPrivate Qt6::WaylandClient Qt6::Concurrent + Qt6Xdg lxqt-panel-backend-common ) diff --git a/panel/backends/wayland/wlroots/icontools.cpp b/panel/backends/wayland/wlroots/icontools.cpp new file mode 100644 index 000000000..d77165d68 --- /dev/null +++ b/panel/backends/wayland/wlroots/icontools.cpp @@ -0,0 +1,150 @@ +#include + +#include "icontools.hpp" + +static inline QString getPixmapIcon( QString name ) { + QStringList paths{ + QString::fromUtf8( "/usr/local/share/pixmaps/" ), + QString::fromUtf8( "/usr/share/pixmaps/" ), + }; + + QStringList sfxs{ + QString::fromUtf8( ".svg" ), QString::fromUtf8( ".png" ), QString::fromUtf8( ".xpm" ) + }; + + for ( QString path: paths ) { + for ( QString sfx: sfxs ) { + if ( QFile::exists( path + name + sfx ) ) { + return path + name + sfx; + } + } + } + + return QString(); +} + + +QIcon getIconForAppId( QString mAppId ) { + if ( mAppId.isEmpty() or (mAppId == QString::fromUtf8("Unknown")) ) { + return QIcon(); + } + + /** Wine apps */ + if ( mAppId.endsWith( QString::fromUtf8(".exe") ) ) { + return QIcon::fromTheme( QString::fromUtf8("wine") ); + } + + /** Check if a theme icon exists called @mAppId */ + if ( QIcon::hasThemeIcon( mAppId ) ) { + return QIcon::fromTheme( mAppId ); + } + + /** Check if the theme icon is @mAppId, but all lower-case letters */ + else if ( QIcon::hasThemeIcon( mAppId.toLower() ) ) { + return QIcon::fromTheme( mAppId.toLower() ); + } + + QStringList appDirs = { + QDir::home().filePath( QString::fromUtf8( ".local/share/applications/" ) ), + QString::fromUtf8( "/usr/local/share/applications/" ), + QString::fromUtf8( "/usr/share/applications/" ), + QString::fromUtf8( "/usr/local/share/games/" ), + QString::fromUtf8( "/usr/share/games/" ), + }; + + /** + * Assume mAppId == desktop-file-name (ideal situation) + * or mAppId.toLower() == desktop-file-name (cheap fallback) + */ + QString iconName; + + for ( QString path: appDirs ) { + /** Get the icon name from desktop (mAppId: as it is) */ + if ( QFile::exists( path + mAppId + QString::fromUtf8(".desktop") ) ) { + QSettings desktop( path + mAppId + QString::fromUtf8(".desktop"), QSettings::IniFormat ); + iconName = desktop.value( QString::fromUtf8( "Desktop Entry/Icon" ) ).toString(); + } + + /** Get the icon name from desktop (mAppId: all lower-case letters) */ + else if ( QFile::exists( path + mAppId.toLower() + QString::fromUtf8(".desktop") ) ) { + QSettings desktop( path + mAppId.toLower() + QString::fromUtf8(".desktop"), QSettings::IniFormat ); + iconName = desktop.value( QString::fromUtf8("Desktop Entry/Icon") ).toString(); + } + + /** No icon specified: try else-where */ + if ( iconName.isEmpty() ) { + continue; + } + + /** We got an iconName, and it's in the current theme */ + if ( QIcon::hasThemeIcon( iconName ) ) { + return QIcon::fromTheme( iconName ); + } + + /** Not a theme icon, but an absolute path */ + else if ( QFile::exists( iconName ) ) { + return QIcon( iconName ); + } + + /** Not theme icon or absolute path. So check /usr/share/pixmaps/ */ + else { + iconName = getPixmapIcon( iconName ); + + if ( not iconName.isEmpty() ) { + return QIcon( iconName ); + } + } + } + + /* Check all desktop files for @mAppId */ + for ( QString path: appDirs ) { + QStringList desktops = QDir( path ).entryList( { QString::fromUtf8( "*.desktop" ) } ); + for ( QString dskf: desktops ) { + QSettings desktop( path + dskf, QSettings::IniFormat ); + + QString exec = desktop.value( QString::fromUtf8("Desktop Entry/Exec"), QString::fromUtf8("abcd1234/-") ).toString(); + QString name = desktop.value( QString::fromUtf8("Desktop Entry/Name"), QString::fromUtf8("abcd1234/-") ).toString(); + QString cls = desktop.value( QString::fromUtf8("Desktop Entry/StartupWMClass"), QString::fromUtf8("abcd1234/-") ).toString(); + + QString execPath( QString::fromUtf8( std::filesystem::path( exec.toStdString() ).filename().c_str() ) ); + + if ( mAppId.compare( execPath, Qt::CaseInsensitive ) == 0 ) { + iconName = desktop.value( QString::fromUtf8("Desktop Entry/Icon") ).toString(); + } + + else if ( mAppId.compare( name, Qt::CaseInsensitive ) == 0 ) { + iconName = desktop.value( QString::fromUtf8("Desktop Entry/Icon") ).toString(); + } + + else if ( mAppId.compare( cls, Qt::CaseInsensitive ) == 0 ) { + iconName = desktop.value( QString::fromUtf8("Desktop Entry/Icon") ).toString(); + } + + if ( not iconName.isEmpty() ) { + if ( QIcon::hasThemeIcon( iconName ) ) { + return QIcon::fromTheme( iconName ); + } + + else if ( QFile::exists( iconName ) ) { + return QIcon( iconName ); + } + + else { + iconName = getPixmapIcon( iconName ); + + if ( not iconName.isEmpty() ) { + return QIcon( iconName ); + } + } + } + } + } + + iconName = getPixmapIcon( iconName ); + + if ( not iconName.isEmpty() ) { + return QIcon( iconName ); + } + + return QIcon(); +} diff --git a/panel/backends/wayland/wlroots/icontools.hpp b/panel/backends/wayland/wlroots/icontools.hpp new file mode 100644 index 000000000..a119bb14a --- /dev/null +++ b/panel/backends/wayland/wlroots/icontools.hpp @@ -0,0 +1,6 @@ +#pragma once + +#include +#include + +QIcon getIconForAppId( QString ); diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp index 179d823d9..c4c073424 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp @@ -1,4 +1,5 @@ #include "lxqttaskbarwlrwindowmanagment.h" +#include "icontools.hpp" #include #include @@ -11,6 +12,8 @@ #include #include +#include + #include #include @@ -97,6 +100,8 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_app_id(const QStr appIdRecieved = true; emit appIdChanged(); + this->icon = getIconForAppId( app_id ); + if ( appIdRecieved && titleRecieved ) { qDebug() << "--------------> windowReady!!" << getWindowId() << title << appId; diff --git a/plugin-taskbar/lxqttaskbar.cpp b/plugin-taskbar/lxqttaskbar.cpp index 4d8b1bb2d..43090c396 100644 --- a/plugin-taskbar/lxqttaskbar.cpp +++ b/plugin-taskbar/lxqttaskbar.cpp @@ -105,11 +105,7 @@ LXQtTaskBar::LXQtTaskBar(ILXQtPanelPlugin *plugin, QWidget *parent) : QTimer::singleShot(0, this, &LXQtTaskBar::registerShortcuts); connect(mBackend, &ILXQtTaskbarAbstractBackend::windowPropertyChanged, this, &LXQtTaskBar::onWindowChanged); - // connect(mBackend, &ILXQtTaskbarAbstractBackend::windowAdded, this, &LXQtTaskBar::onWindowAdded); - qDebug() << connect(mBackend, &ILXQtTaskbarAbstractBackend::windowAdded, [=](WId window){ - qDebug() << "--------------> onWindowAdded" << window; - onWindowAdded(window); - }); + connect(mBackend, &ILXQtTaskbarAbstractBackend::windowAdded, this, &LXQtTaskBar::onWindowAdded); connect(mBackend, &ILXQtTaskbarAbstractBackend::windowRemoved, this, &LXQtTaskBar::onWindowRemoved); // Consider already fetched windows @@ -255,8 +251,6 @@ void LXQtTaskBar::addWindow(WId window) // If grouping disabled group behaves like regular button const QString group_id = mGroupingEnabled ? mBackend->getWindowClass(window) : QString::number(window); - qDebug() << "-------------->" << group_id; - LXQtTaskGroup *group = nullptr; auto i_group = mKnownWindows.find(window); if (mKnownWindows.end() != i_group) @@ -351,7 +345,6 @@ void LXQtTaskBar::onWindowChanged(WId window, int prop) void LXQtTaskBar::onWindowAdded(WId window) { - qDebug() << "--------------> onWindowAdded" << window; auto const pos = mKnownWindows.find(window); if (mKnownWindows.end() == pos) addWindow(window); From 4034f257e722fdae3fdbee76f0d8f76c6b867eff Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Wed, 27 Mar 2024 23:54:14 +0530 Subject: [PATCH 135/165] Correct handling of window states event; add support for setting window states --- .../wayland/wlroots/lxqttaskbarbackendwlr.cpp | 45 +++++++--- .../wlroots/lxqttaskbarwlrwindowmanagment.cpp | 86 ++++++++++++------- .../wlroots/lxqttaskbarwlrwindowmanagment.h | 17 +++- 3 files changed, 105 insertions(+), 43 deletions(-) diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp index ccabe0c10..4c42535f6 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp @@ -29,7 +29,6 @@ LXQtTaskbarWlrootsBackend::LXQtTaskbarWlrootsBackend(QObject *parent) : m_workspaceInfo.reset(new LXQtWlrootsWaylandWorkspaceInfo); connect(m_managment.get(), &LXQtTaskBarWlrootsWindowManagment::windowCreated, this, [this](LXQtTaskBarWlrootsWindow *window) { - qDebug() << "--------------> window created" << window->title << window->appId; addWindow(window); }); } @@ -90,7 +89,6 @@ QVector LXQtTaskbarWlrootsBackend::getCurrentWindows() const wids << window->getWindowId(); } - qDebug() << "--------------> current windows" << wids; return wids; } @@ -165,31 +163,59 @@ bool LXQtTaskbarWlrootsBackend::setWindowState(WId windowId, LXQtTaskBarWindowSt { case LXQtTaskBarWindowState::Minimized: { - WlrootsState = LXQtTaskBarWlrootsWindow::state::state_minimized; + if ( set ) { + window->set_minimized(); + } + + else { + window->unset_minimized(); + } + break; } case LXQtTaskBarWindowState::Maximized: case LXQtTaskBarWindowState::MaximizedVertically: case LXQtTaskBarWindowState::MaximizedHorizontally: { - WlrootsState = LXQtTaskBarWlrootsWindow::state::state_maximized; + if ( set ) { + window->set_maximized(); + } + + else { + window->unset_maximized(); + } + break; } case LXQtTaskBarWindowState::Normal: { - WlrootsState = LXQtTaskBarWlrootsWindow::state::state_maximized; - set = !set; //TODO: correct + /** Restore if maximized/minimized */ + if ( window->windowState.testFlag(LXQtTaskBarWlrootsWindow::state_minimized) ) { + window->unset_minimized(); + } + + if ( window->windowState.testFlag(LXQtTaskBarWlrootsWindow::state_maximized) ) { + window->unset_maximized(); + } break; } - case LXQtTaskBarWindowState::RolledUp: + + case LXQtTaskBarWindowState::FullScreen: { + if ( set ) { + window->set_fullscreen(nullptr); + } + + else { + window->unset_fullscreen(); + } break; } + default: return false; } - window->set_state(WlrootsState, set ? WlrootsState : 0); return true; } @@ -305,7 +331,6 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) /** Add the window once it's ready */ connect(window, &LXQtTaskBarWlrootsWindow::windowReady, this, [window, this] { - qDebug() << "--------------> windowReady; adding window"; emit windowAdded( window->getWindowId() ); }); @@ -361,7 +386,7 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) } connect(window, &LXQtTaskBarWlrootsWindow::activeChanged, this, [window, this] { - const bool active = window->windowState & LXQtTaskBarWlrootsWindow::state::state_activated; + const bool active = window->windowState.testFlag( LXQtTaskBarWlrootsWindow::state::state_activated ); LXQtTaskBarWlrootsWindow *effectiveWindow = window; diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp index c4c073424..ebfc8dc7f 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp @@ -55,7 +55,6 @@ LXQtTaskBarWlrootsWindowManagment::~LXQtTaskBarWlrootsWindowManagment() void LXQtTaskBarWlrootsWindowManagment::zwlr_foreign_toplevel_manager_v1_toplevel(struct ::zwlr_foreign_toplevel_handle_v1 *toplevel) { - qDebug() << "new toplevel created"; emit windowCreated( new LXQtTaskBarWlrootsWindow(toplevel) ); } @@ -89,7 +88,6 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_title(const QStri if ( titleRecieved && appIdRecieved ) { - qDebug() << "--------------> windowReady!!" << getWindowId() << title << appId; emit windowReady(); } } @@ -104,7 +102,6 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_app_id(const QStr if ( appIdRecieved && titleRecieved ) { - qDebug() << "--------------> windowReady!!" << getWindowId() << title << appId; emit windowReady(); } } @@ -121,39 +118,70 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_leave(stru void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_state(wl_array *state) { - QFlags wlrState; - - uint32_t* statePtr = static_cast(state->data); - for (size_t i = 0; i < state->size / sizeof(uint32_t); i++) { - switch (statePtr[i]) { - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED: - wlrState |= state_maximized; - break; - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN: - wlrState |= state_fullscreen; - break; - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED: - wlrState |= state_minimized; - break; - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED: - wlrState |= state_activated; - break; + auto *states = static_cast(state->data); + int numStates = static_cast(state->size / sizeof(uint32_t) ); + + for ( int i = 0; i < numStates; i++ ) { + switch ( (uint32_t)states[ i ] ) { + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED: { + m_pendingState.maximized = true; + break; + } + + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED: { + m_pendingState.minimized = true; + break; + } + + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED: { + m_pendingState.activated = true; + break; + } + + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN: { + m_pendingState.fullscreen = true; + break; + } } } +} - if ( windowState.testFlag( state_maximized ) ^ wlrState.testFlag( state_maximized ) ) +void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_done() +{ + /** + * Update windowState flags before emitting the signals. + * Otherwise, windowState.testFlag(...) will return wrong information!! + */ + windowState = QFlags(); + if ( m_pendingState.maximized ) windowState |= state_maximized; + if ( m_pendingState.minimized ) windowState |= state_minimized; + if ( m_pendingState.activated ) windowState |= state_activated; + if ( m_pendingState.fullscreen ) windowState |= state_fullscreen; + + /** Emit the signals. */ + if ( m_viewState.maximized != m_pendingState.maximized ) emit maximizedChanged(); - if ( windowState.testFlag( state_minimized ) ^ wlrState.testFlag( state_minimized ) ) + + if ( m_viewState.minimized != m_pendingState.minimized ) emit minimizedChanged(); - if ( windowState.testFlag( state_fullscreen ) ^ wlrState.testFlag( state_fullscreen ) ) - emit fullscreenChanged(); - if ( windowState.testFlag( state_activated ) ^ wlrState.testFlag( state_activated ) ) + + if ( m_viewState.activated!= m_pendingState.activated ) emit activeChanged(); -} -void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_done() -{ - emit done(); + if ( m_viewState.fullscreen != m_pendingState.fullscreen ) + emit fullscreenChanged(); + + /** Store m_pendingState into m_viewState for the next run */ + m_viewState.maximized = m_pendingState.maximized; + m_viewState.minimized = m_pendingState.minimized; + m_viewState.activated = m_pendingState.activated; + m_viewState.fullscreen = m_pendingState.fullscreen; + + /** Reset m_pendingState for the next run */ + m_pendingState.maximized = false; + m_pendingState.minimized = false; + m_pendingState.activated = false; + m_pendingState.fullscreen = false; } void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_closed() diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h index eb470856c..bc3c2a0b3 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h @@ -53,8 +53,6 @@ class LXQtTaskBarWlrootsWindow : public QObject, QFlags windowState; QPointer parentWindow; - void set_state( LXQtTaskBarWlrootsWindow::state, bool ) {}; - Q_SIGNALS: void titleChanged(); void appIdChanged(); @@ -86,6 +84,17 @@ class LXQtTaskBarWlrootsWindow : public QObject, QMetaObject::Connection parentWindowUnmappedConnection; - mutable bool titleRecieved = false; - mutable bool appIdRecieved = false; + typedef struct view_state_t + { + bool maximized = false; + bool minimized = false; + bool activated = false; + bool fullscreen = false; + } ViewState; + + ViewState m_viewState; + ViewState m_pendingState; + + bool titleRecieved = false; + bool appIdRecieved = false; }; From 5cf1d8a6c32e04b97406b40d00f7b5741eae246e Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Thu, 28 Mar 2024 00:32:03 +0530 Subject: [PATCH 136/165] Fix various warnings --- .../wayland/wlroots/lxqttaskbarbackendwlr.cpp | 32 ++++++++----------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp index 4c42535f6..a40a29ebf 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp @@ -37,21 +37,12 @@ bool LXQtTaskbarWlrootsBackend::supportsAction(WId, LXQtTaskBarBackendAction act { switch (action) { - case LXQtTaskBarBackendAction::Move: - return false; - - case LXQtTaskBarBackendAction::Resize: - return false; - case LXQtTaskBarBackendAction::Maximize: return true; case LXQtTaskBarBackendAction::Minimize: return true; - case LXQtTaskBarBackendAction::RollUp: - return false; - case LXQtTaskBarBackendAction::FullScreen: return true; @@ -132,6 +123,7 @@ LXQtTaskBarWindowLayer LXQtTaskbarWlrootsBackend::getWindowLayer(WId) const bool LXQtTaskbarWlrootsBackend::setWindowLayer(WId, LXQtTaskBarWindowLayer) { + return false; } LXQtTaskBarWindowState LXQtTaskbarWlrootsBackend::getWindowState(WId windowId) const @@ -158,7 +150,6 @@ bool LXQtTaskbarWlrootsBackend::setWindowState(WId windowId, LXQtTaskBarWindowSt if(!window) return false; - LXQtTaskBarWlrootsWindow::state WlrootsState; switch (state) { case LXQtTaskBarWindowState::Minimized: @@ -277,40 +268,42 @@ bool LXQtTaskbarWlrootsBackend::setCurrentWorkspace(int) return false; } -int LXQtTaskbarWlrootsBackend::getWindowWorkspace(WId windowId) const +int LXQtTaskbarWlrootsBackend::getWindowWorkspace(WId) const { return 1; } bool LXQtTaskbarWlrootsBackend::setWindowOnWorkspace(WId, int) { + return false; } -void LXQtTaskbarWlrootsBackend::moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) +void LXQtTaskbarWlrootsBackend::moveApplicationToPrevNextMonitor(WId, bool, bool) { } -bool LXQtTaskbarWlrootsBackend::isWindowOnScreen(QScreen *screen, WId windowId) const +bool LXQtTaskbarWlrootsBackend::isWindowOnScreen(QScreen *, WId) const { - // Manage based on output-enter/output-leave + // TODO: Manage based on output-enter/output-leave return true; } -void LXQtTaskbarWlrootsBackend::moveApplication(WId windowId) +void LXQtTaskbarWlrootsBackend::moveApplication(WId) { } -void LXQtTaskbarWlrootsBackend::resizeApplication(WId windowId) +void LXQtTaskbarWlrootsBackend::resizeApplication(WId) { } -void LXQtTaskbarWlrootsBackend::refreshIconGeometry(WId windowId, const QRect &geom) +void LXQtTaskbarWlrootsBackend::refreshIconGeometry(WId, const QRect &) { } -bool LXQtTaskbarWlrootsBackend::isAreaOverlapped(const QRect &area) const +bool LXQtTaskbarWlrootsBackend::isAreaOverlapped(const QRect &) const { + return false; } bool LXQtTaskbarWlrootsBackend::isShowingDesktop() const @@ -318,8 +311,9 @@ bool LXQtTaskbarWlrootsBackend::isShowingDesktop() const return m_managment->isShowingDesktop(); } -bool LXQtTaskbarWlrootsBackend::showDesktop(bool value) +bool LXQtTaskbarWlrootsBackend::showDesktop(bool) { + return false; } void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) From 4d696cb149fa50b697ee65fef6c295acfa56a465 Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Thu, 28 Mar 2024 00:36:57 +0530 Subject: [PATCH 137/165] Add LXQtTaskBarBackendAction::SetLayer to disable 'Layer' in context-menu --- panel/backends/lxqttaskbartypes.h | 3 ++- plugin-taskbar/lxqttaskbutton.cpp | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/panel/backends/lxqttaskbartypes.h b/panel/backends/lxqttaskbartypes.h index ae3d5f107..dff38db0b 100644 --- a/panel/backends/lxqttaskbartypes.h +++ b/panel/backends/lxqttaskbartypes.h @@ -14,7 +14,8 @@ enum class LXQtTaskBarBackendAction MaximizeHorizontally, Minimize, RollUp, - FullScreen + FullScreen, + SetLayer }; enum class LXQtTaskBarWindowProperty diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 45e836893..851f765bc 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -690,6 +690,7 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) menu->addSeparator(); QMenu* layerMenu = menu->addMenu(tr("&Layer")); + layerMenu->setEnabled(mBackend->supportsAction(mWindow, LXQtTaskBarBackendAction::SetLayer)); LXQtTaskBarWindowLayer currentLayer = mBackend->getWindowLayer(mWindow); From d4d014e226d9c70a5739f3c5345400e90d480dfe Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Thu, 28 Mar 2024 12:56:30 +0530 Subject: [PATCH 138/165] Use XdgIcon::fromTheme(...) instead of own implementation --- panel/backends/wayland/wlroots/CMakeLists.txt | 2 - panel/backends/wayland/wlroots/icontools.cpp | 150 ------------------ panel/backends/wayland/wlroots/icontools.hpp | 6 - .../wlroots/lxqttaskbarwlrwindowmanagment.cpp | 9 +- 4 files changed, 6 insertions(+), 161 deletions(-) delete mode 100644 panel/backends/wayland/wlroots/icontools.cpp delete mode 100644 panel/backends/wayland/wlroots/icontools.hpp diff --git a/panel/backends/wayland/wlroots/CMakeLists.txt b/panel/backends/wayland/wlroots/CMakeLists.txt index 1b4a511f8..9fe10d647 100644 --- a/panel/backends/wayland/wlroots/CMakeLists.txt +++ b/panel/backends/wayland/wlroots/CMakeLists.txt @@ -8,12 +8,10 @@ add_library(lxqt-panel-backend-wlroots STATIC lxqttaskbarbackendwlr.h lxqttaskbarwlrwindowmanagment.h lxqtwlrvirtualdesktop.h - icontools.hpp lxqttaskbarbackendwlr.cpp lxqttaskbarwlrwindowmanagment.cpp lxqtwlrvirtualdesktop.cpp - icontools.cpp ) qt6_generate_wayland_protocol_client_sources(lxqt-panel-backend-wlroots diff --git a/panel/backends/wayland/wlroots/icontools.cpp b/panel/backends/wayland/wlroots/icontools.cpp deleted file mode 100644 index d77165d68..000000000 --- a/panel/backends/wayland/wlroots/icontools.cpp +++ /dev/null @@ -1,150 +0,0 @@ -#include - -#include "icontools.hpp" - -static inline QString getPixmapIcon( QString name ) { - QStringList paths{ - QString::fromUtf8( "/usr/local/share/pixmaps/" ), - QString::fromUtf8( "/usr/share/pixmaps/" ), - }; - - QStringList sfxs{ - QString::fromUtf8( ".svg" ), QString::fromUtf8( ".png" ), QString::fromUtf8( ".xpm" ) - }; - - for ( QString path: paths ) { - for ( QString sfx: sfxs ) { - if ( QFile::exists( path + name + sfx ) ) { - return path + name + sfx; - } - } - } - - return QString(); -} - - -QIcon getIconForAppId( QString mAppId ) { - if ( mAppId.isEmpty() or (mAppId == QString::fromUtf8("Unknown")) ) { - return QIcon(); - } - - /** Wine apps */ - if ( mAppId.endsWith( QString::fromUtf8(".exe") ) ) { - return QIcon::fromTheme( QString::fromUtf8("wine") ); - } - - /** Check if a theme icon exists called @mAppId */ - if ( QIcon::hasThemeIcon( mAppId ) ) { - return QIcon::fromTheme( mAppId ); - } - - /** Check if the theme icon is @mAppId, but all lower-case letters */ - else if ( QIcon::hasThemeIcon( mAppId.toLower() ) ) { - return QIcon::fromTheme( mAppId.toLower() ); - } - - QStringList appDirs = { - QDir::home().filePath( QString::fromUtf8( ".local/share/applications/" ) ), - QString::fromUtf8( "/usr/local/share/applications/" ), - QString::fromUtf8( "/usr/share/applications/" ), - QString::fromUtf8( "/usr/local/share/games/" ), - QString::fromUtf8( "/usr/share/games/" ), - }; - - /** - * Assume mAppId == desktop-file-name (ideal situation) - * or mAppId.toLower() == desktop-file-name (cheap fallback) - */ - QString iconName; - - for ( QString path: appDirs ) { - /** Get the icon name from desktop (mAppId: as it is) */ - if ( QFile::exists( path + mAppId + QString::fromUtf8(".desktop") ) ) { - QSettings desktop( path + mAppId + QString::fromUtf8(".desktop"), QSettings::IniFormat ); - iconName = desktop.value( QString::fromUtf8( "Desktop Entry/Icon" ) ).toString(); - } - - /** Get the icon name from desktop (mAppId: all lower-case letters) */ - else if ( QFile::exists( path + mAppId.toLower() + QString::fromUtf8(".desktop") ) ) { - QSettings desktop( path + mAppId.toLower() + QString::fromUtf8(".desktop"), QSettings::IniFormat ); - iconName = desktop.value( QString::fromUtf8("Desktop Entry/Icon") ).toString(); - } - - /** No icon specified: try else-where */ - if ( iconName.isEmpty() ) { - continue; - } - - /** We got an iconName, and it's in the current theme */ - if ( QIcon::hasThemeIcon( iconName ) ) { - return QIcon::fromTheme( iconName ); - } - - /** Not a theme icon, but an absolute path */ - else if ( QFile::exists( iconName ) ) { - return QIcon( iconName ); - } - - /** Not theme icon or absolute path. So check /usr/share/pixmaps/ */ - else { - iconName = getPixmapIcon( iconName ); - - if ( not iconName.isEmpty() ) { - return QIcon( iconName ); - } - } - } - - /* Check all desktop files for @mAppId */ - for ( QString path: appDirs ) { - QStringList desktops = QDir( path ).entryList( { QString::fromUtf8( "*.desktop" ) } ); - for ( QString dskf: desktops ) { - QSettings desktop( path + dskf, QSettings::IniFormat ); - - QString exec = desktop.value( QString::fromUtf8("Desktop Entry/Exec"), QString::fromUtf8("abcd1234/-") ).toString(); - QString name = desktop.value( QString::fromUtf8("Desktop Entry/Name"), QString::fromUtf8("abcd1234/-") ).toString(); - QString cls = desktop.value( QString::fromUtf8("Desktop Entry/StartupWMClass"), QString::fromUtf8("abcd1234/-") ).toString(); - - QString execPath( QString::fromUtf8( std::filesystem::path( exec.toStdString() ).filename().c_str() ) ); - - if ( mAppId.compare( execPath, Qt::CaseInsensitive ) == 0 ) { - iconName = desktop.value( QString::fromUtf8("Desktop Entry/Icon") ).toString(); - } - - else if ( mAppId.compare( name, Qt::CaseInsensitive ) == 0 ) { - iconName = desktop.value( QString::fromUtf8("Desktop Entry/Icon") ).toString(); - } - - else if ( mAppId.compare( cls, Qt::CaseInsensitive ) == 0 ) { - iconName = desktop.value( QString::fromUtf8("Desktop Entry/Icon") ).toString(); - } - - if ( not iconName.isEmpty() ) { - if ( QIcon::hasThemeIcon( iconName ) ) { - return QIcon::fromTheme( iconName ); - } - - else if ( QFile::exists( iconName ) ) { - return QIcon( iconName ); - } - - else { - iconName = getPixmapIcon( iconName ); - - if ( not iconName.isEmpty() ) { - return QIcon( iconName ); - } - } - } - } - } - - iconName = getPixmapIcon( iconName ); - - if ( not iconName.isEmpty() ) { - return QIcon( iconName ); - } - - return QIcon(); -} diff --git a/panel/backends/wayland/wlroots/icontools.hpp b/panel/backends/wayland/wlroots/icontools.hpp deleted file mode 100644 index a119bb14a..000000000 --- a/panel/backends/wayland/wlroots/icontools.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include -#include - -QIcon getIconForAppId( QString ); diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp index ebfc8dc7f..6fdba648e 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp @@ -1,5 +1,4 @@ #include "lxqttaskbarwlrwindowmanagment.h" -#include "icontools.hpp" #include #include @@ -12,7 +11,7 @@ #include #include -#include +#include #include @@ -98,7 +97,11 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_app_id(const QStr appIdRecieved = true; emit appIdChanged(); - this->icon = getIconForAppId( app_id ); + this->icon = XdgIcon::fromTheme(appId); + if ( this->icon.pixmap(64).width() == 0 ) + { + this->icon = XdgIcon::fromTheme(appId.toLower()); + } if ( appIdRecieved && titleRecieved ) { From 1163a85879d25b08ddffc818dd6e804ae8307916 Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Fri, 29 Mar 2024 16:35:55 +0530 Subject: [PATCH 139/165] Refactoring of handling of zwlr_foreign_toplevel_handle_v1 events --- .../wayland/wlroots/lxqttaskbarbackendwlr.cpp | 117 +++--- .../wayland/wlroots/lxqttaskbarbackendwlr.h | 2 +- .../wlroots/lxqttaskbarwlrwindowmanagment.cpp | 348 +++++++++++++----- .../wlroots/lxqttaskbarwlrwindowmanagment.h | 86 +++-- 4 files changed, 381 insertions(+), 172 deletions(-) diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp index a40a29ebf..5f26ebb2e 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp @@ -6,20 +6,29 @@ #include #include #include +#include -auto findWindow(const std::vector>& windows, LXQtTaskBarWlrootsWindow *window) -{ - //TODO: use algorithms - auto end = windows.end(); - for(auto it = windows.begin(); it != end; it++) - { - if((*it).get() == window) - { - return it; - } +LXQtTaskBarWlrootsWindow* findWindow(const std::vector windows, LXQtTaskBarWlrootsWindow* window) { + // Use std::find to locate the window pointer within the vector + auto it = std::find(windows.begin(), windows.end(), window); + + // Check if the window was found (iterator != end of vector) + if (it != windows.end()) { + // Return the pointer to the found window + return *it; } + // Window not found, return nullptr + return nullptr; +} - return windows.end(); +void eraseWindow(std::vector& windows, LXQtTaskBarWlrootsWindow* window) { + // Use std::remove to find the element to erase + auto it = std::remove(windows.begin(), windows.end(), window); + + // If the element was found, erase it from the vector + if (it != windows.end()) { + windows.erase(it, windows.end()); + } } LXQtTaskbarWlrootsBackend::LXQtTaskbarWlrootsBackend(QObject *parent) : @@ -75,7 +84,7 @@ QVector LXQtTaskbarWlrootsBackend::getCurrentWindows() const QVector wids; wids.reserve(wids.size()); - for(const std::unique_ptr& window : std::as_const(windows)) + for(LXQtTaskBarWlrootsWindow * window : windows) { wids << window->getWindowId(); } @@ -132,13 +141,13 @@ LXQtTaskBarWindowState LXQtTaskbarWlrootsBackend::getWindowState(WId windowId) c if(!window) return LXQtTaskBarWindowState::Normal; - if(window->windowState.testFlag(LXQtTaskBarWlrootsWindow::state::state_minimized)) - return LXQtTaskBarWindowState::Hidden; + if(window->windowState.minimized) + return LXQtTaskBarWindowState::Minimized; - if(window->windowState.testFlag(LXQtTaskBarWlrootsWindow::state::state_maximized)) + if(window->windowState.maximized) return LXQtTaskBarWindowState::Maximized; - if(window->windowState.testFlag(LXQtTaskBarWlrootsWindow::state::state_fullscreen)) + if(window->windowState.fullscreen) return LXQtTaskBarWindowState::FullScreen; return LXQtTaskBarWindowState::Normal; @@ -181,11 +190,11 @@ bool LXQtTaskbarWlrootsBackend::setWindowState(WId windowId, LXQtTaskBarWindowSt case LXQtTaskBarWindowState::Normal: { /** Restore if maximized/minimized */ - if ( window->windowState.testFlag(LXQtTaskBarWlrootsWindow::state_minimized) ) { + if ( window->windowState.minimized) { window->unset_minimized(); } - if ( window->windowState.testFlag(LXQtTaskBarWlrootsWindow::state_maximized) ) { + if ( window->windowState.maximized) { window->unset_maximized(); } break; @@ -216,7 +225,7 @@ bool LXQtTaskbarWlrootsBackend::isWindowActive(WId windowId) const if(!window) return false; - return activeWindow == window || window->windowState.testFlag(LXQtTaskBarWlrootsWindow::state::state_activated); + return activeWindow == window || window->windowState.activated; } bool LXQtTaskbarWlrootsBackend::raiseWindow(WId windowId, bool onCurrentWorkSpace) @@ -318,37 +327,15 @@ bool LXQtTaskbarWlrootsBackend::showDesktop(bool) void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) { - if (findWindow(windows, window) != windows.end() || transients.contains(window)) + if (findWindow(windows, window) != nullptr || transients.contains(window)) { return; } - /** Add the window once it's ready */ - connect(window, &LXQtTaskBarWlrootsWindow::windowReady, this, [window, this] { - emit windowAdded( window->getWindowId() ); - }); - auto removeWindow = [window, this] { - auto it = findWindow(windows, window); - if (it != windows.end()) - { - windows.erase(it); - transientsDemandingAttention.remove(window); - lastActivated.remove(window); - } - else - { - // Could be a transient. - // Removing a transient might change the demands attention state of the leader. - if (transients.remove(window)) - { - if (LXQtTaskBarWlrootsWindow *leader = transientsDemandingAttention.key(window)) { - transientsDemandingAttention.remove(leader, window); - emit windowPropertyChanged(leader->getWindowId(), int(LXQtTaskBarWindowProperty::Urgency)); - } - } - } + eraseWindow(windows, window); + lastActivated.remove(window); if (activeWindow == window) { @@ -369,7 +356,7 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::WindowClass)); }); - if (window->windowState & LXQtTaskBarWlrootsWindow::state::state_activated) { + if (window->windowState.activated) { LXQtTaskBarWlrootsWindow *effectiveActive = window; while (effectiveActive->parentWindow) { effectiveActive = effectiveActive->parentWindow; @@ -379,9 +366,7 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) activeWindow = effectiveActive; } - connect(window, &LXQtTaskBarWlrootsWindow::activeChanged, this, [window, this] { - const bool active = window->windowState.testFlag( LXQtTaskBarWlrootsWindow::state::state_activated ); - + connect(window, &LXQtTaskBarWlrootsWindow::activatedChanged, this, [window, this] { LXQtTaskBarWlrootsWindow *effectiveWindow = window; while (effectiveWindow->parentWindow) @@ -389,7 +374,7 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) effectiveWindow = effectiveWindow->parentWindow; } - if (active) + if (window->windowState.activated) { lastActivated[effectiveWindow] = QTime::currentTime(); @@ -422,24 +407,20 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) else { // lost a leader, add to regular windows list. - Q_ASSERT(findWindow(windows, window) == windows.end()); + Q_ASSERT(findWindow(windows, window) == nullptr); windows.emplace_back(window); } } else if (leader) { - // gained a leader, remove from regular windows list. - auto it = findWindow(windows, window); - Q_ASSERT(it != windows.end()); - - windows.erase(it); + eraseWindow(windows, window); lastActivated.remove(window); } }); auto stateChanged = [window, this] { - // updateWindowAcceptance(window); + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::State)); }; connect(window, &LXQtTaskBarWlrootsWindow::fullscreenChanged, this, stateChanged); @@ -455,9 +436,16 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) } else { - windows.emplace_back(window); - // updateWindowAcceptance(window); + windows.push_back(window); } + + qDebug() << window << window->getWindowId(); + emit windowAdded( window->getWindowId() ); + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Title)); + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Icon)); + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::State)); + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Geometry)); + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::WindowClass)); } bool LXQtTaskbarWlrootsBackend::acceptWindow(LXQtTaskBarWlrootsWindow *window) const @@ -470,10 +458,19 @@ bool LXQtTaskbarWlrootsBackend::acceptWindow(LXQtTaskBarWlrootsWindow *window) c LXQtTaskBarWlrootsWindow *LXQtTaskbarWlrootsBackend::getWindow(WId windowId) const { + /** Easiest way is to convert the quintptr to the actual pointer */ + LXQtTaskBarWlrootsWindow *win = reinterpret_cast( windowId ); + if ( win ) { + qDebug() << "get-window-windowId"; + return win; + } + + /** Fallback attempt */ for(auto &window : std::as_const(windows)) { - if(window->getWindowId() == windowId) - return window.get(); + if(window->getWindowId() == windowId) { + return window; + } } return nullptr; diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h index 61ed5d81a..ef0c67afa 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h @@ -83,7 +83,7 @@ class LXQtTaskbarWlrootsBackend : public ILXQtTaskbarAbstractBackend QHash lastActivated; LXQtTaskBarWlrootsWindow *activeWindow = nullptr; - std::vector> windows; + std::vector windows; // key=transient child, value=leader QHash transients; // key=leader, values=transient children diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp index 6fdba648e..c9fbb6941 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp @@ -19,42 +19,61 @@ #include #include -wl_seat *get_seat() { +static inline wl_seat *get_seat() +{ QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface(); - if ( !native ) { + if (!native) + { return nullptr; } - struct wl_seat *seat = reinterpret_cast(native->nativeResourceForIntegration( "wl_seat" ) ); + struct wl_seat *seat = reinterpret_cast(native->nativeResourceForIntegration("wl_seat")); return seat; } + /* * LXQtTaskBarWlrootsWindowManagment */ -LXQtTaskBarWlrootsWindowManagment::LXQtTaskBarWlrootsWindowManagment() - : QWaylandClientExtensionTemplate(version) +LXQtTaskBarWlrootsWindowManagment::LXQtTaskBarWlrootsWindowManagment() : QWaylandClientExtensionTemplate(version) { - connect(this, &QWaylandClientExtension::activeChanged, this, [this] { - if (!isActive()) { + /** Automatically destroy thie object */ + connect( + this, &QWaylandClientExtension::activeChanged, this, [ this ] { + if (!isActive()) + { zwlr_foreign_toplevel_manager_v1_destroy(object()); } }); } + LXQtTaskBarWlrootsWindowManagment::~LXQtTaskBarWlrootsWindowManagment() { - if (isActive()) { + if (isActive()) + { zwlr_foreign_toplevel_manager_v1_destroy(object()); } } + void LXQtTaskBarWlrootsWindowManagment::zwlr_foreign_toplevel_manager_v1_toplevel(struct ::zwlr_foreign_toplevel_handle_v1 *toplevel) { - emit windowCreated( new LXQtTaskBarWlrootsWindow(toplevel) ); + /** + * A window was created. + * Wait for the window to become ready, i.e. wait for done() event to be sent by the compositor. + * Once we recieve done(), emit the windowReady() signal. + */ + + auto w = new LXQtTaskBarWlrootsWindow(toplevel); + + connect(w, &LXQtTaskBarWlrootsWindow::windowReady, [w, this] () { + qDebug() << "------------> a window was created" << w << w->getWindowId() << w->appId << w->title; + emit windowCreated(w); + }); } @@ -62,157 +81,312 @@ void LXQtTaskBarWlrootsWindowManagment::zwlr_foreign_toplevel_manager_v1_topleve * LXQtTaskBarWlrootsWindow */ -LXQtTaskBarWlrootsWindow::LXQtTaskBarWlrootsWindow(::zwlr_foreign_toplevel_handle_v1 *id) - : zwlr_foreign_toplevel_handle_v1(id) +LXQtTaskBarWlrootsWindow::LXQtTaskBarWlrootsWindow(::zwlr_foreign_toplevel_handle_v1 *id) : zwlr_foreign_toplevel_handle_v1(id) { - title = QString::fromUtf8( "untitled" ); - appId = QString::fromUtf8( "unknown" ); + /** Set a default non-null title and appId */ + title = QString::fromUtf8("untitled"); + appId = QString::fromUtf8("unknown"); } + LXQtTaskBarWlrootsWindow::~LXQtTaskBarWlrootsWindow() { destroy(); } + void LXQtTaskBarWlrootsWindow::activate() { + /** + * Activate on default seat. + * TODO: Worry about multi-seat setups, when we have no other worries :P + */ zwlr_foreign_toplevel_handle_v1::activate(get_seat()); } -void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_title(const QString &title) -{ - this->title = title; - titleRecieved = true; - emit titleChanged(); - if ( titleRecieved && appIdRecieved ) - { - emit windowReady(); - } +void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_title(const QString& title) +{ + /** Store the incoming title in pending */ + m_pendingState.title = title; + m_pendingState.titleChanged = true; } -void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_app_id(const QString &app_id) + +void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_app_id(const QString& app_id) { - this->appId = app_id; - appIdRecieved = true; - emit appIdChanged(); + /** Store the incoming appId in pending */ + m_pendingState.appId = app_id; + m_pendingState.appIdChanged = true; + /** Update the icon */ this->icon = XdgIcon::fromTheme(appId); - if ( this->icon.pixmap(64).width() == 0 ) + + /** Sometimes, appId can be capitalized, for example, Pulsar. So try lower-case. */ + if (this->icon.pixmap(64).width() == 0) { this->icon = XdgIcon::fromTheme(appId.toLower()); } - if ( appIdRecieved && titleRecieved ) + /** We did not get any icon from app-id. Let's use application-x-executable */ + if (this->icon.pixmap(64).width() == 0) { - emit windowReady(); + this->icon = XdgIcon::fromTheme(QString::fromUtf8("application-x-executable")); } } + void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_enter(struct ::wl_output *output) { - emit outputEnter(); + /** This view was added to an output */ + m_pendingState.outputs << output; + m_pendingState.outputsChanged = true; } + void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_leave(struct ::wl_output *output) { - emit outputLeave(); + /** This view was removed from an output; store it in pending. */ + m_pendingState.outputsLeft << output; + + if (m_pendingState.outputs.contains(output)) + { + m_pendingState.outputs.removeAll(output); + } + + m_pendingState.outputsChanged = true; } + void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_state(wl_array *state) { - auto *states = static_cast(state->data); - int numStates = static_cast(state->size / sizeof(uint32_t) ); + /** State of this window was changed; store it in pending. */ + auto *states = static_cast(state->data); + int numStates = static_cast(state->size / sizeof(uint32_t)); - for ( int i = 0; i < numStates; i++ ) { - switch ( (uint32_t)states[ i ] ) { - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED: { - m_pendingState.maximized = true; - break; - } + for (int i = 0; i < numStates; i++) + { + switch ((uint32_t)states[ i ]) + { + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED: { + m_pendingState.maximized = true; + m_pendingState.maximizedChanged = true; + break; + } - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED: { - m_pendingState.minimized = true; - break; - } + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED: { + m_pendingState.minimized = true; + m_pendingState.minimizedChanged = true; + break; + } - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED: { - m_pendingState.activated = true; - break; - } + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED: { + m_pendingState.activated = true; + m_pendingState.activatedChanged = true; + break; + } - case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN: { - m_pendingState.fullscreen = true; - break; - } + case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN: { + m_pendingState.fullscreen = true; + m_pendingState.fullscreenChanged = true; + break; + } } } } + void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_done() { /** - * Update windowState flags before emitting the signals. - * Otherwise, windowState.testFlag(...) will return wrong information!! + * All the states/properties have been sent. + * We can now emit the signals and clear the pending state: + * 1. Update all the variables first. + * 2. Then clear the m_pendingState. + * 3. Emit the changed signals. + * 4. Finally, cleanr the m_pendingState.Changed flags. */ - windowState = QFlags(); - if ( m_pendingState.maximized ) windowState |= state_maximized; - if ( m_pendingState.minimized ) windowState |= state_minimized; - if ( m_pendingState.activated ) windowState |= state_activated; - if ( m_pendingState.fullscreen ) windowState |= state_fullscreen; - - /** Emit the signals. */ - if ( m_viewState.maximized != m_pendingState.maximized ) - emit maximizedChanged(); - - if ( m_viewState.minimized != m_pendingState.minimized ) - emit minimizedChanged(); - - if ( m_viewState.activated!= m_pendingState.activated ) - emit activeChanged(); - - if ( m_viewState.fullscreen != m_pendingState.fullscreen ) - emit fullscreenChanged(); - - /** Store m_pendingState into m_viewState for the next run */ - m_viewState.maximized = m_pendingState.maximized; - m_viewState.minimized = m_pendingState.minimized; - m_viewState.activated = m_pendingState.activated; - m_viewState.fullscreen = m_pendingState.fullscreen; - - /** Reset m_pendingState for the next run */ - m_pendingState.maximized = false; - m_pendingState.minimized = false; - m_pendingState.activated = false; + + // (1) title, if it changed + if (m_pendingState.titleChanged) + { + windowState.title = m_pendingState.title; + } + + // (2) appId, if it changed + if (m_pendingState.appIdChanged) + { + windowState.appId = m_pendingState.appId; + } + + // (3) outputs, if they changed + if (m_pendingState.outputsChanged) + { + for (::wl_output *op: m_pendingState.outputsLeft) + { + if (windowState.outputs.contains(op)) + { + windowState.outputs.removeAll(op); + } + } + + for (::wl_output *op: m_pendingState.outputs) + { + if (!windowState.outputs.contains(op)) + { + windowState.outputs << op; + } + } + } + + // (4) states, if they changed. Don't trust the changed flag. + if (m_pendingState.maximized != windowState.maximized) + { + windowState.maximized = m_pendingState.maximized; + m_pendingState.maximizedChanged = true; + } + + if (m_pendingState.minimized != windowState.minimized) + { + windowState.minimized = m_pendingState.minimized; + m_pendingState.minimizedChanged = true; + } + + if (m_pendingState.activated != windowState.activated) + { + windowState.activated = m_pendingState.activated; + m_pendingState.activatedChanged = true; + } + + if (m_pendingState.fullscreen != windowState.fullscreen) + { + windowState.fullscreen = m_pendingState.fullscreen; + m_pendingState.fullscreenChanged = true; + } + + // (5) parent, if it changed. + if (m_pendingState.parentChanged) + { + if (m_pendingState.parent) + { + setParentWindow(new LXQtTaskBarWlrootsWindow(m_pendingState.parent)); + } + + else + { + setParentWindow(nullptr); + } + } + + /** 2. Clear all m_pendingState. for next run */ + m_pendingState.title = QString(); + m_pendingState.appId = QString(); + m_pendingState.outputs.clear(); + m_pendingState.maximized = false; + m_pendingState.minimized = false; + m_pendingState.activated = false; m_pendingState.fullscreen = false; + m_pendingState.parent = nullptr; + + /** + * 3. Emit signals + * (a) First time done was emitted after the window was created. + * (b) Other times. + */ + + /** (a) First time done was emitted */ + if (initDone == false) + { + qDebug() << " " << this << getWindowId(); + qDebug() << " titleChanged" << m_pendingState.titleChanged << windowState.title; + qDebug() << " appIdChanged" << m_pendingState.appIdChanged << windowState.appId; + qDebug() << " outputsChanged" << m_pendingState.outputsChanged << windowState.outputs; + qDebug() << " maximizedChanged" << m_pendingState.maximizedChanged << windowState.maximized; + qDebug() << " minimizedChanged" << m_pendingState.minimizedChanged << windowState.minimized; + qDebug() << " activatedChanged" << m_pendingState.activatedChanged << windowState.activated; + qDebug() << " fullscreenChanged" << m_pendingState.fullscreenChanged << windowState.fullscreen; + qDebug() << " parentChanged" << m_pendingState.parentChanged << windowState.parent; + + /** + * All the states/properties are already set. + * Any query will give valid results. + */ + initDone = true; + emit windowReady(); + } + + /** (b) All the subsequent times */ + else + { + if (m_pendingState.titleChanged) + emit titleChanged(); + if (m_pendingState.appIdChanged) + emit appIdChanged(); + if (m_pendingState.outputsChanged) + emit outputsChanged(); + if (m_pendingState.maximizedChanged) + emit maximizedChanged(); + if (m_pendingState.minimizedChanged) + emit minimizedChanged(); + if (m_pendingState.activatedChanged) + emit activatedChanged(); + if (m_pendingState.fullscreenChanged) + emit fullscreenChanged(); + if (m_pendingState.parentChanged) + emit parentChanged(); + + emit stateChanged(); + } + + /** 4. Clear m+m_pendingState.Changed flags */ + m_pendingState.titleChanged = false; + m_pendingState.appIdChanged = false; + m_pendingState.outputsChanged = false; + m_pendingState.maximizedChanged = false; + m_pendingState.minimizedChanged = false; + m_pendingState.activatedChanged = false; + m_pendingState.fullscreenChanged = false; + m_pendingState.parentChanged = false; } + void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_closed() { + /** This window was closed */ emit closed(); } + void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_parent(struct ::zwlr_foreign_toplevel_handle_v1 *parent) { - // setParentWindow(new LXQtTaskBarWlrootsWindow(parent)); + /** Parent of this window changed; store it in pending. */ + m_pendingState.parent = parent; + m_pendingState.parentChanged = true; } + void LXQtTaskBarWlrootsWindow::setParentWindow(LXQtTaskBarWlrootsWindow *parent) { const auto old = parentWindow; + QObject::disconnect(parentWindowUnmappedConnection); - if (parent) { + if (parent) + { parentWindow = QPointer(parent); - parentWindowUnmappedConnection = QObject::connect(parent, &LXQtTaskBarWlrootsWindow::closed, this, [this] { + parentWindowUnmappedConnection = QObject::connect( + parent, &LXQtTaskBarWlrootsWindow::closed, this, [ this ] { setParentWindow(nullptr); }); - } else { + } + else + { parentWindow = QPointer(); parentWindowUnmappedConnection = QMetaObject::Connection(); } - if (parentWindow.data() != old.data()) { + if (parentWindow.data() != old.data()) + { Q_EMIT parentChanged(); } } diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h index bc3c2a0b3..705107312 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h @@ -34,6 +34,46 @@ class LXQtTaskBarWlrootsWindowManagment : public QWaylandClientExtensionTemplate bool m_isShowingDesktop = false; }; +using WindowState = QtWayland::zwlr_foreign_toplevel_handle_v1::state; + +class WindowProperties { + public: + /** Title of the window */ + QString title = QString::fromUtf8( "untitled" ); + bool titleChanged = false; + + /** appId of the window */ + QString appId = QString::fromUtf8( "unidentified" ); + bool appIdChanged = false; + + /** List of outputs which the window is currently on */ + QList<::wl_output *> outputs; + bool outputsChanged = false; + + /** Is maximized */ + bool maximized = false; + bool maximizedChanged = false; + + /** Is minimized */ + bool minimized = false; + bool minimizedChanged = false; + + /** Is active */ + bool activated = false; + bool activatedChanged = false; + + /** Is fullscreen */ + bool fullscreen = false; + bool fullscreenChanged = false; + + /** Parent of this view, can be null */ + ::zwlr_foreign_toplevel_handle_v1 * parent = nullptr; + bool parentChanged = false; + + /** List of outputs from which window has left */ + QList<::wl_output *> outputsLeft; +}; + class LXQtTaskBarWlrootsWindow : public QObject, public QtWayland::zwlr_foreign_toplevel_handle_v1 { @@ -46,29 +86,37 @@ class LXQtTaskBarWlrootsWindow : public QObject, void activate(); - using state = QtWayland::zwlr_foreign_toplevel_handle_v1::state; - QString title; - QString appId; - QIcon icon; - QFlags windowState; - QPointer parentWindow; + mutable QString title; + mutable QString appId; + mutable QIcon icon; + mutable WindowProperties windowState; + mutable QPointer parentWindow; Q_SIGNALS: void titleChanged(); void appIdChanged(); - void outputEnter(); - void outputLeave(); - void activeChanged(); + void outputsChanged(); + + /** Individual state change signals */ void maximizedChanged(); void minimizedChanged(); + void activatedChanged(); void fullscreenChanged(); - void done(); - void closed(); + void parentChanged(); - /** We wait to get the title and appId to emit this */ + /** Bulk state change signal */ + void stateChanged(); + + /** First state change signal: Before this, the window did not have a valid state */ void windowReady(); + /** All state changes have been sent. */ + void done(); + + /** Window closed signal */ + void closed(); + protected: void zwlr_foreign_toplevel_handle_v1_title(const QString &title); void zwlr_foreign_toplevel_handle_v1_app_id(const QString &app_id); @@ -84,17 +132,7 @@ class LXQtTaskBarWlrootsWindow : public QObject, QMetaObject::Connection parentWindowUnmappedConnection; - typedef struct view_state_t - { - bool maximized = false; - bool minimized = false; - bool activated = false; - bool fullscreen = false; - } ViewState; - - ViewState m_viewState; - ViewState m_pendingState; + WindowProperties m_pendingState; - bool titleRecieved = false; - bool appIdRecieved = false; + bool initDone = false; }; From cbc9d991735e8448532e9bb43df7f0004cd08960 Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Sat, 30 Mar 2024 00:29:10 +0530 Subject: [PATCH 140/165] Refactor LXQtTaskbarWlrootsBackend Instead of using LXQtTaskbarWlrootsWindow* pointers, it's easier to store and use WId a.k.a quintptr which are derived from the address of the LXQtTaskbarWlrootsWindow pointer. This way, it's trivial to retrieve the underlying pointer with a reinterpret_cast<...>(). Also, switch to using std::find(...) to search a given WId in windows. Use a dedicated function to erase a WId from windows: eraseWindow(...). --- .../wayland/wlroots/lxqttaskbarbackendwlr.cpp | 175 +++++++++--------- .../wayland/wlroots/lxqttaskbarbackendwlr.h | 24 +-- .../wlroots/lxqttaskbarwlrwindowmanagment.cpp | 73 +++----- .../wlroots/lxqttaskbarwlrwindowmanagment.h | 26 ++- 4 files changed, 135 insertions(+), 163 deletions(-) diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp index 5f26ebb2e..3c5754244 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp @@ -8,38 +8,39 @@ #include #include -LXQtTaskBarWlrootsWindow* findWindow(const std::vector windows, LXQtTaskBarWlrootsWindow* window) { - // Use std::find to locate the window pointer within the vector - auto it = std::find(windows.begin(), windows.end(), window); +// Function to search for a window in the vector +WId findWindow(const std::vector& windows, WId tgt) { + // Use std::find to locate the target window + auto it = std::find(windows.begin(), windows.end(), tgt); - // Check if the window was found (iterator != end of vector) + // Check if the window was found (iterator points to windows.end() if not found) if (it != windows.end()) { - // Return the pointer to the found window + // If found, return the window ID by dereferencing the iterator return *it; } - // Window not found, return nullptr - return nullptr; + + return 0; } -void eraseWindow(std::vector& windows, LXQtTaskBarWlrootsWindow* window) { - // Use std::remove to find the element to erase - auto it = std::remove(windows.begin(), windows.end(), window); +// Function to erase a window from the vector +void eraseWindow(std::vector& windows, WId tgt) { + // Use std::vector::iterator to find the window + auto it = std::find(windows.begin(), windows.end(), tgt); - // If the element was found, erase it from the vector + // Check if the window was found if (it != windows.end()) { - windows.erase(it, windows.end()); + // If found, erase the element pointed to by the iterator + windows.erase(it); } } LXQtTaskbarWlrootsBackend::LXQtTaskbarWlrootsBackend(QObject *parent) : ILXQtTaskbarAbstractBackend(parent) { - m_managment.reset(new LXQtTaskBarWlrootsWindowManagment); + m_managment.reset(new LXQtTaskbarWlrootsWindowManagment); m_workspaceInfo.reset(new LXQtWlrootsWaylandWorkspaceInfo); - connect(m_managment.get(), &LXQtTaskBarWlrootsWindowManagment::windowCreated, this, [this](LXQtTaskBarWlrootsWindow *window) { - addWindow(window); - }); + connect(m_managment.get(), &LXQtTaskbarWlrootsWindowManagment::windowCreated, this, &LXQtTaskbarWlrootsBackend::addWindow); } bool LXQtTaskbarWlrootsBackend::supportsAction(WId, LXQtTaskBarBackendAction action) const @@ -82,11 +83,9 @@ bool LXQtTaskbarWlrootsBackend::reloadWindows() QVector LXQtTaskbarWlrootsBackend::getCurrentWindows() const { QVector wids; - wids.reserve(wids.size()); - for(LXQtTaskBarWlrootsWindow * window : windows) - { - wids << window->getWindowId(); + for( WId wid: windows ){ + wids << wid; } return wids; @@ -94,11 +93,11 @@ QVector LXQtTaskbarWlrootsBackend::getCurrentWindows() const QString LXQtTaskbarWlrootsBackend::getWindowTitle(WId windowId) const { - LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + LXQtTaskbarWlrootsWindow *window = getWindow(windowId); if(!window) return QString(); - return window->title; + return window->windowState.title; } bool LXQtTaskbarWlrootsBackend::applicationDemandsAttention(WId) const @@ -110,7 +109,7 @@ QIcon LXQtTaskbarWlrootsBackend::getApplicationIcon(WId windowId, int devicePixe { Q_UNUSED(devicePixels) - LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + LXQtTaskbarWlrootsWindow *window = getWindow(windowId); if(!window) return QIcon(); @@ -119,10 +118,10 @@ QIcon LXQtTaskbarWlrootsBackend::getApplicationIcon(WId windowId, int devicePixe QString LXQtTaskbarWlrootsBackend::getWindowClass(WId windowId) const { - LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + LXQtTaskbarWlrootsWindow *window = getWindow(windowId); if(!window) return QString(); - return window->appId; + return window->windowState.appId; } LXQtTaskBarWindowLayer LXQtTaskbarWlrootsBackend::getWindowLayer(WId) const @@ -137,7 +136,7 @@ bool LXQtTaskbarWlrootsBackend::setWindowLayer(WId, LXQtTaskBarWindowLayer) LXQtTaskBarWindowState LXQtTaskbarWlrootsBackend::getWindowState(WId windowId) const { - LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + LXQtTaskbarWlrootsWindow *window = getWindow(windowId); if(!window) return LXQtTaskBarWindowState::Normal; @@ -155,7 +154,7 @@ LXQtTaskBarWindowState LXQtTaskbarWlrootsBackend::getWindowState(WId windowId) c bool LXQtTaskbarWlrootsBackend::setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set) { - LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + LXQtTaskbarWlrootsWindow *window = getWindow(windowId); if(!window) return false; @@ -221,18 +220,18 @@ bool LXQtTaskbarWlrootsBackend::setWindowState(WId windowId, LXQtTaskBarWindowSt bool LXQtTaskbarWlrootsBackend::isWindowActive(WId windowId) const { - LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + LXQtTaskbarWlrootsWindow *window = getWindow(windowId); if(!window) return false; - return activeWindow == window || window->windowState.activated; + return activeWindow == windowId || window->windowState.activated; } bool LXQtTaskbarWlrootsBackend::raiseWindow(WId windowId, bool onCurrentWorkSpace) { Q_UNUSED(onCurrentWorkSpace) //TODO - LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + LXQtTaskbarWlrootsWindow *window = getWindow(windowId); if(!window) return false; @@ -242,7 +241,7 @@ bool LXQtTaskbarWlrootsBackend::raiseWindow(WId windowId, bool onCurrentWorkSpac bool LXQtTaskbarWlrootsBackend::closeWindow(WId windowId) { - LXQtTaskBarWlrootsWindow *window = getWindow(windowId); + LXQtTaskbarWlrootsWindow *window = getWindow(windowId); if(!window) return false; @@ -252,9 +251,7 @@ bool LXQtTaskbarWlrootsBackend::closeWindow(WId windowId) WId LXQtTaskbarWlrootsBackend::getActiveWindow() const { - if(activeWindow) - return activeWindow->getWindowId(); - return 0; + return activeWindow; } int LXQtTaskbarWlrootsBackend::getWorkspacesCount() const @@ -325,53 +322,60 @@ bool LXQtTaskbarWlrootsBackend::showDesktop(bool) return false; } -void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) +void LXQtTaskbarWlrootsBackend::addWindow(WId winId) { - if (findWindow(windows, window) != nullptr || transients.contains(window)) + if (findWindow(windows, winId) != 0 || transients.contains(winId)) { return; } - auto removeWindow = [window, this] + auto removeWindow = [winId, this] { - eraseWindow(windows, window); - lastActivated.remove(window); + eraseWindow(windows, winId); + lastActivated.remove(winId); - if (activeWindow == window) + if (activeWindow == winId) { - activeWindow = nullptr; + activeWindow = 0; emit activeWindowChanged(0); } - emit windowRemoved(window->getWindowId()); + emit windowRemoved(winId); }; - connect(window, &LXQtTaskBarWlrootsWindow::closed, this, removeWindow); + LXQtTaskbarWlrootsWindow *window = getWindow( winId ); + if ( window == nullptr ) { + return; + } + + /** The window was closed. Remove from our lists */ + connect(window, &LXQtTaskbarWlrootsWindow::closed, this, removeWindow); - connect(window, &LXQtTaskBarWlrootsWindow::titleChanged, this, [window, this] { - emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Title)); + /** */ + connect(window, &LXQtTaskbarWlrootsWindow::titleChanged, this, [winId, this] { + emit windowPropertyChanged(winId, int(LXQtTaskBarWindowProperty::Title)); }); - connect(window, &LXQtTaskBarWlrootsWindow::appIdChanged, this, [window, this] { - emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::WindowClass)); + connect(window, &LXQtTaskbarWlrootsWindow::appIdChanged, this, [winId, this] { + emit windowPropertyChanged(winId, int(LXQtTaskBarWindowProperty::WindowClass)); }); if (window->windowState.activated) { - LXQtTaskBarWlrootsWindow *effectiveActive = window; + LXQtTaskbarWlrootsWindow *effectiveActive = window; while (effectiveActive->parentWindow) { - effectiveActive = effectiveActive->parentWindow; + effectiveActive = getWindow(effectiveActive->parentWindow); } - lastActivated[effectiveActive] = QTime::currentTime(); - activeWindow = effectiveActive; + lastActivated[effectiveActive->getWindowId()] = QTime::currentTime(); + activeWindow = effectiveActive->getWindowId(); } - connect(window, &LXQtTaskBarWlrootsWindow::activatedChanged, this, [window, this] { - LXQtTaskBarWlrootsWindow *effectiveWindow = window; + connect(window, &LXQtTaskbarWlrootsWindow::activatedChanged, this, [window, this] { + WId effectiveWindow = window->getWindowId(); - while (effectiveWindow->parentWindow) + while (getWindow(effectiveWindow)->parentWindow) { - effectiveWindow = effectiveWindow->parentWindow; + effectiveWindow = getWindow(effectiveWindow)->parentWindow; } if (window->windowState.activated) @@ -381,41 +385,43 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) if (activeWindow != effectiveWindow) { activeWindow = effectiveWindow; - emit activeWindowChanged(activeWindow->getWindowId()); + emit activeWindowChanged(activeWindow); } } else { if (activeWindow == effectiveWindow) { - activeWindow = nullptr; + activeWindow = 0; emit activeWindowChanged(0); } } }); - connect(window, &LXQtTaskBarWlrootsWindow::parentChanged, this, [window, this] { - LXQtTaskBarWlrootsWindow *leader = window->parentWindow.data(); + connect(window, &LXQtTaskbarWlrootsWindow::parentChanged, this, [window, this] { + WId leader = window->parentWindow; - if (transients.remove(window)) + /** Basically, check if this window is a transient */ + if (transients.remove(leader)) { if (leader) { // leader change. - transients.insert(window, leader); + transients.insert(window->getWindowId(), leader); } else { // lost a leader, add to regular windows list. - Q_ASSERT(findWindow(windows, window) == nullptr); + Q_ASSERT(findWindow(windows, leader) == 0); - windows.emplace_back(window); + windows.push_back(leader); } } + else if (leader) { - eraseWindow(windows, window); - lastActivated.remove(window); + eraseWindow(windows, window->getWindowId()); + lastActivated.remove(window->getWindowId()); } }); @@ -423,32 +429,30 @@ void LXQtTaskbarWlrootsBackend::addWindow(LXQtTaskBarWlrootsWindow *window) emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::State)); }; - connect(window, &LXQtTaskBarWlrootsWindow::fullscreenChanged, this, stateChanged); + connect(window, &LXQtTaskbarWlrootsWindow::fullscreenChanged, this, stateChanged); - connect(window, &LXQtTaskBarWlrootsWindow::maximizedChanged, this, stateChanged); + connect(window, &LXQtTaskbarWlrootsWindow::maximizedChanged, this, stateChanged); - connect(window, &LXQtTaskBarWlrootsWindow::minimizedChanged, this, stateChanged); + connect(window, &LXQtTaskbarWlrootsWindow::minimizedChanged, this, stateChanged); // Handle transient. - if (LXQtTaskBarWlrootsWindow *leader = window->parentWindow.data()) + if (WId leader = window->parentWindow) { - transients.insert(window, leader); + transients.insert(winId, leader); } else { - windows.push_back(window); + windows.push_back(winId); } - qDebug() << window << window->getWindowId(); - emit windowAdded( window->getWindowId() ); - emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Title)); - emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Icon)); - emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::State)); - emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Geometry)); - emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::WindowClass)); + emit windowAdded( winId ); + emit windowPropertyChanged(winId, int(LXQtTaskBarWindowProperty::WindowClass)); + emit windowPropertyChanged(winId, int(LXQtTaskBarWindowProperty::Title)); + emit windowPropertyChanged(winId, int(LXQtTaskBarWindowProperty::Icon)); + emit windowPropertyChanged(winId, int(LXQtTaskBarWindowProperty::State)); } -bool LXQtTaskbarWlrootsBackend::acceptWindow(LXQtTaskBarWlrootsWindow *window) const +bool LXQtTaskbarWlrootsBackend::acceptWindow(WId window) const { if(transients.contains(window)) return false; @@ -456,22 +460,13 @@ bool LXQtTaskbarWlrootsBackend::acceptWindow(LXQtTaskBarWlrootsWindow *window) c return true; } -LXQtTaskBarWlrootsWindow *LXQtTaskbarWlrootsBackend::getWindow(WId windowId) const +LXQtTaskbarWlrootsWindow *LXQtTaskbarWlrootsBackend::getWindow(WId windowId) const { /** Easiest way is to convert the quintptr to the actual pointer */ - LXQtTaskBarWlrootsWindow *win = reinterpret_cast( windowId ); + LXQtTaskbarWlrootsWindow *win = reinterpret_cast( windowId ); if ( win ) { - qDebug() << "get-window-windowId"; return win; } - /** Fallback attempt */ - for(auto &window : std::as_const(windows)) - { - if(window->getWindowId() == windowId) { - return window; - } - } - return nullptr; } diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h index ef0c67afa..b2ae9a925 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.h @@ -6,8 +6,8 @@ #include #include -class LXQtTaskBarWlrootsWindow; -class LXQtTaskBarWlrootsWindowManagment; +class LXQtTaskbarWlrootsWindow; +class LXQtTaskbarWlrootsWindowManagment; class LXQtWlrootsWaylandWorkspaceInfo; @@ -71,21 +71,21 @@ class LXQtTaskbarWlrootsBackend : public ILXQtTaskbarAbstractBackend virtual bool showDesktop(bool value) override; private: - void addWindow(LXQtTaskBarWlrootsWindow *window); - bool acceptWindow(LXQtTaskBarWlrootsWindow *window) const; + void addWindow(WId wid); + bool acceptWindow(WId wid) const; private: - LXQtTaskBarWlrootsWindow *getWindow(WId windowId) const; + /** Convert WId (i.e. quintptr into LXQtTaskbarWlrootsWindow*) */ + LXQtTaskbarWlrootsWindow *getWindow(WId windowId) const; std::unique_ptr m_workspaceInfo; - std::unique_ptr m_managment; + std::unique_ptr m_managment; + + QHash lastActivated; + WId activeWindow = 0; + std::vector windows; - QHash lastActivated; - LXQtTaskBarWlrootsWindow *activeWindow = nullptr; - std::vector windows; // key=transient child, value=leader - QHash transients; - // key=leader, values=transient children - QMultiHash transientsDemandingAttention; + QHash transients; }; diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp index c9fbb6941..80c0892fa 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp @@ -35,10 +35,10 @@ static inline wl_seat *get_seat() /* - * LXQtTaskBarWlrootsWindowManagment + * LXQtTaskbarWlrootsWindowManagment */ -LXQtTaskBarWlrootsWindowManagment::LXQtTaskBarWlrootsWindowManagment() : QWaylandClientExtensionTemplate(version) +LXQtTaskbarWlrootsWindowManagment::LXQtTaskbarWlrootsWindowManagment() : QWaylandClientExtensionTemplate(version) { /** Automatically destroy thie object */ connect( @@ -51,7 +51,7 @@ LXQtTaskBarWlrootsWindowManagment::LXQtTaskBarWlrootsWindowManagment() : QWaylan } -LXQtTaskBarWlrootsWindowManagment::~LXQtTaskBarWlrootsWindowManagment() +LXQtTaskbarWlrootsWindowManagment::~LXQtTaskbarWlrootsWindowManagment() { if (isActive()) { @@ -60,7 +60,7 @@ LXQtTaskBarWlrootsWindowManagment::~LXQtTaskBarWlrootsWindowManagment() } -void LXQtTaskBarWlrootsWindowManagment::zwlr_foreign_toplevel_manager_v1_toplevel(struct ::zwlr_foreign_toplevel_handle_v1 *toplevel) +void LXQtTaskbarWlrootsWindowManagment::zwlr_foreign_toplevel_manager_v1_toplevel(struct ::zwlr_foreign_toplevel_handle_v1 *toplevel) { /** * A window was created. @@ -68,34 +68,30 @@ void LXQtTaskBarWlrootsWindowManagment::zwlr_foreign_toplevel_manager_v1_topleve * Once we recieve done(), emit the windowReady() signal. */ - auto w = new LXQtTaskBarWlrootsWindow(toplevel); + auto w = new LXQtTaskbarWlrootsWindow(toplevel); - connect(w, &LXQtTaskBarWlrootsWindow::windowReady, [w, this] () { - qDebug() << "------------> a window was created" << w << w->getWindowId() << w->appId << w->title; - emit windowCreated(w); + connect(w, &LXQtTaskbarWlrootsWindow::windowReady, [w, this] () { + emit windowCreated(w->getWindowId()); }); } /* - * LXQtTaskBarWlrootsWindow + * LXQtTaskbarWlrootsWindow */ -LXQtTaskBarWlrootsWindow::LXQtTaskBarWlrootsWindow(::zwlr_foreign_toplevel_handle_v1 *id) : zwlr_foreign_toplevel_handle_v1(id) +LXQtTaskbarWlrootsWindow::LXQtTaskbarWlrootsWindow(::zwlr_foreign_toplevel_handle_v1 *id) : zwlr_foreign_toplevel_handle_v1(id) { - /** Set a default non-null title and appId */ - title = QString::fromUtf8("untitled"); - appId = QString::fromUtf8("unknown"); } -LXQtTaskBarWlrootsWindow::~LXQtTaskBarWlrootsWindow() +LXQtTaskbarWlrootsWindow::~LXQtTaskbarWlrootsWindow() { destroy(); } -void LXQtTaskBarWlrootsWindow::activate() +void LXQtTaskbarWlrootsWindow::activate() { /** * Activate on default seat. @@ -105,7 +101,7 @@ void LXQtTaskBarWlrootsWindow::activate() } -void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_title(const QString& title) +void LXQtTaskbarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_title(const QString& title) { /** Store the incoming title in pending */ m_pendingState.title = title; @@ -113,19 +109,19 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_title(const QStri } -void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_app_id(const QString& app_id) +void LXQtTaskbarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_app_id(const QString& app_id) { /** Store the incoming appId in pending */ m_pendingState.appId = app_id; m_pendingState.appIdChanged = true; /** Update the icon */ - this->icon = XdgIcon::fromTheme(appId); + this->icon = XdgIcon::fromTheme(app_id); /** Sometimes, appId can be capitalized, for example, Pulsar. So try lower-case. */ if (this->icon.pixmap(64).width() == 0) { - this->icon = XdgIcon::fromTheme(appId.toLower()); + this->icon = XdgIcon::fromTheme(app_id.toLower()); } /** We did not get any icon from app-id. Let's use application-x-executable */ @@ -136,7 +132,7 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_app_id(const QStr } -void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_enter(struct ::wl_output *output) +void LXQtTaskbarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_enter(struct ::wl_output *output) { /** This view was added to an output */ m_pendingState.outputs << output; @@ -144,7 +140,7 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_enter(stru } -void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_leave(struct ::wl_output *output) +void LXQtTaskbarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_leave(struct ::wl_output *output) { /** This view was removed from an output; store it in pending. */ m_pendingState.outputsLeft << output; @@ -158,7 +154,7 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_output_leave(stru } -void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_state(wl_array *state) +void LXQtTaskbarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_state(wl_array *state) { /** State of this window was changed; store it in pending. */ auto *states = static_cast(state->data); @@ -196,7 +192,7 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_state(wl_array *s } -void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_done() +void LXQtTaskbarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_done() { /** * All the states/properties have been sent. @@ -269,7 +265,7 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_done() { if (m_pendingState.parent) { - setParentWindow(new LXQtTaskBarWlrootsWindow(m_pendingState.parent)); + setParentWindow(new LXQtTaskbarWlrootsWindow(m_pendingState.parent)); } else @@ -297,16 +293,6 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_done() /** (a) First time done was emitted */ if (initDone == false) { - qDebug() << " " << this << getWindowId(); - qDebug() << " titleChanged" << m_pendingState.titleChanged << windowState.title; - qDebug() << " appIdChanged" << m_pendingState.appIdChanged << windowState.appId; - qDebug() << " outputsChanged" << m_pendingState.outputsChanged << windowState.outputs; - qDebug() << " maximizedChanged" << m_pendingState.maximizedChanged << windowState.maximized; - qDebug() << " minimizedChanged" << m_pendingState.minimizedChanged << windowState.minimized; - qDebug() << " activatedChanged" << m_pendingState.activatedChanged << windowState.activated; - qDebug() << " fullscreenChanged" << m_pendingState.fullscreenChanged << windowState.fullscreen; - qDebug() << " parentChanged" << m_pendingState.parentChanged << windowState.parent; - /** * All the states/properties are already set. * Any query will give valid results. @@ -350,14 +336,14 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_done() } -void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_closed() +void LXQtTaskbarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_closed() { /** This window was closed */ emit closed(); } -void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_parent(struct ::zwlr_foreign_toplevel_handle_v1 *parent) +void LXQtTaskbarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_parent(struct ::zwlr_foreign_toplevel_handle_v1 *parent) { /** Parent of this window changed; store it in pending. */ m_pendingState.parent = parent; @@ -365,28 +351,21 @@ void LXQtTaskBarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_parent(struct ::z } -void LXQtTaskBarWlrootsWindow::setParentWindow(LXQtTaskBarWlrootsWindow *parent) +void LXQtTaskbarWlrootsWindow::setParentWindow(LXQtTaskbarWlrootsWindow *parent) { - const auto old = parentWindow; - QObject::disconnect(parentWindowUnmappedConnection); if (parent) { - parentWindow = QPointer(parent); + parentWindow = parent->getWindowId(); parentWindowUnmappedConnection = QObject::connect( - parent, &LXQtTaskBarWlrootsWindow::closed, this, [ this ] { + parent, &LXQtTaskbarWlrootsWindow::closed, this, [ this ] { setParentWindow(nullptr); }); } else { - parentWindow = QPointer(); + parentWindow = 0; parentWindowUnmappedConnection = QMetaObject::Connection(); } - - if (parentWindow.data() != old.data()) - { - Q_EMIT parentChanged(); - } } diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h index 705107312..9e4dbb6e6 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h @@ -9,17 +9,17 @@ typedef quintptr WId; -class LXQtTaskBarWlrootsWindow; +class LXQtTaskbarWlrootsWindow; -class LXQtTaskBarWlrootsWindowManagment : public QWaylandClientExtensionTemplate, +class LXQtTaskbarWlrootsWindowManagment : public QWaylandClientExtensionTemplate, public QtWayland::zwlr_foreign_toplevel_manager_v1 { Q_OBJECT public: static constexpr int version = 16; - LXQtTaskBarWlrootsWindowManagment(); - ~LXQtTaskBarWlrootsWindowManagment(); + LXQtTaskbarWlrootsWindowManagment(); + ~LXQtTaskbarWlrootsWindowManagment(); inline bool isShowingDesktop() const { return m_isShowingDesktop; } @@ -28,7 +28,7 @@ class LXQtTaskBarWlrootsWindowManagment : public QWaylandClientExtensionTemplate void zwlr_foreign_toplevel_manager_v1_finished() {}; Q_SIGNALS: - void windowCreated(LXQtTaskBarWlrootsWindow *window); + void windowCreated(WId wid); private: bool m_isShowingDesktop = false; @@ -74,23 +74,21 @@ class WindowProperties { QList<::wl_output *> outputsLeft; }; -class LXQtTaskBarWlrootsWindow : public QObject, +class LXQtTaskbarWlrootsWindow : public QObject, public QtWayland::zwlr_foreign_toplevel_handle_v1 { Q_OBJECT public: - LXQtTaskBarWlrootsWindow(::zwlr_foreign_toplevel_handle_v1 *id); - ~LXQtTaskBarWlrootsWindow(); + LXQtTaskbarWlrootsWindow(::zwlr_foreign_toplevel_handle_v1 *id); + ~LXQtTaskbarWlrootsWindow(); inline WId getWindowId() const { return reinterpret_cast(this); } void activate(); - mutable QString title; - mutable QString appId; - mutable QIcon icon; - mutable WindowProperties windowState; - mutable QPointer parentWindow; + QIcon icon; + WindowProperties windowState; + WId parentWindow; Q_SIGNALS: void titleChanged(); @@ -128,7 +126,7 @@ class LXQtTaskBarWlrootsWindow : public QObject, void zwlr_foreign_toplevel_handle_v1_parent(struct ::zwlr_foreign_toplevel_handle_v1 *parent); private: - void setParentWindow(LXQtTaskBarWlrootsWindow *parent); + void setParentWindow(LXQtTaskbarWlrootsWindow *parent); QMetaObject::Connection parentWindowUnmappedConnection; From d19aff44f2402d5698773a388b7e70202ea05b0c Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Sat, 30 Mar 2024 00:43:02 +0530 Subject: [PATCH 141/165] Add a generic wlroots fallback in lxqttaskbarbackendwayland.cpp --- panel/backends/wayland/lxqttaskbarbackendwayland.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index 05d5941ef..c1b3f28af 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -55,6 +55,13 @@ LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) break; } + else if ( desktop == "wlroots" ) + { + qDebug() << "--------------> Using generic wlroots backend"; + m_backend = new LXQtTaskbarWlrootsBackend(); + break; + } + else { // m_backend = nullptr; From 6cdf8c3553720042a46e4e6c554df1e1c83c0b6e Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Sun, 31 Mar 2024 13:40:45 +0530 Subject: [PATCH 142/165] Fix issue with restore (from task context menu. Remove debug messages. --- .../backends/wayland/lxqttaskbarbackendwayland.cpp | 8 -------- .../wayland/wlroots/lxqttaskbarbackendwlr.cpp | 13 ++++++------- panel/lxqtpanelapplication.cpp | 2 -- plugin-taskbar/lxqttaskbarplugin.cpp | 1 - plugin-taskbar/lxqttaskbutton.cpp | 3 ++- 5 files changed, 8 insertions(+), 19 deletions(-) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index c1b3f28af..4ded124d3 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -18,46 +18,39 @@ LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) * If wayfire/sway/labwc/hyprland/wlroots is in desktopsList, we'll use the wlroots backend. */ QList desktopsList = qgetenv("XDG_CURRENT_DESKTOP").toLower().split(':'); - qDebug() << "--------------> Current desktop" << desktopsList; for( QByteArray desktop: desktopsList ) { if ( desktop == "plasma" || desktop == "kde" || desktop == "kwin_wayland" ) { - qDebug() << "--------------> Using plasma backend"; m_backend = new LXQtTaskbarPlasmaBackend(); break; } else if ( desktop == "wayfire" ) { - qDebug() << "--------------> Using wayfire backend"; m_backend = new LXQtTaskbarWlrootsBackend(); break; } else if ( desktop == "sway" ) { - qDebug() << "--------------> Using sway backend"; m_backend = new LXQtTaskbarWlrootsBackend(); break; } else if ( desktop == "labwc" ) { - qDebug() << "--------------> Using labwc backend"; m_backend = new LXQtTaskbarWlrootsBackend(); break; } else if ( desktop == "hyprland" ) { - qDebug() << "--------------> Using hyprland backend"; m_backend = new LXQtTaskbarWlrootsBackend(); break; } else if ( desktop == "wlroots" ) { - qDebug() << "--------------> Using generic wlroots backend"; m_backend = new LXQtTaskbarWlrootsBackend(); break; } @@ -69,7 +62,6 @@ LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) } if ( m_backend == nullptr ) { - qDebug() << "--------------> Using dummy backend. No window management will be done"; } else { diff --git a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp index 3c5754244..f9af04fa3 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarbackendwlr.cpp @@ -188,14 +188,13 @@ bool LXQtTaskbarWlrootsBackend::setWindowState(WId windowId, LXQtTaskBarWindowSt } case LXQtTaskBarWindowState::Normal: { - /** Restore if maximized/minimized */ - if ( window->windowState.minimized) { - window->unset_minimized(); + if (set) + { + if ( window->windowState.maximized) { + window->unset_maximized(); + } } - if ( window->windowState.maximized) { - window->unset_maximized(); - } break; } @@ -229,7 +228,7 @@ bool LXQtTaskbarWlrootsBackend::isWindowActive(WId windowId) const bool LXQtTaskbarWlrootsBackend::raiseWindow(WId windowId, bool onCurrentWorkSpace) { - Q_UNUSED(onCurrentWorkSpace) //TODO + Q_UNUSED(onCurrentWorkSpace) // Cannot be done on a generic wlroots-based compositor! LXQtTaskbarWlrootsWindow *window = getWindow(windowId); if(!window) diff --git a/panel/lxqtpanelapplication.cpp b/panel/lxqtpanelapplication.cpp index 024ba4c05..2a3f08c36 100644 --- a/panel/lxqtpanelapplication.cpp +++ b/panel/lxqtpanelapplication.cpp @@ -47,12 +47,10 @@ ILXQtTaskbarAbstractBackend *createWMBackend() { if(qGuiApp->nativeInterface()){ - qDebug() << "--------------> X11Application"; return new LXQtTaskbarX11Backend; } else if(qGuiApp->nativeInterface()){ - qDebug() << "--------------> WaylandApplication"; return new LXQtTaskbarWaylandBackend; } diff --git a/plugin-taskbar/lxqttaskbarplugin.cpp b/plugin-taskbar/lxqttaskbarplugin.cpp index fbdd5b305..759e6db46 100644 --- a/plugin-taskbar/lxqttaskbarplugin.cpp +++ b/plugin-taskbar/lxqttaskbarplugin.cpp @@ -37,7 +37,6 @@ LXQtTaskBarPlugin::LXQtTaskBarPlugin(const ILXQtPanelPluginStartupInfo &startupI ILXQtPanelPlugin(startupInfo) { - qDebug() << "--------------> Init plugin taskbar"; mTaskBar = new LXQtTaskBar(this); } diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index 851f765bc..c20c0bd92 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -439,7 +439,8 @@ void LXQtTaskButton::deMaximizeApplication() { mBackend->setWindowState(mWindow, LXQtTaskBarWindowState::Maximized, false); - if(!mBackend->isWindowActive(mWindow)) + /** This guard is unnecessary */ + // if(!mBackend->isWindowActive(mWindow)) mBackend->raiseWindow(mWindow, parentTaskBar()->raiseOnCurrentDesktop()); } From bf283bff29f01b5e0ed485ff35e553b372234dbe Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Thu, 4 Apr 2024 14:58:54 +0530 Subject: [PATCH 143/165] Remove qdebug message from lxqttaskbarbackendplasma.cpp --- panel/backends/wayland/plasma/lxqttaskbarbackendplasma.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.cpp b/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.cpp index 84bcc0ca3..4631c03d3 100644 --- a/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.cpp +++ b/panel/backends/wayland/plasma/lxqttaskbarbackendplasma.cpp @@ -118,7 +118,6 @@ bool LXQtTaskbarPlasmaBackend::reloadWindows() QVector LXQtTaskbarPlasmaBackend::getCurrentWindows() const { - qDebug() << "--------------> Retrieving current window list"; QVector wids; wids.reserve(wids.size()); From b10a5dad7a49af598b7edcc6442239effd937fb5 Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Mon, 22 Apr 2024 15:41:18 +0530 Subject: [PATCH 144/165] BugFix: Initialize LXQtTaskbarWlrootsWindow::parentWindow to 0 --- panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h index 9e4dbb6e6..c79f174da 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.h @@ -88,7 +88,7 @@ class LXQtTaskbarWlrootsWindow : public QObject, QIcon icon; WindowProperties windowState; - WId parentWindow; + WId parentWindow = 0; Q_SIGNALS: void titleChanged(); From b1cb8c3c635bb977420861f1bbec8d506219c384 Mon Sep 17 00:00:00 2001 From: Marcus Britanicus Date: Wed, 24 Apr 2024 02:11:20 +0530 Subject: [PATCH 145/165] Extract the icon from the desktop file obtained from app-id --- .../wlroots/lxqttaskbarwlrwindowmanagment.cpp | 188 +++++++++++++++++- 1 file changed, 181 insertions(+), 7 deletions(-) diff --git a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp index 80c0892fa..f3b5f9710 100644 --- a/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp +++ b/panel/backends/wayland/wlroots/lxqttaskbarwlrwindowmanagment.cpp @@ -19,6 +19,186 @@ #include #include +#include + +QString U8Str( const char *str ) { + return QString::fromUtf8( str ); +} + +static inline QString getPixmapIcon(QString name) +{ + QStringList paths{ + U8Str("/usr/local/share/pixmaps/"), + U8Str("/usr/share/pixmaps/"), + }; + + QStringList sfxs{ + U8Str( ".svg" ), U8Str( ".png" ), U8Str( ".xpm" ) + }; + + for (QString path: paths) + { + for (QString sfx: sfxs) + { + if (QFile::exists(path + name + sfx)) + { + return path + name + sfx; + } + } + } + + return QString(); +} + + +QIcon getIconForAppId(QString mAppId) +{ + if (mAppId.isEmpty() or (mAppId == U8Str("Unknown"))) + { + return QIcon(); + } + + /** Wine apps */ + if (mAppId.endsWith(U8Str(".exe"))) + { + return QIcon::fromTheme(U8Str("wine")); + } + + /** Check if a theme icon exists called @mAppId */ + if (QIcon::hasThemeIcon(mAppId)) + { + return QIcon::fromTheme(mAppId); + } + + /** Check if the theme icon is @mAppId, but all lower-case letters */ + else if (QIcon::hasThemeIcon(mAppId.toLower())) + { + return QIcon::fromTheme(mAppId.toLower()); + } + + QStringList appDirs = { + QDir::home().filePath(U8Str(".local/share/applications/")), + U8Str("/usr/local/share/applications/"), + U8Str("/usr/share/applications/"), + }; + + /** + * Assume mAppId == desktop-file-name (ideal situation) + * or mAppId.toLower() == desktop-file-name (cheap fallback) + */ + QString iconName; + + for (QString path: appDirs) + { + /** Get the icon name from desktop (mAppId: as it is) */ + if (QFile::exists(path + mAppId + U8Str(".desktop"))) + { + QSettings desktop(path + mAppId + U8Str(".desktop"), QSettings::IniFormat); + iconName = desktop.value(U8Str("Desktop Entry/Icon")).toString(); + } + + /** Get the icon name from desktop (mAppId: all lower-case letters) */ + else if (QFile::exists(path + mAppId.toLower() + U8Str(".desktop"))) + { + QSettings desktop(path + mAppId.toLower() + U8Str(".desktop"), QSettings::IniFormat); + iconName = desktop.value(U8Str("Desktop Entry/Icon")).toString(); + } + + /** No icon specified: try else-where */ + if (iconName.isEmpty()) + { + continue; + } + + /** We got an iconName, and it's in the current theme */ + if (QIcon::hasThemeIcon(iconName)) + { + return QIcon::fromTheme(iconName); + } + + /** Not a theme icon, but an absolute path */ + else if (QFile::exists(iconName)) + { + return QIcon(iconName); + } + + /** Not theme icon or absolute path. So check /usr/share/pixmaps/ */ + else + { + iconName = getPixmapIcon(iconName); + + if (not iconName.isEmpty()) + { + return QIcon(iconName); + } + } + } + + /* Check all desktop files for @mAppId */ + for (QString path: appDirs) + { + QStringList desktops = QDir(path).entryList({ U8Str("*.desktop") }); + for (QString dskf: desktops) + { + QSettings desktop(path + dskf, QSettings::IniFormat); + + QString exec = desktop.value(U8Str("Desktop Entry/Exec"), U8Str("abcd1234/-")).toString(); + QString name = desktop.value(U8Str("Desktop Entry/Name"), U8Str("abcd1234/-")).toString(); + QString cls = desktop.value(U8Str("Desktop Entry/StartupWMClass"), U8Str("abcd1234/-")).toString(); + + QString execPath = U8Str(std::filesystem::path(exec.toStdString()).filename().c_str()); + + if (mAppId.compare(execPath, Qt::CaseInsensitive) == 0) + { + iconName = desktop.value(U8Str("Desktop Entry/Icon")).toString(); + } + + else if (mAppId.compare(name, Qt::CaseInsensitive) == 0) + { + iconName = desktop.value(U8Str("Desktop Entry/Icon")).toString(); + } + + else if (mAppId.compare(cls, Qt::CaseInsensitive) == 0) + { + iconName = desktop.value(U8Str("Desktop Entry/Icon")).toString(); + } + + if (not iconName.isEmpty()) + { + if (QIcon::hasThemeIcon(iconName)) + { + return QIcon::fromTheme(iconName); + } + + else if (QFile::exists(iconName)) + { + return QIcon(iconName); + } + + else + { + iconName = getPixmapIcon(iconName); + + if (not iconName.isEmpty()) + { + return QIcon(iconName); + } + } + } + } + } + + iconName = getPixmapIcon(iconName); + + if (not iconName.isEmpty()) + { + return QIcon(iconName); + } + + return QIcon(); +} + + static inline wl_seat *get_seat() { QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface(); @@ -116,13 +296,7 @@ void LXQtTaskbarWlrootsWindow::zwlr_foreign_toplevel_handle_v1_app_id(const QStr m_pendingState.appIdChanged = true; /** Update the icon */ - this->icon = XdgIcon::fromTheme(app_id); - - /** Sometimes, appId can be capitalized, for example, Pulsar. So try lower-case. */ - if (this->icon.pixmap(64).width() == 0) - { - this->icon = XdgIcon::fromTheme(app_id.toLower()); - } + this->icon = getIconForAppId(app_id); /** We did not get any icon from app-id. Let's use application-x-executable */ if (this->icon.pixmap(64).width() == 0) From 8a3c8ce27632be38c4205528484129b050d265a2 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 17 Feb 2024 19:18:09 +0100 Subject: [PATCH 146/165] TaskBar: add experimental KWin Wayland backend NOTE: works only on KWin - Choose backend at runtime - Windows filter logic is re-evaluated on window property changes --- panel/CMakeLists.txt | 20 + panel/backends/CMakeLists.txt | 1 + panel/backends/wayland/CMakeLists.txt | 1 + .../wayland/lxqttaskbarbackendwayland.cpp | 626 +++++++++ .../wayland/lxqttaskbarbackendwayland.h | 87 ++ .../lxqttaskbarplasmawindowmanagment.cpp | 1146 +++++++++++++++++ .../lxqttaskbarplasmawindowmanagment.h | 154 +++ .../protocols/plasma-window-management.xml | 425 ++++++ panel/lxqtpanelapplication.cpp | 3 + 9 files changed, 2463 insertions(+) create mode 100644 panel/backends/wayland/CMakeLists.txt create mode 100644 panel/backends/wayland/lxqttaskbarbackendwayland.cpp create mode 100644 panel/backends/wayland/lxqttaskbarbackendwayland.h create mode 100644 panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp create mode 100644 panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h create mode 100644 panel/backends/wayland/protocols/plasma-window-management.xml diff --git a/panel/CMakeLists.txt b/panel/CMakeLists.txt index 074c62af3..767b459e2 100644 --- a/panel/CMakeLists.txt +++ b/panel/CMakeLists.txt @@ -3,6 +3,10 @@ set(PROJECT lxqt-panel) # TODO add_subdirectory(backends) +# TODO: move to backend folder +find_package(Qt6 ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS WaylandClient Concurrent) + + set(PRIV_HEADERS panelpluginsmodel.h windownotifier.h @@ -26,7 +30,11 @@ set(PRIV_HEADERS backends/lxqttaskbartypes.h backends/lxqttaskbardummybackend.h + backends/xcb/lxqttaskbarbackend_x11.h + + backends/wayland/lxqttaskbarbackendwayland.h + backends/wayland/lxqttaskbarplasmawindowmanagment.h ) # using LXQt namespace in the public headers. @@ -61,7 +69,11 @@ set(SOURCES backends/ilxqttaskbarabstractbackend.cpp backends/lxqttaskbardummybackend.cpp + backends/xcb/lxqttaskbarbackend_x11.cpp + + backends/wayland/lxqttaskbarbackendwayland.cpp + backends/wayland/lxqttaskbarplasmawindowmanagment.cpp ) set(UI @@ -116,11 +128,19 @@ add_executable(${PROJECT} ${UI} ) +# TODO: move to backend folder +qt6_generate_wayland_protocol_client_sources(${PROJECT} +FILES + ${CMAKE_CURRENT_SOURCE_DIR}/backends/wayland/protocols/plasma-window-management.xml +) + target_link_libraries(${PROJECT} ${LIBRARIES} ${QTX_LIBRARIES} KF6::WindowSystem LayerShellQt::Interface + Qt6::WaylandClient # TODO: Move to backend folder + Qt6::Concurrent ${STATIC_PLUGINS} ) diff --git a/panel/backends/CMakeLists.txt b/panel/backends/CMakeLists.txt index 8f34a3c67..f1915b823 100644 --- a/panel/backends/CMakeLists.txt +++ b/panel/backends/CMakeLists.txt @@ -1 +1,2 @@ +add_subdirectory(wayland) add_subdirectory(xcb) diff --git a/panel/backends/wayland/CMakeLists.txt b/panel/backends/wayland/CMakeLists.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/panel/backends/wayland/CMakeLists.txt @@ -0,0 +1 @@ + diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp new file mode 100644 index 000000000..a2a415504 --- /dev/null +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -0,0 +1,626 @@ +#include "lxqttaskbarbackendwayland.h" + +#include "lxqttaskbarplasmawindowmanagment.h" + +#include +#include +#include + +auto findWindow(const std::vector>& windows, LXQtTaskBarPlasmaWindow *window) +{ + //TODO: use algorithms + auto end = windows.end(); + for(auto it = windows.begin(); it != end; it++) + { + if((*it).get() == window) + { + return it; + } + } + + return windows.end(); +} + +LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) : + ILXQtTaskbarAbstractBackend(parent) +{ + m_managment.reset(new LXQtTaskBarPlasmaWindowManagment); + + connect(m_managment.get(), &LXQtTaskBarPlasmaWindowManagment::windowCreated, this, [this](LXQtTaskBarPlasmaWindow *window) { + connect(window, &LXQtTaskBarPlasmaWindow::initialStateDone, this, [this, window] { + addWindow(window); + }); + }); + + // connect(m_managment.get(), &LXQtTaskBarPlasmaWindowManagment::stackingOrderChanged, + // this, [this](const QString &order) { + // // stackingOrder = order.split(QLatin1Char(';')); + // // for (const auto &window : std::as_const(windows)) { + // // this->dataChanged(window.get(), StackingOrder); + // // } + // }); +} + +bool LXQtTaskbarWaylandBackend::supportsAction(WId windowId, LXQtTaskBarBackendAction action) const +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + LXQtTaskBarPlasmaWindow::state state; + + switch (action) + { + case LXQtTaskBarBackendAction::Move: + state = LXQtTaskBarPlasmaWindow::state::state_movable; + break; + + case LXQtTaskBarBackendAction::Resize: + state = LXQtTaskBarPlasmaWindow::state::state_resizable; + break; + + case LXQtTaskBarBackendAction::Maximize: + state = LXQtTaskBarPlasmaWindow::state::state_maximizable; + break; + + case LXQtTaskBarBackendAction::Minimize: + state = LXQtTaskBarPlasmaWindow::state::state_minimizable; + break; + + case LXQtTaskBarBackendAction::RollUp: + state = LXQtTaskBarPlasmaWindow::state::state_shadeable; + break; + + case LXQtTaskBarBackendAction::FullScreen: + state = LXQtTaskBarPlasmaWindow::state::state_fullscreenable; + break; + + default: + return false; + } + + return window->windowState.testFlag(state); +} + +bool LXQtTaskbarWaylandBackend::reloadWindows() +{ + return false; //TODO +} + +QVector LXQtTaskbarWaylandBackend::getCurrentWindows() const +{ + QVector wids(windows.size()); + for(const auto& window : std::as_const(windows)) + { + wids << window->getWindowId(); + } + return wids; +} + +QString LXQtTaskbarWaylandBackend::getWindowTitle(WId windowId) const +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return QString(); + + return window->title; +} + +bool LXQtTaskbarWaylandBackend::applicationDemandsAttention(WId windowId) const +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + return window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_demands_attention) || transientsDemandingAttention.contains(window); +} + +QIcon LXQtTaskbarWaylandBackend::getApplicationIcon(WId windowId, int devicePixels) const +{ + Q_UNUSED(devicePixels) + + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return QIcon(); + + return window->icon; +} + +QString LXQtTaskbarWaylandBackend::getWindowClass(WId windowId) const +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return QString(); + return window->appId; +} + +LXQtTaskBarWindowLayer LXQtTaskbarWaylandBackend::getWindowLayer(WId windowId) const +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return LXQtTaskBarWindowLayer::Normal; + + if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_keep_above)) + return LXQtTaskBarWindowLayer::KeepAbove; + + if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_keep_below)) + return LXQtTaskBarWindowLayer::KeepBelow; + + return LXQtTaskBarWindowLayer::Normal; +} + +bool LXQtTaskbarWaylandBackend::setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + if(getWindowLayer(windowId) == layer) + return true; //TODO: make more efficient + + LXQtTaskBarPlasmaWindow::state plasmaState = LXQtTaskBarPlasmaWindow::state::state_keep_above; + switch (layer) + { + case LXQtTaskBarWindowLayer::Normal: + case LXQtTaskBarWindowLayer::KeepAbove: + break; + case LXQtTaskBarWindowLayer::KeepBelow: + plasmaState = LXQtTaskBarPlasmaWindow::state::state_keep_below; + break; + default: + return false; + } + + window->set_state(plasmaState, layer == LXQtTaskBarWindowLayer::Normal ? 0 : plasmaState); + return false; +} + +LXQtTaskBarWindowState LXQtTaskbarWaylandBackend::getWindowState(WId windowId) const +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return LXQtTaskBarWindowState::Normal; + + if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_minimized)) + return LXQtTaskBarWindowState::Hidden; + if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_maximizable)) + return LXQtTaskBarWindowState::Maximized; + if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_shaded)) + return LXQtTaskBarWindowState::RolledUp; + if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_fullscreen)) + return LXQtTaskBarWindowState::FullScreen; + + return LXQtTaskBarWindowState::Normal; +} + +bool LXQtTaskbarWaylandBackend::setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set) +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + LXQtTaskBarPlasmaWindow::state plasmaState; + switch (state) + { + case LXQtTaskBarWindowState::Minimized: + { + plasmaState = LXQtTaskBarPlasmaWindow::state::state_minimized; + break; + } + case LXQtTaskBarWindowState::Maximized: + case LXQtTaskBarWindowState::MaximizedVertically: + case LXQtTaskBarWindowState::MaximizedHorizontally: + { + plasmaState = LXQtTaskBarPlasmaWindow::state::state_maximized; + break; + } + case LXQtTaskBarWindowState::Normal: + { + plasmaState = LXQtTaskBarPlasmaWindow::state::state_maximized; + set = !set; //TODO: correct + break; + } + case LXQtTaskBarWindowState::RolledUp: + { + plasmaState = LXQtTaskBarPlasmaWindow::state::state_shaded; + break; + } + default: + return false; + } + + window->set_state(plasmaState, set ? plasmaState : 0); + return true; +} + +bool LXQtTaskbarWaylandBackend::isWindowActive(WId windowId) const +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + return activeWindow == window || window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_active); +} + +bool LXQtTaskbarWaylandBackend::raiseWindow(WId windowId, bool onCurrentWorkSpace) +{ + Q_UNUSED(onCurrentWorkSpace) //TODO + + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + // Pull forward any transient demanding attention. + if (auto *transientDemandingAttention = transientsDemandingAttention.value(window)) + { + window = transientDemandingAttention; + } + else + { + // TODO Shouldn't KWin take care of that? + // Bringing a transient to the front usually brings its parent with it + // but focus is not handled properly. + // TODO take into account d->lastActivation instead + // of just taking the first one. + while (transients.key(window)) + { + window = transients.key(window); + } + } + + window->set_state(LXQtTaskBarPlasmaWindow::state::state_active, LXQtTaskBarPlasmaWindow::state::state_active); + return true; +} + +bool LXQtTaskbarWaylandBackend::closeWindow(WId windowId) +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + window->close(); + return true; +} + +WId LXQtTaskbarWaylandBackend::getActiveWindow() const +{ + if(activeWindow) + return activeWindow->getWindowId(); + return 0; +} + +int LXQtTaskbarWaylandBackend::getWorkspacesCount() const +{ + return 1; //TODO +} + +QString LXQtTaskbarWaylandBackend::getWorkspaceName(int idx) const +{ + return QStringLiteral("TestWorkspace"); +} + +int LXQtTaskbarWaylandBackend::getCurrentWorkspace() const +{ + return 0; //TODO +} + +bool LXQtTaskbarWaylandBackend::setCurrentWorkspace(int idx) +{ + Q_UNUSED(idx) + return false; //TODO +} + +int LXQtTaskbarWaylandBackend::getWindowWorkspace(WId windowId) const +{ + Q_UNUSED(windowId) + return 0; //TODO +} + +bool LXQtTaskbarWaylandBackend::setWindowOnWorkspace(WId windowId, int idx) +{ + Q_UNUSED(windowId) + Q_UNUSED(idx) + return false; //TODO +} + +void LXQtTaskbarWaylandBackend::moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) +{ + +} + +bool LXQtTaskbarWaylandBackend::isWindowOnScreen(QScreen *screen, WId windowId) const +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + return screen->geometry().intersects(window->geometry); +} + +void LXQtTaskbarWaylandBackend::moveApplication(WId windowId) +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return; + + window->set_state(LXQtTaskBarPlasmaWindow::state::state_active, LXQtTaskBarPlasmaWindow::state::state_active); + window->request_move(); +} + +void LXQtTaskbarWaylandBackend::resizeApplication(WId windowId) +{ + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return; + + window->set_state(LXQtTaskBarPlasmaWindow::state::state_active, LXQtTaskBarPlasmaWindow::state::state_active); + window->request_resize(); +} + +void LXQtTaskbarWaylandBackend::refreshIconGeometry(WId windowId, const QRect &geom) +{ + +} + +bool LXQtTaskbarWaylandBackend::isAreaOverlapped(const QRect &area) const +{ + for(auto &window : std::as_const(windows)) + { + if(window->geometry.intersects(area)) + return true; + } + return false; +} + +void LXQtTaskbarWaylandBackend::addWindow(LXQtTaskBarPlasmaWindow *window) +{ + if (findWindow(windows, window) != windows.end() || transients.contains(window)) + { + return; + } + + auto removeWindow = [window, this] + { + auto it = findWindow(windows, window); + if (it != windows.end()) + { + if(window->acceptedInTaskBar) + emit windowRemoved(window->getWindowId()); + windows.erase(it); + transientsDemandingAttention.remove(window); + lastActivated.remove(window); + } + else + { + // Could be a transient. + // Removing a transient might change the demands attention state of the leader. + if (transients.remove(window)) + { + if (LXQtTaskBarPlasmaWindow *leader = transientsDemandingAttention.key(window)) { + transientsDemandingAttention.remove(leader, window); + emit windowPropertyChanged(leader->getWindowId(), int(LXQtTaskBarWindowProperty::Urgency)); + } + } + } + + if (activeWindow == window) + { + activeWindow = nullptr; + emit activeWindowChanged(0); + } + }; + + connect(window, &LXQtTaskBarPlasmaWindow::unmapped, this, removeWindow); + + connect(window, &LXQtTaskBarPlasmaWindow::titleChanged, this, [window, this] { + updateWindowAcceptance(window); + if(window->acceptedInTaskBar) + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Title)); + }); + + connect(window, &LXQtTaskBarPlasmaWindow::iconChanged, this, [window, this] { + updateWindowAcceptance(window); + if(window->acceptedInTaskBar) + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Icon)); + }); + + connect(window, &LXQtTaskBarPlasmaWindow::geometryChanged, this, [window, this] { + updateWindowAcceptance(window); + if(window->acceptedInTaskBar) + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Geometry)); + }); + + connect(window, &LXQtTaskBarPlasmaWindow::appIdChanged, this, [window, this] { + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::WindowClass)); + }); + + if (window->windowState & LXQtTaskBarPlasmaWindow::state::state_active) { + LXQtTaskBarPlasmaWindow *effectiveActive = window; + while (effectiveActive->parentWindow) { + effectiveActive = effectiveActive->parentWindow; + } + + lastActivated[effectiveActive] = QTime::currentTime(); + activeWindow = effectiveActive; + } + + connect(window, &LXQtTaskBarPlasmaWindow::activeChanged, this, [window, this] { + const bool active = window->windowState & LXQtTaskBarPlasmaWindow::state::state_active; + + LXQtTaskBarPlasmaWindow *effectiveWindow = window; + + while (effectiveWindow->parentWindow) + { + effectiveWindow = effectiveWindow->parentWindow; + } + + if (active) + { + lastActivated[effectiveWindow] = QTime::currentTime(); + + if (activeWindow != effectiveWindow) + { + activeWindow = effectiveWindow; + emit activeWindowChanged(activeWindow->getWindowId()); + } + } + else + { + if (activeWindow == effectiveWindow) + { + activeWindow = nullptr; + emit activeWindowChanged(0); + } + } + }); + + connect(window, &LXQtTaskBarPlasmaWindow::parentWindowChanged, this, [window, this] { + LXQtTaskBarPlasmaWindow *leader = window->parentWindow.data(); + + // Migrate demanding attention to new leader. + if (window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_demands_attention)) + { + if (auto *oldLeader = transientsDemandingAttention.key(window)) + { + if (window->parentWindow != oldLeader) + { + transientsDemandingAttention.remove(oldLeader, window); + transientsDemandingAttention.insert(leader, window); + emit windowPropertyChanged(oldLeader->getWindowId(), int(LXQtTaskBarWindowProperty::Urgency)); + emit windowPropertyChanged(leader->getWindowId(), int(LXQtTaskBarWindowProperty::Urgency)); + } + } + } + + if (transients.remove(window)) + { + if (leader) + { + // leader change. + transients.insert(window, leader); + } + else + { + // lost a leader, add to regular windows list. + Q_ASSERT(findWindow(windows, window) == windows.end()); + + windows.emplace_back(window); + } + } + else if (leader) + { + // gained a leader, remove from regular windows list. + auto it = findWindow(windows, window); + Q_ASSERT(it != windows.end()); + + windows.erase(it); + lastActivated.remove(window); + } + }); + + auto stateChanged = [window, this] { + updateWindowAcceptance(window); + if(window->acceptedInTaskBar) + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::State)); + }; + + connect(window, &LXQtTaskBarPlasmaWindow::fullscreenChanged, this, stateChanged); + + connect(window, &LXQtTaskBarPlasmaWindow::maximizedChanged, this, stateChanged); + + connect(window, &LXQtTaskBarPlasmaWindow::minimizedChanged, this, stateChanged); + + connect(window, &LXQtTaskBarPlasmaWindow::shadedChanged, this, stateChanged); + + auto workspaceChanged = [window, this] { + updateWindowAcceptance(window); + if(window->acceptedInTaskBar) + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Workspace)); + }; + + connect(window, &LXQtTaskBarPlasmaWindow::virtualDesktopEntered, this, workspaceChanged); + connect(window, &LXQtTaskBarPlasmaWindow::virtualDesktopLeft, this, workspaceChanged); + + connect(window, &LXQtTaskBarPlasmaWindow::demandsAttentionChanged, this, [window, this] { + // Changes to a transient's state might change demands attention state for leader. + if (auto *leader = transients.value(window)) + { + if (window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_demands_attention)) + { + if (!transientsDemandingAttention.values(leader).contains(window)) + { + transientsDemandingAttention.insert(leader, window); + emit windowPropertyChanged(leader->getWindowId(), int(LXQtTaskBarWindowProperty::Urgency)); + } + } + else if (transientsDemandingAttention.remove(window)) + { + emit windowPropertyChanged(leader->getWindowId(), int(LXQtTaskBarWindowProperty::Urgency)); + } + } + else + { + emit windowPropertyChanged(window->getWindowId(), int(LXQtTaskBarWindowProperty::Urgency)); + } + }); + + connect(window, &LXQtTaskBarPlasmaWindow::skipTaskbarChanged, this, [window, this] { + updateWindowAcceptance(window); + }); + + // QObject::connect(window, &PlasmaWindow::applicationMenuChanged, q, [window, this] { + // this->dataChanged(window, QList{ApplicationMenuServiceName, ApplicationMenuObjectPath}); + // }); + + // Handle transient. + if (LXQtTaskBarPlasmaWindow *leader = window->parentWindow.data()) + { + transients.insert(window, leader); + + // Update demands attention state for leader. + if (window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_demands_attention)) + { + transientsDemandingAttention.insert(leader, window); + if(leader->acceptedInTaskBar) + emit windowPropertyChanged(leader->getWindowId(), int(LXQtTaskBarWindowProperty::Urgency)); + } + } + else + { + windows.emplace_back(window); + updateWindowAcceptance(window); + } +} + +bool LXQtTaskbarWaylandBackend::acceptWindow(LXQtTaskBarPlasmaWindow *window) const +{ + if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_skiptaskbar)) + return false; + + return true; +} + +void LXQtTaskbarWaylandBackend::updateWindowAcceptance(LXQtTaskBarPlasmaWindow *window) +{ + if(!window->acceptedInTaskBar && acceptWindow(window)) + { + window->acceptedInTaskBar = true; + emit windowAdded(window->getWindowId()); + } + else if(window->acceptedInTaskBar && !acceptWindow(window)) + { + window->acceptedInTaskBar = false; + emit windowRemoved(window->getWindowId()); + } +} + +LXQtTaskBarPlasmaWindow *LXQtTaskbarWaylandBackend::getWindow(WId windowId) const +{ + for(auto &window : std::as_const(windows)) + { + if(window->getWindowId() == windowId) + return window.get(); + } + + return nullptr; +} diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.h b/panel/backends/wayland/lxqttaskbarbackendwayland.h new file mode 100644 index 000000000..553a7e585 --- /dev/null +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.h @@ -0,0 +1,87 @@ +#ifndef LXQTTASKBARBACKENDWAYLAND_H +#define LXQTTASKBARBACKENDWAYLAND_H + +#include "../ilxqttaskbarabstractbackend.h" + +#include +#include + +class LXQtTaskBarPlasmaWindow; +class LXQtTaskBarPlasmaWindowManagment; + + +class LXQtTaskbarWaylandBackend : public ILXQtTaskbarAbstractBackend +{ + Q_OBJECT + +public: + explicit LXQtTaskbarWaylandBackend(QObject *parent = nullptr); + + // Backend + virtual bool supportsAction(WId windowId, LXQtTaskBarBackendAction action) const override; + + // Windows + virtual bool reloadWindows() override; + + virtual QVector getCurrentWindows() const override; + virtual QString getWindowTitle(WId windowId) const override; + virtual bool applicationDemandsAttention(WId windowId) const override; + virtual QIcon getApplicationIcon(WId windowId, int devicePixels) const override; + virtual QString getWindowClass(WId windowId) const override; + + virtual LXQtTaskBarWindowLayer getWindowLayer(WId windowId) const override; + virtual bool setWindowLayer(WId windowId, LXQtTaskBarWindowLayer layer) override; + + virtual LXQtTaskBarWindowState getWindowState(WId windowId) const override; + virtual bool setWindowState(WId windowId, LXQtTaskBarWindowState state, bool set) override; + + virtual bool isWindowActive(WId windowId) const override; + virtual bool raiseWindow(WId windowId, bool onCurrentWorkSpace) override; + + virtual bool closeWindow(WId windowId) override; + + virtual WId getActiveWindow() const override; + + // Workspaces + virtual int getWorkspacesCount() const override; + virtual QString getWorkspaceName(int idx) const override; + + virtual int getCurrentWorkspace() const override; + virtual bool setCurrentWorkspace(int idx) override; + + virtual int getWindowWorkspace(WId windowId) const override; + virtual bool setWindowOnWorkspace(WId windowId, int idx) override; + + virtual void moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) override; + + virtual bool isWindowOnScreen(QScreen *screen, WId windowId) const override; + + // X11 Specific + virtual void moveApplication(WId windowId) override; + virtual void resizeApplication(WId windowId) override; + + virtual void refreshIconGeometry(WId windowId, const QRect &geom) override; + + // Panel internal + virtual bool isAreaOverlapped(const QRect& area) const override; + +private: + void addWindow(LXQtTaskBarPlasmaWindow *window); + bool acceptWindow(LXQtTaskBarPlasmaWindow *window) const; + void updateWindowAcceptance(LXQtTaskBarPlasmaWindow *window); + +private: + LXQtTaskBarPlasmaWindow *getWindow(WId windowId) const; + + std::unique_ptr m_managment; + + QHash lastActivated; + LXQtTaskBarPlasmaWindow *activeWindow = nullptr; + std::vector> windows; + // key=transient child, value=leader + QHash transients; + // key=leader, values=transient children + QMultiHash transientsDemandingAttention; +}; + +#endif // LXQTTASKBARBACKENDWAYLAND_H diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp new file mode 100644 index 000000000..0a282eadd --- /dev/null +++ b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp @@ -0,0 +1,1146 @@ +#include "lxqttaskbarplasmawindowmanagment.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +// QUuid WaylandTasksModel::Private::uuid = QUuid::createUuid(); + +// WaylandTasksModel::Private::Private(WaylandTasksModel *q) +// : q(q) +// { +// } + +// void WaylandTasksModel::Private::init() +// { +// auto clearCacheAndRefresh = [this] { +// if (windows.empty()) { +// return; +// } + +// appDataCache.clear(); + +// // Emit changes of all roles satisfied from app data cache. +// Q_EMIT q->dataChanged(q->index(0, 0), +// q->index(windows.size() - 1, 0), +// QList{Qt::DecorationRole, +// AbstractTasksModel::AppId, +// AbstractTasksModel::AppName, +// AbstractTasksModel::GenericName, +// AbstractTasksModel::LauncherUrl, +// AbstractTasksModel::LauncherUrlWithoutIcon, +// AbstractTasksModel::CanLaunchNewInstance, +// AbstractTasksModel::SkipTaskbar}); +// }; + +// rulesConfig = KSharedConfig::openConfig(QStringLiteral("taskmanagerrulesrc")); +// configWatcher = new KDirWatch(q); + +// for (const QString &location : QStandardPaths::standardLocations(QStandardPaths::ConfigLocation)) { +// configWatcher->addFile(location + QLatin1String("/taskmanagerrulesrc")); +// } + +// auto rulesConfigChange = [this, clearCacheAndRefresh] { +// rulesConfig->reparseConfiguration(); +// clearCacheAndRefresh(); +// }; + +// QObject::connect(configWatcher, &KDirWatch::dirty, rulesConfigChange); +// QObject::connect(configWatcher, &KDirWatch::created, rulesConfigChange); +// QObject::connect(configWatcher, &KDirWatch::deleted, rulesConfigChange); + +// virtualDesktopInfo = new VirtualDesktopInfo(q); + +// initWayland(); +// } + +// void WaylandTasksModel::Private::initWayland() +// { +// if (!KWindowSystem::isPlatformWayland()) { +// return; +// } + +// windowManagement = std::make_unique(); + +// QObject::connect(windowManagement.get(), &PlasmaWindowManagement::activeChanged, q, [this] { +// q->beginResetModel(); +// windows.clear(); +// q->endResetModel(); +// }); + +// QObject::connect(windowManagement.get(), &PlasmaWindowManagement::windowCreated, q, [this](PlasmaWindow *window) { +// connect(window, &PlasmaWindow::initialStateDone, q, [this, window] { +// addWindow(window); +// }); +// }); + +// QObject::connect(windowManagement.get(), &PlasmaWindowManagement::stackingOrderChanged, q, [this](const QString &order) { +// stackingOrder = order.split(QLatin1Char(';')); +// for (const auto &window : std::as_const(windows)) { +// this->dataChanged(window.get(), StackingOrder); +// } +// }); +// } + +// auto WaylandTasksModel::Private::findWindow(PlasmaWindow *window) const +// { +// return std::find_if(windows.begin(), windows.end(), [window](const std::unique_ptr &candidate) { +// return candidate.get() == window; +// }); +// } + +// void WaylandTasksModel::Private::addWindow(PlasmaWindow *window) +// { +// if (findWindow(window) != windows.end() || transients.contains(window)) { +// return; +// } + +// auto removeWindow = [window, this] { +// auto it = findWindow(window); +// if (it != windows.end()) { +// const int row = it - windows.begin(); +// q->beginRemoveRows(QModelIndex(), row, row); +// windows.erase(it); +// transientsDemandingAttention.remove(window); +// appDataCache.remove(window); +// lastActivated.remove(window); +// q->endRemoveRows(); +// } else { // Could be a transient. +// // Removing a transient might change the demands attention state of the leader. +// if (transients.remove(window)) { +// if (PlasmaWindow *leader = transientsDemandingAttention.key(window)) { +// transientsDemandingAttention.remove(leader, window); +// dataChanged(leader, QVector{IsDemandingAttention}); +// } +// } +// } + +// if (activeWindow == window) { +// activeWindow = nullptr; +// } +// }; + +// QObject::connect(window, &PlasmaWindow::unmapped, q, removeWindow); + +// QObject::connect(window, &PlasmaWindow::titleChanged, q, [window, this] { +// this->dataChanged(window, Qt::DisplayRole); +// }); + +// QObject::connect(window, &PlasmaWindow::iconChanged, q, [window, this] { +// // The icon in the AppData struct might come from PlasmaWindow if it wasn't +// // filled in by windowUrlFromMetadata+appDataFromUrl. +// // TODO: Don't evict the cache unnecessarily if this isn't the case. As icons +// // are currently very static on Wayland, this eviction is unlikely to happen +// // frequently as of now. +// appDataCache.remove(window); +// this->dataChanged(window, Qt::DecorationRole); +// }); + +// QObject::connect(window, &PlasmaWindow::appIdChanged, q, [window, this] { +// // The AppData struct in the cache is derived from this and needs +// // to be evicted in favor of a fresh struct based on the changed +// // window metadata. +// appDataCache.remove(window); + +// // Refresh roles satisfied from the app data cache. +// this->dataChanged(window, +// QList{Qt::DecorationRole, AppId, AppName, GenericName, LauncherUrl, LauncherUrlWithoutIcon, SkipTaskbar, CanLaunchNewInstance}); +// }); + +// if (window->windowState & PlasmaWindow::state::state_active) { +// PlasmaWindow *effectiveActive = window; +// while (effectiveActive->parentWindow) { +// effectiveActive = effectiveActive->parentWindow; +// } + +// lastActivated[effectiveActive] = QTime::currentTime(); +// activeWindow = effectiveActive; +// } + +// QObject::connect(window, &PlasmaWindow::activeChanged, q, [window, this] { +// const bool active = window->windowState & PlasmaWindow::state::state_active; + +// PlasmaWindow *effectiveWindow = window; + +// while (effectiveWindow->parentWindow) { +// effectiveWindow = effectiveWindow->parentWindow; +// } + +// if (active) { +// lastActivated[effectiveWindow] = QTime::currentTime(); + +// if (activeWindow != effectiveWindow) { +// activeWindow = effectiveWindow; +// this->dataChanged(effectiveWindow, IsActive); +// } +// } else { +// if (activeWindow == effectiveWindow) { +// activeWindow = nullptr; +// this->dataChanged(effectiveWindow, IsActive); +// } +// } +// }); + +// QObject::connect(window, &PlasmaWindow::parentWindowChanged, q, [window, this] { +// PlasmaWindow *leader = window->parentWindow.data(); + +// // Migrate demanding attention to new leader. +// if (window->windowState.testFlag(PlasmaWindow::state::state_demands_attention)) { +// if (auto *oldLeader = transientsDemandingAttention.key(window)) { +// if (window->parentWindow != oldLeader) { +// transientsDemandingAttention.remove(oldLeader, window); +// transientsDemandingAttention.insert(leader, window); +// dataChanged(oldLeader, QVector{IsDemandingAttention}); +// dataChanged(leader, QVector{IsDemandingAttention}); +// } +// } +// } + +// if (transients.remove(window)) { +// if (leader) { // leader change. +// transients.insert(window, leader); +// } else { // lost a leader, add to regular windows list. +// Q_ASSERT(findWindow(window) == windows.end()); + +// const int count = windows.size(); +// q->beginInsertRows(QModelIndex(), count, count); +// windows.emplace_back(window); +// q->endInsertRows(); +// } +// } else if (leader) { // gained a leader, remove from regular windows list. +// auto it = findWindow(window); +// Q_ASSERT(it != windows.end()); + +// const int row = it - windows.begin(); +// q->beginRemoveRows(QModelIndex(), row, row); +// windows.erase(it); +// appDataCache.remove(window); +// lastActivated.remove(window); +// q->endRemoveRows(); +// } +// }); + +// QObject::connect(window, &PlasmaWindow::closeableChanged, q, [window, this] { +// this->dataChanged(window, IsClosable); +// }); + +// QObject::connect(window, &PlasmaWindow::movableChanged, q, [window, this] { +// this->dataChanged(window, IsMovable); +// }); + +// QObject::connect(window, &PlasmaWindow::resizableChanged, q, [window, this] { +// this->dataChanged(window, IsResizable); +// }); + +// QObject::connect(window, &PlasmaWindow::fullscreenableChanged, q, [window, this] { +// this->dataChanged(window, IsFullScreenable); +// }); + +// QObject::connect(window, &PlasmaWindow::fullscreenChanged, q, [window, this] { +// this->dataChanged(window, IsFullScreen); +// }); + +// QObject::connect(window, &PlasmaWindow::maximizeableChanged, q, [window, this] { +// this->dataChanged(window, IsMaximizable); +// }); + +// QObject::connect(window, &PlasmaWindow::maximizedChanged, q, [window, this] { +// this->dataChanged(window, IsMaximized); +// }); + +// QObject::connect(window, &PlasmaWindow::minimizeableChanged, q, [window, this] { +// this->dataChanged(window, IsMinimizable); +// }); + +// QObject::connect(window, &PlasmaWindow::minimizedChanged, q, [window, this] { +// this->dataChanged(window, IsMinimized); +// }); + +// QObject::connect(window, &PlasmaWindow::keepAboveChanged, q, [window, this] { +// this->dataChanged(window, IsKeepAbove); +// }); + +// QObject::connect(window, &PlasmaWindow::keepBelowChanged, q, [window, this] { +// this->dataChanged(window, IsKeepBelow); +// }); + +// QObject::connect(window, &PlasmaWindow::shadeableChanged, q, [window, this] { +// this->dataChanged(window, IsShadeable); +// }); + +// QObject::connect(window, &PlasmaWindow::virtualDesktopChangeableChanged, q, [window, this] { +// this->dataChanged(window, IsVirtualDesktopsChangeable); +// }); + +// QObject::connect(window, &PlasmaWindow::virtualDesktopEntered, q, [window, this] { +// this->dataChanged(window, VirtualDesktops); + +// // If the count has changed from 0, the window may no longer be on all virtual +// // desktops. +// if (window->virtualDesktops.count() > 0) { +// this->dataChanged(window, IsOnAllVirtualDesktops); +// } +// }); + +// QObject::connect(window, &PlasmaWindow::virtualDesktopLeft, q, [window, this] { +// this->dataChanged(window, VirtualDesktops); + +// // If the count has changed to 0, the window is now on all virtual desktops. +// if (window->virtualDesktops.count() == 0) { +// this->dataChanged(window, IsOnAllVirtualDesktops); +// } +// }); + +// QObject::connect(window, &PlasmaWindow::geometryChanged, q, [window, this] { +// this->dataChanged(window, QList{Geometry, ScreenGeometry}); +// }); + +// QObject::connect(window, &PlasmaWindow::demandsAttentionChanged, q, [window, this] { +// // Changes to a transient's state might change demands attention state for leader. +// if (auto *leader = transients.value(window)) { +// if (window->windowState.testFlag(PlasmaWindow::state::state_demands_attention)) { +// if (!transientsDemandingAttention.values(leader).contains(window)) { +// transientsDemandingAttention.insert(leader, window); +// this->dataChanged(leader, QVector{IsDemandingAttention}); +// } +// } else if (transientsDemandingAttention.remove(window)) { +// this->dataChanged(leader, QVector{IsDemandingAttention}); +// } +// } else { +// this->dataChanged(window, QVector{IsDemandingAttention}); +// } +// }); + +// QObject::connect(window, &PlasmaWindow::skipTaskbarChanged, q, [window, this] { +// this->dataChanged(window, SkipTaskbar); +// }); + +// QObject::connect(window, &PlasmaWindow::applicationMenuChanged, q, [window, this] { +// this->dataChanged(window, QList{ApplicationMenuServiceName, ApplicationMenuObjectPath}); +// }); + +// QObject::connect(window, &PlasmaWindow::activitiesChanged, q, [window, this] { +// this->dataChanged(window, Activities); +// }); + +// // Handle transient. +// if (PlasmaWindow *leader = window->parentWindow.data()) { +// transients.insert(window, leader); + +// // Update demands attention state for leader. +// if (window->windowState.testFlag(PlasmaWindow::state::state_demands_attention)) { +// transientsDemandingAttention.insert(leader, window); +// dataChanged(leader, QVector{IsDemandingAttention}); +// } +// } else { +// const int count = windows.size(); + +// q->beginInsertRows(QModelIndex(), count, count); + +// windows.emplace_back(window); + +// q->endInsertRows(); +// } +// } + +// const AppData &WaylandTasksModel::Private::appData(PlasmaWindow *window) +// { +// static_assert(!std::is_trivially_copy_assignable_v); +// if (auto it = appDataCache.constFind(window); it != appDataCache.constEnd()) { +// return *it; +// } + +// return *appDataCache.emplace(window, appDataFromUrl(windowUrlFromMetadata(window->appId, window->pid, rulesConfig, window->resourceName))); +// } + +// QIcon WaylandTasksModel::Private::icon(PlasmaWindow *window) +// { +// const AppData &app = appData(window); + +// if (!app.icon.isNull()) { +// return app.icon; +// } + +// appDataCache[window].icon = window->icon; + +// return window->icon; +// } + +// QString WaylandTasksModel::Private::mimeType() +// { +// // Use a unique format id to make this intentionally useless for +// // cross-process DND. +// return QStringLiteral("windowsystem/winid+") + uuid.toString(); +// } + +// QString WaylandTasksModel::Private::groupMimeType() +// { +// // Use a unique format id to make this intentionally useless for +// // cross-process DND. +// return QStringLiteral("windowsystem/multiple-winids+") + uuid.toString(); +// } + +// void WaylandTasksModel::Private::dataChanged(PlasmaWindow *window, int role) +// { +// auto it = findWindow(window); +// if (it == windows.end()) { +// return; +// } +// QModelIndex idx = q->index(it - windows.begin()); +// Q_EMIT q->dataChanged(idx, idx, QList{role}); +// } + +// void WaylandTasksModel::Private::dataChanged(PlasmaWindow *window, const QList &roles) +// { +// auto it = findWindow(window); +// if (it == windows.end()) { +// return; +// } +// QModelIndex idx = q->index(it - windows.begin()); +// Q_EMIT q->dataChanged(idx, idx, roles); +// } + +// WaylandTasksModel::WaylandTasksModel(QObject *parent) +// : AbstractWindowTasksModel(parent) +// , d(new Private(this)) +// { +// d->init(); +// } + +// WaylandTasksModel::~WaylandTasksModel() = default; + +// QVariant WaylandTasksModel::data(const QModelIndex &index, int role) const +// { +// // Note: when index is valid, its row >= 0, so casting to unsigned is safe +// if (!index.isValid() || static_cast(index.row()) >= d->windows.size()) { +// return QVariant(); +// } + +// PlasmaWindow *window = d->windows.at(index.row()).get(); + +// if (role == Qt::DisplayRole) { +// return window->title; +// } else if (role == Qt::DecorationRole) { +// return d->icon(window); +// } else if (role == AppId) { +// const QString &id = d->appData(window).id; + +// if (id.isEmpty()) { +// return window->appId; +// } else { +// return id; +// } +// } else if (role == AppName) { +// return d->appData(window).name; +// } else if (role == GenericName) { +// return d->appData(window).genericName; +// } else if (role == LauncherUrl || role == LauncherUrlWithoutIcon) { +// return d->appData(window).url; +// } else if (role == WinIdList) { +// return QVariantList{window->uuid}; +// } else if (role == MimeType) { +// return d->mimeType(); +// } else if (role == MimeData) { +// return window->uuid; +// } else if (role == IsWindow) { +// return true; +// } else if (role == IsActive) { +// return (window == d->activeWindow); +// } else if (role == IsClosable) { +// return window->windowState.testFlag(PlasmaWindow::state::state_closeable); +// } else if (role == IsMovable) { +// return window->windowState.testFlag(PlasmaWindow::state::state_movable); +// } else if (role == IsResizable) { +// return window->windowState.testFlag(PlasmaWindow::state::state_resizable); +// } else if (role == IsMaximizable) { +// return window->windowState.testFlag(PlasmaWindow::state::state_maximizable); +// } else if (role == IsMaximized) { +// return window->windowState.testFlag(PlasmaWindow::state::state_maximized); +// } else if (role == IsMinimizable) { +// return window->windowState.testFlag(PlasmaWindow::state::state_minimizable); +// } else if (role == IsMinimized || role == IsHidden) { +// return window->windowState.testFlag(PlasmaWindow::state::state_minimized); +// } else if (role == IsKeepAbove) { +// return window->windowState.testFlag(PlasmaWindow::state::state_keep_above); +// } else if (role == IsKeepBelow) { +// return window->windowState.testFlag(PlasmaWindow::state::state_keep_below); +// } else if (role == IsFullScreenable) { +// return window->windowState.testFlag(PlasmaWindow::state::state_fullscreenable); +// } else if (role == IsFullScreen) { +// return window->windowState.testFlag(PlasmaWindow::state::state_fullscreen); +// } else if (role == IsShadeable) { +// return window->windowState.testFlag(PlasmaWindow::state::state_shadeable); +// } else if (role == IsShaded) { +// return window->windowState.testFlag(PlasmaWindow::state::state_shaded); +// } else if (role == IsVirtualDesktopsChangeable) { +// return window->windowState.testFlag(PlasmaWindow::state::state_virtual_desktop_changeable); +// } else if (role == VirtualDesktops) { +// return window->virtualDesktops; +// } else if (role == IsOnAllVirtualDesktops) { +// return window->virtualDesktops.isEmpty(); +// } else if (role == Geometry) { +// return window->geometry; +// } else if (role == ScreenGeometry) { +// return screenGeometry(window->geometry.center()); +// } else if (role == Activities) { +// return window->activities; +// } else if (role == IsDemandingAttention) { +// return window->windowState.testFlag(PlasmaWindow::state::state_demands_attention) || d->transientsDemandingAttention.contains(window); +// } else if (role == SkipTaskbar) { +// return window->windowState.testFlag(PlasmaWindow::state::state_skiptaskbar) || d->appData(window).skipTaskbar; +// } else if (role == SkipPager) { +// // FIXME Implement. +// } else if (role == AppPid) { +// return window->pid; +// } else if (role == StackingOrder) { +// return d->stackingOrder.indexOf(window->uuid); +// } else if (role == LastActivated) { +// if (d->lastActivated.contains(window)) { +// return d->lastActivated.value(window); +// } +// } else if (role == ApplicationMenuObjectPath) { +// return window->applicationMenuObjectPath; +// } else if (role == ApplicationMenuServiceName) { +// return window->applicationMenuService; +// } else if (role == CanLaunchNewInstance) { +// return canLauchNewInstance(d->appData(window)); +// } + +// return AbstractTasksModel::data(index, role); +// } + +// int WaylandTasksModel::rowCount(const QModelIndex &parent) const +// { +// return parent.isValid() ? 0 : d->windows.size(); +// } + +// QModelIndex WaylandTasksModel::index(int row, int column, const QModelIndex &parent) const +// { +// return hasIndex(row, column, parent) ? createIndex(row, column, d->windows.at(row).get()) : QModelIndex(); +// } + +// void WaylandTasksModel::requestActivate(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// PlasmaWindow *window = d->windows.at(index.row()).get(); + +// // Pull forward any transient demanding attention. +// if (auto *transientDemandingAttention = d->transientsDemandingAttention.value(window)) { +// window = transientDemandingAttention; +// } else { +// // TODO Shouldn't KWin take care of that? +// // Bringing a transient to the front usually brings its parent with it +// // but focus is not handled properly. +// // TODO take into account d->lastActivation instead +// // of just taking the first one. +// while (d->transients.key(window)) { +// window = d->transients.key(window); +// } +// } + +// window->set_state(PlasmaWindow::state::state_active, PlasmaWindow::state::state_active); +// } + +// void WaylandTasksModel::requestNewInstance(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// runApp(d->appData(d->windows.at(index.row()).get())); +// } + +// void WaylandTasksModel::requestOpenUrls(const QModelIndex &index, const QList &urls) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent) || urls.isEmpty()) { +// return; +// } + +// runApp(d->appData(d->windows.at(index.row()).get()), urls); +// } + +// void WaylandTasksModel::requestClose(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// d->windows.at(index.row())->close(); +// } + +// void WaylandTasksModel::requestMove(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); + +// window->set_state(PlasmaWindow::state::state_active, PlasmaWindow::state::state_active); +// window->request_move(); +// } + +// void WaylandTasksModel::requestResize(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); + +// window->set_state(PlasmaWindow::state::state_active, PlasmaWindow::state::state_active); +// window->request_resize(); +// } + +// void WaylandTasksModel::requestToggleMinimized(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); + +// if (window->windowState & PlasmaWindow::state::state_minimized) { +// window->set_state(PlasmaWindow::state::state_minimized, 0); +// } else { +// window->set_state(PlasmaWindow::state::state_minimized, PlasmaWindow::state::state_minimized); +// } +// } + +// void WaylandTasksModel::requestToggleMaximized(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); + +// if (window->windowState & PlasmaWindow::state::state_maximized) { +// window->set_state(PlasmaWindow::state::state_maximized | PlasmaWindow::state::state_active, PlasmaWindow::state::state_active); +// } else { +// window->set_state(PlasmaWindow::state::state_maximized | PlasmaWindow::state::state_active, +// PlasmaWindow::state::state_maximized | PlasmaWindow::state::state_active); +// } +// } + +// void WaylandTasksModel::requestToggleKeepAbove(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); + +// if (window->windowState & PlasmaWindow::state::state_keep_above) { +// window->set_state(PlasmaWindow::state::state_keep_above, 0); +// } else { +// window->set_state(PlasmaWindow::state::state_keep_above, PlasmaWindow::state::state_keep_above); +// } +// } + +// void WaylandTasksModel::requestToggleKeepBelow(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } +// auto &window = d->windows.at(index.row()); + +// if (window->windowState & PlasmaWindow::state::state_keep_below) { +// window->set_state(PlasmaWindow::state::state_keep_below, 0); +// } else { +// window->set_state(PlasmaWindow::state::state_keep_below, PlasmaWindow::state::state_keep_below); +// } +// } + +// void WaylandTasksModel::requestToggleFullScreen(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); + +// if (window->windowState & PlasmaWindow::state::state_fullscreen) { +// window->set_state(PlasmaWindow::state::state_fullscreen, 0); +// } else { +// window->set_state(PlasmaWindow::state::state_fullscreen, PlasmaWindow::state::state_fullscreen); +// } +// } + +// void WaylandTasksModel::requestToggleShaded(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); + +// if (window->windowState & PlasmaWindow::state::state_shaded) { +// window->set_state(PlasmaWindow::state::state_shaded, 0); +// } else { +// window->set_state(PlasmaWindow::state::state_shaded, PlasmaWindow::state::state_shaded); +// }; +// } + +// void WaylandTasksModel::requestVirtualDesktops(const QModelIndex &index, const QVariantList &desktops) +// { +// // FIXME TODO: Lacks the "if we've requested the current desktop, force-activate +// // the window" logic from X11 version. This behavior should be in KWin rather than +// // libtm however. + +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); + +// if (desktops.isEmpty()) { +// const QStringList virtualDesktops = window->virtualDesktops; +// for (const QString &desktop : virtualDesktops) { +// window->request_leave_virtual_desktop(desktop); +// } +// } else { +// const QStringList &now = window->virtualDesktops; +// QStringList next; + +// for (const QVariant &desktop : desktops) { +// const QString &desktopId = desktop.toString(); + +// if (!desktopId.isEmpty()) { +// next << desktopId; + +// if (!now.contains(desktopId)) { +// window->request_enter_virtual_desktop(desktopId); +// } +// } +// } + +// for (const QString &desktop : now) { +// if (!next.contains(desktop)) { +// window->request_leave_virtual_desktop(desktop); +// } +// } +// } +// } + +// void WaylandTasksModel::requestNewVirtualDesktop(const QModelIndex &index) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// d->windows.at(index.row())->request_enter_new_virtual_desktop(); +// } + +// void WaylandTasksModel::requestActivities(const QModelIndex &index, const QStringList &activities) +// { +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// auto &window = d->windows.at(index.row()); +// const auto newActivities = QSet(activities.begin(), activities.end()); +// const auto plasmaActivities = window->activities; +// const auto oldActivities = QSet(plasmaActivities.begin(), plasmaActivities.end()); + +// const auto activitiesToAdd = newActivities - oldActivities; +// for (const auto &activity : activitiesToAdd) { +// window->request_enter_activity(activity); +// } + +// const auto activitiesToRemove = oldActivities - newActivities; +// for (const auto &activity : activitiesToRemove) { +// window->request_leave_activity(activity); +// } +// } + +// void WaylandTasksModel::requestPublishDelegateGeometry(const QModelIndex &index, const QRect &geometry, QObject *delegate) +// { +// /* +// FIXME: This introduces the dependency on Qt::Quick. I might prefer +// reversing this and publishing the window pointer through the model, +// then calling PlasmaWindow::setMinimizeGeometry in the applet backend, +// rather than hand delegate items into the lib, keeping the lib more UI- +// agnostic. +// */ + +// Q_UNUSED(geometry) + +// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { +// return; +// } + +// const QQuickItem *item = qobject_cast(delegate); + +// if (!item || !item->parentItem()) { +// return; +// } + +// QWindow *itemWindow = item->window(); + +// if (!itemWindow) { +// return; +// } + +// auto waylandWindow = itemWindow->nativeInterface(); + +// if (!waylandWindow || !waylandWindow->surface()) { +// return; +// } + +// QRect rect(item->x(), item->y(), item->width(), item->height()); +// rect.moveTopLeft(item->parentItem()->mapToScene(rect.topLeft()).toPoint()); + +// auto &window = d->windows.at(index.row()); + +// window->set_minimized_geometry(waylandWindow->surface(), rect.x(), rect.y(), rect.width(), rect.height()); +// } + +// QUuid WaylandTasksModel::winIdFromMimeData(const QMimeData *mimeData, bool *ok) +// { +// Q_ASSERT(mimeData); + +// if (ok) { +// *ok = false; +// } + +// if (!mimeData->hasFormat(Private::mimeType())) { +// return {}; +// } + +// QUuid id(mimeData->data(Private::mimeType())); +// *ok = !id.isNull(); + +// return id; +// } + +// QList WaylandTasksModel::winIdsFromMimeData(const QMimeData *mimeData, bool *ok) +// { +// Q_ASSERT(mimeData); +// QList ids; + +// if (ok) { +// *ok = false; +// } + +// if (!mimeData->hasFormat(Private::groupMimeType())) { +// // Try to extract single window id. +// bool singularOk; +// QUuid id = winIdFromMimeData(mimeData, &singularOk); + +// if (ok) { +// *ok = singularOk; +// } + +// if (singularOk) { +// ids << id; +// } + +// return ids; +// } + +// // FIXME: Extracting multiple winids is still unimplemented; +// // TaskGroupingProxy::data(..., ::MimeData) can't produce +// // a payload with them anyways. + +// return ids; +// } + +LXQtTaskBarPlasmaWindow::LXQtTaskBarPlasmaWindow(const QString &uuid, ::org_kde_plasma_window *id) + : org_kde_plasma_window(id) + , uuid(uuid) +{ +} + +/* + * LXQtTaskBarPlasmaWindow + */ + +LXQtTaskBarPlasmaWindow::~LXQtTaskBarPlasmaWindow() +{ + destroy(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_unmapped() +{ + wasUnmapped = true; + Q_EMIT unmapped(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_title_changed(const QString &title) +{ + if(this->title == title) + return; + this->title = title; + Q_EMIT titleChanged(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_app_id_changed(const QString &app_id) +{ + if(appId == app_id) + return; + appId = app_id; + Q_EMIT appIdChanged(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_icon_changed() +{ + int pipeFds[2]; + if (pipe2(pipeFds, O_CLOEXEC) != 0) { + qWarning() << "TaskManager: failed creating pipe"; + return; + } + get_icon(pipeFds[1]); + ::close(pipeFds[1]); + auto readIcon = [uuid = uuid](int fd) { + auto closeGuard = qScopeGuard([fd]() { + ::close(fd); + }); + pollfd pollFd; + pollFd.fd = fd; + pollFd.events = POLLIN; + QByteArray data; + while (true) { + int ready = poll(&pollFd, 1, 1000); + if (ready < 0 && errno != EINTR) { + qWarning() << "TaskManager: polling for icon of window" << uuid << "failed"; + return QIcon(); + } else if (ready == 0) { + qWarning() << "TaskManager: time out polling for icon of window" << uuid; + return QIcon(); + } else { + char buffer[4096]; + int n = read(fd, buffer, sizeof(buffer)); + if (n < 0) { + qWarning() << "TaskManager: error reading icon of window" << uuid; + return QIcon(); + } else if (n > 0) { + data.append(buffer, n); + } else { + QIcon icon; + QDataStream ds(data); + ds >> icon; + return icon; + } + } + } + }; + QFuture future = QtConcurrent::run(readIcon, pipeFds[0]); + auto watcher = new QFutureWatcher(); + watcher->setFuture(future); + connect(watcher, &QFutureWatcher::finished, this, [this, watcher] { + icon = watcher->future().result(); + Q_EMIT iconChanged(); + }); + connect(watcher, &QFutureWatcher::finished, watcher, &QObject::deleteLater); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_themed_icon_name_changed(const QString &name) +{ + icon = QIcon::fromTheme(name); + Q_EMIT iconChanged(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_state_changed(uint32_t flags) +{ + auto diff = windowState ^ flags; + if (diff & state::state_active) { + windowState.setFlag(state::state_active, flags & state::state_active); + Q_EMIT activeChanged(); + } + if (diff & state::state_minimized) { + windowState.setFlag(state::state_minimized, flags & state::state_minimized); + Q_EMIT minimizedChanged(); + } + if (diff & state::state_maximized) { + windowState.setFlag(state::state_maximized, flags & state::state_maximized); + Q_EMIT maximizedChanged(); + } + if (diff & state::state_fullscreen) { + windowState.setFlag(state::state_fullscreen, flags & state::state_fullscreen); + Q_EMIT fullscreenChanged(); + } + if (diff & state::state_keep_above) { + windowState.setFlag(state::state_keep_above, flags & state::state_keep_above); + Q_EMIT keepAboveChanged(); + } + if (diff & state::state_keep_below) { + windowState.setFlag(state::state_keep_below, flags & state::state_keep_below); + Q_EMIT keepBelowChanged(); + } + if (diff & state::state_on_all_desktops) { + windowState.setFlag(state::state_on_all_desktops, flags & state::state_on_all_desktops); + Q_EMIT onAllDesktopsChanged(); + } + if (diff & state::state_demands_attention) { + windowState.setFlag(state::state_demands_attention, flags & state::state_demands_attention); + Q_EMIT demandsAttentionChanged(); + } + if (diff & state::state_closeable) { + windowState.setFlag(state::state_closeable, flags & state::state_closeable); + Q_EMIT closeableChanged(); + } + if (diff & state::state_minimizable) { + windowState.setFlag(state::state_minimizable, flags & state::state_minimizable); + Q_EMIT minimizeableChanged(); + } + if (diff & state::state_maximizable) { + windowState.setFlag(state::state_maximizable, flags & state::state_maximizable); + Q_EMIT maximizeableChanged(); + } + if (diff & state::state_fullscreenable) { + windowState.setFlag(state::state_fullscreenable, flags & state::state_fullscreenable); + Q_EMIT fullscreenableChanged(); + } + if (diff & state::state_skiptaskbar) { + windowState.setFlag(state::state_skiptaskbar, flags & state::state_skiptaskbar); + Q_EMIT skipTaskbarChanged(); + } + if (diff & state::state_shadeable) { + windowState.setFlag(state::state_shadeable, flags & state::state_shadeable); + Q_EMIT shadeableChanged(); + } + if (diff & state::state_shaded) { + windowState.setFlag(state::state_shaded, flags & state::state_shaded); + Q_EMIT shadedChanged(); + } + if (diff & state::state_movable) { + windowState.setFlag(state::state_movable, flags & state::state_movable); + Q_EMIT movableChanged(); + } + if (diff & state::state_resizable) { + windowState.setFlag(state::state_resizable, flags & state::state_resizable); + Q_EMIT resizableChanged(); + } + if (diff & state::state_virtual_desktop_changeable) { + windowState.setFlag(state::state_virtual_desktop_changeable, flags & state::state_virtual_desktop_changeable); + Q_EMIT virtualDesktopChangeableChanged(); + } + if (diff & state::state_skipswitcher) { + windowState.setFlag(state::state_skipswitcher, flags & state::state_skipswitcher); + Q_EMIT skipSwitcherChanged(); + } +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_virtual_desktop_entered(const QString &id) +{ + virtualDesktops.push_back(id); + Q_EMIT virtualDesktopEntered(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_virtual_desktop_left(const QString &id) +{ + virtualDesktops.removeAll(id); + Q_EMIT virtualDesktopLeft(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_geometry(int32_t x, int32_t y, uint32_t width, uint32_t height) +{ + geometry = QRect(x, y, width, height); + Q_EMIT geometryChanged(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_application_menu(const QString &service_name, const QString &object_path) +{ + applicationMenuService = service_name; + applicationMenuObjectPath = object_path; + Q_EMIT applicationMenuChanged(); +} + +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_activity_entered(const QString &id) +{ + activities.push_back(id); + Q_EMIT activitiesChanged(); +} +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_activity_left(const QString &id) +{ + activities.removeAll(id); + Q_EMIT activitiesChanged(); +} +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_pid_changed(uint32_t pid) +{ + this->pid = pid; +} +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_resource_name_changed(const QString &resource_name) +{ + resourceName = resource_name; +} +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_parent_window(::org_kde_plasma_window *parent) +{ + LXQtTaskBarPlasmaWindow *parentWindow = nullptr; + if (parent) { + parentWindow = dynamic_cast(LXQtTaskBarPlasmaWindow::fromObject(parent)); + } + setParentWindow(parentWindow); +} +void LXQtTaskBarPlasmaWindow::org_kde_plasma_window_initial_state() +{ + Q_EMIT initialStateDone(); +} + +void LXQtTaskBarPlasmaWindow::setParentWindow(LXQtTaskBarPlasmaWindow *parent) +{ + const auto old = parentWindow; + QObject::disconnect(parentWindowUnmappedConnection); + + if (parent && !parent->wasUnmapped) { + parentWindow = QPointer(parent); + parentWindowUnmappedConnection = QObject::connect(parent, &LXQtTaskBarPlasmaWindow::unmapped, this, [this] { + setParentWindow(nullptr); + }); + } else { + parentWindow = QPointer(); + parentWindowUnmappedConnection = QMetaObject::Connection(); + } + + if (parentWindow.data() != old.data()) { + Q_EMIT parentWindowChanged(); + } +} + +/* + * LXQtTaskBarPlasmaWindowManagment + */ + +LXQtTaskBarPlasmaWindowManagment::LXQtTaskBarPlasmaWindowManagment() + : QWaylandClientExtensionTemplate(version) +{ + connect(this, &QWaylandClientExtension::activeChanged, this, [this] { + if (!isActive()) { + wl_proxy_destroy(reinterpret_cast(object())); + } + }); +} + +LXQtTaskBarPlasmaWindowManagment::~LXQtTaskBarPlasmaWindowManagment() +{ + if (isActive()) { + wl_proxy_destroy(reinterpret_cast(object())); + } +} + +void LXQtTaskBarPlasmaWindowManagment::org_kde_plasma_window_management_window_with_uuid(uint32_t id, const QString &uuid) +{ + Q_UNUSED(id) + Q_EMIT windowCreated(new LXQtTaskBarPlasmaWindow(uuid, get_window_by_uuid(uuid))); +} +void LXQtTaskBarPlasmaWindowManagment::org_kde_plasma_window_management_stacking_order_uuid_changed(const QString &uuids) +{ + Q_EMIT stackingOrderChanged(uuids); +} diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h new file mode 100644 index 000000000..78c75693d --- /dev/null +++ b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h @@ -0,0 +1,154 @@ +#ifndef LXQTTASKBARPLASMAWINDOWMANAGMENT_H +#define LXQTTASKBARPLASMAWINDOWMANAGMENT_H + +#include +#include +#include + +#include "qwayland-plasma-window-management.h" + +typedef quintptr WId; + +class LXQtTaskBarPlasmaWindowManagment; + +class LXQtTaskBarPlasmaWindow : public QObject, + public QtWayland::org_kde_plasma_window +{ + Q_OBJECT +public: + LXQtTaskBarPlasmaWindow(const QString &uuid, ::org_kde_plasma_window *id); + ~LXQtTaskBarPlasmaWindow(); + + inline WId getWindowId() const { return reinterpret_cast(this); } + + using state = QtWayland::org_kde_plasma_window_management::state; + const QString uuid; + QString title; + QString appId; + QIcon icon; + QFlags windowState; + QList virtualDesktops; + QRect geometry; + QString applicationMenuService; + QString applicationMenuObjectPath; + QList activities; + quint32 pid; + QString resourceName; + QPointer parentWindow; + bool wasUnmapped = false; + bool acceptedInTaskBar = false; + +Q_SIGNALS: + void unmapped(); + void titleChanged(); + void appIdChanged(); + void iconChanged(); + void activeChanged(); + void minimizedChanged(); + void maximizedChanged(); + void fullscreenChanged(); + void keepAboveChanged(); + void keepBelowChanged(); + void onAllDesktopsChanged(); + void demandsAttentionChanged(); + void closeableChanged(); + void minimizeableChanged(); + void maximizeableChanged(); + void fullscreenableChanged(); + void skiptaskbarChanged(); + void shadeableChanged(); + void shadedChanged(); + void movableChanged(); + void resizableChanged(); + void virtualDesktopChangeableChanged(); + void skipSwitcherChanged(); + void virtualDesktopEntered(); + void virtualDesktopLeft(); + void geometryChanged(); + void skipTaskbarChanged(); + void applicationMenuChanged(); + void activitiesChanged(); + void parentWindowChanged(); + void initialStateDone(); + +protected: + void org_kde_plasma_window_unmapped() override; + void org_kde_plasma_window_title_changed(const QString &title) override; + void org_kde_plasma_window_app_id_changed(const QString &app_id) override; + void org_kde_plasma_window_icon_changed() override; + void org_kde_plasma_window_themed_icon_name_changed(const QString &name) override; + void org_kde_plasma_window_state_changed(uint32_t flags) override; + void org_kde_plasma_window_virtual_desktop_entered(const QString &id) override; + + void org_kde_plasma_window_virtual_desktop_left(const QString &id) override; + void org_kde_plasma_window_geometry(int32_t x, int32_t y, uint32_t width, uint32_t height) override; + void org_kde_plasma_window_application_menu(const QString &service_name, const QString &object_path) override; + void org_kde_plasma_window_activity_entered(const QString &id) override; + void org_kde_plasma_window_activity_left(const QString &id) override; + void org_kde_plasma_window_pid_changed(uint32_t pid) override; + void org_kde_plasma_window_resource_name_changed(const QString &resource_name) override; + void org_kde_plasma_window_parent_window(::org_kde_plasma_window *parent) override; + void org_kde_plasma_window_initial_state() override; + +private: + void setParentWindow(LXQtTaskBarPlasmaWindow *parent); + + QMetaObject::Connection parentWindowUnmappedConnection; +}; + +class LXQtTaskBarPlasmaWindowManagment : public QWaylandClientExtensionTemplate, + public QtWayland::org_kde_plasma_window_management +{ + Q_OBJECT +public: + static constexpr int version = 16; + LXQtTaskBarPlasmaWindowManagment(); + ~LXQtTaskBarPlasmaWindowManagment(); + + void org_kde_plasma_window_management_window_with_uuid(uint32_t id, const QString &uuid) override; + void org_kde_plasma_window_management_stacking_order_uuid_changed(const QString &uuids) override; + +Q_SIGNALS: + void windowCreated(LXQtTaskBarPlasmaWindow *window); + void stackingOrderChanged(const QString &uuids); +}; + +// class Q_DECL_HIDDEN WaylandTasksModel::Private +// { +// public: +// Private(WaylandTasksModel *q); +// QHash appDataCache; +// QHash lastActivated; +// PlasmaWindow *activeWindow = nullptr; +// std::vector> windows; +// // key=transient child, value=leader +// QHash transients; +// // key=leader, values=transient children +// QMultiHash transientsDemandingAttention; +// std::unique_ptr windowManagement; +// KSharedConfig::Ptr rulesConfig; +// KDirWatch *configWatcher = nullptr; +// VirtualDesktopInfo *virtualDesktopInfo = nullptr; +// static QUuid uuid; +// QList stackingOrder; + +// void init(); +// void initWayland(); +// auto findWindow(PlasmaWindow *window) const; +// void addWindow(PlasmaWindow *window); + +// const AppData &appData(PlasmaWindow *window); + +// QIcon icon(PlasmaWindow *window); + +// static QString mimeType(); +// static QString groupMimeType(); + +// void dataChanged(PlasmaWindow *window, int role); +// void dataChanged(PlasmaWindow *window, const QList &roles); + +// private: +// WaylandTasksModel *q; +// }; + +#endif // LXQTTASKBARPLASMAWINDOWMANAGMENT_H diff --git a/panel/backends/wayland/protocols/plasma-window-management.xml b/panel/backends/wayland/protocols/plasma-window-management.xml new file mode 100644 index 000000000..5990ebfda --- /dev/null +++ b/panel/backends/wayland/protocols/plasma-window-management.xml @@ -0,0 +1,425 @@ + + + + + + + This interface manages application windows. + It provides requests to show and hide the desktop and emits + an event every time a window is created so that the client can + use it to manage the window. + + Only one client can bind this interface at a time. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tell the compositor to show/hide the desktop. + + + + + + Deprecated: use get_window_by_uuid + + + + + + + + + + + + This event will be sent whenever the show desktop mode changes. E.g. when it is entered + or left. + + On binding the interface the current state is sent. + + + + + + + This event will be sent immediately after a window is mapped. + + + + + + + This event will be sent when stacking order changed and on bind + + + + + + + This event will be sent when stacking order changed and on bind + + + + + + + This event will be sent immediately after a window is mapped. + + + + + + + + + Manages and control an application window. + + Only one client can bind this interface at a time. + + + + + Set window state. + + Values for state argument are described by org_kde_plasma_window_management.state + and can be used together in a bitfield. The flags bitfield describes which flags are + supposed to be set, the state bitfield the value for the set flags + + + + + + + + Deprecated: use enter_virtual_desktop + Maps the window to a different virtual desktop. + + To show the window on all virtual desktops, call the + org_kde_plasma_window.set_state request and specify a on_all_desktops + state in the bitfield. + + + + + + + Sets the geometry of the taskbar entry for this window. + The geometry is relative to a panel in particular. + + + + + + + + + + + Remove the task geometry information for a particular panel. + + + + + + + + + Close this window. + + + + + + Request an interactive move for this window. + + + + + + Request an interactive resize for this window. + + + + + + Removes the resource bound for this org_kde_plasma_window. + + + + + + The compositor will write the window icon into the provided file descriptor. + The data is a serialized QIcon with QDataStream. + + + + + + + This event will be sent as soon as the window title is changed. + + + + + + + This event will be sent as soon as the application + identifier is changed. + + + + + + + This event will be sent as soon as the window state changes. + + Values for state argument are described by org_kde_plasma_window_management.state. + + + + + + + DEPRECATED: use virtual_desktop_entered and virtual_desktop_left instead + This event will be sent when a window is moved to another + virtual desktop. + + It is not sent if it becomes visible on all virtual desktops though. + + + + + + + This event will be sent whenever the themed icon name changes. May be null. + + + + + + + This event will be sent immediately after the window is closed + and its surface is unmapped. + + + + + + This event will be sent immediately after all initial state been sent to the client. + If the Plasma window is already unmapped, the unmapped event will be sent before the + initial_state event. + + + + + + This event will be sent whenever the parent window of this org_kde_plasma_window changes. + The passed parent is another org_kde_plasma_window and this org_kde_plasma_window is a + transient window to the parent window. If the parent argument is null, this + org_kde_plasma_window does not have a parent window. + + + + + + + This event will be sent whenever the window geometry of this org_kde_plasma_window changes. + The coordinates are in absolute coordinates of the windowing system. + + + + + + + + + + This event will be sent whenever the icon of the window changes, but there is no themed + icon name. Common examples are Xwayland windows which have a pixmap based icon. + + The client can request the icon using get_icon. + + + + + + This event will be sent when the compositor has set the process id this window belongs to. + This should be set once before the initial_state is sent. + + + + + + + + + + Make the window enter a virtual desktop. A window can enter more + than one virtual desktop. if the id is empty or invalid, no action will be performed. + + + + + + RFC: do this with an empty id to request_enter_virtual_desktop? + Make the window enter a new virtual desktop. If the server consents the request, + it will create a new virtual desktop and assign the window to it. + + + + + + Make the window exit a virtual desktop. If it exits all desktops it will be considered on all of them. + + + + + + + This event will be sent when the window has entered a new virtual desktop. The window can be on more than one desktop, or none: then is considered on all of them. + + + + + + + This event will be sent when the window left a virtual desktop. If the window leaves all desktops, it can be considered on all. + If the window gets manually added on all desktops, the server has to send virtual_desktop_left for every previous desktop it was in for the window to be really considered on all desktops. + + + + + + + + + This event will be sent after the application menu + for the window has changed. + + + + + + + + Make the window enter an activity. A window can enter more activity. If the id is empty or invalid, no action will be performed. + + + + + + + Make the window exit a an activity. If it exits all activities it will be considered on all of them. + + + + + + + This event will be sent when the window has entered an activity. The window can be on more than one activity, or none: then is considered on all of them. + + + + + + + This event will be sent when the window left an activity. If the window leaves all activities, it will be considered on all. + If the window gets manually added on all activities, the server has to send activity_left for every previous activity it was in for the window to be really considered on all activities. + + + + + + + Requests this window to be displayed in a specific output. + + + + + + + This event will be sent when the X11 resource name of the window has changed. + This is only set for XWayland windows. + + + + + + + + The activation manager interface provides a way to get notified + when an application is about to be activated. + + + + + Destroy the activation manager object. The activation objects introduced + by this manager object will be unaffected. + + + + + + Will be issued when an app is set to be activated. It offers + an instance of org_kde_plasma_activation that will tell us the app_id + and the extent of the activation. + + + + + + + + + Notify the compositor that the org_kde_plasma_activation object will no + longer be used. + + + + + + + + + + + + + diff --git a/panel/lxqtpanelapplication.cpp b/panel/lxqtpanelapplication.cpp index c3b666620..507163221 100644 --- a/panel/lxqtpanelapplication.cpp +++ b/panel/lxqtpanelapplication.cpp @@ -40,11 +40,14 @@ #include "backends/lxqttaskbardummybackend.h" #include "backends/xcb/lxqttaskbarbackend_x11.h" +#include "backends/wayland/lxqttaskbarbackendwayland.h" ILXQtTaskbarAbstractBackend *createWMBackend() { if(qGuiApp->nativeInterface()) return new LXQtTaskbarX11Backend; + else if(qGuiApp->nativeInterface()) + return new LXQtTaskbarWaylandBackend; qWarning() << "\n" << "ERROR: Could not create a backend for window managment operations.\n" From 3e242e9ec04ad1d2823c0993f114363890f68228 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 17:12:45 +0100 Subject: [PATCH 147/165] LXQtTaskBarPlasmaWindowManagment: implement showDesktop() --- .../wayland/lxqttaskbarbackendwayland.cpp | 17 +++++++++++++++++ .../wayland/lxqttaskbarbackendwayland.h | 4 ++++ .../lxqttaskbarplasmawindowmanagment.cpp | 5 +++++ .../wayland/lxqttaskbarplasmawindowmanagment.h | 7 +++++++ 4 files changed, 33 insertions(+) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index a2a415504..d85ec99d8 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -372,6 +372,23 @@ bool LXQtTaskbarWaylandBackend::isAreaOverlapped(const QRect &area) const return false; } +bool LXQtTaskbarWaylandBackend::isShowingDesktop() const +{ + return m_managment->isShowingDesktop(); +} + +bool LXQtTaskbarWaylandBackend::showDesktop(bool value) +{ + enum LXQtTaskBarPlasmaWindowManagment::show_desktop flag_; + if(value) + flag_ = LXQtTaskBarPlasmaWindowManagment::show_desktop::show_desktop_enabled; + else + flag_ = LXQtTaskBarPlasmaWindowManagment::show_desktop::show_desktop_disabled; + + m_managment->show_desktop(flag_); + return true; +} + void LXQtTaskbarWaylandBackend::addWindow(LXQtTaskBarPlasmaWindow *window) { if (findWindow(windows, window) != windows.end() || transients.contains(window)) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.h b/panel/backends/wayland/lxqttaskbarbackendwayland.h index 553a7e585..77d857eb9 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.h +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.h @@ -65,6 +65,10 @@ class LXQtTaskbarWaylandBackend : public ILXQtTaskbarAbstractBackend // Panel internal virtual bool isAreaOverlapped(const QRect& area) const override; + // Show Destop + virtual bool isShowingDesktop() const override; + virtual bool showDesktop(bool value) override; + private: void addWindow(LXQtTaskBarPlasmaWindow *window); bool acceptWindow(LXQtTaskBarPlasmaWindow *window) const; diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp index 0a282eadd..3b765d3b3 100644 --- a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp +++ b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp @@ -1135,6 +1135,11 @@ LXQtTaskBarPlasmaWindowManagment::~LXQtTaskBarPlasmaWindowManagment() } } +void LXQtTaskBarPlasmaWindowManagment::org_kde_plasma_window_management_show_desktop_changed(uint32_t state) +{ + m_isShowingDesktop = (state == show_desktop::show_desktop_enabled); +} + void LXQtTaskBarPlasmaWindowManagment::org_kde_plasma_window_management_window_with_uuid(uint32_t id, const QString &uuid) { Q_UNUSED(id) diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h index 78c75693d..c21b57b3c 100644 --- a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h +++ b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h @@ -105,12 +105,19 @@ class LXQtTaskBarPlasmaWindowManagment : public QWaylandClientExtensionTemplate< LXQtTaskBarPlasmaWindowManagment(); ~LXQtTaskBarPlasmaWindowManagment(); + inline bool isShowingDesktop() const { return m_isShowingDesktop; } + +protected: + void org_kde_plasma_window_management_show_desktop_changed(uint32_t state) override; void org_kde_plasma_window_management_window_with_uuid(uint32_t id, const QString &uuid) override; void org_kde_plasma_window_management_stacking_order_uuid_changed(const QString &uuids) override; Q_SIGNALS: void windowCreated(LXQtTaskBarPlasmaWindow *window); void stackingOrderChanged(const QString &uuids); + +private: + bool m_isShowingDesktop = false; }; // class Q_DECL_HIDDEN WaylandTasksModel::Private From 622d196feb608017fc0f1ae3d359bef51edff4ce Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 11:17:24 +0100 Subject: [PATCH 148/165] LXQtTaskbarWaylandBackend: do not show transient windows --- panel/backends/wayland/lxqttaskbarbackendwayland.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index d85ec99d8..5def725ef 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -614,6 +614,9 @@ bool LXQtTaskbarWaylandBackend::acceptWindow(LXQtTaskBarPlasmaWindow *window) co if(window->windowState.testFlag(LXQtTaskBarPlasmaWindow::state::state_skiptaskbar)) return false; + if(transients.contains(window)) + return false; + return true; } From ceef8d115539a69d71e3f2d3b2a4c6256e5b5425 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Tue, 20 Feb 2024 10:04:23 +0100 Subject: [PATCH 149/165] LXQtTaskBarPlasmaWindowManagment: fix destructor TODO TODO: is this correct? Seems to call wl_proxy_destroy underneath --- panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp | 4 ++-- panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp index 3b765d3b3..f813181ed 100644 --- a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp +++ b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp @@ -1123,7 +1123,7 @@ LXQtTaskBarPlasmaWindowManagment::LXQtTaskBarPlasmaWindowManagment() { connect(this, &QWaylandClientExtension::activeChanged, this, [this] { if (!isActive()) { - wl_proxy_destroy(reinterpret_cast(object())); + org_kde_plasma_window_management_destroy(object()); } }); } @@ -1131,7 +1131,7 @@ LXQtTaskBarPlasmaWindowManagment::LXQtTaskBarPlasmaWindowManagment() LXQtTaskBarPlasmaWindowManagment::~LXQtTaskBarPlasmaWindowManagment() { if (isActive()) { - wl_proxy_destroy(reinterpret_cast(object())); + org_kde_plasma_window_management_destroy(object()); } } diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h index c21b57b3c..976ac53e2 100644 --- a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h +++ b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h @@ -102,6 +102,7 @@ class LXQtTaskBarPlasmaWindowManagment : public QWaylandClientExtensionTemplate< Q_OBJECT public: static constexpr int version = 16; + LXQtTaskBarPlasmaWindowManagment(); ~LXQtTaskBarPlasmaWindowManagment(); From 165f5a9911025cd775871b1e2cf319e81bf37670 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Wed, 21 Feb 2024 11:52:48 +0100 Subject: [PATCH 150/165] LXQtPanel: basic virtual desktop support on Plasma Wayland --- panel/CMakeLists.txt | 7 + .../wayland/lxqtplasmavirtualdesktop.cpp | 231 ++++++++++++++++++ .../wayland/lxqtplasmavirtualdesktop.h | 87 +++++++ .../wayland/lxqttaskbarbackendwayland.cpp | 18 +- .../wayland/lxqttaskbarbackendwayland.h | 3 + .../org-kde-plasma-virtual-desktop.xml | 110 +++++++++ 6 files changed, 454 insertions(+), 2 deletions(-) create mode 100644 panel/backends/wayland/lxqtplasmavirtualdesktop.cpp create mode 100644 panel/backends/wayland/lxqtplasmavirtualdesktop.h create mode 100644 panel/backends/wayland/protocols/org-kde-plasma-virtual-desktop.xml diff --git a/panel/CMakeLists.txt b/panel/CMakeLists.txt index 767b459e2..18cd58b01 100644 --- a/panel/CMakeLists.txt +++ b/panel/CMakeLists.txt @@ -35,6 +35,7 @@ set(PRIV_HEADERS backends/wayland/lxqttaskbarbackendwayland.h backends/wayland/lxqttaskbarplasmawindowmanagment.h + backends/wayland/lxqtplasmavirtualdesktop.h ) # using LXQt namespace in the public headers. @@ -74,6 +75,7 @@ set(SOURCES backends/wayland/lxqttaskbarbackendwayland.cpp backends/wayland/lxqttaskbarplasmawindowmanagment.cpp + backends/wayland/lxqtplasmavirtualdesktop.cpp ) set(UI @@ -134,6 +136,11 @@ FILES ${CMAKE_CURRENT_SOURCE_DIR}/backends/wayland/protocols/plasma-window-management.xml ) +qt6_generate_wayland_protocol_client_sources(${PROJECT} +FILES + ${CMAKE_CURRENT_SOURCE_DIR}/backends/wayland/protocols/org-kde-plasma-virtual-desktop.xml +) + target_link_libraries(${PROJECT} ${LIBRARIES} ${QTX_LIBRARIES} diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp new file mode 100644 index 000000000..a3d987f06 --- /dev/null +++ b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp @@ -0,0 +1,231 @@ +#include "lxqtplasmavirtualdesktop.h" + +#include + +LXQtPlasmaVirtualDesktop::LXQtPlasmaVirtualDesktop(::org_kde_plasma_virtual_desktop *object, const QString &id) + : org_kde_plasma_virtual_desktop(object) + , id(id) +{ +} + +LXQtPlasmaVirtualDesktop::~LXQtPlasmaVirtualDesktop() +{ + wl_proxy_destroy(reinterpret_cast(object())); +} + +void LXQtPlasmaVirtualDesktop::org_kde_plasma_virtual_desktop_name(const QString &name) +{ + this->name = name; +} + +void LXQtPlasmaVirtualDesktop::org_kde_plasma_virtual_desktop_done() +{ + Q_EMIT done(); +} + +void LXQtPlasmaVirtualDesktop::org_kde_plasma_virtual_desktop_activated() +{ + Q_EMIT activated(); +} + +LXQtPlasmaVirtualDesktopManagment::LXQtPlasmaVirtualDesktopManagment() + : QWaylandClientExtensionTemplate(version) +{ + connect(this, &QWaylandClientExtension::activeChanged, this, [this] { + if (!isActive()) { + org_kde_plasma_virtual_desktop_management_destroy(object()); + } + }); +} + +LXQtPlasmaVirtualDesktopManagment::~LXQtPlasmaVirtualDesktopManagment() +{ + if (isActive()) { + org_kde_plasma_virtual_desktop_management_destroy(object()); + } +} + +void LXQtPlasmaVirtualDesktopManagment::org_kde_plasma_virtual_desktop_management_desktop_created(const QString &desktop_id, uint32_t position) +{ + emit desktopCreated(desktop_id, position); +} + +void LXQtPlasmaVirtualDesktopManagment::org_kde_plasma_virtual_desktop_management_desktop_removed(const QString &desktop_id) +{ + emit desktopRemoved(desktop_id); +} + +void LXQtPlasmaVirtualDesktopManagment::org_kde_plasma_virtual_desktop_management_rows(uint32_t rows) +{ + emit rowsChanged(rows); +} + +LXQtPlasmaWaylandWorkspaceInfo::LXQtPlasmaWaylandWorkspaceInfo() +{ + init(); +} + +LXQtPlasmaWaylandWorkspaceInfo::VirtualDesktopsIterator LXQtPlasmaWaylandWorkspaceInfo::findDesktop(const QString &id) const +{ + return std::find_if(virtualDesktops.begin(), virtualDesktops.end(), + [&id](const std::unique_ptr &desktop) { + return desktop->id == id; + }); +} + +void LXQtPlasmaWaylandWorkspaceInfo::init() +{ + virtualDesktopManagement = std::make_unique(); + + connect(virtualDesktopManagement.get(), &LXQtPlasmaVirtualDesktopManagment::activeChanged, this, [this] { + if (!virtualDesktopManagement->isActive()) { + rows = 0; + virtualDesktops.clear(); + currentVirtualDesktop.clear(); + Q_EMIT currentDesktopChanged(); + Q_EMIT numberOfDesktopsChanged(); + Q_EMIT navigationWrappingAroundChanged(); + Q_EMIT desktopIdsChanged(); + Q_EMIT desktopNamesChanged(); + Q_EMIT desktopLayoutRowsChanged(); + } + }); + + connect(virtualDesktopManagement.get(), &LXQtPlasmaVirtualDesktopManagment::desktopCreated, + this, &LXQtPlasmaWaylandWorkspaceInfo::addDesktop); + + connect(virtualDesktopManagement.get(), &LXQtPlasmaVirtualDesktopManagment::desktopRemoved, this, [this](const QString &id) { + + + std::remove_if(virtualDesktops.begin(), virtualDesktops.end(), + [id](const std::unique_ptr &desktop) + { + return desktop->id == id; + }); + + Q_EMIT numberOfDesktopsChanged(); + Q_EMIT desktopIdsChanged(); + Q_EMIT desktopNamesChanged(); + + if (currentVirtualDesktop == id) { + currentVirtualDesktop.clear(); + Q_EMIT currentDesktopChanged(); + } + }); + + connect(virtualDesktopManagement.get(), &LXQtPlasmaVirtualDesktopManagment::rowsChanged, this, [this](quint32 rows) { + this->rows = rows; + Q_EMIT desktopLayoutRowsChanged(); + }); +} + +void LXQtPlasmaWaylandWorkspaceInfo::addDesktop(const QString &id, quint32 position) +{ + if (findDesktop(id) != virtualDesktops.end()) { + return; + } + + auto desktop = std::make_unique(virtualDesktopManagement->get_virtual_desktop(id), id); + + connect(desktop.get(), &LXQtPlasmaVirtualDesktop::activated, this, [id, this]() { + currentVirtualDesktop = id; + Q_EMIT currentDesktopChanged(); + }); + + connect(desktop.get(), &LXQtPlasmaVirtualDesktop::done, this, [this]() { + Q_EMIT desktopNamesChanged(); + }); + + virtualDesktops.insert(std::next(virtualDesktops.begin(), position), std::move(desktop)); + + Q_EMIT numberOfDesktopsChanged(); + Q_EMIT desktopIdsChanged(); + Q_EMIT desktopNamesChanged(); +} + +QVariant LXQtPlasmaWaylandWorkspaceInfo::currentDesktop() const +{ + return currentVirtualDesktop; +} + +int LXQtPlasmaWaylandWorkspaceInfo::numberOfDesktops() const +{ + return virtualDesktops.size(); +} + +quint32 LXQtPlasmaWaylandWorkspaceInfo::position(const QVariant &desktop) const +{ + return std::distance(virtualDesktops.begin(), findDesktop(desktop.toString())); +} + +QVariantList LXQtPlasmaWaylandWorkspaceInfo::desktopIds() const +{ + QVariantList ids; + ids.reserve(virtualDesktops.size()); + + std::transform(virtualDesktops.cbegin(), virtualDesktops.cend(), std::back_inserter(ids), [](const std::unique_ptr &desktop) { + return desktop->id; + }); + return ids; +} + +QStringList LXQtPlasmaWaylandWorkspaceInfo::desktopNames() const +{ + if (!virtualDesktopManagement->isActive()) { + return QStringList(); + } + QStringList names; + names.reserve(virtualDesktops.size()); + + std::transform(virtualDesktops.cbegin(), virtualDesktops.cend(), std::back_inserter(names), [](const std::unique_ptr &desktop) { + return desktop->name; + }); + return names; +} + +int LXQtPlasmaWaylandWorkspaceInfo::desktopLayoutRows() const +{ + if (!virtualDesktopManagement->isActive()) { + return 0; + } + + return rows; +} + +void LXQtPlasmaWaylandWorkspaceInfo::requestActivate(const QVariant &desktop) +{ + if (!virtualDesktopManagement->isActive()) { + return; + } + + if (auto it = findDesktop(desktop.toString()); it != virtualDesktops.end()) { + (*it)->request_activate(); + } +} + +void LXQtPlasmaWaylandWorkspaceInfo::requestCreateDesktop(quint32 position) +{ + if (!virtualDesktopManagement->isActive()) { + return; + } + + //TODO: translatestd + virtualDesktopManagement->request_create_virtual_desktop(QLatin1String("New Desktop"), position); +} + +void LXQtPlasmaWaylandWorkspaceInfo::requestRemoveDesktop(quint32 position) +{ + if (!virtualDesktopManagement->isActive()) { + return; + } + if (virtualDesktops.size() == 1) { + return; + } + + if (position > (virtualDesktops.size() - 1)) { + return; + } + + virtualDesktopManagement->request_remove_virtual_desktop(virtualDesktops.at(position)->id); +} + diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.h b/panel/backends/wayland/lxqtplasmavirtualdesktop.h new file mode 100644 index 000000000..3dabb6242 --- /dev/null +++ b/panel/backends/wayland/lxqtplasmavirtualdesktop.h @@ -0,0 +1,87 @@ +#ifndef LXQTPLASMAVIRTUALDESKTOP_H +#define LXQTPLASMAVIRTUALDESKTOP_H + +#include +#include + +#include + +#include "qwayland-org-kde-plasma-virtual-desktop.h" + +class LXQtPlasmaVirtualDesktop : public QObject, public QtWayland::org_kde_plasma_virtual_desktop +{ + Q_OBJECT +public: + LXQtPlasmaVirtualDesktop(::org_kde_plasma_virtual_desktop *object, const QString &id); + ~LXQtPlasmaVirtualDesktop(); + const QString id; + QString name; +Q_SIGNALS: + void done(); + void activated(); + +protected: + void org_kde_plasma_virtual_desktop_name(const QString &name) override; + void org_kde_plasma_virtual_desktop_done() override; + void org_kde_plasma_virtual_desktop_activated() override; +}; + + +class LXQtPlasmaVirtualDesktopManagment : public QWaylandClientExtensionTemplate, + public QtWayland::org_kde_plasma_virtual_desktop_management +{ + Q_OBJECT +public: + static constexpr int version = 2; + + LXQtPlasmaVirtualDesktopManagment(); + ~LXQtPlasmaVirtualDesktopManagment(); + +signals: + void desktopCreated(const QString &id, quint32 position); + void desktopRemoved(const QString &id); + void rowsChanged(const quint32 rows); + +protected: + virtual void org_kde_plasma_virtual_desktop_management_desktop_created(const QString &desktop_id, uint32_t position) override; + virtual void org_kde_plasma_virtual_desktop_management_desktop_removed(const QString &desktop_id) override; + virtual void org_kde_plasma_virtual_desktop_management_rows(uint32_t rows) override; +}; + +class Q_DECL_HIDDEN LXQtPlasmaWaylandWorkspaceInfo : public QObject +{ + Q_OBJECT +public: + LXQtPlasmaWaylandWorkspaceInfo(); + + QVariant currentVirtualDesktop; + std::vector> virtualDesktops; + std::unique_ptr virtualDesktopManagement; + quint32 rows; + + typedef std::vector>::const_iterator VirtualDesktopsIterator; + + VirtualDesktopsIterator findDesktop(const QString &id) const; + + void init(); + void addDesktop(const QString &id, quint32 position); + QVariant currentDesktop() const; + int numberOfDesktops() const; + QVariantList desktopIds() const; + QStringList desktopNames() const; + quint32 position(const QVariant &desktop) const; + int desktopLayoutRows() const; + void requestActivate(const QVariant &desktop); + void requestCreateDesktop(quint32 position); + void requestRemoveDesktop(quint32 position); + +signals: + void currentDesktopChanged(); + void numberOfDesktopsChanged(); + void navigationWrappingAroundChanged(); + void desktopIdsChanged(); + void desktopNamesChanged(); + void desktopLayoutRowsChanged(); +}; + +#endif // LXQTPLASMAVIRTUALDESKTOP_H diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index 5def725ef..57b77c906 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -1,6 +1,7 @@ #include "lxqttaskbarbackendwayland.h" #include "lxqttaskbarplasmawindowmanagment.h" +#include "lxqtplasmavirtualdesktop.h" #include #include @@ -25,6 +26,7 @@ LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) : ILXQtTaskbarAbstractBackend(parent) { m_managment.reset(new LXQtTaskBarPlasmaWindowManagment); + m_workspaceInfo.reset(new LXQtPlasmaWaylandWorkspaceInfo); connect(m_managment.get(), &LXQtTaskBarPlasmaWindowManagment::windowCreated, this, [this](LXQtTaskBarPlasmaWindow *window) { connect(window, &LXQtTaskBarPlasmaWindow::initialStateDone, this, [this, window] { @@ -39,6 +41,17 @@ LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) : // // this->dataChanged(window.get(), StackingOrder); // // } // }); + + connect(m_workspaceInfo.get(), &LXQtPlasmaWaylandWorkspaceInfo::currentDesktopChanged, this, + [this](){ + int idx = m_workspaceInfo->position(m_workspaceInfo->currentDesktop()); + emit currentWorkspaceChanged(idx); + }); + + connect(m_workspaceInfo.get(), &LXQtPlasmaWaylandWorkspaceInfo::numberOfDesktopsChanged, + this, &ILXQtTaskbarAbstractBackend::workspacesCountChanged); + + //TODO: connect name changed } bool LXQtTaskbarWaylandBackend::supportsAction(WId windowId, LXQtTaskBarBackendAction action) const @@ -291,12 +304,13 @@ WId LXQtTaskbarWaylandBackend::getActiveWindow() const int LXQtTaskbarWaylandBackend::getWorkspacesCount() const { - return 1; //TODO + return m_workspaceInfo->numberOfDesktops(); } QString LXQtTaskbarWaylandBackend::getWorkspaceName(int idx) const { - return QStringLiteral("TestWorkspace"); + //TODO: optimize + return m_workspaceInfo->desktopNames().value(idx, QStringLiteral("ERROR")); } int LXQtTaskbarWaylandBackend::getCurrentWorkspace() const diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.h b/panel/backends/wayland/lxqttaskbarbackendwayland.h index 77d857eb9..0b12ef2fd 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.h +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.h @@ -8,6 +8,7 @@ class LXQtTaskBarPlasmaWindow; class LXQtTaskBarPlasmaWindowManagment; +class LXQtPlasmaWaylandWorkspaceInfo; class LXQtTaskbarWaylandBackend : public ILXQtTaskbarAbstractBackend @@ -77,6 +78,8 @@ class LXQtTaskbarWaylandBackend : public ILXQtTaskbarAbstractBackend private: LXQtTaskBarPlasmaWindow *getWindow(WId windowId) const; + std::unique_ptr m_workspaceInfo; + std::unique_ptr m_managment; QHash lastActivated; diff --git a/panel/backends/wayland/protocols/org-kde-plasma-virtual-desktop.xml b/panel/backends/wayland/protocols/org-kde-plasma-virtual-desktop.xml new file mode 100644 index 000000000..0e0551b0a --- /dev/null +++ b/panel/backends/wayland/protocols/org-kde-plasma-virtual-desktop.xml @@ -0,0 +1,110 @@ + + + + + + + + Given the id of a particular virtual desktop, get the corresponding org_kde_plasma_virtual_desktop which represents only the desktop with that id. + + + + + + + + Ask the server to create a new virtual desktop, and position it at a specified position. If the position is zero or less, it will be positioned at the beginning, if the position is the count or more, it will be positioned at the end. + + + + + + + + Ask the server to get rid of a virtual desktop, the server may or may not acconsent to the request. + + + + + + + + + + + + + + + + + + + This event is sent after all other properties has been + sent after binding to the desktop manager object and after any + other property changes done after that. This allows + changes to the org_kde_plasma_virtual_desktop_management properties to be seen as + atomic, even if they happen via multiple events. + + + + + + + + + + + + + Request the server to set the status of this desktop to active: The server is free to consent or deny the request. This will be the new "current" virtual desktop of the system. + + + + + + The format of the id is decided by the compositor implementation. A desktop id univocally identifies a virtual desktop and must be guaranteed to never exist two desktops with the same id. The format of the string id is up to the server implementation. + + + + + + + + + + + The desktop will be the new "current" desktop of the system. The server may support either one virtual desktop active at a time, or other combinations such as one virtual desktop active per screen. + Windows associated to this virtual desktop will be shown. + + + + + + Windows that were associated only to this desktop will be hidden. + + + + + + This event is sent after all other properties has been + sent after binding to the desktop object and after any + other property changes done after that. This allows + changes to the org_kde_plasma_virtual_desktop properties to be seen as + atomic, even if they happen via multiple events. + + + + + + This virtual desktop has just been removed by the server: + All windows will lose the association to this desktop. + + + + + From 08b716a570a53f36e45c11c6514d5397ca75637a Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 17 Feb 2024 23:32:46 +0100 Subject: [PATCH 151/165] LXQtPanel: workaround KAcceleratorManager changing button text FIXME TODO TODO: is this correct approach? --- plugin-taskbar/lxqttaskbutton.cpp | 27 ++++++++++++++++++++++++++- plugin-taskbar/lxqttaskbutton.h | 4 ++++ plugin-taskbar/lxqttaskgroup.cpp | 6 +++--- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/plugin-taskbar/lxqttaskbutton.cpp b/plugin-taskbar/lxqttaskbutton.cpp index c86e6af5f..cbe5785a3 100644 --- a/plugin-taskbar/lxqttaskbutton.cpp +++ b/plugin-taskbar/lxqttaskbutton.cpp @@ -128,7 +128,7 @@ LXQtTaskButton::~LXQtTaskButton() = default; void LXQtTaskButton::updateText() { QString title = mBackend->getWindowTitle(mWindow); - setText(title.replace(QStringLiteral("&"), QStringLiteral("&&"))); + setTextExplicitly(title.replace(QStringLiteral("&"), QStringLiteral("&&"))); setToolTip(title); } @@ -314,6 +314,30 @@ QMimeData * LXQtTaskButton::mimeData() return mimedata; } +/*! + * \brief LXQtTaskButton::setTextExplicitly + * \param str + * + * This is needed to workaround flickering caused by KAcceleratorManager + * This class is hooked by KDE Integration and adds accelerators to button text + * (Adds some '&' characters) + * This triggers widget update but soon after text is reset to original value + * This triggers a KAcceleratorManager update which again adds accelerator + * This happens in loop + * + * TODO: investigate proper solution + */ +void LXQtTaskButton::setTextExplicitly(const QString &str) +{ + if(str == mExplicitlySetText) + { + return; + } + + mExplicitlySetText = str; + setText(mExplicitlySetText); +} + /************************************************ ************************************************/ @@ -688,6 +712,7 @@ void LXQtTaskButton::contextMenuEvent(QContextMenuEvent* event) menu->addSeparator(); a = menu->addAction(XdgIcon::fromTheme(QStringLiteral("process-stop")), tr("&Close")); connect(a, &QAction::triggered, this, &LXQtTaskButton::closeApplication); + menu->setGeometry(mParentTaskBar->panel()->calculatePopupWindowPos(mapToGlobal(event->pos()), menu->sizeHint())); mPlugin->willShowWindow(menu); menu->show(); diff --git a/plugin-taskbar/lxqttaskbutton.h b/plugin-taskbar/lxqttaskbutton.h index 9ccca36fa..6d5841df4 100644 --- a/plugin-taskbar/lxqttaskbutton.h +++ b/plugin-taskbar/lxqttaskbutton.h @@ -122,6 +122,8 @@ public slots: inline ILXQtPanelPlugin * plugin() const { return mPlugin; } + void setTextExplicitly(const QString& str); + protected: //TODO: public getter instead? ILXQtTaskbarAbstractBackend *mBackend; @@ -138,6 +140,8 @@ public slots: int mIconSize; int mWheelDelta; + QString mExplicitlySetText; + // Timer for when draggind something into a button (the button's window // must be activated so that the use can continue dragging to the window QTimer * mDNDTimer; diff --git a/plugin-taskbar/lxqttaskgroup.cpp b/plugin-taskbar/lxqttaskgroup.cpp index 8317c034f..b86b1657c 100644 --- a/plugin-taskbar/lxqttaskgroup.cpp +++ b/plugin-taskbar/lxqttaskgroup.cpp @@ -57,7 +57,7 @@ LXQtTaskGroup::LXQtTaskGroup(const QString &groupName, WId window, LXQtTaskBar * Q_ASSERT(parent); setObjectName(groupName); - setText(groupName); + setTextExplicitly(groupName); connect(this, &LXQtTaskGroup::clicked, this, &LXQtTaskGroup::onClicked); connect(parent, &LXQtTaskBar::buttonRotationRefreshed, this, &LXQtTaskGroup::setAutoRotation); @@ -336,7 +336,7 @@ void LXQtTaskGroup::regroup() if (button) { - setText(button->text()); + setTextExplicitly(button->text()); setToolTip(button->toolTip()); setWindowId(button->windowId()); } @@ -347,7 +347,7 @@ void LXQtTaskGroup::regroup() { mSingleButton = false; QString t = QString(QStringLiteral("%1 - %2 windows")).arg(mGroupName).arg(cont); - setText(t); + setTextExplicitly(t); setToolTip(parentTaskBar()->isShowGroupOnHover() ? QString() : t); } } From ca5e778bf61c2dee48f79f8b53189d3df7ea73bf Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 14:19:31 +0100 Subject: [PATCH 152/165] Add desktop file to be recognized by KWin Wayland NOTE: absolute path is needed inside .desktop file for this to work use CMake to get it. - Prevent double dekstop file installed in autostart --- autostart/CMakeLists.txt | 11 ++++++++++- autostart/lxqt-panel_wayland.desktop.in | 13 +++++++++++++ panel/lxqtpanelapplication.cpp | 2 ++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 autostart/lxqt-panel_wayland.desktop.in diff --git a/autostart/CMakeLists.txt b/autostart/CMakeLists.txt index 098103168..6d044738b 100644 --- a/autostart/CMakeLists.txt +++ b/autostart/CMakeLists.txt @@ -1,4 +1,4 @@ -file(GLOB DESKTOP_FILES_IN *.desktop.in) +set(DESKTOP_FILES lxqt-panel.desktop.in) # Translations ********************************** lxqt_translate_desktop(DESKTOP_FILES @@ -14,3 +14,12 @@ install(FILES DESTINATION "${LXQT_ETC_XDG_DIR}/autostart" COMPONENT Runtime ) + +configure_file(lxqt-panel_wayland.desktop.in lxqt-panel_wayland.desktop @ONLY) + +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/lxqt-panel_wayland.desktop" + DESTINATION "/usr/share/applications" + RENAME "lxqt-panel.desktop" + COMPONENT Runtime +) diff --git a/autostart/lxqt-panel_wayland.desktop.in b/autostart/lxqt-panel_wayland.desktop.in new file mode 100644 index 000000000..089082aea --- /dev/null +++ b/autostart/lxqt-panel_wayland.desktop.in @@ -0,0 +1,13 @@ +[Desktop Entry] +Type=Application +TryExec=lxqt-panel + +# NOTE: KWin wants absolute path here, get it from CMake install path +Exec=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@/lxqt-panel + +# NOTE: adding KDE to make it work under Plasma Wayland session +OnlyShowIn=LXQt;KDE +X-LXQt-Module=true + +# Make KWin recognize us as priviledged client +X-KDE-Wayland-Interfaces=org_kde_plasma_window_management diff --git a/panel/lxqtpanelapplication.cpp b/panel/lxqtpanelapplication.cpp index 507163221..a3d2132f1 100644 --- a/panel/lxqtpanelapplication.cpp +++ b/panel/lxqtpanelapplication.cpp @@ -107,6 +107,8 @@ LXQtPanelApplication::LXQtPanelApplication(int& argc, char** argv) QCoreApplication::setApplicationVersion(VERINFO); + QGuiApplication::setDesktopFileName(QLatin1String("lxqt-panel")); + QCommandLineParser parser; parser.setApplicationDescription(QLatin1String("LXQt Panel")); parser.addHelpOption(); From c2b935fe4aa03e8aa5053b6cf45741e3f4ccffbf Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 22 Feb 2024 18:01:49 +0100 Subject: [PATCH 153/165] LXQtTaskbarWaylandBackend: return only accepted windows - reloadWindows() force removal and readding of windows This fixes changing windows grouping settings and adding taskbar plugin AFTER panel is started. Both situations resulted in empty taskbar previously --- .../wayland/lxqttaskbarbackendwayland.cpp | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index 57b77c906..27e6b97e5 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -97,15 +97,30 @@ bool LXQtTaskbarWaylandBackend::supportsAction(WId windowId, LXQtTaskBarBackendA bool LXQtTaskbarWaylandBackend::reloadWindows() { - return false; //TODO + const QVector wids = getCurrentWindows(); + + // Force removal and re-adding + for(WId windowId : wids) + { + emit windowRemoved(windowId); + } + for(WId windowId : wids) + { + emit windowAdded(windowId); + } + + return true; } QVector LXQtTaskbarWaylandBackend::getCurrentWindows() const { - QVector wids(windows.size()); - for(const auto& window : std::as_const(windows)) + QVector wids; + wids.reserve(wids.size()); + + for(const std::unique_ptr& window : std::as_const(windows)) { - wids << window->getWindowId(); + if(window->acceptedInTaskBar) + wids << window->getWindowId(); } return wids; } From 3e2015e42fae88fa2525ba673385fe56b94828be Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 23 Feb 2024 12:27:57 +0100 Subject: [PATCH 154/165] LXQtTaskbarWaylandBackend: fix workspace logic --- .../wayland/lxqtplasmavirtualdesktop.cpp | 31 ++++++++++++++----- .../wayland/lxqtplasmavirtualdesktop.h | 8 +++-- .../wayland/lxqttaskbarbackendwayland.cpp | 20 ++++++++---- 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp index a3d987f06..fe3d3a435 100644 --- a/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp +++ b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp @@ -16,6 +16,7 @@ LXQtPlasmaVirtualDesktop::~LXQtPlasmaVirtualDesktop() void LXQtPlasmaVirtualDesktop::org_kde_plasma_virtual_desktop_name(const QString &name) { this->name = name; + Q_EMIT nameChanged(); } void LXQtPlasmaVirtualDesktop::org_kde_plasma_virtual_desktop_done() @@ -73,6 +74,20 @@ LXQtPlasmaWaylandWorkspaceInfo::VirtualDesktopsIterator LXQtPlasmaWaylandWorkspa }); } +QString LXQtPlasmaWaylandWorkspaceInfo::getDesktopName(int pos) const +{ + if(pos < 0 || pos >= virtualDesktops.size()) + return QString(); + return virtualDesktops[pos]->name; +} + +QString LXQtPlasmaWaylandWorkspaceInfo::getDesktopId(int pos) const +{ + if(pos < 0 || pos >= virtualDesktops.size()) + return QString(); + return virtualDesktops[pos]->id; +} + void LXQtPlasmaWaylandWorkspaceInfo::init() { virtualDesktopManagement = std::make_unique(); @@ -86,7 +101,6 @@ void LXQtPlasmaWaylandWorkspaceInfo::init() Q_EMIT numberOfDesktopsChanged(); Q_EMIT navigationWrappingAroundChanged(); Q_EMIT desktopIdsChanged(); - Q_EMIT desktopNamesChanged(); Q_EMIT desktopLayoutRowsChanged(); } }); @@ -105,7 +119,6 @@ void LXQtPlasmaWaylandWorkspaceInfo::init() Q_EMIT numberOfDesktopsChanged(); Q_EMIT desktopIdsChanged(); - Q_EMIT desktopNamesChanged(); if (currentVirtualDesktop == id) { currentVirtualDesktop.clear(); @@ -119,7 +132,7 @@ void LXQtPlasmaWaylandWorkspaceInfo::init() }); } -void LXQtPlasmaWaylandWorkspaceInfo::addDesktop(const QString &id, quint32 position) +void LXQtPlasmaWaylandWorkspaceInfo::addDesktop(const QString &id, quint32 pos) { if (findDesktop(id) != virtualDesktops.end()) { return; @@ -132,15 +145,19 @@ void LXQtPlasmaWaylandWorkspaceInfo::addDesktop(const QString &id, quint32 posit Q_EMIT currentDesktopChanged(); }); - connect(desktop.get(), &LXQtPlasmaVirtualDesktop::done, this, [this]() { - Q_EMIT desktopNamesChanged(); + connect(desktop.get(), &LXQtPlasmaVirtualDesktop::nameChanged, this, [id, this]() { + Q_EMIT desktopNameChanged(position(id)); + }); + + connect(desktop.get(), &LXQtPlasmaVirtualDesktop::done, this, [id, this]() { + Q_EMIT desktopNameChanged(position(id)); }); - virtualDesktops.insert(std::next(virtualDesktops.begin(), position), std::move(desktop)); + virtualDesktops.insert(std::next(virtualDesktops.begin(), pos), std::move(desktop)); Q_EMIT numberOfDesktopsChanged(); Q_EMIT desktopIdsChanged(); - Q_EMIT desktopNamesChanged(); + Q_EMIT desktopNameChanged(position(id)); } QVariant LXQtPlasmaWaylandWorkspaceInfo::currentDesktop() const diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.h b/panel/backends/wayland/lxqtplasmavirtualdesktop.h index 3dabb6242..fff025213 100644 --- a/panel/backends/wayland/lxqtplasmavirtualdesktop.h +++ b/panel/backends/wayland/lxqtplasmavirtualdesktop.h @@ -19,6 +19,7 @@ class LXQtPlasmaVirtualDesktop : public QObject, public QtWayland::org_kde_plasm Q_SIGNALS: void done(); void activated(); + void nameChanged(); protected: void org_kde_plasma_virtual_desktop_name(const QString &name) override; @@ -63,8 +64,11 @@ class Q_DECL_HIDDEN LXQtPlasmaWaylandWorkspaceInfo : public QObject VirtualDesktopsIterator findDesktop(const QString &id) const; + QString getDesktopName(int pos) const; + QString getDesktopId(int pos) const; + void init(); - void addDesktop(const QString &id, quint32 position); + void addDesktop(const QString &id, quint32 pos); QVariant currentDesktop() const; int numberOfDesktops() const; QVariantList desktopIds() const; @@ -80,7 +84,7 @@ class Q_DECL_HIDDEN LXQtPlasmaWaylandWorkspaceInfo : public QObject void numberOfDesktopsChanged(); void navigationWrappingAroundChanged(); void desktopIdsChanged(); - void desktopNamesChanged(); + void desktopNameChanged(quint32 position); void desktopLayoutRowsChanged(); }; diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index 27e6b97e5..8aa214c65 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -45,13 +45,17 @@ LXQtTaskbarWaylandBackend::LXQtTaskbarWaylandBackend(QObject *parent) : connect(m_workspaceInfo.get(), &LXQtPlasmaWaylandWorkspaceInfo::currentDesktopChanged, this, [this](){ int idx = m_workspaceInfo->position(m_workspaceInfo->currentDesktop()); + idx += 1; // Make 1-based emit currentWorkspaceChanged(idx); }); connect(m_workspaceInfo.get(), &LXQtPlasmaWaylandWorkspaceInfo::numberOfDesktopsChanged, this, &ILXQtTaskbarAbstractBackend::workspacesCountChanged); - //TODO: connect name changed + connect(m_workspaceInfo.get(), &LXQtPlasmaWaylandWorkspaceInfo::desktopNameChanged, + this, [this](int idx) { + emit workspaceNameChanged(idx + 1); // Make 1-based + }); } bool LXQtTaskbarWaylandBackend::supportsAction(WId windowId, LXQtTaskBarBackendAction action) const @@ -324,19 +328,23 @@ int LXQtTaskbarWaylandBackend::getWorkspacesCount() const QString LXQtTaskbarWaylandBackend::getWorkspaceName(int idx) const { - //TODO: optimize - return m_workspaceInfo->desktopNames().value(idx, QStringLiteral("ERROR")); + return m_workspaceInfo->getDesktopName(idx - 1); //Return to 0-based } int LXQtTaskbarWaylandBackend::getCurrentWorkspace() const { - return 0; //TODO + if(!m_workspaceInfo->currentDesktop().isValid()) + return 0; + return m_workspaceInfo->position(m_workspaceInfo->currentDesktop()) + 1; // 1-based } bool LXQtTaskbarWaylandBackend::setCurrentWorkspace(int idx) { - Q_UNUSED(idx) - return false; //TODO + QString id = m_workspaceInfo->getDesktopId(idx - 1); + if(id.isEmpty()) + return false; + m_workspaceInfo->requestActivate(id); + return true; } int LXQtTaskbarWaylandBackend::getWindowWorkspace(WId windowId) const From d6d1e0647cf037ac3a3694124055e33cbd2c9f4c Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 23 Feb 2024 12:58:54 +0100 Subject: [PATCH 155/165] LXQtTaskbarWaylandBackend: fix workspace removal logic --- panel/backends/wayland/lxqtplasmavirtualdesktop.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp index fe3d3a435..b3e49a681 100644 --- a/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp +++ b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp @@ -111,11 +111,12 @@ void LXQtPlasmaWaylandWorkspaceInfo::init() connect(virtualDesktopManagement.get(), &LXQtPlasmaVirtualDesktopManagment::desktopRemoved, this, [this](const QString &id) { - std::remove_if(virtualDesktops.begin(), virtualDesktops.end(), - [id](const std::unique_ptr &desktop) - { - return desktop->id == id; - }); + virtualDesktops.erase(std::remove_if(virtualDesktops.begin(), virtualDesktops.end(), + [id](const std::unique_ptr &desktop) + { + return desktop->id == id; + }), + virtualDesktops.end()); Q_EMIT numberOfDesktopsChanged(); Q_EMIT desktopIdsChanged(); From 512ecc338d62df58a82a1d81c84e54acb7736801 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 23 Feb 2024 13:33:40 +0100 Subject: [PATCH 156/165] lxqttaskbartypes.h: fix ShowOnAll desktops flag value --- panel/backends/lxqttaskbartypes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panel/backends/lxqttaskbartypes.h b/panel/backends/lxqttaskbartypes.h index 656591fbc..801e8e2d1 100644 --- a/panel/backends/lxqttaskbartypes.h +++ b/panel/backends/lxqttaskbartypes.h @@ -50,7 +50,7 @@ enum class LXQtTaskBarWindowLayer enum class LXQtTaskBarWorkspace { - ShowOnAll = -1 + ShowOnAll = 0 // Virtual destops have 1-based indexes }; #endif // LXQTTASKBARTYPES_H From dff72a4bfc3d02d6af41e0b08cc3d671fe6b7a5d Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Fri, 23 Feb 2024 13:34:16 +0100 Subject: [PATCH 157/165] LXQtTaskbarWaylandBackend: implement moving window to virtual desktop workspace --- .../wayland/lxqttaskbarbackendwayland.cpp | 51 ++++++++++++++++--- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index 8aa214c65..fb6e940a8 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -340,7 +340,7 @@ int LXQtTaskbarWaylandBackend::getCurrentWorkspace() const bool LXQtTaskbarWaylandBackend::setCurrentWorkspace(int idx) { - QString id = m_workspaceInfo->getDesktopId(idx - 1); + QString id = m_workspaceInfo->getDesktopId(idx - 1); //Return to 0-based if(id.isEmpty()) return false; m_workspaceInfo->requestActivate(id); @@ -349,15 +349,54 @@ bool LXQtTaskbarWaylandBackend::setCurrentWorkspace(int idx) int LXQtTaskbarWaylandBackend::getWindowWorkspace(WId windowId) const { - Q_UNUSED(windowId) - return 0; //TODO + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return 0; + + // TODO: this protocol seems to allow multiple desktop for each window + // We do not support that yet + // Also from KDE Plasma task switch it's not clear how to actually put + // a window on multiple desktops (which is different from "All desktops") + QString id = window->virtualDesktops.value(0, QString()); + if(id.isEmpty()) + return 0; + + return m_workspaceInfo->position(id) + 1; //Make 1-based } bool LXQtTaskbarWaylandBackend::setWindowOnWorkspace(WId windowId, int idx) { - Q_UNUSED(windowId) - Q_UNUSED(idx) - return false; //TODO + LXQtTaskBarPlasmaWindow *window = getWindow(windowId); + if(!window) + return false; + + // Prepare for future multiple virtual desktops per window + QList newDesktops; + + // Fill the list + newDesktops.append(m_workspaceInfo->getDesktopId(idx - 1)); //Return to 0-based + + // Keep only valid IDs + newDesktops.erase(std::remove_if(newDesktops.begin(), newDesktops.end(), + [](const QString& id) { return id.isEmpty(); }), + newDesktops.end()); + + // Add to new requested desktops + for(const QString& id : std::as_const(newDesktops)) + { + if(!window->virtualDesktops.contains(id)) + window->request_enter_virtual_desktop(id); + } + + // Remove from non-requested destops + const QList currentDesktops = window->virtualDesktops; + for(const QString& id : std::as_const(currentDesktops)) + { + if(!newDesktops.contains(id)) + window->request_leave_virtual_desktop(id); + } + + return true; } void LXQtTaskbarWaylandBackend::moveApplicationToPrevNextMonitor(WId windowId, bool next, bool raiseOnCurrentDesktop) From a2ce0901315a97011ca0d0035fb781751cc2beda Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Mon, 26 Feb 2024 09:48:03 +0100 Subject: [PATCH 158/165] ColorPicker: use XDG Desktop Portal on Wayland TODO TODO: show error message when not supported --- plugin-colorpicker/colorpicker.cpp | 156 ++++++++++++++++++++++++++--- plugin-colorpicker/colorpicker.h | 8 ++ 2 files changed, 150 insertions(+), 14 deletions(-) diff --git a/plugin-colorpicker/colorpicker.cpp b/plugin-colorpicker/colorpicker.cpp index 004314557..0827686d5 100644 --- a/plugin-colorpicker/colorpicker.cpp +++ b/plugin-colorpicker/colorpicker.cpp @@ -36,6 +36,9 @@ #include #include +#include +#include + //NOTE: Xlib.h defines Bool which conflicts with QJsonValue::Type enum #include #undef Bool @@ -77,6 +80,33 @@ void ColorPicker::realign() mWidget.update(panel()->lineCount() <= 1 ? panel()->isHorizontal() : !panel()->isHorizontal()); } +void ColorPicker::queryXDGSupport() +{ + if (qEnvironmentVariableIntValue("QT_NO_XDG_DESKTOP_PORTAL") > 0) { + return; + } + QDBusMessage message = QDBusMessage::createMethodCall( + QLatin1String("org.freedesktop.portal.Desktop"), + QLatin1String("/org/freedesktop/portal/desktop"), + QLatin1String("org.freedesktop.DBus.Properties"), + QLatin1String("Get")); + message << QLatin1String("org.freedesktop.portal.Screenshot") + << QLatin1String("version"); + + QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message); + auto watcher = new QDBusPendingCallWatcher(pendingCall); + QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher, + [this](QDBusPendingCallWatcher *watcher) { + watcher->deleteLater(); + QDBusPendingReply reply = *watcher; + if (!reply.isError() && reply.value().toUInt() >= 2) + m_hasScreenshotPortalWithColorPicking = true; + }); + + //TODO: show error tooltip if not supported + //NOTE: on Wayland we cannot pick color without it +} + ColorPickerWidget::ColorPickerWidget(QWidget *parent) : QWidget(parent) { @@ -108,7 +138,8 @@ ColorPickerWidget::ColorPickerWidget(QWidget *parent) : QWidget(parent) layout->addWidget(mColorButton); setLayout(layout); - connect(mPickerButton, &QToolButton::clicked, this, &ColorPickerWidget::captureMouse); + connect(mPickerButton, &QToolButton::clicked, this, &ColorPickerWidget::startCapturingColor); + connect(mColorButton, &QToolButton::clicked, this, [&]() { buildMenu(); @@ -162,29 +193,86 @@ void ColorPickerWidget::mouseReleaseEvent(QMouseEvent *event) qWarning() << "WAYLAND does not support grabbing windows"; } - mColorButton->setColor(col); - paste(col.name()); + setCapturedColor(col); - if (mColorsList.contains(col)) + mCapturing = false; + releaseMouse(); + + if (!mPickerButton->contentsRect().contains(mapFromGlobal(QCursor::pos()))) { - mColorsList.move(mColorsList.indexOf(col), 0); + QApplication::sendEvent(mPickerButton, new QEvent(QEvent::Leave)); } - else +} + +void ColorPickerWidget::startCapturingColor() +{ + //NOTE: see qt6 `src/gui/platform/unix/qgenericunixservices.cpp` + + // Make double sure that we are in a wayland environment. In particular check + // WAYLAND_DISPLAY so also XWayland apps benefit from portal-based color picking. + // Outside wayland we'll rather rely on other means than the XDG desktop portal. + if (!qEnvironmentVariableIsEmpty("WAYLAND_DISPLAY") + || QGuiApplication::platformName().startsWith(QLatin1String("wayland"))) { - mColorsList.prepend(col); + // On Wayland use XDG Desktop Portal + + QString m_parentWindowId; //TODO + + QDBusMessage message = QDBusMessage::createMethodCall( + QLatin1String("org.freedesktop.portal.Desktop"), + QLatin1String("/org/freedesktop/portal/desktop"), + QLatin1String("org.freedesktop.portal.Screenshot"), + QLatin1String("PickColor")); + message << m_parentWindowId << QVariantMap(); + + QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message); + auto watcher = new QDBusPendingCallWatcher(pendingCall, this); + connect(watcher, &QDBusPendingCallWatcher::finished, this, + [this](QDBusPendingCallWatcher *watcher) { + watcher->deleteLater(); + QDBusPendingReply reply = *watcher; + if (reply.isError()) { + qWarning("DBus call to pick color failed: %s", + qPrintable(reply.error().message())); + setCapturedColor({}); + } else { + QDBusConnection::sessionBus().connect( + QLatin1String("org.freedesktop.portal.Desktop"), + reply.value().path(), + QLatin1String("org.freedesktop.portal.Request"), + QLatin1String("Response"), + this, + // clang-format off + SLOT(gotColorResponse(uint,QVariantMap)) + // clang-format on + ); + } + }); } - - if (mColorsList.size() > 10) + else if (qGuiApp->nativeInterface()) { - mColorsList.removeLast(); + // On X11 grab mouse and let `mouseReleaseEvent()` retrieve color + captureMouse(); } +} - mCapturing = false; - releaseMouse(); +void ColorPickerWidget::setCapturedColor(const QColor &color) +{ + mColorButton->setColor(color); + paste(color.name()); - if (!mPickerButton->contentsRect().contains(mapFromGlobal(QCursor::pos()))) + if (mColorsList.contains(color)) { - QApplication::sendEvent(mPickerButton, new QEvent(QEvent::Leave)); + mColorsList.move(mColorsList.indexOf(color), 0); + } + else + { + mColorsList.prepend(color); + } + + if (mColorsList.size() > 10) + { + mColorsList.removeLast(); } } @@ -195,6 +283,46 @@ void ColorPickerWidget::captureMouse() mCapturing = true; } +struct XDGDesktopColor +{ + double r = 0; + double g = 0; + double b = 0; + + QColor toQColor() const + { + constexpr auto rgbMax = 255; + return { static_cast(r * rgbMax), static_cast(g * rgbMax), + static_cast(b * rgbMax) }; + } +}; + +const QDBusArgument &operator>>(const QDBusArgument &argument, XDGDesktopColor &myStruct) +{ + argument.beginStructure(); + argument >> myStruct.r >> myStruct.g >> myStruct.b; + argument.endStructure(); + return argument; +} + +void ColorPickerWidget::gotColorResponse(uint result, const QVariantMap &map) +{ + auto colorProp = QStringLiteral("color"); + + if (result != 0) + return; + if (map.contains(colorProp)) + { + XDGDesktopColor color{}; + map.value(colorProp).value() >> color; + setCapturedColor(color.toQColor()); + } + else + { + setCapturedColor({}); + } +} + QIcon ColorPickerWidget::colorIcon(QColor color) { diff --git a/plugin-colorpicker/colorpicker.h b/plugin-colorpicker/colorpicker.h index 919f42490..ecf2a3b41 100644 --- a/plugin-colorpicker/colorpicker.h +++ b/plugin-colorpicker/colorpicker.h @@ -58,7 +58,10 @@ class ColorPickerWidget : public QWidget void mouseReleaseEvent(QMouseEvent *event); private slots: + void startCapturingColor(); + void setCapturedColor(const QColor& color); void captureMouse(); + void gotColorResponse(uint result, const QVariantMap& map); private: static const QString svgIcon; @@ -91,8 +94,13 @@ class ColorPicker : public QObject, public ILXQtPanelPlugin virtual void realign() override; +private: + void queryXDGSupport(); + private: ColorPickerWidget mWidget; + + bool m_hasScreenshotPortalWithColorPicking = false; }; class ColorPickerLibrary: public QObject, public ILXQtPanelPluginLibrary From 1ed2559ee18189336e29a8a698dbf19ecf32f3e6 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Wed, 28 Feb 2024 20:43:50 +0100 Subject: [PATCH 159/165] LXQtPlasmaWaylandWorkspaceInfo: fix signedness comparison --- panel/backends/wayland/lxqtplasmavirtualdesktop.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp index b3e49a681..01e28fe75 100644 --- a/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp +++ b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp @@ -76,14 +76,14 @@ LXQtPlasmaWaylandWorkspaceInfo::VirtualDesktopsIterator LXQtPlasmaWaylandWorkspa QString LXQtPlasmaWaylandWorkspaceInfo::getDesktopName(int pos) const { - if(pos < 0 || pos >= virtualDesktops.size()) + if(pos < 0 || size_t(pos) >= virtualDesktops.size()) return QString(); return virtualDesktops[pos]->name; } QString LXQtPlasmaWaylandWorkspaceInfo::getDesktopId(int pos) const { - if(pos < 0 || pos >= virtualDesktops.size()) + if(pos < 0 || size_t(pos) >= virtualDesktops.size()) return QString(); return virtualDesktops[pos]->id; } From d7bc74562755da4f1fae59231e8cdf52b6576f08 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 29 Feb 2024 00:09:55 +0100 Subject: [PATCH 160/165] CMake: move panel WM backends to separate libraries --- panel/CMakeLists.txt | 47 +++---------------- panel/backends/CMakeLists.txt | 17 +++++++ .../backends/ilxqttaskbarabstractbackend.cpp | 2 +- panel/backends/wayland/CMakeLists.txt | 31 ++++++++++++ .../wayland/lxqttaskbarbackendwayland.h | 1 + .../lxqttaskbarplasmawindowmanagment.cpp | 2 - panel/backends/xcb/CMakeLists.txt | 13 +++++ 7 files changed, 70 insertions(+), 43 deletions(-) diff --git a/panel/CMakeLists.txt b/panel/CMakeLists.txt index 18cd58b01..bbd054495 100644 --- a/panel/CMakeLists.txt +++ b/panel/CMakeLists.txt @@ -1,10 +1,13 @@ set(PROJECT lxqt-panel) -# TODO +# Window Manager abstraction backend add_subdirectory(backends) -# TODO: move to backend folder -find_package(Qt6 ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS WaylandClient Concurrent) +# TODO: allow compile time selection via CMake variables +set(PANEL_BACKENDS + lxqt-panel-backend-wayland + lxqt-panel-backend-xcb +) set(PRIV_HEADERS @@ -25,17 +28,6 @@ set(PRIV_HEADERS config/configstyling.h config/configpluginswidget.h config/addplugindialog.h - - backends/ilxqttaskbarabstractbackend.h - backends/lxqttaskbartypes.h - - backends/lxqttaskbardummybackend.h - - backends/xcb/lxqttaskbarbackend_x11.h - - backends/wayland/lxqttaskbarbackendwayland.h - backends/wayland/lxqttaskbarplasmawindowmanagment.h - backends/wayland/lxqtplasmavirtualdesktop.h ) # using LXQt namespace in the public headers. @@ -44,9 +36,6 @@ set(PUB_HEADERS pluginsettings.h ilxqtpanelplugin.h ilxqtpanel.h - - backends/ilxqttaskbarabstractbackend.h - backends/lxqttaskbartypes.h ) set(SOURCES @@ -66,16 +55,6 @@ set(SOURCES config/configstyling.cpp config/configpluginswidget.cpp config/addplugindialog.cpp - - backends/ilxqttaskbarabstractbackend.cpp - - backends/lxqttaskbardummybackend.cpp - - backends/xcb/lxqttaskbarbackend_x11.cpp - - backends/wayland/lxqttaskbarbackendwayland.cpp - backends/wayland/lxqttaskbarplasmawindowmanagment.cpp - backends/wayland/lxqtplasmavirtualdesktop.cpp ) set(UI @@ -130,24 +109,12 @@ add_executable(${PROJECT} ${UI} ) -# TODO: move to backend folder -qt6_generate_wayland_protocol_client_sources(${PROJECT} -FILES - ${CMAKE_CURRENT_SOURCE_DIR}/backends/wayland/protocols/plasma-window-management.xml -) - -qt6_generate_wayland_protocol_client_sources(${PROJECT} -FILES - ${CMAKE_CURRENT_SOURCE_DIR}/backends/wayland/protocols/org-kde-plasma-virtual-desktop.xml -) - target_link_libraries(${PROJECT} ${LIBRARIES} ${QTX_LIBRARIES} KF6::WindowSystem + ${PANEL_BACKENDS} LayerShellQt::Interface - Qt6::WaylandClient # TODO: Move to backend folder - Qt6::Concurrent ${STATIC_PLUGINS} ) diff --git a/panel/backends/CMakeLists.txt b/panel/backends/CMakeLists.txt index f1915b823..c369eb81f 100644 --- a/panel/backends/CMakeLists.txt +++ b/panel/backends/CMakeLists.txt @@ -1,2 +1,19 @@ +# Common interface for Window Manager abstraction backend +# This also contains dummy backend + +add_library(lxqt-panel-backend-common STATIC + + lxqttaskbartypes.h + ilxqttaskbarabstractbackend.h + ilxqttaskbarabstractbackend.cpp + + lxqttaskbardummybackend.h + lxqttaskbardummybackend.cpp +) + +target_link_libraries(lxqt-panel-backend-common + Qt6::Gui +) + add_subdirectory(wayland) add_subdirectory(xcb) diff --git a/panel/backends/ilxqttaskbarabstractbackend.cpp b/panel/backends/ilxqttaskbarabstractbackend.cpp index 137728263..dbad943d5 100644 --- a/panel/backends/ilxqttaskbarabstractbackend.cpp +++ b/panel/backends/ilxqttaskbarabstractbackend.cpp @@ -1,4 +1,4 @@ -#include "../panel/backends/ilxqttaskbarabstractbackend.h" +#include "ilxqttaskbarabstractbackend.h" ILXQtTaskbarAbstractBackend::ILXQtTaskbarAbstractBackend(QObject *parent) diff --git a/panel/backends/wayland/CMakeLists.txt b/panel/backends/wayland/CMakeLists.txt index 8b1378917..e0ecab6e9 100644 --- a/panel/backends/wayland/CMakeLists.txt +++ b/panel/backends/wayland/CMakeLists.txt @@ -1 +1,32 @@ +# Wayland WM Backend +project(lxqt-panel-backend-wayland) + +find_package(Qt6 ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS WaylandClient Concurrent) + +add_library(lxqt-panel-backend-wayland STATIC + lxqttaskbarbackendwayland.h + lxqttaskbarplasmawindowmanagment.h + lxqtplasmavirtualdesktop.h + + lxqttaskbarbackendwayland.cpp + lxqttaskbarplasmawindowmanagment.cpp + lxqtplasmavirtualdesktop.cpp +) + +qt6_generate_wayland_protocol_client_sources(lxqt-panel-backend-wayland +FILES + ${CMAKE_CURRENT_SOURCE_DIR}/protocols/plasma-window-management.xml +) + +qt6_generate_wayland_protocol_client_sources(lxqt-panel-backend-wayland +FILES + ${CMAKE_CURRENT_SOURCE_DIR}/protocols/org-kde-plasma-virtual-desktop.xml +) + +target_link_libraries(lxqt-panel-backend-wayland + Qt6::GuiPrivate + Qt6::WaylandClient + Qt6::Concurrent + lxqt-panel-backend-common +) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.h b/panel/backends/wayland/lxqttaskbarbackendwayland.h index 0b12ef2fd..df202f076 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.h +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.h @@ -3,6 +3,7 @@ #include "../ilxqttaskbarabstractbackend.h" +#include #include #include diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp index f813181ed..b4339dbb0 100644 --- a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp +++ b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp @@ -1,7 +1,5 @@ #include "lxqttaskbarplasmawindowmanagment.h" -#include - #include #include #include diff --git a/panel/backends/xcb/CMakeLists.txt b/panel/backends/xcb/CMakeLists.txt index 8b1378917..ad36ccaa3 100644 --- a/panel/backends/xcb/CMakeLists.txt +++ b/panel/backends/xcb/CMakeLists.txt @@ -1 +1,14 @@ +# XCB WM Backend + +project(lxqt-panel-backend-xcb) + +add_library(lxqt-panel-backend-xcb STATIC + lxqttaskbarbackend_x11.h + lxqttaskbarbackend_x11.cpp +) + +target_link_libraries(lxqt-panel-backend-xcb + KF6::WindowSystem + lxqt-panel-backend-common +) From 8c5faad35a3fd91f214c8ea87e492e21628b77d4 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 28 Mar 2024 11:21:38 +0100 Subject: [PATCH 161/165] LXQtTaskbarWaylandBackend: possibly fix crash on showDesktop for non- KWin --- panel/backends/wayland/lxqttaskbarbackendwayland.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index fb6e940a8..37b9817b7 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -450,11 +450,14 @@ bool LXQtTaskbarWaylandBackend::isAreaOverlapped(const QRect &area) const bool LXQtTaskbarWaylandBackend::isShowingDesktop() const { - return m_managment->isShowingDesktop(); + return m_managment->isActive() ? m_managment->isShowingDesktop() : false; } bool LXQtTaskbarWaylandBackend::showDesktop(bool value) { + if(!m_managment->isActive()) + return false; + enum LXQtTaskBarPlasmaWindowManagment::show_desktop flag_; if(value) flag_ = LXQtTaskBarPlasmaWindowManagment::show_desktop::show_desktop_enabled; From dcff927a52683c5e2011748cb5a55499d75c8426 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Tue, 9 Apr 2024 11:52:46 +0200 Subject: [PATCH 162/165] Hide lxqt-panel application from applications menu - Add NoDisplay=true to .desktop file --- autostart/lxqt-panel_wayland.desktop.in | 1 + 1 file changed, 1 insertion(+) diff --git a/autostart/lxqt-panel_wayland.desktop.in b/autostart/lxqt-panel_wayland.desktop.in index 089082aea..540955e18 100644 --- a/autostart/lxqt-panel_wayland.desktop.in +++ b/autostart/lxqt-panel_wayland.desktop.in @@ -1,6 +1,7 @@ [Desktop Entry] Type=Application TryExec=lxqt-panel +NoDisplay=true # NOTE: KWin wants absolute path here, get it from CMake install path Exec=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@/lxqt-panel From 4c67d6da328c7ded35315af7361d769d45a23c1f Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Mon, 22 Apr 2024 16:34:42 +0200 Subject: [PATCH 163/165] Update license headers --- .../backends/ilxqttaskbarabstractbackend.cpp | 28 + panel/backends/ilxqttaskbarabstractbackend.h | 28 + panel/backends/lxqttaskbardummybackend.cpp | 28 + panel/backends/lxqttaskbardummybackend.h | 28 + panel/backends/lxqttaskbartypes.h | 28 + .../wayland/lxqtplasmavirtualdesktop.cpp | 9 + .../wayland/lxqtplasmavirtualdesktop.h | 8 + .../wayland/lxqttaskbarbackendwayland.cpp | 27 + .../wayland/lxqttaskbarbackendwayland.h | 27 + .../lxqttaskbarplasmawindowmanagment.cpp | 860 +----------------- .../lxqttaskbarplasmawindowmanagment.h | 8 + panel/backends/xcb/lxqttaskbarbackend_x11.cpp | 28 + panel/backends/xcb/lxqttaskbarbackend_x11.h | 28 + 13 files changed, 286 insertions(+), 849 deletions(-) diff --git a/panel/backends/ilxqttaskbarabstractbackend.cpp b/panel/backends/ilxqttaskbarabstractbackend.cpp index dbad943d5..e47df7555 100644 --- a/panel/backends/ilxqttaskbarabstractbackend.cpp +++ b/panel/backends/ilxqttaskbarabstractbackend.cpp @@ -1,3 +1,31 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXQt - a lightweight, Qt based, desktop toolset + * https://lxqt.org + * + * Copyright: 2023 LXQt team + * Authors: + * Filippo Gentile + * + * This program or library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + + #include "ilxqttaskbarabstractbackend.h" diff --git a/panel/backends/ilxqttaskbarabstractbackend.h b/panel/backends/ilxqttaskbarabstractbackend.h index 44840671a..dbceaff71 100644 --- a/panel/backends/ilxqttaskbarabstractbackend.h +++ b/panel/backends/ilxqttaskbarabstractbackend.h @@ -1,3 +1,31 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXQt - a lightweight, Qt based, desktop toolset + * https://lxqt.org + * + * Copyright: 2023 LXQt team + * Authors: + * Filippo Gentile + * + * This program or library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + + #ifndef ILXQTTASKBARABSTRACTBACKEND_H #define ILXQTTASKBARABSTRACTBACKEND_H diff --git a/panel/backends/lxqttaskbardummybackend.cpp b/panel/backends/lxqttaskbardummybackend.cpp index 15e7e1149..05bbfb0ff 100644 --- a/panel/backends/lxqttaskbardummybackend.cpp +++ b/panel/backends/lxqttaskbardummybackend.cpp @@ -1,3 +1,31 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXQt - a lightweight, Qt based, desktop toolset + * https://lxqt.org + * + * Copyright: 2023 LXQt team + * Authors: + * Filippo Gentile + * + * This program or library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + + #include "lxqttaskbardummybackend.h" #include diff --git a/panel/backends/lxqttaskbardummybackend.h b/panel/backends/lxqttaskbardummybackend.h index 15506838f..64dce2b11 100644 --- a/panel/backends/lxqttaskbardummybackend.h +++ b/panel/backends/lxqttaskbardummybackend.h @@ -1,3 +1,31 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXQt - a lightweight, Qt based, desktop toolset + * https://lxqt.org + * + * Copyright: 2023 LXQt team + * Authors: + * Filippo Gentile + * + * This program or library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + + #ifndef LXQTTASKBARDUMMYBACKEND_H #define LXQTTASKBARDUMMYBACKEND_H diff --git a/panel/backends/lxqttaskbartypes.h b/panel/backends/lxqttaskbartypes.h index 801e8e2d1..e821b410d 100644 --- a/panel/backends/lxqttaskbartypes.h +++ b/panel/backends/lxqttaskbartypes.h @@ -1,3 +1,31 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXQt - a lightweight, Qt based, desktop toolset + * https://lxqt.org + * + * Copyright: 2023 LXQt team + * Authors: + * Filippo Gentile + * + * This program or library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + + #ifndef LXQTTASKBARTYPES_H #define LXQTTASKBARTYPES_H diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp index 01e28fe75..d26574e62 100644 --- a/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp +++ b/panel/backends/wayland/lxqtplasmavirtualdesktop.cpp @@ -1,3 +1,12 @@ +/* + SPDX-FileCopyrightText: 2016 Eike Hein + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL + + Adapted from KDE Plasma Workspace: plasma-workspace/libtaskmanager/waylandtasksmodel.cpp +*/ + + #include "lxqtplasmavirtualdesktop.h" #include diff --git a/panel/backends/wayland/lxqtplasmavirtualdesktop.h b/panel/backends/wayland/lxqtplasmavirtualdesktop.h index fff025213..16935be1a 100644 --- a/panel/backends/wayland/lxqtplasmavirtualdesktop.h +++ b/panel/backends/wayland/lxqtplasmavirtualdesktop.h @@ -1,3 +1,11 @@ +/* + SPDX-FileCopyrightText: 2016 Eike Hein + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL + + Adapted from KDE Plasma Workspace: plasma-workspace/libtaskmanager/waylandtasksmodel.cpp +*/ + #ifndef LXQTPLASMAVIRTUALDESKTOP_H #define LXQTPLASMAVIRTUALDESKTOP_H diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index 37b9817b7..7cc134417 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -1,3 +1,30 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXQt - a lightweight, Qt based, desktop toolset + * https://lxqt.org + * + * Copyright: 2023 LXQt team + * Authors: + * Filippo Gentile + * + * This program or library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + #include "lxqttaskbarbackendwayland.h" #include "lxqttaskbarplasmawindowmanagment.h" diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.h b/panel/backends/wayland/lxqttaskbarbackendwayland.h index df202f076..ff9ec04a9 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.h +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.h @@ -1,3 +1,30 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXQt - a lightweight, Qt based, desktop toolset + * https://lxqt.org + * + * Copyright: 2023 LXQt team + * Authors: + * Filippo Gentile + * + * This program or library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + #ifndef LXQTTASKBARBACKENDWAYLAND_H #define LXQTTASKBARBACKENDWAYLAND_H diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp index b4339dbb0..d64ee9b0d 100644 --- a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp +++ b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.cpp @@ -1,3 +1,11 @@ +/* + SPDX-FileCopyrightText: 2016 Eike Hein + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL + + Adapted from KDE Plasma Workspace: plasma-workspace/libtaskmanager/waylandtasksmodel.cpp +*/ + #include "lxqttaskbarplasmawindowmanagment.h" #include @@ -14,851 +22,9 @@ #include #include - -// QUuid WaylandTasksModel::Private::uuid = QUuid::createUuid(); - -// WaylandTasksModel::Private::Private(WaylandTasksModel *q) -// : q(q) -// { -// } - -// void WaylandTasksModel::Private::init() -// { -// auto clearCacheAndRefresh = [this] { -// if (windows.empty()) { -// return; -// } - -// appDataCache.clear(); - -// // Emit changes of all roles satisfied from app data cache. -// Q_EMIT q->dataChanged(q->index(0, 0), -// q->index(windows.size() - 1, 0), -// QList{Qt::DecorationRole, -// AbstractTasksModel::AppId, -// AbstractTasksModel::AppName, -// AbstractTasksModel::GenericName, -// AbstractTasksModel::LauncherUrl, -// AbstractTasksModel::LauncherUrlWithoutIcon, -// AbstractTasksModel::CanLaunchNewInstance, -// AbstractTasksModel::SkipTaskbar}); -// }; - -// rulesConfig = KSharedConfig::openConfig(QStringLiteral("taskmanagerrulesrc")); -// configWatcher = new KDirWatch(q); - -// for (const QString &location : QStandardPaths::standardLocations(QStandardPaths::ConfigLocation)) { -// configWatcher->addFile(location + QLatin1String("/taskmanagerrulesrc")); -// } - -// auto rulesConfigChange = [this, clearCacheAndRefresh] { -// rulesConfig->reparseConfiguration(); -// clearCacheAndRefresh(); -// }; - -// QObject::connect(configWatcher, &KDirWatch::dirty, rulesConfigChange); -// QObject::connect(configWatcher, &KDirWatch::created, rulesConfigChange); -// QObject::connect(configWatcher, &KDirWatch::deleted, rulesConfigChange); - -// virtualDesktopInfo = new VirtualDesktopInfo(q); - -// initWayland(); -// } - -// void WaylandTasksModel::Private::initWayland() -// { -// if (!KWindowSystem::isPlatformWayland()) { -// return; -// } - -// windowManagement = std::make_unique(); - -// QObject::connect(windowManagement.get(), &PlasmaWindowManagement::activeChanged, q, [this] { -// q->beginResetModel(); -// windows.clear(); -// q->endResetModel(); -// }); - -// QObject::connect(windowManagement.get(), &PlasmaWindowManagement::windowCreated, q, [this](PlasmaWindow *window) { -// connect(window, &PlasmaWindow::initialStateDone, q, [this, window] { -// addWindow(window); -// }); -// }); - -// QObject::connect(windowManagement.get(), &PlasmaWindowManagement::stackingOrderChanged, q, [this](const QString &order) { -// stackingOrder = order.split(QLatin1Char(';')); -// for (const auto &window : std::as_const(windows)) { -// this->dataChanged(window.get(), StackingOrder); -// } -// }); -// } - -// auto WaylandTasksModel::Private::findWindow(PlasmaWindow *window) const -// { -// return std::find_if(windows.begin(), windows.end(), [window](const std::unique_ptr &candidate) { -// return candidate.get() == window; -// }); -// } - -// void WaylandTasksModel::Private::addWindow(PlasmaWindow *window) -// { -// if (findWindow(window) != windows.end() || transients.contains(window)) { -// return; -// } - -// auto removeWindow = [window, this] { -// auto it = findWindow(window); -// if (it != windows.end()) { -// const int row = it - windows.begin(); -// q->beginRemoveRows(QModelIndex(), row, row); -// windows.erase(it); -// transientsDemandingAttention.remove(window); -// appDataCache.remove(window); -// lastActivated.remove(window); -// q->endRemoveRows(); -// } else { // Could be a transient. -// // Removing a transient might change the demands attention state of the leader. -// if (transients.remove(window)) { -// if (PlasmaWindow *leader = transientsDemandingAttention.key(window)) { -// transientsDemandingAttention.remove(leader, window); -// dataChanged(leader, QVector{IsDemandingAttention}); -// } -// } -// } - -// if (activeWindow == window) { -// activeWindow = nullptr; -// } -// }; - -// QObject::connect(window, &PlasmaWindow::unmapped, q, removeWindow); - -// QObject::connect(window, &PlasmaWindow::titleChanged, q, [window, this] { -// this->dataChanged(window, Qt::DisplayRole); -// }); - -// QObject::connect(window, &PlasmaWindow::iconChanged, q, [window, this] { -// // The icon in the AppData struct might come from PlasmaWindow if it wasn't -// // filled in by windowUrlFromMetadata+appDataFromUrl. -// // TODO: Don't evict the cache unnecessarily if this isn't the case. As icons -// // are currently very static on Wayland, this eviction is unlikely to happen -// // frequently as of now. -// appDataCache.remove(window); -// this->dataChanged(window, Qt::DecorationRole); -// }); - -// QObject::connect(window, &PlasmaWindow::appIdChanged, q, [window, this] { -// // The AppData struct in the cache is derived from this and needs -// // to be evicted in favor of a fresh struct based on the changed -// // window metadata. -// appDataCache.remove(window); - -// // Refresh roles satisfied from the app data cache. -// this->dataChanged(window, -// QList{Qt::DecorationRole, AppId, AppName, GenericName, LauncherUrl, LauncherUrlWithoutIcon, SkipTaskbar, CanLaunchNewInstance}); -// }); - -// if (window->windowState & PlasmaWindow::state::state_active) { -// PlasmaWindow *effectiveActive = window; -// while (effectiveActive->parentWindow) { -// effectiveActive = effectiveActive->parentWindow; -// } - -// lastActivated[effectiveActive] = QTime::currentTime(); -// activeWindow = effectiveActive; -// } - -// QObject::connect(window, &PlasmaWindow::activeChanged, q, [window, this] { -// const bool active = window->windowState & PlasmaWindow::state::state_active; - -// PlasmaWindow *effectiveWindow = window; - -// while (effectiveWindow->parentWindow) { -// effectiveWindow = effectiveWindow->parentWindow; -// } - -// if (active) { -// lastActivated[effectiveWindow] = QTime::currentTime(); - -// if (activeWindow != effectiveWindow) { -// activeWindow = effectiveWindow; -// this->dataChanged(effectiveWindow, IsActive); -// } -// } else { -// if (activeWindow == effectiveWindow) { -// activeWindow = nullptr; -// this->dataChanged(effectiveWindow, IsActive); -// } -// } -// }); - -// QObject::connect(window, &PlasmaWindow::parentWindowChanged, q, [window, this] { -// PlasmaWindow *leader = window->parentWindow.data(); - -// // Migrate demanding attention to new leader. -// if (window->windowState.testFlag(PlasmaWindow::state::state_demands_attention)) { -// if (auto *oldLeader = transientsDemandingAttention.key(window)) { -// if (window->parentWindow != oldLeader) { -// transientsDemandingAttention.remove(oldLeader, window); -// transientsDemandingAttention.insert(leader, window); -// dataChanged(oldLeader, QVector{IsDemandingAttention}); -// dataChanged(leader, QVector{IsDemandingAttention}); -// } -// } -// } - -// if (transients.remove(window)) { -// if (leader) { // leader change. -// transients.insert(window, leader); -// } else { // lost a leader, add to regular windows list. -// Q_ASSERT(findWindow(window) == windows.end()); - -// const int count = windows.size(); -// q->beginInsertRows(QModelIndex(), count, count); -// windows.emplace_back(window); -// q->endInsertRows(); -// } -// } else if (leader) { // gained a leader, remove from regular windows list. -// auto it = findWindow(window); -// Q_ASSERT(it != windows.end()); - -// const int row = it - windows.begin(); -// q->beginRemoveRows(QModelIndex(), row, row); -// windows.erase(it); -// appDataCache.remove(window); -// lastActivated.remove(window); -// q->endRemoveRows(); -// } -// }); - -// QObject::connect(window, &PlasmaWindow::closeableChanged, q, [window, this] { -// this->dataChanged(window, IsClosable); -// }); - -// QObject::connect(window, &PlasmaWindow::movableChanged, q, [window, this] { -// this->dataChanged(window, IsMovable); -// }); - -// QObject::connect(window, &PlasmaWindow::resizableChanged, q, [window, this] { -// this->dataChanged(window, IsResizable); -// }); - -// QObject::connect(window, &PlasmaWindow::fullscreenableChanged, q, [window, this] { -// this->dataChanged(window, IsFullScreenable); -// }); - -// QObject::connect(window, &PlasmaWindow::fullscreenChanged, q, [window, this] { -// this->dataChanged(window, IsFullScreen); -// }); - -// QObject::connect(window, &PlasmaWindow::maximizeableChanged, q, [window, this] { -// this->dataChanged(window, IsMaximizable); -// }); - -// QObject::connect(window, &PlasmaWindow::maximizedChanged, q, [window, this] { -// this->dataChanged(window, IsMaximized); -// }); - -// QObject::connect(window, &PlasmaWindow::minimizeableChanged, q, [window, this] { -// this->dataChanged(window, IsMinimizable); -// }); - -// QObject::connect(window, &PlasmaWindow::minimizedChanged, q, [window, this] { -// this->dataChanged(window, IsMinimized); -// }); - -// QObject::connect(window, &PlasmaWindow::keepAboveChanged, q, [window, this] { -// this->dataChanged(window, IsKeepAbove); -// }); - -// QObject::connect(window, &PlasmaWindow::keepBelowChanged, q, [window, this] { -// this->dataChanged(window, IsKeepBelow); -// }); - -// QObject::connect(window, &PlasmaWindow::shadeableChanged, q, [window, this] { -// this->dataChanged(window, IsShadeable); -// }); - -// QObject::connect(window, &PlasmaWindow::virtualDesktopChangeableChanged, q, [window, this] { -// this->dataChanged(window, IsVirtualDesktopsChangeable); -// }); - -// QObject::connect(window, &PlasmaWindow::virtualDesktopEntered, q, [window, this] { -// this->dataChanged(window, VirtualDesktops); - -// // If the count has changed from 0, the window may no longer be on all virtual -// // desktops. -// if (window->virtualDesktops.count() > 0) { -// this->dataChanged(window, IsOnAllVirtualDesktops); -// } -// }); - -// QObject::connect(window, &PlasmaWindow::virtualDesktopLeft, q, [window, this] { -// this->dataChanged(window, VirtualDesktops); - -// // If the count has changed to 0, the window is now on all virtual desktops. -// if (window->virtualDesktops.count() == 0) { -// this->dataChanged(window, IsOnAllVirtualDesktops); -// } -// }); - -// QObject::connect(window, &PlasmaWindow::geometryChanged, q, [window, this] { -// this->dataChanged(window, QList{Geometry, ScreenGeometry}); -// }); - -// QObject::connect(window, &PlasmaWindow::demandsAttentionChanged, q, [window, this] { -// // Changes to a transient's state might change demands attention state for leader. -// if (auto *leader = transients.value(window)) { -// if (window->windowState.testFlag(PlasmaWindow::state::state_demands_attention)) { -// if (!transientsDemandingAttention.values(leader).contains(window)) { -// transientsDemandingAttention.insert(leader, window); -// this->dataChanged(leader, QVector{IsDemandingAttention}); -// } -// } else if (transientsDemandingAttention.remove(window)) { -// this->dataChanged(leader, QVector{IsDemandingAttention}); -// } -// } else { -// this->dataChanged(window, QVector{IsDemandingAttention}); -// } -// }); - -// QObject::connect(window, &PlasmaWindow::skipTaskbarChanged, q, [window, this] { -// this->dataChanged(window, SkipTaskbar); -// }); - -// QObject::connect(window, &PlasmaWindow::applicationMenuChanged, q, [window, this] { -// this->dataChanged(window, QList{ApplicationMenuServiceName, ApplicationMenuObjectPath}); -// }); - -// QObject::connect(window, &PlasmaWindow::activitiesChanged, q, [window, this] { -// this->dataChanged(window, Activities); -// }); - -// // Handle transient. -// if (PlasmaWindow *leader = window->parentWindow.data()) { -// transients.insert(window, leader); - -// // Update demands attention state for leader. -// if (window->windowState.testFlag(PlasmaWindow::state::state_demands_attention)) { -// transientsDemandingAttention.insert(leader, window); -// dataChanged(leader, QVector{IsDemandingAttention}); -// } -// } else { -// const int count = windows.size(); - -// q->beginInsertRows(QModelIndex(), count, count); - -// windows.emplace_back(window); - -// q->endInsertRows(); -// } -// } - -// const AppData &WaylandTasksModel::Private::appData(PlasmaWindow *window) -// { -// static_assert(!std::is_trivially_copy_assignable_v); -// if (auto it = appDataCache.constFind(window); it != appDataCache.constEnd()) { -// return *it; -// } - -// return *appDataCache.emplace(window, appDataFromUrl(windowUrlFromMetadata(window->appId, window->pid, rulesConfig, window->resourceName))); -// } - -// QIcon WaylandTasksModel::Private::icon(PlasmaWindow *window) -// { -// const AppData &app = appData(window); - -// if (!app.icon.isNull()) { -// return app.icon; -// } - -// appDataCache[window].icon = window->icon; - -// return window->icon; -// } - -// QString WaylandTasksModel::Private::mimeType() -// { -// // Use a unique format id to make this intentionally useless for -// // cross-process DND. -// return QStringLiteral("windowsystem/winid+") + uuid.toString(); -// } - -// QString WaylandTasksModel::Private::groupMimeType() -// { -// // Use a unique format id to make this intentionally useless for -// // cross-process DND. -// return QStringLiteral("windowsystem/multiple-winids+") + uuid.toString(); -// } - -// void WaylandTasksModel::Private::dataChanged(PlasmaWindow *window, int role) -// { -// auto it = findWindow(window); -// if (it == windows.end()) { -// return; -// } -// QModelIndex idx = q->index(it - windows.begin()); -// Q_EMIT q->dataChanged(idx, idx, QList{role}); -// } - -// void WaylandTasksModel::Private::dataChanged(PlasmaWindow *window, const QList &roles) -// { -// auto it = findWindow(window); -// if (it == windows.end()) { -// return; -// } -// QModelIndex idx = q->index(it - windows.begin()); -// Q_EMIT q->dataChanged(idx, idx, roles); -// } - -// WaylandTasksModel::WaylandTasksModel(QObject *parent) -// : AbstractWindowTasksModel(parent) -// , d(new Private(this)) -// { -// d->init(); -// } - -// WaylandTasksModel::~WaylandTasksModel() = default; - -// QVariant WaylandTasksModel::data(const QModelIndex &index, int role) const -// { -// // Note: when index is valid, its row >= 0, so casting to unsigned is safe -// if (!index.isValid() || static_cast(index.row()) >= d->windows.size()) { -// return QVariant(); -// } - -// PlasmaWindow *window = d->windows.at(index.row()).get(); - -// if (role == Qt::DisplayRole) { -// return window->title; -// } else if (role == Qt::DecorationRole) { -// return d->icon(window); -// } else if (role == AppId) { -// const QString &id = d->appData(window).id; - -// if (id.isEmpty()) { -// return window->appId; -// } else { -// return id; -// } -// } else if (role == AppName) { -// return d->appData(window).name; -// } else if (role == GenericName) { -// return d->appData(window).genericName; -// } else if (role == LauncherUrl || role == LauncherUrlWithoutIcon) { -// return d->appData(window).url; -// } else if (role == WinIdList) { -// return QVariantList{window->uuid}; -// } else if (role == MimeType) { -// return d->mimeType(); -// } else if (role == MimeData) { -// return window->uuid; -// } else if (role == IsWindow) { -// return true; -// } else if (role == IsActive) { -// return (window == d->activeWindow); -// } else if (role == IsClosable) { -// return window->windowState.testFlag(PlasmaWindow::state::state_closeable); -// } else if (role == IsMovable) { -// return window->windowState.testFlag(PlasmaWindow::state::state_movable); -// } else if (role == IsResizable) { -// return window->windowState.testFlag(PlasmaWindow::state::state_resizable); -// } else if (role == IsMaximizable) { -// return window->windowState.testFlag(PlasmaWindow::state::state_maximizable); -// } else if (role == IsMaximized) { -// return window->windowState.testFlag(PlasmaWindow::state::state_maximized); -// } else if (role == IsMinimizable) { -// return window->windowState.testFlag(PlasmaWindow::state::state_minimizable); -// } else if (role == IsMinimized || role == IsHidden) { -// return window->windowState.testFlag(PlasmaWindow::state::state_minimized); -// } else if (role == IsKeepAbove) { -// return window->windowState.testFlag(PlasmaWindow::state::state_keep_above); -// } else if (role == IsKeepBelow) { -// return window->windowState.testFlag(PlasmaWindow::state::state_keep_below); -// } else if (role == IsFullScreenable) { -// return window->windowState.testFlag(PlasmaWindow::state::state_fullscreenable); -// } else if (role == IsFullScreen) { -// return window->windowState.testFlag(PlasmaWindow::state::state_fullscreen); -// } else if (role == IsShadeable) { -// return window->windowState.testFlag(PlasmaWindow::state::state_shadeable); -// } else if (role == IsShaded) { -// return window->windowState.testFlag(PlasmaWindow::state::state_shaded); -// } else if (role == IsVirtualDesktopsChangeable) { -// return window->windowState.testFlag(PlasmaWindow::state::state_virtual_desktop_changeable); -// } else if (role == VirtualDesktops) { -// return window->virtualDesktops; -// } else if (role == IsOnAllVirtualDesktops) { -// return window->virtualDesktops.isEmpty(); -// } else if (role == Geometry) { -// return window->geometry; -// } else if (role == ScreenGeometry) { -// return screenGeometry(window->geometry.center()); -// } else if (role == Activities) { -// return window->activities; -// } else if (role == IsDemandingAttention) { -// return window->windowState.testFlag(PlasmaWindow::state::state_demands_attention) || d->transientsDemandingAttention.contains(window); -// } else if (role == SkipTaskbar) { -// return window->windowState.testFlag(PlasmaWindow::state::state_skiptaskbar) || d->appData(window).skipTaskbar; -// } else if (role == SkipPager) { -// // FIXME Implement. -// } else if (role == AppPid) { -// return window->pid; -// } else if (role == StackingOrder) { -// return d->stackingOrder.indexOf(window->uuid); -// } else if (role == LastActivated) { -// if (d->lastActivated.contains(window)) { -// return d->lastActivated.value(window); -// } -// } else if (role == ApplicationMenuObjectPath) { -// return window->applicationMenuObjectPath; -// } else if (role == ApplicationMenuServiceName) { -// return window->applicationMenuService; -// } else if (role == CanLaunchNewInstance) { -// return canLauchNewInstance(d->appData(window)); -// } - -// return AbstractTasksModel::data(index, role); -// } - -// int WaylandTasksModel::rowCount(const QModelIndex &parent) const -// { -// return parent.isValid() ? 0 : d->windows.size(); -// } - -// QModelIndex WaylandTasksModel::index(int row, int column, const QModelIndex &parent) const -// { -// return hasIndex(row, column, parent) ? createIndex(row, column, d->windows.at(row).get()) : QModelIndex(); -// } - -// void WaylandTasksModel::requestActivate(const QModelIndex &index) -// { -// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { -// return; -// } - -// PlasmaWindow *window = d->windows.at(index.row()).get(); - -// // Pull forward any transient demanding attention. -// if (auto *transientDemandingAttention = d->transientsDemandingAttention.value(window)) { -// window = transientDemandingAttention; -// } else { -// // TODO Shouldn't KWin take care of that? -// // Bringing a transient to the front usually brings its parent with it -// // but focus is not handled properly. -// // TODO take into account d->lastActivation instead -// // of just taking the first one. -// while (d->transients.key(window)) { -// window = d->transients.key(window); -// } -// } - -// window->set_state(PlasmaWindow::state::state_active, PlasmaWindow::state::state_active); -// } - -// void WaylandTasksModel::requestNewInstance(const QModelIndex &index) -// { -// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { -// return; -// } - -// runApp(d->appData(d->windows.at(index.row()).get())); -// } - -// void WaylandTasksModel::requestOpenUrls(const QModelIndex &index, const QList &urls) -// { -// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent) || urls.isEmpty()) { -// return; -// } - -// runApp(d->appData(d->windows.at(index.row()).get()), urls); -// } - -// void WaylandTasksModel::requestClose(const QModelIndex &index) -// { -// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { -// return; -// } - -// d->windows.at(index.row())->close(); -// } - -// void WaylandTasksModel::requestMove(const QModelIndex &index) -// { -// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { -// return; -// } - -// auto &window = d->windows.at(index.row()); - -// window->set_state(PlasmaWindow::state::state_active, PlasmaWindow::state::state_active); -// window->request_move(); -// } - -// void WaylandTasksModel::requestResize(const QModelIndex &index) -// { -// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { -// return; -// } - -// auto &window = d->windows.at(index.row()); - -// window->set_state(PlasmaWindow::state::state_active, PlasmaWindow::state::state_active); -// window->request_resize(); -// } - -// void WaylandTasksModel::requestToggleMinimized(const QModelIndex &index) -// { -// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { -// return; -// } - -// auto &window = d->windows.at(index.row()); - -// if (window->windowState & PlasmaWindow::state::state_minimized) { -// window->set_state(PlasmaWindow::state::state_minimized, 0); -// } else { -// window->set_state(PlasmaWindow::state::state_minimized, PlasmaWindow::state::state_minimized); -// } -// } - -// void WaylandTasksModel::requestToggleMaximized(const QModelIndex &index) -// { -// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { -// return; -// } - -// auto &window = d->windows.at(index.row()); - -// if (window->windowState & PlasmaWindow::state::state_maximized) { -// window->set_state(PlasmaWindow::state::state_maximized | PlasmaWindow::state::state_active, PlasmaWindow::state::state_active); -// } else { -// window->set_state(PlasmaWindow::state::state_maximized | PlasmaWindow::state::state_active, -// PlasmaWindow::state::state_maximized | PlasmaWindow::state::state_active); -// } -// } - -// void WaylandTasksModel::requestToggleKeepAbove(const QModelIndex &index) -// { -// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { -// return; -// } - -// auto &window = d->windows.at(index.row()); - -// if (window->windowState & PlasmaWindow::state::state_keep_above) { -// window->set_state(PlasmaWindow::state::state_keep_above, 0); -// } else { -// window->set_state(PlasmaWindow::state::state_keep_above, PlasmaWindow::state::state_keep_above); -// } -// } - -// void WaylandTasksModel::requestToggleKeepBelow(const QModelIndex &index) -// { -// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { -// return; -// } -// auto &window = d->windows.at(index.row()); - -// if (window->windowState & PlasmaWindow::state::state_keep_below) { -// window->set_state(PlasmaWindow::state::state_keep_below, 0); -// } else { -// window->set_state(PlasmaWindow::state::state_keep_below, PlasmaWindow::state::state_keep_below); -// } -// } - -// void WaylandTasksModel::requestToggleFullScreen(const QModelIndex &index) -// { -// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { -// return; -// } - -// auto &window = d->windows.at(index.row()); - -// if (window->windowState & PlasmaWindow::state::state_fullscreen) { -// window->set_state(PlasmaWindow::state::state_fullscreen, 0); -// } else { -// window->set_state(PlasmaWindow::state::state_fullscreen, PlasmaWindow::state::state_fullscreen); -// } -// } - -// void WaylandTasksModel::requestToggleShaded(const QModelIndex &index) -// { -// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { -// return; -// } - -// auto &window = d->windows.at(index.row()); - -// if (window->windowState & PlasmaWindow::state::state_shaded) { -// window->set_state(PlasmaWindow::state::state_shaded, 0); -// } else { -// window->set_state(PlasmaWindow::state::state_shaded, PlasmaWindow::state::state_shaded); -// }; -// } - -// void WaylandTasksModel::requestVirtualDesktops(const QModelIndex &index, const QVariantList &desktops) -// { -// // FIXME TODO: Lacks the "if we've requested the current desktop, force-activate -// // the window" logic from X11 version. This behavior should be in KWin rather than -// // libtm however. - -// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { -// return; -// } - -// auto &window = d->windows.at(index.row()); - -// if (desktops.isEmpty()) { -// const QStringList virtualDesktops = window->virtualDesktops; -// for (const QString &desktop : virtualDesktops) { -// window->request_leave_virtual_desktop(desktop); -// } -// } else { -// const QStringList &now = window->virtualDesktops; -// QStringList next; - -// for (const QVariant &desktop : desktops) { -// const QString &desktopId = desktop.toString(); - -// if (!desktopId.isEmpty()) { -// next << desktopId; - -// if (!now.contains(desktopId)) { -// window->request_enter_virtual_desktop(desktopId); -// } -// } -// } - -// for (const QString &desktop : now) { -// if (!next.contains(desktop)) { -// window->request_leave_virtual_desktop(desktop); -// } -// } -// } -// } - -// void WaylandTasksModel::requestNewVirtualDesktop(const QModelIndex &index) -// { -// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { -// return; -// } - -// d->windows.at(index.row())->request_enter_new_virtual_desktop(); -// } - -// void WaylandTasksModel::requestActivities(const QModelIndex &index, const QStringList &activities) -// { -// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { -// return; -// } - -// auto &window = d->windows.at(index.row()); -// const auto newActivities = QSet(activities.begin(), activities.end()); -// const auto plasmaActivities = window->activities; -// const auto oldActivities = QSet(plasmaActivities.begin(), plasmaActivities.end()); - -// const auto activitiesToAdd = newActivities - oldActivities; -// for (const auto &activity : activitiesToAdd) { -// window->request_enter_activity(activity); -// } - -// const auto activitiesToRemove = oldActivities - newActivities; -// for (const auto &activity : activitiesToRemove) { -// window->request_leave_activity(activity); -// } -// } - -// void WaylandTasksModel::requestPublishDelegateGeometry(const QModelIndex &index, const QRect &geometry, QObject *delegate) -// { -// /* -// FIXME: This introduces the dependency on Qt::Quick. I might prefer -// reversing this and publishing the window pointer through the model, -// then calling PlasmaWindow::setMinimizeGeometry in the applet backend, -// rather than hand delegate items into the lib, keeping the lib more UI- -// agnostic. -// */ - -// Q_UNUSED(geometry) - -// if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid | QAbstractItemModel::CheckIndexOption::DoNotUseParent)) { -// return; -// } - -// const QQuickItem *item = qobject_cast(delegate); - -// if (!item || !item->parentItem()) { -// return; -// } - -// QWindow *itemWindow = item->window(); - -// if (!itemWindow) { -// return; -// } - -// auto waylandWindow = itemWindow->nativeInterface(); - -// if (!waylandWindow || !waylandWindow->surface()) { -// return; -// } - -// QRect rect(item->x(), item->y(), item->width(), item->height()); -// rect.moveTopLeft(item->parentItem()->mapToScene(rect.topLeft()).toPoint()); - -// auto &window = d->windows.at(index.row()); - -// window->set_minimized_geometry(waylandWindow->surface(), rect.x(), rect.y(), rect.width(), rect.height()); -// } - -// QUuid WaylandTasksModel::winIdFromMimeData(const QMimeData *mimeData, bool *ok) -// { -// Q_ASSERT(mimeData); - -// if (ok) { -// *ok = false; -// } - -// if (!mimeData->hasFormat(Private::mimeType())) { -// return {}; -// } - -// QUuid id(mimeData->data(Private::mimeType())); -// *ok = !id.isNull(); - -// return id; -// } - -// QList WaylandTasksModel::winIdsFromMimeData(const QMimeData *mimeData, bool *ok) -// { -// Q_ASSERT(mimeData); -// QList ids; - -// if (ok) { -// *ok = false; -// } - -// if (!mimeData->hasFormat(Private::groupMimeType())) { -// // Try to extract single window id. -// bool singularOk; -// QUuid id = winIdFromMimeData(mimeData, &singularOk); - -// if (ok) { -// *ok = singularOk; -// } - -// if (singularOk) { -// ids << id; -// } - -// return ids; -// } - -// // FIXME: Extracting multiple winids is still unimplemented; -// // TaskGroupingProxy::data(..., ::MimeData) can't produce -// // a payload with them anyways. - -// return ids; -// } +/* + * LXQtTaskBarPlasmaWindow + */ LXQtTaskBarPlasmaWindow::LXQtTaskBarPlasmaWindow(const QString &uuid, ::org_kde_plasma_window *id) : org_kde_plasma_window(id) @@ -866,10 +32,6 @@ LXQtTaskBarPlasmaWindow::LXQtTaskBarPlasmaWindow(const QString &uuid, ::org_kde_ { } -/* - * LXQtTaskBarPlasmaWindow - */ - LXQtTaskBarPlasmaWindow::~LXQtTaskBarPlasmaWindow() { destroy(); diff --git a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h index 976ac53e2..b7c6d1f00 100644 --- a/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h +++ b/panel/backends/wayland/lxqttaskbarplasmawindowmanagment.h @@ -1,3 +1,11 @@ +/* + SPDX-FileCopyrightText: 2016 Eike Hein + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL + + Adapted from KDE Plasma Workspace: plasma-workspace/libtaskmanager/waylandtasksmodel.cpp +*/ + #ifndef LXQTTASKBARPLASMAWINDOWMANAGMENT_H #define LXQTTASKBARPLASMAWINDOWMANAGMENT_H diff --git a/panel/backends/xcb/lxqttaskbarbackend_x11.cpp b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp index fe0452776..8156a729e 100644 --- a/panel/backends/xcb/lxqttaskbarbackend_x11.cpp +++ b/panel/backends/xcb/lxqttaskbarbackend_x11.cpp @@ -1,3 +1,31 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXQt - a lightweight, Qt based, desktop toolset + * https://lxqt.org + * + * Copyright: 2023 LXQt team + * Authors: + * Filippo Gentile + * + * This program or library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + + #include "lxqttaskbarbackend_x11.h" #include diff --git a/panel/backends/xcb/lxqttaskbarbackend_x11.h b/panel/backends/xcb/lxqttaskbarbackend_x11.h index 2478b3fff..fe2e30783 100644 --- a/panel/backends/xcb/lxqttaskbarbackend_x11.h +++ b/panel/backends/xcb/lxqttaskbarbackend_x11.h @@ -1,3 +1,31 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXQt - a lightweight, Qt based, desktop toolset + * https://lxqt.org + * + * Copyright: 2023 LXQt team + * Authors: + * Filippo Gentile + * + * This program or library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + + #ifndef LXQTTASKBARBACKEND_X11_H #define LXQTTASKBARBACKEND_X11_H From e2b97ef29ab47152c414df5a794194f67e47ce0f Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Mon, 22 Apr 2024 16:42:56 +0200 Subject: [PATCH 164/165] CMake: rename autostart desktop variable --- autostart/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/autostart/CMakeLists.txt b/autostart/CMakeLists.txt index 6d044738b..f56d282ff 100644 --- a/autostart/CMakeLists.txt +++ b/autostart/CMakeLists.txt @@ -1,9 +1,9 @@ -set(DESKTOP_FILES lxqt-panel.desktop.in) +set(AUTOSTART_DESKTOP_FILES_IN lxqt-panel.desktop.in) # Translations ********************************** lxqt_translate_desktop(DESKTOP_FILES SOURCES - ${DESKTOP_FILES_IN} + ${AUTOSTART_DESKTOP_FILES_IN} USE_YAML ) add_custom_target(lxqt_panel_autostart_desktop_files ALL DEPENDS ${DESKTOP_FILES}) From 1fc55941df0a55bf52e1f82b0a4864a6e8fe333b Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Sat, 8 Jun 2024 17:40:49 +0200 Subject: [PATCH 165/165] LXQtTaskbarWaylandBackend: add dummy setDesktopLayout() --- panel/backends/wayland/lxqttaskbarbackendwayland.cpp | 6 ++++++ panel/backends/wayland/lxqttaskbarbackendwayland.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp index 7cc134417..1d0644284 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.cpp +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.cpp @@ -440,6 +440,12 @@ bool LXQtTaskbarWaylandBackend::isWindowOnScreen(QScreen *screen, WId windowId) return screen->geometry().intersects(window->geometry); } +bool LXQtTaskbarWaylandBackend::setDesktopLayout(Qt::Orientation, int, int, bool) +{ + //TODO: implement + return false; +} + void LXQtTaskbarWaylandBackend::moveApplication(WId windowId) { LXQtTaskBarPlasmaWindow *window = getWindow(windowId); diff --git a/panel/backends/wayland/lxqttaskbarbackendwayland.h b/panel/backends/wayland/lxqttaskbarbackendwayland.h index ff9ec04a9..22a699fa8 100644 --- a/panel/backends/wayland/lxqttaskbarbackendwayland.h +++ b/panel/backends/wayland/lxqttaskbarbackendwayland.h @@ -85,6 +85,8 @@ class LXQtTaskbarWaylandBackend : public ILXQtTaskbarAbstractBackend virtual bool isWindowOnScreen(QScreen *screen, WId windowId) const override; + virtual bool setDesktopLayout(Qt::Orientation orientation, int rows, int columns, bool rightToLeft) override; + // X11 Specific virtual void moveApplication(WId windowId) override; virtual void resizeApplication(WId windowId) override;