xinput_helper.cpp 6.35 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
 *  Copyright (C) 2010 Andriy Rysin (rysin@kde.org)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "xinput_helper.h"
20
#include "debug.h"
21
#include <config-keyboard.h>
22

Andriy Rysin's avatar
Andriy Rysin committed
23
24
#include <QCoreApplication>
#include <QDebug>
Alexander Lohnau's avatar
Alexander Lohnau committed
25
#include <QX11Info>
26
27
28
29

#include <X11/X.h>
#include <X11/Xatom.h>

30
#ifdef HAVE_XINPUT
31
#include <X11/extensions/XInput.h>
32
33
#include <xcb/xproto.h>
typedef struct xcb_input_device_presence_notify_event_t {
Alexander Lohnau's avatar
Alexander Lohnau committed
34
35
36
    uint8_t response_type;
    uint8_t pad0;
    uint16_t sequence;
37
    xcb_timestamp_t time;
Alexander Lohnau's avatar
Alexander Lohnau committed
38
39
40
41
    uint8_t devchange;
    uint8_t device_id;
    uint16_t control;
    uint8_t pad1[20];
42
43
} xcb_input_device_presence_notify_event_t;
// FIXME: #include <xcb/xinput.h> once xcb-xinput is stable
44
45
#endif

46
#include "udev_helper.h"
47

48
49
50
static const int DEVICE_NONE = 0;
static const int DEVICE_KEYBOARD = 1;
static const int DEVICE_POINTER = 2;
51

Alexander Lohnau's avatar
Alexander Lohnau committed
52
53
54
55
56
XInputEventNotifier::XInputEventNotifier(QWidget *parent)
    : XEventNotifier()
    , // TODO: destruct properly?
    xinputEventType(-1)
    , udevNotifier(nullptr)
57
{
Alexander Lohnau's avatar
Alexander Lohnau committed
58
    Q_UNUSED(parent)
59
60
61
62
}

void XInputEventNotifier::start()
{
Alexander Lohnau's avatar
Alexander Lohnau committed
63
64
65
    if (QCoreApplication::instance() != nullptr) {
        registerForNewDeviceEvent(QX11Info::display());
    }
66

Alexander Lohnau's avatar
Alexander Lohnau committed
67
    XEventNotifier::start();
68
69
70
71
}

void XInputEventNotifier::stop()
{
Alexander Lohnau's avatar
Alexander Lohnau committed
72
    XEventNotifier::stop();
73
74
}

Alexander Lohnau's avatar
Alexander Lohnau committed
75
bool XInputEventNotifier::processOtherEvents(xcb_generic_event_t *event)
76
{
Alexander Lohnau's avatar
Alexander Lohnau committed
77
78
79
80
81
82
83
84
    int newDeviceType = getNewDeviceEventType(event);
    if (newDeviceType == DEVICE_KEYBOARD) {
        Q_EMIT emit(newKeyboardDevice());
    } else if (newDeviceType == DEVICE_POINTER) {
        Q_EMIT emit(newPointerDevice());
        Q_EMIT emit(newKeyboardDevice()); // arghhh, looks like X resets xkb map even when only pointer device is connected
    }
    return true;
85
86
}

87
#if defined(HAVE_XINPUT)
88

89
// This is ugly but allows to skip multiple execution of setxkbmap
90
// for all keyboard devices that don't care about layouts
Alexander Lohnau's avatar
Alexander Lohnau committed
91
static bool isRealKeyboard(const char *deviceName)
92
{
Alexander Lohnau's avatar
Alexander Lohnau committed
93
    return strstr(deviceName, "Video Bus") == nullptr && strstr(deviceName, "Sleep Button") == nullptr && strstr(deviceName, "Power Button") == nullptr
94
        && strstr(deviceName, "WMI hotkeys") == nullptr && strstr(deviceName, "Camera") == nullptr;
95
96
}

Alexander Lohnau's avatar
Alexander Lohnau committed
97
int XInputEventNotifier::getNewDeviceEventType(xcb_generic_event_t *event)
98
{
Alexander Lohnau's avatar
Alexander Lohnau committed
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
    int newDeviceType = DEVICE_NONE;
    if (xinputEventType != -1 && event->response_type == xinputEventType) {
        xcb_input_device_presence_notify_event_t *xdpne = reinterpret_cast<xcb_input_device_presence_notify_event_t *>(event);
        if (xdpne->devchange == DeviceEnabled) {
            int ndevices;
            XDeviceInfo *devices = XListInputDevices(display, &ndevices);
            if (devices != nullptr) {
                qCDebug(KCM_KEYBOARD) << "New device id:" << xdpne->device_id;
                for (int i = 0; i < ndevices; i++) {
                    qCDebug(KCM_KEYBOARD) << "id:" << devices[i].id << "name:" << devices[i].name << "used as:" << devices[i].use;
                    if (devices[i].id == xdpne->device_id) {
                        if (devices[i].use == IsXKeyboard || devices[i].use == IsXExtensionKeyboard) {
                            if (isRealKeyboard(devices[i].name)) {
                                newDeviceType = DEVICE_KEYBOARD;
                                qCDebug(KCM_KEYBOARD) << "new keyboard device, id:" << devices[i].id << "name:" << devices[i].name
                                                      << "used as:" << devices[i].use;
                                break;
                            }
                        }
                        if (devices[i].use == IsXPointer || devices[i].use == IsXExtensionPointer) {
                            newDeviceType = DEVICE_POINTER;
                            qCDebug(KCM_KEYBOARD) << "new pointer device, id:" << devices[i].id << "name:" << devices[i].name << "used as:" << devices[i].use;
                            break;
                        }
                    }
                }
                XFreeDeviceList(devices);
            }
        }
    }
    return newDeviceType;
130
131
}

Alexander Lohnau's avatar
Alexander Lohnau committed
132
int XInputEventNotifier::registerForNewDeviceEvent(Display *display_)
133
{
Alexander Lohnau's avatar
Alexander Lohnau committed
134
135
136
137
138
139
140
141
142
    int xitype;
    XEventClass xiclass;
    display = display_;

    DevicePresence(display, xitype, xiclass);
    XSelectExtensionEvent(display, DefaultRootWindow(display), &xiclass, 1);
    qCDebug(KCM_KEYBOARD) << "Registered for new device events from XInput, class" << xitype;
    xinputEventType = xitype;
    return xitype;
143
144
}

145
146
#elif defined(HAVE_UDEV)

Alexander Lohnau's avatar
Alexander Lohnau committed
147
int XInputEventNotifier::registerForNewDeviceEvent(Display * /*display*/)
148
149
150
151
152
153
154
155
156
157
158
159
{
    if (!udevNotifier) {
        udevNotifier = new UdevDeviceNotifier(this);
        connect(udevNotifier, &UdevDeviceNotifier::newKeyboardDevice, this, &XInputEventNotifier::newKeyboardDevice);
        connect(udevNotifier, &UdevDeviceNotifier::newPointerDevice, this, &XInputEventNotifier::newPointerDevice);
        // Same as with XInput notifier, also emit newKeyboardDevice when pointer device is found
        connect(udevNotifier, &UdevDeviceNotifier::newPointerDevice, this, &XInputEventNotifier::newKeyboardDevice);
    }

    return -1;
}

Alexander Lohnau's avatar
Alexander Lohnau committed
160
int XInputEventNotifier::getNewDeviceEventType(xcb_generic_event_t * /*event*/)
161
162
163
164
{
    return DEVICE_NONE;
}

165
166
167
#else

#ifdef __GNUC__
168
#warning "Keyboard daemon is compiled without XInput and UDev, keyboard settings will be reset when new keyboard device is plugged in!"
169
170
#endif

Alexander Lohnau's avatar
Alexander Lohnau committed
171
int XInputEventNotifier::registerForNewDeviceEvent(Display * /*display*/)
172
{
Alexander Lohnau's avatar
Alexander Lohnau committed
173
174
    qCWarning(KCM_KEYBOARD) << "Keyboard kded daemon is compiled without XInput, xkb configuration will be reset when new keyboard device is plugged in!";
    return -1;
175
176
}

Alexander Lohnau's avatar
Alexander Lohnau committed
177
int XInputEventNotifier::getNewDeviceEventType(xcb_generic_event_t * /*event*/)
178
{
Alexander Lohnau's avatar
Alexander Lohnau committed
179
    return DEVICE_NONE;
180
181
182
}

#endif