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
This commit is contained in:
Aleix Pol 2019-03-22 18:29:15 +01:00
parent e7d3099ddd
commit 4020056532
4 changed files with 99 additions and 30 deletions

View file

@ -43,7 +43,7 @@ class Event;
class Device;
class Context;
class Connection : public QObject
class KWIN_EXPORT Connection : public QObject
{
Q_OBJECT

View file

@ -28,6 +28,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QMatrix4x4>
#include <QSizeF>
#include <QVector>
#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")

View file

@ -25,53 +25,92 @@
#include "input_event_spy.h"
#include "libinput/device.h"
#include "libinput/connection.h"
#include <QTimer>
#include <QDBusConnection>
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);
}
}

View file

@ -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)
};
}