diff --git a/native-filter/devicemonitoreventfilter.cpp b/native-filter/devicemonitoreventfilter.cpp new file mode 100644 index 0000000..e96f11f --- /dev/null +++ b/native-filter/devicemonitoreventfilter.cpp @@ -0,0 +1,158 @@ +#include "devicemonitoreventfilter.h" +#include "geeventmemmover.h" + +#include + +#include +#include +#include + +// some lib of x11 need include after qt, because of some definitions +#include +#include +#include + +struct FreeDeleter { + void operator()(void* p) const Q_DECL_NOTHROW { return std::free(p); } +}; +#define XCB_REPLY(call, ...) \ + std::unique_ptr( \ + call##_reply(XCB_REPLY_CONNECTION_ARG(__VA_ARGS__), call(__VA_ARGS__), nullptr) \ + ) +#define XCB_REPLY_CONNECTION_ARG(connection, ...) connection + +namespace { +bool existTouchDevice(xcb_input_touch_mode_t type); +bool findTouchFromReply(xcb_input_xi_query_device_reply_t* queryReply , xcb_input_touch_mode_t type); +bool findTouchModeFromDevice(xcb_input_xi_device_info_t* deviceInfo, xcb_input_touch_mode_t type); +} + +DeviceMonitorEventFilter::DeviceMonitorEventFilter(QObject* parent) + : QObject(parent), QAbstractNativeEventFilter(), m_timer(new QTimer(this)) +{ + m_timer->setSingleShot(true); +} + +bool DeviceMonitorEventFilter::nativeEventFilter(const QByteArray &eventType, + void* message, long* result) +{ + //! \todo support wayland + Q_UNUSED(result) + if (eventType != "xcb_generic_event_t") { + qWarning() << __FILE__ << __FUNCTION__ << __LINE__ + << "Event type is:" << eventType + << "But currently only supported xcb_generic_event_t"; + return false; + } + return handleXcbGeEvent(message); +} + +bool DeviceMonitorEventFilter::handleXcbGeEvent(void* message) +{ + xcb_generic_event_t* event = static_cast(message); + if (!event) { + qWarning() << __FILE__ << __FUNCTION__ << __LINE__ << "Message is empty"; + return false; + } + + const uint8_t type = event->response_type & ~0x80; + if (type != XCB_GE_GENERIC) { + return false; + } + + GeEventMemMover ge(event); + switch (ge->event_type) { + case XI_HierarchyChanged: { + auto* he = reinterpret_cast(event); + if ((he->flags & (XCB_INPUT_HIERARCHY_MASK_SLAVE_REMOVED + | XCB_INPUT_HIERARCHY_MASK_SLAVE_ADDED))) { + // XI_HierarchyChanged events are sent continuously as the device changes + if (m_timer->isActive()) + break; + m_timer->start(20); + + setupTouchDevices(); + } + break; + } + default: + break; + } + + return false; +} + +void DeviceMonitorEventFilter::setupTouchDevices() +{ + bool existTouchScreen = existTouchDevice(XCB_INPUT_TOUCH_MODE_DIRECT); + qDebug() << __FILE__ << __FUNCTION__ << __LINE__ + << "Touch Screen exist state change to:" << existTouchScreen; + Q_EMIT touchScreenChanged(existTouchScreen); +} + +namespace { +bool findTouchModeFromDevice(xcb_input_xi_device_info_t* deviceInfo, xcb_input_touch_mode_t type) +{ + // iterate over all classes of device + auto classes_it = xcb_input_xi_device_info_classes_iterator(deviceInfo); + for (; classes_it.rem > 0; xcb_input_device_class_next(&classes_it)) { + xcb_input_device_class_t* classinfo = classes_it.data; + if (XCB_INPUT_DEVICE_CLASS_TYPE_TOUCH != classinfo->type) + continue; + + auto* touchClassInfo = reinterpret_cast(classinfo); + if (type == touchClassInfo->mode) + return true; + } + return false; +} + +bool findTouchFromReply(xcb_input_xi_query_device_reply_t* queryReply , xcb_input_touch_mode_t type) +{ + // iterate over all devices + auto it = xcb_input_xi_query_device_infos_iterator(queryReply); + for (; it.rem > 0; xcb_input_xi_device_info_next(&it)) { + xcb_input_xi_device_info_t* deviceInfo = it.data; + + // touch device is slave pointer device + if (deviceInfo->type != XCB_INPUT_DEVICE_TYPE_SLAVE_POINTER) + continue; + + if (findTouchModeFromDevice(deviceInfo, type)) + return true; + } + return false; +} + +bool existTouchDevice(xcb_input_touch_mode_t type) +{ + // connect to xserver + int primaryScreen = 0; + xcb_connection_t* conn = xcb_connect(nullptr, &primaryScreen); + if (!conn) { + qWarning() << __FILE__ << __FUNCTION__ << __LINE__ + << "The X11 connection broke. Did the X11 server die?"; + return false; + } + int error = xcb_connection_has_error(conn); + if (error) { + qWarning() << __FILE__ << __FUNCTION__ << __LINE__ + << "The X11 connection broke. Did the X11 server die?" + << "Error:" << error; + xcb_disconnect(conn); + return false; + } + + // query all device + auto reply = XCB_REPLY(xcb_input_xi_query_device, conn, XCB_INPUT_DEVICE_ALL); + if (!reply) { + qWarning() << __FILE__ << __FUNCTION__ << __LINE__ + << "Failed to query device"; + xcb_disconnect(conn); + return false; + } + xcb_disconnect(conn); + + return findTouchFromReply(reply.get(), type); +} +} // namespace diff --git a/native-filter/devicemonitoreventfilter.h b/native-filter/devicemonitoreventfilter.h new file mode 100644 index 0000000..0edb7fc --- /dev/null +++ b/native-filter/devicemonitoreventfilter.h @@ -0,0 +1,29 @@ +#ifndef DEVICEMONITOREVENTFILTER_H +#define DEVICEMONITOREVENTFILTER_H + +#include +#include + +class QTimer; +// If you are using multiple inheritance, moc assumes that the first inherited class is +// a subclass of QObject. Also, be sure that only the first inherited class is a QObject. +class DeviceMonitorEventFilter : public QObject, public QAbstractNativeEventFilter +{ + Q_OBJECT +public: + explicit DeviceMonitorEventFilter(QObject* parent = nullptr); + ~DeviceMonitorEventFilter() override = default; + + bool nativeEventFilter(const QByteArray &eventType, + void* message, long int* result) override; +Q_SIGNALS: + void touchScreenChanged(bool exist); +private: + void setupTouchDevices(); + bool handleXcbGeEvent(void* message); + + // Filter recurring events for short periods of time(20ms) + QTimer* m_timer = nullptr; +}; + +#endif // DEVICEMONITOREVENTFILTER_H diff --git a/native-filter/geeventmemmover.h b/native-filter/geeventmemmover.h new file mode 100644 index 0000000..c1fb32d --- /dev/null +++ b/native-filter/geeventmemmover.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +#include + +#include + +class GeEventMemMover +{ +public: + GeEventMemMover(xcb_generic_event_t *event) + : m_event(reinterpret_cast(event)) + { + // xcb event structs contain stuff that wasn't on the wire, the full_sequence field + // adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes. + // Move this data back to have the same layout in memory as it was on the wire + // and allow casting, overwriting the full_sequence field. + memmove((char*) m_event + 32, (char*) m_event + 36, m_event->length * 4); + } + ~GeEventMemMover() + { + // move memory layout back, so that Qt can do the same without breaking + memmove((char*) m_event + 36, (char *) m_event + 32, m_event->length * 4); + } + + xcb_ge_generic_event_t *operator->() const { + return m_event; + } + +private: + xcb_ge_generic_event_t *m_event; +}; diff --git a/native-filter/native-filter.pri b/native-filter/native-filter.pri new file mode 100644 index 0000000..8981551 --- /dev/null +++ b/native-filter/native-filter.pri @@ -0,0 +1,10 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/devicemonitoreventfilter.h \ + $$PWD/geeventmemmover.h + +SOURCES += \ + $$PWD/devicemonitoreventfilter.cpp + +LIBS += -lxcb-xinput -lxcb diff --git a/touchscreen-settings/touchscreen-settings.cpp b/touchscreen-settings/touchscreen-settings.cpp index 9213eee..119850c 100644 --- a/touchscreen-settings/touchscreen-settings.cpp +++ b/touchscreen-settings/touchscreen-settings.cpp @@ -1,8 +1,12 @@ #include "touchscreen-settings.h" #include "touchscreen.h" #include "gestureguidance.h" +#include "devicemonitoreventfilter.h" #include + +#include + #include #include #include @@ -10,11 +14,15 @@ #include #include #include - #include +#include +#include static const std::string PROJECT_V10SP1 = "V10SP1"; static const std::string PROJECT_INTEL = "V10SP1-edu"; +static const std::string PLUGINS_SCHEMA = "org.ukui.control-center.plugins"; +static const std::string PLUGINS_DIR = "/org/ukui/control-center/plugins/"; +static const std::string KEY_SHOW = "show"; TouchscreenSettings::TouchscreenSettings() : mFirstLoad(true) { @@ -24,6 +32,11 @@ TouchscreenSettings::TouchscreenSettings() : mFirstLoad(true) prjCodeName = QString::fromStdString(KDKGetPrjCodeName()); productFeatures = QString::fromStdString(KDKGetOSRelease("PRODUCT_FEATURES")); + + m_deviceMonitor = new DeviceMonitorEventFilter(this); + qApp->installNativeEventFilter(m_deviceMonitor); + connect(m_deviceMonitor, &DeviceMonitorEventFilter::touchScreenChanged, + this, &TouchscreenSettings::setShow); } TouchscreenSettings::~TouchscreenSettings() @@ -112,3 +125,25 @@ QString TouchscreenSettings::translationPath() const void TouchscreenSettings::initSearchText() { } + +void TouchscreenSettings::setShow(bool isShow) +{ + const QByteArray id(PLUGINS_SCHEMA.c_str()); + if (!QGSettings::isSchemaInstalled(id)) { + qWarning() << __FILE__ << __FUNCTION__ << __LINE__ + << "schema :" << id << "is not install"; + return; + } + + QString path(QString::fromStdString(PLUGINS_DIR + "TouchScreen/")); + QGSettings settings(id, path.toUtf8()); + + // It is signaled whenever the device list changes, + // so it is possible that the touchscreen state has not changed + if (settings.get(KEY_SHOW.c_str()).toBool() == isShow) + return; + + settings.set(KEY_SHOW.c_str(), isShow); + qDebug() << __FILE__ << __FUNCTION__ << __LINE__ + << "Set GSetting" << id << path << KEY_SHOW.c_str() << isShow; +} diff --git a/touchscreen-settings/touchscreen-settings.h b/touchscreen-settings/touchscreen-settings.h index 710a681..e31361a 100644 --- a/touchscreen-settings/touchscreen-settings.h +++ b/touchscreen-settings/touchscreen-settings.h @@ -8,6 +8,7 @@ class QWidget; class QString; class QIcon; +class DeviceMonitorEventFilter; class TouchscreenSettings : public QObject, CommonInterface { Q_OBJECT @@ -30,11 +31,15 @@ public: private: QString prjCodeName; QString productFeatures; - QWidget *pluginWidget; + QWidget *pluginWidget = nullptr; bool mFirstLoad; + DeviceMonitorEventFilter* m_deviceMonitor = nullptr; private: void initSearchText(); // 搜索翻译 + +private Q_SLOTS: + void setShow(bool isShow); // 显示/隐藏插件 }; #endif // TOUCHSCREENSETTINGS_H diff --git a/touchscreen-settings/touchscreen-settings.pro b/touchscreen-settings/touchscreen-settings.pro index 80f7e2e..caacf8e 100644 --- a/touchscreen-settings/touchscreen-settings.pro +++ b/touchscreen-settings/touchscreen-settings.pro @@ -32,6 +32,7 @@ LIBS += $$[QT_INSTALL_LIBS]/libukui-com4cxx.so include(../gesture-widget/gesture-widget.pri) include(item-widget/item-widget.pri) include(flow-layout/flow-layout.pri) +include(../native-filter/native-filter.pri) #include(video-widget/video-widget.pri) SOURCES += \