summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Prabir Pradhan <prabirmsp@google.com> 2024-12-23 22:52:53 +0000
committer Prabir Pradhan <prabirmsp@google.com> 2025-01-06 20:06:35 +0000
commit72c6990eb8e8abc5ca6c2ef7cf69447d4f3613f2 (patch)
tree08c4a7298017b36858ddcb52de829fc2a08217f2
parent2e5d4dd74b5cc042e938e041f6edcfc394e02cb9 (diff)
Use the cloned window's layer stack space for raw coordinates
Use the cloned window's layer stack (screen) space as the raw coordinate space for input going to clones. This ensures the raw coordinates are consistent between the clone and cloned windows while mirroring. Flag: com.android.input.flags.use_cloned_screen_coordinates_as_raw Test: Presubmit Bug: 377846505 Change-Id: I7370ac64585315d9c754eaa1eb0077c8555fc6a9
-rw-r--r--libs/gui/WindowInfo.cpp62
-rw-r--r--libs/gui/include/gui/WindowInfo.h7
-rw-r--r--libs/gui/tests/WindowInfo_test.cpp16
-rw-r--r--libs/input/input_flags.aconfig10
-rw-r--r--services/inputflinger/dispatcher/InputDispatcher.cpp38
-rw-r--r--services/inputflinger/dispatcher/InputDispatcher.h3
-rw-r--r--services/inputflinger/tests/InputDispatcher_test.cpp50
-rw-r--r--services/surfaceflinger/FrontEnd/LayerHierarchy.h2
-rw-r--r--services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp20
9 files changed, 162 insertions, 46 deletions
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index 82d2554340..6a32d55d25 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -59,6 +59,32 @@ std::ostream& operator<<(std::ostream& out, const Region& region) {
return out;
}
+status_t writeTransform(android::Parcel* parcel, const ui::Transform& transform) {
+ return parcel->writeFloat(transform.dsdx()) ?:
+ parcel->writeFloat(transform.dtdx()) ?:
+ parcel->writeFloat(transform.tx()) ?:
+ parcel->writeFloat(transform.dtdy()) ?:
+ parcel->writeFloat(transform.dsdy()) ?:
+ parcel->writeFloat(transform.ty());
+}
+
+status_t readTransform(const android::Parcel* parcel, ui::Transform& transform) {
+ float dsdx, dtdx, tx, dtdy, dsdy, ty;
+
+ const status_t status = parcel->readFloat(&dsdx) ?:
+ parcel->readFloat(&dtdx) ?:
+ parcel->readFloat(&tx) ?:
+ parcel->readFloat(&dtdy) ?:
+ parcel->readFloat(&dsdy) ?:
+ parcel->readFloat(&ty);
+ if (status != OK) {
+ return status;
+ }
+
+ transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
+ return OK;
+}
+
} // namespace
void WindowInfo::setInputConfig(ftl::Flags<InputConfig> config, bool value) {
@@ -135,12 +161,7 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const {
parcel->writeInt32(surfaceInset) ?:
parcel->writeFloat(globalScaleFactor) ?:
parcel->writeFloat(alpha) ?:
- parcel->writeFloat(transform.dsdx()) ?:
- parcel->writeFloat(transform.dtdx()) ?:
- parcel->writeFloat(transform.tx()) ?:
- parcel->writeFloat(transform.dtdy()) ?:
- parcel->writeFloat(transform.dsdy()) ?:
- parcel->writeFloat(transform.ty()) ?:
+ writeTransform(parcel, transform) ?:
parcel->writeInt32(static_cast<int32_t>(touchOcclusionMode)) ?:
parcel->writeInt32(ownerPid.val()) ?:
parcel->writeInt32(ownerUid.val()) ?:
@@ -153,8 +174,12 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const {
parcel->writeStrongBinder(touchableRegionCropHandle.promote()) ?:
parcel->writeStrongBinder(windowToken) ?:
parcel->writeStrongBinder(focusTransferTarget) ?:
- parcel->writeBool(canOccludePresentation);
+ parcel->writeBool(canOccludePresentation) ?:
+ parcel->writeBool(cloneLayerStackTransform.has_value());
// clang-format on
+ if (cloneLayerStackTransform) {
+ status = status ?: writeTransform(parcel, *cloneLayerStackTransform);
+ }
return status;
}
@@ -174,10 +199,10 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) {
return status;
}
- float dsdx, dtdx, tx, dtdy, dsdy, ty;
int32_t lpFlags, lpType, touchOcclusionModeInt, inputConfigInt, ownerPidInt, ownerUidInt,
displayIdInt;
sp<IBinder> touchableRegionCropHandleSp;
+ bool hasCloneLayerStackTransform = false;
// clang-format off
status = parcel->readInt32(&lpFlags) ?:
@@ -188,12 +213,7 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) {
parcel->readInt32(&surfaceInset) ?:
parcel->readFloat(&globalScaleFactor) ?:
parcel->readFloat(&alpha) ?:
- parcel->readFloat(&dsdx) ?:
- parcel->readFloat(&dtdx) ?:
- parcel->readFloat(&tx) ?:
- parcel->readFloat(&dtdy) ?:
- parcel->readFloat(&dsdy) ?:
- parcel->readFloat(&ty) ?:
+ readTransform(parcel, /*byRef*/ transform) ?:
parcel->readInt32(&touchOcclusionModeInt) ?:
parcel->readInt32(&ownerPidInt) ?:
parcel->readInt32(&ownerUidInt) ?:
@@ -206,8 +226,8 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) {
parcel->readNullableStrongBinder(&touchableRegionCropHandleSp) ?:
parcel->readNullableStrongBinder(&windowToken) ?:
parcel->readNullableStrongBinder(&focusTransferTarget) ?:
- parcel->readBool(&canOccludePresentation);
-
+ parcel->readBool(&canOccludePresentation)?:
+ parcel->readBool(&hasCloneLayerStackTransform);
// clang-format on
if (status != OK) {
@@ -216,7 +236,6 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) {
layoutParamsFlags = ftl::Flags<Flag>(lpFlags);
layoutParamsType = static_cast<Type>(lpType);
- transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
touchOcclusionMode = static_cast<TouchOcclusionMode>(touchOcclusionModeInt);
inputConfig = ftl::Flags<InputConfig>(inputConfigInt);
ownerPid = Pid{ownerPidInt};
@@ -224,6 +243,15 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) {
touchableRegionCropHandle = touchableRegionCropHandleSp;
displayId = ui::LogicalDisplayId{displayIdInt};
+ cloneLayerStackTransform =
+ hasCloneLayerStackTransform ? std::make_optional<ui::Transform>() : std::nullopt;
+ if (cloneLayerStackTransform) {
+ status = readTransform(parcel, /*byRef*/ *cloneLayerStackTransform);
+ if (status != OK) {
+ return status;
+ }
+ }
+
return OK;
}
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index eb3be5588a..b48e696b4a 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -220,9 +220,14 @@ struct WindowInfo : public Parcelable {
// An alpha of 1.0 means fully opaque and 0.0 means fully transparent.
float alpha;
- // Transform applied to individual windows.
+ // Transform applied to individual windows for input.
+ // Maps display coordinates to the window's input coordinate space.
ui::Transform transform;
+ // Transform applied to get to the layer stack space of the cloned window for input.
+ // Maps display coordinates of the clone window to the layer stack space of the cloned window.
+ std::optional<ui::Transform> cloneLayerStackTransform;
+
/*
* This is filled in by the WM relative to the frame and then translated
* to absolute coordinates by SurfaceFlinger once the frame is computed.
diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp
index ce22082a9f..e3f9a07b34 100644
--- a/libs/gui/tests/WindowInfo_test.cpp
+++ b/libs/gui/tests/WindowInfo_test.cpp
@@ -40,7 +40,18 @@ TEST(WindowInfo, ParcellingWithoutToken) {
ASSERT_EQ(OK, i.writeToParcel(&p));
p.setDataPosition(0);
i2.readFromParcel(&p);
- ASSERT_TRUE(i2.token == nullptr);
+ ASSERT_EQ(i2.token, nullptr);
+}
+
+TEST(WindowInfo, ParcellingWithoutCloneTransform) {
+ WindowInfo i, i2;
+ i.cloneLayerStackTransform.reset();
+
+ Parcel p;
+ ASSERT_EQ(OK, i.writeToParcel(&p));
+ p.setDataPosition(0);
+ i2.readFromParcel(&p);
+ ASSERT_EQ(i2.cloneLayerStackTransform, std::nullopt);
}
TEST(WindowInfo, Parcelling) {
@@ -71,6 +82,8 @@ TEST(WindowInfo, Parcelling) {
i.applicationInfo.token = new BBinder();
i.applicationInfo.dispatchingTimeoutMillis = 0x12345678ABCD;
i.focusTransferTarget = new BBinder();
+ i.cloneLayerStackTransform = ui::Transform();
+ i.cloneLayerStackTransform->set({5, -1, 100, 4, 0, 40, 0, 0, 1});
Parcel p;
i.writeToParcel(&p);
@@ -100,6 +113,7 @@ TEST(WindowInfo, Parcelling) {
ASSERT_EQ(i.touchableRegionCropHandle, i2.touchableRegionCropHandle);
ASSERT_EQ(i.applicationInfo, i2.applicationInfo);
ASSERT_EQ(i.focusTransferTarget, i2.focusTransferTarget);
+ ASSERT_EQ(i.cloneLayerStackTransform, i2.cloneLayerStackTransform);
}
TEST(InputApplicationInfo, Parcelling) {
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index 6cdd249b9e..5da110ef02 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -224,3 +224,13 @@ flag {
description: "Allow cursor to transition across multiple connected displays"
bug: "362719483"
}
+
+flag {
+ name: "use_cloned_screen_coordinates_as_raw"
+ namespace: "input"
+ description: "Use the cloned window's layer stack (screen) space as the raw coordinate space for input going to clones"
+ bug: "377846505"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index bfab666278..28422d636a 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -3013,11 +3013,10 @@ void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHa
windowHandle->getName().c_str());
return;
}
- inputTargets.push_back(createInputTarget(connection, windowHandle, dispatchMode,
- targetFlags,
- mWindowInfos.getDisplayTransform(
- windowHandle->getInfo()->displayId),
- firstDownTimeInTarget));
+ inputTargets.push_back(
+ createInputTarget(connection, windowHandle, dispatchMode, targetFlags,
+ mWindowInfos.getRawTransform(*windowHandle->getInfo()),
+ firstDownTimeInTarget));
it = inputTargets.end() - 1;
}
@@ -3068,11 +3067,10 @@ void InputDispatcher::addPointerWindowTargetLocked(
windowHandle->getName().c_str());
return;
}
- inputTargets.push_back(createInputTarget(connection, windowHandle, dispatchMode,
- targetFlags,
- mWindowInfos.getDisplayTransform(
- windowHandle->getInfo()->displayId),
- firstDownTimeInTarget));
+ inputTargets.push_back(
+ createInputTarget(connection, windowHandle, dispatchMode, targetFlags,
+ mWindowInfos.getRawTransform(*windowHandle->getInfo()),
+ firstDownTimeInTarget));
it = inputTargets.end() - 1;
}
@@ -3104,7 +3102,8 @@ void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>&
for (const Monitor& monitor : selectResponsiveMonitorsLocked(monitorsIt->second)) {
InputTarget target{monitor.connection};
// target.firstDownTimeInTarget is not set for global monitors. It is only required in split
- // touch and global monitoring works as intended even without setting firstDownTimeInTarget
+ // touch and global monitoring works as intended even without setting firstDownTimeInTarget.
+ // Since global monitors don't have windows, use the display transform as the raw transform.
target.rawTransform = mWindowInfos.getDisplayTransform(displayId);
target.setDefaultPointerTransform(target.rawTransform);
inputTargets.push_back(target);
@@ -4291,6 +4290,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
motionEntry.downTime, targets);
} else {
targets.emplace_back(fallbackTarget);
+ // Since we don't have a window, use the display transform as the raw transform.
const ui::Transform displayTransform =
mWindowInfos.getDisplayTransform(motionEntry.displayId);
targets.back().rawTransform = displayTransform;
@@ -4376,6 +4376,7 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked(
targets);
} else {
targets.emplace_back(connection, targetFlags);
+ // Since we don't have a window, use the display transform as the raw transform.
const ui::Transform displayTransform =
mWindowInfos.getDisplayTransform(motionEntry.displayId);
targets.back().rawTransform = displayTransform;
@@ -5289,6 +5290,16 @@ ui::Transform InputDispatcher::DispatcherWindowInfo::getDisplayTransform(
: kIdentityTransform;
}
+ui::Transform InputDispatcher::DispatcherWindowInfo::getRawTransform(
+ const android::gui::WindowInfo& windowInfo) const {
+ // If the window has a cloneLayerStackTransform, always use it as the transform for the "getRaw"
+ // APIs. If not, fall back to using the DisplayInfo transform of the window's display.
+ return (input_flags::use_cloned_screen_coordinates_as_raw() &&
+ windowInfo.cloneLayerStackTransform)
+ ? *windowInfo.cloneLayerStackTransform
+ : getDisplayTransform(windowInfo.displayId);
+}
+
std::string InputDispatcher::DispatcherWindowInfo::dumpDisplayAndWindowInfo() const {
std::string dump;
if (!mWindowHandlesByDisplay.empty()) {
@@ -6567,9 +6578,8 @@ void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime,
createInputTarget(connection, windowHandle,
InputTarget::DispatchMode::AS_IS,
dispatchEntry->targetFlags,
- mWindowInfos.getDisplayTransform(
- windowHandle->getInfo()
- ->displayId),
+ mWindowInfos.getRawTransform(
+ *windowHandle->getInfo()),
downTime));
}
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index fd550dd396..8485b44a03 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -384,6 +384,9 @@ private:
// Get the transform for display, returns Identity-transform if display is missing.
ui::Transform getDisplayTransform(ui::LogicalDisplayId displayId) const;
+ // Get the raw transform to use for motion events going to the given window.
+ ui::Transform getRawTransform(const android::gui::WindowInfo&) const;
+
// Lookup for WindowInfoHandle from token and optionally a display-id. In cases where
// display-id is not provided lookup is done for all displays.
sp<android::gui::WindowInfoHandle> findWindowHandle(
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 3413caa1f0..a5b8e4b0c4 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -6501,19 +6501,47 @@ TEST_F(InputDispatcherDisplayProjectionTest, WindowGetsEventsInCorrectCoordinate
ui::LogicalDisplayId::DEFAULT, {PointF{150, 220}}));
firstWindow->assertNoEvents();
- std::unique_ptr<MotionEvent> event = secondWindow->consumeMotionEvent();
- ASSERT_NE(nullptr, event);
- EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, event->getAction());
+ secondWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN),
+ // Ensure that the events from the "getRaw" API are in logical display
+ // coordinates, which has an x-scale of 2 and y-scale of 4.
+ WithRawCoords(300, 880),
+ // Ensure that the x and y values are in the window's coordinate space.
+ // The left-top of the second window is at (100, 200) in display space, which is
+ // (200, 800) in the logical display space. This will be the origin of the window
+ // space.
+ WithCoords(100, 80)));
+}
- // Ensure that the events from the "getRaw" API are in logical display coordinates.
- EXPECT_EQ(300, event->getRawX(0));
- EXPECT_EQ(880, event->getRawY(0));
+TEST_F(InputDispatcherDisplayProjectionTest, UseCloneLayerStackTransformForRawCoordinates) {
+ SCOPED_FLAG_OVERRIDE(use_cloned_screen_coordinates_as_raw, true);
- // Ensure that the x and y values are in the window's coordinate space.
- // The left-top of the second window is at (100, 200) in display space, which is (200, 800) in
- // the logical display space. This will be the origin of the window space.
- EXPECT_EQ(100, event->getX(0));
- EXPECT_EQ(80, event->getY(0));
+ auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+
+ const std::array<float, 9> matrix = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0.0, 0.0, 1.0};
+ ui::Transform secondDisplayTransform;
+ secondDisplayTransform.set(matrix);
+ addDisplayInfo(SECOND_DISPLAY_ID, secondDisplayTransform);
+
+ // When a clone layer stack transform is provided for a window, we should use that as the
+ // "display transform" for input going to that window.
+ sp<FakeWindowHandle> secondWindowClone = secondWindow->clone(SECOND_DISPLAY_ID);
+ secondWindowClone->editInfo()->cloneLayerStackTransform = ui::Transform();
+ secondWindowClone->editInfo()->cloneLayerStackTransform->set(0.5, 0, 0, 0.25);
+ addWindow(secondWindowClone);
+
+ // Touch down on the clone window, and ensure its raw coordinates use
+ // the clone layer stack transform.
+ mDispatcher->notifyMotion(generateMotionArgs(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ SECOND_DISPLAY_ID, {PointF{150, 220}}));
+ secondWindowClone->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN),
+ // Ensure the x and y coordinates are in the window's coordinate space.
+ // See previous test case for calculation.
+ WithCoords(100, 80),
+ // Ensure the "getRaw" API uses the clone layer stack transform when it is
+ // provided for the window. It has an x-scale of 0.5 and y-scale of 0.25.
+ WithRawCoords(75, 55)));
}
TEST_F(InputDispatcherDisplayProjectionTest, CancelMotionWithCorrectCoordinates) {
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index 47d0041a8b..4fdbae1831 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -102,6 +102,8 @@ public:
// Returns true if the node is a clone.
bool isClone() const { return !mirrorRootIds.empty(); }
+ TraversalPath getClonedFrom() const { return {.id = id, .variant = variant}; }
+
bool operator==(const TraversalPath& other) const {
return id == other.id && mirrorRootIds == other.mirrorRootIds;
}
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 022588deef..d0dc065353 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -16,6 +16,8 @@
// #define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include "FrontEnd/LayerSnapshot.h"
+#include "ui/Transform.h"
#undef LOG_TAG
#define LOG_TAG "SurfaceFlinger"
@@ -1205,13 +1207,27 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot,
snapshot.inputInfo.contentSize = {snapshot.croppedBufferSize.getHeight(),
snapshot.croppedBufferSize.getWidth()};
- // If the layer is a clone, we need to crop the input region to cloned root to prevent
- // touches from going outside the cloned area.
+ snapshot.inputInfo.cloneLayerStackTransform.reset();
+
if (path.isClone()) {
snapshot.inputInfo.inputConfig |= InputConfig::CLONE;
// Cloned layers shouldn't handle watch outside since their z order is not determined by
// WM or the client.
snapshot.inputInfo.inputConfig.clear(InputConfig::WATCH_OUTSIDE_TOUCH);
+
+ // Compute the transform that maps the clone's display to the layer stack space of the
+ // cloned window.
+ const LayerSnapshot* clonedSnapshot = getSnapshot(path.getClonedFrom());
+ if (clonedSnapshot != nullptr) {
+ const auto& [clonedInputBounds, s] =
+ getInputBounds(*clonedSnapshot, /*fillParentBounds=*/false);
+ ui::Transform inputToLayer;
+ inputToLayer.set(clonedInputBounds.left, clonedInputBounds.top);
+ const ui::Transform& layerToLayerStack = getInputTransform(*clonedSnapshot);
+ const auto& displayToInput = snapshot.inputInfo.transform;
+ snapshot.inputInfo.cloneLayerStackTransform =
+ layerToLayerStack * inputToLayer * displayToInput;
+ }
}
}