File indexing completed on 2024-12-08 13:22:02
0001 /* 0002 SPDX-FileCopyrightText: 2018 Marco Martin <mart@kde.org> 0003 SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org> 0004 0005 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0006 0007 */ 0008 0009 #include "tabletmodemanager.h" 0010 0011 #include "backends/fakeinput/fakeinputdevice.h" 0012 #include "backends/libinput/device.h" 0013 #include "core/inputdevice.h" 0014 #include "input.h" 0015 #include "input_event.h" 0016 #include "input_event_spy.h" 0017 #include "main.h" 0018 #include "wayland_server.h" 0019 0020 #include <QDBusConnection> 0021 0022 namespace KWin 0023 { 0024 0025 static bool shouldIgnoreDevice(InputDevice *device) 0026 { 0027 if (qobject_cast<FakeInputDevice*>(device)) { 0028 return true; 0029 } 0030 0031 auto libinput_device = qobject_cast<LibInput::Device *>(device); 0032 if (!libinput_device) { 0033 return false; 0034 } 0035 0036 bool ignore = false; 0037 if (auto udev = libinput_device_get_udev_device(libinput_device->device()); udev) { 0038 ignore = udev_device_has_tag(udev, "kwin-ignore-tablet-mode"); 0039 udev_device_unref(udev); 0040 } 0041 return ignore; 0042 } 0043 0044 class TabletModeSwitchEventSpy : public QObject, public InputEventSpy 0045 { 0046 public: 0047 explicit TabletModeSwitchEventSpy(TabletModeManager *parent) 0048 : QObject(parent) 0049 , m_parent(parent) 0050 { 0051 } 0052 0053 void switchEvent(SwitchEvent *event) override 0054 { 0055 if (!event->device()->isTabletModeSwitch()) { 0056 return; 0057 } 0058 0059 switch (event->state()) { 0060 case SwitchEvent::State::Off: 0061 m_parent->setIsTablet(false); 0062 break; 0063 case SwitchEvent::State::On: 0064 m_parent->setIsTablet(true); 0065 break; 0066 default: 0067 Q_UNREACHABLE(); 0068 } 0069 } 0070 0071 private: 0072 TabletModeManager *const m_parent; 0073 }; 0074 0075 class TabletModeTouchpadRemovedSpy : public QObject 0076 { 0077 public: 0078 explicit TabletModeTouchpadRemovedSpy(TabletModeManager *parent) 0079 : QObject(parent) 0080 , m_parent(parent) 0081 { 0082 connect(input(), &InputRedirection::deviceAdded, this, &TabletModeTouchpadRemovedSpy::refresh); 0083 connect(input(), &InputRedirection::deviceRemoved, this, &TabletModeTouchpadRemovedSpy::refresh); 0084 0085 check(); 0086 } 0087 0088 void refresh(InputDevice *inputDevice) 0089 { 0090 if (inputDevice->isTouch() || inputDevice->isPointer()) { 0091 check(); 0092 } 0093 } 0094 0095 void check() 0096 { 0097 const auto devices = input()->devices(); 0098 const bool hasTouch = std::any_of(devices.constBegin(), devices.constEnd(), [](InputDevice *device) { 0099 return device->isTouch() && !shouldIgnoreDevice(device); 0100 }); 0101 m_parent->setTabletModeAvailable(hasTouch); 0102 0103 const bool hasPointer = std::any_of(devices.constBegin(), devices.constEnd(), [](InputDevice *device) { 0104 return device->isPointer() && !shouldIgnoreDevice(device); 0105 }); 0106 m_parent->setIsTablet(hasTouch && !hasPointer); 0107 } 0108 0109 private: 0110 TabletModeManager *const m_parent; 0111 }; 0112 0113 TabletModeManager::TabletModeManager() 0114 { 0115 if (waylandServer()) { 0116 if (input()->hasTabletModeSwitch()) { 0117 input()->installInputEventSpy(new TabletModeSwitchEventSpy(this)); 0118 } else { 0119 hasTabletModeInputChanged(false); 0120 } 0121 } 0122 0123 KSharedConfig::Ptr kwinSettings = kwinApp()->config(); 0124 m_settingsWatcher = KConfigWatcher::create(kwinSettings); 0125 connect(m_settingsWatcher.data(), &KConfigWatcher::configChanged, this, &KWin::TabletModeManager::refreshSettings); 0126 refreshSettings(); 0127 0128 QDBusConnection::sessionBus().registerObject(QStringLiteral("/org/kde/KWin"), 0129 QStringLiteral("org.kde.KWin.TabletModeManager"), 0130 this, 0131 // NOTE: slots must be exported for properties to work correctly 0132 QDBusConnection::ExportAllProperties | QDBusConnection::ExportAllSignals | QDBusConnection::ExportAllSlots); 0133 0134 if (waylandServer()) { 0135 connect(input(), &InputRedirection::hasTabletModeSwitchChanged, this, &TabletModeManager::hasTabletModeInputChanged); 0136 } 0137 } 0138 0139 void KWin::TabletModeManager::refreshSettings() 0140 { 0141 KSharedConfig::Ptr kwinSettings = kwinApp()->config(); 0142 KConfigGroup cg = kwinSettings->group("Input"); 0143 const QString tabletModeConfig = cg.readPathEntry("TabletMode", QStringLiteral("auto")); 0144 const bool oldEffectiveTabletMode = effectiveTabletMode(); 0145 if (tabletModeConfig == QStringLiteral("on")) { 0146 m_configuredMode = ConfiguredMode::On; 0147 if (!m_detecting) { 0148 Q_EMIT tabletModeAvailableChanged(true); 0149 } 0150 } else if (tabletModeConfig == QStringLiteral("off")) { 0151 m_configuredMode = ConfiguredMode::Off; 0152 } else { 0153 m_configuredMode = ConfiguredMode::Auto; 0154 } 0155 if (effectiveTabletMode() != oldEffectiveTabletMode) { 0156 Q_EMIT tabletModeChanged(effectiveTabletMode()); 0157 } 0158 } 0159 0160 void KWin::TabletModeManager::hasTabletModeInputChanged(bool set) 0161 { 0162 if (set) { 0163 input()->installInputEventSpy(new TabletModeSwitchEventSpy(this)); 0164 setTabletModeAvailable(true); 0165 } else { 0166 auto spy = new TabletModeTouchpadRemovedSpy(this); 0167 connect(input(), &InputRedirection::hasTabletModeSwitchChanged, spy, [spy](bool set) { 0168 if (set) { 0169 spy->deleteLater(); 0170 } 0171 }); 0172 } 0173 } 0174 0175 bool TabletModeManager::isTabletModeAvailable() const 0176 { 0177 return m_detecting; 0178 } 0179 0180 bool TabletModeManager::effectiveTabletMode() const 0181 { 0182 switch (m_configuredMode) { 0183 case ConfiguredMode::Off: 0184 return false; 0185 case ConfiguredMode::On: 0186 return true; 0187 case ConfiguredMode::Auto: 0188 default: 0189 if (!waylandServer()) { 0190 return false; 0191 } else { 0192 return m_isTabletMode; 0193 } 0194 } 0195 } 0196 0197 bool TabletModeManager::isTablet() const 0198 { 0199 return m_isTabletMode; 0200 } 0201 0202 void TabletModeManager::setIsTablet(bool tablet) 0203 { 0204 if (m_isTabletMode == tablet) { 0205 return; 0206 } 0207 0208 const bool oldTabletMode = effectiveTabletMode(); 0209 m_isTabletMode = tablet; 0210 if (effectiveTabletMode() != oldTabletMode) { 0211 Q_EMIT tabletModeChanged(effectiveTabletMode()); 0212 } 0213 } 0214 0215 void KWin::TabletModeManager::setTabletModeAvailable(bool detecting) 0216 { 0217 if (m_detecting == detecting) { 0218 return; 0219 } 0220 0221 m_detecting = detecting; 0222 Q_EMIT tabletModeAvailableChanged(isTabletModeAvailable()); 0223 } 0224 0225 KWin::TabletModeManager::ConfiguredMode KWin::TabletModeManager::configuredMode() const 0226 { 0227 return m_configuredMode; 0228 } 0229 0230 } // namespace KWin