Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 89ef5267 authored by Prabir Pradhan's avatar Prabir Pradhan Committed by Android (Google) Code Review
Browse files

Merge changes Ia1ed53b6,Ic561e8b3

* changes:
  SF: Clean up input info calculations
  SF: Enforce that layers with invalid input bounds are not focusable
parents 24cb2435 c9589c17
Loading
Loading
Loading
Loading
+30 −2
Original line number Diff line number Diff line
@@ -546,7 +546,10 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets) {
}

TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) {
    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
    std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
    bgSurface->showAt(100, 100);

    // In case we pass the very big inset without any checking.
    fgSurface->mInputInfo.surfaceInset = INT32_MAX;
    fgSurface->showAt(100, 100);
@@ -554,8 +557,8 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) {
    fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); });

    // expect no crash for overflow, and inset size to be clamped to surface size
    injectTap(202, 202);
    fgSurface->expectTap(1, 1);
    injectTap(112, 124);
    bgSurface->expectTap(12, 24);
}

// Ensure we ignore transparent region when getting screen bounds when positioning input frame.
@@ -987,6 +990,31 @@ TEST_F(InputSurfacesTest, drop_input_policy) {
    EXPECT_EQ(surface->consumeEvent(100), nullptr);
}

TEST_F(InputSurfacesTest, layer_with_empty_crop_cannot_be_focused) {
    std::unique_ptr<InputSurface> bufferSurface =
            InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);

    bufferSurface->showAt(50, 50, Rect::EMPTY_RECT);

    bufferSurface->requestFocus();
    EXPECT_EQ(bufferSurface->consumeEvent(100), nullptr);

    bufferSurface->showAt(50, 50, Rect::INVALID_RECT);

    bufferSurface->requestFocus();
    EXPECT_EQ(bufferSurface->consumeEvent(100), nullptr);
}

TEST_F(InputSurfacesTest, layer_with_valid_crop_can_be_focused) {
    std::unique_ptr<InputSurface> bufferSurface =
            InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);

    bufferSurface->showAt(50, 50, Rect{0, 0, 100, 100});

    bufferSurface->requestFocus();
    bufferSurface->assertFocusChange(true);
}

/**
 * If a cropped layer's touchable region is replaced with a null crop, it should receive input in
 * its own crop.
+59 −79
Original line number Diff line number Diff line
@@ -2160,91 +2160,71 @@ Rect Layer::getInputBounds() const {
    return getCroppedBufferSize(getDrawingState());
}

void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& displayTransform) {
    Rect layerBounds = getInputBounds();
    if (!layerBounds.isValid()) {
void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& screenToDisplay) {
    Rect tmpBounds = getInputBounds();
    if (!tmpBounds.isValid()) {
        info.flags = WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::NOT_FOCUSABLE;
        info.focusable = false;
        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();
    // Transform that takes window coordinates to non-rotated display coordinates
    ui::Transform t = displayTransform * layerTransform;
    int32_t xSurfaceInset = info.surfaceInset;
    int32_t ySurfaceInset = info.surfaceInset;
    // Bring screenBounds into non-unrotated space
    Rect screenBounds = displayTransform.transform(Rect{mScreenBounds});

    const float xScale = t.getScaleX();
    const float yScale = t.getScaleY();
    if (xScale != 1.0f || yScale != 1.0f) {
        xSurfaceInset = std::round(xSurfaceInset * xScale);
        ySurfaceInset = std::round(ySurfaceInset * yScale);
    }

    // Transform the layer bounds from layer coordinate space to display coordinate space.
    Rect transformedLayerBounds = t.transform(layerBounds);

    // clamp inset to layer bounds
    xSurfaceInset = (xSurfaceInset >= 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
    // in the overflow scenario?
    {
    int32_t tmp;
    if (!__builtin_add_overflow(transformedLayerBounds.left, xSurfaceInset, &tmp))
        transformedLayerBounds.left = tmp;
    if (!__builtin_sub_overflow(transformedLayerBounds.right, xSurfaceInset, &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;
    }

    // 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 layer 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);
    inputTransform.set(translation.x, translation.y);
    info.transform = inputTransform.inverse();

    // We need to send the layer bounds cropped to the screenbounds since the layer can be cropped.
    // The frame should be the area the user sees on screen since it's used for occlusion
    // detection.
    transformedLayerBounds.intersect(screenBounds, &transformedLayerBounds);
    info.frameLeft = transformedLayerBounds.left;
    info.frameTop = transformedLayerBounds.top;
    info.frameRight = transformedLayerBounds.right;
    info.frameBottom = transformedLayerBounds.bottom;

    // Position the touchable region relative to frame screen location and restrict it to frame
    // bounds.
    info.touchableRegion = inputTransform.transform(info.touchableRegion);
        tmpBounds = Rect::EMPTY_RECT;
    }

    // InputDispatcher works in the display device's coordinate space. Here, we calculate the
    // frame and transform used for the layer, which determines the bounds and the coordinate space
    // within which the layer will receive input.
    //
    // The coordinate space within which each of the bounds are specified is explicitly documented
    // in the variable name. For example "inputBoundsInLayer" is specified in layer space. A
    // Transform converts one coordinate space to another, which is apparent in its naming. For
    // example, "layerToDisplay" transforms layer space to display space.
    //
    // Coordinate space definitions:
    //   - display: The display device's coordinate space. Correlates to pixels on the display.
    //   - screen: The post-rotation coordinate space for the display, a.k.a. logical display space.
    //   - layer: The coordinate space of this layer.
    //   - input: The coordinate space in which this layer will receive input events. This could be
    //            different than layer space if a surfaceInset is used, which changes the origin
    //            of the input space.
    const FloatRect inputBoundsInLayer = tmpBounds.toFloatRect();

    // Clamp surface inset to the input bounds.
    const auto surfaceInset = static_cast<float>(info.surfaceInset);
    const float xSurfaceInset =
            std::max(0.f, std::min(surfaceInset, inputBoundsInLayer.getWidth() / 2.f));
    const float ySurfaceInset =
            std::max(0.f, std::min(surfaceInset, inputBoundsInLayer.getHeight() / 2.f));

    // Apply the insets to the input bounds.
    const FloatRect insetBoundsInLayer(inputBoundsInLayer.left + xSurfaceInset,
                                       inputBoundsInLayer.top + ySurfaceInset,
                                       inputBoundsInLayer.right - xSurfaceInset,
                                       inputBoundsInLayer.bottom - ySurfaceInset);

    // Crop the input bounds to ensure it is within the parent's bounds.
    const FloatRect croppedInsetBoundsInLayer = mBounds.intersect(insetBoundsInLayer);

    const ui::Transform layerToScreen = getInputTransform();
    const ui::Transform layerToDisplay = screenToDisplay * layerToScreen;

    const Rect roundedFrameInDisplay{layerToDisplay.transform(croppedInsetBoundsInLayer)};
    info.frameLeft = roundedFrameInDisplay.left;
    info.frameTop = roundedFrameInDisplay.top;
    info.frameRight = roundedFrameInDisplay.right;
    info.frameBottom = roundedFrameInDisplay.bottom;

    ui::Transform inputToLayer;
    inputToLayer.set(insetBoundsInLayer.left, insetBoundsInLayer.top);
    const ui::Transform inputToDisplay = layerToDisplay * inputToLayer;

    // InputDispatcher expects a display-to-input transform.
    info.transform = inputToDisplay.inverse();

    // The touchable region is specified in the input coordinate space. Change it to display space.
    info.touchableRegion = inputToDisplay.transform(info.touchableRegion);
}

void Layer::fillTouchOcclusionMode(WindowInfo& info) {
+1 −1
Original line number Diff line number Diff line
@@ -1093,7 +1093,7 @@ private:
    void fillTouchOcclusionMode(gui::WindowInfo& info);

    // Fills in the frame and transform info for the gui::WindowInfo.
    void fillInputFrameInfo(gui::WindowInfo&, const ui::Transform& displayTransform);
    void fillInputFrameInfo(gui::WindowInfo&, const ui::Transform& screenToDisplay);

    // Cached properties computed from drawing state
    // Effective transform taking into account parent transforms and any parent scaling, which is