SurfaceFlinger Input: Correct screen magnification.
We need to pass the computed scaling factors through SurfaceFlinger
as well as appropriately scaling the touchable region. We also need
to be careful as to which axes we scale. In the past screen magnification
has not lead to scaling of the TOUCH_MAJOR/MINOR axes, whereas whole-screen
display compatibility scaling has. We preserve this behavior by differentiating
between the global scale and a scale on any particular window. The window scale
works like the global scale used to and the global scale is only used for additional
scaling of the MAJOR/MINOR axes.
Bug: 80101428
Bug: 113136004
Bug: 111440400
Change-Id: I97d809826f86b452f28443cb1046e8bfef1bbf9d
diff --git a/include/input/Input.h b/include/input/Input.h
index cc45aef..ee22bc6 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -244,7 +244,12 @@
float getAxisValue(int32_t axis) const;
status_t setAxisValue(int32_t axis, float value);
- void scale(float scale);
+ void scale(float globalScale);
+
+ // Scale the pointer coordinates according to a global scale and a
+ // window scale. The global scale will be applied to TOUCH/TOOL_MAJOR/MINOR
+ // axes, however the window scaling will not.
+ void scale(float globalScale, float windowXScale, float windowYScale);
void applyOffset(float xOffset, float yOffset);
inline float getX() const {
@@ -595,7 +600,7 @@
void offsetLocation(float xOffset, float yOffset);
- void scale(float scaleFactor);
+ void scale(float globalScaleFactor);
// Apply 3x3 perspective matrix transformation.
// Matrix is in row-major form and compatible with SkMatrix.
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index df401f0..8dd95cf 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -140,7 +140,13 @@
*/
int32_t surfaceInset = 0;
- float scaleFactor;
+ // A global scaling factor for all windows. Unlike windowScaleX/Y this results
+ // in scaling of the TOUCH_MAJOR/TOUCH_MINOR axis.
+ float globalScaleFactor;
+
+ // Scaling factors applied to individual windows.
+ float windowXScale = 1.0f;
+ float windowYScale = 1.0f;
/*
* This is filled in by the WM relative to the frame and then translated
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 2cc0c54..2871302 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -140,7 +140,7 @@
mInputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL;
mInputInfo.layoutParamsType = InputWindowInfo::TYPE_BASE_APPLICATION;
mInputInfo.dispatchingTimeout = 100000;
- mInputInfo.scaleFactor = 1.0;
+ mInputInfo.globalScaleFactor = 1.0;
mInputInfo.canReceiveKeys = true;
mInputInfo.hasFocus = true;
mInputInfo.hasWallpaper = false;
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 8a15e2f..a558970 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -131,15 +131,24 @@
}
}
-void PointerCoords::scale(float scaleFactor) {
+void PointerCoords::scale(float globalScaleFactor, float windowXScale, float windowYScale) {
// No need to scale pressure or size since they are normalized.
// No need to scale orientation since it is meaningless to do so.
- scaleAxisValue(*this, AMOTION_EVENT_AXIS_X, scaleFactor);
- scaleAxisValue(*this, AMOTION_EVENT_AXIS_Y, scaleFactor);
- scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MAJOR, scaleFactor);
- scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, scaleFactor);
- scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, scaleFactor);
- scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, scaleFactor);
+
+ // If there is a global scale factor, it is included in the windowX/YScale
+ // so we don't need to apply it twice to the X/Y axes.
+ // However we don't want to apply any windowXYScale not included in the global scale
+ // to the TOUCH_MAJOR/MINOR coordinates.
+ scaleAxisValue(*this, AMOTION_EVENT_AXIS_X, windowXScale);
+ scaleAxisValue(*this, AMOTION_EVENT_AXIS_Y, windowYScale);
+ scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MAJOR, globalScaleFactor);
+ scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, globalScaleFactor);
+ scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, globalScaleFactor);
+ scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, globalScaleFactor);
+}
+
+void PointerCoords::scale(float globalScaleFactor) {
+ scale(globalScaleFactor, globalScaleFactor, globalScaleFactor);
}
void PointerCoords::applyOffset(float xOffset, float yOffset) {
@@ -345,15 +354,15 @@
mYOffset += yOffset;
}
-void MotionEvent::scale(float scaleFactor) {
- mXOffset *= scaleFactor;
- mYOffset *= scaleFactor;
- mXPrecision *= scaleFactor;
- mYPrecision *= scaleFactor;
+void MotionEvent::scale(float globalScaleFactor) {
+ mXOffset *= globalScaleFactor;
+ mYOffset *= globalScaleFactor;
+ mXPrecision *= globalScaleFactor;
+ mYPrecision *= globalScaleFactor;
size_t numSamples = mSamplePointerCoords.size();
for (size_t i = 0; i < numSamples; i++) {
- mSamplePointerCoords.editItemAt(i).scale(scaleFactor);
+ mSamplePointerCoords.editItemAt(i).scale(globalScaleFactor);
}
}
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index e6bb255..556a005 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -82,7 +82,9 @@
output.writeInt32(frameRight);
output.writeInt32(frameBottom);
output.writeInt32(surfaceInset);
- output.writeFloat(scaleFactor);
+ output.writeFloat(globalScaleFactor);
+ output.writeFloat(windowXScale);
+ output.writeFloat(windowYScale);
output.writeBool(visible);
output.writeBool(canReceiveKeys);
output.writeBool(hasFocus);
@@ -121,7 +123,9 @@
ret.frameRight = from.readInt32();
ret.frameBottom = from.readInt32();
ret.surfaceInset = from.readInt32();
- ret.scaleFactor = from.readFloat();
+ ret.globalScaleFactor = from.readFloat();
+ ret.windowXScale = from.readFloat();
+ ret.windowYScale = from.readFloat();
ret.visible = from.readBool();
ret.canReceiveKeys = from.readBool();
ret.hasFocus = from.readBool();
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
index cc6962f..5e5893f 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -48,7 +48,9 @@
i.frameRight = 16;
i.frameBottom = 19;
i.surfaceInset = 17;
- i.scaleFactor = 0.3;
+ i.globalScaleFactor = 0.3;
+ i.windowXScale = 0.4;
+ i.windowYScale = 0.5;
i.visible = false;
i.canReceiveKeys = false;
i.hasFocus = false;
@@ -75,7 +77,9 @@
ASSERT_EQ(i.frameRight, i2.frameRight);
ASSERT_EQ(i.frameBottom, i2.frameBottom);
ASSERT_EQ(i.surfaceInset, i2.surfaceInset);
- ASSERT_EQ(i.scaleFactor, i2.scaleFactor);
+ ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor);
+ ASSERT_EQ(i.windowXScale, i2.windowXScale);
+ ASSERT_EQ(i.windowYScale, i2.windowYScale);
ASSERT_EQ(i.visible, i2.visible);
ASSERT_EQ(i.canReceiveKeys, i2.canReceiveKeys);
ASSERT_EQ(i.hasFocus, i2.hasFocus);
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index fe4ae6c..8150931 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -325,6 +325,20 @@
return *this;
}
+Region& Region::scaleSelf(int sx, int sy) {
+ size_t count = mStorage.size();
+ Rect* rects = mStorage.editArray();
+ while (count) {
+ rects->left *= sx;
+ rects->right *= sx;
+ rects->top *= sy;
+ rects->bottom *= sy;
+ rects++;
+ count--;
+ }
+ return *this;
+}
+
// ----------------------------------------------------------------------------
const Region Region::merge(const Rect& rhs) const {
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index 8e949ec..25128ef 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -95,6 +95,14 @@
return mMatrix[2][1];
}
+float Transform::sx() const {
+ return mMatrix[0][0];
+}
+
+float Transform::sy() const {
+ return mMatrix[1][1];
+}
+
void Transform::reset() {
mType = IDENTITY;
for(size_t i = 0; i < 3; i++) {
diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h
index 68b60fc..c5e31c5 100644
--- a/libs/ui/include/ui/Region.h
+++ b/libs/ui/include/ui/Region.h
@@ -89,11 +89,13 @@
// these translate rhs first
Region& translateSelf(int dx, int dy);
+ Region& scaleSelf(int sx, int sy);
Region& orSelf(const Region& rhs, int dx, int dy);
Region& xorSelf(const Region& rhs, int dx, int dy);
Region& andSelf(const Region& rhs, int dx, int dy);
Region& subtractSelf(const Region& rhs, int dx, int dy);
+
// these translate rhs first
const Region translate(int dx, int dy) const WARN_UNUSED;
const Region merge(const Region& rhs, int dx, int dy) const WARN_UNUSED;
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h
index 42dca75..900a5c4 100644
--- a/libs/ui/include/ui/Transform.h
+++ b/libs/ui/include/ui/Transform.h
@@ -65,6 +65,8 @@
const vec3& operator [] (size_t i) const; // returns column i
float tx() const;
float ty() const;
+ float sx() const;
+ float sy() const;
// modify the transform
void reset();
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 93a8ac0..bea4f91 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -1671,7 +1671,9 @@
target.flags = targetFlags;
target.xOffset = - windowInfo->frameLeft;
target.yOffset = - windowInfo->frameTop;
- target.scaleFactor = windowInfo->scaleFactor;
+ target.globalScaleFactor = windowInfo->globalScaleFactor;
+ target.windowXScale = windowInfo->windowXScale;
+ target.windowYScale = windowInfo->windowYScale;
target.pointerIds = pointerIds;
}
@@ -1692,7 +1694,7 @@
target.xOffset = 0;
target.yOffset = 0;
target.pointerIds.clear();
- target.scaleFactor = 1.0f;
+ target.globalScaleFactor = 1.0f;
}
} else {
// If there is no monitor channel registered or all monitor channel unregistered,
@@ -1912,11 +1914,13 @@
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
- "xOffset=%f, yOffset=%f, scaleFactor=%f, "
- "pointerIds=0x%x",
+ "xOffset=%f, yOffset=%f, globalScaleFactor=%f, "
+ "windowScaleFactor=(%f, %f), pointerIds=0x%x",
connection->getInputChannelName().c_str(), inputTarget->flags,
inputTarget->xOffset, inputTarget->yOffset,
- inputTarget->scaleFactor, inputTarget->pointerIds.value);
+ inputTarget->globalScaleFactor,
+ inputTarget->windowXScale, inputTarget->windowYScale,
+ inputTarget->pointerIds.value);
#endif
// Skip this event if the connection status is not normal.
@@ -1993,7 +1997,8 @@
// Enqueue a new dispatch entry onto the outbound queue for this connection.
DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref
inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
- inputTarget->scaleFactor);
+ inputTarget->globalScaleFactor, inputTarget->windowXScale,
+ inputTarget->windowYScale);
// Apply target flags and update the connection's input state.
switch (eventEntry->type) {
@@ -2109,13 +2114,15 @@
float xOffset, yOffset;
if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
&& !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
- float scaleFactor = dispatchEntry->scaleFactor;
- xOffset = dispatchEntry->xOffset * scaleFactor;
- yOffset = dispatchEntry->yOffset * scaleFactor;
- if (scaleFactor != 1.0f) {
+ float globalScaleFactor = dispatchEntry->globalScaleFactor;
+ float wxs = dispatchEntry->windowXScale;
+ float wys = dispatchEntry->windowYScale;
+ xOffset = dispatchEntry->xOffset * wxs;
+ yOffset = dispatchEntry->yOffset * wys;
+ if (wxs != 1.0f || wys != 1.0f || globalScaleFactor != 1.0f) {
for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
scaledCoords[i] = motionEntry->pointerCoords[i];
- scaledCoords[i].scale(scaleFactor);
+ scaledCoords[i].scale(globalScaleFactor, wxs, wys);
}
usingCoords = scaledCoords;
}
@@ -2373,11 +2380,13 @@
const InputWindowInfo* windowInfo = windowHandle->getInfo();
target.xOffset = -windowInfo->frameLeft;
target.yOffset = -windowInfo->frameTop;
- target.scaleFactor = windowInfo->scaleFactor;
+ target.globalScaleFactor = windowInfo->globalScaleFactor;
+ target.windowXScale = windowInfo->windowXScale;
+ target.windowYScale = windowInfo->windowYScale;
} else {
target.xOffset = 0;
target.yOffset = 0;
- target.scaleFactor = 1.0f;
+ target.globalScaleFactor = 1.0f;
}
target.inputChannel = connection->inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
@@ -3509,7 +3518,7 @@
dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, "
"paused=%s, hasFocus=%s, hasWallpaper=%s, "
"visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
- "frame=[%d,%d][%d,%d], scale=%f, "
+ "frame=[%d,%d][%d,%d], globalScale=%f, windowScale=%f,%f"
"touchableRegion=",
i, windowInfo->name.c_str(), windowInfo->displayId,
toString(windowInfo->paused),
@@ -3521,7 +3530,8 @@
windowInfo->layer,
windowInfo->frameLeft, windowInfo->frameTop,
windowInfo->frameRight, windowInfo->frameBottom,
- windowInfo->scaleFactor);
+ windowInfo->globalScaleFactor,
+ windowInfo->windowXScale, windowInfo->windowYScale);
dumpRegion(dump, windowInfo->touchableRegion);
dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures);
dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
@@ -4383,10 +4393,12 @@
volatile int32_t InputDispatcher::DispatchEntry::sNextSeqAtomic;
InputDispatcher::DispatchEntry::DispatchEntry(EventEntry* eventEntry,
- int32_t targetFlags, float xOffset, float yOffset, float scaleFactor) :
+ int32_t targetFlags, float xOffset, float yOffset, float globalScaleFactor,
+ float windowXScale, float windowYScale) :
seq(nextSeq()),
eventEntry(eventEntry), targetFlags(targetFlags),
- xOffset(xOffset), yOffset(yOffset), scaleFactor(scaleFactor),
+ xOffset(xOffset), yOffset(yOffset), globalScaleFactor(globalScaleFactor),
+ windowXScale(windowXScale), windowYScale(windowYScale),
deliveryTime(0), resolvedAction(0), resolvedFlags(0) {
eventEntry->refCount += 1;
}
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index 11df5d9..73bcc25 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -160,7 +160,9 @@
// Scaling factor to apply to MotionEvent as it is delivered.
// (ignored for KeyEvents)
- float scaleFactor;
+ float globalScaleFactor;
+ float windowXScale = 1.0f;
+ float windowYScale = 1.0f;
// The subset of pointer ids to include in motion events dispatched to this input target
// if FLAG_SPLIT is set.
@@ -563,7 +565,9 @@
int32_t targetFlags;
float xOffset;
float yOffset;
- float scaleFactor;
+ float globalScaleFactor;
+ float windowXScale = 1.0f;
+ float windowYScale = 1.0f;
nsecs_t deliveryTime; // time when the event was actually delivered
// Set to the resolved action and flags when the event is enqueued.
@@ -571,7 +575,8 @@
int32_t resolvedFlags;
DispatchEntry(EventEntry* eventEntry,
- int32_t targetFlags, float xOffset, float yOffset, float scaleFactor);
+ int32_t targetFlags, float xOffset, float yOffset,
+ float globalScaleFactor, float windowXScale, float windowYScale);
~DispatchEntry();
inline bool hasForegroundTarget() const {
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 3afee73..26f01b7 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -388,7 +388,7 @@
mInfo.frameTop = 0;
mInfo.frameRight = WIDTH;
mInfo.frameBottom = HEIGHT;
- mInfo.scaleFactor = 1.0;
+ mInfo.globalScaleFactor = 1.0;
mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
mInfo.visible = true;
mInfo.canReceiveKeys = true;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index bfae03d..6ac0901 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2074,6 +2074,12 @@
info.frameRight = screenBounds.right - info.surfaceInset;
info.frameBottom = screenBounds.bottom - info.surfaceInset;
+ ui::Transform t = getTransform();
+ info.windowXScale *= 1.0f / t.sx();
+ info.windowYScale *= 1.0f / t.sy();
+
+ info.touchableRegion.scaleSelf(t.sx(), t.sy());
+
info.touchableRegion = info.touchableRegion.translate(
screenBounds.left,
screenBounds.top);