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"