diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -185,8 +185,12 @@ PURPOSE "Required for input handling on Wayland." ) set(HAVE_INPUT FALSE) +set(HAVE_INPUT_1_9 FALSE) if (Libinput_FOUND AND UDEV_FOUND) set(HAVE_INPUT TRUE) + if(Libinput_VERSION VERSION_GREATER_EQUAL "1.9.0") + set(HAVE_INPUT_1_9 TRUE) + endif() endif() set(HAVE_UDEV FALSE) if (UDEV_FOUND) diff --git a/autotests/libinput/CMakeLists.txt b/autotests/libinput/CMakeLists.txt --- a/autotests/libinput/CMakeLists.txt +++ b/autotests/libinput/CMakeLists.txt @@ -65,6 +65,22 @@ add_test(NAME kwin-testLibinputGestureEvent COMMAND testLibinputGestureEvent) ecm_mark_as_test(testLibinputGestureEvent) +######################################################## +# Test Switch Event +######################################################## +if(HAVE_INPUT_1_9) + set( testLibinputSwitchEvent_SRCS + switch_event_test.cpp + mock_libinput.cpp + ../../libinput/device.cpp + ../../libinput/events.cpp + ) + add_executable(testLibinputSwitchEvent ${testLibinputSwitchEvent_SRCS}) + target_link_libraries(testLibinputSwitchEvent Qt5::Test Qt5::DBus Qt5::Widgets KF5::ConfigCore) + add_test(NAME kwin-testLibinputSwitchEvent COMMAND testLibinputSwitchEvent) + ecm_mark_as_test(testLibinputSwitchEvent) +endif() + ######################################################## # Test Context ######################################################## diff --git a/autotests/libinput/device_test.cpp b/autotests/libinput/device_test.cpp --- a/autotests/libinput/device_test.cpp +++ b/autotests/libinput/device_test.cpp @@ -159,6 +159,8 @@ void testOrientation_data(); void testOrientation(); void testCalibrationWithDefault(); + void testSwitch_data(); + void testSwitch(); }; void TestLibinputDevice::testStaticGetter() @@ -209,15 +211,19 @@ QTest::addColumn("pointer"); QTest::addColumn("touch"); QTest::addColumn("tabletTool"); + QTest::addColumn("switchDevice"); - QTest::newRow("keyboard") << true << false << false << false; - QTest::newRow("pointer") << false << true << false << false; - QTest::newRow("touch") << false << false << true << false; - QTest::newRow("keyboard/pointer") << true << true << false << false; - QTest::newRow("keyboard/touch") << true << false << true << false; - QTest::newRow("pointer/touch") << false << true << true << false; - QTest::newRow("keyboard/pointer/touch") << true << true << true << false; - QTest::newRow("tabletTool") << false << false << false << true; + QTest::newRow("keyboard") << true << false << false << false << false; + QTest::newRow("pointer") << false << true << false << false << false; + QTest::newRow("touch") << false << false << true << false << false; + QTest::newRow("keyboard/pointer") << true << true << false << false << false; + QTest::newRow("keyboard/touch") << true << false << true << false << false; + QTest::newRow("pointer/touch") << false << true << true << false << false; + QTest::newRow("keyboard/pointer/touch") << true << true << true << false << false; + QTest::newRow("tabletTool") << false << false << false << true << false; +#if HAVE_INPUT_1_9 + QTest::newRow("switch") << false << false << false << false << true; +#endif } void TestLibinputDevice::testDeviceType() @@ -227,12 +233,14 @@ QFETCH(bool, pointer); QFETCH(bool, touch); QFETCH(bool, tabletTool); + QFETCH(bool, switchDevice); libinput_device device; device.keyboard = keyboard; device.pointer = pointer; device.touch = touch; device.tabletTool = tabletTool; + device.switchDevice = switchDevice; Device d(&device); QCOMPARE(d.isKeyboard(), keyboard); @@ -245,6 +253,8 @@ QCOMPARE(d.property("tabletPad").toBool(), false); QCOMPARE(d.isTabletTool(), tabletTool); QCOMPARE(d.property("tabletTool").toBool(), tabletTool); + QCOMPARE(d.isSwitch(), switchDevice); + QCOMPARE(d.property("switchDevice").toBool(), switchDevice); QCOMPARE(d.device(), &device); } @@ -2160,5 +2170,35 @@ QCOMPARE(device.calibrationMatrix[5], 4.0f); } +void TestLibinputDevice::testSwitch_data() +{ + QTest::addColumn("lid"); + QTest::addColumn("tablet"); + + QTest::newRow("lid") << true << false; + QTest::newRow("tablet") << false << true; +} + +void TestLibinputDevice::testSwitch() +{ +#if HAVE_INPUT_1_9 + libinput_device device; + device.switchDevice = true; + QFETCH(bool, lid); + QFETCH(bool, tablet); + device.lidSwitch = lid; + device.tabletModeSwitch = tablet; + + Device d(&device); + QCOMPARE(d.isSwitch(), true); + QCOMPARE(d.isLidSwitch(), lid); + QCOMPARE(d.property("lidSwitch").toBool(), lid); + QCOMPARE(d.isTabletModeSwitch(), tablet); + QCOMPARE(d.property("tabletModeSwitch").toBool(), tablet); +#else + QSKIP("Requires libinput 1.9"); +#endif +} + QTEST_GUILESS_MAIN(TestLibinputDevice) #include "device_test.moc" diff --git a/autotests/libinput/mock_libinput.h b/autotests/libinput/mock_libinput.h --- a/autotests/libinput/mock_libinput.h +++ b/autotests/libinput/mock_libinput.h @@ -34,6 +34,7 @@ bool touch = false; bool tabletTool = false; bool gestureSupported = false; + bool switchDevice = false; QByteArray name; QByteArray sysName = QByteArrayLiteral("event0"); QByteArray outputName; @@ -97,6 +98,9 @@ std::array calibrationMatrix{{1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f}}; bool defaultCalibrationMatrixIsIdentity = true; + + bool lidSwitch = false; + bool tabletModeSwitch = false; }; struct libinput_event { @@ -137,6 +141,15 @@ qreal angleDelta = 0.0; }; +struct libinput_event_switch : libinput_event { + enum class State { + Off, + On + }; + State state = State::Off; + quint64 timeMicroseconds = 0; +}; + struct libinput { int refCount = 1; QByteArray seat; diff --git a/autotests/libinput/mock_libinput.cpp b/autotests/libinput/mock_libinput.cpp --- a/autotests/libinput/mock_libinput.cpp +++ b/autotests/libinput/mock_libinput.cpp @@ -41,6 +41,10 @@ return device->gestureSupported; case LIBINPUT_DEVICE_CAP_TABLET_TOOL: return device->tabletTool; +#if HAVE_INPUT_1_9 + case LIBINPUT_DEVICE_CAP_SWITCH: + return device->switchDevice; +#endif default: return 0; } @@ -810,3 +814,51 @@ { return device->defaultScrollButton; } + +#if HAVE_INPUT_1_9 + +int libinput_device_switch_has_switch(struct libinput_device *device, enum libinput_switch sw) +{ + switch (sw) { + case LIBINPUT_SWITCH_LID: + return device->lidSwitch; + case LIBINPUT_SWITCH_TABLET_MODE: + return device->tabletModeSwitch; + default: + Q_UNREACHABLE(); + } + return 0; +} + +struct libinput_event_switch *libinput_event_get_switch_event(struct libinput_event *event) +{ + if (event->type == LIBINPUT_EVENT_SWITCH_TOGGLE) { + return reinterpret_cast(event); + } else { + return nullptr; + } +} + +enum libinput_switch_state libinput_event_switch_get_switch_state(struct libinput_event_switch *event) +{ + switch (event->state) { + case libinput_event_switch::State::On: + return LIBINPUT_SWITCH_STATE_ON; + case libinput_event_switch::State::Off: + return LIBINPUT_SWITCH_STATE_OFF; + default: + Q_UNREACHABLE(); + } +} + +uint32_t libinput_event_switch_get_time(struct libinput_event_switch *event) +{ + return event->time;; +} + +uint64_t libinput_event_switch_get_time_usec(struct libinput_event_switch *event) +{ + return event->timeMicroseconds; +} + +#endif diff --git a/autotests/libinput/switch_event_test.cpp b/autotests/libinput/switch_event_test.cpp new file mode 100644 --- /dev/null +++ b/autotests/libinput/switch_event_test.cpp @@ -0,0 +1,99 @@ +/******************************************************************** +KWin - the KDE window manager +This file is part of the KDE project. + +Copyright (C) 2017 Martin Flöser + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#include "mock_libinput.h" +#include "../../libinput/device.h" +#include "../../libinput/events.h" + +#include + +#include + +Q_DECLARE_METATYPE(KWin::LibInput::SwitchEvent::State) + +using namespace KWin::LibInput; + +class TestLibinputSwitchEvent : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void init(); + void cleanup(); + + void testToggled_data(); + void testToggled(); + +private: + std::unique_ptr m_nativeDevice; + std::unique_ptr m_device; +}; + +void TestLibinputSwitchEvent::init() +{ + m_nativeDevice = std::make_unique(); + m_nativeDevice->switchDevice = true; + m_device = std::make_unique(m_nativeDevice.get()); +} + +void TestLibinputSwitchEvent::cleanup() +{ + m_device.reset(); + m_nativeDevice.reset(); +} + +void TestLibinputSwitchEvent::testToggled_data() +{ + QTest::addColumn("state"); + + QTest::newRow("on") << KWin::LibInput::SwitchEvent::State::On; + QTest::newRow("off") << KWin::LibInput::SwitchEvent::State::Off; +} + +void TestLibinputSwitchEvent::testToggled() +{ + libinput_event_switch *nativeEvent = new libinput_event_switch; + nativeEvent->type = LIBINPUT_EVENT_SWITCH_TOGGLE; + nativeEvent->device = m_nativeDevice.get(); + QFETCH(KWin::LibInput::SwitchEvent::State, state); + switch (state) { + case SwitchEvent::State::Off: + nativeEvent->state = libinput_event_switch::State::Off; + break; + case SwitchEvent::State::On: + nativeEvent->state = libinput_event_switch::State::On; + break; + default: + Q_UNREACHABLE(); + } + nativeEvent->time = 23; + nativeEvent->timeMicroseconds = 23456789; + + QScopedPointer event(Event::create(nativeEvent)); + auto se = dynamic_cast(event.data()); + QVERIFY(se); + QCOMPARE(se->device(), m_device.get()); + QCOMPARE(se->nativeDevice(), m_nativeDevice.get()); + QCOMPARE(se->type(), LIBINPUT_EVENT_SWITCH_TOGGLE); + QCOMPARE(se->state(), state); + QCOMPARE(se->time(), 23u); + QCOMPARE(se->timeMicroseconds(), 23456789u); +} + +QTEST_GUILESS_MAIN(TestLibinputSwitchEvent) +#include "switch_event_test.moc" diff --git a/config-kwin.h.cmake b/config-kwin.h.cmake --- a/config-kwin.h.cmake +++ b/config-kwin.h.cmake @@ -10,6 +10,7 @@ #define KWIN_RULES_DIALOG_BIN "${CMAKE_INSTALL_FULL_LIBEXECDIR}/kwin_rules_dialog" #define KWIN_XCLIPBOARD_SYNC_BIN "${CMAKE_INSTALL_FULL_LIBEXECDIR}/org_kde_kwin_xclipboard_syncer" #cmakedefine01 HAVE_INPUT +#cmakedefine01 HAVE_INPUT_1_9 #cmakedefine01 HAVE_X11_XCB #cmakedefine01 HAVE_X11_XINPUT #cmakedefine01 HAVE_DRM diff --git a/libinput/connection.h b/libinput/connection.h --- a/libinput/connection.h +++ b/libinput/connection.h @@ -123,6 +123,8 @@ void pinchGestureUpdate(qreal scale, qreal angleDelta, const QSizeF &delta, quint32 time, KWin::LibInput::Device *device); void pinchGestureEnd(quint32 time, KWin::LibInput::Device *device); void pinchGestureCancelled(quint32 time, KWin::LibInput::Device *device); + void switchToggledOn(quint32 time, quint64 timeMicroseconds, KWin::LibInput::Device *device); + void switchToggledOff(quint32 time, quint64 timeMicroseconds, KWin::LibInput::Device *device); void eventsRead(); diff --git a/libinput/connection.cpp b/libinput/connection.cpp --- a/libinput/connection.cpp +++ b/libinput/connection.cpp @@ -460,6 +460,22 @@ } break; } +#if HAVE_INPUT_1_9 + case LIBINPUT_EVENT_SWITCH_TOGGLE: { + SwitchEvent *se = static_cast(event.data()); + switch (se->state()) { + case SwitchEvent::State::Off: + emit switchToggledOff(se->time(), se->timeMicroseconds(), se->device()); + break; + case SwitchEvent::State::On: + emit switchToggledOn(se->time(), se->timeMicroseconds(), se->device()); + break; + default: + Q_UNREACHABLE(); + } + break; + } +#endif default: // nothing break; diff --git a/libinput/device.h b/libinput/device.h --- a/libinput/device.h +++ b/libinput/device.h @@ -124,6 +124,11 @@ Q_PROPERTY(bool scrollOnButtonDown READ isScrollOnButtonDown WRITE setScrollOnButtonDown NOTIFY scrollMethodChanged) Q_PROPERTY(quint32 scrollButton READ scrollButton WRITE setScrollButton NOTIFY scrollButtonChanged) + // switches + Q_PROPERTY(bool switchDevice READ isSwitch CONSTANT) + Q_PROPERTY(bool lidSwitch READ isLidSwitch CONSTANT) + Q_PROPERTY(bool tabletModeSwitch READ isTabletModeSwitch CONSTANT) + public: explicit Device(libinput_device *device, QObject *parent = nullptr); @@ -414,6 +419,18 @@ **/ void loadConfiguration(); + bool isSwitch() const { + return m_switch; + } + + bool isLidSwitch() const { + return m_lidSwitch; + } + + bool isTabletModeSwitch() const { + return m_tabletSwitch; + } + /** * All created Devices **/ @@ -453,6 +470,9 @@ bool m_tabletTool; bool m_tabletPad; bool m_supportsGesture; + bool m_switch = false; + bool m_lidSwitch = false; + bool m_tabletSwitch = false; QString m_name; QString m_sysName; QString m_outputName; diff --git a/libinput/device.cpp b/libinput/device.cpp --- a/libinput/device.cpp +++ b/libinput/device.cpp @@ -163,6 +163,11 @@ , m_tabletPad(false) #endif , m_supportsGesture(libinput_device_has_capability(m_device, LIBINPUT_DEVICE_CAP_GESTURE)) +#if HAVE_INPUT_1_9 + , m_switch(libinput_device_has_capability(m_device, LIBINPUT_DEVICE_CAP_SWITCH)) + , m_lidSwitch(m_switch ? libinput_device_switch_has_switch(m_device, LIBINPUT_SWITCH_LID) : false) + , m_tabletSwitch(m_switch ? libinput_device_switch_has_switch(m_device, LIBINPUT_SWITCH_TABLET_MODE) : false) +#endif , m_name(QString::fromLocal8Bit(libinput_device_get_name(m_device))) , m_sysName(QString::fromLocal8Bit(libinput_device_get_sysname(m_device))) , m_outputName(QString::fromLocal8Bit(libinput_device_get_output_name(m_device))) diff --git a/libinput/events.h b/libinput/events.h --- a/libinput/events.h +++ b/libinput/events.h @@ -24,6 +24,11 @@ #include +#include +#if !(HAVE_INPUT_1_9) +struct libinput_event_switch; +#endif + namespace KWin { namespace LibInput @@ -172,6 +177,25 @@ virtual ~SwipeGestureEvent(); }; +class SwitchEvent : public Event +{ +public: + SwitchEvent(libinput_event *event, libinput_event_type type); + ~SwitchEvent() override; + + enum class State { + Off, + On + }; + State state() const; + + quint32 time() const; + quint64 timeMicroseconds() const; + +private: + libinput_event_switch *m_switchEvent; +}; + inline libinput_event_type Event::type() const { diff --git a/libinput/events.cpp b/libinput/events.cpp --- a/libinput/events.cpp +++ b/libinput/events.cpp @@ -57,6 +57,10 @@ case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE: case LIBINPUT_EVENT_GESTURE_PINCH_END: return new PinchGestureEvent(event, t); +#if HAVE_INPUT_1_9 + case LIBINPUT_EVENT_SWITCH_TOGGLE: + return new SwitchEvent(event, t); +#endif default: return new Event(event, t); } @@ -284,5 +288,49 @@ SwipeGestureEvent::~SwipeGestureEvent() = default; +SwitchEvent::SwitchEvent(libinput_event *event, libinput_event_type type) + : Event(event, type) +#if HAVE_INPUT_1_9 + , m_switchEvent(libinput_event_get_switch_event(event)) +#else + , m_switchEvent(nullptr) +#endif +{ +} + +SwitchEvent::~SwitchEvent() = default; + +SwitchEvent::State SwitchEvent::state() const +{ +#if HAVE_INPUT_1_9 + switch (libinput_event_switch_get_switch_state(m_switchEvent)) + { + case LIBINPUT_SWITCH_STATE_OFF: + return State::Off; + case LIBINPUT_SWITCH_STATE_ON: + return State::On; + default: + Q_UNREACHABLE(); + } +#endif + return State::Off; +} + +quint32 SwitchEvent::time() const +{ +#if HAVE_INPUT_1_9 + return libinput_event_switch_get_time(m_switchEvent); +#endif + return 0; +} + +quint64 SwitchEvent::timeMicroseconds() const +{ +#if HAVE_INPUT_1_9 + return libinput_event_switch_get_time_usec(m_switchEvent); +#endif + return 0; +} + } }