File indexing completed on 2024-04-28 15:29:09

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2006 Aaron Seigo <aseigo@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "knotificationrestrictions.h"
0009 
0010 #include <QApplication>
0011 #include <QDBusConnection>
0012 #include <QDBusMessage>
0013 #include <QDBusReply>
0014 
0015 #include "debug_p.h"
0016 #include <config-knotifications.h>
0017 
0018 #if HAVE_XTEST
0019 #include <QTimer>
0020 
0021 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0022 #include <QX11Info>
0023 #endif
0024 
0025 #include <X11/extensions/XTest.h>
0026 #include <X11/keysym.h>
0027 #endif // HAVE_XTEST
0028 
0029 class Q_DECL_HIDDEN KNotificationRestrictions::Private
0030 {
0031 public:
0032     Private(KNotificationRestrictions *qq, Services c, const QString &r)
0033         : q(qq)
0034         , control(c)
0035         , screenSaverDbusCookie(-1)
0036         , reason(r)
0037 #if HAVE_XTEST
0038         , screensaverTimer(nullptr)
0039         , haveXTest(0)
0040         , XTestKeyCode(0)
0041         , isX11(QGuiApplication::platformName() == QLatin1String("xcb"))
0042 #endif // HAVE_XTEST
0043     {
0044     }
0045 
0046     void screensaverFakeKeyEvent();
0047     void startScreenSaverPrevention();
0048     void stopScreenSaverPrevention();
0049 
0050     static QString determineProgramName();
0051 
0052     KNotificationRestrictions *q;
0053     Services control;
0054     int screenSaverDbusCookie;
0055     QString reason;
0056 #if HAVE_XTEST
0057     QTimer *screensaverTimer;
0058     int haveXTest;
0059     int XTestKeyCode;
0060     bool isX11;
0061 #endif // HAVE_XTEST
0062 };
0063 
0064 KNotificationRestrictions::KNotificationRestrictions(Services control, QObject *parent)
0065     : KNotificationRestrictions(control, QStringLiteral("no_reason_specified"), parent)
0066 {
0067 }
0068 
0069 KNotificationRestrictions::KNotificationRestrictions(Services control, const QString &reason, QObject *parent)
0070     : QObject(parent)
0071     , d(new Private(this, control, reason))
0072 {
0073     if (d->control & ScreenSaver) {
0074         d->startScreenSaverPrevention();
0075     }
0076 }
0077 
0078 KNotificationRestrictions::~KNotificationRestrictions()
0079 {
0080     if (d->control & ScreenSaver) {
0081         d->stopScreenSaverPrevention();
0082     }
0083 }
0084 
0085 void KNotificationRestrictions::Private::screensaverFakeKeyEvent()
0086 {
0087     qCDebug(LOG_KNOTIFICATIONS);
0088 #if HAVE_XTEST
0089     if (!isX11) {
0090         return;
0091     }
0092     qCDebug(LOG_KNOTIFICATIONS) << "---- using XTestFakeKeyEvent";
0093 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0094     Display *display = QX11Info::display();
0095 #else
0096     Display *display = qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->display();
0097 #endif
0098 
0099     XTestFakeKeyEvent(display, XTestKeyCode, true, CurrentTime);
0100     XTestFakeKeyEvent(display, XTestKeyCode, false, CurrentTime);
0101     XSync(display, false);
0102 #endif // HAVE_XTEST
0103 }
0104 
0105 void KNotificationRestrictions::Private::startScreenSaverPrevention()
0106 {
0107     qCDebug(LOG_KNOTIFICATIONS);
0108 
0109     QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.ScreenSaver"),
0110                                                           QStringLiteral("/ScreenSaver"),
0111                                                           QStringLiteral("org.freedesktop.ScreenSaver"),
0112                                                           QStringLiteral("Inhibit"));
0113     message << determineProgramName();
0114     message << reason;
0115     QDBusReply<uint> reply = QDBusConnection::sessionBus().call(message);
0116     if (reply.isValid()) {
0117         screenSaverDbusCookie = reply.value();
0118         return;
0119     }
0120 #if HAVE_XTEST
0121     if (!isX11) {
0122         return;
0123     }
0124 
0125 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0126     Display *display = QX11Info::display();
0127 #else
0128     Display *display = qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->display();
0129 #endif
0130 
0131     if (!haveXTest) {
0132         int a;
0133         int b;
0134         int c;
0135         int e;
0136         haveXTest = XTestQueryExtension(display, &a, &b, &c, &e);
0137 
0138         if (!haveXTest) {
0139             qCDebug(LOG_KNOTIFICATIONS) << "--- No XTEST!";
0140             return;
0141         }
0142     }
0143 
0144     if (!XTestKeyCode) {
0145         XTestKeyCode = XKeysymToKeycode(display, XK_Shift_L);
0146 
0147         if (!XTestKeyCode) {
0148             qCDebug(LOG_KNOTIFICATIONS) << "--- No XKeyCode for XK_Shift_L!";
0149             return;
0150         }
0151     }
0152 
0153     if (!screensaverTimer) {
0154         screensaverTimer = new QTimer(q);
0155         connect(screensaverTimer, SIGNAL(timeout()), q, SLOT(screensaverFakeKeyEvent()));
0156     }
0157 
0158     qCDebug(LOG_KNOTIFICATIONS) << "---- using XTest";
0159     // send a fake event right away in case this got started after a period of
0160     // inactivity leading to the screensaver set to activate in <55s
0161     screensaverFakeKeyEvent();
0162     screensaverTimer->start(55000);
0163 #endif // HAVE_XTEST
0164 }
0165 
0166 void KNotificationRestrictions::Private::stopScreenSaverPrevention()
0167 {
0168     if (screenSaverDbusCookie != -1) {
0169         QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.ScreenSaver"),
0170                                                               QStringLiteral("/ScreenSaver"),
0171                                                               QStringLiteral("org.freedesktop.ScreenSaver"),
0172                                                               QStringLiteral("UnInhibit"));
0173         message << static_cast<uint>(screenSaverDbusCookie);
0174         screenSaverDbusCookie = -1;
0175         if (QDBusConnection::sessionBus().send(message)) {
0176             return;
0177         }
0178     }
0179 #if HAVE_XTEST
0180     if (!isX11) {
0181         return;
0182     }
0183     delete screensaverTimer;
0184     screensaverTimer = nullptr;
0185 #endif // HAVE_XTEST
0186 }
0187 
0188 QString KNotificationRestrictions::Private::determineProgramName()
0189 {
0190     QString appName = QGuiApplication::applicationDisplayName();
0191     if (appName.isEmpty()) {
0192         appName = QCoreApplication::applicationName();
0193     }
0194     if (appName.isEmpty()) {
0195         appName = tr("Unknown Application");
0196     }
0197     return appName;
0198 }
0199 
0200 #include "moc_knotificationrestrictions.cpp"