Loading libs/gui/tests/EndToEndNativeInputTest.cpp +69 −0 Original line number Original line Diff line number Diff line Loading @@ -536,4 +536,73 @@ TEST_F(InputSurfacesTest, can_be_focused) { surface->assertFocusChange(true); surface->assertFocusChange(true); } } TEST_F(InputSurfacesTest, rotate_surface) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); surface->showAt(10, 10); surface->doTransaction([](auto &t, auto &sc) { t.setMatrix(sc, 0, 1, -1, 0); // 90 degrees }); injectTap(8, 11); surface->expectTap(1, 2); surface->doTransaction([](auto &t, auto &sc) { t.setMatrix(sc, -1, 0, 0, -1); // 180 degrees }); injectTap(9, 8); surface->expectTap(1, 2); surface->doTransaction([](auto &t, auto &sc) { t.setMatrix(sc, 0, -1, 1, 0); // 270 degrees }); injectTap(12, 9); surface->expectTap(1, 2); } TEST_F(InputSurfacesTest, rotate_surface_with_scale) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); surface->showAt(10, 10); surface->doTransaction([](auto &t, auto &sc) { t.setMatrix(sc, 0, 2, -4, 0); // 90 degrees }); injectTap(2, 12); surface->expectTap(1, 2); surface->doTransaction([](auto &t, auto &sc) { t.setMatrix(sc, -2, 0, 0, -4); // 180 degrees }); injectTap(8, 2); surface->expectTap(1, 2); surface->doTransaction([](auto &t, auto &sc) { t.setMatrix(sc, 0, -2, 4, 0); // 270 degrees }); injectTap(18, 8); surface->expectTap(1, 2); } TEST_F(InputSurfacesTest, rotate_surface_with_scale_and_insets) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); surface->mInputInfo.surfaceInset = 5; surface->showAt(100, 100); surface->doTransaction([](auto &t, auto &sc) { t.setMatrix(sc, 0, 2, -4, 0); // 90 degrees }); injectTap(40, 120); surface->expectTap(5, 10); surface->doTransaction([](auto &t, auto &sc) { t.setMatrix(sc, -2, 0, 0, -4); // 180 degrees }); injectTap(80, 40); surface->expectTap(5, 10); surface->doTransaction([](auto &t, auto &sc) { t.setMatrix(sc, 0, -2, 4, 0); // 270 degrees }); injectTap(160, 80); surface->expectTap(5, 10); } } // namespace android::test } // namespace android::test libs/ui/Transform.cpp +4 −1 Original line number Original line Diff line number Diff line Loading @@ -14,6 +14,9 @@ * limitations under the License. * limitations under the License. */ */ #undef LOG_TAG #define LOG_TAG "Transform" #include <math.h> #include <math.h> #include <android-base/stringprintf.h> #include <android-base/stringprintf.h> Loading Loading @@ -132,7 +135,7 @@ float Transform::dsdy() const { } } float Transform::getScaleX() const { float Transform::getScaleX() const { return sqrt(dsdx() * dsdx()) + (dtdx() * dtdx()); return sqrt((dsdx() * dsdx()) + (dtdx() * dtdx())); } } float Transform::getScaleY() const { float Transform::getScaleY() const { Loading services/surfaceflinger/Layer.cpp +39 −15 Original line number Original line Diff line number Diff line Loading @@ -2418,40 +2418,64 @@ InputWindowInfo Layer::fillInputInfo() { const float xScale = t.getScaleX(); const float xScale = t.getScaleX(); const float yScale = t.getScaleY(); const float yScale = t.getScaleY(); if (xScale != 1.0f || yScale != 1.0f) { if (xScale != 1.0f || yScale != 1.0f) { info.touchableRegion.scaleSelf(xScale, yScale); xSurfaceInset = std::round(xSurfaceInset * xScale); xSurfaceInset = std::round(xSurfaceInset * xScale); ySurfaceInset = std::round(ySurfaceInset * yScale); ySurfaceInset = std::round(ySurfaceInset * yScale); } } layerBounds = t.transform(layerBounds); Rect transformedLayerBounds = t.transform(layerBounds); // clamp inset to layer bounds // clamp inset to layer bounds xSurfaceInset = (xSurfaceInset >= 0) ? std::min(xSurfaceInset, layerBounds.getWidth() / 2) : 0; xSurfaceInset = (xSurfaceInset >= 0) ySurfaceInset = (ySurfaceInset >= 0) ? std::min(ySurfaceInset, layerBounds.getHeight() / 2) : 0; ? std::min(xSurfaceInset, transformedLayerBounds.getWidth() / 2) : 0; ySurfaceInset = (ySurfaceInset >= 0) ? std::min(ySurfaceInset, transformedLayerBounds.getHeight() / 2) : 0; // inset while protecting from overflow TODO(b/161235021): What is going wrong // inset while protecting from overflow TODO(b/161235021): What is going wrong // in the overflow scenario? // in the overflow scenario? { { int32_t tmp; int32_t tmp; if (!__builtin_add_overflow(layerBounds.left, xSurfaceInset, &tmp)) layerBounds.left = tmp; if (!__builtin_add_overflow(transformedLayerBounds.left, xSurfaceInset, &tmp)) if (!__builtin_sub_overflow(layerBounds.right, xSurfaceInset, &tmp)) layerBounds.right = tmp; transformedLayerBounds.left = tmp; if (!__builtin_add_overflow(layerBounds.top, ySurfaceInset, &tmp)) layerBounds.top = tmp; if (!__builtin_sub_overflow(transformedLayerBounds.right, xSurfaceInset, &tmp)) if (!__builtin_sub_overflow(layerBounds.bottom, ySurfaceInset, &tmp)) layerBounds.bottom = tmp; transformedLayerBounds.right = tmp; if (!__builtin_add_overflow(transformedLayerBounds.top, ySurfaceInset, &tmp)) transformedLayerBounds.top = tmp; if (!__builtin_sub_overflow(transformedLayerBounds.bottom, ySurfaceInset, &tmp)) transformedLayerBounds.bottom = tmp; } } // Input coordinate should match the layer bounds. // Input coordinate should match the layer bounds. info.frameLeft = layerBounds.left; info.frameLeft = transformedLayerBounds.left; info.frameTop = layerBounds.top; info.frameTop = transformedLayerBounds.top; info.frameRight = layerBounds.right; info.frameRight = transformedLayerBounds.right; info.frameBottom = layerBounds.bottom; info.frameBottom = transformedLayerBounds.bottom; // Compute the correct transform to send to input. This will allow it to transform the // input coordinates from display space into window space. Therefore, it needs to use the // final layer frame to create the inverse transform. Since surface insets are added later, // along with the overflow, the best way to ensure we get the correct transform is to use // the final frame calculated. // 1. Take the original transform set on the window and get the inverse transform. This is // used to get the final bounds in display space (ignorning the transform). Apply the // inverse transform on the layerBounds to get the untransformed frame (in display space) // 2. Take the top and left of the untransformed frame to get the real position on screen. // Apply the layer transform on top/left so it includes any scale or rotation. These will // be the new translation values for the transform. // 3. Update the translation of the original transform to the new translation values. // 4. Send the inverse transform to input so the coordinates can be transformed back into // window space. ui::Transform inverseTransform = t.inverse(); Rect nonTransformedBounds = inverseTransform.transform(transformedLayerBounds); vec2 translation = t.transform(nonTransformedBounds.left, nonTransformedBounds.top); ui::Transform inputTransform(t); ui::Transform inputTransform(t); inputTransform.set(layerBounds.left, layerBounds.top); inputTransform.set(translation.x, translation.y); info.transform = inputTransform.inverse(); info.transform = inputTransform.inverse(); // Position the touchable region relative to frame screen location and restrict it to frame // Position the touchable region relative to frame screen location and restrict it to frame // bounds. // bounds. info.touchableRegion = info.touchableRegion.translate(info.frameLeft, info.frameTop); info.touchableRegion = inputTransform.transform(info.touchableRegion); // For compatibility reasons we let layers which can receive input // For compatibility reasons we let layers which can receive input // receive input before they have actually submitted a buffer. Because // receive input before they have actually submitted a buffer. Because // of this we use canReceiveInput instead of isVisible to check the // of this we use canReceiveInput instead of isVisible to check the Loading Loading
libs/gui/tests/EndToEndNativeInputTest.cpp +69 −0 Original line number Original line Diff line number Diff line Loading @@ -536,4 +536,73 @@ TEST_F(InputSurfacesTest, can_be_focused) { surface->assertFocusChange(true); surface->assertFocusChange(true); } } TEST_F(InputSurfacesTest, rotate_surface) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); surface->showAt(10, 10); surface->doTransaction([](auto &t, auto &sc) { t.setMatrix(sc, 0, 1, -1, 0); // 90 degrees }); injectTap(8, 11); surface->expectTap(1, 2); surface->doTransaction([](auto &t, auto &sc) { t.setMatrix(sc, -1, 0, 0, -1); // 180 degrees }); injectTap(9, 8); surface->expectTap(1, 2); surface->doTransaction([](auto &t, auto &sc) { t.setMatrix(sc, 0, -1, 1, 0); // 270 degrees }); injectTap(12, 9); surface->expectTap(1, 2); } TEST_F(InputSurfacesTest, rotate_surface_with_scale) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); surface->showAt(10, 10); surface->doTransaction([](auto &t, auto &sc) { t.setMatrix(sc, 0, 2, -4, 0); // 90 degrees }); injectTap(2, 12); surface->expectTap(1, 2); surface->doTransaction([](auto &t, auto &sc) { t.setMatrix(sc, -2, 0, 0, -4); // 180 degrees }); injectTap(8, 2); surface->expectTap(1, 2); surface->doTransaction([](auto &t, auto &sc) { t.setMatrix(sc, 0, -2, 4, 0); // 270 degrees }); injectTap(18, 8); surface->expectTap(1, 2); } TEST_F(InputSurfacesTest, rotate_surface_with_scale_and_insets) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); surface->mInputInfo.surfaceInset = 5; surface->showAt(100, 100); surface->doTransaction([](auto &t, auto &sc) { t.setMatrix(sc, 0, 2, -4, 0); // 90 degrees }); injectTap(40, 120); surface->expectTap(5, 10); surface->doTransaction([](auto &t, auto &sc) { t.setMatrix(sc, -2, 0, 0, -4); // 180 degrees }); injectTap(80, 40); surface->expectTap(5, 10); surface->doTransaction([](auto &t, auto &sc) { t.setMatrix(sc, 0, -2, 4, 0); // 270 degrees }); injectTap(160, 80); surface->expectTap(5, 10); } } // namespace android::test } // namespace android::test
libs/ui/Transform.cpp +4 −1 Original line number Original line Diff line number Diff line Loading @@ -14,6 +14,9 @@ * limitations under the License. * limitations under the License. */ */ #undef LOG_TAG #define LOG_TAG "Transform" #include <math.h> #include <math.h> #include <android-base/stringprintf.h> #include <android-base/stringprintf.h> Loading Loading @@ -132,7 +135,7 @@ float Transform::dsdy() const { } } float Transform::getScaleX() const { float Transform::getScaleX() const { return sqrt(dsdx() * dsdx()) + (dtdx() * dtdx()); return sqrt((dsdx() * dsdx()) + (dtdx() * dtdx())); } } float Transform::getScaleY() const { float Transform::getScaleY() const { Loading
services/surfaceflinger/Layer.cpp +39 −15 Original line number Original line Diff line number Diff line Loading @@ -2418,40 +2418,64 @@ InputWindowInfo Layer::fillInputInfo() { const float xScale = t.getScaleX(); const float xScale = t.getScaleX(); const float yScale = t.getScaleY(); const float yScale = t.getScaleY(); if (xScale != 1.0f || yScale != 1.0f) { if (xScale != 1.0f || yScale != 1.0f) { info.touchableRegion.scaleSelf(xScale, yScale); xSurfaceInset = std::round(xSurfaceInset * xScale); xSurfaceInset = std::round(xSurfaceInset * xScale); ySurfaceInset = std::round(ySurfaceInset * yScale); ySurfaceInset = std::round(ySurfaceInset * yScale); } } layerBounds = t.transform(layerBounds); Rect transformedLayerBounds = t.transform(layerBounds); // clamp inset to layer bounds // clamp inset to layer bounds xSurfaceInset = (xSurfaceInset >= 0) ? std::min(xSurfaceInset, layerBounds.getWidth() / 2) : 0; xSurfaceInset = (xSurfaceInset >= 0) ySurfaceInset = (ySurfaceInset >= 0) ? std::min(ySurfaceInset, layerBounds.getHeight() / 2) : 0; ? std::min(xSurfaceInset, transformedLayerBounds.getWidth() / 2) : 0; ySurfaceInset = (ySurfaceInset >= 0) ? std::min(ySurfaceInset, transformedLayerBounds.getHeight() / 2) : 0; // inset while protecting from overflow TODO(b/161235021): What is going wrong // inset while protecting from overflow TODO(b/161235021): What is going wrong // in the overflow scenario? // in the overflow scenario? { { int32_t tmp; int32_t tmp; if (!__builtin_add_overflow(layerBounds.left, xSurfaceInset, &tmp)) layerBounds.left = tmp; if (!__builtin_add_overflow(transformedLayerBounds.left, xSurfaceInset, &tmp)) if (!__builtin_sub_overflow(layerBounds.right, xSurfaceInset, &tmp)) layerBounds.right = tmp; transformedLayerBounds.left = tmp; if (!__builtin_add_overflow(layerBounds.top, ySurfaceInset, &tmp)) layerBounds.top = tmp; if (!__builtin_sub_overflow(transformedLayerBounds.right, xSurfaceInset, &tmp)) if (!__builtin_sub_overflow(layerBounds.bottom, ySurfaceInset, &tmp)) layerBounds.bottom = tmp; transformedLayerBounds.right = tmp; if (!__builtin_add_overflow(transformedLayerBounds.top, ySurfaceInset, &tmp)) transformedLayerBounds.top = tmp; if (!__builtin_sub_overflow(transformedLayerBounds.bottom, ySurfaceInset, &tmp)) transformedLayerBounds.bottom = tmp; } } // Input coordinate should match the layer bounds. // Input coordinate should match the layer bounds. info.frameLeft = layerBounds.left; info.frameLeft = transformedLayerBounds.left; info.frameTop = layerBounds.top; info.frameTop = transformedLayerBounds.top; info.frameRight = layerBounds.right; info.frameRight = transformedLayerBounds.right; info.frameBottom = layerBounds.bottom; info.frameBottom = transformedLayerBounds.bottom; // Compute the correct transform to send to input. This will allow it to transform the // input coordinates from display space into window space. Therefore, it needs to use the // final layer frame to create the inverse transform. Since surface insets are added later, // along with the overflow, the best way to ensure we get the correct transform is to use // the final frame calculated. // 1. Take the original transform set on the window and get the inverse transform. This is // used to get the final bounds in display space (ignorning the transform). Apply the // inverse transform on the layerBounds to get the untransformed frame (in display space) // 2. Take the top and left of the untransformed frame to get the real position on screen. // Apply the layer transform on top/left so it includes any scale or rotation. These will // be the new translation values for the transform. // 3. Update the translation of the original transform to the new translation values. // 4. Send the inverse transform to input so the coordinates can be transformed back into // window space. ui::Transform inverseTransform = t.inverse(); Rect nonTransformedBounds = inverseTransform.transform(transformedLayerBounds); vec2 translation = t.transform(nonTransformedBounds.left, nonTransformedBounds.top); ui::Transform inputTransform(t); ui::Transform inputTransform(t); inputTransform.set(layerBounds.left, layerBounds.top); inputTransform.set(translation.x, translation.y); info.transform = inputTransform.inverse(); info.transform = inputTransform.inverse(); // Position the touchable region relative to frame screen location and restrict it to frame // Position the touchable region relative to frame screen location and restrict it to frame // bounds. // bounds. info.touchableRegion = info.touchableRegion.translate(info.frameLeft, info.frameTop); info.touchableRegion = inputTransform.transform(info.touchableRegion); // For compatibility reasons we let layers which can receive input // For compatibility reasons we let layers which can receive input // receive input before they have actually submitted a buffer. Because // receive input before they have actually submitted a buffer. Because // of this we use canReceiveInput instead of isVisible to check the // of this we use canReceiveInput instead of isVisible to check the Loading