Advance Wayland and KDE package bring-up

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-04-14 10:51:06 +01:00
parent 51f3c21121
commit cf12defd28
15214 changed files with 20594243 additions and 269 deletions
@@ -0,0 +1,98 @@
configure_file(config-kidletime.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kidletime.h )
add_library(KF6IdleTime)
add_library(KF6::IdleTime ALIAS KF6IdleTime)
set_target_properties(KF6IdleTime PROPERTIES
VERSION ${KIDLETIME_VERSION}
SOVERSION ${KIDLETIME_SOVERSION}
EXPORT_NAME IdleTime
)
target_sources(KF6IdleTime PRIVATE
kidletime.cpp
kabstractidletimepoller.cpp
kwindowbasedidletimepoller.cpp
)
ecm_qt_declare_logging_category(KF6IdleTime
HEADER logging.h
IDENTIFIER KIDLETIME
CATEGORY_NAME kf.idletime
OLD_CATEGORY_NAMES org.kde.kf5.idletime
DESCRIPTION "kidletime (lib)"
EXPORT KIDLETIME
)
ecm_generate_export_header(KF6IdleTime
BASE_NAME KIdleTime
GROUP_BASE_NAME KF
VERSION ${KF_VERSION}
USE_VERSION_HEADER
DEPRECATED_BASE_VERSION 0
DEPRECATION_VERSIONS
EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
)
target_include_directories(KF6IdleTime INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/KIdleTime>")
target_link_libraries(KF6IdleTime PUBLIC Qt6::Core)
target_link_libraries(KF6IdleTime PRIVATE Qt6::Gui)
add_subdirectory(plugins)
ecm_generate_headers(KIdleTime_HEADERS
HEADER_NAMES
KIdleTime
REQUIRED_HEADERS KIdleTime_HEADERS
)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/kidletime_export.h
${KIdleTime_HEADERS}
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KIdleTime COMPONENT Devel
)
install(
FILES
kabstractidletimepoller_p.h
DESTINATION
${KDE_INSTALL_INCLUDEDIR_KF}/KIdleTime/private
COMPONENT
Devel
)
install(TARGETS KF6IdleTime EXPORT KF6IdleTimeTargets ${KF_INSTALL_TARGETS_DEFAULT_ARGS})
ecm_qt_install_logging_categories(
EXPORT KIDLETIME
FILE kidletime.categories
DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}
)
if(BUILD_QCH)
ecm_add_qch(
KF6IdleTime_QCH
NAME KIdleTime
BASE_NAME KF6IdleTime
VERSION ${KF_VERSION}
ORG_DOMAIN org.kde
SOURCES # using only public headers, to cover only public API
${KIdleTime_HEADERS}
MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md"
LINK_QCHS
Qt6Core_QCH
INCLUDE_DIRS
${CMAKE_CURRENT_BINARY_DIR}
BLANK_MACROS
KIDLETIME_EXPORT
"KIDLETIME_DEPRECATED_VERSION(x, y, t)"
TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
COMPONENT Devel
)
endif()
@@ -0,0 +1,10 @@
/* config-kidletime.h. Generated by cmake from config-kidletime.h.cmake */
/* Define if you have the XScreensaver extension */
#cmakedefine01 HAVE_XSCREENSAVER
/* Define if you have the XSync extension */
#cmakedefine01 HAVE_XSYNC
/* Define if you have X11 */
#cmakedefine01 HAVE_X11
@@ -0,0 +1,18 @@
/* This file is part of the KDE libraries
* SPDX-FileCopyrightText: 2009 Dario Freddi <drf at kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "kabstractidletimepoller_p.h"
KAbstractIdleTimePoller::KAbstractIdleTimePoller(QObject *parent)
: QObject(parent)
{
}
KAbstractIdleTimePoller::~KAbstractIdleTimePoller()
{
}
#include "moc_kabstractidletimepoller_p.cpp"
@@ -0,0 +1,47 @@
/* This file is part of the KDE libraries
* SPDX-FileCopyrightText: 2009 Dario Freddi <drf at kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef KABSTRACTIDLETIMEPOLLER_P_H
#define KABSTRACTIDLETIMEPOLLER_P_H
#include <kidletime_export.h>
#include <QObject>
#define KAbstractIdleTimePoller_iid "org.kde.kidletime.KAbstractIdleTimePoller"
class KIDLETIME_EXPORT KAbstractIdleTimePoller : public QObject
{
Q_OBJECT
public:
KAbstractIdleTimePoller(QObject *parent = nullptr);
~KAbstractIdleTimePoller() override;
virtual bool isAvailable() = 0;
virtual bool setUpPoller() = 0;
virtual void unloadPoller() = 0;
public Q_SLOTS:
virtual void addTimeout(int nextTimeout) = 0;
virtual void removeTimeout(int nextTimeout) = 0;
// TODO KF6: Make it a public method
virtual QList<int> timeouts() const = 0; // clazy:exclude=const-signal-or-slot
virtual int forcePollRequest() = 0;
virtual void catchIdleEvent() = 0;
virtual void stopCatchingIdleEvents() = 0;
virtual void simulateUserActivity() = 0;
Q_SIGNALS:
void resumingFromIdle();
void timeoutReached(int msec);
};
Q_DECLARE_INTERFACE(KAbstractIdleTimePoller, KAbstractIdleTimePoller_iid)
#endif /* KABSTRACTIDLETIMEPOLLER_P_H */
@@ -0,0 +1,327 @@
/* This file is part of the KDE libraries
* SPDX-FileCopyrightText: 2009 Dario Freddi <drf at kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "kidletime.h"
#include <config-kidletime.h>
#include "kabstractidletimepoller_p.h"
#include "logging.h"
#include <QDir>
#include <QGuiApplication>
#include <QJsonArray>
#include <QPluginLoader>
#include <QPointer>
#include <QSet>
class KIdleTimeHelper
{
public:
KIdleTimeHelper()
: q(nullptr)
{
}
~KIdleTimeHelper()
{
delete q;
}
KIdleTimeHelper(const KIdleTimeHelper &) = delete;
KIdleTimeHelper &operator=(const KIdleTimeHelper &) = delete;
KIdleTime *q;
};
Q_GLOBAL_STATIC(KIdleTimeHelper, s_globalKIdleTime)
KIdleTime *KIdleTime::instance()
{
if (!s_globalKIdleTime()->q) {
new KIdleTime;
}
return s_globalKIdleTime()->q;
}
class KIdleTimePrivate
{
Q_DECLARE_PUBLIC(KIdleTime)
KIdleTime *q_ptr;
public:
KIdleTimePrivate()
: catchResume(false)
, currentId(0)
{
}
void loadSystem();
void unloadCurrentSystem();
void resumingFromIdle();
void timeoutReached(int msec);
QPointer<KAbstractIdleTimePoller> poller;
bool catchResume;
int currentId;
QHash<int, int> associations;
};
KIdleTime::KIdleTime()
: QObject(nullptr)
, d_ptr(new KIdleTimePrivate())
{
Q_ASSERT(!s_globalKIdleTime()->q);
s_globalKIdleTime()->q = this;
d_ptr->q_ptr = this;
Q_D(KIdleTime);
d->loadSystem();
connect(d->poller.data(), &KAbstractIdleTimePoller::resumingFromIdle, this, [d]() {
d->resumingFromIdle();
});
connect(d->poller.data(), &KAbstractIdleTimePoller::timeoutReached, this, [d](int msec) {
d->timeoutReached(msec);
});
}
KIdleTime::~KIdleTime()
{
Q_D(KIdleTime);
d->unloadCurrentSystem();
}
void KIdleTime::catchNextResumeEvent()
{
Q_D(KIdleTime);
if (!d->catchResume && d->poller) {
d->catchResume = true;
d->poller.data()->catchIdleEvent();
}
}
void KIdleTime::stopCatchingResumeEvent()
{
Q_D(KIdleTime);
if (d->catchResume && d->poller) {
d->catchResume = false;
d->poller.data()->stopCatchingIdleEvents();
}
}
int KIdleTime::addIdleTimeout(int msec)
{
Q_D(KIdleTime);
if (Q_UNLIKELY(msec < 0)) {
qCWarning(KIDLETIME, "KIdleTime::addIdleTimeout: invalid timeout: %d", msec);
return 0;
}
if (Q_UNLIKELY(!d->poller)) {
return 0;
}
d->poller.data()->addTimeout(msec);
++d->currentId;
d->associations[d->currentId] = msec;
return d->currentId;
}
void KIdleTime::removeIdleTimeout(int identifier)
{
Q_D(KIdleTime);
const auto it = d->associations.constFind(identifier);
if (it == d->associations.cend() || !d->poller) {
return;
}
const int msec = it.value();
d->associations.erase(it);
const bool isFound = std::any_of(d->associations.cbegin(), d->associations.cend(), [msec](int i) {
return i == msec;
});
if (!isFound) {
d->poller.data()->removeTimeout(msec);
}
}
void KIdleTime::removeAllIdleTimeouts()
{
Q_D(KIdleTime);
std::vector<int> removed;
for (auto it = d->associations.cbegin(); it != d->associations.cend(); ++it) {
const int msec = it.value();
const bool alreadyIns = std::find(removed.cbegin(), removed.cend(), msec) != removed.cend();
if (!alreadyIns && d->poller) {
removed.push_back(msec);
d->poller.data()->removeTimeout(msec);
}
}
d->associations.clear();
}
static QStringList pluginCandidates()
{
QStringList ret;
const QStringList libPath = QCoreApplication::libraryPaths();
for (const QString &path : libPath) {
#ifdef Q_OS_MACOS
const QDir pluginDir(path + QStringLiteral("/kf6/kidletime"));
#else
const QDir pluginDir(path + QStringLiteral("/kf6/org.kde.kidletime.platforms"));
#endif
if (!pluginDir.exists()) {
continue;
}
const auto entries = pluginDir.entryList(QDir::Files | QDir::NoDotAndDotDot);
ret.reserve(ret.size() + entries.size());
for (const QString &entry : entries) {
ret << pluginDir.absoluteFilePath(entry);
}
}
return ret;
}
static bool checkPlatform(const QJsonObject &metadata, const QString &platformName)
{
const QJsonArray platforms = metadata.value(QStringLiteral("MetaData")).toObject().value(QStringLiteral("platforms")).toArray();
return std::any_of(platforms.begin(), platforms.end(), [&platformName](const QJsonValue &value) {
return QString::compare(platformName, value.toString(), Qt::CaseInsensitive) == 0;
});
}
static KAbstractIdleTimePoller *loadPoller()
{
const QString platformName = QGuiApplication::platformName();
const QList<QStaticPlugin> staticPlugins = QPluginLoader::staticPlugins();
for (const QStaticPlugin &staticPlugin : staticPlugins) {
const QJsonObject metadata = staticPlugin.metaData();
if (metadata.value(QLatin1String("IID")) != QLatin1String(KAbstractIdleTimePoller_iid)) {
continue;
}
if (checkPlatform(metadata, platformName)) {
auto *poller = qobject_cast<KAbstractIdleTimePoller *>(staticPlugin.instance());
if (poller) {
if (poller->isAvailable()) {
qCDebug(KIDLETIME) << "Loaded system poller from a static plugin";
return poller;
}
delete poller;
}
}
}
const QStringList lstPlugins = pluginCandidates();
for (const QString &candidate : lstPlugins) {
if (!QLibrary::isLibrary(candidate)) {
continue;
}
QPluginLoader loader(candidate);
if (checkPlatform(loader.metaData(), platformName)) {
auto *poller = qobject_cast<KAbstractIdleTimePoller *>(loader.instance());
if (poller) {
qCDebug(KIDLETIME) << "Trying plugin" << candidate;
if (poller->isAvailable()) {
qCDebug(KIDLETIME) << "Using" << candidate << "for platform" << platformName;
return poller;
}
delete poller;
}
}
}
qCWarning(KIDLETIME) << "Could not find any system poller plugin";
return nullptr;
}
void KIdleTimePrivate::loadSystem()
{
if (!poller.isNull()) {
unloadCurrentSystem();
}
// load plugin
poller = loadPoller();
if (poller && !poller->isAvailable()) {
poller = nullptr;
}
if (!poller.isNull()) {
poller.data()->setUpPoller();
}
}
void KIdleTimePrivate::unloadCurrentSystem()
{
if (!poller.isNull()) {
poller.data()->unloadPoller();
poller.data()->deleteLater();
}
}
void KIdleTimePrivate::resumingFromIdle()
{
Q_Q(KIdleTime);
if (catchResume) {
Q_EMIT q->resumingFromIdle();
q->stopCatchingResumeEvent();
}
}
void KIdleTimePrivate::timeoutReached(int msec)
{
Q_Q(KIdleTime);
const auto listKeys = associations.keys(msec);
for (const auto key : listKeys) {
Q_EMIT q->timeoutReached(key, msec);
}
}
void KIdleTime::simulateUserActivity()
{
Q_D(KIdleTime);
if (Q_LIKELY(d->poller)) {
d->poller.data()->simulateUserActivity();
}
}
int KIdleTime::idleTime() const
{
Q_D(const KIdleTime);
if (Q_LIKELY(d->poller)) {
return d->poller.data()->forcePollRequest();
}
return 0;
}
QHash<int, int> KIdleTime::idleTimeouts() const
{
Q_D(const KIdleTime);
return d->associations;
}
#include "moc_kidletime.cpp"
@@ -0,0 +1,189 @@
/* This file is part of the KDE libraries
* SPDX-FileCopyrightText: 2009 Dario Freddi <drf at kde.org>
* SPDX-FileCopyrightText: 2021 Harald Sitter <sitter@kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef KIDLETIME_H
#define KIDLETIME_H
#include <QHash>
#include <QObject>
#include <kidletime_export.h>
#include <memory>
#if __has_include(<chrono>)
#include <chrono>
#endif
class KIdleTimePrivate;
/**
* @class KIdleTime kidletime.h KIdleTime
*
* KIdleTime is a singleton reporting information on idle time. It is useful not
* only for finding out about the current idle time of the PC, but also for getting
* notified upon idle time events, such as custom timeouts, or user activity.
*
* @note All the intervals and times in this library are in milliseconds, unless
* specified otherwise
*
* @author Dario Freddi
*
* @since 4.4
*/
class KIDLETIME_EXPORT KIdleTime : public QObject
{
Q_OBJECT
Q_DECLARE_PRIVATE(KIdleTime)
Q_DISABLE_COPY(KIdleTime)
public:
/**
* Returns the singleton instance. Use this method to access KIdleTime
*
* @returns the instance of KIdleTime
*/
static KIdleTime *instance();
/**
* The destructor
*/
~KIdleTime() override;
/**
* Retrieves the idle time of the system, in milliseconds
*
* @returns the idle time of the system
*/
int idleTime() const;
/**
* Returns the list of timeout identifiers associated with their duration, in milliseconds,
* the library is currently listening to.
*
* @see addIdleTimeout
* @see removeIdleTimeout
* @see timeoutReached
*/
QHash<int, int> idleTimeouts() const;
/**
* Attempts to simulate user activity. This implies that after calling this
* method, the idle time of the system will become 0 and eventually \link resumingFromIdle \endlink
* will be triggered
*
* @see resumingFromIdle
*/
void simulateUserActivity();
public Q_SLOTS:
/**
* Adds a new timeout to catch. When calling this method, after the system will be idle for
* \c msec milliseconds, the signal \c timeoutReached will be triggered. Please note that until you will
* call \c removeIdleTimeout or \c removeAllIdleTimeouts, the signal will be triggered every
* time the system will be idle for \c msec milliseconds. This function also returns an unique
* token for the timeout just added to allow easier identification.
*
* @param msec the time, in milliseconds, after which the signal will be triggered
*
* @returns an unique identifier for the timeout being added, that will be streamed by timeoutReached
*
* @see removeIdleTimeout
* @see removeAllIdleTimeouts
* @see timeoutReached
*
*/
int addIdleTimeout(int msec);
#if __has_include(<chrono>)
/**
* Convenience overload supporting C++ chrono types. May also be used with chrono literals.
* @since 5.83
*/
int addIdleTimeout(std::chrono::milliseconds msec)
{
return addIdleTimeout(int(msec.count()));
}
#endif
/**
* Stops catching the idle timeout identified by the token \c identifier,
* if it was registered earlier with addIdleTimeout.
* Otherwise does nothing.
*
* @param identifier the token returned from addIdleTimeout of the timeout you want to stop listening to
*/
void removeIdleTimeout(int identifier);
/**
* Stops catching every set timeout (if any). This means that after calling this method, the signal
* \link timeoutReached \endlink won't be called again until you will add another timeout
*
* @see timeoutReached
* @see addIdleTimeout
*/
void removeAllIdleTimeouts();
/**
* Catches the next resume from idle event. This means that whenever user activity will be registered, or
* \link simulateUserActivity \endlink is called, the signal \link resumingFromIdle \endlink will be triggered.
* <p>
* Please note that this method will trigger the signal just for the very first resume event after the call:
* this means you explicitly have to request to track every single resume event you are interested in.
*
* @note This behavior is due to the fact that a resume event happens whenever the user sends an input to the
* system. This would lead to a massive amount of signals being delivered when the PC is being used.
* Moreover, you are usually interested in catching just significant resume events, such as the ones after
* a significant period of inactivity. For tracking user input, you can use the more efficient methods provided
* by Qt. The purpose of this library is just monitoring the activity of the user.
*
* @see resumingFromIdle
* @see simulateUserActivity
*
*/
void catchNextResumeEvent();
/**
* Stops listening for resume event. This function serves for canceling \c catchNextResumeEvent, as it
* will have effect just when \c catchNextResumeEvent has been called and \c resumingFromIdle not
* yet triggered
*
* @see resumingFromIdle
* @see catchNextResumeEvent
*
*/
void stopCatchingResumeEvent();
Q_SIGNALS:
/**
* Triggered, if KIdleTime is catching resume events, when the system resumes from an idle state. This means
* that either \link simulateUserActivity \endlink was called or the user sent an input to the system.
*
* @see catchNextResumeEvent
*/
void resumingFromIdle();
/**
* Triggered when the system has been idle for x milliseconds, identified by the previously set
* timeout.
*
* This signal is triggered whenever each timeout previously registered with addIdleTimeout(int)
* is reached. It is guaranteed that \p msec will exactly correspond to the identified timeout.
*
* @param identifier the identifier of the timeout the system has reached
* @param msec the time, in milliseconds, the system has been idle for
*
* @see addIdleTimeout
* @see removeIdleTimeout
*/
void timeoutReached(int identifier, int msec); // clazy:exclude=overloaded-signal
private:
KIDLETIME_NO_EXPORT KIdleTime();
std::unique_ptr<KIdleTimePrivate> const d_ptr;
};
#endif /* KIDLETIME_H */
@@ -0,0 +1,147 @@
/* This file is part of the KDE libraries
* SPDX-FileCopyrightText: 2009 Dario Freddi <drf at kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "kwindowbasedidletimepoller_p.h"
#include <QEvent>
#include <QTimer>
#include <QWindow>
KWindowBasedIdleTimePoller::KWindowBasedIdleTimePoller(QObject *parent)
: KAbstractIdleTimePoller(parent)
{
}
KWindowBasedIdleTimePoller::~KWindowBasedIdleTimePoller()
{
}
bool KWindowBasedIdleTimePoller::isAvailable()
{
return true;
}
bool KWindowBasedIdleTimePoller::setUpPoller()
{
m_pollTimer = new QTimer(this);
// setup idle timer, with some smart polling
connect(m_pollTimer, &QTimer::timeout, this, &KWindowBasedIdleTimePoller::poll);
m_grabber = new QWindow();
m_grabber->setFlag(Qt::X11BypassWindowManagerHint);
m_grabber->setPosition(-1000, -1000);
m_grabber->installEventFilter(this);
m_grabber->setObjectName(QStringLiteral("KIdleGrabberWidget"));
return additionalSetUp();
}
void KWindowBasedIdleTimePoller::unloadPoller()
{
m_pollTimer->deleteLater();
m_grabber->deleteLater();
}
QList<int> KWindowBasedIdleTimePoller::timeouts() const
{
return m_timeouts;
}
void KWindowBasedIdleTimePoller::addTimeout(int nextTimeout)
{
m_timeouts.append(nextTimeout);
poll();
}
bool KWindowBasedIdleTimePoller::eventFilter(QObject *object, QEvent *event)
{
if (object == m_grabber && (event->type() == QEvent::MouseMove || event->type() == QEvent::KeyPress)) {
detectedActivity();
return true;
} else if (object != m_grabber) {
// If it's not the grabber, fallback to default event filter
return false;
}
// Otherwise, simply ignore it
return false;
}
void KWindowBasedIdleTimePoller::waitForActivity()
{
m_grabber->show();
m_grabber->setMouseGrabEnabled(true);
m_grabber->setKeyboardGrabEnabled(true);
}
void KWindowBasedIdleTimePoller::detectedActivity()
{
stopCatchingIdleEvents();
Q_EMIT resumingFromIdle();
}
void KWindowBasedIdleTimePoller::releaseInputLock()
{
m_grabber->setMouseGrabEnabled(false);
m_grabber->setKeyboardGrabEnabled(false);
m_grabber->hide();
}
int KWindowBasedIdleTimePoller::poll()
{
int idle = getIdleTime();
// Check if we reached a timeout..
for (int timeOut : std::as_const(m_timeouts)) {
if ((timeOut - idle < 300 && timeOut >= idle) || (idle - timeOut < 300 && idle > timeOut)) {
// Bingo!
Q_EMIT timeoutReached(timeOut);
}
}
// Let's check the timer now!
int mintime = 0;
for (int i : std::as_const(m_timeouts)) {
if (i > idle && (i < mintime || mintime == 0)) {
mintime = i;
}
}
// qDebug() << "mintime " << mintime << "idle " << idle;
if (mintime != 0) {
m_pollTimer->start(mintime - idle);
} else {
m_pollTimer->stop();
}
return idle;
}
int KWindowBasedIdleTimePoller::forcePollRequest()
{
return poll();
}
void KWindowBasedIdleTimePoller::removeTimeout(int timeout)
{
m_timeouts.removeOne(timeout);
poll();
}
void KWindowBasedIdleTimePoller::catchIdleEvent()
{
waitForActivity();
}
void KWindowBasedIdleTimePoller::stopCatchingIdleEvents()
{
releaseInputLock();
}
#include "moc_kwindowbasedidletimepoller_p.cpp"
@@ -0,0 +1,59 @@
/* This file is part of the KDE libraries
* SPDX-FileCopyrightText: 2009 Dario Freddi <drf at kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef KWINDOWBASEDIDLETIMEPOLLER_P_H
#define KWINDOWBASEDIDLETIMEPOLLER_P_H
#include <kidletime_export.h>
#include "kabstractidletimepoller_p.h"
class QTimer;
class QEvent;
class QWindow;
class KIDLETIME_EXPORT KWindowBasedIdleTimePoller : public KAbstractIdleTimePoller
{
Q_OBJECT
public:
explicit KWindowBasedIdleTimePoller(QObject *parent = nullptr);
~KWindowBasedIdleTimePoller() override;
bool isAvailable() override;
bool setUpPoller() override;
void unloadPoller() override;
QList<int> timeouts() const override;
protected:
bool eventFilter(QObject *object, QEvent *event) override;
public Q_SLOTS:
void addTimeout(int nextTimeout) override;
void removeTimeout(int nextTimeout) override;
int forcePollRequest() override;
void catchIdleEvent() override;
void stopCatchingIdleEvents() override;
private Q_SLOTS:
int poll();
virtual int getIdleTime() = 0;
void detectedActivity();
void waitForActivity();
void releaseInputLock();
private:
virtual bool additionalSetUp() = 0;
private:
QTimer *m_pollTimer;
QWindow *m_grabber;
QList<int> m_timeouts;
};
#endif /* KWINDOWBASEDIDLETIMEPOLLER_P_H */
@@ -0,0 +1,20 @@
if(APPLE)
add_subdirectory(osx)
endif()
if(WIN32)
add_subdirectory(windows)
endif()
if(WITH_X11)
if(HAVE_XSCREENSAVER)
add_subdirectory(xscreensaver)
endif()
if(HAVE_XSYNC)
add_subdirectory(xsync)
endif()
endif()
if (WITH_WAYLAND)
add_subdirectory(wayland)
endif()
@@ -0,0 +1,17 @@
add_library(KF6IdleTimeOsxPlugin MODULE)
target_sources(KF6IdleTimeOsxPlugin PRIVATE
macpoller.cpp
)
target_link_libraries(KF6IdleTimeOsxPlugin
KF6IdleTime
"-framework CoreFoundation -framework Carbon"
)
install(
TARGETS
KF6IdleTimeOsxPlugin
DESTINATION
${KDE_INSTALL_PLUGINDIR}/kf6/kidletime/
)
@@ -0,0 +1,217 @@
/* This file is part of the KDE libraries
* SPDX-FileCopyrightText: 2009 Dario Freddi <drf at kde.org>
* SPDX-FileCopyrightText: 2003 Tarkvara Design Inc. (from KVIrc source code)
* SPDX-FileCopyrightText: 2008 Roman Jarosz <kedgedev at centrum.cz>
* SPDX-FileCopyrightText: 2008 the Kopete developers <kopete-devel at kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "macpoller.h"
// Why does Apple have to make this so complicated?
static OSStatus LoadFrameworkBundle(CFStringRef framework, CFBundleRef *bundlePtr)
{
OSStatus err;
FSRef frameworksFolderRef;
CFURLRef baseURL;
CFURLRef bundleURL;
if (bundlePtr == nil) {
return (-1);
}
*bundlePtr = nil;
baseURL = nil;
bundleURL = nil;
err = FSFindFolder(kOnAppropriateDisk, kFrameworksFolderType, true, &frameworksFolderRef);
if (err == noErr) {
baseURL = CFURLCreateFromFSRef(kCFAllocatorSystemDefault, &frameworksFolderRef);
if (baseURL == nil) {
err = coreFoundationUnknownErr;
}
}
if (err == noErr) {
bundleURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, baseURL, framework, false);
if (bundleURL == nil) {
err = coreFoundationUnknownErr;
}
}
if (err == noErr) {
*bundlePtr = CFBundleCreate(kCFAllocatorSystemDefault, bundleURL);
if (*bundlePtr == nil) {
err = coreFoundationUnknownErr;
}
}
if (err == noErr) {
if (!CFBundleLoadExecutable(*bundlePtr)) {
err = coreFoundationUnknownErr;
}
}
// Clean up.
if (err != noErr && *bundlePtr != nil) {
CFRelease(*bundlePtr);
*bundlePtr = nil;
}
if (bundleURL != nil) {
CFRelease(bundleURL);
}
if (baseURL != nil) {
CFRelease(baseURL);
}
return err;
}
pascal void MacPoller::IdleTimerAction(EventLoopTimerRef, EventLoopIdleTimerMessage inState, void *inUserData)
{
Q_ASSERT(inUserData);
switch (inState) {
case kEventLoopIdleTimerStarted:
case kEventLoopIdleTimerStopped:
// Get invoked with this constant at the start of the idle period,
// or whenever user activity cancels the idle.
((MacPoller *)inUserData)->m_secondsIdle = 0;
((MacPoller *)inUserData)->triggerResume();
break;
case kEventLoopIdleTimerIdling:
// Called every time the timer fires (i.e. every second).
((MacPoller *)inUserData)->m_secondsIdle++;
((MacPoller *)inUserData)->poll();
break;
}
}
// Typedef for the function we're getting back from CFBundleGetFunctionPointerForName.
typedef OSStatus (*InstallEventLoopIdleTimerPtr)(EventLoopRef inEventLoop,
EventTimerInterval inFireDelay,
EventTimerInterval inInterval,
EventLoopIdleTimerUPP inTimerProc,
void *inTimerData,
EventLoopTimerRef *outTimer);
MacPoller::MacPoller(QObject *parent)
: KAbstractIdleTimePoller(parent)
, m_timerRef(0)
, m_secondsIdle(0)
, m_catch(false)
{
}
MacPoller::~MacPoller()
{
}
void MacPoller::unloadPoller()
{
RemoveEventLoopTimer(m_timerRef);
}
bool MacPoller::isAvailable()
{
return true;
}
bool MacPoller::setUpPoller()
{
// May already be init'ed.
if (m_timerRef) {
return true;
}
// According to the docs, InstallEventLoopIdleTimer is new in 10.2.
// According to the headers, it has been around since 10.0.
// One of them is lying. We'll play it safe and weak-link the function.
// Load the "Carbon.framework" bundle.
CFBundleRef carbonBundle;
if (LoadFrameworkBundle(CFSTR("Carbon.framework"), &carbonBundle) != noErr) {
return false;
}
// Load the Mach-O function pointers for the routine we will be using.
InstallEventLoopIdleTimerPtr myInstallEventLoopIdleTimer =
(InstallEventLoopIdleTimerPtr)CFBundleGetFunctionPointerForName(carbonBundle, CFSTR("InstallEventLoopIdleTimer"));
if (myInstallEventLoopIdleTimer == 0) {
return false;
}
EventLoopIdleTimerUPP timerUPP = NewEventLoopIdleTimerUPP(IdleTimerAction);
if ((*myInstallEventLoopIdleTimer)(GetMainEventLoop(), kEventDurationSecond, kEventDurationSecond, timerUPP, this, &m_timerRef)) {
return true;
}
return false;
}
QList<int> MacPoller::timeouts() const
{
return m_timeouts;
}
void MacPoller::addTimeout(int nextTimeout)
{
m_timeouts.append(nextTimeout);
poll();
}
int MacPoller::poll()
{
int idle = m_secondsIdle * 1000;
// Check if we reached a timeout..
for (int i : std::as_const(m_timeouts)) {
if ((i - idle < 1000 && i > idle) || (idle - i < 1000 && idle > i)) {
// Bingo!
Q_EMIT timeoutReached(i);
}
}
return idle;
}
int MacPoller::forcePollRequest()
{
return poll();
}
void MacPoller::removeTimeout(int timeout)
{
m_timeouts.removeOne(timeout);
poll();
}
void MacPoller::catchIdleEvent()
{
m_catch = true;
}
void MacPoller::stopCatchingIdleEvents()
{
m_catch = false;
}
void MacPoller::triggerResume()
{
if (m_catch) {
Q_EMIT resumingFromIdle();
stopCatchingIdleEvents();
}
}
void MacPoller::simulateUserActivity()
{
// TODO
}
#include "moc_macpoller.cpp"
@@ -0,0 +1,50 @@
/* This file is part of the KDE libraries
* SPDX-FileCopyrightText: 2009 Dario Freddi <drf at kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef MACPOLLER_H
#define MACPOLLER_H
#include "kabstractidletimepoller_p.h"
#include <Carbon/Carbon.h>
class MacPoller : public KAbstractIdleTimePoller
{
Q_OBJECT
Q_PLUGIN_METADATA(IID KAbstractIdleTimePoller_iid FILE "osx.json")
Q_INTERFACES(KAbstractIdleTimePoller)
public:
MacPoller(QObject *parent = 0);
virtual ~MacPoller();
bool isAvailable();
bool setUpPoller();
void unloadPoller();
static pascal void IdleTimerAction(EventLoopTimerRef, EventLoopIdleTimerMessage inState, void *inUserData);
public Q_SLOTS:
void addTimeout(int nextTimeout);
void removeTimeout(int nextTimeout);
QList<int> timeouts() const;
int forcePollRequest();
void catchIdleEvent();
void stopCatchingIdleEvents();
void simulateUserActivity();
void triggerResume();
private Q_SLOTS:
int poll();
private:
QList<int> m_timeouts;
EventLoopTimerRef m_timerRef;
int m_secondsIdle;
bool m_catch;
};
#endif /* MACPOLLER_H */
@@ -0,0 +1,3 @@
{
"platforms": ["cocoa"]
}
@@ -0,0 +1,25 @@
add_library(KF6IdleTimeWaylandPlugin MODULE)
if (Qt6_VERSION VERSION_GREATER_EQUAL "6.8.0")
set(private_code_option "PRIVATE_CODE")
endif()
qt6_generate_wayland_protocol_client_sources(KF6IdleTimeWaylandPlugin FILES
${PLASMA_WAYLAND_PROTOCOLS_DIR}/idle.xml
${WaylandProtocols_DATADIR}/staging/ext-idle-notify/ext-idle-notify-v1.xml
${private_code_option}
)
target_sources(KF6IdleTimeWaylandPlugin PRIVATE poller.cpp ${idletime_plugin_SRCS})
target_link_libraries(KF6IdleTimeWaylandPlugin
KF6::IdleTime
Qt6::WaylandClient
Wayland::Client
)
install(
TARGETS
KF6IdleTimeWaylandPlugin
DESTINATION
${KDE_INSTALL_PLUGINDIR}/kf6/org.kde.kidletime.platforms/
)
@@ -0,0 +1,218 @@
/*
SPDX-FileCopyrightText: 2021 David Edmundson <davidedmundson@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "poller.h"
#include <QDebug>
#include <QGuiApplication>
#include <QLoggingCategory>
#include <QWaylandClientExtensionTemplate>
#include <QtWaylandClientVersion>
#include "qwayland-ext-idle-notify-v1.h"
#include "qwayland-idle.h"
Q_DECLARE_LOGGING_CATEGORY(POLLER)
Q_LOGGING_CATEGORY(POLLER, "kf5idletime_wayland")
/*
* Porting notes:
* org_kde_kwin_idle refers to an early specific idle timeout protocol
* the version ext_idle refers to an upstream stable protocol
*
* Pragmattically they're both the same, but we have to have two implementations for a while
*
* When a suitable amount of time passes (Plasma 5.24 being EOL) drop IdleTimeoutKwin and drop IdleManagerKwin as well as merge the abstract IdleTimeout class into the real implementation
*/
class IdleTimeout : public QObject
{
Q_OBJECT
public:
IdleTimeout() = default;
Q_SIGNALS:
void idle();
void resumeFromIdle();
};
class IdleTimeoutKwin : public IdleTimeout, public QtWayland::org_kde_kwin_idle_timeout
{
Q_OBJECT
public:
IdleTimeoutKwin(struct ::org_kde_kwin_idle_timeout *object)
: IdleTimeout()
, QtWayland::org_kde_kwin_idle_timeout(object)
{}
~IdleTimeoutKwin()
{
if (qGuiApp) {
release();
}
}
protected:
void org_kde_kwin_idle_timeout_idle() override {
Q_EMIT idle();
}
void org_kde_kwin_idle_timeout_resumed() override {
Q_EMIT resumeFromIdle();
}
};
class IdleTimeoutExt : public IdleTimeout, public QtWayland::ext_idle_notification_v1
{
Q_OBJECT
public:
IdleTimeoutExt(struct ::ext_idle_notification_v1 *object)
: IdleTimeout()
, QtWayland::ext_idle_notification_v1(object)
{
}
~IdleTimeoutExt()
{
if (qGuiApp) {
destroy();
}
}
protected:
void ext_idle_notification_v1_idled() override
{
Q_EMIT idle();
}
void ext_idle_notification_v1_resumed() override
{
Q_EMIT resumeFromIdle();
}
};
class IdleManagerKwin : public QWaylandClientExtensionTemplate<IdleManagerKwin>, public QtWayland::org_kde_kwin_idle
{
public:
IdleManagerKwin()
: QWaylandClientExtensionTemplate<IdleManagerKwin>(1)
{
initialize();
}
};
class IdleManagerExt : public QWaylandClientExtensionTemplate<IdleManagerExt>, public QtWayland::ext_idle_notifier_v1
{
public:
IdleManagerExt()
: QWaylandClientExtensionTemplate<IdleManagerExt>(1)
{
initialize();
}
~IdleManagerExt()
{
if (qGuiApp && isActive()) {
destroy();
}
}
};
Poller::Poller(QObject *parent)
: KAbstractIdleTimePoller(parent)
, m_idleManagerKwin(new IdleManagerKwin)
, m_idleManagerExt(new IdleManagerExt)
{
}
Poller::~Poller() = default;
bool Poller::isAvailable()
{
return m_idleManagerKwin->isActive() || m_idleManagerExt->isActive();
}
void Poller::addTimeout(int nextTimeout)
{
if (m_timeouts.contains(nextTimeout)) {
return;
}
auto timeout = createTimeout(nextTimeout);
if (!timeout) {
return;
}
connect(timeout, &IdleTimeout::idle, this, [this, nextTimeout] {
Q_EMIT timeoutReached(nextTimeout);
});
connect(timeout, &IdleTimeout::resumeFromIdle, this, &Poller::resumingFromIdle);
m_timeouts.insert(nextTimeout, QSharedPointer<IdleTimeout>(timeout));
}
void Poller::removeTimeout(int nextTimeout)
{
m_timeouts.remove(nextTimeout);
}
QList<int> Poller::timeouts() const
{
return QList<int>();
}
void Poller::catchIdleEvent()
{
if (m_catchResumeTimeout) {
// already setup
return;
}
if (!isAvailable()) {
return;
}
m_catchResumeTimeout.reset(createTimeout(0));
if (!m_catchResumeTimeout) {
return;
}
connect(m_catchResumeTimeout.get(), &IdleTimeout::resumeFromIdle, this, [this] {
stopCatchingIdleEvents();
Q_EMIT resumingFromIdle();
});
}
void Poller::stopCatchingIdleEvents()
{
m_catchResumeTimeout.reset();
}
int Poller::forcePollRequest()
{
qCWarning(POLLER) << "This plugin does not support polling idle time";
return 0;
}
void Poller::simulateUserActivity()
{
}
IdleTimeout* Poller::createTimeout(int timeout)
{
auto waylandApp = qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>();
if (!waylandApp) {
return nullptr;
}
auto seat = waylandApp->seat();
if (!seat) {
return nullptr;
}
if (m_idleManagerExt->isActive()) {
return new IdleTimeoutExt(m_idleManagerExt->get_idle_notification(timeout, seat));
}
if (m_idleManagerKwin->isActive()) {
return new IdleTimeoutKwin(m_idleManagerKwin->get_idle_timeout(seat, timeout));
}
return nullptr;
}
#include "moc_poller.cpp"
#include "poller.moc"
@@ -0,0 +1,52 @@
/*
SPDX-FileCopyrightText: 2021 David Edmundson <davidedmundson@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include "kabstractidletimepoller_p.h"
#include <QHash>
#include <memory>
class IdleManagerKwin;
class IdleManagerExt;
class IdleTimeout;
class Poller : public KAbstractIdleTimePoller
{
Q_OBJECT
Q_PLUGIN_METADATA(IID KAbstractIdleTimePoller_iid FILE "wayland.json")
Q_INTERFACES(KAbstractIdleTimePoller)
public:
explicit Poller(QObject *parent = nullptr);
~Poller() override;
bool isAvailable() override;
bool setUpPoller() override {
return true;
}
void unloadPoller() override {}
public Q_SLOTS:
void addTimeout(int nextTimeout) override;
void removeTimeout(int nextTimeout) override;
QList<int> timeouts() const override;
int forcePollRequest() override;
void catchIdleEvent() override;
void stopCatchingIdleEvents() override;
void simulateUserActivity() override;
private:
bool initWayland();
IdleTimeout* createTimeout(int timeout);
QScopedPointer<IdleManagerKwin> m_idleManagerKwin;
QScopedPointer<IdleManagerExt> m_idleManagerExt;
QHash<int, QSharedPointer<IdleTimeout>> m_timeouts;
QScopedPointer<IdleTimeout> m_catchResumeTimeout;
};
@@ -0,0 +1,3 @@
{
"platforms": ["wayland", "wayland-egl"]
}
@@ -0,0 +1,16 @@
add_library(KF6IdleTimeWindowsPlugin MODULE)
target_sources(KF6IdleTimeWindowsPlugin PRIVATE
windowspoller.cpp
)
target_link_libraries(KF6IdleTimeWindowsPlugin
KF6IdleTime
)
install(
TARGETS
KF6IdleTimeWindowsPlugin
DESTINATION
${KDE_INSTALL_PLUGINDIR}/kf6/org.kde.kidletime.platforms/
)
@@ -0,0 +1,3 @@
{
"platforms": ["windows"]
}
@@ -0,0 +1,79 @@
/* This file is part of the KDE libraries
* SPDX-FileCopyrightText: 2009 Dario Freddi <drf at kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "windowspoller.h"
#include <windows.h>
#include <QTimer>
WindowsPoller::WindowsPoller(QObject *parent)
: KWindowBasedIdleTimePoller(parent)
{
}
WindowsPoller::~WindowsPoller()
{
}
int WindowsPoller::getIdleTime()
{
#ifndef _WIN32_WCE
int idle = 0;
LASTINPUTINFO lii;
memset(&lii, 0, sizeof(lii));
lii.cbSize = sizeof(lii);
BOOL ok = GetLastInputInfo(&lii);
if (ok) {
idle = GetTickCount() - lii.dwTime;
}
return idle;
#else
return GetIdleTime();
#endif
}
bool WindowsPoller::additionalSetUp()
{
m_idleTimer = new QTimer(this);
connect(m_idleTimer, &QTimer::timeout, this, &WindowsPoller::checkForIdle);
return true;
}
void WindowsPoller::simulateUserActivity()
{
int width = GetSystemMetrics(SM_CXSCREEN);
int height = GetSystemMetrics(SM_CYSCREEN);
int x = (int)100 * 65536 / width;
int y = (int)100 * 65536 / height;
mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, x, y, NULL, NULL);
}
void WindowsPoller::catchIdleEvent()
{
m_idleTimer->start(800);
}
void WindowsPoller::stopCatchingIdleEvents()
{
m_idleTimer->stop();
}
void WindowsPoller::checkForIdle()
{
if (getIdleTime() < 1000) {
stopCatchingIdleEvents();
Q_EMIT resumingFromIdle();
}
}
#include "moc_windowspoller.cpp"
@@ -0,0 +1,40 @@
/* This file is part of the KDE libraries
* SPDX-FileCopyrightText: 2009 Dario Freddi <drf at kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef WINDOWSPOLLER_H
#define WINDOWSPOLLER_H
#include "kwindowbasedidletimepoller_p.h"
class QTimer;
class WindowsPoller : public KWindowBasedIdleTimePoller
{
Q_OBJECT
Q_PLUGIN_METADATA(IID KAbstractIdleTimePoller_iid FILE "windows.json")
Q_INTERFACES(KAbstractIdleTimePoller)
public:
WindowsPoller(QObject *parent = 0);
virtual ~WindowsPoller();
public Q_SLOTS:
void simulateUserActivity();
void catchIdleEvent();
void stopCatchingIdleEvents();
private:
bool additionalSetUp();
private Q_SLOTS:
int getIdleTime();
void checkForIdle();
private:
QTimer *m_idleTimer;
};
#endif /* WINDOWSPOLLER_H */
@@ -0,0 +1,24 @@
add_library(KF6IdleTimeXcbPlugin1 MODULE)
set(xscreensaver_plugin_dbus_SRCS)
qt_add_dbus_interface(xscreensaver_plugin_dbus_SRCS org.freedesktop.ScreenSaver.xml screensaver_interface)
target_sources(KF6IdleTimeXcbPlugin1 PRIVATE
xscreensaverbasedpoller.cpp
${xscreensaver_plugin_dbus_SRCS}
)
target_link_libraries(KF6IdleTimeXcbPlugin1
KF6IdleTime
Qt6::DBus
Qt6::Gui
${X11_LIBRARIES}
${X11_Xscreensaver_LIB}
)
install(
TARGETS
KF6IdleTimeXcbPlugin1
DESTINATION
${KDE_INSTALL_PLUGINDIR}/kf6/org.kde.kidletime.platforms/
)
@@ -0,0 +1,41 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.freedesktop.ScreenSaver">
<signal name="ActiveChanged">
<arg type="b"/>
</signal>
<method name="Lock">
</method>
<method name="SimulateUserActivity">
</method>
<method name="GetActive">
<arg type="b" direction="out"/>
</method>
<method name="GetActiveTime">
<arg name="seconds" type="u" direction="out"/>
</method>
<method name="GetSessionIdleTime">
<arg name="seconds" type="u" direction="out"/>
</method>
<method name="SetActive">
<arg type="b" direction="out"/>
<arg name="e" type="b" direction="in"/>
</method>
<method name="Inhibit">
<arg name="application_name" type="s" direction="in"/>
<arg name="reason_for_inhibit" type="s" direction="in"/>
<arg name="cookie" type="u" direction="out"/>
</method>
<method name="UnInhibit">
<arg name="cookie" type="u" direction="in"/>
</method>
<method name="Throttle">
<arg name="application_name" type="s" direction="in"/>
<arg name="reason_for_inhibit" type="s" direction="in"/>
<arg name="cookie" type="u" direction="out"/>
</method>
<method name="UnThrottle">
<arg name="cookie" type="u" direction="in"/>
</method>
</interface>
</node>
@@ -0,0 +1,3 @@
{
"platforms": ["xcb"]
}
@@ -0,0 +1,72 @@
/* This file is part of the KDE libraries
* SPDX-FileCopyrightText: 2009 Dario Freddi <drf at kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "xscreensaverbasedpoller.h"
#include <config-kidletime.h>
#include <QGuiApplication>
// sourcing the moc file before the Xlib include,
// whose defines like "Bool" break Qt names
#include "moc_xscreensaverbasedpoller.cpp"
#include <X11/Xlib.h>
#include <X11/extensions/scrnsaver.h>
static Display *display()
{
return qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->display();
}
XScreensaverBasedPoller::XScreensaverBasedPoller(QObject *parent)
: KWindowBasedIdleTimePoller(parent)
, m_screenSaverIface(nullptr)
{
}
XScreensaverBasedPoller::~XScreensaverBasedPoller()
{
}
bool XScreensaverBasedPoller::additionalSetUp()
{
m_screenSaverIface = new OrgFreedesktopScreenSaverInterface(QLatin1String("org.freedesktop.ScreenSaver"),
QLatin1String("/ScreenSaver"),
QDBusConnection::sessionBus(),
this);
connect(m_screenSaverIface, &OrgFreedesktopScreenSaverInterface::ActiveChanged, this, &XScreensaverBasedPoller::screensaverActivated);
return true;
}
void XScreensaverBasedPoller::screensaverActivated(bool activated)
{
// We care only if it has been disactivated
if (!activated) {
m_screenSaverIface->SimulateUserActivity();
Q_EMIT resumingFromIdle();
}
}
int XScreensaverBasedPoller::getIdleTime()
{
XScreenSaverInfo *mitInfo = nullptr;
mitInfo = XScreenSaverAllocInfo();
XScreenSaverQueryInfo(display(), DefaultRootWindow(display()), mitInfo);
int ret = mitInfo->idle;
XFree(mitInfo);
return ret;
}
void XScreensaverBasedPoller::simulateUserActivity()
{
stopCatchingIdleEvents();
XResetScreenSaver(display());
XFlush(display());
}
@@ -0,0 +1,38 @@
/* This file is part of the KDE libraries
* SPDX-FileCopyrightText: 2009 Dario Freddi <drf at kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef XSCREENSAVERBASEDPOLLER_H
#define XSCREENSAVERBASEDPOLLER_H
#include "kwindowbasedidletimepoller_p.h"
#include "screensaver_interface.h"
class XScreensaverBasedPoller : public KWindowBasedIdleTimePoller
{
Q_OBJECT
Q_PLUGIN_METADATA(IID KAbstractIdleTimePoller_iid FILE "xcb.json")
Q_INTERFACES(KAbstractIdleTimePoller)
public:
explicit XScreensaverBasedPoller(QObject *parent = nullptr);
~XScreensaverBasedPoller() override;
public Q_SLOTS:
void simulateUserActivity() override;
private:
bool additionalSetUp() override;
private Q_SLOTS:
void screensaverActivated(bool activated);
int getIdleTime() override;
private:
OrgFreedesktopScreenSaverInterface *m_screenSaverIface;
};
#endif /* XSCREENSAVERBASEDPOLLER_H_ */
@@ -0,0 +1,38 @@
add_library(KF6IdleTimeXcbPlugin0 MODULE)
target_include_directories(KF6IdleTimeXcbPlugin0
PRIVATE
${XCB_XCB_INCLUDE_DIR}
${XCB_SYNC_INCLUDE_DIR}
)
target_sources(KF6IdleTimeXcbPlugin0 PRIVATE
xsyncbasedpoller.cpp
../../logging.cpp
)
ecm_qt_declare_logging_category(KF6IdleTimeXcbPlugin0
HEADER xsync_logging.h
IDENTIFIER KIDLETIME_XSYNC_PLUGIN
CATEGORY_NAME kf.idletime.xsync
OLD_CATEGORY_NAMES org.kde.kf5.idletime.xsync
DESCRIPTION "xsync plugin"
EXPORT KIDLETIME
)
target_link_libraries(KF6IdleTimeXcbPlugin0
KF6IdleTime
Qt6::Gui
XCB::XCB
XCB::SYNC
X11::XCB
${X11_LIBRARIES}
${X11_Xext_LIB}
)
install(
TARGETS
KF6IdleTimeXcbPlugin0
DESTINATION
${KDE_INSTALL_PLUGINDIR}/kf6/org.kde.kidletime.platforms/
)
@@ -0,0 +1,278 @@
/*
* SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
*
* SPDX-License-Identifier: MIT
*/
//#ifdef don't do this, this file is supposed to be included
//#define multiple times
#include <QtGlobal>
/* Usage:
If you get compile errors caused by X11 includes (the line
where first error appears contains word like None, Unsorted,
Below, etc.), put #include <fixx11h.h> in the .cpp file
(not .h file!) between the place where X11 headers are
included and the place where the file with compile
error is included (or the place where the compile error
in the .cpp file occurs).
This file remaps X11 #defines to const variables or
inline functions. The side effect may be that these
symbols may now refer to different variables
(e.g. if X11 #defined NoButton, after this file
is included NoButton would no longer be X11's
NoButton, but Qt::NoButton instead). At this time,
there's no conflict known that could cause problems.
The original X11 symbols are still accessible
(e.g. for None) as X::None, XNone, and also still
None, unless name lookup finds different None
first (in the current class, etc.)
Use 'Unsorted', 'Bool' and 'index' as templates.
*/
namespace X
{
// template --->
// Affects: Should be without side effects.
#ifdef Unsorted
#ifndef FIXX11H_Unsorted
#define FIXX11H_Unsorted
const int XUnsorted = Unsorted;
#undef Unsorted
const int Unsorted = XUnsorted;
#endif
#undef Unsorted
#endif
// template <---
// Affects: Should be without side effects.
#ifdef None
#ifndef FIXX11H_None
#define FIXX11H_None
const XID XNone = None;
#undef None
const XID None = XNone;
#endif
#undef None
#endif
// template --->
// Affects: Should be without side effects.
#ifdef Bool
#ifndef FIXX11H_Bool
#define FIXX11H_Bool
#ifdef _XTYPEDEF_BOOL /* Xdefs.h has typedef'ed Bool already */
#undef Bool
#else
typedef Bool XBool;
#undef Bool
typedef XBool Bool;
#endif
#endif
#undef Bool
#define _XTYPEDEF_BOOL
#endif
// template <---
// Affects: Should be without side effects.
#ifdef KeyPress
#ifndef FIXX11H_KeyPress
#define FIXX11H_KeyPress
const int XKeyPress = KeyPress;
#undef KeyPress
const int KeyPress = XKeyPress;
#endif
#undef KeyPress
#endif
// Affects: Should be without side effects.
#ifdef KeyRelease
#ifndef FIXX11H_KeyRelease
#define FIXX11H_KeyRelease
const int XKeyRelease = KeyRelease;
#undef KeyRelease
const int KeyRelease = XKeyRelease;
#endif
#undef KeyRelease
#endif
// Affects: Should be without side effects.
#ifdef Above
#ifndef FIXX11H_Above
#define FIXX11H_Above
const int XAbove = Above;
#undef Above
const int Above = XAbove;
#endif
#undef Above
#endif
// Affects: Should be without side effects.
#ifdef Below
#ifndef FIXX11H_Below
#define FIXX11H_Below
const int XBelow = Below;
#undef Below
const int Below = XBelow;
#endif
#undef Below
#endif
// Affects: Should be without side effects.
#ifdef FocusIn
#ifndef FIXX11H_FocusIn
#define FIXX11H_FocusIn
const int XFocusIn = FocusIn;
#undef FocusIn
const int FocusIn = XFocusIn;
#endif
#undef FocusIn
#endif
// Affects: Should be without side effects.
#ifdef FocusOut
#ifndef FIXX11H_FocusOut
#define FIXX11H_FocusOut
const int XFocusOut = FocusOut;
#undef FocusOut
const int FocusOut = XFocusOut;
#endif
#undef FocusOut
#endif
// Affects: Should be without side effects.
#ifdef Always
#ifndef FIXX11H_Always
#define FIXX11H_Always
const int XAlways = Always;
#undef Always
const int Always = XAlways;
#endif
#undef Always
#endif
// Affects: Should be without side effects.
#ifdef Expose
#ifndef FIXX11H_Expose
#define FIXX11H_Expose
const int XExpose = Expose;
#undef Expose
const int Expose = XExpose;
#endif
#undef Expose
#endif
// Affects: Should be without side effects.
#ifdef Success
#ifndef FIXX11H_Success
#define FIXX11H_Success
const int XSuccess = Success;
#undef Success
const int Success = XSuccess;
#endif
#undef Success
#endif
// Affects: Should be without side effects.
#ifdef GrayScale
#ifndef FIXX11H_GrayScale
#define FIXX11H_GrayScale
const int XGrayScale = GrayScale;
#undef GrayScale
const int GrayScale = XGrayScale;
#endif
#undef GrayScale
#endif
// Affects: Should be without side effects.
#ifdef Status
#ifndef FIXX11H_Status
#define FIXX11H_Status
typedef Status XStatus;
#undef Status
typedef XStatus Status;
#endif
#undef Status
#endif
// template --->
// Affects: Should be without side effects.
#ifdef CursorShape
#ifndef FIXX11H_CursorShape
#define FIXX11H_CursorShape
const int XCursorShape = CursorShape;
#undef CursorShape
const int CursorShape = XCursorShape;
#endif
#undef CursorShape
#endif
// template <---
// template --->
// Affects: Should be without side effects.
#ifdef FontChange
#ifndef FIXX11H_FontChange
#define FIXX11H_FontChange
const int XFontChange = FontChange;
#undef FontChange
const int FontChange = XFontChange;
#endif
#undef FontChange
#endif
// template <---
// Affects: Should be without side effects.
#ifdef NormalState
#ifndef FIXX11H_NormalState
#define FIXX11H_NormalState
const int XNormalState = NormalState;
#undef NormalState
const int NormalState = XNormalState;
#endif
#undef NormalState
#endif
// template --->
// Affects: Should be without side effects.
#ifdef index
#ifndef FIXX11H_index
#define FIXX11H_index
inline const char *Xindex(const char *s, int c)
{
return index(s, c);
}
#undef index
inline const char *index(const char *s, int c)
{
return Xindex(s, c);
}
#endif
#undef index
#endif
// template <---
#ifdef rindex
// Affects: Should be without side effects.
#ifndef FIXX11H_rindex
#define FIXX11H_rindex
inline const char *Xrindex(const char *s, int c)
{
return rindex(s, c);
}
#undef rindex
inline const char *rindex(const char *s, int c)
{
return Xrindex(s, c);
}
#endif
#undef rindex
#endif
}
using namespace X;
@@ -0,0 +1,3 @@
{
"platforms": ["xcb"]
}
@@ -0,0 +1,338 @@
/* This file is part of the KDE libraries
SPDX-FileCopyrightText: 2009 Dario Freddi <drf at kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
// Exceptionnally, include QCoreApplication before our own header, because that one includes X11 headers (#define None...)
#include <QCoreApplication>
#include "xsync_logging.h"
#include "xsyncbasedpoller.h"
#include <QAbstractNativeEventFilter>
#include <QGuiApplication>
#include <X11/Xlib-xcb.h> // XGetXCBConnection
#include <xcb/sync.h>
class XSyncBasedPollerHelper : public QAbstractNativeEventFilter
{
public:
XSyncBasedPollerHelper()
: q(nullptr)
, isActive(false)
{
}
~XSyncBasedPollerHelper() override
{
delete q;
}
bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override
{
Q_UNUSED(result);
if (isActive && eventType == "xcb_generic_event_t") {
q->xcbEvent(reinterpret_cast<xcb_generic_event_t *>(message));
}
return false;
}
XSyncBasedPoller *q;
bool isActive;
};
Q_GLOBAL_STATIC(XSyncBasedPollerHelper, s_globalXSyncBasedPoller)
XSyncBasedPoller *XSyncBasedPoller::instance()
{
if (!s_globalXSyncBasedPoller()->q) {
new XSyncBasedPoller;
}
return s_globalXSyncBasedPoller()->q;
}
XSyncBasedPoller::XSyncBasedPoller(QObject *parent)
: KAbstractIdleTimePoller(parent)
, m_display(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->display())
, m_xcb_connection(nullptr)
, m_sync_event(0)
, m_idleCounter(None)
, m_resetAlarm(None)
, m_available(true)
{
Q_ASSERT(!s_globalXSyncBasedPoller()->q);
s_globalXSyncBasedPoller()->q = this;
if (Q_UNLIKELY(!m_display)) {
m_available = false;
qCWarning(KIDLETIME_XSYNC_PLUGIN) << "xcb sync could not find display";
return;
}
m_xcb_connection = XGetXCBConnection(m_display);
QCoreApplication::instance()->installNativeEventFilter(s_globalXSyncBasedPoller());
const xcb_query_extension_reply_t *sync_reply = xcb_get_extension_data(m_xcb_connection, &xcb_sync_id);
if (!sync_reply || !sync_reply->present) {
qCWarning(KIDLETIME_XSYNC_PLUGIN) << "xcb sync extension not found";
m_available = false;
return;
}
m_sync_event = sync_reply->first_event;
#if 0
// Workaround for https://bugs.freedesktop.org/show_bug.cgi?id=23403
#define xcb_sync_systemcounter_name(sc) (((char *)&(sc)->name_len) + 2)
xcb_sync_list_system_counters_cookie_t cookie = xcb_sync_list_system_counters(m_xcb_connection);
xcb_sync_list_system_counters_reply_t *reply = xcb_sync_list_system_counters_reply(m_xcb_connection, cookie, NULL);
xcb_sync_systemcounter_iterator_t iter;
for (iter = xcb_sync_list_system_counters_counters_iterator(reply);
iter.rem; xcb_sync_systemcounter_next(&iter)) {
printf("%d: %.*s\n", iter.data->counter,
iter.data->name_len, xcb_sync_systemcounter_name(iter.data));
/* Extra info for debugging: */
printf(" Actual name: %.*s\n", iter.data->name_len,
((char *) &iter.data->name_len) + 2);
}
int xcbcounters = xcb_sync_list_system_counters_counters_length(reply);
xcb_sync_systemcounter_iterator_t it = xcb_sync_list_system_counters_counters_iterator(reply);
for (int i = 0; i < xcbcounters; ++i) {
qCDebug(KIDLETIME_XSYNC_PLUGIN) << it.data->counter << it.rem << it.index;
qCDebug(KIDLETIME_XSYNC_PLUGIN) << "name length" << xcb_sync_systemcounter_name_length(it.data);
QByteArray name(xcb_sync_systemcounter_name(it.data), xcb_sync_systemcounter_name_length(it.data));
qCDebug(KIDLETIME_XSYNC_PLUGIN) << name;
xcb_sync_systemcounter_next(&it);
}
delete reply;
#endif
int sync_major;
int sync_minor;
int old_sync_event;
int old_sync_error;
if (!XSyncQueryExtension(m_display, &old_sync_event, &old_sync_error)) {
m_available = false;
return;
}
if (!XSyncInitialize(m_display, &sync_major, &sync_minor)) {
m_available = false;
return;
}
int ncounters;
XSyncSystemCounter *counters = XSyncListSystemCounters(m_display, &ncounters);
bool idleFound = false;
qCDebug(KIDLETIME_XSYNC_PLUGIN) << ncounters << "counters";
for (int i = 0; i < ncounters; ++i) {
qCDebug(KIDLETIME_XSYNC_PLUGIN) << counters[i].name << counters[i].counter;
if (!strcmp(counters[i].name, "IDLETIME")) {
m_idleCounter = counters[i].counter;
idleFound = true;
break;
}
}
XSyncFreeSystemCounterList(counters);
if (!idleFound) {
m_available = false;
}
if (m_available) {
qCDebug(KIDLETIME_XSYNC_PLUGIN) << "XSync seems available and ready";
} else {
qCDebug(KIDLETIME_XSYNC_PLUGIN) << "XSync seems not available";
}
}
XSyncBasedPoller::~XSyncBasedPoller()
{
}
bool XSyncBasedPoller::isAvailable()
{
return m_available;
}
bool XSyncBasedPoller::setUpPoller()
{
if (!isAvailable()) {
return false;
}
qCDebug(KIDLETIME_XSYNC_PLUGIN) << "XSync Inited";
s_globalXSyncBasedPoller()->isActive = true;
qCDebug(KIDLETIME_XSYNC_PLUGIN) << "Supported, init completed";
return true;
}
void XSyncBasedPoller::unloadPoller()
{
s_globalXSyncBasedPoller()->isActive = false;
}
void XSyncBasedPoller::addTimeout(int nextTimeout)
{
/* We need to set the counter to the idle time + the value
* requested for next timeout
*/
// If there's already an alarm for the requested timeout, skip
if (m_timeoutAlarm.contains(nextTimeout)) {
return;
}
XSyncValue timeout;
XSyncAlarm newalarm = None;
XSyncIntToValue(&timeout, nextTimeout);
setAlarm(m_display, &newalarm, m_idleCounter, XSyncPositiveComparison, timeout);
m_timeoutAlarm.insert(nextTimeout, newalarm);
}
int XSyncBasedPoller::forcePollRequest()
{
return poll();
}
int XSyncBasedPoller::poll()
{
XSyncValue idleTime;
XSyncQueryCounter(m_display, m_idleCounter, &idleTime);
return XSyncValueLow32(idleTime);
}
void XSyncBasedPoller::removeTimeout(int timeout)
{
if (m_timeoutAlarm.contains(timeout)) {
XSyncAlarm a = m_timeoutAlarm[timeout];
XSyncDestroyAlarm(m_display, a);
m_timeoutAlarm.remove(timeout);
}
}
QList<int> XSyncBasedPoller::timeouts() const
{
return m_timeoutAlarm.keys();
}
void XSyncBasedPoller::stopCatchingIdleEvents()
{
if (m_resetAlarm != None) {
XSyncDestroyAlarm(m_display, m_resetAlarm);
m_resetAlarm = None;
}
}
void XSyncBasedPoller::catchIdleEvent()
{
XSyncValue idleTime;
XSyncQueryCounter(m_display, m_idleCounter, &idleTime);
/* Set the reset alarm to fire the next time idleCounter < the
* current counter value. XSyncNegativeComparison means <= so
* we have to subtract 1 from the counter value
*/
// NOTE: this must be a int, else compilation might fail
int overflow;
XSyncValue add;
XSyncValue plusone;
XSyncIntToValue(&add, -1);
XSyncValueAdd(&plusone, idleTime, add, &overflow);
setAlarm(m_display, &m_resetAlarm, m_idleCounter, XSyncNegativeComparison, plusone);
}
void XSyncBasedPoller::reloadAlarms()
{
XSyncValue timeout;
for (QHash<int, XSyncAlarm>::iterator i = m_timeoutAlarm.begin(); i != m_timeoutAlarm.end(); ++i) {
XSyncIntToValue(&timeout, i.key());
setAlarm(m_display, &(i.value()), m_idleCounter, XSyncPositiveComparison, timeout);
}
}
bool XSyncBasedPoller::xcbEvent(xcb_generic_event_t *event)
{
// qCDebug(KIDLETIME_XSYNC_PLUGIN) << event->response_type << "waiting for" << m_sync_event+XCB_SYNC_ALARM_NOTIFY;
if (event->response_type != m_sync_event + XCB_SYNC_ALARM_NOTIFY) {
return false;
}
xcb_sync_alarm_notify_event_t *alarmEvent = reinterpret_cast<xcb_sync_alarm_notify_event_t *>(event);
if (alarmEvent->state == XCB_SYNC_ALARMSTATE_DESTROYED) {
return false;
}
for (QHash<int, XSyncAlarm>::const_iterator i = m_timeoutAlarm.constBegin(); i != m_timeoutAlarm.constEnd(); ++i) {
if (alarmEvent->alarm == i.value()) {
/* Bling! Caught! */
Q_EMIT timeoutReached(i.key());
// Update the alarm to fire back if the system gets inactive for the same time
catchIdleEvent();
return false;
}
}
if (alarmEvent->alarm == m_resetAlarm) {
/* Resuming from idle here! */
stopCatchingIdleEvents();
reloadAlarms();
Q_EMIT resumingFromIdle();
}
return false;
}
void XSyncBasedPoller::setAlarm(Display *dpy, XSyncAlarm *alarm, XSyncCounter counter, XSyncTestType test, XSyncValue value)
{
XSyncAlarmAttributes attr;
XSyncValue delta;
unsigned int flags;
XSyncIntToValue(&delta, 0);
attr.trigger.counter = counter;
attr.trigger.value_type = XSyncAbsolute;
attr.trigger.test_type = test;
attr.trigger.wait_value = value;
attr.delta = delta;
flags = XSyncCACounter | XSyncCAValueType | XSyncCATestType | XSyncCAValue | XSyncCADelta;
if (*alarm) {
// xcb_sync_change_alarm_checked(m_xcb_connection, alarmId, ...
XSyncChangeAlarm(dpy, *alarm, flags, &attr);
} else {
*alarm = XSyncCreateAlarm(dpy, flags, &attr);
qCDebug(KIDLETIME_XSYNC_PLUGIN) << "Created alarm" << *alarm;
}
XFlush(m_display);
}
void XSyncBasedPoller::simulateUserActivity()
{
XResetScreenSaver(m_display);
XFlush(m_display);
}
#include "moc_xsyncbasedpoller.cpp"
@@ -0,0 +1,68 @@
/* This file is part of the KDE libraries
* SPDX-FileCopyrightText: 2009 Dario Freddi <drf at kde.org>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef XSYNCBASEDPOLLER_H
#define XSYNCBASEDPOLLER_H
#include "kabstractidletimepoller_p.h"
#include <QHash>
#include <config-kidletime.h>
#include <X11/Xlib.h>
#include <X11/extensions/sync.h>
#include <xcb/xcb.h>
#include "fixx11h.h"
class XSyncBasedPoller : public KAbstractIdleTimePoller
{
Q_OBJECT
Q_PLUGIN_METADATA(IID KAbstractIdleTimePoller_iid FILE "xcb.json")
Q_INTERFACES(KAbstractIdleTimePoller)
public:
static XSyncBasedPoller *instance();
explicit XSyncBasedPoller(QObject *parent = nullptr);
~XSyncBasedPoller() override;
bool isAvailable() override;
bool setUpPoller() override;
void unloadPoller() override;
bool xcbEvent(xcb_generic_event_t *event);
QList<int> timeouts() const override;
public Q_SLOTS:
void addTimeout(int nextTimeout) override;
void removeTimeout(int nextTimeout) override;
int forcePollRequest() override;
void catchIdleEvent() override;
void stopCatchingIdleEvents() override;
void simulateUserActivity() override;
private Q_SLOTS:
int poll();
void reloadAlarms();
private:
void setAlarm(Display *dpy, XSyncAlarm *alarm, XSyncCounter counter, XSyncTestType test, XSyncValue value);
private:
Display *m_display;
xcb_connection_t *m_xcb_connection;
int m_sync_event;
XSyncCounter m_idleCounter;
QHash<int, XSyncAlarm> m_timeoutAlarm;
XSyncAlarm m_resetAlarm;
bool m_available;
};
#endif /* XSYNCBASEDPOLLER_H */