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 @@ -155,6 +155,9 @@ void testLoadLmrTapButtonMap(); void testLoadLeftHanded_data(); void testLoadLeftHanded(); + void testScreenId(); + void testOrientation_data(); + void testOrientation(); }; void TestLibinputDevice::testStaticGetter() @@ -2095,5 +2098,47 @@ } } +void TestLibinputDevice::testScreenId() +{ + libinput_device device; + Device d(&device); + QCOMPARE(d.screenId(), 0); + d.setScreenId(1); + QCOMPARE(d.screenId(), 1); +} + +void TestLibinputDevice::testOrientation_data() +{ + QTest::addColumn("orientation"); + QTest::addColumn("m11"); + QTest::addColumn("m12"); + QTest::addColumn("m13"); + QTest::addColumn("m21"); + QTest::addColumn("m22"); + QTest::addColumn("m23"); + + QTest::newRow("Primary") << Qt::PrimaryOrientation << 1.0f << 2.0f << 3.0f << 4.0f << 5.0f << 6.0f; + QTest::newRow("Landscape") << Qt::LandscapeOrientation << 1.0f << 2.0f << 3.0f << 4.0f << 5.0f << 6.0f; + QTest::newRow("Portrait") << Qt::PortraitOrientation << 0.0f << -1.0f << 1.0f << 1.0f << 0.0f << 0.0f; + QTest::newRow("InvertedLandscape") << Qt::InvertedLandscapeOrientation << -1.0f << 0.0f << 1.0f << 0.0f << -1.0f << 1.0f; + QTest::newRow("InvertedPortrait") << Qt::InvertedPortraitOrientation << 0.0f << 1.0f << 0.0f << -1.0f << 0.0f << 1.0f; +} + +void TestLibinputDevice::testOrientation() +{ + libinput_device device; + device.supportsCalibrationMatrix = true; + device.defaultCalibrationMatrix = std::array{{1.0, 2.0, 3.0, 4.0, 5.0, 6.0}}; + Device d(&device); + QFETCH(Qt::ScreenOrientation, orientation); + d.setOrientation(orientation); + QTEST(device.calibrationMatrix[0], "m11"); + QTEST(device.calibrationMatrix[1], "m12"); + QTEST(device.calibrationMatrix[2], "m13"); + QTEST(device.calibrationMatrix[3], "m21"); + QTEST(device.calibrationMatrix[4], "m22"); + QTEST(device.calibrationMatrix[5], "m23"); +} + 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 @@ -26,6 +26,8 @@ #include #include +#include + struct libinput_device { bool keyboard = false; bool pointer = false; @@ -90,6 +92,10 @@ enum libinput_config_accel_profile defaultPointerAccelerationProfile = LIBINPUT_CONFIG_ACCEL_PROFILE_NONE; enum libinput_config_accel_profile pointerAccelerationProfile = LIBINPUT_CONFIG_ACCEL_PROFILE_NONE; bool setPointerAccelerationProfileReturnValue = 0; + std::array defaultCalibrationMatrix{{1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f}}; + std::array calibrationMatrix{{1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f}}; }; struct libinput_event { 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 @@ -194,6 +194,22 @@ return device->supportsCalibrationMatrix; } +enum libinput_config_status libinput_device_config_calibration_set_matrix(struct libinput_device *device, const float matrix[6]) +{ + for (std::size_t i = 0; i < 6; i++) { + device->calibrationMatrix[i] = matrix[i]; + } + return LIBINPUT_CONFIG_STATUS_SUCCESS; +} + +int libinput_device_config_calibration_get_default_matrix(struct libinput_device *device, float matrix[6]) +{ + for (std::size_t i = 0; i < 6; i++) { + matrix[i] = device->defaultCalibrationMatrix[i]; + } + return 1; +} + int libinput_device_config_left_handed_is_available(struct libinput_device *device) { return device->supportsLeftHanded; diff --git a/input.cpp b/input.cpp --- a/input.cpp +++ b/input.cpp @@ -1820,11 +1820,13 @@ return; } m_libInput->setScreenSize(screens()->size()); + m_libInput->updateScreens(); connect(screens(), &Screens::sizeChanged, this, [this] { m_libInput->setScreenSize(screens()->size()); } ); + connect(screens(), &Screens::changed, m_libInput, &LibInput::Connection::updateScreens); #endif } diff --git a/libinput/connection.h b/libinput/connection.h --- a/libinput/connection.h +++ b/libinput/connection.h @@ -61,6 +61,8 @@ **/ void setScreenSize(const QSize &size); + void updateScreens(); + bool hasKeyboard() const { return m_keyboard > 0; } @@ -132,6 +134,7 @@ Connection(Context *input, QObject *parent = nullptr); void handleEvent(); void applyDeviceConfig(Device *device); + void applyScreenToDevice(Device *device); Context *m_input; QSocketNotifier *m_notifier; QSize m_size; diff --git a/libinput/connection.cpp b/libinput/connection.cpp --- a/libinput/connection.cpp +++ b/libinput/connection.cpp @@ -21,6 +21,9 @@ #include "context.h" #include "device.h" #include "events.h" +#ifndef KWIN_BUILD_TESTING +#include "../screens.h" +#endif #include "../logind.h" #include "../udev.h" #include "libinput_logging.h" @@ -277,6 +280,7 @@ } } applyDeviceConfig(device); + applyScreenToDevice(device); // enable possible leds libinput_device_led_update(device->device(), static_cast(toLibinputLEDS(m_leds))); @@ -389,19 +393,25 @@ break; } case LIBINPUT_EVENT_TOUCH_DOWN: { +#ifndef KWIN_BUILD_TESTING TouchEvent *te = static_cast(event.data()); - emit touchDown(te->id(), te->absolutePos(m_size), te->time(), te->device()); + const auto &geo = screens()->geometry(te->device()->screenId()); + emit touchDown(te->id(), geo.topLeft() + te->absolutePos(geo.size()), te->time(), te->device()); break; +#endif } case LIBINPUT_EVENT_TOUCH_UP: { TouchEvent *te = static_cast(event.data()); emit touchUp(te->id(), te->time(), te->device()); break; } case LIBINPUT_EVENT_TOUCH_MOTION: { +#ifndef KWIN_BUILD_TESTING TouchEvent *te = static_cast(event.data()); - emit touchMotion(te->id(), te->absolutePos(m_size), te->time(), te->device()); + const auto &geo = screens()->geometry(te->device()->screenId()); + emit touchMotion(te->id(), geo.topLeft() + te->absolutePos(geo.size()), te->time(), te->device()); break; +#endif } case LIBINPUT_EVENT_TOUCH_CANCEL: { emit touchCanceled(event->device()); @@ -476,6 +486,79 @@ m_size = size; } +void Connection::updateScreens() +{ + QMutexLocker locker(&m_mutex); + for (auto device: qAsConst(m_devices)) { + applyScreenToDevice(device); + } +} + + +void Connection::applyScreenToDevice(Device *device) +{ +#ifndef KWIN_BUILD_TESTING + QMutexLocker locker(&m_mutex); + if (!device->isTouch()) { + return; + } + int id = -1; + // let's try to find a screen for it + if (screens()->count() == 1) { + id = 0; + } + if (id == -1 && !device->outputName().isEmpty()) { + // we have an output name, try to find a screen with matching name + for (int i = 0; i < screens()->count(); i++) { + if (screens()->name(i) == device->outputName()) { + id = i; + break; + } + } + } + if (id == -1) { + // do we have an internal screen? + int internalId = -1; + for (int i = 0; i < screens()->count(); i++) { + if (screens()->isInternal(i)) { + internalId = i; + break; + } + } + auto testScreenMatches = [device] (int id) { + const auto &size = device->size(); + const auto &screenSize = screens()->physicalSize(id); + return std::round(size.width()) == std::round(screenSize.width()) + && std::round(size.height()) == std::round(screenSize.height()); + }; + if (internalId != -1 && testScreenMatches(internalId)) { + id = internalId; + } + // let's compare all screens for size + for (int i = 0; i < screens()->count(); i++) { + if (testScreenMatches(i)) { + id = i; + break; + } + } + if (id == -1) { + // still not found + if (internalId != -1) { + // we have an internal id, so let's use that + id = internalId; + } else { + // just take first screen, we have no clue + id = 0; + } + } + } + device->setScreenId(id); + device->setOrientation(screens()->orientation(id)); +#else + Q_UNUSED(device) +#endif +} + bool Connection::isSuspended() const { if (!s_context) { diff --git a/libinput/device.h b/libinput/device.h --- a/libinput/device.h +++ b/libinput/device.h @@ -393,6 +393,22 @@ } /** + * The id of the screen in KWin identifiers. Set from KWin through @link setScreenId. + **/ + int screenId() const { + return m_screenId; + } + + /** + * Sets the KWin screen id for the device + **/ + void setScreenId(int screenId) { + m_screenId = screenId; + } + + void setOrientation(Qt::ScreenOrientation orientation); + + /** * Loads the configuration and applies it to the Device **/ void loadConfiguration(); @@ -485,6 +501,9 @@ KConfigGroup m_config; bool m_loading = false; + int m_screenId = 0; + Qt::ScreenOrientation m_orientation = Qt::PrimaryOrientation; + static QVector s_devices; }; diff --git a/libinput/device.cpp b/libinput/device.cpp --- a/libinput/device.cpp +++ b/libinput/device.cpp @@ -424,5 +424,45 @@ #undef CONFIG +void Device::setOrientation(Qt::ScreenOrientation orientation) +{ + if (!m_supportsCalibrationMatrix) { + return; + } + // 90 deg cw: + static std::array portraitMatrix{{ + 0.0f, -1.0f, 1.0f, + 1.0f, 0.0f, 0.0f + }}; + // 180 deg cw: + static std::array invertedLandscapeMatrix{{ + -1.0f, 0.0f, 1.0f, + 0.0f, -1.0f, 1.0f + }}; + // 270 deg cw + static std::array invertedPortraitMatrix{{ + 0.0f, 1.0f, 0.0f, + -1.0f, 0.0f, 1.0f + }}; + std::array matrix; + switch (orientation) { + case Qt::PortraitOrientation: + matrix = portraitMatrix; + break; + case Qt::InvertedLandscapeOrientation: + matrix = invertedLandscapeMatrix; + break; + case Qt::InvertedPortraitOrientation: + matrix = invertedPortraitMatrix; + break; + case Qt::PrimaryOrientation: + case Qt::LandscapeOrientation: + default: + libinput_device_config_calibration_get_default_matrix(m_device, matrix.data()); + break; + } + libinput_device_config_calibration_set_matrix(m_device, matrix.data()); +} + } } diff --git a/plugins/platforms/drm/drm_output.h b/plugins/platforms/drm/drm_output.h --- a/plugins/platforms/drm/drm_output.h +++ b/plugins/platforms/drm/drm_output.h @@ -118,6 +118,10 @@ return m_internal; } + Qt::ScreenOrientation orientation() const { + return m_orientation; + } + Q_SIGNALS: void dpmsChanged(); void modeChanged(); diff --git a/plugins/platforms/drm/screens_drm.h b/plugins/platforms/drm/screens_drm.h --- a/plugins/platforms/drm/screens_drm.h +++ b/plugins/platforms/drm/screens_drm.h @@ -43,6 +43,7 @@ QSizeF physicalSize(int screen) const override; bool isInternal(int screen) const override; bool supportsTransformations(int screen) const override; + Qt::ScreenOrientation orientation(int screen) const override; private: DrmBackend *m_backend; diff --git a/plugins/platforms/drm/screens_drm.cpp b/plugins/platforms/drm/screens_drm.cpp --- a/plugins/platforms/drm/screens_drm.cpp +++ b/plugins/platforms/drm/screens_drm.cpp @@ -140,4 +140,13 @@ return outputs.at(screen)->supportsTransformations(); } +Qt::ScreenOrientation DrmScreens::orientation(int screen) const +{ + const auto outputs = m_backend->outputs(); + if (screen >= outputs.size()) { + return Qt::PrimaryOrientation; + } + return outputs.at(screen)->orientation(); +} + } diff --git a/screens.h b/screens.h --- a/screens.h +++ b/screens.h @@ -137,6 +137,8 @@ **/ virtual bool supportsTransformations(int screen) const; + virtual Qt::ScreenOrientation orientation(int screen) const; + /** * Provides access to the OrientationSensor. The OrientationSensor is controlled by the * base implementation. The implementing subclass can use this to get notifications about diff --git a/screens.cpp b/screens.cpp --- a/screens.cpp +++ b/screens.cpp @@ -221,6 +221,12 @@ return false; } +Qt::ScreenOrientation Screens::orientation(int screen) const +{ + Q_UNUSED(screen) + return Qt::PrimaryOrientation; +} + BasicScreens::BasicScreens(Platform *backend, QObject *parent) : Screens(parent) , m_backend(backend)