blob: 39a161a05ee44c39197df97796e786b26d3bb0af [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'dart:io';
import 'dart:ui';
import 'dart:zircon';
import 'package:args/args.dart';
import 'package:vector_math/vector_math_64.dart' as vector_math_64;
final _argsCsvFilePath = '/config/data/args.csv';
void main(List<String> args) async {
print('Launching embedding-flutter-view');
args = args + _GetArgsFromConfigFile();
final parser = ArgParser()
..addFlag('showOverlay', defaultsTo: false)
..addFlag('focusable', defaultsTo: true);
final arguments = parser.parse(args);
for (final option in arguments.options) {
print('embedding-flutter-view args: $option: ${arguments[option]}');
}
TestApp app = TestApp(
ChildView(await _launchChildView()),
showOverlay: arguments['showOverlay'],
focusable: arguments['focusable'],
);
app.run();
}
class TestApp {
static const _black = Color.fromARGB(255, 0, 0, 0);
static const _blue = Color.fromARGB(255, 0, 0, 255);
final ChildView childView;
final bool showOverlay;
final bool focusable;
Color _backgroundColor = _blue;
TestApp(
this.childView,
{this.showOverlay = false,
this.focusable = true}) {
}
void run() {
childView.create(focusable, (ByteData? reply) {
// Set up window callbacks.
window.onPointerDataPacket = (PointerDataPacket packet) {
this.pointerDataPacket(packet);
};
window.onMetricsChanged = () {
window.scheduleFrame();
};
window.onBeginFrame = (Duration duration) {
this.beginFrame(duration);
};
// The child view should be attached to Scenic now.
// Ready to build the scene.
window.scheduleFrame();
});
}
void beginFrame(Duration duration) {
// Convert physical screen size of device to values
final pixelRatio = window.devicePixelRatio;
final size = window.physicalSize / pixelRatio;
final physicalBounds = Offset.zero & window.physicalSize;
final windowBounds = Offset.zero & size;
// Set up a Canvas that uses the screen size
final recorder = PictureRecorder();
final canvas = Canvas(recorder, physicalBounds);
canvas.scale(pixelRatio);
// Draw something
final paint = Paint()..color = this._backgroundColor;
canvas.drawRect(windowBounds, paint);
final picture = recorder.endRecording();
// Build the scene
final sceneBuilder = SceneBuilder()
..pushClipRect(physicalBounds)
..addPicture(Offset.zero, picture);
final childPhysicalSize = window.physicalSize * 0.25;
// Alignment.center
final windowCenter = size.center(Offset.zero);
final windowPhysicalCenter = window.physicalSize.center(Offset.zero);
final childPhysicalOffset = windowPhysicalCenter - childPhysicalSize.center(Offset.zero);
sceneBuilder
..pushTransform(
vector_math_64.Matrix4.translationValues(childPhysicalOffset.dx,
childPhysicalOffset.dy,
0.0).storage)
..addPlatformView(childView.viewId,
width: childPhysicalSize.width,
height: childPhysicalSize.height)
..pop();
if (showOverlay) {
final containerSize = size * 0.5;
// Alignment.center
final containerOffset = windowCenter - containerSize.center(Offset.zero);
final overlaySize = containerSize * 0.5;
// Alignment.topRight
final overlayOffset = Offset(
containerOffset.dx + containerSize.width - overlaySize.width,
containerOffset.dy);
final overlayPhysicalSize = overlaySize * pixelRatio;
final overlayPhysicalOffset = overlayOffset * pixelRatio;
final overlayPhysicalBounds = overlayPhysicalOffset & overlayPhysicalSize;
final recorder = PictureRecorder();
final overlayCullRect = Offset.zero & overlayPhysicalSize; // in canvas physical coordinates
final canvas = Canvas(recorder, overlayCullRect);
canvas.scale(pixelRatio);
final paint = Paint()..color = Color.fromARGB(255, 0, 255, 0);
canvas.drawRect(Offset.zero & overlaySize, paint);
final overlayPicture = recorder.endRecording();
sceneBuilder
..pushClipRect(overlayPhysicalBounds) // in window physical coordinates
..addPicture(overlayPhysicalOffset, overlayPicture)
..pop();
}
sceneBuilder.pop();
window.render(sceneBuilder.build());
}
void pointerDataPacket(PointerDataPacket packet) async {
int nowNanos = System.clockGetMonotonic();
for (PointerData data in packet.data) {
print('embedding-flutter-view received tap: ${data.toStringFull()}');
if (data.change == PointerChange.down) {
this._backgroundColor = _black;
}
if (data.change == PointerChange.down || data.change == PointerChange.move) {
_reportTouchInput(
localX: data.physicalX,
localY: data.physicalY,
timeReceived: nowNanos,
);
}
}
window.scheduleFrame();
}
void _reportTouchInput({required double localX, required double localY, required int timeReceived}) {
print('embedding-flutter-view reporting touch input to TouchInputListener');
final message = utf8.encode(json.encode({
'method': 'TouchInputListener.ReportTouchInput',
'local_x': localX,
'local_y': localY,
'time_received': timeReceived,
'component_name': 'embedding-flutter-view',
})).buffer.asByteData();
PlatformDispatcher.instance.sendPlatformMessage('fuchsia/input_test', message, null);
}
}
class ChildView {
final int viewId;
ChildView(this.viewId);
void create(
bool focusable,
PlatformMessageResponseCallback callback) {
// Construct the dart:ui platform message to create the view, and when the
// return callback is invoked, build the scene. At that point, it is safe
// to embed the child view in the scene.
final viewOcclusionHint = Rect.zero;
final Map<String, dynamic> args = <String, dynamic>{
'viewId': viewId,
// Flatland doesn't support disabling hit testing.
'hitTestable': true,
'focusable': focusable,
'viewOcclusionHintLTRB': <double>[
viewOcclusionHint.left,
viewOcclusionHint.top,
viewOcclusionHint.right,
viewOcclusionHint.bottom
],
};
final ByteData createViewMessage = utf8.encode(
json.encode(<String, Object>{
'method': 'View.create',
'args': args,
})
).buffer.asByteData();
final platformViewsChannel = 'flutter/platform_views';
PlatformDispatcher.instance.sendPlatformMessage(
platformViewsChannel,
createViewMessage,
callback);
}
}
Future<int> _launchChildView() async {
final message = Int8List.fromList([0x31]);
final completer = new Completer<ByteData>();
PlatformDispatcher.instance.sendPlatformMessage(
'fuchsia/child_view', ByteData.sublistView(message), (ByteData? reply) {
completer.complete(reply!);
});
return int.parse(
ascii.decode(((await completer.future).buffer.asUint8List())));
}
List<String> _GetArgsFromConfigFile() {
List<String> args;
final f = File(_argsCsvFilePath);
if (!f.existsSync()) {
return List.empty();
}
final fileContentCsv = f.readAsStringSync();
args = fileContentCsv.split('\n');
return args;
}