blob: e9c6ad5f0a559fe15c48f613ac129098d5b423a7 [file] [log] [blame]
Garfield Tane84e6f92019-08-29 17:28:41 -07001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Siarhei Vishniakou6e1e9872022-11-08 17:51:35 -080017#include <android-base/stringprintf.h>
chaviw98318de2021-05-19 16:45:23 -050018#include <gui/WindowInfo.h>
Garfield Tane84e6f92019-08-29 17:28:41 -070019
20#include "InputTarget.h"
Garfield Tane84e6f92019-08-29 17:28:41 -070021#include "TouchState.h"
22
Siarhei Vishniakou253f4642022-11-09 13:42:06 -080023using namespace android::ftl::flag_operators;
Siarhei Vishniakou6e1e9872022-11-08 17:51:35 -080024using android::base::StringPrintf;
chaviw98318de2021-05-19 16:45:23 -050025using android::gui::WindowInfo;
26using android::gui::WindowInfoHandle;
Garfield Tane84e6f92019-08-29 17:28:41 -070027
28namespace android::inputdispatcher {
29
Garfield Tane84e6f92019-08-29 17:28:41 -070030void TouchState::reset() {
Prabir Pradhane680f9b2022-02-04 04:24:00 -080031 *this = TouchState();
Garfield Tane84e6f92019-08-29 17:28:41 -070032}
33
Siarhei Vishniakoub581f7f2022-12-07 20:23:06 +000034void TouchState::removeTouchedPointer(int32_t pointerId) {
35 for (TouchedWindow& touchedWindow : windows) {
36 touchedWindow.pointerIds.clearBit(pointerId);
Siarhei Vishniakou060f82b2023-01-27 06:39:14 -080037 touchedWindow.pilferedPointerIds.reset(pointerId);
Siarhei Vishniakoub581f7f2022-12-07 20:23:06 +000038 }
39}
40
Siarhei Vishniakou0026b4c2022-11-10 19:33:29 -080041void TouchState::removeTouchedPointerFromWindow(
42 int32_t pointerId, const sp<android::gui::WindowInfoHandle>& windowHandle) {
43 for (TouchedWindow& touchedWindow : windows) {
44 if (touchedWindow.windowHandle == windowHandle) {
45 touchedWindow.pointerIds.clearBit(pointerId);
Siarhei Vishniakou060f82b2023-01-27 06:39:14 -080046 touchedWindow.pilferedPointerIds.reset(pointerId);
Siarhei Vishniakou0026b4c2022-11-10 19:33:29 -080047 return;
48 }
49 }
50}
51
Siarhei Vishniakoub581f7f2022-12-07 20:23:06 +000052void TouchState::clearHoveringPointers() {
53 for (TouchedWindow& touchedWindow : windows) {
54 touchedWindow.clearHoveringPointers();
55 }
56}
57
58void TouchState::clearWindowsWithoutPointers() {
59 std::erase_if(windows, [](const TouchedWindow& w) {
60 return w.pointerIds.isEmpty() && !w.hasHoveringPointers();
61 });
62}
63
Siarhei Vishniakou253f4642022-11-09 13:42:06 -080064void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle,
65 ftl::Flags<InputTarget::Flags> targetFlags, BitSet32 pointerIds,
66 std::optional<nsecs_t> eventTime) {
Siarhei Vishniakou5cee1e32022-11-29 12:35:39 -080067 for (TouchedWindow& touchedWindow : windows) {
Siarhei Vishniakoub581f7f2022-12-07 20:23:06 +000068 // We do not compare windows by token here because two windows that share the same token
69 // may have a different transform
Garfield Tane84e6f92019-08-29 17:28:41 -070070 if (touchedWindow.windowHandle == windowHandle) {
71 touchedWindow.targetFlags |= targetFlags;
Siarhei Vishniakou253f4642022-11-09 13:42:06 -080072 if (targetFlags.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT)) {
73 touchedWindow.targetFlags.clear(InputTarget::Flags::DISPATCH_AS_IS);
Garfield Tane84e6f92019-08-29 17:28:41 -070074 }
Vaibhav Devmurari882bd9b2022-06-23 14:54:54 +000075 // For cases like hover enter/exit or DISPATCH_AS_OUTSIDE a touch window might not have
76 // downTime set initially. Need to update existing window when an pointer is down for
77 // the window.
Garfield Tane84e6f92019-08-29 17:28:41 -070078 touchedWindow.pointerIds.value |= pointerIds.value;
Vaibhav Devmurari882bd9b2022-06-23 14:54:54 +000079 if (!touchedWindow.firstDownTimeInTarget.has_value()) {
80 touchedWindow.firstDownTimeInTarget = eventTime;
81 }
Garfield Tane84e6f92019-08-29 17:28:41 -070082 return;
83 }
84 }
Garfield Tane84e6f92019-08-29 17:28:41 -070085 TouchedWindow touchedWindow;
86 touchedWindow.windowHandle = windowHandle;
87 touchedWindow.targetFlags = targetFlags;
88 touchedWindow.pointerIds = pointerIds;
Vaibhav Devmurari882bd9b2022-06-23 14:54:54 +000089 touchedWindow.firstDownTimeInTarget = eventTime;
Garfield Tane84e6f92019-08-29 17:28:41 -070090 windows.push_back(touchedWindow);
91}
92
Siarhei Vishniakoub581f7f2022-12-07 20:23:06 +000093void TouchState::addHoveringPointerToWindow(const sp<WindowInfoHandle>& windowHandle,
94 int32_t hoveringDeviceId, int32_t hoveringPointerId) {
95 for (TouchedWindow& touchedWindow : windows) {
96 if (touchedWindow.windowHandle == windowHandle) {
97 touchedWindow.addHoveringPointer(hoveringDeviceId, hoveringPointerId);
98 return;
99 }
100 }
101
102 TouchedWindow touchedWindow;
103 touchedWindow.windowHandle = windowHandle;
104 touchedWindow.addHoveringPointer(hoveringDeviceId, hoveringPointerId);
105 windows.push_back(touchedWindow);
106}
107
Garfield Tane84e6f92019-08-29 17:28:41 -0700108void TouchState::removeWindowByToken(const sp<IBinder>& token) {
109 for (size_t i = 0; i < windows.size(); i++) {
110 if (windows[i].windowHandle->getToken() == token) {
111 windows.erase(windows.begin() + i);
112 return;
113 }
114 }
115}
116
Sam Dubeyf886dec2023-01-27 13:28:19 +0000117void TouchState::filterNonAsIsTouchWindows() {
118 for (size_t i = 0; i < windows.size();) {
119 TouchedWindow& window = windows[i];
120 if (window.targetFlags.any(InputTarget::Flags::DISPATCH_AS_IS |
121 InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER)) {
122 window.targetFlags.clear(InputTarget::DISPATCH_MASK);
123 window.targetFlags |= InputTarget::Flags::DISPATCH_AS_IS;
124 i += 1;
125 } else {
126 windows.erase(windows.begin() + i);
127 }
128 }
129}
130
Vaibhav Devmurariff798f32022-05-09 23:45:04 +0000131void TouchState::cancelPointersForWindowsExcept(const BitSet32 pointerIds,
132 const sp<IBinder>& token) {
133 if (pointerIds.isEmpty()) return;
134 std::for_each(windows.begin(), windows.end(), [&pointerIds, &token](TouchedWindow& w) {
135 if (w.windowHandle->getToken() != token) {
136 w.pointerIds &= BitSet32(~pointerIds.value);
137 }
138 });
139 std::erase_if(windows, [](const TouchedWindow& w) { return w.pointerIds.isEmpty(); });
140}
141
Siarhei Vishniakou060f82b2023-01-27 06:39:14 -0800142/**
143 * For any pointer that's being pilfered, remove it from all of the other windows that currently
144 * aren't pilfering it. For example, if we determined that pointer 1 is going to both window A and
145 * window B, but window A is currently pilfering pointer 1, then pointer 1 should not go to window
146 * B.
147 */
148void TouchState::cancelPointersForNonPilferingWindows() {
149 // First, find all pointers that are being pilfered, across all windows
150 std::bitset<MAX_POINTERS> allPilferedPointerIds;
151 std::for_each(windows.begin(), windows.end(), [&allPilferedPointerIds](const TouchedWindow& w) {
152 allPilferedPointerIds |= w.pilferedPointerIds;
153 });
154
155 // Optimization: most of the time, pilfering does not occur
156 if (allPilferedPointerIds.none()) return;
157
158 // Now, remove all pointers from every window that's being pilfered by other windows.
159 // For example, if window A is pilfering pointer 1 (only), and window B is pilfering pointer 2
160 // (only), the remove pointer 2 from window A and pointer 1 from window B. Usually, the set of
161 // pilfered pointers will be disjoint across all windows, but there's no reason to cause that
162 // limitation here.
163 std::for_each(windows.begin(), windows.end(), [&allPilferedPointerIds](TouchedWindow& w) {
164 std::bitset<MAX_POINTERS> pilferedByOtherWindows =
165 w.pilferedPointerIds ^ allPilferedPointerIds;
166 // TODO(b/211379801) : convert pointerIds to use std::bitset, which would allow us to
167 // replace the loop below with a bitwise operation. Currently, the XOR operation above is
168 // redundant, but is done to make the code more explicit / easier to convert later.
169 for (std::size_t i = 0; i < pilferedByOtherWindows.size(); i++) {
170 if (pilferedByOtherWindows.test(i) && !w.pilferedPointerIds.test(i)) {
171 // Pointer is pilfered by other windows, but not by this one! Remove it from here.
172 // We could call 'removeTouchedPointerFromWindow' here, but it's faster to directly
173 // manipulate it.
174 w.pointerIds.clearBit(i);
175 }
Vaibhav Devmurariff798f32022-05-09 23:45:04 +0000176 }
177 });
178 std::erase_if(windows, [](const TouchedWindow& w) { return w.pointerIds.isEmpty(); });
179}
180
chaviw98318de2021-05-19 16:45:23 -0500181sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle() const {
Garfield Tane84e6f92019-08-29 17:28:41 -0700182 for (size_t i = 0; i < windows.size(); i++) {
183 const TouchedWindow& window = windows[i];
Siarhei Vishniakou253f4642022-11-09 13:42:06 -0800184 if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) {
Garfield Tane84e6f92019-08-29 17:28:41 -0700185 return window.windowHandle;
186 }
187 }
188 return nullptr;
189}
190
191bool TouchState::isSlippery() const {
192 // Must have exactly one foreground window.
193 bool haveSlipperyForegroundWindow = false;
194 for (const TouchedWindow& window : windows) {
Siarhei Vishniakou253f4642022-11-09 13:42:06 -0800195 if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) {
Garfield Tane84e6f92019-08-29 17:28:41 -0700196 if (haveSlipperyForegroundWindow ||
Prabir Pradhan4d5c52f2022-01-31 08:52:10 -0800197 !window.windowHandle->getInfo()->inputConfig.test(
198 WindowInfo::InputConfig::SLIPPERY)) {
Garfield Tane84e6f92019-08-29 17:28:41 -0700199 return false;
200 }
201 haveSlipperyForegroundWindow = true;
202 }
203 }
204 return haveSlipperyForegroundWindow;
205}
206
Siarhei Vishniakouca205502021-07-16 21:31:58 +0000207sp<WindowInfoHandle> TouchState::getWallpaperWindow() const {
208 for (size_t i = 0; i < windows.size(); i++) {
209 const TouchedWindow& window = windows[i];
Prabir Pradhan4d5c52f2022-01-31 08:52:10 -0800210 if (window.windowHandle->getInfo()->inputConfig.test(
211 gui::WindowInfo::InputConfig::IS_WALLPAPER)) {
Siarhei Vishniakouca205502021-07-16 21:31:58 +0000212 return window.windowHandle;
213 }
214 }
215 return nullptr;
216}
217
Siarhei Vishniakou0026b4c2022-11-10 19:33:29 -0800218const TouchedWindow& TouchState::getTouchedWindow(const sp<WindowInfoHandle>& windowHandle) const {
219 auto it = std::find_if(windows.begin(), windows.end(),
220 [&](const TouchedWindow& w) { return w.windowHandle == windowHandle; });
221 LOG_ALWAYS_FATAL_IF(it == windows.end(), "Could not find %s", windowHandle->getName().c_str());
222 return *it;
223}
224
Siarhei Vishniakou3ad385b2022-11-04 10:09:53 -0700225bool TouchState::isDown() const {
226 return std::any_of(windows.begin(), windows.end(),
227 [](const TouchedWindow& window) { return !window.pointerIds.isEmpty(); });
228}
229
Siarhei Vishniakoub581f7f2022-12-07 20:23:06 +0000230std::set<sp<WindowInfoHandle>> TouchState::getWindowsWithHoveringPointer(int32_t hoveringDeviceId,
231 int32_t pointerId) const {
232 std::set<sp<WindowInfoHandle>> out;
233 for (const TouchedWindow& window : windows) {
234 if (window.hasHoveringPointer(hoveringDeviceId, pointerId)) {
235 out.insert(window.windowHandle);
236 }
237 }
238 return out;
239}
240
241void TouchState::removeHoveringPointer(int32_t hoveringDeviceId, int32_t hoveringPointerId) {
242 for (TouchedWindow& window : windows) {
243 window.removeHoveringPointer(hoveringDeviceId, hoveringPointerId);
244 }
245 std::erase_if(windows, [](const TouchedWindow& w) {
246 return w.pointerIds.isEmpty() && !w.hasHoveringPointers();
247 });
248}
249
Siarhei Vishniakou6e1e9872022-11-08 17:51:35 -0800250std::string TouchState::dump() const {
251 std::string out;
Siarhei Vishniakou0b0374d2022-11-17 17:40:53 -0800252 out += StringPrintf("deviceId=%d, source=%s\n", deviceId,
253 inputEventSourceToString(source).c_str());
Siarhei Vishniakou6e1e9872022-11-08 17:51:35 -0800254 if (!windows.empty()) {
255 out += " Windows:\n";
256 for (size_t i = 0; i < windows.size(); i++) {
257 const TouchedWindow& touchedWindow = windows[i];
258 out += StringPrintf(" %zu : ", i) + touchedWindow.dump();
259 }
260 } else {
261 out += " Windows: <none>\n";
262 }
263 return out;
264}
265
Garfield Tane84e6f92019-08-29 17:28:41 -0700266} // namespace android::inputdispatcher