Changeset View
Changeset View
Standalone View
Standalone View
touch_input.cpp
1 | /******************************************************************** | 1 | /******************************************************************** | ||
---|---|---|---|---|---|
2 | KWin - the KDE window manager | 2 | KWin - the KDE window manager | ||
3 | This file is part of the KDE project. | 3 | This file is part of the KDE project. | ||
4 | 4 | | |||
5 | Copyright (C) 2013, 2016 Martin Gräßlin <mgraesslin@kde.org> | 5 | Copyright (C) 2013, 2016 Martin Gräßlin <mgraesslin@kde.org> | ||
6 | Copyright (C) 2018 Roman Gilg <subdiff@gmail.com> | ||||
6 | 7 | | |||
7 | This program is free software; you can redistribute it and/or modify | 8 | This program is free software; you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | 9 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation; either version 2 of the License, or | 10 | the Free Software Foundation; either version 2 of the License, or | ||
10 | (at your option) any later version. | 11 | (at your option) any later version. | ||
11 | 12 | | |||
12 | This program is distributed in the hope that it will be useful, | 13 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | 16 | GNU General Public License for more details. | ||
16 | 17 | | |||
17 | You should have received a copy of the GNU General Public License | 18 | You should have received a copy of the GNU General Public License | ||
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. | 19 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | *********************************************************************/ | 20 | *********************************************************************/ | ||
20 | #include "touch_input.h" | 21 | #include "touch_input.h" | ||
21 | #include "abstract_client.h" | 22 | #include "abstract_client.h" | ||
22 | #include "input.h" | 23 | #include "input.h" | ||
24 | #include "pointer_input.h" | ||||
23 | #include "input_event_spy.h" | 25 | #include "input_event_spy.h" | ||
24 | #include "toplevel.h" | 26 | #include "toplevel.h" | ||
25 | #include "wayland_server.h" | 27 | #include "wayland_server.h" | ||
26 | #include "workspace.h" | 28 | #include "workspace.h" | ||
27 | #include "decorations/decoratedclient.h" | 29 | #include "decorations/decoratedclient.h" | ||
28 | // KDecoration | 30 | // KDecoration | ||
29 | #include <KDecoration2/Decoration> | 31 | #include <KDecoration2/Decoration> | ||
30 | // KWayland | 32 | // KWayland | ||
Show All 11 Lines | 43 | TouchInputRedirection::TouchInputRedirection(InputRedirection *parent) | |||
42 | : InputDeviceHandler(parent) | 44 | : InputDeviceHandler(parent) | ||
43 | { | 45 | { | ||
44 | } | 46 | } | ||
45 | 47 | | |||
46 | TouchInputRedirection::~TouchInputRedirection() = default; | 48 | TouchInputRedirection::~TouchInputRedirection() = default; | ||
47 | 49 | | |||
48 | void TouchInputRedirection::init() | 50 | void TouchInputRedirection::init() | ||
49 | { | 51 | { | ||
50 | Q_ASSERT(!m_inited); | 52 | Q_ASSERT(!inited()); | ||
51 | m_inited = true; | 53 | setInited(true); | ||
54 | InputDeviceHandler::init(); | ||||
52 | 55 | | |||
53 | if (waylandServer()->hasScreenLockerIntegration()) { | 56 | if (waylandServer()->hasScreenLockerIntegration()) { | ||
54 | connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged, this, | 57 | connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged, this, | ||
55 | [this] { | 58 | [this] { | ||
56 | cancel(); | 59 | cancel(); | ||
57 | // position doesn't matter | 60 | // position doesn't matter | ||
58 | update(); | 61 | update(); | ||
59 | } | 62 | } | ||
60 | ); | 63 | ); | ||
61 | } | 64 | } | ||
62 | connect(workspace(), &QObject::destroyed, this, [this] { m_inited = false; }); | 65 | connect(workspace(), &QObject::destroyed, this, [this] { setInited(false); }); | ||
63 | connect(waylandServer(), &QObject::destroyed, this, [this] { m_inited = false; }); | 66 | connect(waylandServer(), &QObject::destroyed, this, [this] { setInited(false); }); | ||
64 | } | 67 | } | ||
65 | 68 | | |||
66 | void TouchInputRedirection::update(const QPointF &pos) | 69 | bool TouchInputRedirection::focusUpdatesBlocked() | ||
67 | { | 70 | { | ||
68 | if (!m_inited) { | 71 | if (!inited()) { | ||
69 | return; | 72 | return true; | ||
70 | } | 73 | } | ||
71 | if (m_windowUpdatedInCycle) { | 74 | if (m_windowUpdatedInCycle) { | ||
72 | return; | 75 | return true; | ||
73 | } | 76 | } | ||
74 | m_windowUpdatedInCycle = true; | 77 | m_windowUpdatedInCycle = true; | ||
78 | if (m_touches > 0) { | ||||
79 | // first touch defines focus | ||||
80 | return true; | ||||
81 | } | ||||
82 | return false; | ||||
83 | } | ||||
84 | | ||||
85 | void TouchInputRedirection::focusUpdate(Toplevel *focusOld, Toplevel *focusNow) | ||||
86 | { | ||||
75 | // TODO: handle pointer grab aka popups | 87 | // TODO: handle pointer grab aka popups | ||
76 | Toplevel *t = input()->findToplevel(pos.toPoint()); | 88 | | ||
77 | auto oldWindow = window(); | 89 | if (AbstractClient *ac = qobject_cast<AbstractClient*>(focusOld)) { | ||
78 | updateInternalWindow(pos); | 90 | ac->leaveEvent(); | ||
79 | if (!internalWindow()) { | | |||
80 | updateDecoration(t, pos); | | |||
81 | } else { | | |||
82 | // TODO: send hover leave to decoration | | |||
83 | if (decoration()) { | | |||
84 | decoration()->client()->leaveEvent(); | | |||
85 | } | | |||
86 | clearDecoration(); | | |||
87 | } | | |||
88 | if (decoration() || internalWindow()) { | | |||
89 | t = nullptr; | | |||
90 | } else if (!decoration()) { | | |||
91 | m_decorationId = -1; | | |||
92 | } else if (!internalWindow()) { | | |||
93 | m_internalId = -1; | | |||
94 | } | 91 | } | ||
95 | if (!oldWindow.isNull() && t == oldWindow.data()) { | 92 | disconnect(m_focusGeometryConnection); | ||
96 | return; | 93 | m_focusGeometryConnection = QMetaObject::Connection(); | ||
94 | | ||||
95 | if (AbstractClient *ac = qobject_cast<AbstractClient*>(focusNow)) { | ||||
96 | ac->enterEvent(m_lastPosition.toPoint()); | ||||
97 | workspace()->updateFocusMousePosition(m_lastPosition.toPoint()); | ||||
97 | } | 98 | } | ||
99 | | ||||
98 | auto seat = waylandServer()->seat(); | 100 | auto seat = waylandServer()->seat(); | ||
99 | // disconnect old surface | 101 | if (!focusNow || !focusNow->surface() || decoration()) { | ||
100 | if (oldWindow) { | 102 | // no new surface or internal window or on decoration -> cleanup | ||
101 | disconnect(m_windowGeometryConnection); | 103 | seat->setFocusedTouchSurface(nullptr); | ||
102 | m_windowGeometryConnection = QMetaObject::Connection(); | 104 | return; | ||
103 | } | 105 | } | ||
104 | if (t && t->surface()) { | 106 | | ||
107 | // TODO: invalidate pointer focus? | ||||
108 | | ||||
105 | // FIXME: add input transformation API to KWayland::Server::SeatInterface for touch input | 109 | // FIXME: add input transformation API to KWayland::Server::SeatInterface for touch input | ||
106 | seat->setFocusedTouchSurface(t->surface(), -1 * t->inputTransformation().map(t->pos()) + t->pos()); | 110 | seat->setFocusedTouchSurface(focusNow->surface(), -1 * focusNow->inputTransformation().map(focusNow->pos()) + focusNow->pos()); | ||
107 | m_windowGeometryConnection = connect(t, &Toplevel::geometryChanged, this, | 111 | m_focusGeometryConnection = connect(focusNow, &Toplevel::geometryChanged, this, | ||
108 | [this] { | 112 | [this] { | ||
109 | if (window().isNull()) { | 113 | if (focus().isNull()) { | ||
110 | return; | 114 | return; | ||
111 | } | 115 | } | ||
112 | auto seat = waylandServer()->seat(); | 116 | auto seat = waylandServer()->seat(); | ||
113 | if (window().data()->surface() != seat->focusedTouchSurface()) { | 117 | if (focus().data()->surface() != seat->focusedTouchSurface()) { | ||
114 | return; | 118 | return; | ||
115 | } | 119 | } | ||
116 | auto t = window().data(); | 120 | seat->setFocusedTouchSurfacePosition(-1 * focus()->inputTransformation().map(focus()->pos()) + focus()->pos()); | ||
117 | seat->setFocusedTouchSurfacePosition(-1 * t->inputTransformation().map(t->pos()) + t->pos()); | | |||
118 | } | 121 | } | ||
119 | ); | 122 | ); | ||
120 | } else { | | |||
121 | seat->setFocusedTouchSurface(nullptr); | | |||
122 | t = nullptr; | | |||
123 | } | 123 | } | ||
124 | if (!t) { | 124 | | ||
125 | setWindow(); | 125 | void TouchInputRedirection::cleanupInternalWindow(QWindow *old, QWindow *now) | ||
126 | return; | 126 | { | ||
127 | Q_UNUSED(old); | ||||
128 | Q_UNUSED(now); | ||||
129 | | ||||
130 | // nothing to do | ||||
131 | } | ||||
132 | | ||||
133 | void TouchInputRedirection::cleanupDecoration(Decoration::DecoratedClientImpl *old, Decoration::DecoratedClientImpl *now) | ||||
134 | { | ||||
135 | Q_UNUSED(now); | ||||
136 | | ||||
137 | if (old) { | ||||
138 | // send leave event to old decoration | ||||
139 | QHoverEvent event(QEvent::HoverLeave, QPointF(), QPointF()); | ||||
140 | QCoreApplication::instance()->sendEvent(old->decoration(), &event); | ||||
127 | } | 141 | } | ||
128 | setWindow(t); | | |||
129 | } | 142 | } | ||
130 | 143 | | |||
131 | void TouchInputRedirection::insertId(quint32 internalId, qint32 kwaylandId) | 144 | void TouchInputRedirection::insertId(quint32 internalId, qint32 kwaylandId) | ||
132 | { | 145 | { | ||
133 | m_idMapper.insert(internalId, kwaylandId); | 146 | m_idMapper.insert(internalId, kwaylandId); | ||
134 | } | 147 | } | ||
135 | 148 | | |||
136 | qint32 TouchInputRedirection::mappedId(quint32 internalId) | 149 | qint32 TouchInputRedirection::mappedId(quint32 internalId) | ||
137 | { | 150 | { | ||
138 | auto it = m_idMapper.constFind(internalId); | 151 | auto it = m_idMapper.constFind(internalId); | ||
139 | if (it != m_idMapper.constEnd()) { | 152 | if (it != m_idMapper.constEnd()) { | ||
140 | return it.value(); | 153 | return it.value(); | ||
141 | } | 154 | } | ||
142 | return -1; | 155 | return -1; | ||
143 | } | 156 | } | ||
144 | 157 | | |||
145 | void TouchInputRedirection::removeId(quint32 internalId) | 158 | void TouchInputRedirection::removeId(quint32 internalId) | ||
146 | { | 159 | { | ||
147 | m_idMapper.remove(internalId); | 160 | m_idMapper.remove(internalId); | ||
148 | } | 161 | } | ||
149 | 162 | | |||
150 | void TouchInputRedirection::processDown(qint32 id, const QPointF &pos, quint32 time, LibInput::Device *device) | 163 | void TouchInputRedirection::processDown(qint32 id, const QPointF &pos, quint32 time, LibInput::Device *device) | ||
151 | { | 164 | { | ||
152 | Q_UNUSED(device) | 165 | Q_UNUSED(device) | ||
153 | if (!m_inited) { | 166 | if (!inited()) { | ||
154 | return; | 167 | return; | ||
155 | } | 168 | } | ||
169 | m_lastPosition = pos; | ||||
156 | m_windowUpdatedInCycle = false; | 170 | m_windowUpdatedInCycle = false; | ||
171 | if (m_touches == 0) { | ||||
172 | update(); | ||||
173 | } | ||||
174 | m_touches++; | ||||
157 | input()->processSpies(std::bind(&InputEventSpy::touchDown, std::placeholders::_1, id, pos, time)); | 175 | input()->processSpies(std::bind(&InputEventSpy::touchDown, std::placeholders::_1, id, pos, time)); | ||
158 | input()->processFilters(std::bind(&InputEventFilter::touchDown, std::placeholders::_1, id, pos, time)); | 176 | input()->processFilters(std::bind(&InputEventFilter::touchDown, std::placeholders::_1, id, pos, time)); | ||
159 | m_windowUpdatedInCycle = false; | 177 | m_windowUpdatedInCycle = false; | ||
160 | } | 178 | } | ||
161 | 179 | | |||
162 | void TouchInputRedirection::processUp(qint32 id, quint32 time, LibInput::Device *device) | 180 | void TouchInputRedirection::processUp(qint32 id, quint32 time, LibInput::Device *device) | ||
163 | { | 181 | { | ||
164 | Q_UNUSED(device) | 182 | Q_UNUSED(device) | ||
165 | if (!m_inited) { | 183 | if (!inited()) { | ||
166 | return; | 184 | return; | ||
167 | } | 185 | } | ||
168 | m_windowUpdatedInCycle = false; | 186 | m_windowUpdatedInCycle = false; | ||
169 | input()->processSpies(std::bind(&InputEventSpy::touchUp, std::placeholders::_1, id, time)); | 187 | input()->processSpies(std::bind(&InputEventSpy::touchUp, std::placeholders::_1, id, time)); | ||
170 | input()->processFilters(std::bind(&InputEventFilter::touchUp, std::placeholders::_1, id, time)); | 188 | input()->processFilters(std::bind(&InputEventFilter::touchUp, std::placeholders::_1, id, time)); | ||
171 | m_windowUpdatedInCycle = false; | 189 | m_windowUpdatedInCycle = false; | ||
190 | m_touches--; | ||||
191 | if (m_touches == 0) { | ||||
192 | update(); | ||||
193 | } | ||||
172 | } | 194 | } | ||
173 | 195 | | |||
174 | void TouchInputRedirection::processMotion(qint32 id, const QPointF &pos, quint32 time, LibInput::Device *device) | 196 | void TouchInputRedirection::processMotion(qint32 id, const QPointF &pos, quint32 time, LibInput::Device *device) | ||
175 | { | 197 | { | ||
176 | Q_UNUSED(device) | 198 | Q_UNUSED(device) | ||
177 | if (!m_inited) { | 199 | if (!inited()) { | ||
178 | return; | 200 | return; | ||
179 | } | 201 | } | ||
202 | m_lastPosition = pos; | ||||
180 | m_windowUpdatedInCycle = false; | 203 | m_windowUpdatedInCycle = false; | ||
181 | input()->processSpies(std::bind(&InputEventSpy::touchMotion, std::placeholders::_1, id, pos, time)); | 204 | input()->processSpies(std::bind(&InputEventSpy::touchMotion, std::placeholders::_1, id, pos, time)); | ||
182 | input()->processFilters(std::bind(&InputEventFilter::touchMotion, std::placeholders::_1, id, pos, time)); | 205 | input()->processFilters(std::bind(&InputEventFilter::touchMotion, std::placeholders::_1, id, pos, time)); | ||
183 | m_windowUpdatedInCycle = false; | 206 | m_windowUpdatedInCycle = false; | ||
184 | } | 207 | } | ||
185 | 208 | | |||
186 | void TouchInputRedirection::cancel() | 209 | void TouchInputRedirection::cancel() | ||
187 | { | 210 | { | ||
188 | if (!m_inited) { | 211 | if (!inited()) { | ||
189 | return; | 212 | return; | ||
190 | } | 213 | } | ||
191 | waylandServer()->seat()->cancelTouchSequence(); | 214 | waylandServer()->seat()->cancelTouchSequence(); | ||
192 | m_idMapper.clear(); | 215 | m_idMapper.clear(); | ||
193 | } | 216 | } | ||
194 | 217 | | |||
195 | void TouchInputRedirection::frame() | 218 | void TouchInputRedirection::frame() | ||
196 | { | 219 | { | ||
197 | if (!m_inited) { | 220 | if (!inited()) { | ||
198 | return; | 221 | return; | ||
199 | } | 222 | } | ||
200 | waylandServer()->seat()->touchFrame(); | 223 | waylandServer()->seat()->touchFrame(); | ||
201 | } | 224 | } | ||
202 | 225 | | |||
203 | } | 226 | } |