feat(driver): expose Snapshot on DeviceDriver and sidecar client

Snapshot wraps the new atomic-snapshot gRPC: the runner gets hierarchy
and screenshot from one round-trip whose two reads are serialized on
the sidecar side.
This commit is contained in:
pj committed 2026-05-31 15:25:58 +05:30
1 parent 8f29c63d12
commit c11beb1728
2 files changed
+22

No files matched your search

+5
View File
@@ -22,6 +22,11 @@ type DeviceDriver interface {
Hierarchy(ctx context.Context) (string, error)
Screenshot(ctx context.Context) (Image, error)
// Snapshot returns the hierarchy and screenshot captured back-to-back
// under a backend-side mutex, so the pair describes the same on-device
// frame. Prefer this over calling Hierarchy and Screenshot separately:
// independent reads can land on different frames during transitions.
Snapshot(ctx context.Context) (string, Image, error)
// RecentLogs returns log entries at or after `since`, filtered to
// `minLevel` or above. An empty minLevel defaults to "E".
RecentLogs(ctx context.Context, since time.Time, minLevel string) ([]LogEntry, error)
+17
View File
@@ -162,6 +162,23 @@ func (c *Client) Screenshot(ctx context.Context) (driver.Image, error) {
}, nil
}
// Snapshot fetches hierarchy and screenshot in a single sidecar round-trip.
// The sidecar serializes the two reads behind a mutex so the returned pair
// describes the same on-device frame, removing the cross-fade race the
// runner used to see when fetching them as independent goroutines.
func (c *Client) Snapshot(ctx context.Context) (string, driver.Image, error) {
response, err := c.stub.Snapshot(ctx, &driverpb.Empty{})
if err != nil {
return "", driver.Image{}, err
}
image := response.GetScreenshot()
return response.GetHierarchy().GetJson(), driver.Image{
PNG: image.GetPng(),
Width: int(image.GetWidth()),
Height: int(image.GetHeight()),
}, nil
}
func (c *Client) WaitForIdle(ctx context.Context, duration time.Duration) error {
_, err := c.stub.WaitForIdle(ctx, &driverpb.Duration{Millis: duration.Milliseconds()})
return err