[libinput] Support toggleTouchpad

Summary:
The LibInput::Device provides a way to enable/disable the device.
This is used by the Connection to toggle all touchpad devices on/off
when the touchpad key is pressed. For this KWin "steals" the global
shortcuts from the touchpad kded.

Detecting what is a touchpad is unfortunately not tivial. The code
uses the following approach:
* it's a pointer
* it's not also a keyboard or touch screen
* it's at least one of the following:
 ** supports multiple tap fingers
 ** supports disable while typing
 ** supports disable on external mouse

If the code finds a touchpad and changes it's state successfully,
it triggers the touchpadEnabledChanged on Plasma's osdService.

Test Plan: Tested on notebook with toggle touchpad button

Reviewers: #plasma

Subscribers: plasma-devel

Projects: #plasma

Differential Revision: https://phabricator.kde.org/D1545
This commit is contained in:
Martin Gräßlin 2016-05-06 12:28:07 +02:00
parent 78c70a0775
commit 5e284224ee
5 changed files with 110 additions and 1 deletions

View file

@ -26,7 +26,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "libinput_logging.h"
#include <KConfigGroup>
#include <KGlobalAccel>
#include <QDBusMessage>
#include <QDBusConnection>
#include <QDBusPendingCall>
#include <QMutexLocker>
#include <QSocketNotifier>
#include <QThread>
@ -83,6 +87,8 @@ Connection *Connection::create(QObject *parent)
return s_self;
}
static const QString s_touchpadComponent = QStringLiteral("kcm_touchpad");
Connection::Connection(Context *input, QObject *parent)
: QObject(parent)
, m_input(input)
@ -90,6 +96,47 @@ Connection::Connection(Context *input, QObject *parent)
, m_mutex(QMutex::Recursive)
{
Q_ASSERT(m_input);
// steal touchpad shortcuts
QAction *touchpadToggleAction = new QAction(this);
QAction *touchpadOnAction = new QAction(this);
QAction *touchpadOffAction = new QAction(this);
touchpadToggleAction->setObjectName(QStringLiteral("Toggle Touchpad"));
touchpadToggleAction->setProperty("componentName", s_touchpadComponent);
touchpadOnAction->setObjectName(QStringLiteral("Enable Touchpad"));
touchpadOnAction->setProperty("componentName", s_touchpadComponent);
touchpadOffAction->setObjectName(QStringLiteral("Disable Touchpad"));
touchpadOffAction->setProperty("componentName", s_touchpadComponent);
KGlobalAccel::self()->setDefaultShortcut(touchpadToggleAction, QList<QKeySequence>{Qt::Key_TouchpadToggle});
KGlobalAccel::self()->setShortcut(touchpadToggleAction, QList<QKeySequence>{Qt::Key_TouchpadToggle});
KGlobalAccel::self()->setDefaultShortcut(touchpadOnAction, QList<QKeySequence>{Qt::Key_TouchpadOn});
KGlobalAccel::self()->setShortcut(touchpadOnAction, QList<QKeySequence>{Qt::Key_TouchpadOn});
KGlobalAccel::self()->setDefaultShortcut(touchpadOffAction, QList<QKeySequence>{Qt::Key_TouchpadOff});
KGlobalAccel::self()->setShortcut(touchpadOffAction, QList<QKeySequence>{Qt::Key_TouchpadOff});
#ifndef KWIN_BUILD_TESTING
InputRedirection::self()->registerShortcut(Qt::Key_TouchpadToggle, touchpadToggleAction);
InputRedirection::self()->registerShortcut(Qt::Key_TouchpadOn, touchpadOnAction);
InputRedirection::self()->registerShortcut(Qt::Key_TouchpadOff, touchpadOffAction);
#endif
connect(touchpadToggleAction, &QAction::triggered, this, &Connection::toggleTouchpads);
connect(touchpadOnAction, &QAction::triggered, this,
[this] {
if (m_touchpadsEnabled) {
return;
}
toggleTouchpads();
}
);
connect(touchpadOffAction, &QAction::triggered, this,
[this] {
if (!m_touchpadsEnabled) {
return;
}
toggleTouchpads();
}
);
// need to connect to KGlobalSettings as the mouse KCM does not emit a dedicated signal
QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/KGlobalSettings"), QStringLiteral("org.kde.KGlobalSettings"),
QStringLiteral("notifyChange"), this, SLOT(slotKGlobalSettingsNotifyChange(int,int)));
@ -371,5 +418,41 @@ void Connection::slotKGlobalSettingsNotifyChange(int type, int arg)
}
}
void Connection::toggleTouchpads()
{
bool changed = false;
m_touchpadsEnabled = !m_touchpadsEnabled;
for (auto it = m_devices.constBegin(); it != m_devices.constEnd(); ++it) {
auto device = *it;
if (!device->isPointer()) {
continue;
}
if (device->isKeyboard() || device->isTouch() || device->isTabletPad() || device->isTabletTool()) {
// ignore all combined devices. E.g. a touchpad on a keyboard we don't want to toggle
// as that would result in the keyboard going off as well
continue;
}
// is this a touch pad? We don't really know, let's do some assumptions
if (device->tapFingerCount() > 0 || device->supportsDisableWhileTyping() || device->supportsDisableEventsOnExternalMouse()) {
const bool old = device->isEnabled();
device->setEnabled(m_touchpadsEnabled);
if (old != device->isEnabled()) {
changed = true;
}
}
}
if (changed) {
// send OSD message
QDBusMessage msg = QDBusMessage::createMethodCall(
QStringLiteral("org.kde.plasmashell"),
QStringLiteral("/org/kde/osdService"),
QStringLiteral("org.kde.osdService"),
QStringLiteral("touchpadEnabledChanged")
);
msg.setArguments({m_touchpadsEnabled});
QDBusConnection::sessionBus().asyncCall(msg);
}
}
}
}

View file

@ -73,6 +73,8 @@ public:
void processEvents();
void toggleTouchpads();
QVector<Device*> devices() const {
return m_devices;
}
@ -118,6 +120,7 @@ private:
bool wasSuspended = false;
QVector<Device*> m_devices;
KSharedConfigPtr m_config;
bool m_touchpadsEnabled = true;
KWIN_SINGLETON(Connection)
static QThread *s_thread;

View file

@ -82,6 +82,7 @@ Device::Device(libinput_device *device, QObject *parent)
, m_supportsDisableEventsOnExternalMouse(libinput_device_config_send_events_get_modes(m_device) & LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE)
, m_leftHanded(m_supportsLeftHanded ? libinput_device_config_left_handed_get(m_device) : false)
, m_pointerAcceleration(libinput_device_config_accel_get_speed(m_device))
, m_enabled(m_supportsDisableEvents ? libinput_device_config_send_events_get_mode(m_device) == LIBINPUT_CONFIG_SEND_EVENTS_ENABLED : true)
{
libinput_device_ref(m_device);
@ -153,5 +154,18 @@ void Device::setPointerAcceleration(qreal acceleration)
}
}
void Device::setEnabled(bool enabled)
{
if (!m_supportsDisableEvents) {
return;
}
if (libinput_device_config_send_events_set_mode(m_device, enabled ? LIBINPUT_CONFIG_SEND_EVENTS_ENABLED : LIBINPUT_CONFIG_SEND_EVENTS_DISABLED) == LIBINPUT_CONFIG_STATUS_SUCCESS) {
if (m_enabled != enabled) {
m_enabled = enabled;
emit enabledChanged();
}
}
}
}
}

View file

@ -57,6 +57,7 @@ class Device : public QObject
Q_PROPERTY(bool supportsDisableEventsOnExternalMouse READ supportsDisableEventsOnExternalMouse CONSTANT)
Q_PROPERTY(bool leftHanded READ isLeftHanded WRITE setLeftHanded NOTIFY leftHandedChanged)
Q_PROPERTY(qreal pointerAcceleration READ pointerAcceleration WRITE setPointerAcceleration NOTIFY pointerAccelerationChanged)
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged)
public:
explicit Device(libinput_device *device, QObject *parent = nullptr);
virtual ~Device();
@ -145,6 +146,11 @@ public:
**/
void setPointerAcceleration(qreal acceleration);
bool isEnabled() const {
return m_enabled;
}
void setEnabled(bool enabled);
libinput_device *device() const {
return m_device;
}
@ -152,6 +158,7 @@ public:
Q_SIGNALS:
void leftHandedChanged();
void pointerAccelerationChanged();
void enabledChanged();
private:
libinput_device *m_device;
@ -179,6 +186,7 @@ private:
bool m_supportsDisableEventsOnExternalMouse;
bool m_leftHanded;
qreal m_pointerAcceleration;
bool m_enabled;
};
}

View file

@ -30,5 +30,6 @@ if (HAVE_INPUT)
${KWIN_SOURCE_DIR}/udev.cpp
)
add_executable(libinputtest ${libinputtest_SRCS})
target_link_libraries(libinputtest Qt5::Core Qt5::DBus Libinput::Libinput ${UDEV_LIBS} KF5::ConfigCore KF5::WindowSystem)
add_definitions(-DKWIN_BUILD_TESTING)
target_link_libraries(libinputtest Qt5::Core Qt5::DBus Libinput::Libinput ${UDEV_LIBS} KF5::ConfigCore KF5::GlobalAccel KF5::WindowSystem)
endif()