diff --git a/libtaskmanager/CMakeLists.txt b/libtaskmanager/CMakeLists.txt --- a/libtaskmanager/CMakeLists.txt +++ b/libtaskmanager/CMakeLists.txt @@ -24,6 +24,7 @@ if (X11_FOUND) set(taskmanager_LIB_SRCS ${taskmanager_LIB_SRCS} + xwindowsystemeventbatcher.cpp xwindowtasksmodel.cpp ) endif() diff --git a/libtaskmanager/TODO.txt b/libtaskmanager/TODO.txt --- a/libtaskmanager/TODO.txt +++ b/libtaskmanager/TODO.txt @@ -2,7 +2,6 @@ - Implement missing kwayland bits (e.g. transient handling, virtual desktop logic). Other: -- Old lib compressed window changes over 200ms before grabbing new data from the server, might need to be added back to avoid a DoS vector. - Tests tests tests. - Code cleanup and addressing FIXMEs. - Reconsider the library exports. diff --git a/libtaskmanager/xwindowsystemeventbatcher.h b/libtaskmanager/xwindowsystemeventbatcher.h new file mode 100644 --- /dev/null +++ b/libtaskmanager/xwindowsystemeventbatcher.h @@ -0,0 +1,51 @@ +/******************************************************************** +Copyright 2017 David Edmundson + +This 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) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +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, see . +*********************************************************************/ + +#ifndef XWIDOWSYSTEMEVENTBATCHER_H +#define XWIDOWSYSTEMEVENTBATCHER_H + +#include + +#include + +/* + * Relay class for KWindowSystem events that batches updates + */ +class XWindowSystemEventBatcher : public QObject +{ + Q_OBJECT +public: + XWindowSystemEventBatcher(QObject *parent); +Q_SIGNALS: + void windowAdded(WId window); + void windowRemoved(WId window); + void windowChanged(WId window, NET::Properties properties, NET::Properties2 properties2); +protected: + void timerEvent(QTimerEvent *event); +private: + struct AllProps { + NET::Properties properties = 0; + NET::Properties2 properties2 = 0; + }; + QHash m_cache; + int m_timerId = 0; +}; + +#endif diff --git a/libtaskmanager/xwindowsystemeventbatcher.cpp b/libtaskmanager/xwindowsystemeventbatcher.cpp new file mode 100644 --- /dev/null +++ b/libtaskmanager/xwindowsystemeventbatcher.cpp @@ -0,0 +1,80 @@ +/******************************************************************** +Copyright 2017 David Edmundson + +This 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) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +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, see . +*********************************************************************/ + +#include "xwindowsystemeventbatcher.h" + +#include +#include +#include + +#define BATCH_TIME 10 + +static const NET::Properties s_cachableProperties = NET::WMName | NET::WMVisibleName; +static const NET::Properties2 s_cachableProperties2 = NET::WM2UserTime; + +XWindowSystemEventBatcher::XWindowSystemEventBatcher(QObject* parent) + : QObject(parent) +{ + connect(KWindowSystem::self(), &KWindowSystem::windowAdded, this, &XWindowSystemEventBatcher::windowAdded); + + //remove our cache entries when we lose a window, otherwise we might fire change signals after a window is destroyed which wouldn't make sense + connect(KWindowSystem::self(), &KWindowSystem::windowRemoved, this, [this](WId wid) { + m_cache.remove(wid); + emit windowRemoved(wid); + }); + + void (KWindowSystem::*myWindowChangeSignal)(WId window, + NET::Properties properties, NET::Properties2 properties2) = &KWindowSystem::windowChanged; + QObject::connect(KWindowSystem::self(), myWindowChangeSignal, this, + [this](WId window, NET::Properties properties, NET::Properties2 properties2) { + //if properties contained only cachable flags + if ((properties | s_cachableProperties) == s_cachableProperties && + (properties2 | s_cachableProperties2) == s_cachableProperties2) { + m_cache[window].properties |= properties; + m_cache[window].properties2 |= properties2; + if (!m_timerId) { + m_timerId = startTimer(BATCH_TIME); + } + } else { + //submit all caches along with any real updates + auto it = m_cache.constFind(window); + if (it != m_cache.constEnd()) { + properties |= it->properties; + properties2 |= it->properties2; + m_cache.erase(it); + } + emit windowChanged(window, properties, properties2); + } + } + ); +} + +void XWindowSystemEventBatcher::timerEvent(QTimerEvent* event) +{ + if (event->timerId() != m_timerId) { + return; + } + for (auto it = m_cache.constBegin(); it!= m_cache.constEnd(); it++) { + emit windowChanged(it.key(), it.value().properties, it.value().properties2); + }; + m_cache.clear(); + killTimer(m_timerId); + m_timerId = 0; +} diff --git a/libtaskmanager/xwindowtasksmodel.cpp b/libtaskmanager/xwindowtasksmodel.cpp --- a/libtaskmanager/xwindowtasksmodel.cpp +++ b/libtaskmanager/xwindowtasksmodel.cpp @@ -21,6 +21,7 @@ #include "xwindowtasksmodel.h" #include "tasktools.h" +#include "xwindowsystemeventbatcher.h" #include #include @@ -149,21 +150,21 @@ QObject::connect(configWatcher, &KDirWatch::created, rulesConfigChange); QObject::connect(configWatcher, &KDirWatch::deleted, rulesConfigChange); - QObject::connect(KWindowSystem::self(), &KWindowSystem::windowAdded, q, + auto windowSystem = new XWindowSystemEventBatcher(q); + + QObject::connect(windowSystem, &XWindowSystemEventBatcher::windowAdded, q, [this](WId window) { addWindow(window); } ); - QObject::connect(KWindowSystem::self(), &KWindowSystem::windowRemoved, q, + QObject::connect(windowSystem, &XWindowSystemEventBatcher::windowRemoved, q, [this](WId window) { removeWindow(window); } ); - void (KWindowSystem::*myWindowChangeSignal)(WId window, - NET::Properties properties, NET::Properties2 properties2) = &KWindowSystem::windowChanged; - QObject::connect(KWindowSystem::self(), myWindowChangeSignal, q, + QObject::connect(windowSystem, &XWindowSystemEventBatcher::windowChanged, q, [this](WId window, NET::Properties properties, NET::Properties2 properties2) { windowChanged(window, properties, properties2); }