diff --git a/CMakeLists.txt b/CMakeLists.txt index 19ba8ee..dc75e8f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,55 +1,55 @@ cmake_minimum_required(VERSION 3.0) project(wacomtablet) set(QT_MIN_VERSION "5.3.0") find_package(ECM 1.6.0 REQUIRED CONFIG) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_SOURCE_DIR}/cmake/modules) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(FeatureSummary) include(ECMOptionalAddSubdirectory) include(ECMInstallIcons) find_package(Qt5 REQUIRED COMPONENTS Core Gui Widgets DBus X11Extras Qml) find_package(KF5 REQUIRED COMPONENTS CoreAddons I18n GlobalAccel Config XmlGui WidgetsAddons WindowSystem Notifications DBusAddons Plasma DocTools) find_package(XCB REQUIRED COMPONENTS RANDR OPTIONAL_COMPONENTS XINPUT) find_package(X11 REQUIRED COMPONENTS XLIB RANDR) find_package(XorgWacom REQUIRED) # xcb-xinput is an experimental API and is not built by default in the xcb # repository. Downstream distributions without xcb-xinput, such as Kubuntu, can # build using the Xlib version of the library instead. # # Because Qt5 uses xcb exclusively for native event handling, kded is unable # to monitor hotplugging events, since these are signaled as xinput events. # # Users can enable xcb-xinput by building xcb with the configuration flag --enable-xinput. if(XCB_XINPUT_FOUND) message(STATUS "Using XCB_XINPUT. Please note this is an unstable API.") set(USING_X_LIBRARIES XCB::XINPUT XCB::RANDR ${X11_Xrandr_LIB} ${X11_Xinput_LIB}) add_definitions(-DHAVE_XCB_XINPUT) else() - message(STATUS "Falling back to X11_XINPUT. (Daemon will be unable to monitor hotplugging.)") + message(STATUS "Falling back to X11_XINPUT.") set(USING_X_LIBRARIES XCB::RANDR ${X11_LIBRARIES} ${X11_Xinput_LIB} ${X11_Xrandr_LIB}) endif() add_definitions( -DQT_STRICT_ITERATORS ) add_definitions( -DQT_NO_CAST_FROM_ASCII ) add_definitions( -DQT_NO_CAST_TO_ASCII ) add_definitions( -DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS ) add_subdirectory( src ) add_subdirectory( data ) add_subdirectory( images ) add_subdirectory( doc ) ### Tests if(BUILD_TESTING) find_package(Qt5Test ${QT_MIN_VERSION} CONFIG REQUIRED) add_subdirectory( autotests ) endif() feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/src/kded/x11eventnotifier-xlib.cpp b/src/kded/x11eventnotifier-xlib.cpp index 82c1da7..705e2f0 100644 --- a/src/kded/x11eventnotifier-xlib.cpp +++ b/src/kded/x11eventnotifier-xlib.cpp @@ -1,146 +1,194 @@ /* * This file is part of the KDE wacomtablet project. For copyright * information and license terms see the AUTHORS and COPYING files * in the top-level directory of this distribution. * * 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, see . */ #include "debug.h" // always needs to be first include #include #include - #include "x11eventnotifier.h" #include "x11input.h" #include "x11inputdevice.h" #include -#include - -#include -#include -#include -#include #include +/* XCB events have a slightly different layout, so they can't be directly + * treated as XIGenericDeviceEvent. Instead of doing a memmove like + * Qt does in qtbase/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp, + * we pull in the correct struct definition here. */ + +typedef enum xcb_input_hierarchy_mask_t { + XCB_INPUT_HIERARCHY_MASK_MASTER_ADDED = 1, + XCB_INPUT_HIERARCHY_MASK_MASTER_REMOVED = 2, + XCB_INPUT_HIERARCHY_MASK_SLAVE_ADDED = 4, + XCB_INPUT_HIERARCHY_MASK_SLAVE_REMOVED = 8, + XCB_INPUT_HIERARCHY_MASK_SLAVE_ATTACHED = 16, + XCB_INPUT_HIERARCHY_MASK_SLAVE_DETACHED = 32, + XCB_INPUT_HIERARCHY_MASK_DEVICE_ENABLED = 64, + XCB_INPUT_HIERARCHY_MASK_DEVICE_DISABLED = 128 +} xcb_input_hierarchy_mask_t; + +typedef struct xcb_input_hierarchy_event_t { + uint8_t response_type; + uint8_t extension; + uint16_t sequence; + uint32_t length; + uint16_t event_type; + uint8_t deviceid; + xcb_timestamp_t time; + uint32_t flags; + uint16_t num_infos; + uint8_t pad0[10]; + uint32_t full_sequence; +} xcb_input_hierarchy_event_t; + +typedef struct xcb_input_hierarchy_info_t { + uint8_t deviceid; + uint8_t attachment; + uint8_t type; + uint8_t enabled; + uint8_t pad0[2]; + uint32_t flags; +} xcb_input_hierarchy_info_t; + namespace Wacom { class X11EventNotifierPrivate { public: bool isStarted = false; }; } using namespace Wacom; X11EventNotifier::X11EventNotifier() : EventNotifier(nullptr) , QAbstractNativeEventFilter() , d_ptr(new X11EventNotifierPrivate) { } X11EventNotifier::~X11EventNotifier() { delete d_ptr; } X11EventNotifier& X11EventNotifier::instance() { static X11EventNotifier instance; return instance; } void X11EventNotifier::start() { Q_D (X11EventNotifier); if (d->isStarted) { return; } if( QCoreApplication::instance() != nullptr ) { registerForNewDeviceEvent(QX11Info::connection()); QCoreApplication::instance()->installNativeEventFilter(this); d->isStarted = true; } } void X11EventNotifier::stop() { Q_D (X11EventNotifier); if( QCoreApplication::instance() != nullptr ) { QCoreApplication::instance()->removeNativeEventFilter(this); d->isStarted = false; } } bool X11EventNotifier::nativeEventFilter(const QByteArray &eventType, void *message, long int *result) { - Q_UNUSED(eventType); Q_UNUSED(result); + if (eventType != "xcb_generic_event_t") { + return false; + } + xcb_generic_event_t *event = static_cast(message); xcb_ge_generic_event_t *cookie = reinterpret_cast(message); if (event->response_type == XCB_GE_GENERIC && cookie->event_type == XI_HierarchyChanged) { - // handleX11InputEvent(cookie); - } else { - // handleX11ScreenEvent(event); + handleX11InputEvent(cookie); } - // return QWidget::x11Event(event); return false; } // Handle plug/unplug events void X11EventNotifier::handleX11InputEvent(xcb_ge_generic_event_t* event) { - Q_UNUSED(event) - // Sadly there does not seem to be a way to use this functionality :( + xcb_input_hierarchy_event_t *hev = reinterpret_cast(event); + xcb_input_hierarchy_info_t *data = reinterpret_cast(hev + 1); + + while(hev->num_infos--) { + if (data->flags & XCB_INPUT_HIERARCHY_MASK_SLAVE_REMOVED) { + dbgWacom << QString::fromLatin1("X11 device with id '%1' removed.").arg(data->deviceid); + emit tabletRemoved(data->deviceid); + + } else if (data->flags & XCB_INPUT_HIERARCHY_MASK_SLAVE_ADDED) { + dbgWacom << QString::fromLatin1("X11 device with id '%1' added.").arg(data->deviceid); + + X11InputDevice device (data->deviceid, QLatin1String("Unknown X11 Device")); + + if (device.isOpen() && device.isTabletDevice()) { + dbgWacom << QString::fromLatin1("Wacom tablet device with X11 id '%1' added.").arg(data->deviceid); + emit tabletAdded(data->deviceid); + } + } + data++; + } } -int X11EventNotifier::registerForNewDeviceEvent(xcb_connection_t* conn) +int X11EventNotifier::registerForNewDeviceEvent(xcb_connection_t *conn) { Q_UNUSED(conn) - // This is already done by xcb plugin with more flags, doing this ourselves - // will break Qt's xrandr functionality because connection is shared with Qt. - // TODO: uncomment this again when we use our private connection. -#if 0 - //register RandR events - int rrmask = XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE; - - xcb_randr_select_input(conn, QX11Info::appRootWindow(), 0); - xcb_randr_select_input(conn, QX11Info::appRootWindow(), rrmask); -#endif - - return 0; + + Display *display = QX11Info::display(); + + unsigned int bitmask = XI_HierarchyChangedMask; + + XIEventMask mask; + mask.mask_len = sizeof(bitmask); + mask.mask = reinterpret_cast(&bitmask); + int status = XISelectEvents(display, DefaultRootWindow(display), &mask, 1); + return status == Success ? 0 : 1; }