blob: f8aa62500eef9ab1074287d3a3f3884a4fdb462e [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <gui/WindowInfo.h>
#include "InputTarget.h"
#include "TouchState.h"
using namespace android::ftl::flag_operators;
using android::base::StringPrintf;
using android::gui::WindowInfo;
using android::gui::WindowInfoHandle;
namespace android::inputdispatcher {
void TouchState::reset() {
*this = TouchState();
}
bool TouchState::hasTouchingPointers(DeviceId deviceId) const {
return std::any_of(windows.begin(), windows.end(), [&](const TouchedWindow& window) {
return window.hasTouchingPointers(deviceId);
});
}
void TouchState::removeTouchingPointer(DeviceId deviceId, int32_t pointerId) {
for (TouchedWindow& touchedWindow : windows) {
touchedWindow.removeTouchingPointer(deviceId, pointerId);
}
clearWindowsWithoutPointers();
}
void TouchState::removeTouchingPointerFromWindow(
DeviceId deviceId, int32_t pointerId,
const sp<android::gui::WindowInfoHandle>& windowHandle) {
for (TouchedWindow& touchedWindow : windows) {
if (touchedWindow.windowHandle == windowHandle) {
touchedWindow.removeTouchingPointer(deviceId, pointerId);
clearWindowsWithoutPointers();
return;
}
}
}
void TouchState::clearHoveringPointers(DeviceId deviceId) {
for (TouchedWindow& touchedWindow : windows) {
touchedWindow.removeAllHoveringPointersForDevice(deviceId);
}
clearWindowsWithoutPointers();
}
void TouchState::clearWindowsWithoutPointers() {
std::erase_if(windows, [](const TouchedWindow& w) {
return !w.hasTouchingPointers() && !w.hasHoveringPointers();
});
}
void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle,
InputTarget::DispatchMode dispatchMode,
ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId,
const std::vector<PointerProperties>& touchingPointers,
std::optional<nsecs_t> firstDownTimeInTarget) {
if (touchingPointers.empty()) {
LOG(FATAL) << __func__ << "No pointers specified for " << windowHandle->getName();
return;
}
for (TouchedWindow& touchedWindow : windows) {
// We do not compare windows by token here because two windows that share the same token
// may have a different transform. They will be combined later when we create InputTargets.
// At that point, per-pointer window transform will be considered.
// An alternative design choice here would have been to compare here by token, but to
// store per-pointer transform.
if (touchedWindow.windowHandle == windowHandle) {
touchedWindow.dispatchMode = dispatchMode;
touchedWindow.targetFlags |= targetFlags;
// For cases like hover enter/exit or DISPATCH_AS_OUTSIDE a touch window might not have
// downTime set initially. Need to update existing window when a pointer is down for the
// window.
touchedWindow.addTouchingPointers(deviceId, touchingPointers);
if (firstDownTimeInTarget) {
touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget);
}
return;
}
}
TouchedWindow touchedWindow;
touchedWindow.windowHandle = windowHandle;
touchedWindow.dispatchMode = dispatchMode;
touchedWindow.targetFlags = targetFlags;
touchedWindow.addTouchingPointers(deviceId, touchingPointers);
if (firstDownTimeInTarget) {
touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget);
}
windows.push_back(touchedWindow);
}
void TouchState::addHoveringPointerToWindow(const sp<WindowInfoHandle>& windowHandle,
DeviceId deviceId, const PointerProperties& pointer) {
for (TouchedWindow& touchedWindow : windows) {
if (touchedWindow.windowHandle == windowHandle) {
touchedWindow.addHoveringPointer(deviceId, pointer);
return;
}
}
TouchedWindow touchedWindow;
touchedWindow.windowHandle = windowHandle;
touchedWindow.addHoveringPointer(deviceId, pointer);
windows.push_back(touchedWindow);
}
void TouchState::removeWindowByToken(const sp<IBinder>& token) {
for (size_t i = 0; i < windows.size(); i++) {
if (windows[i].windowHandle->getToken() == token) {
windows.erase(windows.begin() + i);
return;
}
}
}
void TouchState::cancelPointersForWindowsExcept(DeviceId deviceId,
std::bitset<MAX_POINTER_ID + 1> pointerIds,
const sp<IBinder>& token) {
std::for_each(windows.begin(), windows.end(), [&](TouchedWindow& w) {
if (w.windowHandle->getToken() != token) {
w.removeTouchingPointers(deviceId, pointerIds);
}
});
clearWindowsWithoutPointers();
}
/**
* For any pointer that's being pilfered, remove it from all of the other windows that currently
* aren't pilfering it. For example, if we determined that pointer 1 is going to both window A and
* window B, but window A is currently pilfering pointer 1, then pointer 1 should not go to window
* B.
*/
void TouchState::cancelPointersForNonPilferingWindows() {
// First, find all pointers that are being pilfered, across all windows
std::map<DeviceId, std::bitset<MAX_POINTER_ID + 1>> allPilferedPointerIdsByDevice;
for (const TouchedWindow& w : windows) {
for (const auto& [deviceId, pilferedPointerIds] : w.getPilferingPointers()) {
allPilferedPointerIdsByDevice[deviceId] |= pilferedPointerIds;
}
};
// Optimization: most of the time, pilfering does not occur
if (allPilferedPointerIdsByDevice.empty()) return;
// Now, remove all pointers from every window that's being pilfered by other windows.
// For example, if window A is pilfering pointer 1 (only), and window B is pilfering pointer 2
// (only), the remove pointer 2 from window A and pointer 1 from window B. Usually, the set of
// pilfered pointers will be disjoint across all windows, but there's no reason to cause that
// limitation here.
for (const auto& [deviceId, allPilferedPointerIds] : allPilferedPointerIdsByDevice) {
std::for_each(windows.begin(), windows.end(), [&](TouchedWindow& w) {
std::bitset<MAX_POINTER_ID + 1> pilferedByOtherWindows =
w.getPilferingPointers(deviceId) ^ allPilferedPointerIds;
// Remove all pointers pilfered by other windows
w.removeTouchingPointers(deviceId, pilferedByOtherWindows);
});
}
clearWindowsWithoutPointers();
}
sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle() const {
for (size_t i = 0; i < windows.size(); i++) {
const TouchedWindow& window = windows[i];
if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) {
return window.windowHandle;
}
}
return nullptr;
}
bool TouchState::isSlippery() const {
// Must have exactly one foreground window.
bool haveSlipperyForegroundWindow = false;
for (const TouchedWindow& window : windows) {
if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) {
if (haveSlipperyForegroundWindow ||
!window.windowHandle->getInfo()->inputConfig.test(
WindowInfo::InputConfig::SLIPPERY)) {
return false;
}
haveSlipperyForegroundWindow = true;
}
}
return haveSlipperyForegroundWindow;
}
sp<WindowInfoHandle> TouchState::getWallpaperWindow() const {
for (size_t i = 0; i < windows.size(); i++) {
const TouchedWindow& window = windows[i];
if (window.windowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::IS_WALLPAPER)) {
return window.windowHandle;
}
}
return nullptr;
}
const TouchedWindow& TouchState::getTouchedWindow(const sp<WindowInfoHandle>& windowHandle) const {
auto it = std::find_if(windows.begin(), windows.end(),
[&](const TouchedWindow& w) { return w.windowHandle == windowHandle; });
LOG_ALWAYS_FATAL_IF(it == windows.end(), "Could not find %s", windowHandle->getName().c_str());
return *it;
}
bool TouchState::isDown(DeviceId deviceId) const {
return std::any_of(windows.begin(), windows.end(), [&deviceId](const TouchedWindow& window) {
return window.hasTouchingPointers(deviceId);
});
}
bool TouchState::hasHoveringPointers(DeviceId deviceId) const {
return std::any_of(windows.begin(), windows.end(), [&deviceId](const TouchedWindow& window) {
return window.hasHoveringPointers(deviceId);
});
}
bool TouchState::hasActiveStylus() const {
return std::any_of(windows.begin(), windows.end(),
[](const TouchedWindow& window) { return window.hasActiveStylus(); });
}
std::set<sp<WindowInfoHandle>> TouchState::getWindowsWithHoveringPointer(DeviceId deviceId,
int32_t pointerId) const {
std::set<sp<WindowInfoHandle>> out;
for (const TouchedWindow& window : windows) {
if (window.hasHoveringPointer(deviceId, pointerId)) {
out.insert(window.windowHandle);
}
}
return out;
}
void TouchState::removeHoveringPointer(int32_t hoveringDeviceId, int32_t hoveringPointerId) {
for (TouchedWindow& window : windows) {
window.removeHoveringPointer(hoveringDeviceId, hoveringPointerId);
}
clearWindowsWithoutPointers();
}
void TouchState::removeAllPointersForDevice(DeviceId deviceId) {
for (TouchedWindow& window : windows) {
window.removeAllHoveringPointersForDevice(deviceId);
window.removeAllTouchingPointersForDevice(deviceId);
}
clearWindowsWithoutPointers();
}
std::string TouchState::dump() const {
std::string out;
if (!windows.empty()) {
out += " Windows:\n";
for (size_t i = 0; i < windows.size(); i++) {
const TouchedWindow& touchedWindow = windows[i];
out += StringPrintf(" %zu : ", i) + touchedWindow.dump();
}
} else {
out += " Windows: <none>\n";
}
return out;
}
std::ostream& operator<<(std::ostream& out, const TouchState& state) {
out << state.dump();
return out;
}
} // namespace android::inputdispatcher