From 574058bec21f4ae41e5d0caf28d8e67b7de7fbc4 Mon Sep 17 00:00:00 2001 From: brunoherbelin Date: Tue, 18 Nov 2025 11:16:51 +0100 Subject: [PATCH] Re-implementation of TabletInput for Linux with X11 input for compatibility with Flatpak --- CMakeLists.txt | 34 ++-- osx/TabletInput_macos.mm | 2 +- src/CMakeLists.txt | 17 +- src/TabletInput.h | 21 ++- src/TabletInput_linux.cpp | 205 ------------------------ src/TabletInput_linux_libinput.cpp | 206 ++++++++++++++++++++++++ src/TabletInput_x11.cpp | 249 +++++++++++++++++++++++++++++ 7 files changed, 510 insertions(+), 224 deletions(-) create mode 100644 src/TabletInput_linux_libinput.cpp create mode 100644 src/TabletInput_x11.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1932759..64cfc4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,20 +103,28 @@ if(UNIX) ${X11_INCLUDE_DIR} ) - # libinput and libudev for tablet pressure support - if (PKG_CONFIG_FOUND) - pkg_check_modules(LIBINPUT libinput>=1.19) - pkg_check_modules(LIBUDEV libudev) - endif() - - if(LIBINPUT_FOUND AND LIBUDEV_FOUND) - include_directories(${LIBINPUT_INCLUDE_DIRS} ${LIBUDEV_INCLUDE_DIRS}) - link_directories(${LIBINPUT_LIBRARY_DIRS} ${LIBUDEV_LIBRARY_DIRS}) - add_definitions(-DHAVE_LIBINPUT) - macro_log_feature(LIBINPUT_FOUND "libinput" "Input device library for tablet support" "https://wayland.freedesktop.org/libinput" FALSE) - macro_log_feature(LIBUDEV_FOUND "libudev" "Device management library" "https://www.freedesktop.org/software/systemd/man/libudev.html" FALSE) + # XInput2 for tablet pressure support (optional) + find_package(X11 COMPONENTS Xi) + if(X11_Xi_FOUND) + add_definitions(-DHAVE_X11TABLETINPUT) + macro_log_feature(X11_Xi_FOUND "XInput2" "X11 Input extension for tablet support" "https://www.x.org/wiki/" FALSE) else() - message(STATUS "libinput or libudev not found - tablet pressure support will be disabled") + + # alternatively libinput and libudev for tablet pressure support + if (PKG_CONFIG_FOUND) + pkg_check_modules(LIBINPUT libinput>=1.19) + pkg_check_modules(LIBUDEV libudev) + endif() + + if(LIBINPUT_FOUND AND LIBUDEV_FOUND) + include_directories(${LIBINPUT_INCLUDE_DIRS} ${LIBUDEV_INCLUDE_DIRS}) + link_directories(${LIBINPUT_LIBRARY_DIRS} ${LIBUDEV_LIBRARY_DIRS}) + add_definitions(-DHAVE_LIBINPUT) + macro_log_feature(LIBINPUT_FOUND "libinput" "Input device library for tablet support" "https://wayland.freedesktop.org/libinput" FALSE) + macro_log_feature(LIBUDEV_FOUND "libudev" "Device management library" "https://www.freedesktop.org/software/systemd/man/libudev.html" FALSE) + else() + message(STATUS "XInput2 (Xi) or libinput or libudev not found - tablet pressure support will be disabled") + endif() endif() endif() diff --git a/osx/TabletInput_macos.mm b/osx/TabletInput_macos.mm index 67435c9..7d4411b 100644 --- a/osx/TabletInput_macos.mm +++ b/osx/TabletInput_macos.mm @@ -35,7 +35,7 @@ TabletInput::~TabletInput() terminate(); } -bool TabletInput::init(void* platform_handle) +bool TabletInput::init() { // Create event monitor for tablet events NSEventMask mask = NSEventMaskTabletPoint | diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f129b9c..7cedef9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -120,7 +120,15 @@ IF(APPLE) ELSE(APPLE) # Add Linux tablet input implementation - list(APPEND VMIX_SRCS TabletInput_linux.cpp) + if(X11_Xi_FOUND) + # Use X11/XInput2 version (works in Flatpak and everywhere) + list(APPEND VMIX_SRCS TabletInput_x11.cpp) + else() + if(LIBINPUT_FOUND AND LIBUDEV_FOUND) + # Use X11/XInput2 version (works in Flatpak and everywhere) + list(APPEND VMIX_SRCS TabletInput_linux_libinput.cpp) + endif() + endif() ENDIF(APPLE) @@ -164,7 +172,12 @@ ELSE(APPLE) X11::xcb ) - # Add libinput and libudev for tablet support on Linux + # Add XInput2 library for tablet support on Linux (if available) + if(X11_Xi_FOUND) + list(APPEND PLATFORM_LIBS ${X11_Xi_LIB}) + endif() + + # Add libinput and libudev for tablet support on Linux (if available) if(LIBINPUT_FOUND AND LIBUDEV_FOUND) list(APPEND PLATFORM_LIBS ${LIBINPUT_LIBRARIES} ${LIBUDEV_LIBRARIES}) endif() diff --git a/src/TabletInput.h b/src/TabletInput.h index 70f7e6b..281a206 100644 --- a/src/TabletInput.h +++ b/src/TabletInput.h @@ -23,9 +23,13 @@ #include // Platform-specific forward declarations -#if defined(LINUX) && defined(HAVE_LIBINPUT) +#ifdef LINUX +// X11 forward declarations (for XInput2) +typedef struct _XDisplay Display; +#if defined(HAVE_LIBINPUT) struct libinput; struct udev; +#endif #elif defined(APPLE) #ifdef __OBJC__ @class NSEvent; @@ -54,7 +58,7 @@ public: static TabletInput& instance(); // Initialize tablet input system - bool init(void* platform_handle = nullptr); + bool init(); // Poll for new tablet events (call once per frame) void pollEvents(); @@ -80,10 +84,21 @@ private: TabletData data_; bool active_; -#if defined(LINUX) && defined(HAVE_LIBINPUT) +#ifdef LINUX +#if defined(HAVE_X11TABLETINPUT) + // X11/XInput2 members (used when libinput is not available or in Flatpak) + Display *display_; + int xi_opcode_; + int pressure_valuator_; + int tilt_x_valuator_; + int tilt_y_valuator_; +#endif +#if defined(HAVE_LIBINPUT) + // libinput members (used for native builds with libinput) struct udev *udev_; struct libinput *li_; int fd_; +#endif #elif defined(APPLE) void* monitor_; // Event monitor handle #endif diff --git a/src/TabletInput_linux.cpp b/src/TabletInput_linux.cpp index ee6027e..e69de29 100644 --- a/src/TabletInput_linux.cpp +++ b/src/TabletInput_linux.cpp @@ -1,205 +0,0 @@ -/* - * This file is part of vimix - video live mixer - * - * **Copyright** (C) 2019-2025 Bruno Herbelin - * - * 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 3 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 . -**/ - -#ifdef LINUX - -#include "TabletInput.h" -#include "Log.h" - -#ifdef HAVE_LIBINPUT -#include -#include -#include -#include -#include - -static int open_restricted(const char *path, int flags, void *user_data) -{ - int fd = open(path, flags); - return fd < 0 ? -errno : fd; -} - -static void close_restricted(int fd, void *user_data) -{ - close(fd); -} - -static const struct libinput_interface interface = { - .open_restricted = open_restricted, - .close_restricted = close_restricted, -}; - -TabletInput::TabletInput() - : data_({0.0f, false, 0.0f, 0.0f, false, false}) - , active_(false) - , udev_(nullptr) - , li_(nullptr) - , fd_(-1) -{ -} - -TabletInput::~TabletInput() -{ - terminate(); -} - -bool TabletInput::init(void* platform_handle) -{ - udev_ = udev_new(); - if (!udev_) { - Log::Info("TabletInput: Failed to initialize udev"); - return false; - } - - li_ = libinput_udev_create_context(&interface, nullptr, udev_); - if (!li_) { - Log::Info("TabletInput: Failed to create libinput context"); - udev_unref(udev_); - udev_ = nullptr; - return false; - } - - if (libinput_udev_assign_seat(li_, "seat0") != 0) { - Log::Info("TabletInput: Failed to assign seat"); - libinput_unref(li_); - li_ = nullptr; - udev_unref(udev_); - udev_ = nullptr; - return false; - } - - fd_ = libinput_get_fd(li_); - - // Set non-blocking - int flags = fcntl(fd_, F_GETFL, 0); - fcntl(fd_, F_SETFL, flags | O_NONBLOCK); - - active_ = true; - Log::Info("TabletInput: Linux tablet input initialized (libinput)"); - return true; -} - -void TabletInput::pollEvents() -{ - if (!li_) return; - - libinput_dispatch(li_); - - struct libinput_event *event; - while ((event = libinput_get_event(li_)) != nullptr) { - enum libinput_event_type type = libinput_event_get_type(event); - - switch (type) { - case LIBINPUT_EVENT_TABLET_TOOL_AXIS: - case LIBINPUT_EVENT_TABLET_TOOL_TIP: - case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY: { - struct libinput_event_tablet_tool *tablet_event = - libinput_event_get_tablet_tool_event(event); - - // Update pressure - if (libinput_event_tablet_tool_pressure_has_changed(tablet_event)) { - data_.has_pressure = true; - data_.pressure = libinput_event_tablet_tool_get_pressure(tablet_event); - } - - // Update tilt (if available) - if (libinput_event_tablet_tool_tilt_x_has_changed(tablet_event)) { - data_.tilt_x = libinput_event_tablet_tool_get_tilt_x(tablet_event) / 90.0f; - } - if (libinput_event_tablet_tool_tilt_y_has_changed(tablet_event)) { - data_.tilt_y = libinput_event_tablet_tool_get_tilt_y(tablet_event) / 90.0f; - } - - // Update proximity - if (type == LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY) { - data_.in_proximity = (libinput_event_tablet_tool_get_proximity_state(tablet_event) - == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN); - if (!data_.in_proximity) { - data_.has_pressure = true; - data_.pressure = 0.0f; - data_.tip_down = false; - } - } - - // Update tip state - if (type == LIBINPUT_EVENT_TABLET_TOOL_TIP) { - data_.tip_down = (libinput_event_tablet_tool_get_tip_state(tablet_event) - == LIBINPUT_TABLET_TOOL_TIP_DOWN); - if (!data_.tip_down) { - data_.has_pressure = true; - data_.pressure = 0.0f; - } - } - break; - } - default: - break; - } - - libinput_event_destroy(event); - } -} - -void TabletInput::terminate() -{ - if (li_) { - libinput_unref(li_); - li_ = nullptr; - } - if (udev_) { - udev_unref(udev_); - udev_ = nullptr; - } - active_ = false; -} - -#else // !HAVE_LIBINPUT - -// Stub implementation when libinput is not available - -TabletInput::TabletInput() - : data_({0.0f, false, 0.0f, 0.0f, false, false}) - , active_(false) -{ -} - -TabletInput::~TabletInput() -{ -} - -bool TabletInput::init(void* platform_handle) -{ - Log::Info("TabletInput: libinput not available - tablet pressure support disabled"); - active_ = false; - return false; -} - -void TabletInput::pollEvents() -{ - // No-op when libinput is not available -} - -void TabletInput::terminate() -{ - active_ = false; -} - -#endif // HAVE_LIBINPUT - -#endif // LINUX diff --git a/src/TabletInput_linux_libinput.cpp b/src/TabletInput_linux_libinput.cpp new file mode 100644 index 0000000..eb4d881 --- /dev/null +++ b/src/TabletInput_linux_libinput.cpp @@ -0,0 +1,206 @@ +/* + * This file is part of vimix - video live mixer + * + * **Copyright** (C) 2019-2025 Bruno Herbelin + * + * 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 3 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 +#ifdef LINUX + +#include "TabletInput.h" +#include "Log.h" + +#ifdef HAVE_LIBINPUT +#include +#include +#include +#include +#include + +static int open_restricted(const char *path, int flags, void *user_data) +{ + int fd = open(path, flags); + return fd < 0 ? -errno : fd; +} + +static void close_restricted(int fd, void *user_data) +{ + close(fd); +} + +static const struct libinput_interface interface = { + .open_restricted = open_restricted, + .close_restricted = close_restricted, +}; + +TabletInput::TabletInput() + : data_({0.0f, false, 0.0f, 0.0f, false, false}) + , active_(false) + , udev_(nullptr) + , li_(nullptr) + , fd_(-1) +{ +} + +TabletInput::~TabletInput() +{ + terminate(); +} + +bool TabletInput::init() +{ + udev_ = udev_new(); + if (!udev_) { + Log::Info("TabletInput: Failed to initialize udev"); + return false; + } + + li_ = libinput_udev_create_context(&interface, nullptr, udev_); + if (!li_) { + Log::Info("TabletInput: Failed to create libinput context"); + udev_unref(udev_); + udev_ = nullptr; + return false; + } + + if (libinput_udev_assign_seat(li_, "seat0") != 0) { + Log::Info("TabletInput: Failed to assign seat"); + libinput_unref(li_); + li_ = nullptr; + udev_unref(udev_); + udev_ = nullptr; + return false; + } + + fd_ = libinput_get_fd(li_); + + // Set non-blocking + int flags = fcntl(fd_, F_GETFL, 0); + fcntl(fd_, F_SETFL, flags | O_NONBLOCK); + + active_ = true; + Log::Info("TabletInput: Linux tablet input initialized (libinput)"); + return true; +} + +void TabletInput::pollEvents() +{ + if (!li_) return; + + libinput_dispatch(li_); + + struct libinput_event *event; + while ((event = libinput_get_event(li_)) != nullptr) { + enum libinput_event_type type = libinput_event_get_type(event); + + switch (type) { + case LIBINPUT_EVENT_TABLET_TOOL_AXIS: + case LIBINPUT_EVENT_TABLET_TOOL_TIP: + case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY: { + struct libinput_event_tablet_tool *tablet_event = + libinput_event_get_tablet_tool_event(event); + + // Update pressure + if (libinput_event_tablet_tool_pressure_has_changed(tablet_event)) { + data_.has_pressure = true; + data_.pressure = libinput_event_tablet_tool_get_pressure(tablet_event); + } + + // Update tilt (if available) + if (libinput_event_tablet_tool_tilt_x_has_changed(tablet_event)) { + data_.tilt_x = libinput_event_tablet_tool_get_tilt_x(tablet_event) / 90.0f; + } + if (libinput_event_tablet_tool_tilt_y_has_changed(tablet_event)) { + data_.tilt_y = libinput_event_tablet_tool_get_tilt_y(tablet_event) / 90.0f; + } + + // Update proximity + if (type == LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY) { + data_.in_proximity = (libinput_event_tablet_tool_get_proximity_state(tablet_event) + == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN); + if (!data_.in_proximity) { + data_.has_pressure = true; + data_.pressure = 0.0f; + data_.tip_down = false; + } + } + + // Update tip state + if (type == LIBINPUT_EVENT_TABLET_TOOL_TIP) { + data_.tip_down = (libinput_event_tablet_tool_get_tip_state(tablet_event) + == LIBINPUT_TABLET_TOOL_TIP_DOWN); + if (!data_.tip_down) { + data_.has_pressure = true; + data_.pressure = 0.0f; + } + } + break; + } + default: + break; + } + + libinput_event_destroy(event); + } +} + +void TabletInput::terminate() +{ + if (li_) { + libinput_unref(li_); + li_ = nullptr; + } + if (udev_) { + udev_unref(udev_); + udev_ = nullptr; + } + active_ = false; +} + +#else // !HAVE_LIBINPUT + +// Stub implementation when libinput is not available + +TabletInput::TabletInput() + : data_({0.0f, false, 0.0f, 0.0f, false, false}) + , active_(false) +{ +} + +TabletInput::~TabletInput() +{ +} + +bool TabletInput::init() +{ + Log::Info("TabletInput: libinput not available - tablet pressure support disabled"); + active_ = false; + return false; +} + +void TabletInput::pollEvents() +{ + // No-op when libinput is not available +} + +void TabletInput::terminate() +{ + active_ = false; +} + +#endif // HAVE_LIBINPUT + +#endif // LINUX diff --git a/src/TabletInput_x11.cpp b/src/TabletInput_x11.cpp new file mode 100644 index 0000000..4150831 --- /dev/null +++ b/src/TabletInput_x11.cpp @@ -0,0 +1,249 @@ +/* + * This file is part of vimix - video live mixer + * + * **Copyright** (C) 2019-2023 Bruno Herbelin + * + * 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 3 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 . +**/ + +#ifdef LINUX + +#include "TabletInput.h" +#include "Log.h" + +#ifdef HAVE_X11TABLETINPUT +#include +#include +#include +#endif + +TabletInput::TabletInput() + : data_({0.0f, false, 0.0f, 0.0f, false, false}) + , active_(false) +#ifdef HAVE_X11TABLETINPUT + , display_(nullptr) + , xi_opcode_(-1) + , pressure_valuator_(-1) + , tilt_x_valuator_(-1) + , tilt_y_valuator_(-1) +#endif +{ +} + +TabletInput::~TabletInput() +{ + terminate(); +} + +bool TabletInput::init() +{ +#ifdef HAVE_X11TABLETINPUT + // Open X11 display connection + display_ = XOpenDisplay(nullptr); + if (!display_) { + Log::Info("TabletInput: Failed to open X11 display"); + return false; + } + + // Check for XInput2 extension + int event, error; + if (!XQueryExtension(display_, "XInputExtension", &xi_opcode_, &event, &error)) { + Log::Info("TabletInput: XInput extension not available"); + XCloseDisplay(display_); + display_ = nullptr; + return false; + } + + // Check XInput2 version + int major = 2, minor = 2; + if (XIQueryVersion(display_, &major, &minor) != Success) { + Log::Info("TabletInput: XInput2 2.2 not available"); + XCloseDisplay(display_); + display_ = nullptr; + return false; + } + + // Select events for all devices + XIEventMask eventmask; + unsigned char mask[XIMaskLen(XI_LASTEVENT)] = {0}; + + XISetMask(mask, XI_Motion); + XISetMask(mask, XI_ButtonPress); + XISetMask(mask, XI_ButtonRelease); + + eventmask.deviceid = XIAllDevices; + eventmask.mask_len = sizeof(mask); + eventmask.mask = mask; + + Window root = DefaultRootWindow(display_); + XISelectEvents(display_, root, &eventmask, 1); + + // Find tablet devices and their pressure valuators + int ndevices; + XIDeviceInfo *devices = XIQueryDevice(display_, XIAllDevices, &ndevices); + + for (int i = 0; i < ndevices; i++) { + XIDeviceInfo *device = &devices[i]; + + // Look for devices that have valuators (tablets, styluses) + if (device->use == XISlavePointer || device->use == XIFloatingSlave) { + // Check valuator classes for pressure + for (int j = 0; j < device->num_classes; j++) { + if (device->classes[j]->type == XIValuatorClass) { + XIValuatorClassInfo *v = (XIValuatorClassInfo*)device->classes[j]; + + // Try to identify pressure axis + // Pressure is usually valuator 2, but we check the label + Atom pressure_atom = XInternAtom(display_, "Abs Pressure", True); + Atom tilt_x_atom = XInternAtom(display_, "Abs Tilt X", True); + Atom tilt_y_atom = XInternAtom(display_, "Abs Tilt Y", True); + + if (v->label == pressure_atom) { + pressure_valuator_ = v->number; + Log::Info("TabletInput: Found pressure valuator %d on device '%s'", + pressure_valuator_, device->name); + } + else if (v->label == tilt_x_atom) { + tilt_x_valuator_ = v->number; + } + else if (v->label == tilt_y_atom) { + tilt_y_valuator_ = v->number; + } + // Fallback: assume valuator 2 is pressure if we haven't found it + else if (pressure_valuator_ == -1 && v->number == 2) { + pressure_valuator_ = v->number; + Log::Info("TabletInput: Using valuator 2 as pressure (fallback)"); + } + } + } + } + } + + XIFreeDeviceInfo(devices); + + if (pressure_valuator_ == -1) { + Log::Info("TabletInput: No pressure valuator found - tablet may not be connected"); + // Don't fail init, just continue without pressure detection + } + else { + data_.has_pressure = true; + } + + XFlush(display_); + active_ = true; + Log::Info("TabletInput: X11/XInput2 tablet input initialized"); + return true; +#else + Log::Info("TabletInput: XInput2 not available - tablet support disabled"); + return false; +#endif +} + +void TabletInput::pollEvents() +{ +#ifdef HAVE_X11TABLETINPUT + if (!display_) return; + + // Process all pending X11 events + while (XPending(display_) > 0) { + XEvent ev; + XNextEvent(display_, &ev); + + // Check for XInput2 events + if (ev.type == GenericEvent && ev.xcookie.extension == xi_opcode_) { + if (XGetEventData(display_, &ev.xcookie)) { + XIDeviceEvent *device_event = (XIDeviceEvent*)ev.xcookie.data; + + switch (ev.xcookie.evtype) { + case XI_Motion: + case XI_ButtonPress: + case XI_ButtonRelease: { + // Extract pressure from valuators + if (pressure_valuator_ >= 0) { + double *values = device_event->valuators.values; + unsigned char *mask = device_event->valuators.mask; + + int val_index = 0; + for (int i = 0; i <= pressure_valuator_; i++) { + if (XIMaskIsSet(mask, i)) { + if (i == pressure_valuator_) { + // Normalize pressure (typically 0-65535 range) + data_.pressure = values[val_index] / 65535.0f; + if (data_.pressure > 1.0f) data_.pressure = 1.0f; + if (data_.pressure < 0.0f) data_.pressure = 0.0f; + } + val_index++; + } + } + } + + // Extract tilt + if (tilt_x_valuator_ >= 0 || tilt_y_valuator_ >= 0) { + double *values = device_event->valuators.values; + unsigned char *mask = device_event->valuators.mask; + + int val_index = 0; + int max_valuator = tilt_x_valuator_ > tilt_y_valuator_ ? + tilt_x_valuator_ : tilt_y_valuator_; + + for (int i = 0; i <= max_valuator; i++) { + if (XIMaskIsSet(mask, i)) { + if (i == tilt_x_valuator_) { + data_.tilt_x = (values[val_index] - 32767.5f) / 32767.5f; + } + if (i == tilt_y_valuator_) { + data_.tilt_y = (values[val_index] - 32767.5f) / 32767.5f; + } + val_index++; + } + } + } + + // Update button state + if (ev.xcookie.evtype == XI_ButtonPress) { + data_.tip_down = true; + data_.in_proximity = true; + } + else if (ev.xcookie.evtype == XI_ButtonRelease) { + data_.tip_down = false; + if (data_.pressure < 0.01f) { + data_.in_proximity = false; + } + } + else if (ev.xcookie.evtype == XI_Motion) { + data_.in_proximity = true; + } + break; + } + } + + XFreeEventData(display_, &ev.xcookie); + } + } + } +#endif +} + +void TabletInput::terminate() +{ +#ifdef HAVE_X11TABLETINPUT + if (display_) { + XCloseDisplay(display_); + display_ = nullptr; + } +#endif + active_ = false; +} + +#endif // LINUX