Commit 40200565 authored by Aleix Pol Gonzalez's avatar Aleix Pol Gonzalez 🐧

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
parent e7d3099d
......@@ -43,7 +43,7 @@ class Event;
class Device;
class Context;
class Connection : public QObject
class KWIN_EXPORT Connection : public QObject
{
Q_OBJECT
......
......@@ -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")
......
......@@ -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);
}
}
......@@ -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)
};
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment