1/***************************************************************************
2 * Copyright (C) 2011, 2012 Albert Astals Cid <aacid@kde.org> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program 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 *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
18 ***************************************************************************/
19
20#include "ktouchpadenabler_daemon.h"
21
22#include "settings.h"
23
24#include <KApplication>
25#include <KDebug>
26#include <KLocalizedString>
27#include <KNotification>
28#include <KPluginFactory>
29
30#include <QWidget>
31
32#include <X11/Xatom.h>
33#include <X11/extensions/XInput2.h>
34#include <X11/XF86keysym.h>
35
36class TouchpadEnablerDaemonPrivate : public QWidget
37{
38 public:
39 TouchpadEnablerDaemonPrivate();
40 ~TouchpadEnablerDaemonPrivate();
41
42 bool initSuccessful() const { return m_keyCode != 0; }
43
44 bool x11Event(XEvent *event);
45
46 private:
47 enum TouchpadKey { ToggleKey = 0, OnKey, OffKey };
48 static const int nKeys = OffKey + 1;
49
50 bool getEnabled(bool *currentValue) const;
51 void setEnabled(bool enabled) const;
52
53 Display *m_display;
54 KeyCode m_keyCode[nKeys];
55 int m_deviceId;
56 Atom m_enabledProperty;
57};
58
59TouchpadEnablerDaemonPrivate::TouchpadEnablerDaemonPrivate()
60{
61 bool foundTouchpad = false;
62 bool foundMoreThanOneTouchpad = false;
63
64 for (int i = 0; i < nKeys; ++i) {
65 m_keyCode[i] = 0;
66 }
67
68 m_display = QX11Info::display();
69 if (!m_display) {
70 kWarning() << "Did not find a display to use. This should never happen, thus doing nothing. Please report a bug against ktouchpadenabler in http://bugs.kde.org";
71 return;
72 }
73
74 Atom synapticsProperty = XInternAtom (m_display, "Synaptics Off", False);
75 m_enabledProperty = XInternAtom (m_display, "Device Enabled", False);
76
77 if (synapticsProperty && m_enabledProperty) {
78 int nDevices;
79 XIDeviceInfo *devices = XIQueryDevice(m_display, XIAllDevices, &nDevices);
80 for (int i = 0; i < nDevices; ++i) {
81 Atom realtype;
82 int realformat;
83 unsigned long nitems, bytes_after;
84 unsigned char *data;
85 if ((XIGetProperty (m_display, devices[i].deviceid, synapticsProperty, 0, 1, False, XA_INTEGER, &realtype, &realformat, &nitems, &bytes_after, &data) == Success) && (realtype != None)) {
86 XFree (data);
87 if ((XIGetProperty (m_display, devices[i].deviceid, m_enabledProperty, 0, 1, False, XA_INTEGER, &realtype, &realformat, &nitems, &bytes_after, &data) == Success) && (realtype != None)) {
88 XFree (data);
89 if (foundTouchpad) {
90 foundMoreThanOneTouchpad = true;
91 } else {
92 m_deviceId = devices[i].deviceid;
93 foundTouchpad = true;
94 }
95 }
96 }
97 }
98 if (devices) {
99 XIFreeDeviceInfo(devices);
100 }
101 } else {
102 kWarning() << "Could not get atoms for 'Synaptics Off' or 'Device Enabled'. This should never happen, thus doing nothing. Please report a bug against ktouchpadenabler in http://bugs.kde.org";
103 }
104
105 if (foundTouchpad) {
106 if (!foundMoreThanOneTouchpad) {
107 m_keyCode[ToggleKey] = XKeysymToKeycode(m_display, XF86XK_TouchpadToggle);
108 m_keyCode[OnKey] = XKeysymToKeycode(m_display, XF86XK_TouchpadOn);
109 m_keyCode[OffKey] = XKeysymToKeycode(m_display, XF86XK_TouchpadOff);
110 for (int i = 0; i < nKeys; ++i) {
111 if (m_keyCode[i] != 0) {
112 const int grabResult = XGrabKey(m_display, m_keyCode[i], AnyModifier, QX11Info::appRootWindow(), False, GrabModeAsync, GrabModeAsync);
113 if (grabResult == BadAccess || grabResult == BadValue || grabResult == BadWindow) {
114 kDebug() << "Could not grab ktouchpadenabler key index" << i <<". You probably have some other program grabbig it, if you are sure you don't have any, please report a bug against ktouchpadenabler in http://bugs.kde.org";
115 m_keyCode[i] = 0;
116 } else {
117 bool currentlyEnabled;
118 if (getEnabled(&currentlyEnabled)) {
119 const bool newValue = ktouchpadenabler::Settings::self()->touchpadEnabled();
120 if (newValue != currentlyEnabled) {
121 setEnabled(newValue);
122 }
123 }
124 }
125 } else {
126 kWarning() << "Could not match ktouchpadenabler key index" << i << "to a Keycode. This should never happen. Please report a bug against ktouchpadenabler in http://bugs.kde.org";
127 }
128 }
129 } else {
130 KNotification *notification = KNotification::event(KNotification::Warning, i18n("Touchpad status"), i18n("More than one touchpad detected. Touchpad Enabler Daemon does not handle this configuration"));
131 notification->sendEvent();
132 }
133 } else {
134 kDebug() << "Did not find a touchpad. If you have one, please report a bug against ktouchpadenabler in http://bugs.kde.org";
135 }
136}
137
138TouchpadEnablerDaemonPrivate::~TouchpadEnablerDaemonPrivate()
139{
140 for (int i = 0; i < nKeys; ++i) {
141 if (m_keyCode[i] != 0) {
142 XUngrabKey(m_display, m_keyCode[i], 0 /* No modifiers */, QX11Info::appRootWindow());
143 }
144 }
145}
146
147bool TouchpadEnablerDaemonPrivate::x11Event(XEvent *event)
148{
149 if (event->type == KeyPress) {
150 for (int i = 0; i < nKeys; ++i) {
151 if (event->xkey.keycode == m_keyCode[i]) {
152 bool currentlyEnabled;
153 if (getEnabled(&currentlyEnabled)) {
154 bool newValue;
155 switch (i) {
156 case ToggleKey: newValue = !currentlyEnabled; break;
157 case OnKey: newValue = true; break;
158 case OffKey: newValue = false; break;
159 }
160 if (newValue != currentlyEnabled) {
161 setEnabled(newValue);
162
163 KNotification *notification = KNotification::event(KNotification::Notification, i18n("Touchpad status"), newValue ? i18n("Touchpad enabled") : i18n("Touchpad disabled"));
164 notification->sendEvent();
165
166 ktouchpadenabler::Settings::self()->setTouchpadEnabled(newValue);
167 ktouchpadenabler::Settings::self()->writeConfig();
168 }
169 return true;
170 }
171 }
172 }
173 }
174 return false;
175}
176
177bool TouchpadEnablerDaemonPrivate::getEnabled(bool *enabled) const
178{
179 Atom realtype;
180 int realformat;
181 unsigned long nitems, bytes_after;
182 unsigned char *value;
183 if ((XIGetProperty(m_display, m_deviceId, m_enabledProperty, 0, 1, False, XA_INTEGER, &realtype, &realformat, &nitems, &bytes_after, &value) == Success) && (realtype != None)) {
184 *enabled = (*value == 1);
185 XFree(value);
186 return true;
187 } else {
188 return false;
189 }
190}
191
192void TouchpadEnablerDaemonPrivate::setEnabled(bool enabled) const
193{
194 unsigned char newValue = enabled ? 1 : 0;
195 XIChangeProperty(m_display, m_deviceId, m_enabledProperty, XA_INTEGER, 8, PropModeReplace, &newValue, 1);
196 XFlush(m_display);
197}
198
199TouchpadEnablerDaemon::TouchpadEnablerDaemon(QObject *parent, const QList<QVariant>&)
200 : KDEDModule(parent)
201{
202 d = new TouchpadEnablerDaemonPrivate();
203
204 if (d->initSuccessful()) {
205 kapp->installX11EventFilter(d);
206 } else {
207 delete d;
208 d = 0;
209 }
210}
211
212TouchpadEnablerDaemon::~TouchpadEnablerDaemon()
213{
214 delete d;
215}
216
217K_PLUGIN_FACTORY(TouchpadEnablerFactory, registerPlugin<TouchpadEnablerDaemon>();)
218K_EXPORT_PLUGIN(TouchpadEnablerFactory("ktouchpadenabler"))
219