KWin
Loading...
Searching...
No Matches
tabletmodemanager.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2018 Marco Martin <mart@kde.org>
3 SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org>
4
5 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
6
7*/
8
9#include "tabletmodemanager.h"
10
13#include "core/inputdevice.h"
14#include "input.h"
15#include "input_event.h"
16#include "input_event_spy.h"
17#include "main.h"
18#include "wayland_server.h"
19
20#include <QDBusConnection>
21
22namespace KWin
23{
24
25static bool shouldIgnoreDevice(InputDevice *device)
26{
27 if (qobject_cast<FakeInputDevice*>(device)) {
28 return true;
29 }
30
31 auto libinput_device = qobject_cast<LibInput::Device *>(device);
32 if (!libinput_device) {
33 return false;
34 }
35
36 bool ignore = false;
37 if (auto udev = libinput_device_get_udev_device(libinput_device->device()); udev) {
38 ignore = udev_device_has_tag(udev, "kwin-ignore-tablet-mode");
39 udev_device_unref(udev);
40 }
41 return ignore;
42}
43
44class TabletModeSwitchEventSpy : public QObject, public InputEventSpy
45{
46public:
48 : QObject(parent)
49 , m_parent(parent)
50 {
51 }
52
53 void switchEvent(SwitchEvent *event) override
54 {
55 if (!event->device()->isTabletModeSwitch()) {
56 return;
57 }
58
59 switch (event->state()) {
61 m_parent->setIsTablet(false);
62 break;
64 m_parent->setIsTablet(true);
65 break;
66 default:
67 Q_UNREACHABLE();
68 }
69 }
70
71private:
72 TabletModeManager *const m_parent;
73};
74
75class TabletModeTouchpadRemovedSpy : public QObject
76{
77public:
79 : QObject(parent)
80 , m_parent(parent)
81 {
84
85 check();
86 }
87
88 void refresh(InputDevice *inputDevice)
89 {
90 if (inputDevice->isTouch() || inputDevice->isPointer()) {
91 check();
92 }
93 }
94
95 void check()
96 {
97 const auto devices = input()->devices();
98 const bool hasTouch = std::any_of(devices.constBegin(), devices.constEnd(), [](InputDevice *device) {
99 return device->isTouch() && !shouldIgnoreDevice(device);
100 });
101 m_parent->setTabletModeAvailable(hasTouch);
102
103 const bool hasPointer = std::any_of(devices.constBegin(), devices.constEnd(), [](InputDevice *device) {
104 return device->isPointer() && !shouldIgnoreDevice(device);
105 });
106 m_parent->setIsTablet(hasTouch && !hasPointer);
107 }
108
109private:
110 TabletModeManager *const m_parent;
111};
112
114{
115 if (waylandServer()) {
116 if (input()->hasTabletModeSwitch()) {
118 } else {
119 hasTabletModeInputChanged(false);
120 }
121 }
122
123 KSharedConfig::Ptr kwinSettings = kwinApp()->config();
124 m_settingsWatcher = KConfigWatcher::create(kwinSettings);
125 connect(m_settingsWatcher.data(), &KConfigWatcher::configChanged, this, &KWin::TabletModeManager::refreshSettings);
126 refreshSettings();
127
128 QDBusConnection::sessionBus().registerObject(QStringLiteral("/org/kde/KWin"),
129 QStringLiteral("org.kde.KWin.TabletModeManager"),
130 this,
131 // NOTE: slots must be exported for properties to work correctly
132 QDBusConnection::ExportAllProperties | QDBusConnection::ExportAllSignals | QDBusConnection::ExportAllSlots);
133
134 if (waylandServer()) {
135 connect(input(), &InputRedirection::hasTabletModeSwitchChanged, this, &TabletModeManager::hasTabletModeInputChanged);
136 }
137}
138
139void KWin::TabletModeManager::refreshSettings()
140{
141 KSharedConfig::Ptr kwinSettings = kwinApp()->config();
142 KConfigGroup cg = kwinSettings->group(QStringLiteral("Input"));
143 const QString tabletModeConfig = cg.readPathEntry("TabletMode", QStringLiteral("auto"));
144 const bool oldEffectiveTabletMode = effectiveTabletMode();
145 if (tabletModeConfig == QStringLiteral("on")) {
146 m_configuredMode = ConfiguredMode::On;
147 if (!m_detecting) {
148 Q_EMIT tabletModeAvailableChanged(true);
149 }
150 } else if (tabletModeConfig == QStringLiteral("off")) {
151 m_configuredMode = ConfiguredMode::Off;
152 } else {
153 m_configuredMode = ConfiguredMode::Auto;
154 }
155 if (effectiveTabletMode() != oldEffectiveTabletMode) {
156 Q_EMIT tabletModeChanged(effectiveTabletMode());
157 }
158}
159
160void KWin::TabletModeManager::hasTabletModeInputChanged(bool set)
161{
162 if (set) {
163 input()->installInputEventSpy(new TabletModeSwitchEventSpy(this));
164 setTabletModeAvailable(true);
165 } else {
166 auto spy = new TabletModeTouchpadRemovedSpy(this);
167 connect(input(), &InputRedirection::hasTabletModeSwitchChanged, spy, [spy](bool set) {
168 if (set) {
169 spy->deleteLater();
170 }
171 });
172 }
173}
174
176{
177 return m_detecting;
178}
179
181{
182 switch (m_configuredMode) {
184 return false;
186 return true;
188 default:
189 if (!waylandServer()) {
190 return false;
191 } else {
192 return m_isTabletMode;
193 }
194 }
195}
196
198{
199 return m_isTabletMode;
200}
201
203{
204 if (m_isTabletMode == tablet) {
205 return;
206 }
207
208 const bool oldTabletMode = effectiveTabletMode();
209 m_isTabletMode = tablet;
210 if (effectiveTabletMode() != oldTabletMode) {
212 }
213}
214
216{
217 if (m_detecting == detecting) {
218 return;
219 }
220
221 m_detecting = detecting;
222 Q_EMIT tabletModeAvailableChanged(isTabletModeAvailable());
223}
224
229
230} // namespace KWin
231
232#include "moc_tabletmodemanager.cpp"
virtual bool isPointer() const =0
virtual bool isTouch() const =0
virtual bool isTabletModeSwitch() const =0
void installInputEventSpy(InputEventSpy *spy)
void deviceAdded(InputDevice *device)
QList< InputDevice * > devices() const
Definition input.h:554
void hasTabletModeSwitchChanged(bool set)
void deviceRemoved(InputDevice *device)
State state() const
InputDevice * device() const
void setTabletModeAvailable(bool detecting)
ConfiguredMode configuredMode() const
void tabletModeChanged(bool tabletMode)
TabletModeSwitchEventSpy(TabletModeManager *parent)
void switchEvent(SwitchEvent *event) override
TabletModeTouchpadRemovedSpy(TabletModeManager *parent)
void refresh(InputDevice *inputDevice)
WaylandServer * waylandServer()
InputRedirection * input()
Definition input.h:549