From 40200565323f16e0f30ab80329fb277ca0854edf Mon Sep 17 00:00:00 2001 From: Aleix Pol Date: Fri, 22 Mar 2019 18:29:15 +0100 Subject: [PATCH] Make it possible to autodetect the tablet mode Summary: At the moment it's broken for many of the laptops I've seen, this opens the possibility to start toying with them. The right fix would be to get the kernel to emit the right signals but I'm afraid for some hardware it will be close to impossible. This includes a kconfig variable variable that will detect it as follows: if there's a touchscreen, when the touchpad gets removed we are on tablet mode (i.e. the pointing will be done using the finger), when it's connected back tablet mode will be restored. Reviewers: #kwin, #plasma, davidedmundson Reviewed By: #kwin, #plasma, davidedmundson Subscribers: mart, davidedmundson, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D19604 --- libinput/connection.h | 2 +- libinput/device.h | 3 +- tabletmodemanager.cpp | 117 +++++++++++++++++++++++++++++++++--------- tabletmodemanager.h | 7 +-- 4 files changed, 99 insertions(+), 30 deletions(-) diff --git a/libinput/connection.h b/libinput/connection.h index 44495ed190..b45beecaef 100644 --- a/libinput/connection.h +++ b/libinput/connection.h @@ -43,7 +43,7 @@ class Event; class Device; class Context; -class Connection : public QObject +class KWIN_EXPORT Connection : public QObject { Q_OBJECT diff --git a/libinput/device.h b/libinput/device.h index e325a29437..0c827ad2e8 100644 --- a/libinput/device.h +++ b/libinput/device.h @@ -28,6 +28,7 @@ along with this program. If not, see . #include #include #include +#include "kwin_export.h" struct libinput_device; @@ -37,7 +38,7 @@ namespace LibInput { enum class ConfigKey; -class Device : public QObject +class KWIN_EXPORT Device : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.KWin.InputDevice") diff --git a/tabletmodemanager.cpp b/tabletmodemanager.cpp index 9776706093..09d7d9ed3a 100644 --- a/tabletmodemanager.cpp +++ b/tabletmodemanager.cpp @@ -25,53 +25,92 @@ #include "input_event_spy.h" #include "libinput/device.h" +#include "libinput/connection.h" +#include #include using namespace KWin; KWIN_SINGLETON_FACTORY_VARIABLE(TabletModeManager, s_manager) -class KWin::TabletModeInputEventSpy : public InputEventSpy +class TabletModeSwitchEventSpy : public QObject, public InputEventSpy { public: - explicit TabletModeInputEventSpy(TabletModeManager *parent); + explicit TabletModeSwitchEventSpy(TabletModeManager *parent) + : QObject(parent) + , m_parent(parent) + { + } + + void switchEvent(SwitchEvent *event) override + { + if (!event->device()->isTabletModeSwitch()) { + return; + } + + switch (event->state()) { + case SwitchEvent::State::Off: + m_parent->setIsTablet(false); + break; + case SwitchEvent::State::On: + m_parent->setIsTablet(true); + break; + default: + Q_UNREACHABLE(); + } + } - void switchEvent(SwitchEvent *event) override; private: - TabletModeManager *m_parent; + TabletModeManager * const m_parent; }; -TabletModeInputEventSpy::TabletModeInputEventSpy(TabletModeManager *parent) - : m_parent(parent) -{ -} -void TabletModeInputEventSpy::switchEvent(SwitchEvent *event) +class TabletModeTouchpadRemovedSpy : public QObject { - if (!event->device()->isTabletModeSwitch()) { - return; +public: + explicit TabletModeTouchpadRemovedSpy(TabletModeManager *parent) + : QObject(parent) + , m_parent(parent) + { + auto c = LibInput::Connection::self(); + connect(c, &LibInput::Connection::deviceAdded, this, &TabletModeTouchpadRemovedSpy::refresh); + connect(c, &LibInput::Connection::deviceRemoved, this, &TabletModeTouchpadRemovedSpy::refresh); + + check(); } - switch (event->state()) { - case SwitchEvent::State::Off: - m_parent->setIsTablet(false); - break; - case SwitchEvent::State::On: - m_parent->setIsTablet(true); - break; - default: - Q_UNREACHABLE(); + void refresh(LibInput::Device* d) { + if (!d->isTouch() && !d->isPointer()) + return; + check(); } -} + void check() { + if (!LibInput::Connection::self()) { + qDebug() << "no libinput :("; + return; + } + const auto devices = LibInput::Connection::self()->devices(); + const bool hasTouch = std::any_of(devices.constBegin(), devices.constEnd(), [](LibInput::Device* device){ return device->isTouch(); }); + m_parent->setTabletModeAvailable(hasTouch); + const bool hasPointer = std::any_of(devices.constBegin(), devices.constEnd(), [](LibInput::Device* device){ return device->isPointer(); }); + m_parent->setIsTablet(hasTouch && !hasPointer); + } + +private: + TabletModeManager * const m_parent; +}; TabletModeManager::TabletModeManager(QObject *parent) - : QObject(parent), - m_spy(new TabletModeInputEventSpy(this)) + : QObject(parent) { - input()->installInputEventSpy(m_spy); + if (input()->hasTabletModeSwitch()) { + input()->installInputEventSpy(new TabletModeSwitchEventSpy(this)); + } else { + hasTabletModeInputChanged(false); + } QDBusConnection::sessionBus().registerObject(QStringLiteral("/org/kde/KWin"), QStringLiteral("org.kde.KWin.TabletModeManager"), @@ -82,9 +121,29 @@ TabletModeManager::TabletModeManager(QObject *parent) connect(input(), &InputRedirection::hasTabletModeSwitchChanged, this, &TabletModeManager::tabletModeAvailableChanged); } +void KWin::TabletModeManager::hasTabletModeInputChanged(bool set) +{ + if (set) { + input()->installInputEventSpy(new TabletModeSwitchEventSpy(this)); + setTabletModeAvailable(true); + } else { + auto setupDetector = [this] { + auto spy = new TabletModeTouchpadRemovedSpy(this); + connect(input(), &InputRedirection::hasTabletModeSwitchChanged, spy, [spy](bool set){ + if (set) + spy->deleteLater(); + }); + }; + if (LibInput::Connection::self()) + setupDetector(); + else + QTimer::singleShot(2000, this, setupDetector); + } +} + bool TabletModeManager::isTabletModeAvailable() const { - return input()->hasTabletModeSwitch(); + return m_detecting; } bool TabletModeManager::isTablet() const @@ -101,3 +160,11 @@ void TabletModeManager::setIsTablet(bool tablet) m_isTabletMode = tablet; emit tabletModeChanged(tablet); } + +void KWin::TabletModeManager::setTabletModeAvailable(bool detecting) +{ + if (m_detecting != detecting) { + m_detecting = detecting; + tabletModeAvailableChanged(detecting); + } +} diff --git a/tabletmodemanager.h b/tabletmodemanager.h index 83ee27cfc4..add1076cb6 100644 --- a/tabletmodemanager.h +++ b/tabletmodemanager.h @@ -27,8 +27,6 @@ namespace KWin { -class TabletModeInputEventSpy; - class TabletModeManager : public QObject { Q_OBJECT @@ -40,6 +38,7 @@ class TabletModeManager : public QObject public: ~TabletModeManager() = default; + void setTabletModeAvailable(bool detecting); bool isTabletModeAvailable() const; bool isTablet() const; @@ -50,9 +49,11 @@ Q_SIGNALS: void tabletModeChanged(bool tabletMode); private: + void hasTabletModeInputChanged(bool set); + bool m_tabletModeAvailable = false; bool m_isTabletMode = false; - TabletModeInputEventSpy *m_spy; + bool m_detecting = false; KWIN_SINGLETON_VARIABLE(TabletModeManager, s_manager) }; }