blob: 4636a994d6d4a47dda4dcb1ebfa3f8d0e029d165 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
library fuchsia.ui.observation.geometry;
using fuchsia.math as math;
using zx;
/// The maximum number of `ViewTreeSnapshot`s a client can expect in a `Watch` call's
/// response. The number of `ViewTreeSnapshot`s can be less than `BUFFER_SIZE` when
/// the size of a `Watch` call's response exceeds the limit of a FIDL channel.
///
/// There is a limit on the number of `ViewTreeSnapshot`s that can be included in a
/// `Watch` call's response due to the size restrictions of a FIDL channel. This limit
/// was calculated by finding out the maximum number of `ViewTreeSnapshot`s which can be
/// included in a `Watch` call's response when `MAX_VIEW_COUNT` is 1.
///
/// Note: This value would have to adjusted when modifying `ViewTreeSnapshot` or
/// `ViewDescriptor`.
const BUFFER_SIZE uint32 = 200;
/// The maximum number of `ViewDescriptor`s a client can expect in a
/// `ViewTreeSnapshot`.
///
/// There is a limit on the number of `ViewDescriptor`s that can be included in a
/// `ViewTreeSnapshot` due to the size restrictions of a FIDL channel. This limit was
/// calculated by finding out the maximum number of `ViewDescriptor`s which can be
/// included in a `ViewTreeSnapshot` when the `BUFFER_SIZE` is 1.
///
/// Note: This value would have to adjusted when modifying `ViewDescriptor`.
const MAX_VIEW_COUNT uint32 = 300;
/// A method of obtaining view tree snapshots for a particular view, the "context
/// view", and its child views, if any. The returned data is a sequence of
/// snapshots during the period of observation, which starts at the client's
/// prior Watch() call's [`epoch_end`] (or zx.Time 0), and ending at the
/// current [`epoch_end`]. The timebase is ZX_CLOCK_MONOTONIC.
///
/// Clients typically obtain a `ViewTreeWatcher` capability from within a test,
/// and it is not generally possible to obtain outside of a test environment.
/// For more information see `fuchsia.ui.observation.test.Registry` and
/// `fuchsia.ui.test.scene.Controller`.
///
/// Usage note. With this protocol, a client can watch for changes to the view
/// tree over which it has authority. For example, if a client owns view A, then
/// A serves as the context view for A's subtree (i.e., a "root view"), where A
/// is a parent of view B, and B is a parent of view C. The client can then
/// observe key lifecycle events in all of A, B, and C, such as newly connected
/// views, changes to view position and size, etc. In doing so, a client can
/// gate its actions on changes to the view tree, in a reliable and ergonomic
/// manner. For example, a client can wait for a descendant view C to become
/// connected before requesting a focus transfer to C.
///
/// Configuration: The context view is determined outside of this protocol.
///
/// Frequency: A client can receive one or more snapshots per frame. Clients
/// should not "count snapshots", as the per-frame snapshot count can be
/// non-deterministic. Instead, clients should look for specific conditions on
/// the snapshot state.
///
/// Issuance: If the context view is disconnected from a display, no
/// frames are issued on behalf of the context view, and a Watch() call will
/// sit quietly.
///
/// Lifecycle: The server endpoint is closed when the context view dies.
closed protocol ViewTreeWatcher {
/// A method of obtaining view tree snapshots for a particular view.
///
/// This call is formulated as a "hanging get" pattern: the client asks for
/// a set of recent snapshots, and receives them via the callback. This
/// pull-based approach ensures that clients consume events at their own
/// pace; events don't clog up the channel in an unbounded manner.
///
/// Error Handling. If Error is unset, the client may assume that the
/// the response contains updates with complete information over its epoch.
///
/// Flow control. The caller is allowed at most one in-flight |Watch| call
/// at a time; it is a logical error to have concurrent calls to |Watch|.
/// Non-compliance results in channel closure.
///
/// Client pacing. The server will dispatch snapshots to the caller on a
/// lossless, best-effort basis, but the caller must allocate enough time to
/// keep up with new snapshots.
strict Watch() -> (WatchResponse);
};
/// Response for fuchsia.ui.observation.geometry.ViewTreeWatcher.Watch.
type WatchResponse = table {
/// When the response is sent. Timebase is monotonic time.
1: epoch_end zx.Time;
/// A list of most recent updates for a particular view.
2: updates vector<ViewTreeSnapshot>:BUFFER_SIZE;
/// Only set if an error condition is detected. If unset, the client may assume
/// that updates has complete information over its epoch.
3: error Error;
};
/// A description of the context view and its descendant views, if any.
type ViewTreeSnapshot = table {
/// When the snapshot was taken. Timebase is monotonic time.
1: time zx.Time;
/// The context view (at element 0) and a complete list of its descendant views.
///
/// If `MAX_VIEW_COUNT` is exceeded, this field is not set, and an error is reported in
/// `Error`.
2: views vector<ViewDescriptor>:MAX_VIEW_COUNT;
};
// List of possible errors faced by a client during a `Watch` call.
type Error = flexible bits {
/// Set to true when appending a `ViewTreeSnapshot` in `WatchResponse.updates`
/// would exceed the limit of the FIDL channel. That snapshot is dropped, along with
/// older snapshots.
CHANNEL_OVERFLOW = 0x01;
/// Set to true when appending a `ViewTreeSnapshot` in `WatchResponse.updates`
/// would exceed `BUFFER_SIZE`. That snapshot is dropped, along with older snapshots.
BUFFER_OVERFLOW = 0x02;
/// Set to true when the size of `views` in a `ViewTreeSnapshot` exceeds
/// `MAX_VIEW_COUNT`. We represent this situation in the `ViewTreeSnapshot` with an
/// unset `views` field.
VIEWS_OVERFLOW = 0x04;
};
/// A view's bounding box, described in the view's own coordinate system.
/// Concretely, |AlignedExtent| describes the minimal and maximal points of a
/// view's bounding box, which is rectangular and axis-aligned.
///
/// Note: For describing a view's bounding box in another view's coordinate
/// system, see |RotatableExtent|.
///
/// The origin is min.
/// The size is: (abs(max.x - min.x), abs(max.y - min.y)).
type AlignedExtent = struct {
/// The minimal position of the view's bounding box.
min math.PointF;
/// The maximal position of the view's bounding box.
max math.PointF;
};
/// A view bounding box, described in another view's coordinate system.
/// Concretely, |RotatableExtent| describes the origin, size, and rotation angle
/// about the origin, for a view's bounding box.
///
/// Note: For describing a view's bounding box in the view's own coordinate
/// system, see |AlignedExtent|.
///
/// We use "V" to refer to the view being described, and "W" to refer to the
/// view where V is being described.
///
/// Note that while |angle| can be arbitrary, typical usage is axis aligned.
/// To find the bounding box of V in W in clockwise order, starting with
/// |origin|, where |angle| is 0, 90, 180, or 270, and using o=origin, w=width,
/// h=height, a=angle:
/// a= 0: (o.x, o.y), (o.x + w, o.y), (o.x + w, o.y + h), (o.x, o.y + h)
/// a= 90: (o.x, o.y), (o.x, o.y - w), (o.x + h, o.y - w), (o.x + h, o.y)
/// a=180: (o.x, o.y), (o.x - w, o.y), (o.x - w, o.y - h), (o.x, o.y - h)
/// a=270: (o.x, o.y), (o.x, o.y + w), (o.x - h, o.y + w), (o.x - h, o.y)
/// A formula based on sin a and cos a is readily obtained, but floating point
/// computation may give only approximate results.
type RotatableExtent = struct {
/// The origin point of V's bounding box, in W's coordinate system.
origin math.PointF;
/// The width of V's bounding box (along the direction where V's x axis
/// increases), in W's coordinate system.
width float32;
/// The height of V's bounding box (along the direction where V's y axis
/// increases), in W's coordinate system.
height float32;
/// The clockwise rotation about the origin, in degrees.
angle_degrees float32;
};
/// The ratio from physical pixels (of a display) to logical pixels (of the
/// coordinate system of a view).
/// - The values are placed in (x, y) order.
alias PixelScale = array<float32, 2>;
/// Geometric data of a view.
///
/// Note that these are server-side values, and some graphics APIs do not have
/// consistency guarantees with UI clients around when these values "take
/// effect". I.e., the UI client may need to be directly queried to learn what
/// values they are currently using. However, UI clients are expected to use
/// these values "immediately", within a few frames.
type Layout = struct {
/// The minimal and maximal points of a view's bounding box, in the
/// coordinate system of that view.
extent AlignedExtent;
/// The conversion ratio from physical pixels (of a display) to logical pixels
/// (of the coordinate system of the view).
pixel_scale PixelScale;
/// The offset data for the view's bounding box, in the coordinate system of
/// that view.
inset math.InsetF;
};
/// Data for a particular view: identifier, position, and children.
type ViewDescriptor = table {
/// This view's fuchsia.ui.views.ViewRef koid.
1: view_ref_koid zx.Koid;
/// This view's origin, logical size, pixel scale, and inset data, in the view's
/// own coordinate system.
///
/// Limitations. Data consistency between server and client depend on the
/// specific graphics API. Some APIs provide weak consistency, where the
/// server-side data (this data) and the client-side data (in the view's UI
/// client) are allowed to diverge for some time.
2: layout Layout;
/// This view's extent, in the context view's coordinate system.
/// It does NOT describe the child view's logical size.
///
/// This describes the "ground truth" position of this view within the context
/// view, regardless of view tree depth, or specific layout state of
/// intermediate views.
///
/// Limitations. It does NOT describe whether the view is "visible" (e.g.,
/// whether the view has opacity applied, or is not occluded by another view),
/// and it does NOT describe whether the view is "hittable" (e.g., whether the
/// view is positioned fully inside of every ancestor view's bounding box).
3: extent_in_context RotatableExtent;
/// The space occupied within the parent view's coordinate system.
/// It does NOT describe the child view's logical size.
4: extent_in_parent RotatableExtent;
/// The list of child views, in the order known to the graphics API.
///
/// Each integer in this vector refers to the child's position in the
/// |views| or |incomplete| vector that the parent is in.
///
/// The identity, position, and size of each child view. Position and size are
/// described by the extent of the child view within the parent view's
/// coordinate system.
///
/// The view tree topology is reliable. A child placed here is equivalent to
/// the parent view receiving a "child view connected" signal.
///
/// Limitations. A child's view boundary is described in the parent view's
/// coordinate system, which is subject to weak consistency (depending on the
/// graphics API). That is, when a parent view has a change in size or metrics,
/// the context view may observe a "jump" as the parent view incorporates those
/// data. In such cases, a new ViewTreeSnapshot is issued to describe the
/// change in position, relative to the context view.
5: children vector<uint32>:MAX_VIEW_COUNT;
};