diff --git a/kcms/touchpad/src/backends/x11/xlibbackend.cpp b/kcms/touchpad/src/backends/x11/xlibbackend.cpp index 8dedcc8ee..347dc07fb 100644 --- a/kcms/touchpad/src/backends/x11/xlibbackend.cpp +++ b/kcms/touchpad/src/backends/x11/xlibbackend.cpp @@ -1,315 +1,317 @@ /* * Copyright (C) 2013 Alexander Mezin * * 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 #include #include #include #include #include //Includes are ordered this way because of #defines in Xorg's headers #include "xrecordkeyboardmonitor.h" // krazy:exclude=includes #include "xlibbackend.h" // krazy:exclude=includes #include "xlibnotifications.h" // krazy:exclude=includes #include "libinputtouchpad.h" #include "synapticstouchpad.h" #include "propertyinfo.h" #include #include #include #include #include #include struct DeviceListDeleter { static void cleanup(XDeviceInfo *p) { if (p) { XFreeDeviceList(p); } } }; void XlibBackend::XDisplayCleanup::cleanup(Display *p) { if (p) { XCloseDisplay(p); } } XlibBackend* XlibBackend::initialize(QObject *parent) { - XlibBackend* backend = nullptr; - - backend = new XlibBackend(parent); + XlibBackend* backend = new XlibBackend(parent); + if (!backend->m_display) { + delete backend; + return nullptr; + } return backend; } XlibBackend::~XlibBackend() { } XlibBackend::XlibBackend(QObject *parent) : TouchpadBackend(parent), m_display(XOpenDisplay(0)), m_connection(0) { if (m_display) { m_connection = XGetXCBConnection(m_display.data()); } if (!m_connection) { m_errorString = i18n("Cannot connect to X server"); return; } m_mouseAtom.intern(m_connection, XI_MOUSE); m_keyboardAtom.intern(m_connection, XI_KEYBOARD); m_touchpadAtom.intern(m_connection, XI_TOUCHPAD); m_enabledAtom.intern(m_connection, XI_PROP_ENABLED); m_synapticsIdentifierAtom.intern(m_connection, SYNAPTICS_PROP_CAPABILITIES); m_libinputIdentifierAtom.intern(m_connection, "libinput Send Events Modes Available"); m_device.reset(findTouchpad()); if (!m_device) { m_errorString = ("No touchpad found"); } } XlibTouchpad* XlibBackend::findTouchpad() { int nDevices = 0; QScopedPointer deviceInfo(XListInputDevices(m_display.data(), &nDevices)); for (XDeviceInfo *info = deviceInfo.data(); info < deviceInfo.data() + nDevices; info++) { // Make sure device is touchpad if (info->type != m_touchpadAtom.atom()) { continue; } int nProperties = 0; QSharedPointer properties( XIListProperties(m_display.data(), info->id, &nProperties), XDeleter); Atom *atom = properties.data(), *atomEnd = properties.data() + nProperties; for (; atom != atomEnd; atom++) { if (*atom == m_libinputIdentifierAtom.atom()) { return new LibinputTouchpad(m_display.data(), info->id); } else if (*atom == m_synapticsIdentifierAtom.atom()) { return new SynapticsTouchpad(m_display.data(), info->id); } } } return nullptr; } bool XlibBackend::applyConfig(const QVariantHash &p) { if (!m_device) { return false; } bool success = m_device->applyConfig(p); if (!success) { m_errorString = i18n("Cannot apply touchpad configuration"); } return success; } bool XlibBackend::getConfig(QVariantHash &p) { if (!m_device) { return false; } bool success = m_device->getConfig(p); if (!success) { m_errorString = i18n("Cannot read touchpad configuration"); } return success; } void XlibBackend::setTouchpadEnabled(bool enable) { if (!m_device) { return; } m_device->setEnabled(enable); } void XlibBackend::setTouchpadOff(TouchpadBackend::TouchpadOffState state) { if (!m_device) { return; } int touchpadOff = 0; switch (state) { case TouchpadEnabled: touchpadOff = 0; break; case TouchpadFullyDisabled: touchpadOff = 1; break; case TouchpadTapAndScrollDisabled: touchpadOff = 2; break; default: qCritical() << "Unknown TouchpadOffState" << state; return; } m_device->setTouchpadOff(touchpadOff); } bool XlibBackend::isTouchpadAvailable() { return m_device; } bool XlibBackend::isTouchpadEnabled() { if (!m_device) { return false; } return m_device->enabled(); } TouchpadBackend::TouchpadOffState XlibBackend::getTouchpadOff() { if (!m_device) { return TouchpadFullyDisabled; } int value = m_device->touchpadOff(); switch (value) { case 0: return TouchpadEnabled; case 1: return TouchpadFullyDisabled; case 2: return TouchpadTapAndScrollDisabled; default: qCritical() << "Unknown TouchpadOff value" << value; return TouchpadFullyDisabled; } } void XlibBackend::touchpadDetached() { qWarning() << "Touchpad detached"; m_device.reset(); Q_EMIT touchpadReset(); } void XlibBackend::devicePlugged(int device) { if (!m_device) { m_device.reset(findTouchpad()); if (m_device) { qWarning() << "Touchpad reset"; m_notifications.reset(); watchForEvents(m_keyboard); Q_EMIT touchpadReset(); } } if (!m_device || device != m_device->deviceId()) { Q_EMIT mousesChanged(); } } void XlibBackend::propertyChanged(xcb_atom_t prop) { if ((m_device && prop == m_device->touchpadOffAtom().atom()) || prop == m_enabledAtom.atom()) { Q_EMIT touchpadStateChanged(); } } QStringList XlibBackend::listMouses(const QStringList &blacklist) { int nDevices = 0; QScopedPointer info(XListInputDevices(m_display.data(), &nDevices)); QStringList list; for (XDeviceInfo *i = info.data(); i != info.data() + nDevices; i++) { if (m_device && i->id == static_cast(m_device->deviceId())) { continue; } if (i->use != IsXExtensionPointer && i->use != IsXPointer) { continue; } //type = KEYBOARD && use = Pointer means usb receiver for both keyboard //and mouse if (i->type != m_mouseAtom.atom() && i->type != m_keyboardAtom.atom()) { continue; } QString name(i->name); if (blacklist.contains(name, Qt::CaseInsensitive)) { continue; } PropertyInfo enabled(m_display.data(), i->id, m_enabledAtom.atom(), 0); if (enabled.value(0) == false) { continue; } list.append(name); } return list; } void XlibBackend::watchForEvents(bool keyboard) { if (!m_notifications) { m_notifications.reset(new XlibNotifications(m_display.data(), m_device ? m_device->deviceId() : XIAllDevices)); connect(m_notifications.data(), SIGNAL(devicePlugged(int)), SLOT(devicePlugged(int))); connect(m_notifications.data(), SIGNAL(touchpadDetached()), SLOT(touchpadDetached())); connect(m_notifications.data(), SIGNAL(propertyChanged(xcb_atom_t)), SLOT(propertyChanged(xcb_atom_t))); } if (keyboard == !m_keyboard.isNull()) { return; } if (!keyboard) { m_keyboard.reset(); return; } m_keyboard.reset(new XRecordKeyboardMonitor(m_display.data())); connect(m_keyboard.data(), SIGNAL(keyboardActivityStarted()), SIGNAL(keyboardActivityStarted())); connect(m_keyboard.data(), SIGNAL(keyboardActivityFinished()), SIGNAL(keyboardActivityFinished())); } diff --git a/kcms/touchpad/src/backends/x11/xrecordkeyboardmonitor.cpp b/kcms/touchpad/src/backends/x11/xrecordkeyboardmonitor.cpp index 1dceee219..0884dadcd 100644 --- a/kcms/touchpad/src/backends/x11/xrecordkeyboardmonitor.cpp +++ b/kcms/touchpad/src/backends/x11/xrecordkeyboardmonitor.cpp @@ -1,150 +1,156 @@ /* * Copyright (C) 2013 Alexander Mezin * * 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 "xrecordkeyboardmonitor.h" #include #include #include #include #include XRecordKeyboardMonitor::XRecordKeyboardMonitor(Display *display) : m_connection(xcb_connect(XDisplayString(display), 0)), m_modifiersPressed(0), m_keysPressed(0) { if (!m_connection) { return; } xcb_get_modifier_mapping_cookie_t modmapCookie = xcb_get_modifier_mapping(m_connection); m_context = xcb_generate_id(m_connection); xcb_record_range_t range; memset(&range, 0, sizeof(range)); range.device_events.first = XCB_KEY_PRESS; range.device_events.last = XCB_KEY_RELEASE; xcb_record_client_spec_t cs = XCB_RECORD_CS_ALL_CLIENTS; xcb_record_create_context(m_connection, m_context, 0, 1, 1, &cs, &range); xcb_flush(m_connection); QScopedPointer modmap(xcb_get_modifier_mapping_reply(m_connection, modmapCookie, 0)); if (!modmap) { return; } int nModifiers = xcb_get_modifier_mapping_keycodes_length(modmap.data()); xcb_keycode_t *modifiers = xcb_get_modifier_mapping_keycodes(modmap.data()); m_modifier.fill(false, std::numeric_limits::max() + 1); for (xcb_keycode_t *i = modifiers; i < modifiers + nModifiers; i++) { m_modifier[*i] = true; } m_ignore.fill(false, std::numeric_limits::max() + 1); for (xcb_keycode_t *i = modifiers; i < modifiers + modmap->keycodes_per_modifier; i++) { m_ignore[*i] = true; } m_pressed.fill(false, std::numeric_limits::max() + 1); m_cookie = xcb_record_enable_context(m_connection, m_context); xcb_flush(m_connection); m_notifier = new QSocketNotifier(xcb_get_file_descriptor(m_connection), QSocketNotifier::Read, this); connect(m_notifier, SIGNAL(activated(int)), SLOT(processNextReply())); m_notifier->setEnabled(true); } XRecordKeyboardMonitor::~XRecordKeyboardMonitor() { if (!m_connection) { return; } xcb_record_disable_context(m_connection, m_context); xcb_record_free_context(m_connection, m_context); xcb_disconnect(m_connection); } void XRecordKeyboardMonitor::processNextReply() { xcb_generic_event_t *event; while ((event = xcb_poll_for_event(m_connection))) { std::free(event); } void *reply = 0; - while (xcb_poll_for_reply(m_connection, m_cookie.sequence, &reply, 0)) { + xcb_generic_error_t *error = nullptr; + while (m_cookie.sequence && xcb_poll_for_reply(m_connection, m_cookie.sequence, &reply, &error)) { + if (error) { + std::free(error); + break; + } + if (!reply) { continue; } QScopedPointer data(reinterpret_cast(reply)); process(data.data()); } } void XRecordKeyboardMonitor::process(xcb_record_enable_context_reply_t *reply) { bool prevActivity = activity(); xcb_key_press_event_t *events = reinterpret_cast (xcb_record_enable_context_data(reply)); int nEvents = xcb_record_enable_context_data_length(reply) / sizeof(xcb_key_press_event_t); bool wasActivity = prevActivity; for (xcb_key_press_event_t *e = events; e < events + nEvents; e++) { if (e->response_type != XCB_KEY_PRESS && e->response_type != XCB_KEY_RELEASE) { continue; } if (m_ignore[e->detail]) { continue; } bool pressed = (e->response_type == XCB_KEY_PRESS); if (m_pressed[e->detail] == pressed) { continue; } m_pressed[e->detail] = pressed; int &counter = m_modifier[e->detail] ? m_modifiersPressed : m_keysPressed; if (pressed) { counter++; } else { counter--; } wasActivity = wasActivity || activity(); } if (!prevActivity && activity()) { Q_EMIT keyboardActivityStarted(); } else if (!activity() && wasActivity) { Q_EMIT keyboardActivityFinished(); } }