1/* This file is part of the KDE libraries
2 Copyright (C) 2006 Aaron Seigo <aseigo@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18*/
19
20#include "knotificationrestrictions.h"
21
22#include <QApplication>
23#include <QDBusConnection>
24#include <QDBusMessage>
25#include <QDBusReply>
26
27#include <config-knotifications.h>
28#include "debug_p.h"
29
30#if HAVE_XTEST
31#include <QTimer>
32#include <QX11Info>
33
34#include <X11/keysym.h>
35#include <X11/extensions/XTest.h>
36#endif // HAVE_XTEST
37
38class Q_DECL_HIDDEN KNotificationRestrictions::Private
39{
40public:
41 Private(KNotificationRestrictions *qq, Services c, const QString &r)
42 : q(qq),
43 control(c)
44 , screenSaverDbusCookie(-1)
45 , reason(r)
46#if HAVE_XTEST
47 , screensaverTimer(nullptr),
48 haveXTest(0),
49 XTestKeyCode(0),
50 isX11(QX11Info::isPlatformX11())
51#endif // HAVE_XTEST
52 {
53 }
54
55 void screensaverFakeKeyEvent();
56 void startScreenSaverPrevention();
57 void stopScreenSaverPrevention();
58
59 static QString determineProgramName();
60
61 KNotificationRestrictions *q;
62 Services control;
63 int screenSaverDbusCookie;
64 QString reason;
65#if HAVE_XTEST
66 QTimer *screensaverTimer;
67 int haveXTest;
68 int XTestKeyCode;
69 bool isX11;
70#endif // HAVE_XTEST
71};
72
73KNotificationRestrictions::KNotificationRestrictions(Services control,
74 QObject *parent)
75 : KNotificationRestrictions(control, QStringLiteral("no_reason_specified"), parent)
76{
77}
78
79KNotificationRestrictions::KNotificationRestrictions(Services control,
80 const QString &reason,
81 QObject *parent)
82 : QObject(parent),
83 d(new Private(this, control, reason))
84{
85 if (d->control & ScreenSaver) {
86 d->startScreenSaverPrevention();
87 }
88}
89
90KNotificationRestrictions::~KNotificationRestrictions()
91{
92 if (d->control & ScreenSaver) {
93 d->stopScreenSaverPrevention();
94 }
95
96 delete d;
97}
98
99void KNotificationRestrictions::Private::screensaverFakeKeyEvent()
100{
101 qCDebug(LOG_KNOTIFICATIONS);
102#if HAVE_XTEST
103 if (!isX11) {
104 return;
105 }
106 qCDebug(LOG_KNOTIFICATIONS) << "---- using XTestFakeKeyEvent";
107 Display *display = QX11Info::display();
108 XTestFakeKeyEvent(display, XTestKeyCode, true, CurrentTime);
109 XTestFakeKeyEvent(display, XTestKeyCode, false, CurrentTime);
110 XSync(display, false);
111#endif // HAVE_XTEST
112}
113
114void KNotificationRestrictions::Private::startScreenSaverPrevention()
115{
116 qCDebug(LOG_KNOTIFICATIONS);
117
118 QDBusMessage message = QDBusMessage::createMethodCall(
119 QStringLiteral("org.freedesktop.ScreenSaver"), QStringLiteral("/ScreenSaver"), QStringLiteral("org.freedesktop.ScreenSaver"), QStringLiteral("Inhibit"));
120 message << determineProgramName();
121 message << reason;
122 QDBusReply<uint> reply = QDBusConnection::sessionBus().call(message);
123 if (reply.isValid()) {
124 screenSaverDbusCookie = reply.value();
125 return;
126 }
127#if HAVE_XTEST
128 if (!isX11) {
129 return;
130 }
131 if (!haveXTest) {
132 int a, b, c, e;
133 haveXTest = XTestQueryExtension(QX11Info::display(), &a, &b, &c, &e);
134
135 if (!haveXTest) {
136 qCDebug(LOG_KNOTIFICATIONS) << "--- No XTEST!";
137 return;
138 }
139 }
140
141 if (!XTestKeyCode) {
142 XTestKeyCode = XKeysymToKeycode(QX11Info::display(), XK_Shift_L);
143
144 if (!XTestKeyCode) {
145 qCDebug(LOG_KNOTIFICATIONS) << "--- No XKeyCode for XK_Shift_L!";
146 return;
147 }
148 }
149
150 if (!screensaverTimer) {
151 screensaverTimer = new QTimer(q);
152 connect(screensaverTimer, SIGNAL(timeout()),
153 q, SLOT(screensaverFakeKeyEvent()));
154 }
155
156 qCDebug(LOG_KNOTIFICATIONS) << "---- using XTest";
157 // send a fake event right away in case this got started after a period of
158 // inactivity leading to the screensaver set to activate in <55s
159 screensaverFakeKeyEvent();
160 screensaverTimer->start(55000);
161#endif // HAVE_XTEST
162}
163
164void KNotificationRestrictions::Private::stopScreenSaverPrevention()
165{
166
167 if (screenSaverDbusCookie != -1) {
168 QDBusMessage message = QDBusMessage::createMethodCall(
169 QStringLiteral("org.freedesktop.ScreenSaver"), QStringLiteral("/ScreenSaver"), QStringLiteral("org.freedesktop.ScreenSaver"), QStringLiteral("UnInhibit"));
170 message << static_cast<uint>(screenSaverDbusCookie);
171 screenSaverDbusCookie = -1;
172 if (QDBusConnection::sessionBus().send(message)) {
173 return;
174 }
175 }
176#if HAVE_XTEST
177 if (!isX11) {
178 return;
179 }
180 delete screensaverTimer;
181 screensaverTimer = nullptr;
182#endif // HAVE_XTEST
183}
184
185QString KNotificationRestrictions::Private::determineProgramName()
186{
187 QString appName = QGuiApplication::applicationDisplayName();
188 if (appName.isEmpty()) {
189 appName = QCoreApplication::applicationName();
190 }
191 if (appName.isEmpty()) {
192 appName = tr("Unknown Application");
193 }
194 return appName;
195}
196
197#include "moc_knotificationrestrictions.cpp"
198