File indexing completed on 2024-11-10 04:56:31

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "connection.h"
0010 #include "context.h"
0011 #include "device.h"
0012 #include "events.h"
0013 
0014 // TODO: Make it compile also in testing environment
0015 #ifndef KWIN_BUILD_TESTING
0016 #include "core/output.h"
0017 #include "core/outputbackend.h"
0018 #include "main.h"
0019 #include "window.h"
0020 #include "workspace.h"
0021 #endif
0022 
0023 #include "core/session.h"
0024 #include "input_event.h"
0025 #include "libinput_logging.h"
0026 #include "utils/realtime.h"
0027 #include "utils/udev.h"
0028 
0029 #include <QDBusConnection>
0030 #include <QMutexLocker>
0031 #include <QSocketNotifier>
0032 
0033 #include <cmath>
0034 #include <libinput.h>
0035 
0036 namespace KWin
0037 {
0038 namespace LibInput
0039 {
0040 
0041 class ConnectionAdaptor : public QObject
0042 {
0043     Q_OBJECT
0044     Q_CLASSINFO("D-Bus Interface", "org.kde.KWin.InputDeviceManager")
0045     Q_PROPERTY(QStringList devicesSysNames READ devicesSysNames CONSTANT)
0046 
0047 private:
0048     Connection *m_con;
0049 
0050 public:
0051     ConnectionAdaptor(Connection *con)
0052         : QObject(con)
0053         , m_con(con)
0054     {
0055         connect(con, &Connection::deviceAdded, this, [this](LibInput::Device *inputDevice) {
0056             Q_EMIT deviceAdded(inputDevice->sysName());
0057         });
0058         connect(con, &Connection::deviceRemoved, this, [this](LibInput::Device *inputDevice) {
0059             Q_EMIT deviceRemoved(inputDevice->sysName());
0060         });
0061 
0062         QDBusConnection::sessionBus().registerObject(QStringLiteral("/org/kde/KWin/InputDevice"),
0063                                                      QStringLiteral("org.kde.KWin.InputDeviceManager"),
0064                                                      this,
0065                                                      QDBusConnection::ExportAllProperties | QDBusConnection::ExportAllSignals);
0066     }
0067 
0068     ~ConnectionAdaptor() override
0069     {
0070         QDBusConnection::sessionBus().unregisterObject(QStringLiteral("/org/kde/KWin/InputDeviceManager"));
0071     }
0072 
0073     QStringList devicesSysNames()
0074     {
0075         return m_con->devicesSysNames();
0076     }
0077 
0078 Q_SIGNALS:
0079     void deviceAdded(QString sysName);
0080     void deviceRemoved(QString sysName);
0081 };
0082 
0083 std::unique_ptr<Connection> Connection::create(Session *session)
0084 {
0085     std::unique_ptr<Udev> udev = std::make_unique<Udev>();
0086     if (!udev->isValid()) {
0087         qCWarning(KWIN_LIBINPUT) << "Failed to initialize udev";
0088         return nullptr;
0089     }
0090     std::unique_ptr<Context> context = std::make_unique<Context>(session, std::move(udev));
0091     if (!context->isValid()) {
0092         qCWarning(KWIN_LIBINPUT) << "Failed to create context from udev";
0093         return nullptr;
0094     }
0095     if (!context->initialize()) {
0096         qCWarning(KWIN_LIBINPUT) << "Failed to initialize context";
0097         return nullptr;
0098     }
0099     return std::unique_ptr<Connection>(new Connection(std::move(context)));
0100 }
0101 
0102 Connection::Connection(std::unique_ptr<Context> &&input)
0103     : m_notifier(nullptr)
0104     , m_connectionAdaptor(std::make_unique<ConnectionAdaptor>(this))
0105     , m_input(std::move(input))
0106 {
0107     Q_ASSERT(m_input);
0108     // need to connect to KGlobalSettings as the mouse KCM does not emit a dedicated signal
0109     QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/KGlobalSettings"), QStringLiteral("org.kde.KGlobalSettings"),
0110                                           QStringLiteral("notifyChange"), this, SLOT(slotKGlobalSettingsNotifyChange(int, int)));
0111 }
0112 
0113 Connection::~Connection() = default;
0114 
0115 void Connection::setup()
0116 {
0117     QMetaObject::invokeMethod(this, &Connection::doSetup, Qt::QueuedConnection);
0118 }
0119 
0120 void Connection::doSetup()
0121 {
0122     Q_ASSERT(!m_notifier);
0123 
0124     gainRealTime();
0125 
0126     m_notifier = std::make_unique<QSocketNotifier>(m_input->fileDescriptor(), QSocketNotifier::Read);
0127     connect(m_notifier.get(), &QSocketNotifier::activated, this, &Connection::handleEvent);
0128 
0129     connect(m_input->session(), &Session::activeChanged, this, [this](bool active) {
0130         if (active) {
0131             if (!m_input->isSuspended()) {
0132                 return;
0133             }
0134             m_input->resume();
0135         } else {
0136             deactivate();
0137         }
0138     });
0139     handleEvent();
0140 }
0141 
0142 void Connection::deactivate()
0143 {
0144     if (m_input->isSuspended()) {
0145         return;
0146     }
0147     m_input->suspend();
0148     handleEvent();
0149 }
0150 
0151 void Connection::handleEvent()
0152 {
0153     QMutexLocker locker(&m_mutex);
0154     const bool wasEmpty = m_eventQueue.empty();
0155     do {
0156         m_input->dispatch();
0157         std::unique_ptr<Event> event = m_input->event();
0158         if (!event) {
0159             break;
0160         }
0161         m_eventQueue.push_back(std::move(event));
0162     } while (true);
0163     if (wasEmpty && !m_eventQueue.empty()) {
0164         Q_EMIT eventsRead();
0165     }
0166 }
0167 
0168 #ifndef KWIN_BUILD_TESTING
0169 QPointF devicePointToGlobalPosition(const QPointF &devicePos, const Output *output)
0170 {
0171     QPointF pos = devicePos;
0172     // TODO: Do we need to handle the flipped cases differently?
0173     switch (output->transform().kind()) {
0174     case OutputTransform::Normal:
0175     case OutputTransform::FlipX:
0176         break;
0177     case OutputTransform::Rotate90:
0178     case OutputTransform::FlipX90:
0179         pos = QPointF(output->modeSize().height() - devicePos.y(), devicePos.x());
0180         break;
0181     case OutputTransform::Rotate180:
0182     case OutputTransform::FlipX180:
0183         pos = QPointF(output->modeSize().width() - devicePos.x(),
0184                       output->modeSize().height() - devicePos.y());
0185         break;
0186     case OutputTransform::Rotate270:
0187     case OutputTransform::FlipX270:
0188         pos = QPointF(devicePos.y(), output->modeSize().width() - devicePos.x());
0189         break;
0190     default:
0191         Q_UNREACHABLE();
0192     }
0193     return output->geometry().topLeft() + pos / output->scale();
0194 }
0195 #endif
0196 
0197 KWin::TabletToolId createTabletId(libinput_tablet_tool *tool, Device *dev)
0198 {
0199     auto serial = libinput_tablet_tool_get_serial(tool);
0200     auto toolId = libinput_tablet_tool_get_tool_id(tool);
0201     auto type = libinput_tablet_tool_get_type(tool);
0202     InputRedirection::TabletToolType toolType;
0203     switch (type) {
0204     case LIBINPUT_TABLET_TOOL_TYPE_PEN:
0205         toolType = InputRedirection::Pen;
0206         break;
0207     case LIBINPUT_TABLET_TOOL_TYPE_ERASER:
0208         toolType = InputRedirection::Eraser;
0209         break;
0210     case LIBINPUT_TABLET_TOOL_TYPE_BRUSH:
0211         toolType = InputRedirection::Brush;
0212         break;
0213     case LIBINPUT_TABLET_TOOL_TYPE_PENCIL:
0214         toolType = InputRedirection::Pencil;
0215         break;
0216     case LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH:
0217         toolType = InputRedirection::Airbrush;
0218         break;
0219     case LIBINPUT_TABLET_TOOL_TYPE_MOUSE:
0220         toolType = InputRedirection::Mouse;
0221         break;
0222     case LIBINPUT_TABLET_TOOL_TYPE_LENS:
0223         toolType = InputRedirection::Lens;
0224         break;
0225     case LIBINPUT_TABLET_TOOL_TYPE_TOTEM:
0226         toolType = InputRedirection::Totem;
0227         break;
0228     }
0229     QList<InputRedirection::Capability> capabilities;
0230     if (libinput_tablet_tool_has_pressure(tool)) {
0231         capabilities << InputRedirection::Pressure;
0232     }
0233     if (libinput_tablet_tool_has_distance(tool)) {
0234         capabilities << InputRedirection::Distance;
0235     }
0236     if (libinput_tablet_tool_has_rotation(tool)) {
0237         capabilities << InputRedirection::Rotation;
0238     }
0239     if (libinput_tablet_tool_has_tilt(tool)) {
0240         capabilities << InputRedirection::Tilt;
0241     }
0242     if (libinput_tablet_tool_has_slider(tool)) {
0243         capabilities << InputRedirection::Slider;
0244     }
0245     if (libinput_tablet_tool_has_wheel(tool)) {
0246         capabilities << InputRedirection::Wheel;
0247     }
0248     return {dev->sysName(), toolType, capabilities, serial, toolId, dev->groupUserData(), dev->name()};
0249 }
0250 
0251 static TabletPadId createTabletPadId(LibInput::Device *device)
0252 {
0253     if (!device || !device->groupUserData()) {
0254         return {};
0255     }
0256 
0257     return {
0258         device->name(),
0259         device->groupUserData(),
0260     };
0261 }
0262 
0263 void Connection::processEvents()
0264 {
0265     QMutexLocker locker(&m_mutex);
0266     while (m_eventQueue.size() != 0) {
0267         std::unique_ptr<Event> event = std::move(m_eventQueue.front());
0268         m_eventQueue.pop_front();
0269         switch (event->type()) {
0270         case LIBINPUT_EVENT_DEVICE_ADDED: {
0271             auto device = new Device(event->nativeDevice());
0272             device->moveToThread(thread());
0273             m_devices << device;
0274 
0275             applyDeviceConfig(device);
0276             applyScreenToDevice(device);
0277 
0278             connect(device, &Device::outputNameChanged, this, [this, device] {
0279                 // If the output name changes from something to empty we need to
0280                 // re-run the assignment heuristic so that an output is assinged
0281                 if (device->outputName().isEmpty()) {
0282                     applyScreenToDevice(device);
0283                 }
0284             });
0285 
0286             Q_EMIT deviceAdded(device);
0287             break;
0288         }
0289         case LIBINPUT_EVENT_DEVICE_REMOVED: {
0290             auto it = std::find_if(m_devices.begin(), m_devices.end(), [&event](Device *d) {
0291                 return event->device() == d;
0292             });
0293             if (it == m_devices.end()) {
0294                 // we don't know this device
0295                 break;
0296             }
0297             auto device = *it;
0298             m_devices.erase(it);
0299             Q_EMIT deviceRemoved(device);
0300             device->deleteLater();
0301             break;
0302         }
0303         case LIBINPUT_EVENT_KEYBOARD_KEY: {
0304             KeyEvent *ke = static_cast<KeyEvent *>(event.get());
0305             Q_EMIT ke->device()->keyChanged(ke->key(), ke->state(), ke->time(), ke->device());
0306             break;
0307         }
0308         case LIBINPUT_EVENT_POINTER_SCROLL_WHEEL: {
0309             const PointerEvent *pointerEvent = static_cast<PointerEvent *>(event.get());
0310             const auto axes = pointerEvent->axis();
0311             for (const InputRedirection::PointerAxis &axis : axes) {
0312                 Q_EMIT pointerEvent->device()->pointerAxisChanged(axis,
0313                                                                   pointerEvent->scrollValue(axis),
0314                                                                   pointerEvent->scrollValueV120(axis),
0315                                                                   InputRedirection::PointerAxisSourceWheel,
0316                                                                   pointerEvent->time(),
0317                                                                   pointerEvent->device());
0318             }
0319             Q_EMIT pointerEvent->device()->pointerFrame(pointerEvent->device());
0320             break;
0321         }
0322         case LIBINPUT_EVENT_POINTER_SCROLL_FINGER: {
0323             const PointerEvent *pointerEvent = static_cast<PointerEvent *>(event.get());
0324             const auto axes = pointerEvent->axis();
0325             for (const InputRedirection::PointerAxis &axis : axes) {
0326                 Q_EMIT pointerEvent->device()->pointerAxisChanged(axis,
0327                                                                   pointerEvent->scrollValue(axis),
0328                                                                   0,
0329                                                                   InputRedirection::PointerAxisSourceFinger,
0330                                                                   pointerEvent->time(),
0331                                                                   pointerEvent->device());
0332             }
0333             Q_EMIT pointerEvent->device()->pointerFrame(pointerEvent->device());
0334             break;
0335         }
0336         case LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS: {
0337             const PointerEvent *pointerEvent = static_cast<PointerEvent *>(event.get());
0338             const auto axes = pointerEvent->axis();
0339             for (const InputRedirection::PointerAxis &axis : axes) {
0340                 Q_EMIT pointerEvent->device()->pointerAxisChanged(axis,
0341                                                                   pointerEvent->scrollValue(axis),
0342                                                                   0,
0343                                                                   InputRedirection::PointerAxisSourceContinuous,
0344                                                                   pointerEvent->time(),
0345                                                                   pointerEvent->device());
0346             }
0347             Q_EMIT pointerEvent->device()->pointerFrame(pointerEvent->device());
0348             break;
0349         }
0350         case LIBINPUT_EVENT_POINTER_BUTTON: {
0351             PointerEvent *pe = static_cast<PointerEvent *>(event.get());
0352             Q_EMIT pe->device()->pointerButtonChanged(pe->button(), pe->buttonState(), pe->time(), pe->device());
0353             Q_EMIT pe->device()->pointerFrame(pe->device());
0354             break;
0355         }
0356         case LIBINPUT_EVENT_POINTER_MOTION: {
0357             PointerEvent *pe = static_cast<PointerEvent *>(event.get());
0358             auto delta = pe->delta();
0359             auto deltaNonAccel = pe->deltaUnaccelerated();
0360             auto latestTime = pe->time();
0361             auto it = m_eventQueue.begin();
0362             while (it != m_eventQueue.end()) {
0363                 if ((*it)->type() == LIBINPUT_EVENT_POINTER_MOTION) {
0364                     std::unique_ptr<PointerEvent> p{static_cast<PointerEvent *>(it->release())};
0365                     delta += p->delta();
0366                     deltaNonAccel += p->deltaUnaccelerated();
0367                     latestTime = p->time();
0368                     it = m_eventQueue.erase(it);
0369                 } else {
0370                     break;
0371                 }
0372             }
0373             Q_EMIT pe->device()->pointerMotion(delta, deltaNonAccel, latestTime, pe->device());
0374             Q_EMIT pe->device()->pointerFrame(pe->device());
0375             break;
0376         }
0377         case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: {
0378             PointerEvent *pe = static_cast<PointerEvent *>(event.get());
0379             if (workspace()) {
0380                 Q_EMIT pe->device()->pointerMotionAbsolute(pe->absolutePos(workspace()->geometry().size()), pe->time(), pe->device());
0381                 Q_EMIT pe->device()->pointerFrame(pe->device());
0382             }
0383             break;
0384         }
0385         case LIBINPUT_EVENT_TOUCH_DOWN: {
0386 #ifndef KWIN_BUILD_TESTING
0387             TouchEvent *te = static_cast<TouchEvent *>(event.get());
0388             const auto *output = te->device()->output();
0389             if (!output) {
0390                 qCWarning(KWIN_LIBINPUT) << "Touch down received for device with no output assigned";
0391                 break;
0392             }
0393             const QPointF globalPos = devicePointToGlobalPosition(te->absolutePos(output->modeSize()), output);
0394             Q_EMIT te->device()->touchDown(te->id(), globalPos, te->time(), te->device());
0395             break;
0396 #endif
0397         }
0398         case LIBINPUT_EVENT_TOUCH_UP: {
0399             TouchEvent *te = static_cast<TouchEvent *>(event.get());
0400             const auto *output = te->device()->output();
0401             if (!output) {
0402                 break;
0403             }
0404             Q_EMIT te->device()->touchUp(te->id(), te->time(), te->device());
0405             break;
0406         }
0407         case LIBINPUT_EVENT_TOUCH_MOTION: {
0408 #ifndef KWIN_BUILD_TESTING
0409             TouchEvent *te = static_cast<TouchEvent *>(event.get());
0410             const auto *output = te->device()->output();
0411             if (!output) {
0412                 break;
0413             }
0414             const QPointF globalPos = devicePointToGlobalPosition(te->absolutePos(output->modeSize()), output);
0415             Q_EMIT te->device()->touchMotion(te->id(), globalPos, te->time(), te->device());
0416             break;
0417 #endif
0418         }
0419         case LIBINPUT_EVENT_TOUCH_CANCEL: {
0420             Q_EMIT event->device()->touchCanceled(event->device());
0421             break;
0422         }
0423         case LIBINPUT_EVENT_TOUCH_FRAME: {
0424             Q_EMIT event->device()->touchFrame(event->device());
0425             break;
0426         }
0427         case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN: {
0428             PinchGestureEvent *pe = static_cast<PinchGestureEvent *>(event.get());
0429             Q_EMIT pe->device()->pinchGestureBegin(pe->fingerCount(), pe->time(), pe->device());
0430             break;
0431         }
0432         case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE: {
0433             PinchGestureEvent *pe = static_cast<PinchGestureEvent *>(event.get());
0434             Q_EMIT pe->device()->pinchGestureUpdate(pe->scale(), pe->angleDelta(), pe->delta(), pe->time(), pe->device());
0435             break;
0436         }
0437         case LIBINPUT_EVENT_GESTURE_PINCH_END: {
0438             PinchGestureEvent *pe = static_cast<PinchGestureEvent *>(event.get());
0439             if (pe->isCancelled()) {
0440                 Q_EMIT pe->device()->pinchGestureCancelled(pe->time(), pe->device());
0441             } else {
0442                 Q_EMIT pe->device()->pinchGestureEnd(pe->time(), pe->device());
0443             }
0444             break;
0445         }
0446         case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN: {
0447             SwipeGestureEvent *se = static_cast<SwipeGestureEvent *>(event.get());
0448             Q_EMIT se->device()->swipeGestureBegin(se->fingerCount(), se->time(), se->device());
0449             break;
0450         }
0451         case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE: {
0452             SwipeGestureEvent *se = static_cast<SwipeGestureEvent *>(event.get());
0453             Q_EMIT se->device()->swipeGestureUpdate(se->delta(), se->time(), se->device());
0454             break;
0455         }
0456         case LIBINPUT_EVENT_GESTURE_SWIPE_END: {
0457             SwipeGestureEvent *se = static_cast<SwipeGestureEvent *>(event.get());
0458             if (se->isCancelled()) {
0459                 Q_EMIT se->device()->swipeGestureCancelled(se->time(), se->device());
0460             } else {
0461                 Q_EMIT se->device()->swipeGestureEnd(se->time(), se->device());
0462             }
0463             break;
0464         }
0465         case LIBINPUT_EVENT_GESTURE_HOLD_BEGIN: {
0466             HoldGestureEvent *he = static_cast<HoldGestureEvent *>(event.get());
0467             Q_EMIT he->device()->holdGestureBegin(he->fingerCount(), he->time(), he->device());
0468             break;
0469         }
0470         case LIBINPUT_EVENT_GESTURE_HOLD_END: {
0471             HoldGestureEvent *he = static_cast<HoldGestureEvent *>(event.get());
0472             if (he->isCancelled()) {
0473                 Q_EMIT he->device()->holdGestureCancelled(he->time(), he->device());
0474             } else {
0475                 Q_EMIT he->device()->holdGestureEnd(he->time(), he->device());
0476             }
0477             break;
0478         }
0479         case LIBINPUT_EVENT_SWITCH_TOGGLE: {
0480             SwitchEvent *se = static_cast<SwitchEvent *>(event.get());
0481             switch (se->state()) {
0482             case SwitchEvent::State::Off:
0483                 Q_EMIT se->device()->switchToggledOff(se->time(), se->device());
0484                 break;
0485             case SwitchEvent::State::On:
0486                 Q_EMIT se->device()->switchToggledOn(se->time(), se->device());
0487                 break;
0488             default:
0489                 Q_UNREACHABLE();
0490             }
0491             break;
0492         }
0493         case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
0494         case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
0495         case LIBINPUT_EVENT_TABLET_TOOL_TIP: {
0496             auto *tte = static_cast<TabletToolEvent *>(event.get());
0497 
0498             KWin::InputRedirection::TabletEventType tabletEventType;
0499             switch (event->type()) {
0500             case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
0501                 tabletEventType = KWin::InputRedirection::Axis;
0502                 break;
0503             case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
0504                 tabletEventType = KWin::InputRedirection::Proximity;
0505                 break;
0506             case LIBINPUT_EVENT_TABLET_TOOL_TIP:
0507             default:
0508                 tabletEventType = KWin::InputRedirection::Tip;
0509                 break;
0510             }
0511 
0512             if (workspace()) {
0513 #ifndef KWIN_BUILD_TESTING
0514                 QPointF globalPos;
0515                 if (tte->device()->isMapToWorkspace()) {
0516                     globalPos = workspace()->geometry().topLeft() + tte->transformedPosition(workspace()->geometry().size());
0517                 } else {
0518                     Output *output = tte->device()->output();
0519                     if (!output && workspace()->activeWindow()) {
0520                         output = workspace()->activeWindow()->output();
0521                     }
0522                     if (!output) {
0523                         output = workspace()->activeOutput();
0524                     }
0525                     globalPos = devicePointToGlobalPosition(tte->transformedPosition(output->modeSize()), output);
0526                 }
0527 #else
0528                 const QPointF globalPos;
0529 #endif
0530                 Q_EMIT event->device()->tabletToolEvent(tabletEventType,
0531                                                         globalPos, tte->pressure(),
0532                                                         tte->xTilt(), tte->yTilt(), tte->rotation(),
0533                                                         tte->isTipDown(), tte->isNearby(), createTabletId(tte->tool(), event->device()), tte->time());
0534             }
0535             break;
0536         }
0537         case LIBINPUT_EVENT_TABLET_TOOL_BUTTON: {
0538             auto *tabletEvent = static_cast<TabletToolButtonEvent *>(event.get());
0539             Q_EMIT event->device()->tabletToolButtonEvent(tabletEvent->buttonId(),
0540                                                           tabletEvent->isButtonPressed(),
0541                                                           createTabletId(tabletEvent->tool(), event->device()), tabletEvent->time());
0542             break;
0543         }
0544         case LIBINPUT_EVENT_TABLET_PAD_BUTTON: {
0545             auto *tabletEvent = static_cast<TabletPadButtonEvent *>(event.get());
0546             Q_EMIT event->device()->tabletPadButtonEvent(tabletEvent->buttonId(),
0547                                                          tabletEvent->isButtonPressed(),
0548                                                          createTabletPadId(event->device()), tabletEvent->time());
0549             break;
0550         }
0551         case LIBINPUT_EVENT_TABLET_PAD_RING: {
0552             auto *tabletEvent = static_cast<TabletPadRingEvent *>(event.get());
0553             tabletEvent->position();
0554             Q_EMIT event->device()->tabletPadRingEvent(tabletEvent->number(),
0555                                                        tabletEvent->position(),
0556                                                        tabletEvent->source() == LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER,
0557                                                        createTabletPadId(event->device()), tabletEvent->time());
0558             break;
0559         }
0560         case LIBINPUT_EVENT_TABLET_PAD_STRIP: {
0561             auto *tabletEvent = static_cast<TabletPadStripEvent *>(event.get());
0562             Q_EMIT event->device()->tabletPadStripEvent(tabletEvent->number(),
0563                                                         tabletEvent->position(),
0564                                                         tabletEvent->source() == LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER,
0565                                                         createTabletPadId(event->device()), tabletEvent->time());
0566             break;
0567         }
0568         default:
0569             // nothing
0570             break;
0571         }
0572     }
0573 }
0574 
0575 void Connection::updateScreens()
0576 {
0577     QMutexLocker locker(&m_mutex);
0578     for (auto device : std::as_const(m_devices)) {
0579         applyScreenToDevice(device);
0580     }
0581 }
0582 
0583 void Connection::applyScreenToDevice(Device *device)
0584 {
0585 #ifndef KWIN_BUILD_TESTING
0586     QMutexLocker locker(&m_mutex);
0587     if (!device->isTouch() && !device->isTabletTool()) {
0588         return;
0589     }
0590 
0591     Output *deviceOutput = nullptr;
0592     const QList<Output *> outputs = kwinApp()->outputBackend()->outputs();
0593 
0594     // let's try to find a screen for it
0595     if (!device->outputName().isEmpty()) {
0596         // we have an output name, try to find a screen with matching name
0597         for (Output *output : outputs) {
0598             if (!output->isEnabled()) {
0599                 continue;
0600             }
0601             if (output->name() == device->outputName()) {
0602                 deviceOutput = output;
0603                 break;
0604             }
0605         }
0606     }
0607     if (!deviceOutput && device->isTouch()) {
0608         // do we have an internal screen?
0609         Output *internalOutput = nullptr;
0610         for (Output *output : outputs) {
0611             if (!output->isEnabled()) {
0612                 continue;
0613             }
0614             if (output->isInternal()) {
0615                 internalOutput = output;
0616                 break;
0617             }
0618         }
0619         auto testScreenMatches = [device](const Output *output) {
0620             const auto &size = device->size();
0621             const auto &screenSize = output->physicalSize();
0622             return std::round(size.width()) == std::round(screenSize.width())
0623                 && std::round(size.height()) == std::round(screenSize.height());
0624         };
0625         if (internalOutput && testScreenMatches(internalOutput)) {
0626             deviceOutput = internalOutput;
0627         }
0628         // let's compare all screens for size
0629         for (Output *output : outputs) {
0630             if (!output->isEnabled()) {
0631                 continue;
0632             }
0633             if (testScreenMatches(output)) {
0634                 deviceOutput = output;
0635                 break;
0636             }
0637         }
0638         if (!deviceOutput) {
0639             // still not found
0640             if (internalOutput) {
0641                 // we have an internal id, so let's use that
0642                 deviceOutput = internalOutput;
0643             } else {
0644                 for (Output *output : outputs) {
0645                     // just take first screen, we have no clue
0646                     if (output->isEnabled()) {
0647                         deviceOutput = output;
0648                         break;
0649                     }
0650                 }
0651             }
0652         }
0653     }
0654 
0655     device->setOutput(deviceOutput);
0656 
0657     // TODO: this is currently non-functional even on DRM. Needs orientation() override there.
0658     device->setOrientation(Qt::PrimaryOrientation);
0659 #endif
0660 }
0661 
0662 void Connection::applyDeviceConfig(Device *device)
0663 {
0664     KConfigGroup defaults = m_config->group(QStringLiteral("Libinput")).group(QStringLiteral("Defaults"));
0665     if (defaults.isValid()) {
0666         if (device->isAlphaNumericKeyboard() && defaults.hasGroup(QStringLiteral("Keyboard"))) {
0667             defaults = defaults.group(QStringLiteral("Keyboard"));
0668         } else if (device->isTouchpad() && defaults.hasGroup(QStringLiteral("Touchpad"))) {
0669             // A Touchpad is a Pointer, so we need to check for it before Pointer.
0670             defaults = defaults.group(QStringLiteral("Touchpad"));
0671         } else if (device->isPointer() && defaults.hasGroup(QStringLiteral("Pointer"))) {
0672             defaults = defaults.group(QStringLiteral("Pointer"));
0673         }
0674 
0675         device->setDefaultConfig(defaults);
0676     }
0677 
0678     // pass configuration to Device
0679     device->setConfig(m_config->group(QStringLiteral("Libinput")).group(QString::number(device->vendor())).group(QString::number(device->product())).group(device->name()));
0680     device->loadConfiguration();
0681 }
0682 
0683 void Connection::slotKGlobalSettingsNotifyChange(int type, int arg)
0684 {
0685     if (type == 3 /**SettingsChanged**/ && arg == 0 /** SETTINGS_MOUSE */) {
0686         m_config->reparseConfiguration();
0687         for (auto it = m_devices.constBegin(), end = m_devices.constEnd(); it != end; ++it) {
0688             if ((*it)->isPointer()) {
0689                 applyDeviceConfig(*it);
0690             }
0691         }
0692     }
0693 }
0694 
0695 QStringList Connection::devicesSysNames() const
0696 {
0697     QStringList sl;
0698     for (Device *d : std::as_const(m_devices)) {
0699         sl.append(d->sysName());
0700     }
0701     return sl;
0702 }
0703 
0704 }
0705 }
0706 
0707 #include "connection.moc"
0708 
0709 #include "moc_connection.cpp"