Loading libs/gui/WindowInfo.cpp +2 −1 Original line number Diff line number Diff line Loading @@ -52,7 +52,8 @@ bool WindowInfo::interceptsStylus() const { } bool WindowInfo::overlaps(const WindowInfo* other) const { return frameLeft < other->frameRight && frameRight > other->frameLeft && const bool nonEmpty = (frameRight - frameLeft > 0) || (frameBottom - frameTop > 0); return nonEmpty && frameLeft < other->frameRight && frameRight > other->frameLeft && frameTop < other->frameBottom && frameBottom > other->frameTop; } Loading libs/gui/tests/EndToEndNativeInputTest.cpp +76 −0 Original line number Diff line number Diff line Loading @@ -987,6 +987,82 @@ TEST_F(InputSurfacesTest, drop_input_policy) { EXPECT_EQ(surface->consumeEvent(100), nullptr); } /** * If a cropped layer's touchable region is replaced with a null crop, it should receive input in * its own crop. */ TEST_F(InputSurfacesTest, cropped_container_replaces_touchable_region_with_null_crop) { std::unique_ptr<InputSurface> parentContainer = InputSurface::makeContainerInputSurface(mComposerClient, 0, 0); std::unique_ptr<InputSurface> containerSurface = InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); containerSurface->doTransaction( [&](auto &t, auto &sc) { t.reparent(sc, parentContainer->mSurfaceControl); }); containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true; containerSurface->mInputInfo.touchableRegionCropHandle = nullptr; parentContainer->showAt(10, 10, Rect(0, 0, 20, 20)); containerSurface->showAt(10, 10, Rect(0, 0, 5, 5)); // Receives events inside its own crop injectTap(21, 21); containerSurface->expectTap(1, 1); // Event is in layer space // Does not receive events outside its crop injectTap(26, 26); EXPECT_EQ(containerSurface->consumeEvent(100), nullptr); } /** * If an un-cropped layer's touchable region is replaced with a null crop, it should receive input * in its parent's touchable region. The input events should be in the layer's coordinate space. */ TEST_F(InputSurfacesTest, uncropped_container_replaces_touchable_region_with_null_crop) { std::unique_ptr<InputSurface> parentContainer = InputSurface::makeContainerInputSurface(mComposerClient, 0, 0); std::unique_ptr<InputSurface> containerSurface = InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); containerSurface->doTransaction( [&](auto &t, auto &sc) { t.reparent(sc, parentContainer->mSurfaceControl); }); containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true; containerSurface->mInputInfo.touchableRegionCropHandle = nullptr; parentContainer->showAt(10, 10, Rect(0, 0, 20, 20)); containerSurface->showAt(10, 10, Rect::INVALID_RECT); // Receives events inside parent bounds injectTap(21, 21); containerSurface->expectTap(1, 1); // Event is in layer space // Does not receive events outside parent bounds injectTap(31, 31); EXPECT_EQ(containerSurface->consumeEvent(100), nullptr); } /** * If a layer's touchable region is replaced with a layer crop, it should receive input in the crop * layer's bounds. The input events should be in the layer's coordinate space. */ TEST_F(InputSurfacesTest, replace_touchable_region_with_crop) { std::unique_ptr<InputSurface> cropLayer = InputSurface::makeContainerInputSurface(mComposerClient, 0, 0); cropLayer->showAt(50, 50, Rect(0, 0, 20, 20)); std::unique_ptr<InputSurface> containerSurface = InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true; containerSurface->mInputInfo.touchableRegionCropHandle = cropLayer->mSurfaceControl->getHandle(); containerSurface->showAt(10, 10, Rect::INVALID_RECT); // Receives events inside crop layer bounds injectTap(51, 51); containerSurface->expectTap(41, 41); // Event is in layer space // Does not receive events outside crop layer bounds injectTap(21, 21); injectTap(71, 71); EXPECT_EQ(containerSurface->consumeEvent(100), nullptr); } class MultiDisplayTests : public InputSurfacesTest { public: MultiDisplayTests() : InputSurfacesTest() { ProcessState::self()->startThreadPool(); } Loading services/surfaceflinger/Layer.cpp +7 −17 Original line number Diff line number Diff line Loading @@ -2161,25 +2161,15 @@ Rect Layer::getInputBounds() const { } void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& displayTransform) { // Transform layer size to screen space and inset it by surface insets. // If this is a portal window, set the touchableRegion to the layerBounds. Rect layerBounds = info.portalToDisplayId == ADISPLAY_ID_NONE ? getInputBounds() : info.touchableRegion.getBounds(); Rect layerBounds = getInputBounds(); if (!layerBounds.isValid()) { layerBounds = getInputBounds(); } if (!layerBounds.isValid()) { // If the layer bounds is empty, set the frame to empty and clear the transform info.frameLeft = 0; info.frameTop = 0; info.frameRight = 0; info.frameBottom = 0; info.transform.reset(); info.touchableRegion = Region(); info.flags = WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::NOT_FOCUSABLE; return; info.touchableRegion.clear(); // A layer could have invalid input bounds and still expect to receive touch input if it has // replaceTouchableRegionWithCrop. For that case, the input transform needs to be calculated // correctly to determine the coordinate space for input events. Use an empty rect so that // the layer will receive input in its own layer space. layerBounds = Rect::EMPTY_RECT; } const ui::Transform layerTransform = getInputTransform(); Loading services/surfaceflinger/Layer.h +13 −0 Original line number Diff line number Diff line Loading @@ -959,6 +959,19 @@ protected: bool usingRelativeZ(LayerVector::StateSet) const; virtual ui::Transform getInputTransform() const; /** * Get the bounds in layer space within which this layer can receive input. * * These bounds are used to: * - Determine the input frame for the layer to be used for occlusion detection; and * - Determine the coordinate space within which the layer will receive input. The top-left of * this rect will be the origin of the coordinate space that the input events sent to the * layer will be in (prior to accounting for surface insets). * * The layer can still receive touch input if these bounds are invalid if * "replaceTouchableRegionWithCrop" is specified. In this case, the layer will receive input * in this layer's space, regardless of the specified crop layer. */ virtual Rect getInputBounds() const; // constant Loading Loading
libs/gui/WindowInfo.cpp +2 −1 Original line number Diff line number Diff line Loading @@ -52,7 +52,8 @@ bool WindowInfo::interceptsStylus() const { } bool WindowInfo::overlaps(const WindowInfo* other) const { return frameLeft < other->frameRight && frameRight > other->frameLeft && const bool nonEmpty = (frameRight - frameLeft > 0) || (frameBottom - frameTop > 0); return nonEmpty && frameLeft < other->frameRight && frameRight > other->frameLeft && frameTop < other->frameBottom && frameBottom > other->frameTop; } Loading
libs/gui/tests/EndToEndNativeInputTest.cpp +76 −0 Original line number Diff line number Diff line Loading @@ -987,6 +987,82 @@ TEST_F(InputSurfacesTest, drop_input_policy) { EXPECT_EQ(surface->consumeEvent(100), nullptr); } /** * If a cropped layer's touchable region is replaced with a null crop, it should receive input in * its own crop. */ TEST_F(InputSurfacesTest, cropped_container_replaces_touchable_region_with_null_crop) { std::unique_ptr<InputSurface> parentContainer = InputSurface::makeContainerInputSurface(mComposerClient, 0, 0); std::unique_ptr<InputSurface> containerSurface = InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); containerSurface->doTransaction( [&](auto &t, auto &sc) { t.reparent(sc, parentContainer->mSurfaceControl); }); containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true; containerSurface->mInputInfo.touchableRegionCropHandle = nullptr; parentContainer->showAt(10, 10, Rect(0, 0, 20, 20)); containerSurface->showAt(10, 10, Rect(0, 0, 5, 5)); // Receives events inside its own crop injectTap(21, 21); containerSurface->expectTap(1, 1); // Event is in layer space // Does not receive events outside its crop injectTap(26, 26); EXPECT_EQ(containerSurface->consumeEvent(100), nullptr); } /** * If an un-cropped layer's touchable region is replaced with a null crop, it should receive input * in its parent's touchable region. The input events should be in the layer's coordinate space. */ TEST_F(InputSurfacesTest, uncropped_container_replaces_touchable_region_with_null_crop) { std::unique_ptr<InputSurface> parentContainer = InputSurface::makeContainerInputSurface(mComposerClient, 0, 0); std::unique_ptr<InputSurface> containerSurface = InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); containerSurface->doTransaction( [&](auto &t, auto &sc) { t.reparent(sc, parentContainer->mSurfaceControl); }); containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true; containerSurface->mInputInfo.touchableRegionCropHandle = nullptr; parentContainer->showAt(10, 10, Rect(0, 0, 20, 20)); containerSurface->showAt(10, 10, Rect::INVALID_RECT); // Receives events inside parent bounds injectTap(21, 21); containerSurface->expectTap(1, 1); // Event is in layer space // Does not receive events outside parent bounds injectTap(31, 31); EXPECT_EQ(containerSurface->consumeEvent(100), nullptr); } /** * If a layer's touchable region is replaced with a layer crop, it should receive input in the crop * layer's bounds. The input events should be in the layer's coordinate space. */ TEST_F(InputSurfacesTest, replace_touchable_region_with_crop) { std::unique_ptr<InputSurface> cropLayer = InputSurface::makeContainerInputSurface(mComposerClient, 0, 0); cropLayer->showAt(50, 50, Rect(0, 0, 20, 20)); std::unique_ptr<InputSurface> containerSurface = InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true; containerSurface->mInputInfo.touchableRegionCropHandle = cropLayer->mSurfaceControl->getHandle(); containerSurface->showAt(10, 10, Rect::INVALID_RECT); // Receives events inside crop layer bounds injectTap(51, 51); containerSurface->expectTap(41, 41); // Event is in layer space // Does not receive events outside crop layer bounds injectTap(21, 21); injectTap(71, 71); EXPECT_EQ(containerSurface->consumeEvent(100), nullptr); } class MultiDisplayTests : public InputSurfacesTest { public: MultiDisplayTests() : InputSurfacesTest() { ProcessState::self()->startThreadPool(); } Loading
services/surfaceflinger/Layer.cpp +7 −17 Original line number Diff line number Diff line Loading @@ -2161,25 +2161,15 @@ Rect Layer::getInputBounds() const { } void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& displayTransform) { // Transform layer size to screen space and inset it by surface insets. // If this is a portal window, set the touchableRegion to the layerBounds. Rect layerBounds = info.portalToDisplayId == ADISPLAY_ID_NONE ? getInputBounds() : info.touchableRegion.getBounds(); Rect layerBounds = getInputBounds(); if (!layerBounds.isValid()) { layerBounds = getInputBounds(); } if (!layerBounds.isValid()) { // If the layer bounds is empty, set the frame to empty and clear the transform info.frameLeft = 0; info.frameTop = 0; info.frameRight = 0; info.frameBottom = 0; info.transform.reset(); info.touchableRegion = Region(); info.flags = WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::NOT_FOCUSABLE; return; info.touchableRegion.clear(); // A layer could have invalid input bounds and still expect to receive touch input if it has // replaceTouchableRegionWithCrop. For that case, the input transform needs to be calculated // correctly to determine the coordinate space for input events. Use an empty rect so that // the layer will receive input in its own layer space. layerBounds = Rect::EMPTY_RECT; } const ui::Transform layerTransform = getInputTransform(); Loading
services/surfaceflinger/Layer.h +13 −0 Original line number Diff line number Diff line Loading @@ -959,6 +959,19 @@ protected: bool usingRelativeZ(LayerVector::StateSet) const; virtual ui::Transform getInputTransform() const; /** * Get the bounds in layer space within which this layer can receive input. * * These bounds are used to: * - Determine the input frame for the layer to be used for occlusion detection; and * - Determine the coordinate space within which the layer will receive input. The top-left of * this rect will be the origin of the coordinate space that the input events sent to the * layer will be in (prior to accounting for surface insets). * * The layer can still receive touch input if these bounds are invalid if * "replaceTouchableRegionWithCrop" is specified. In this case, the layer will receive input * in this layer's space, regardless of the specified crop layer. */ virtual Rect getInputBounds() const; // constant Loading