1// Copyright 2013 The Flutter Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5// TODO(dnfield): Remove unused_import ignores when https://github.com/dart-lang/sdk/issues/35164 is resolved. 6 7part of dart.ui; 8 9// ignore: unused_element 10String _decodeUTF8(ByteData message) { 11 return message != null ? utf8.decoder.convert(message.buffer.asUint8List()) : null; 12} 13 14// ignore: unused_element 15dynamic _decodeJSON(String message) { 16 return message != null ? json.decode(message) : null; 17} 18 19@pragma('vm:entry-point') 20// ignore: unused_element 21void _updateWindowMetrics( 22 double devicePixelRatio, 23 double width, 24 double height, 25 double depth, 26 double viewPaddingTop, 27 double viewPaddingRight, 28 double viewPaddingBottom, 29 double viewPaddingLeft, 30 double viewInsetTop, 31 double viewInsetRight, 32 double viewInsetBottom, 33 double viewInsetLeft, 34 double systemGestureInsetTop, 35 double systemGestureInsetRight, 36 double systemGestureInsetBottom, 37 double systemGestureInsetLeft, 38) { 39 window 40 .._devicePixelRatio = devicePixelRatio 41 .._physicalSize = Size(width, height) 42 .._physicalDepth = depth 43 .._viewPadding = WindowPadding._( 44 top: viewPaddingTop, 45 right: viewPaddingRight, 46 bottom: viewPaddingBottom, 47 left: viewPaddingLeft) 48 .._viewInsets = WindowPadding._( 49 top: viewInsetTop, 50 right: viewInsetRight, 51 bottom: viewInsetBottom, 52 left: viewInsetLeft) 53 .._padding = WindowPadding._( 54 top: math.max(0.0, viewPaddingTop - viewInsetTop), 55 right: math.max(0.0, viewPaddingRight - viewInsetRight), 56 bottom: math.max(0.0, viewPaddingBottom - viewInsetBottom), 57 left: math.max(0.0, viewPaddingLeft - viewInsetLeft)) 58 .._systemGestureInsets = WindowPadding._( 59 top: math.max(0.0, systemGestureInsetTop), 60 right: math.max(0.0, systemGestureInsetRight), 61 bottom: math.max(0.0, systemGestureInsetBottom), 62 left: math.max(0.0, systemGestureInsetLeft)); 63 _invoke(window.onMetricsChanged, window._onMetricsChangedZone); 64} 65 66typedef _LocaleClosure = String Function(); 67 68String _localeClosure() { 69 if (window.locale == null) { 70 return null; 71 } 72 return window.locale.toString(); 73} 74 75@pragma('vm:entry-point') 76// ignore: unused_element 77_LocaleClosure _getLocaleClosure() => _localeClosure; 78 79@pragma('vm:entry-point') 80// ignore: unused_element 81void _updateLocales(List<String> locales) { 82 const int stringsPerLocale = 4; 83 final int numLocales = locales.length ~/ stringsPerLocale; 84 window._locales = List<Locale>(numLocales); 85 for (int localeIndex = 0; localeIndex < numLocales; localeIndex++) { 86 final String countryCode = locales[localeIndex * stringsPerLocale + 1]; 87 final String scriptCode = locales[localeIndex * stringsPerLocale + 2]; 88 89 window._locales[localeIndex] = Locale.fromSubtags( 90 languageCode: locales[localeIndex * stringsPerLocale], 91 countryCode: countryCode.isEmpty ? null : countryCode, 92 scriptCode: scriptCode.isEmpty ? null : scriptCode, 93 ); 94 } 95 _invoke(window.onLocaleChanged, window._onLocaleChangedZone); 96} 97 98@pragma('vm:entry-point') 99// ignore: unused_element 100void _updateUserSettingsData(String jsonData) { 101 final Map<String, dynamic> data = json.decode(jsonData); 102 if (data.isEmpty) { 103 return; 104 } 105 _updateTextScaleFactor(data['textScaleFactor'].toDouble()); 106 _updateAlwaysUse24HourFormat(data['alwaysUse24HourFormat']); 107 _updatePlatformBrightness(data['platformBrightness']); 108} 109 110@pragma('vm:entry-point') 111// ignore: unused_element 112void _updateLifecycleState(String state) { 113 // We do not update the state if the state has already been used to initialize 114 // the lifecycleState. 115 if (!window._initialLifecycleStateAccessed) 116 window._initialLifecycleState = state; 117} 118 119 120void _updateTextScaleFactor(double textScaleFactor) { 121 window._textScaleFactor = textScaleFactor; 122 _invoke(window.onTextScaleFactorChanged, window._onTextScaleFactorChangedZone); 123} 124 125void _updateAlwaysUse24HourFormat(bool alwaysUse24HourFormat) { 126 window._alwaysUse24HourFormat = alwaysUse24HourFormat; 127} 128 129void _updatePlatformBrightness(String brightnessName) { 130 window._platformBrightness = brightnessName == 'dark' ? Brightness.dark : Brightness.light; 131 _invoke(window.onPlatformBrightnessChanged, window._onPlatformBrightnessChangedZone); 132} 133 134@pragma('vm:entry-point') 135// ignore: unused_element 136void _updateSemanticsEnabled(bool enabled) { 137 window._semanticsEnabled = enabled; 138 _invoke(window.onSemanticsEnabledChanged, window._onSemanticsEnabledChangedZone); 139} 140 141@pragma('vm:entry-point') 142// ignore: unused_element 143void _updateAccessibilityFeatures(int values) { 144 final AccessibilityFeatures newFeatures = AccessibilityFeatures._(values); 145 if (newFeatures == window._accessibilityFeatures) 146 return; 147 window._accessibilityFeatures = newFeatures; 148 _invoke(window.onAccessibilityFeaturesChanged, window._onAccessibilityFlagsChangedZone); 149} 150 151@pragma('vm:entry-point') 152void _dispatchPlatformMessage(String name, ByteData data, int responseId) { 153 if (window.onPlatformMessage != null) { 154 _invoke3<String, ByteData, PlatformMessageResponseCallback>( 155 window.onPlatformMessage, 156 window._onPlatformMessageZone, 157 name, 158 data, 159 (ByteData responseData) { 160 window._respondToPlatformMessage(responseId, responseData); 161 }, 162 ); 163 } else { 164 window._respondToPlatformMessage(responseId, null); 165 } 166} 167 168@pragma('vm:entry-point') 169// ignore: unused_element 170void _dispatchPointerDataPacket(ByteData packet) { 171 if (window.onPointerDataPacket != null) 172 _invoke1<PointerDataPacket>(window.onPointerDataPacket, window._onPointerDataPacketZone, _unpackPointerDataPacket(packet)); 173} 174 175@pragma('vm:entry-point') 176// ignore: unused_element 177void _dispatchSemanticsAction(int id, int action, ByteData args) { 178 _invoke3<int, SemanticsAction, ByteData>( 179 window.onSemanticsAction, 180 window._onSemanticsActionZone, 181 id, 182 SemanticsAction.values[action], 183 args, 184 ); 185} 186 187@pragma('vm:entry-point') 188// ignore: unused_element 189void _beginFrame(int microseconds) { 190 _invoke1<Duration>(window.onBeginFrame, window._onBeginFrameZone, Duration(microseconds: microseconds)); 191} 192 193@pragma('vm:entry-point') 194// ignore: unused_element 195void _reportTimings(List<int> timings) { 196 assert(timings.length % FramePhase.values.length == 0); 197 final List<FrameTiming> frameTimings = <FrameTiming>[]; 198 for (int i = 0; i < timings.length; i += FramePhase.values.length) { 199 frameTimings.add(FrameTiming(timings.sublist(i, i + FramePhase.values.length))); 200 } 201 _invoke1(window.onReportTimings, window._onReportTimingsZone, frameTimings); 202} 203 204@pragma('vm:entry-point') 205// ignore: unused_element 206void _drawFrame() { 207 _invoke(window.onDrawFrame, window._onDrawFrameZone); 208} 209 210// ignore: always_declare_return_types, prefer_generic_function_type_aliases 211typedef _UnaryFunction(Null args); 212// ignore: always_declare_return_types, prefer_generic_function_type_aliases 213typedef _BinaryFunction(Null args, Null message); 214 215@pragma('vm:entry-point') 216// ignore: unused_element 217void _runMainZoned(Function startMainIsolateFunction, 218 Function userMainFunction, 219 List<String> args) { 220 startMainIsolateFunction((){ 221 runZoned<void>(() { 222 if (userMainFunction is _BinaryFunction) { 223 // This seems to be undocumented but supported by the command line VM. 224 // Let's do the same in case old entry-points are ported to Flutter. 225 (userMainFunction as dynamic)(args, ''); 226 } else if (userMainFunction is _UnaryFunction) { 227 (userMainFunction as dynamic)(args); 228 } else { 229 userMainFunction(); 230 } 231 }, onError: (Object error, StackTrace stackTrace) { 232 _reportUnhandledException(error.toString(), stackTrace.toString()); 233 }); 234 }, null); 235} 236 237void _reportUnhandledException(String error, String stackTrace) native 'Window_reportUnhandledException'; 238 239/// Invokes [callback] inside the given [zone]. 240void _invoke(void callback(), Zone zone) { 241 if (callback == null) 242 return; 243 244 assert(zone != null); 245 246 if (identical(zone, Zone.current)) { 247 callback(); 248 } else { 249 zone.runGuarded(callback); 250 } 251} 252 253/// Invokes [callback] inside the given [zone] passing it [arg]. 254void _invoke1<A>(void callback(A a), Zone zone, A arg) { 255 if (callback == null) 256 return; 257 258 assert(zone != null); 259 260 if (identical(zone, Zone.current)) { 261 callback(arg); 262 } else { 263 zone.runUnaryGuarded<A>(callback, arg); 264 } 265} 266 267/// Invokes [callback] inside the given [zone] passing it [arg1] and [arg2]. 268// ignore: unused_element 269void _invoke2<A1, A2>(void callback(A1 a1, A2 a2), Zone zone, A1 arg1, A2 arg2) { 270 if (callback == null) 271 return; 272 273 assert(zone != null); 274 275 if (identical(zone, Zone.current)) { 276 callback(arg1, arg2); 277 } else { 278 zone.runBinaryGuarded<A1, A2>(callback, arg1, arg2); 279 } 280} 281 282/// Invokes [callback] inside the given [zone] passing it [arg1], [arg2] and [arg3]. 283void _invoke3<A1, A2, A3>(void callback(A1 a1, A2 a2, A3 a3), Zone zone, A1 arg1, A2 arg2, A3 arg3) { 284 if (callback == null) 285 return; 286 287 assert(zone != null); 288 289 if (identical(zone, Zone.current)) { 290 callback(arg1, arg2, arg3); 291 } else { 292 zone.runGuarded(() { 293 callback(arg1, arg2, arg3); 294 }); 295 } 296} 297 298// If this value changes, update the encoding code in the following files: 299// 300// * pointer_data.cc 301// * FlutterView.java 302const int _kPointerDataFieldCount = 24; 303 304PointerDataPacket _unpackPointerDataPacket(ByteData packet) { 305 const int kStride = Int64List.bytesPerElement; 306 const int kBytesPerPointerData = _kPointerDataFieldCount * kStride; 307 final int length = packet.lengthInBytes ~/ kBytesPerPointerData; 308 assert(length * kBytesPerPointerData == packet.lengthInBytes); 309 final List<PointerData> data = List<PointerData>(length); 310 for (int i = 0; i < length; ++i) { 311 int offset = i * _kPointerDataFieldCount; 312 data[i] = PointerData( 313 timeStamp: Duration(microseconds: packet.getInt64(kStride * offset++, _kFakeHostEndian)), 314 change: PointerChange.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], 315 kind: PointerDeviceKind.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], 316 signalKind: PointerSignalKind.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], 317 device: packet.getInt64(kStride * offset++, _kFakeHostEndian), 318 physicalX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), 319 physicalY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), 320 buttons: packet.getInt64(kStride * offset++, _kFakeHostEndian), 321 obscured: packet.getInt64(kStride * offset++, _kFakeHostEndian) != 0, 322 pressure: packet.getFloat64(kStride * offset++, _kFakeHostEndian), 323 pressureMin: packet.getFloat64(kStride * offset++, _kFakeHostEndian), 324 pressureMax: packet.getFloat64(kStride * offset++, _kFakeHostEndian), 325 distance: packet.getFloat64(kStride * offset++, _kFakeHostEndian), 326 distanceMax: packet.getFloat64(kStride * offset++, _kFakeHostEndian), 327 size: packet.getFloat64(kStride * offset++, _kFakeHostEndian), 328 radiusMajor: packet.getFloat64(kStride * offset++, _kFakeHostEndian), 329 radiusMinor: packet.getFloat64(kStride * offset++, _kFakeHostEndian), 330 radiusMin: packet.getFloat64(kStride * offset++, _kFakeHostEndian), 331 radiusMax: packet.getFloat64(kStride * offset++, _kFakeHostEndian), 332 orientation: packet.getFloat64(kStride * offset++, _kFakeHostEndian), 333 tilt: packet.getFloat64(kStride * offset++, _kFakeHostEndian), 334 platformData: packet.getInt64(kStride * offset++, _kFakeHostEndian), 335 scrollDeltaX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), 336 scrollDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian) 337 ); 338 assert(offset == (i + 1) * _kPointerDataFieldCount); 339 } 340 return PointerDataPacket(data: data); 341} 342