mirror of
https://github.com/wgh136/flutter_qjs.git
synced 2025-09-27 13:27:24 +00:00
init
This commit is contained in:
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
.DS_Store
|
||||
.dart_tool/
|
||||
|
||||
.packages
|
||||
.pub/
|
||||
|
||||
build/
|
19
.idea/libraries/Dart_SDK.xml
generated
Normal file
19
.idea/libraries/Dart_SDK.xml
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
<component name="libraryTable">
|
||||
<library name="Dart SDK">
|
||||
<CLASSES>
|
||||
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/async" />
|
||||
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/collection" />
|
||||
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/convert" />
|
||||
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/core" />
|
||||
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/developer" />
|
||||
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/html" />
|
||||
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/io" />
|
||||
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/isolate" />
|
||||
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/math" />
|
||||
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/mirrors" />
|
||||
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/typed_data" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
10
.idea/modules.xml
generated
Normal file
10
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/flutter_qjs.iml" filepath="$PROJECT_DIR$/flutter_qjs.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/android/flutter_qjs_android.iml" filepath="$PROJECT_DIR$/android/flutter_qjs_android.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/example/android/flutter_qjs_example_android.iml" filepath="$PROJECT_DIR$/example/android/flutter_qjs_example_android.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
6
.idea/runConfigurations/example_lib_main_dart.xml
generated
Normal file
6
.idea/runConfigurations/example_lib_main_dart.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="example/lib/main.dart" type="FlutterRunConfigurationType" factoryName="Flutter">
|
||||
<option name="filePath" value="$PROJECT_DIR$/example/lib/main.dart" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
45
.idea/workspace.xml
generated
Normal file
45
.idea/workspace.xml
generated
Normal file
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="FileEditorManager">
|
||||
<leaf>
|
||||
<file leaf-file-name="flutter_qjs.dart" pinned="false" current-in-tab="true">
|
||||
<entry file="file://$PROJECT_DIR$/lib/flutter_qjs.dart">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="0">
|
||||
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file leaf-file-name="main.dart" pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/example/lib/main.dart">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="0">
|
||||
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
</leaf>
|
||||
</component>
|
||||
<component name="ToolWindowManager">
|
||||
<editor active="true" />
|
||||
<layout>
|
||||
<window_info id="Project" active="true" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
|
||||
</layout>
|
||||
</component>
|
||||
<component name="ProjectView">
|
||||
<navigator currentView="ProjectPane" proportions="" version="1">
|
||||
</navigator>
|
||||
<panes>
|
||||
<pane id="ProjectPane">
|
||||
<option name="show-excluded-files" value="false" />
|
||||
</pane>
|
||||
</panes>
|
||||
</component>
|
||||
<component name="PropertiesComponent">
|
||||
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
|
||||
<property name="dart.analysis.tool.window.force.activate" value="true" />
|
||||
<property name="show.migrate.to.gradle.popup" value="false" />
|
||||
</component>
|
||||
</project>
|
10
.metadata
Normal file
10
.metadata
Normal file
@@ -0,0 +1,10 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: f3b7788f7754a51092ae1d677001767960c21910
|
||||
channel: master
|
||||
|
||||
project_type: plugin
|
14
.vscode/launch.json
vendored
Normal file
14
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
// 使用 IntelliSense 了解相关属性。
|
||||
// 悬停以查看现有属性的描述。
|
||||
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Flutter",
|
||||
"program": "lib/main.dart",
|
||||
"request": "launch",
|
||||
"type": "dart"
|
||||
}
|
||||
]
|
||||
}
|
62
.vscode/settings.json
vendored
Normal file
62
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"algorithm": "cpp",
|
||||
"atomic": "cpp",
|
||||
"cctype": "cpp",
|
||||
"chrono": "cpp",
|
||||
"cmath": "cpp",
|
||||
"concepts": "cpp",
|
||||
"condition_variable": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"deque": "cpp",
|
||||
"exception": "cpp",
|
||||
"resumable": "cpp",
|
||||
"functional": "cpp",
|
||||
"future": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"ios": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"iostream": "cpp",
|
||||
"istream": "cpp",
|
||||
"iterator": "cpp",
|
||||
"limits": "cpp",
|
||||
"list": "cpp",
|
||||
"map": "cpp",
|
||||
"memory": "cpp",
|
||||
"mutex": "cpp",
|
||||
"new": "cpp",
|
||||
"ostream": "cpp",
|
||||
"queue": "cpp",
|
||||
"ratio": "cpp",
|
||||
"set": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"string": "cpp",
|
||||
"system_error": "cpp",
|
||||
"thread": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"utility": "cpp",
|
||||
"vector": "cpp",
|
||||
"xfacet": "cpp",
|
||||
"xhash": "cpp",
|
||||
"xiosbase": "cpp",
|
||||
"xlocale": "cpp",
|
||||
"xlocinfo": "cpp",
|
||||
"xlocnum": "cpp",
|
||||
"xmemory": "cpp",
|
||||
"xstddef": "cpp",
|
||||
"xstring": "cpp",
|
||||
"xtr1common": "cpp",
|
||||
"xtree": "cpp",
|
||||
"xutility": "cpp"
|
||||
}
|
||||
}
|
3
CHANGELOG.md
Normal file
3
CHANGELOG.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.0.1
|
||||
|
||||
* TODO: Describe initial release.
|
18
README.md
Normal file
18
README.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# flutter_qjs
|
||||
|
||||
A new flutter plugin project.
|
||||
|
||||
## Getting Started
|
||||
|
||||
This project is a starting point for a Flutter
|
||||
[plug-in package](https://flutter.dev/developing-packages/),
|
||||
a specialized package that includes platform-specific implementation code for
|
||||
Android and/or iOS.
|
||||
|
||||
For help getting started with Flutter, view our
|
||||
[online documentation](https://flutter.dev/docs), which offers tutorials,
|
||||
samples, guidance on mobile development, and a full API reference.
|
||||
|
||||
The plugin project was generated without specifying the `--platforms` flag, no platforms are currently supported.
|
||||
To add platforms, run `flutter create -t plugin --platforms <platforms> .` under the same
|
||||
directory. You can also find a detailed instruction on how to add platforms in the `pubspec.yaml` at https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms.
|
41
example/.gitignore
vendored
Normal file
41
example/.gitignore
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
**/ios/Flutter/.last_build_id
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
.packages
|
||||
.pub-cache/
|
||||
.pub/
|
||||
/build/
|
||||
|
||||
# Web related
|
||||
lib/generated_plugin_registrant.dart
|
||||
|
||||
# Symbolication related
|
||||
app.*.symbols
|
||||
|
||||
# Obfuscation related
|
||||
app.*.map.json
|
10
example/.metadata
Normal file
10
example/.metadata
Normal file
@@ -0,0 +1,10 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: f3b7788f7754a51092ae1d677001767960c21910
|
||||
channel: master
|
||||
|
||||
project_type: app
|
16
example/README.md
Normal file
16
example/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# flutter_qjs_example
|
||||
|
||||
Demonstrates how to use the flutter_qjs plugin.
|
||||
|
||||
## Getting Started
|
||||
|
||||
This project is a starting point for a Flutter application.
|
||||
|
||||
A few resources to get you started if this is your first Flutter project:
|
||||
|
||||
- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
|
||||
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
|
||||
|
||||
For help getting started with Flutter, view our
|
||||
[online documentation](https://flutter.dev/docs), which offers tutorials,
|
||||
samples, guidance on mobile development, and a full API reference.
|
34
example/lib/code/editor.dart
Normal file
34
example/lib/code/editor.dart
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* @Description:
|
||||
* @Author: ekibun
|
||||
* @Date: 2020-08-01 13:20:06
|
||||
* @LastEditors: ekibun
|
||||
* @LastEditTime: 2020-08-08 17:52:22
|
||||
*/
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'highlight.dart';
|
||||
|
||||
class CodeEditor extends StatefulWidget {
|
||||
final void Function(String) onChanged;
|
||||
|
||||
const CodeEditor({Key key, this.onChanged}) : super(key: key);
|
||||
@override
|
||||
_CodeEditorState createState() => _CodeEditorState();
|
||||
}
|
||||
|
||||
class _CodeEditorState extends State<CodeEditor> {
|
||||
CodeInputController _controller = CodeInputController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextField(
|
||||
autofocus: true,
|
||||
controller: _controller,
|
||||
textCapitalization: TextCapitalization.none,
|
||||
decoration: null,
|
||||
maxLines: null,
|
||||
onChanged: this.widget.onChanged,
|
||||
);
|
||||
}
|
||||
}
|
106
example/lib/code/highlight.dart
Normal file
106
example/lib/code/highlight.dart
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* @Description: Code highlight controller
|
||||
* @Author: ekibun
|
||||
* @Date: 2020-08-01 17:42:06
|
||||
* @LastEditors: ekibun
|
||||
* @LastEditTime: 2020-08-02 12:39:26
|
||||
*/
|
||||
import 'dart:math';
|
||||
import 'dart:ui';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_highlight/themes/a11y-light.dart';
|
||||
import 'package:highlight/highlight.dart';
|
||||
|
||||
Map<String, TextStyle> _theme = a11yLightTheme;
|
||||
List<TextSpan> _convert(String code) {
|
||||
var nodes = highlight.parse(code, language: 'javascript').nodes;
|
||||
List<TextSpan> spans = [];
|
||||
var currentSpans = spans;
|
||||
List<List<TextSpan>> stack = [];
|
||||
|
||||
_traverse(Node node) {
|
||||
if (node.value != null) {
|
||||
currentSpans.add(node.className == null
|
||||
? TextSpan(text: node.value)
|
||||
: TextSpan(text: node.value, style: _theme[node.className]));
|
||||
} else if (node.children != null) {
|
||||
List<TextSpan> tmp = [];
|
||||
currentSpans.add(TextSpan(children: tmp, style: _theme[node.className]));
|
||||
stack.add(currentSpans);
|
||||
currentSpans = tmp;
|
||||
|
||||
node.children.forEach((n) {
|
||||
_traverse(n);
|
||||
if (n == node.children.last) {
|
||||
currentSpans = stack.isEmpty ? spans : stack.removeLast();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (var node in nodes) {
|
||||
_traverse(node);
|
||||
}
|
||||
|
||||
return spans;
|
||||
}
|
||||
|
||||
class CodeInputController extends TextEditingController {
|
||||
CodeInputController({String text}) : super(text: text);
|
||||
|
||||
TextSpan oldSpan = TextSpan();
|
||||
Future<void> spanCall;
|
||||
|
||||
@override
|
||||
TextSpan buildTextSpan({TextStyle style, bool withComposing}) {
|
||||
String oldText = oldSpan.toPlainText();
|
||||
String newText = value.text;
|
||||
if (oldText == newText) return oldSpan;
|
||||
(spanCall?.timeout(Duration.zero) ?? Future.delayed(Duration.zero))
|
||||
.then((_) => spanCall = compute(_convert, value.text).then((lsSpan) {
|
||||
TextSpan newSpan = TextSpan(style: style, children: lsSpan);
|
||||
if (newSpan.toPlainText() == value.text) oldSpan = newSpan;
|
||||
notifyListeners();
|
||||
}))
|
||||
.catchError((_) => {});
|
||||
|
||||
List<TextSpan> beforeSpans = [];
|
||||
int splitAt = value.selection.start;
|
||||
if (splitAt < 0) splitAt = newText.length ~/ 2;
|
||||
int start = 0;
|
||||
InlineSpan leftSpan;
|
||||
oldSpan.children?.indexWhere((element) {
|
||||
String elementText = element.toPlainText();
|
||||
if (start + elementText.length > splitAt || !newText.startsWith(elementText, start)) {
|
||||
leftSpan = element;
|
||||
return true;
|
||||
}
|
||||
beforeSpans.add(element);
|
||||
start += elementText.length;
|
||||
return false;
|
||||
});
|
||||
List<TextSpan> endSpans = [];
|
||||
int end = 0;
|
||||
InlineSpan rightSpan;
|
||||
oldSpan.children?.sublist(beforeSpans.length)?.lastIndexWhere((element) {
|
||||
String elementText = element.toPlainText();
|
||||
if (splitAt + end + elementText.length >= newText.length ||
|
||||
!newText.substring(start, newText.length - end).endsWith(elementText)) {
|
||||
rightSpan = element;
|
||||
return true;
|
||||
}
|
||||
endSpans.add(element);
|
||||
end += elementText.length;
|
||||
return false;
|
||||
});
|
||||
|
||||
return TextSpan(style: style, children: [
|
||||
...beforeSpans,
|
||||
TextSpan(
|
||||
style: leftSpan != null && leftSpan == rightSpan ? leftSpan.style : style,
|
||||
text: newText.substring(start, max(start, newText.length - end))),
|
||||
...endSpans.reversed
|
||||
]);
|
||||
}
|
||||
}
|
39
example/lib/main.dart
Normal file
39
example/lib/main.dart
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* @Description:
|
||||
* @Author: ekibun
|
||||
* @Date: 2020-08-08 08:16:51
|
||||
* @LastEditors: ekibun
|
||||
* @LastEditTime: 2020-08-08 17:50:30
|
||||
*/
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_qjs_example/test.dart';
|
||||
|
||||
void main() {
|
||||
runApp(MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
|
||||
const MyApp({ Key key }) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'flutter_qjs',
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: ThemeData(
|
||||
appBarTheme: AppBarTheme(brightness: Brightness.dark, elevation: 0),
|
||||
primaryColor: Color(0xfff09199),
|
||||
accentColor: Color(0xffec818a),
|
||||
backgroundColor: Colors.grey[300],
|
||||
primaryColorBrightness: Brightness.dark,
|
||||
),
|
||||
routes: {
|
||||
'home': (BuildContext context) => TestPage(),
|
||||
},
|
||||
initialRoute: 'home',
|
||||
);
|
||||
}
|
||||
|
||||
}
|
102
example/lib/test.dart
Normal file
102
example/lib/test.dart
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* @Description:
|
||||
* @Author: ekibun
|
||||
* @Date: 2020-07-18 23:28:55
|
||||
* @LastEditors: ekibun
|
||||
* @LastEditTime: 2020-08-08 17:38:48
|
||||
*/
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_qjs/flutter_qjs.dart';
|
||||
|
||||
import 'code/editor.dart';
|
||||
|
||||
class TestPage extends StatefulWidget {
|
||||
@override
|
||||
State<StatefulWidget> createState() => _TestPageState();
|
||||
}
|
||||
|
||||
class _TestPageState extends State<TestPage> {
|
||||
|
||||
String code, resp;
|
||||
int engine;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("JS 引擎功能测试"),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: [
|
||||
FlatButton(child: Text("初始化引擎"), onPressed: () async {
|
||||
if ((engine?? 0) != 0) return;
|
||||
engine = await FlutterJs.initEngine();
|
||||
// dart 函数回调
|
||||
FlutterJs.methodHandler = (String method, List arg) async {
|
||||
switch (method) {
|
||||
case "delay":
|
||||
await Future.delayed(Duration(milliseconds: arg[0]));
|
||||
return;
|
||||
case "http":
|
||||
Response response = await Dio().get(arg[0]);
|
||||
return response.data;
|
||||
default:
|
||||
}
|
||||
};
|
||||
}),
|
||||
FlatButton(child: Text("运行"), onPressed: () async {
|
||||
if ((engine?? 0) == 0) {
|
||||
print("请先初始化引擎");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
resp = await FlutterJs.evaluate(code ?? '', "<eval>");
|
||||
} catch(e) {
|
||||
resp = e.toString();
|
||||
}
|
||||
setState(() {
|
||||
code = code;
|
||||
});
|
||||
}),
|
||||
FlatButton(child: Text("释放引擎"), onPressed: () async {
|
||||
if ((engine?? 0) == 0) return;
|
||||
await FlutterJs.close();
|
||||
engine = 0;
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
color: Colors.grey.withOpacity(0.1),
|
||||
constraints: BoxConstraints(minHeight: 200),
|
||||
child: CodeEditor(
|
||||
onChanged: (v) {
|
||||
code = v;
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Text("运行结果:"),
|
||||
SizedBox(height: 16),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(12),
|
||||
color: Colors.green.withOpacity(0.05),
|
||||
constraints: BoxConstraints(minHeight: 100),
|
||||
child: Text(resp ?? ''),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
182
example/pubspec.lock
Normal file
182
example/pubspec.lock
Normal file
@@ -0,0 +1,182 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.5.0-nullsafety"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.0-nullsafety"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.0-nullsafety.2"
|
||||
charcode:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: charcode
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.2.0-nullsafety"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.0-nullsafety"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.15.0-nullsafety.2"
|
||||
dio:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dio
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.0.10"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.0-nullsafety"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_highlight:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_highlight
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.6.0"
|
||||
flutter_qjs:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: ".."
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
highlight:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: highlight
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.6.0"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.1.4"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.12.10-nullsafety"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.3.0-nullsafety.2"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.8.0-nullsafety"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.99"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.8.0-nullsafety"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.10.0-nullsafety"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.0-nullsafety"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.0-nullsafety"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.2.0-nullsafety"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.2.19-nullsafety"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.3.0-nullsafety.2"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.0-nullsafety.2"
|
||||
sdks:
|
||||
dart: ">=2.10.0-0.0.dev <2.10.0"
|
||||
flutter: ">=1.20.0 <2.0.0"
|
71
example/pubspec.yaml
Normal file
71
example/pubspec.yaml
Normal file
@@ -0,0 +1,71 @@
|
||||
name: flutter_qjs_example
|
||||
description: Demonstrates how to use the flutter_qjs plugin.
|
||||
|
||||
# The following line prevents the package from being accidentally published to
|
||||
# pub.dev using `pub publish`. This is preferred for private packages.
|
||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
|
||||
environment:
|
||||
sdk: ">=2.7.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
flutter_qjs:
|
||||
# When depending on this package from a real application you should use:
|
||||
# flutter_qjs: ^x.y.z
|
||||
# See https://dart.dev/tools/pub/dependencies#version-constraints
|
||||
# The example app is bundled with the plugin so we use a path dependency on
|
||||
# the parent directory to use the current plugin's version.
|
||||
path: ../
|
||||
|
||||
highlight: 0.6.0
|
||||
flutter_highlight: 0.6.0
|
||||
dio: 3.0.10
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
# The following section is specific to Flutter.
|
||||
flutter:
|
||||
|
||||
# The following line ensures that the Material Icons font is
|
||||
# included with your application, so that you can use the icons in
|
||||
# the material Icons class.
|
||||
uses-material-design: true
|
||||
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
# assets:
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/assets-and-images/#resolution-aware.
|
||||
|
||||
# For details regarding adding assets from package dependencies, see
|
||||
# https://flutter.dev/assets-and-images/#from-packages
|
||||
|
||||
# To add custom fonts to your application, add a fonts section here,
|
||||
# in this "flutter" section. Each entry in this list should have a
|
||||
# "family" key with the font family name, and a "fonts" key with a
|
||||
# list giving the asset and other descriptors for the font. For
|
||||
# example:
|
||||
# fonts:
|
||||
# - family: Schyler
|
||||
# fonts:
|
||||
# - asset: fonts/Schyler-Regular.ttf
|
||||
# - asset: fonts/Schyler-Italic.ttf
|
||||
# style: italic
|
||||
# - family: Trajan Pro
|
||||
# fonts:
|
||||
# - asset: fonts/TrajanPro.ttf
|
||||
# - asset: fonts/TrajanPro_Bold.ttf
|
||||
# weight: 700
|
||||
#
|
||||
# For details regarding fonts from package dependencies,
|
||||
# see https://flutter.dev/custom-fonts/#from-packages
|
27
example/test/widget_test.dart
Normal file
27
example/test/widget_test.dart
Normal file
@@ -0,0 +1,27 @@
|
||||
// This is a basic Flutter widget test.
|
||||
//
|
||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
||||
// utility that Flutter provides. For example, you can send tap and scroll
|
||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
||||
// tree, read text, and verify that the values of widget properties are correct.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:flutter_qjs_example/main.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Verify Platform version', (WidgetTester tester) async {
|
||||
// Build our app and trigger a frame.
|
||||
await tester.pumpWidget(MyApp());
|
||||
|
||||
// Verify that platform version is retrieved.
|
||||
expect(
|
||||
find.byWidgetPredicate(
|
||||
(Widget widget) => widget is Text &&
|
||||
widget.data.startsWith('Running on:'),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
});
|
||||
}
|
17
example/windows/.gitignore
vendored
Normal file
17
example/windows/.gitignore
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
flutter/ephemeral/
|
||||
|
||||
# Visual Studio user-specific files.
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# Visual Studio build-related files.
|
||||
x64/
|
||||
x86/
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
95
example/windows/CMakeLists.txt
Normal file
95
example/windows/CMakeLists.txt
Normal file
@@ -0,0 +1,95 @@
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
project(flutter_qjs_example LANGUAGES CXX)
|
||||
|
||||
set(BINARY_NAME "flutter_qjs_example")
|
||||
|
||||
cmake_policy(SET CMP0063 NEW)
|
||||
|
||||
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
|
||||
|
||||
# Configure build options.
|
||||
get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if(IS_MULTICONFIG)
|
||||
set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
|
||||
CACHE STRING "" FORCE)
|
||||
else()
|
||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
set(CMAKE_BUILD_TYPE "Debug" CACHE
|
||||
STRING "Flutter build mode" FORCE)
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
|
||||
"Debug" "Profile" "Release")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
|
||||
set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
|
||||
set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
|
||||
# Use Unicode for all projects.
|
||||
add_definitions(-DUNICODE -D_UNICODE)
|
||||
|
||||
# Compilation settings that should be applied to most targets.
|
||||
function(APPLY_STANDARD_SETTINGS TARGET)
|
||||
target_compile_features(${TARGET} PUBLIC cxx_std_17)
|
||||
target_compile_options(${TARGET} PRIVATE /W4 /WX- /wd"4100")
|
||||
target_compile_options(${TARGET} PRIVATE /EHsc)
|
||||
target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
|
||||
target_compile_definitions(${TARGET} PRIVATE "$<$<CONFIG:Debug>:_DEBUG>")
|
||||
endfunction()
|
||||
|
||||
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
|
||||
|
||||
# Flutter library and tool build rules.
|
||||
add_subdirectory(${FLUTTER_MANAGED_DIR})
|
||||
|
||||
# Application build
|
||||
add_subdirectory("runner")
|
||||
|
||||
# Generated plugin build rules, which manage building the plugins and adding
|
||||
# them to the application.
|
||||
include(flutter/generated_plugins.cmake)
|
||||
|
||||
|
||||
# === Installation ===
|
||||
# Support files are copied into place next to the executable, so that it can
|
||||
# run in place. This is done instead of making a separate bundle (as on Linux)
|
||||
# so that building and running from within Visual Studio will work.
|
||||
set(BUILD_BUNDLE_DIR "$<TARGET_FILE_DIR:${BINARY_NAME}>")
|
||||
# Make the "install" step default, as it's required to run.
|
||||
set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
|
||||
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
|
||||
endif()
|
||||
|
||||
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
|
||||
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
|
||||
|
||||
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
if(PLUGIN_BUNDLED_LIBRARIES)
|
||||
install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
|
||||
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
endif()
|
||||
|
||||
# Fully re-copy the assets directory on each build to avoid having stale files
|
||||
# from a previous install.
|
||||
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
|
||||
install(CODE "
|
||||
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
|
||||
" COMPONENT Runtime)
|
||||
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
|
||||
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
|
||||
|
||||
# Install the AOT library on non-Debug builds only.
|
||||
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
|
||||
CONFIGURATIONS Profile;Release
|
||||
COMPONENT Runtime)
|
1
example/windows/flutter/.template_version
Normal file
1
example/windows/flutter/.template_version
Normal file
@@ -0,0 +1 @@
|
||||
4
|
98
example/windows/flutter/CMakeLists.txt
Normal file
98
example/windows/flutter/CMakeLists.txt
Normal file
@@ -0,0 +1,98 @@
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
|
||||
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
|
||||
|
||||
# Configuration provided via flutter tool.
|
||||
include(${EPHEMERAL_DIR}/generated_config.cmake)
|
||||
|
||||
# TODO: Move the rest of this into files in ephemeral. See
|
||||
# https://github.com/flutter/flutter/issues/57146.
|
||||
set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
|
||||
|
||||
# === Flutter Library ===
|
||||
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
|
||||
|
||||
# Published to parent scope for install step.
|
||||
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
|
||||
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
|
||||
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
|
||||
set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
|
||||
|
||||
list(APPEND FLUTTER_LIBRARY_HEADERS
|
||||
"flutter_export.h"
|
||||
"flutter_windows.h"
|
||||
"flutter_messenger.h"
|
||||
"flutter_plugin_registrar.h"
|
||||
)
|
||||
list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
|
||||
add_library(flutter INTERFACE)
|
||||
target_include_directories(flutter INTERFACE
|
||||
"${EPHEMERAL_DIR}"
|
||||
)
|
||||
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
|
||||
add_dependencies(flutter flutter_assemble)
|
||||
|
||||
# === Wrapper ===
|
||||
list(APPEND CPP_WRAPPER_SOURCES_CORE
|
||||
"engine_method_result.cc"
|
||||
"standard_codec.cc"
|
||||
)
|
||||
list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
|
||||
list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
|
||||
"plugin_registrar.cc"
|
||||
)
|
||||
list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
|
||||
list(APPEND CPP_WRAPPER_SOURCES_APP
|
||||
"flutter_view_controller.cc"
|
||||
)
|
||||
list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
|
||||
|
||||
# Wrapper sources needed for a plugin.
|
||||
add_library(flutter_wrapper_plugin STATIC
|
||||
${CPP_WRAPPER_SOURCES_CORE}
|
||||
${CPP_WRAPPER_SOURCES_PLUGIN}
|
||||
)
|
||||
apply_standard_settings(flutter_wrapper_plugin)
|
||||
set_target_properties(flutter_wrapper_plugin PROPERTIES
|
||||
POSITION_INDEPENDENT_CODE ON)
|
||||
set_target_properties(flutter_wrapper_plugin PROPERTIES
|
||||
CXX_VISIBILITY_PRESET hidden)
|
||||
target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
|
||||
target_include_directories(flutter_wrapper_plugin PUBLIC
|
||||
"${WRAPPER_ROOT}/include"
|
||||
)
|
||||
add_dependencies(flutter_wrapper_plugin flutter_assemble)
|
||||
|
||||
# Wrapper sources needed for the runner.
|
||||
add_library(flutter_wrapper_app STATIC
|
||||
${CPP_WRAPPER_SOURCES_CORE}
|
||||
${CPP_WRAPPER_SOURCES_APP}
|
||||
)
|
||||
apply_standard_settings(flutter_wrapper_app)
|
||||
target_link_libraries(flutter_wrapper_app PUBLIC flutter)
|
||||
target_include_directories(flutter_wrapper_app PUBLIC
|
||||
"${WRAPPER_ROOT}/include"
|
||||
)
|
||||
add_dependencies(flutter_wrapper_app flutter_assemble)
|
||||
|
||||
# === Flutter tool backend ===
|
||||
# _phony_ is a non-existent file to force this command to run every time,
|
||||
# since currently there's no way to get a full input/output list from the
|
||||
# flutter tool.
|
||||
add_custom_command(
|
||||
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
|
||||
${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
|
||||
${CPP_WRAPPER_SOURCES_APP}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/_phony_
|
||||
COMMAND ${CMAKE_COMMAND} -E env
|
||||
${FLUTTER_TOOL_ENVIRONMENT}
|
||||
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
|
||||
windows-x64 $<CONFIG>
|
||||
)
|
||||
add_custom_target(flutter_assemble DEPENDS
|
||||
"${FLUTTER_LIBRARY}"
|
||||
${FLUTTER_LIBRARY_HEADERS}
|
||||
${CPP_WRAPPER_SOURCES_CORE}
|
||||
${CPP_WRAPPER_SOURCES_PLUGIN}
|
||||
${CPP_WRAPPER_SOURCES_APP}
|
||||
)
|
12
example/windows/flutter/generated_plugin_registrant.cc
Normal file
12
example/windows/flutter/generated_plugin_registrant.cc
Normal file
@@ -0,0 +1,12 @@
|
||||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <flutter_qjs/flutter_qjs_plugin.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
FlutterQjsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FlutterQjsPlugin"));
|
||||
}
|
13
example/windows/flutter/generated_plugin_registrant.h
Normal file
13
example/windows/flutter/generated_plugin_registrant.h
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
#ifndef GENERATED_PLUGIN_REGISTRANT_
|
||||
#define GENERATED_PLUGIN_REGISTRANT_
|
||||
|
||||
#include <flutter/plugin_registry.h>
|
||||
|
||||
// Registers Flutter plugins.
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry);
|
||||
|
||||
#endif // GENERATED_PLUGIN_REGISTRANT_
|
16
example/windows/flutter/generated_plugins.cmake
Normal file
16
example/windows/flutter/generated_plugins.cmake
Normal file
@@ -0,0 +1,16 @@
|
||||
#
|
||||
# Generated file, do not edit.
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
flutter_qjs
|
||||
)
|
||||
|
||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||
|
||||
foreach(plugin ${FLUTTER_PLUGIN_LIST})
|
||||
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
|
||||
endforeach(plugin)
|
18
example/windows/runner/CMakeLists.txt
Normal file
18
example/windows/runner/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
project(runner LANGUAGES CXX)
|
||||
|
||||
add_executable(${BINARY_NAME} WIN32
|
||||
"flutter_window.cpp"
|
||||
"main.cpp"
|
||||
"run_loop.cpp"
|
||||
"utils.cpp"
|
||||
"win32_window.cpp"
|
||||
"window_configuration.cpp"
|
||||
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
|
||||
"Runner.rc"
|
||||
"runner.exe.manifest"
|
||||
)
|
||||
apply_standard_settings(${BINARY_NAME})
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
|
||||
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
|
||||
add_dependencies(${BINARY_NAME} flutter_assemble)
|
70
example/windows/runner/Runner.rc
Normal file
70
example/windows/runner/Runner.rc
Normal file
@@ -0,0 +1,70 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#pragma code_page(65001)
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "winres.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (United States) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""winres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Icon
|
||||
//
|
||||
|
||||
// Icon with lowest ID value placed first to ensure application icon
|
||||
// remains consistent on all systems.
|
||||
IDI_APP_ICON ICON "resources\\app_icon.ico"
|
||||
|
||||
#endif // English (United States) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
29
example/windows/runner/flutter_window.cpp
Normal file
29
example/windows/runner/flutter_window.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "flutter_window.h"
|
||||
|
||||
#include "flutter/generated_plugin_registrant.h"
|
||||
|
||||
FlutterWindow::FlutterWindow(RunLoop* run_loop,
|
||||
const flutter::DartProject& project)
|
||||
: run_loop_(run_loop), project_(project) {}
|
||||
|
||||
FlutterWindow::~FlutterWindow() {}
|
||||
|
||||
void FlutterWindow::OnCreate() {
|
||||
Win32Window::OnCreate();
|
||||
|
||||
// The size here is arbitrary since SetChildContent will resize it.
|
||||
flutter_controller_ =
|
||||
std::make_unique<flutter::FlutterViewController>(100, 100, project_);
|
||||
RegisterPlugins(flutter_controller_.get());
|
||||
run_loop_->RegisterFlutterInstance(flutter_controller_.get());
|
||||
SetChildContent(flutter_controller_->view()->GetNativeWindow());
|
||||
}
|
||||
|
||||
void FlutterWindow::OnDestroy() {
|
||||
if (flutter_controller_) {
|
||||
run_loop_->UnregisterFlutterInstance(flutter_controller_.get());
|
||||
flutter_controller_ = nullptr;
|
||||
}
|
||||
|
||||
Win32Window::OnDestroy();
|
||||
}
|
37
example/windows/runner/flutter_window.h
Normal file
37
example/windows/runner/flutter_window.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef FLUTTER_WINDOW_H_
|
||||
#define FLUTTER_WINDOW_H_
|
||||
|
||||
#include <flutter/dart_project.h>
|
||||
#include <flutter/flutter_view_controller.h>
|
||||
|
||||
#include "run_loop.h"
|
||||
#include "win32_window.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
// A window that does nothing but host a Flutter view.
|
||||
class FlutterWindow : public Win32Window {
|
||||
public:
|
||||
// Creates a new FlutterWindow driven by the |run_loop|, hosting a
|
||||
// Flutter view running |project|.
|
||||
explicit FlutterWindow(RunLoop* run_loop,
|
||||
const flutter::DartProject& project);
|
||||
virtual ~FlutterWindow();
|
||||
|
||||
protected:
|
||||
// Win32Window:
|
||||
void OnCreate() override;
|
||||
void OnDestroy() override;
|
||||
|
||||
private:
|
||||
// The run loop driving events for this window.
|
||||
RunLoop* run_loop_;
|
||||
|
||||
// The project to run.
|
||||
flutter::DartProject project_;
|
||||
|
||||
// The Flutter instance hosted by this window.
|
||||
std::unique_ptr<flutter::FlutterViewController> flutter_controller_;
|
||||
};
|
||||
|
||||
#endif // FLUTTER_WINDOW_H_
|
37
example/windows/runner/main.cpp
Normal file
37
example/windows/runner/main.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include <flutter/dart_project.h>
|
||||
#include <flutter/flutter_view_controller.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "flutter_window.h"
|
||||
#include "run_loop.h"
|
||||
#include "utils.h"
|
||||
#include "window_configuration.h"
|
||||
|
||||
int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
|
||||
_In_ wchar_t *command_line, _In_ int show_command) {
|
||||
// Attach to console when present (e.g., 'flutter run') or create a
|
||||
// new console when running with a debugger.
|
||||
if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
|
||||
CreateAndAttachConsole();
|
||||
}
|
||||
|
||||
// Initialize COM, so that it is available for use in the library and/or
|
||||
// plugins.
|
||||
::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
|
||||
|
||||
RunLoop run_loop;
|
||||
|
||||
flutter::DartProject project(L"data");
|
||||
FlutterWindow window(&run_loop, project);
|
||||
Win32Window::Point origin(kFlutterWindowOriginX, kFlutterWindowOriginY);
|
||||
Win32Window::Size size(kFlutterWindowWidth, kFlutterWindowHeight);
|
||||
if (!window.CreateAndShow(kFlutterWindowTitle, origin, size)) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
window.SetQuitOnClose(true);
|
||||
|
||||
run_loop.Run();
|
||||
|
||||
::CoUninitialize();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
16
example/windows/runner/resource.h
Normal file
16
example/windows/runner/resource.h
Normal file
@@ -0,0 +1,16 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by Runner.rc
|
||||
//
|
||||
#define IDI_APP_ICON 101
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 102
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
BIN
example/windows/runner/resources/app_icon.ico
Normal file
BIN
example/windows/runner/resources/app_icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
70
example/windows/runner/run_loop.cpp
Normal file
70
example/windows/runner/run_loop.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
#include "run_loop.h"
|
||||
|
||||
#include <Windows.h>
|
||||
// Don't stomp std::min/std::max
|
||||
#undef max
|
||||
#undef min
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
RunLoop::RunLoop() {}
|
||||
|
||||
RunLoop::~RunLoop() {}
|
||||
|
||||
void RunLoop::Run() {
|
||||
bool keep_running = true;
|
||||
TimePoint next_flutter_event_time = TimePoint::clock::now();
|
||||
while (keep_running) {
|
||||
std::chrono::nanoseconds wait_duration =
|
||||
std::max(std::chrono::nanoseconds(0),
|
||||
next_flutter_event_time - TimePoint::clock::now());
|
||||
::MsgWaitForMultipleObjects(
|
||||
0, nullptr, FALSE, static_cast<DWORD>(wait_duration.count() / 1000),
|
||||
QS_ALLINPUT);
|
||||
bool processed_events = false;
|
||||
MSG message;
|
||||
// All pending Windows messages must be processed; MsgWaitForMultipleObjects
|
||||
// won't return again for items left in the queue after PeekMessage.
|
||||
while (::PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) {
|
||||
processed_events = true;
|
||||
if (message.message == WM_QUIT) {
|
||||
keep_running = false;
|
||||
break;
|
||||
}
|
||||
::TranslateMessage(&message);
|
||||
::DispatchMessage(&message);
|
||||
// Allow Flutter to process messages each time a Windows message is
|
||||
// processed, to prevent starvation.
|
||||
next_flutter_event_time =
|
||||
std::min(next_flutter_event_time, ProcessFlutterMessages());
|
||||
}
|
||||
// If the PeekMessage loop didn't run, process Flutter messages.
|
||||
if (!processed_events) {
|
||||
next_flutter_event_time =
|
||||
std::min(next_flutter_event_time, ProcessFlutterMessages());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RunLoop::RegisterFlutterInstance(
|
||||
flutter::FlutterViewController* flutter_instance) {
|
||||
flutter_instances_.insert(flutter_instance);
|
||||
}
|
||||
|
||||
void RunLoop::UnregisterFlutterInstance(
|
||||
flutter::FlutterViewController* flutter_instance) {
|
||||
flutter_instances_.erase(flutter_instance);
|
||||
}
|
||||
|
||||
RunLoop::TimePoint RunLoop::ProcessFlutterMessages() {
|
||||
TimePoint next_event_time = TimePoint::max();
|
||||
for (auto flutter_controller : flutter_instances_) {
|
||||
std::chrono::nanoseconds wait_duration =
|
||||
flutter_controller->ProcessMessages();
|
||||
if (wait_duration != std::chrono::nanoseconds::max()) {
|
||||
next_event_time =
|
||||
std::min(next_event_time, TimePoint::clock::now() + wait_duration);
|
||||
}
|
||||
}
|
||||
return next_event_time;
|
||||
}
|
40
example/windows/runner/run_loop.h
Normal file
40
example/windows/runner/run_loop.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef RUN_LOOP_H_
|
||||
#define RUN_LOOP_H_
|
||||
|
||||
#include <flutter/flutter_view_controller.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <set>
|
||||
|
||||
// A runloop that will service events for Flutter instances as well
|
||||
// as native messages.
|
||||
class RunLoop {
|
||||
public:
|
||||
RunLoop();
|
||||
~RunLoop();
|
||||
|
||||
// Prevent copying
|
||||
RunLoop(RunLoop const&) = delete;
|
||||
RunLoop& operator=(RunLoop const&) = delete;
|
||||
|
||||
// Runs the run loop until the application quits.
|
||||
void Run();
|
||||
|
||||
// Registers the given Flutter instance for event servicing.
|
||||
void RegisterFlutterInstance(
|
||||
flutter::FlutterViewController* flutter_instance);
|
||||
|
||||
// Unregisters the given Flutter instance from event servicing.
|
||||
void UnregisterFlutterInstance(
|
||||
flutter::FlutterViewController* flutter_instance);
|
||||
|
||||
private:
|
||||
using TimePoint = std::chrono::steady_clock::time_point;
|
||||
|
||||
// Processes all currently pending messages for registered Flutter instances.
|
||||
TimePoint ProcessFlutterMessages();
|
||||
|
||||
std::set<flutter::FlutterViewController*> flutter_instances_;
|
||||
};
|
||||
|
||||
#endif // RUN_LOOP_H_
|
20
example/windows/runner/runner.exe.manifest
Normal file
20
example/windows/runner/runner.exe.manifest
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- Windows 10 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
<!-- Windows 8.1 -->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
<!-- Windows 8 -->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
<!-- Windows 7 -->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
</assembly>
|
22
example/windows/runner/utils.cpp
Normal file
22
example/windows/runner/utils.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "utils.h"
|
||||
|
||||
#include <flutter_windows.h>
|
||||
#include <io.h>
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
void CreateAndAttachConsole() {
|
||||
if (::AllocConsole()) {
|
||||
FILE *unused;
|
||||
if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
|
||||
_dup2(_fileno(stdout), 1);
|
||||
}
|
||||
if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
|
||||
_dup2(_fileno(stdout), 2);
|
||||
}
|
||||
std::ios::sync_with_stdio();
|
||||
FlutterDesktopResyncOutputStreams();
|
||||
}
|
||||
}
|
8
example/windows/runner/utils.h
Normal file
8
example/windows/runner/utils.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef CONSOLE_UTILS_H_
|
||||
#define CONSOLE_UTILS_H_
|
||||
|
||||
// Creates a console for the process, and redirects stdout and stderr to
|
||||
// it for both the runner and the Flutter library.
|
||||
void CreateAndAttachConsole();
|
||||
|
||||
#endif // CONSOLE_UTILS_H_
|
249
example/windows/runner/win32_window.cpp
Normal file
249
example/windows/runner/win32_window.cpp
Normal file
@@ -0,0 +1,249 @@
|
||||
#include "win32_window.h"
|
||||
|
||||
#include <flutter_windows.h>
|
||||
|
||||
#include "resource.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
|
||||
|
||||
// The number of Win32Window objects that currently exist.
|
||||
static int g_active_window_count = 0;
|
||||
|
||||
using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
|
||||
|
||||
// Scale helper to convert logical scaler values to physical using passed in
|
||||
// scale factor
|
||||
int Scale(int source, double scale_factor) {
|
||||
return static_cast<int>(source * scale_factor);
|
||||
}
|
||||
|
||||
// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
|
||||
// This API is only needed for PerMonitor V1 awareness mode.
|
||||
void EnableFullDpiSupportIfAvailable(HWND hwnd) {
|
||||
HMODULE user32_module = LoadLibraryA("User32.dll");
|
||||
if (!user32_module) {
|
||||
return;
|
||||
}
|
||||
auto enable_non_client_dpi_scaling =
|
||||
reinterpret_cast<EnableNonClientDpiScaling*>(
|
||||
GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
|
||||
if (enable_non_client_dpi_scaling != nullptr) {
|
||||
enable_non_client_dpi_scaling(hwnd);
|
||||
FreeLibrary(user32_module);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Manages the Win32Window's window class registration.
|
||||
class WindowClassRegistrar {
|
||||
public:
|
||||
~WindowClassRegistrar() = default;
|
||||
|
||||
// Returns the singleton registar instance.
|
||||
static WindowClassRegistrar* GetInstance() {
|
||||
if (!instance_) {
|
||||
instance_ = new WindowClassRegistrar();
|
||||
}
|
||||
return instance_;
|
||||
}
|
||||
|
||||
// Returns the name of the window class, registering the class if it hasn't
|
||||
// previously been registered.
|
||||
const wchar_t* GetWindowClass();
|
||||
|
||||
// Unregisters the window class. Should only be called if there are no
|
||||
// instances of the window.
|
||||
void UnregisterWindowClass();
|
||||
|
||||
private:
|
||||
WindowClassRegistrar() = default;
|
||||
|
||||
static WindowClassRegistrar* instance_;
|
||||
|
||||
bool class_registered_ = false;
|
||||
};
|
||||
|
||||
WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
|
||||
|
||||
const wchar_t* WindowClassRegistrar::GetWindowClass() {
|
||||
if (!class_registered_) {
|
||||
WNDCLASS window_class{};
|
||||
window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||
window_class.lpszClassName = kWindowClassName;
|
||||
window_class.style = CS_HREDRAW | CS_VREDRAW;
|
||||
window_class.cbClsExtra = 0;
|
||||
window_class.cbWndExtra = 0;
|
||||
window_class.hInstance = GetModuleHandle(nullptr);
|
||||
window_class.hIcon =
|
||||
LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
|
||||
window_class.hbrBackground = 0;
|
||||
window_class.lpszMenuName = nullptr;
|
||||
window_class.lpfnWndProc = Win32Window::WndProc;
|
||||
RegisterClass(&window_class);
|
||||
class_registered_ = true;
|
||||
}
|
||||
return kWindowClassName;
|
||||
}
|
||||
|
||||
void WindowClassRegistrar::UnregisterWindowClass() {
|
||||
UnregisterClass(kWindowClassName, nullptr);
|
||||
class_registered_ = false;
|
||||
}
|
||||
|
||||
Win32Window::Win32Window() {
|
||||
++g_active_window_count;
|
||||
}
|
||||
|
||||
Win32Window::~Win32Window() {
|
||||
--g_active_window_count;
|
||||
Destroy();
|
||||
}
|
||||
|
||||
bool Win32Window::CreateAndShow(const std::wstring& title,
|
||||
const Point& origin,
|
||||
const Size& size) {
|
||||
Destroy();
|
||||
|
||||
const wchar_t* window_class =
|
||||
WindowClassRegistrar::GetInstance()->GetWindowClass();
|
||||
|
||||
const POINT target_point = {static_cast<LONG>(origin.x),
|
||||
static_cast<LONG>(origin.y)};
|
||||
HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
|
||||
UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
|
||||
double scale_factor = dpi / 96.0;
|
||||
|
||||
HWND window = CreateWindow(
|
||||
window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
||||
Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
|
||||
Scale(size.width, scale_factor), Scale(size.height, scale_factor),
|
||||
nullptr, nullptr, GetModuleHandle(nullptr), this);
|
||||
|
||||
OnCreate();
|
||||
|
||||
return window != nullptr;
|
||||
}
|
||||
|
||||
// static
|
||||
LRESULT CALLBACK Win32Window::WndProc(HWND const window,
|
||||
UINT const message,
|
||||
WPARAM const wparam,
|
||||
LPARAM const lparam) noexcept {
|
||||
if (message == WM_NCCREATE) {
|
||||
auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
|
||||
SetWindowLongPtr(window, GWLP_USERDATA,
|
||||
reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
|
||||
|
||||
auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);
|
||||
EnableFullDpiSupportIfAvailable(window);
|
||||
that->window_handle_ = window;
|
||||
} else if (Win32Window* that = GetThisFromHandle(window)) {
|
||||
return that->MessageHandler(window, message, wparam, lparam);
|
||||
}
|
||||
|
||||
return DefWindowProc(window, message, wparam, lparam);
|
||||
}
|
||||
|
||||
LRESULT
|
||||
Win32Window::MessageHandler(HWND hwnd,
|
||||
UINT const message,
|
||||
WPARAM const wparam,
|
||||
LPARAM const lparam) noexcept {
|
||||
auto window =
|
||||
reinterpret_cast<Win32Window*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
|
||||
|
||||
if (window == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (message) {
|
||||
case WM_DESTROY:
|
||||
window_handle_ = nullptr;
|
||||
Destroy();
|
||||
if (quit_on_close_) {
|
||||
PostQuitMessage(0);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case WM_DPICHANGED: {
|
||||
auto newRectSize = reinterpret_cast<RECT*>(lparam);
|
||||
LONG newWidth = newRectSize->right - newRectSize->left;
|
||||
LONG newHeight = newRectSize->bottom - newRectSize->top;
|
||||
|
||||
SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
|
||||
newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
case WM_SIZE:
|
||||
RECT rect;
|
||||
GetClientRect(hwnd, &rect);
|
||||
if (child_content_ != nullptr) {
|
||||
// Size and position the child window.
|
||||
MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
|
||||
rect.bottom - rect.top, TRUE);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case WM_ACTIVATE:
|
||||
if (child_content_ != nullptr) {
|
||||
SetFocus(child_content_);
|
||||
}
|
||||
return 0;
|
||||
|
||||
// Messages that are directly forwarded to embedding.
|
||||
case WM_FONTCHANGE:
|
||||
SendMessage(child_content_, WM_FONTCHANGE, NULL, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return DefWindowProc(window_handle_, message, wparam, lparam);
|
||||
}
|
||||
|
||||
void Win32Window::Destroy() {
|
||||
OnDestroy();
|
||||
|
||||
if (window_handle_) {
|
||||
DestroyWindow(window_handle_);
|
||||
window_handle_ = nullptr;
|
||||
}
|
||||
if (g_active_window_count == 0) {
|
||||
WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
|
||||
}
|
||||
}
|
||||
|
||||
Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
|
||||
return reinterpret_cast<Win32Window*>(
|
||||
GetWindowLongPtr(window, GWLP_USERDATA));
|
||||
}
|
||||
|
||||
void Win32Window::SetChildContent(HWND content) {
|
||||
child_content_ = content;
|
||||
SetParent(content, window_handle_);
|
||||
RECT frame;
|
||||
GetClientRect(window_handle_, &frame);
|
||||
|
||||
MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
|
||||
frame.bottom - frame.top, true);
|
||||
|
||||
SetFocus(child_content_);
|
||||
}
|
||||
|
||||
HWND Win32Window::GetHandle() {
|
||||
return window_handle_;
|
||||
}
|
||||
|
||||
void Win32Window::SetQuitOnClose(bool quit_on_close) {
|
||||
quit_on_close_ = quit_on_close;
|
||||
}
|
||||
|
||||
void Win32Window::OnCreate() {
|
||||
// No-op; provided for subclasses.
|
||||
}
|
||||
|
||||
void Win32Window::OnDestroy() {
|
||||
// No-op; provided for subclasses.
|
||||
}
|
96
example/windows/runner/win32_window.h
Normal file
96
example/windows/runner/win32_window.h
Normal file
@@ -0,0 +1,96 @@
|
||||
#ifndef WIN32_WINDOW_H_
|
||||
#define WIN32_WINDOW_H_
|
||||
|
||||
#include <Windows.h>
|
||||
#include <Windowsx.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
// A class abstraction for a high DPI-aware Win32 Window. Intended to be
|
||||
// inherited from by classes that wish to specialize with custom
|
||||
// rendering and input handling
|
||||
class Win32Window {
|
||||
public:
|
||||
struct Point {
|
||||
unsigned int x;
|
||||
unsigned int y;
|
||||
Point(unsigned int x, unsigned int y) : x(x), y(y) {}
|
||||
};
|
||||
|
||||
struct Size {
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
Size(unsigned int width, unsigned int height)
|
||||
: width(width), height(height) {}
|
||||
};
|
||||
|
||||
Win32Window();
|
||||
virtual ~Win32Window();
|
||||
|
||||
// Creates and shows a win32 window with |title| and position and size using
|
||||
// |origin| and |size|. New windows are created on the default monitor. Window
|
||||
// sizes are specified to the OS in physical pixels, hence to ensure a
|
||||
// consistent size to will treat the width height passed in to this function
|
||||
// as logical pixels and scale to appropriate for the default monitor. Returns
|
||||
// true if the window was created successfully.
|
||||
bool CreateAndShow(const std::wstring& title,
|
||||
const Point& origin,
|
||||
const Size& size);
|
||||
|
||||
// Release OS resources associated with window.
|
||||
void Destroy();
|
||||
|
||||
// Inserts |content| into the window tree.
|
||||
void SetChildContent(HWND content);
|
||||
|
||||
// Returns the backing Window handle to enable clients to set icon and other
|
||||
// window properties. Returns nullptr if the window has been destroyed.
|
||||
HWND GetHandle();
|
||||
|
||||
// If true, closing this window will quit the application.
|
||||
void SetQuitOnClose(bool quit_on_close);
|
||||
|
||||
protected:
|
||||
// Processes and route salient window messages for mouse handling,
|
||||
// size change and DPI. Delegates handling of these to member overloads that
|
||||
// inheriting classes can handle.
|
||||
virtual LRESULT MessageHandler(HWND window,
|
||||
UINT const message,
|
||||
WPARAM const wparam,
|
||||
LPARAM const lparam) noexcept;
|
||||
|
||||
// Called when CreateAndShow is called, allowing subclass window-related
|
||||
// setup.
|
||||
virtual void OnCreate();
|
||||
|
||||
// Called when Destroy is called.
|
||||
virtual void OnDestroy();
|
||||
|
||||
private:
|
||||
friend class WindowClassRegistrar;
|
||||
|
||||
// OS callback called by message pump. Handles the WM_NCCREATE message which
|
||||
// is passed when the non-client area is being created and enables automatic
|
||||
// non-client DPI scaling so that the non-client area automatically
|
||||
// responsponds to changes in DPI. All other messages are handled by
|
||||
// MessageHandler.
|
||||
static LRESULT CALLBACK WndProc(HWND const window,
|
||||
UINT const message,
|
||||
WPARAM const wparam,
|
||||
LPARAM const lparam) noexcept;
|
||||
|
||||
// Retrieves a class instance pointer for |window|
|
||||
static Win32Window* GetThisFromHandle(HWND const window) noexcept;
|
||||
|
||||
bool quit_on_close_ = false;
|
||||
|
||||
// window handle for top level window.
|
||||
HWND window_handle_ = nullptr;
|
||||
|
||||
// window handle for hosted content.
|
||||
HWND child_content_ = nullptr;
|
||||
};
|
||||
|
||||
#endif // WIN32_WINDOW_H_
|
7
example/windows/runner/window_configuration.cpp
Normal file
7
example/windows/runner/window_configuration.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#include "window_configuration.h"
|
||||
|
||||
const wchar_t* kFlutterWindowTitle = L"flutter_qjs_example";
|
||||
const unsigned int kFlutterWindowOriginX = 10;
|
||||
const unsigned int kFlutterWindowOriginY = 10;
|
||||
const unsigned int kFlutterWindowWidth = 1280;
|
||||
const unsigned int kFlutterWindowHeight = 720;
|
18
example/windows/runner/window_configuration.h
Normal file
18
example/windows/runner/window_configuration.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef WINDOW_CONFIGURATION_
|
||||
#define WINDOW_CONFIGURATION_
|
||||
|
||||
// This is a temporary approach to isolate changes that people are likely to
|
||||
// make to main.cpp, where the APIs are still in flux. This will reduce the
|
||||
// need to resolve conflicts or re-create changes slightly differently every
|
||||
// time the Windows Flutter API surface changes.
|
||||
//
|
||||
// Longer term there should be simpler configuration options for common
|
||||
// customizations like this, without requiring native code changes.
|
||||
|
||||
extern const wchar_t* kFlutterWindowTitle;
|
||||
extern const unsigned int kFlutterWindowOriginX;
|
||||
extern const unsigned int kFlutterWindowOriginY;
|
||||
extern const unsigned int kFlutterWindowWidth;
|
||||
extern const unsigned int kFlutterWindowHeight;
|
||||
|
||||
#endif // WINDOW_CONFIGURATION_
|
19
flutter_qjs.iml
Normal file
19
flutter_qjs.iml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/lib" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.idea" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/build" />
|
||||
</content>
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Dart Packages" level="project" />
|
||||
<orderEntry type="library" name="Dart SDK" level="project" />
|
||||
<orderEntry type="library" name="Flutter Plugins" level="project" />
|
||||
</component>
|
||||
</module>
|
37
lib/flutter_qjs.dart
Normal file
37
lib/flutter_qjs.dart
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* @Description:
|
||||
* @Author: ekibun
|
||||
* @Date: 2020-08-08 08:29:09
|
||||
* @LastEditors: ekibun
|
||||
* @LastEditTime: 2020-08-08 17:40:35
|
||||
*/
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class FlutterJs {
|
||||
static const MethodChannel _channel = const MethodChannel('soko.ekibun.flutter_qjs');
|
||||
|
||||
static Future<dynamic> Function(String method, List args) methodHandler;
|
||||
|
||||
static Future<int> initEngine() async {
|
||||
final int engineId = await _channel.invokeMethod("initEngine");
|
||||
_channel.setMethodCallHandler((call) async {
|
||||
if (methodHandler == null) return call.noSuchMethod(null);
|
||||
List args = jsonDecode(call.arguments);
|
||||
return jsonEncode(await methodHandler(call.method, args));
|
||||
});
|
||||
return engineId;
|
||||
}
|
||||
|
||||
static Future<String> evaluate(String command, String name) async {
|
||||
var arguments = {"script": command, "name": command};
|
||||
final String jsResult = await _channel.invokeMethod("evaluate", arguments);
|
||||
return jsResult ?? "null";
|
||||
}
|
||||
|
||||
static Future<void> close() async {
|
||||
return await _channel.invokeMethod("close");
|
||||
}
|
||||
}
|
147
pubspec.lock
Normal file
147
pubspec.lock
Normal file
@@ -0,0 +1,147 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.5.0-nullsafety"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.0-nullsafety"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.0-nullsafety.2"
|
||||
charcode:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: charcode
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.2.0-nullsafety"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.0-nullsafety"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.15.0-nullsafety.2"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.0-nullsafety"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.12.10-nullsafety"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.3.0-nullsafety.2"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.8.0-nullsafety"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.99"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.8.0-nullsafety"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.10.0-nullsafety"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.0-nullsafety"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.0-nullsafety"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.2.0-nullsafety"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.2.19-nullsafety"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.3.0-nullsafety.2"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.0-nullsafety.2"
|
||||
sdks:
|
||||
dart: ">=2.10.0-0.0.dev <2.10.0"
|
||||
flutter: ">=1.20.0 <2.0.0"
|
68
pubspec.yaml
Normal file
68
pubspec.yaml
Normal file
@@ -0,0 +1,68 @@
|
||||
name: flutter_qjs
|
||||
description: A new flutter plugin project.
|
||||
version: 0.0.1
|
||||
author:
|
||||
homepage:
|
||||
|
||||
environment:
|
||||
sdk: ">=2.7.0 <3.0.0"
|
||||
flutter: ">=1.20.0 <2.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
# The following section is specific to Flutter.
|
||||
flutter:
|
||||
# This section identifies this Flutter project as a plugin project.
|
||||
# The 'pluginClass' and Android 'package' identifiers should not ordinarily
|
||||
# be modified. They are used by the tooling to maintain consistency when
|
||||
# adding or updating assets for this project.
|
||||
plugin:
|
||||
platforms:
|
||||
# This plugin project was generated without specifying any
|
||||
# platforms with the `--platform` argument. If you see the `fake_platform` map below, remove it and
|
||||
# then add platforms following the instruction here:
|
||||
# https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms
|
||||
# -------------------
|
||||
windows:
|
||||
pluginClass: FlutterQjsPlugin
|
||||
# -------------------
|
||||
|
||||
# To add assets to your plugin package, add an assets section, like this:
|
||||
# assets:
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
#
|
||||
# For details regarding assets in packages, see
|
||||
# https://flutter.dev/assets-and-images/#from-packages
|
||||
#
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/assets-and-images/#resolution-aware.
|
||||
|
||||
# To add custom fonts to your plugin package, add a fonts section here,
|
||||
# in this "flutter" section. Each entry in this list should have a
|
||||
# "family" key with the font family name, and a "fonts" key with a
|
||||
# list giving the asset and other descriptors for the font. For
|
||||
# example:
|
||||
# fonts:
|
||||
# - family: Schyler
|
||||
# fonts:
|
||||
# - asset: fonts/Schyler-Regular.ttf
|
||||
# - asset: fonts/Schyler-Italic.ttf
|
||||
# style: italic
|
||||
# - family: Trajan Pro
|
||||
# fonts:
|
||||
# - asset: fonts/TrajanPro.ttf
|
||||
# - asset: fonts/TrajanPro_Bold.ttf
|
||||
# weight: 700
|
||||
#
|
||||
# For details regarding fonts in packages, see
|
||||
# https://flutter.dev/custom-fonts/#from-packages
|
19
test/flutter_qjs_test.dart
Normal file
19
test/flutter_qjs_test.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
const MethodChannel channel = MethodChannel('flutter_qjs');
|
||||
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
setUp(() {
|
||||
channel.setMockMethodCallHandler((MethodCall methodCall) async {
|
||||
return '42';
|
||||
});
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
channel.setMockMethodCallHandler(null);
|
||||
});
|
||||
|
||||
}
|
17
windows/.gitignore
vendored
Normal file
17
windows/.gitignore
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
flutter/
|
||||
|
||||
# Visual Studio user-specific files.
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# Visual Studio build-related files.
|
||||
x64/
|
||||
x86/
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
25
windows/CMakeLists.txt
Normal file
25
windows/CMakeLists.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
set(PROJECT_NAME "flutter_qjs")
|
||||
project(${PROJECT_NAME} LANGUAGES CXX)
|
||||
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
|
||||
|
||||
set(PLUGIN_NAME "${PROJECT_NAME}_plugin")
|
||||
|
||||
add_library(${PLUGIN_NAME} SHARED
|
||||
"${PLUGIN_NAME}.cpp"
|
||||
)
|
||||
apply_standard_settings(${PLUGIN_NAME})
|
||||
set_target_properties(${PLUGIN_NAME} PROPERTIES
|
||||
CXX_VISIBILITY_PRESET hidden)
|
||||
target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)
|
||||
target_include_directories(${PLUGIN_NAME} INTERFACE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/quickjs/libquickjs64.a"
|
||||
)
|
||||
|
||||
# List of absolute paths to libraries that should be bundled with the plugin
|
||||
set(flutter_qjs_bundled_libraries
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/quickjs/libquickjs64.dll"
|
||||
PARENT_SCOPE
|
||||
)
|
123
windows/flutter_qjs_plugin.cpp
Normal file
123
windows/flutter_qjs_plugin.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
#include "include/flutter_qjs/flutter_qjs_plugin.h"
|
||||
|
||||
// This must be included before many other Windows headers.
|
||||
#include <windows.h>
|
||||
|
||||
// For getPlatformVersion; remove unless needed for your plugin implementation.
|
||||
#include <VersionHelpers.h>
|
||||
|
||||
#include <flutter/method_channel.h>
|
||||
#include <flutter/plugin_registrar_windows.h>
|
||||
#include <flutter/standard_method_codec.h>
|
||||
|
||||
#include "js_engine.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class FlutterQjsPlugin : public flutter::Plugin
|
||||
{
|
||||
public:
|
||||
static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar);
|
||||
|
||||
FlutterQjsPlugin();
|
||||
|
||||
virtual ~FlutterQjsPlugin();
|
||||
|
||||
private:
|
||||
// Called when a method is called on this plugin's channel from Dart.
|
||||
void HandleMethodCall(
|
||||
const flutter::MethodCall<flutter::EncodableValue> &method_call,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
|
||||
};
|
||||
|
||||
std::shared_ptr<flutter::MethodChannel<flutter::EncodableValue>> channel;
|
||||
|
||||
// static
|
||||
void FlutterQjsPlugin::RegisterWithRegistrar(
|
||||
flutter::PluginRegistrarWindows *registrar)
|
||||
{
|
||||
channel =
|
||||
std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
|
||||
registrar->messenger(), "soko.ekibun.flutter_qjs",
|
||||
&flutter::StandardMethodCodec::GetInstance());
|
||||
|
||||
auto plugin = std::make_unique<FlutterQjsPlugin>();
|
||||
|
||||
channel->SetMethodCallHandler(
|
||||
[plugin_pointer = plugin.get()](const auto &call, auto result) {
|
||||
plugin_pointer->HandleMethodCall(call, std::move(result));
|
||||
});
|
||||
|
||||
registrar->AddPlugin(std::move(plugin));
|
||||
}
|
||||
|
||||
FlutterQjsPlugin::FlutterQjsPlugin() {}
|
||||
|
||||
FlutterQjsPlugin::~FlutterQjsPlugin() {}
|
||||
|
||||
const flutter::EncodableValue &ValueOrNull(const flutter::EncodableMap &map, const char *key)
|
||||
{
|
||||
static flutter::EncodableValue null_value;
|
||||
auto it = map.find(flutter::EncodableValue(key));
|
||||
if (it == map.end())
|
||||
{
|
||||
return null_value;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
qjs::Engine *engine = nullptr;
|
||||
|
||||
void FlutterQjsPlugin::HandleMethodCall(
|
||||
const flutter::MethodCall<flutter::EncodableValue> &method_call,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
|
||||
{
|
||||
// Replace "getPlatformVersion" check with your plugin's method.
|
||||
// See:
|
||||
// https://github.com/flutter/engine/tree/master/shell/platform/common/cpp/client_wrapper/include/flutter
|
||||
// and
|
||||
// https://github.com/flutter/engine/tree/master/shell/platform/glfw/client_wrapper/include/flutter
|
||||
// for the relevant Flutter APIs.
|
||||
if (method_call.method_name().compare("initEngine") == 0)
|
||||
{
|
||||
engine = new qjs::Engine(channel);
|
||||
flutter::EncodableValue response((long)engine);
|
||||
result->Success(&response);
|
||||
}
|
||||
else if (method_call.method_name().compare("evaluate") == 0)
|
||||
{
|
||||
flutter::EncodableMap args = *((flutter::EncodableMap *)method_call.arguments());
|
||||
std::string script = std::get<std::string>(ValueOrNull(args, "script"));
|
||||
std::string name = std::get<std::string>(ValueOrNull(args, "name"));
|
||||
auto presult = result.release();
|
||||
engine->commit(qjs::EngineTask{
|
||||
script, name,
|
||||
[presult](std::string resolve) {
|
||||
flutter::EncodableValue response(resolve);
|
||||
presult->Success(&response);
|
||||
},
|
||||
[presult](std::string reject) {
|
||||
presult->Error("FlutterJSException", reject);
|
||||
}});
|
||||
}
|
||||
else if (method_call.method_name().compare("close") == 0)
|
||||
{
|
||||
delete engine;
|
||||
result->Success();
|
||||
}
|
||||
else
|
||||
{
|
||||
result->NotImplemented();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void FlutterQjsPluginRegisterWithRegistrar(
|
||||
FlutterDesktopPluginRegistrarRef registrar)
|
||||
{
|
||||
FlutterQjsPlugin::RegisterWithRegistrar(
|
||||
flutter::PluginRegistrarManager::GetInstance()
|
||||
->GetRegistrar<flutter::PluginRegistrarWindows>(registrar));
|
||||
}
|
23
windows/include/flutter_qjs/flutter_qjs_plugin.h
Normal file
23
windows/include/flutter_qjs/flutter_qjs_plugin.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef FLUTTER_PLUGIN_FLUTTER_JS_PLUGIN_H_
|
||||
#define FLUTTER_PLUGIN_FLUTTER_JS_PLUGIN_H_
|
||||
|
||||
#include <flutter_plugin_registrar.h>
|
||||
|
||||
#ifdef FLUTTER_PLUGIN_IMPL
|
||||
#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport)
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void FlutterQjsPluginRegisterWithRegistrar(
|
||||
FlutterDesktopPluginRegistrarRef registrar);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // FLUTTER_PLUGIN_FLUTTER_JS_PLUGIN_H_
|
207
windows/js_dart_promise.hpp
Normal file
207
windows/js_dart_promise.hpp
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* @Description:
|
||||
* @Author: ekibun
|
||||
* @Date: 2020-08-07 13:55:52
|
||||
* @LastEditors: ekibun
|
||||
* @LastEditTime: 2020-08-08 16:54:23
|
||||
*/
|
||||
#pragma once
|
||||
#include "quickjs/quickjspp.hpp"
|
||||
#include "quickjs/quickjs/list.h"
|
||||
#include <flutter/method_result_functions.h>
|
||||
#include <future>
|
||||
|
||||
namespace qjs
|
||||
{
|
||||
static JSClassID js_dart_promise_class_id;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int count;
|
||||
JSValue *argv;
|
||||
} JSOSFutureArgv;
|
||||
|
||||
using JSFutureReturn = std::function<JSOSFutureArgv(JSContext *)>;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct list_head link;
|
||||
std::shared_future<JSFutureReturn> future;
|
||||
JSValue resolve;
|
||||
JSValue reject;
|
||||
} JSOSFuture;
|
||||
|
||||
typedef struct JSThreadState
|
||||
{
|
||||
struct list_head os_future; /* list of JSOSFuture.link */
|
||||
std::shared_ptr<flutter::MethodChannel<flutter::EncodableValue>> channel;
|
||||
} JSThreadState;
|
||||
|
||||
static JSValue js_add_future(Value resolve, Value reject, std::shared_future<JSFutureReturn> future)
|
||||
{
|
||||
JSRuntime *rt = JS_GetRuntime(resolve.ctx);
|
||||
JSThreadState *ts = (JSThreadState *)JS_GetRuntimeOpaque(rt);
|
||||
JSValueConst jsResolve, jsReject;
|
||||
JSOSFuture *th;
|
||||
JSValue obj;
|
||||
|
||||
jsResolve = resolve.v;
|
||||
if (!JS_IsFunction(resolve.ctx, jsResolve))
|
||||
return JS_ThrowTypeError(resolve.ctx, "resolve not a function");
|
||||
jsReject = reject.v;
|
||||
if (!JS_IsFunction(reject.ctx, jsReject))
|
||||
return JS_ThrowTypeError(reject.ctx, "reject not a function");
|
||||
obj = JS_NewObjectClass(resolve.ctx, js_dart_promise_class_id);
|
||||
if (JS_IsException(obj))
|
||||
return obj;
|
||||
th = (JSOSFuture *)js_mallocz(resolve.ctx, sizeof(*th));
|
||||
if (!th)
|
||||
{
|
||||
JS_FreeValue(resolve.ctx, obj);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
th->future = future;
|
||||
th->resolve = JS_DupValue(resolve.ctx, jsResolve);
|
||||
th->reject = JS_DupValue(reject.ctx, jsReject);
|
||||
list_add_tail(&th->link, &ts->os_future);
|
||||
JS_SetOpaque(obj, th);
|
||||
return obj;
|
||||
}
|
||||
|
||||
JSValue js_dart_future(Value resolve, Value reject, std::string name, std::string args)
|
||||
{
|
||||
auto promise = new std::promise<JSFutureReturn>();
|
||||
JSRuntime *rt = JS_GetRuntime(resolve.ctx);
|
||||
JSThreadState *ts = (JSThreadState *)JS_GetRuntimeOpaque(rt);
|
||||
ts->channel->InvokeMethod(
|
||||
name,
|
||||
std::make_unique<flutter::EncodableValue>(args),
|
||||
std::make_unique<flutter::MethodResultFunctions<flutter::EncodableValue>>(
|
||||
(flutter::ResultHandlerSuccess<flutter::EncodableValue>)[promise](
|
||||
const flutter::EncodableValue *result) {
|
||||
promise->set_value((JSFutureReturn)[rep = std::get<std::string>(*result)](JSContext * ctx) {
|
||||
JSValue *ret = new JSValue{JS_NewString(ctx, rep.c_str())};
|
||||
return JSOSFutureArgv{1, ret};
|
||||
});
|
||||
},
|
||||
(flutter::ResultHandlerError<flutter::EncodableValue>)[promise](
|
||||
const std::string &error_code,
|
||||
const std::string &error_message,
|
||||
const flutter::EncodableValue *error_details) {
|
||||
promise->set_value((JSFutureReturn)[error_message](JSContext * ctx) {
|
||||
JSValue *ret = new JSValue{JS_NewString(ctx, error_message.c_str())};
|
||||
return JSOSFutureArgv{-1, ret};
|
||||
});
|
||||
},
|
||||
(flutter::ResultHandlerNotImplemented<flutter::EncodableValue>)[promise]() {
|
||||
promise->set_value((JSFutureReturn)[](JSContext * ctx) {
|
||||
JSValue *ret = new JSValue{JS_NewString(ctx, "NotImplemented")};
|
||||
return JSOSFutureArgv{-1, ret};
|
||||
});
|
||||
}));
|
||||
return js_add_future(resolve, reject, promise->get_future());
|
||||
}
|
||||
|
||||
static void unlink_future(JSRuntime *rt, JSOSFuture *th)
|
||||
{
|
||||
if (th->link.prev)
|
||||
{
|
||||
list_del(&th->link);
|
||||
th->link.prev = th->link.next = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void free_future(JSRuntime *rt, JSOSFuture *th)
|
||||
{
|
||||
JS_FreeValueRT(rt, th->resolve);
|
||||
JS_FreeValueRT(rt, th->reject);
|
||||
js_free_rt(rt, th);
|
||||
}
|
||||
|
||||
void js_init_handlers(JSRuntime *rt, std::shared_ptr<flutter::MethodChannel<flutter::EncodableValue>> channel)
|
||||
{
|
||||
JSThreadState *ts = (JSThreadState *)malloc(sizeof(*ts));
|
||||
if (!ts)
|
||||
{
|
||||
fprintf(stderr, "Could not allocate memory for the worker");
|
||||
exit(1);
|
||||
}
|
||||
memset(ts, 0, sizeof(*ts));
|
||||
init_list_head(&ts->os_future);
|
||||
ts->channel = channel;
|
||||
|
||||
JS_SetRuntimeOpaque(rt, ts);
|
||||
}
|
||||
|
||||
void js_free_handlers(JSRuntime *rt)
|
||||
{
|
||||
JSThreadState *ts = (JSThreadState *)JS_GetRuntimeOpaque(rt);
|
||||
struct list_head *el, *el1;
|
||||
|
||||
list_for_each_safe(el, el1, &ts->os_future)
|
||||
{
|
||||
JSOSFuture *th = list_entry(el, JSOSFuture, link);
|
||||
th->future.get();
|
||||
unlink_future(rt, th);
|
||||
free_future(rt, th);
|
||||
}
|
||||
ts->channel = nullptr;
|
||||
free(ts);
|
||||
JS_SetRuntimeOpaque(rt, NULL); /* fail safe */
|
||||
}
|
||||
|
||||
static void call_handler(JSContext *ctx, JSValueConst func, int count, JSValue *argv)
|
||||
{
|
||||
JSValue ret, func1;
|
||||
/* 'func' might be destroyed when calling itself (if it frees the
|
||||
handler), so must take extra care */
|
||||
func1 = JS_DupValue(ctx, func);
|
||||
ret = JS_Call(ctx, func1, JS_UNDEFINED, count, argv);
|
||||
JS_FreeValue(ctx, func1);
|
||||
if (JS_IsException(ret))
|
||||
throw exception{};
|
||||
JS_FreeValue(ctx, ret);
|
||||
}
|
||||
|
||||
static int js_dart_poll(JSContext *ctx)
|
||||
{
|
||||
JSRuntime *rt = JS_GetRuntime(ctx);
|
||||
JSThreadState *ts = (JSThreadState *)JS_GetRuntimeOpaque(rt);
|
||||
struct list_head *el;
|
||||
|
||||
/* XXX: handle signals if useful */
|
||||
|
||||
if (list_empty(&ts->os_future))
|
||||
return -1; /* no more events */
|
||||
|
||||
/* XXX: only timers and basic console input are supported */
|
||||
if (!list_empty(&ts->os_future))
|
||||
{
|
||||
list_for_each(el, &ts->os_future)
|
||||
{
|
||||
JSOSFuture *th = list_entry(el, JSOSFuture, link);
|
||||
if (th->future._Is_ready())
|
||||
{
|
||||
JSOSFutureArgv argv = th->future.get()(ctx);
|
||||
JSValue resolve, reject;
|
||||
int64_t delay;
|
||||
/* the timer expired */
|
||||
resolve = th->resolve;
|
||||
th->resolve = JS_UNDEFINED;
|
||||
reject = th->reject;
|
||||
th->reject = JS_UNDEFINED;
|
||||
unlink_future(rt, th);
|
||||
free_future(rt, th);
|
||||
call_handler(ctx, argv.count < 0 ? reject : resolve, abs(argv.count), argv.argv);
|
||||
for (int i = 0; i < abs(argv.count); ++i)
|
||||
JS_FreeValue(ctx, argv.argv[i]);
|
||||
JS_FreeValue(ctx, resolve);
|
||||
JS_FreeValue(ctx, reject);
|
||||
delete argv.argv;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace qjs
|
192
windows/js_engine.hpp
Normal file
192
windows/js_engine.hpp
Normal file
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* @Description:
|
||||
* @Author: ekibun
|
||||
* @Date: 2020-08-08 10:30:59
|
||||
* @LastEditors: ekibun
|
||||
* @LastEditTime: 2020-08-08 17:05:22
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <future>
|
||||
#include <iostream>
|
||||
|
||||
#include "js_dart_promise.hpp"
|
||||
#include "quickjs/quickjspp.hpp"
|
||||
|
||||
namespace qjs
|
||||
{
|
||||
struct EngineTask
|
||||
{
|
||||
std::string command;
|
||||
std::string name;
|
||||
std::function<void(std::string)> resolve;
|
||||
std::function<void(std::string)> reject;
|
||||
};
|
||||
|
||||
struct EngineTaskResolver
|
||||
{
|
||||
Value result;
|
||||
std::function<void(std::string)> resolve;
|
||||
std::function<void(std::string)> reject;
|
||||
};
|
||||
|
||||
std::string getStackTrack(Value exc)
|
||||
{
|
||||
std::string err = (std::string)exc;
|
||||
if ((bool)exc["stack"])
|
||||
err += "\n" + (std::string)exc["stack"];
|
||||
return err;
|
||||
}
|
||||
|
||||
class Engine
|
||||
{
|
||||
// 引擎线程
|
||||
std::thread thread;
|
||||
// 任务队列
|
||||
std::queue<EngineTask> tasks;
|
||||
// 同步
|
||||
std::mutex m_lock;
|
||||
// 是否关闭提交
|
||||
std::atomic<bool> stoped;
|
||||
|
||||
void handleException(qjs::Value exc)
|
||||
{
|
||||
std::cout << getStackTrack(exc) << std::endl;
|
||||
}
|
||||
|
||||
public:
|
||||
inline Engine(std::shared_ptr<flutter::MethodChannel<flutter::EncodableValue>> channel) : stoped{false}
|
||||
{
|
||||
thread = std::thread([this, channel] { // 工作线程函数
|
||||
// 创建运行环境
|
||||
Runtime rt;
|
||||
js_init_handlers(rt.rt, channel);
|
||||
Context ctx(rt);
|
||||
auto &module = ctx.addModule("__DartImpl");
|
||||
module.function<&js_dart_future>("__invoke");
|
||||
ctx.eval(
|
||||
R"xxx(
|
||||
import * as __DartImpl from "__DartImpl";
|
||||
globalThis.dart = (method, ...args) => new Promise((res, rej) =>
|
||||
__DartImpl.__invoke((v) => res(JSON.parse(v)), rej, method, JSON.stringify(args)));
|
||||
)xxx",
|
||||
"<dart>", JS_EVAL_TYPE_MODULE);
|
||||
std::vector<EngineTaskResolver> unresolvedTask;
|
||||
// 循环
|
||||
while (!this->stoped)
|
||||
{
|
||||
// 获取待执行的task
|
||||
EngineTask task;
|
||||
{ // 获取一个待执行的 task
|
||||
std::unique_lock<std::mutex> lock{this->m_lock}; // unique_lock 相比 lock_guard 的好处是:可以随时 unlock() 和 lock()
|
||||
if (!this->tasks.empty())
|
||||
{
|
||||
task = this->tasks.front(); // 取一个 task
|
||||
this->tasks.pop();
|
||||
}
|
||||
}
|
||||
// 执行task
|
||||
if (task.resolve)
|
||||
try
|
||||
{
|
||||
ctx.global()["__evalstr"] = JS_NewString(ctx.ctx, task.command.c_str());
|
||||
Value ret = ctx.eval(
|
||||
R"xxx(
|
||||
(() => {
|
||||
const __ret = Promise.resolve(eval(__evalstr))
|
||||
.then(v => {
|
||||
__ret.__value = v;
|
||||
__ret.__resolved = true;
|
||||
}).catch(e => {
|
||||
__ret.__error = e;
|
||||
__ret.__rejected = true;
|
||||
});
|
||||
return __ret;
|
||||
})()
|
||||
)xxx",
|
||||
task.name.c_str());
|
||||
unresolvedTask.emplace_back(EngineTaskResolver{ret, std::move(task.resolve), std::move(task.reject)});
|
||||
}
|
||||
catch (exception e)
|
||||
{
|
||||
task.reject(getStackTrack(ctx.getException()));
|
||||
}
|
||||
// 执行microtask
|
||||
JSContext *pctx;
|
||||
for (;;)
|
||||
{
|
||||
int err = JS_ExecutePendingJob(rt.rt, &pctx);
|
||||
if (err <= 0)
|
||||
{
|
||||
if (err < 0)
|
||||
std::cout << getStackTrack(ctx.getException()) << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// TODO 检查promise状态
|
||||
for (auto it = unresolvedTask.begin(); it != unresolvedTask.end();)
|
||||
{
|
||||
bool finished = false;
|
||||
if (it->result["__resolved"])
|
||||
{
|
||||
it->resolve((std::string)it->result["__value"]);
|
||||
finished = true;
|
||||
};
|
||||
if (it->result["__rejected"])
|
||||
{
|
||||
it->reject(getStackTrack(it->result["__error"]));
|
||||
finished = true;
|
||||
};
|
||||
if (finished)
|
||||
it = unresolvedTask.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
// 检查dart交互
|
||||
bool idle = true;
|
||||
try
|
||||
{
|
||||
idle = js_dart_poll(ctx.ctx);
|
||||
}
|
||||
catch (exception e)
|
||||
{
|
||||
handleException(ctx.getException());
|
||||
}
|
||||
// 空闲时reject所有task
|
||||
if (idle && !JS_IsJobPending(rt.rt) && !unresolvedTask.empty())
|
||||
{
|
||||
for (EngineTaskResolver &task : unresolvedTask)
|
||||
{
|
||||
task.reject("Promise cannot resolve");
|
||||
}
|
||||
unresolvedTask.clear();
|
||||
}
|
||||
}
|
||||
js_free_handlers(rt.rt);
|
||||
});
|
||||
}
|
||||
inline ~Engine()
|
||||
{
|
||||
stoped.store(true);
|
||||
if (thread.joinable())
|
||||
thread.join(); // 等待任务结束, 前提:线程一定会执行完
|
||||
}
|
||||
|
||||
public:
|
||||
// 提交一个任务
|
||||
void commit(EngineTask task)
|
||||
{
|
||||
if (stoped.load()) // stop == true ??
|
||||
throw std::runtime_error("commit on stopped engine.");
|
||||
{ // 添加任务到队列
|
||||
std::lock_guard<std::mutex> lock{m_lock}; //对当前块的语句加锁 lock_guard 是 mutex 的 stack 封装类,构造的时候 lock(),析构的时候 unlock()
|
||||
tasks.emplace(task);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace qjs
|
195
windows/quickjs/libquickjs.def
Normal file
195
windows/quickjs/libquickjs.def
Normal file
@@ -0,0 +1,195 @@
|
||||
EXPORTS
|
||||
JS_AddIntrinsicAtomics
|
||||
JS_AddIntrinsicBaseObjects
|
||||
JS_AddIntrinsicBigDecimal
|
||||
JS_AddIntrinsicBigFloat
|
||||
JS_AddIntrinsicBigInt
|
||||
JS_AddIntrinsicDate
|
||||
JS_AddIntrinsicEval
|
||||
JS_AddIntrinsicJSON
|
||||
JS_AddIntrinsicMapSet
|
||||
JS_AddIntrinsicOperators
|
||||
JS_AddIntrinsicPromise
|
||||
JS_AddIntrinsicProxy
|
||||
JS_AddIntrinsicRegExp
|
||||
JS_AddIntrinsicRegExpCompiler
|
||||
JS_AddIntrinsicStringNormalize
|
||||
JS_AddIntrinsicTypedArrays
|
||||
JS_AddModuleExport
|
||||
JS_AddModuleExportList
|
||||
JS_AtomToCString
|
||||
JS_AtomToString
|
||||
JS_AtomToValue
|
||||
JS_Call
|
||||
JS_CallConstructor
|
||||
JS_CallConstructor2
|
||||
JS_ComputeMemoryUsage
|
||||
JS_DefineProperty
|
||||
JS_DefinePropertyGetSet
|
||||
JS_DefinePropertyValue
|
||||
JS_DefinePropertyValueInt64
|
||||
JS_DefinePropertyValueStr
|
||||
JS_DefinePropertyValueUint32
|
||||
JS_DefinePropertyValueValue
|
||||
JS_DeleteProperty
|
||||
JS_DeletePropertyInt64
|
||||
JS_DetachArrayBuffer
|
||||
JS_DetectModule
|
||||
JS_DumpMemoryUsage
|
||||
JS_DupAtom
|
||||
JS_DupContext
|
||||
JS_EnableBignumExt
|
||||
JS_EnqueueJob
|
||||
JS_Eval
|
||||
JS_EvalFunction
|
||||
JS_ExecutePendingJob
|
||||
JS_FreeAtom
|
||||
JS_FreeAtomRT
|
||||
JS_FreeCString
|
||||
JS_FreeContext
|
||||
JS_FreeRuntime
|
||||
JS_GetArrayBuffer
|
||||
JS_GetClassProto
|
||||
JS_GetContextOpaque
|
||||
JS_GetException
|
||||
JS_GetGlobalObject
|
||||
JS_GetImportMeta
|
||||
JS_GetModuleName
|
||||
JS_GetOpaque
|
||||
JS_GetOpaque2
|
||||
JS_GetOwnProperty
|
||||
JS_GetOwnPropertyNames
|
||||
JS_GetPropertyInternal
|
||||
JS_GetPropertyStr
|
||||
JS_GetPropertyUint32
|
||||
JS_GetPrototype
|
||||
JS_GetRuntime
|
||||
JS_GetRuntimeOpaque
|
||||
JS_GetTypedArrayBuffer
|
||||
JS_HasProperty
|
||||
JS_Invoke
|
||||
JS_IsArray
|
||||
JS_IsCFunction
|
||||
JS_IsConstructor
|
||||
JS_IsError
|
||||
JS_IsExtensible
|
||||
JS_IsFunction
|
||||
JS_IsInstanceOf
|
||||
JS_IsJobPending
|
||||
JS_IsLiveObject
|
||||
JS_IsRegisteredClass
|
||||
JS_IsUncatchableError
|
||||
JS_JSONStringify
|
||||
JS_MarkValue
|
||||
JS_NewArray
|
||||
JS_NewArrayBuffer
|
||||
JS_NewArrayBufferCopy
|
||||
JS_NewAtom
|
||||
JS_NewAtomLen
|
||||
JS_NewAtomString
|
||||
JS_NewAtomUInt32
|
||||
JS_NewBigInt64
|
||||
JS_NewBigInt64_1
|
||||
JS_NewBigUint64
|
||||
JS_NewCFunction2
|
||||
JS_NewCFunctionData
|
||||
JS_NewCModule
|
||||
JS_NewClass
|
||||
JS_NewClassID
|
||||
JS_NewContext
|
||||
JS_NewContextRaw
|
||||
JS_NewError
|
||||
JS_NewObject
|
||||
JS_NewObjectClass
|
||||
JS_NewObjectProto
|
||||
JS_NewObjectProtoClass
|
||||
JS_NewPromiseCapability
|
||||
JS_NewRuntime
|
||||
JS_NewRuntime2
|
||||
JS_NewString
|
||||
JS_NewStringLen
|
||||
JS_ParseJSON
|
||||
JS_ParseJSON2
|
||||
JS_PreventExtensions
|
||||
JS_ReadObject
|
||||
JS_ResetUncatchableError
|
||||
JS_ResolveModule
|
||||
JS_RunGC
|
||||
JS_SetCanBlock
|
||||
JS_SetClassProto
|
||||
JS_SetConstructor
|
||||
JS_SetConstructorBit
|
||||
JS_SetContextOpaque
|
||||
JS_SetGCThreshold
|
||||
JS_SetHostPromiseRejectionTracker
|
||||
JS_SetInterruptHandler
|
||||
JS_SetMaxStackSize
|
||||
JS_SetMemoryLimit
|
||||
JS_SetModuleExport
|
||||
JS_SetModuleExportList
|
||||
JS_SetModuleLoaderFunc
|
||||
JS_SetOpaque
|
||||
JS_SetPropertyFunctionList
|
||||
JS_SetPropertyInt64
|
||||
JS_SetPropertyInternal
|
||||
JS_SetPropertyStr
|
||||
JS_SetPropertyUint32
|
||||
JS_SetPrototype
|
||||
JS_SetRuntimeInfo
|
||||
JS_SetRuntimeOpaque
|
||||
JS_SetSharedArrayBufferFunctions
|
||||
JS_SetUncatchableError
|
||||
JS_Throw
|
||||
JS_ThrowInternalError
|
||||
JS_ThrowOutOfMemory
|
||||
JS_ThrowRangeError
|
||||
JS_ThrowReferenceError
|
||||
JS_ThrowSyntaxError
|
||||
JS_ThrowTypeError
|
||||
JS_ToBigInt64
|
||||
JS_ToBool
|
||||
JS_ToCStringLen2
|
||||
JS_ToFloat64
|
||||
JS_ToIndex
|
||||
JS_ToInt32
|
||||
JS_ToInt32Clamp
|
||||
JS_ToInt32Sat
|
||||
JS_ToInt64
|
||||
JS_ToInt64Clamp
|
||||
JS_ToInt64Ext
|
||||
JS_ToInt64Sat
|
||||
JS_ToPropertyKey
|
||||
JS_ToString
|
||||
JS_ToStringInternal
|
||||
JS_ValueToAtom
|
||||
JS_WriteObject
|
||||
JS_WriteObject2
|
||||
__JS_FreeValue
|
||||
__JS_FreeValueRT
|
||||
js_free
|
||||
js_free_rt
|
||||
js_init_module_os
|
||||
js_init_module_std
|
||||
js_load_file
|
||||
js_malloc
|
||||
js_malloc_rt
|
||||
js_malloc_usable_size
|
||||
js_malloc_usable_size_rt
|
||||
js_mallocz
|
||||
js_mallocz_rt
|
||||
js_module_loader
|
||||
js_module_set_import_meta
|
||||
js_parse_error
|
||||
js_realloc
|
||||
js_realloc2
|
||||
js_realloc_rt
|
||||
js_std_add_helpers
|
||||
js_std_dump_error
|
||||
js_std_eval_binary
|
||||
js_std_free_handlers
|
||||
js_std_init_handlers
|
||||
js_std_loop
|
||||
js_std_promise_rejection_tracker
|
||||
js_strdup
|
||||
js_string_codePointRange
|
||||
js_strndup
|
BIN
windows/quickjs/libquickjs32.a
Normal file
BIN
windows/quickjs/libquickjs32.a
Normal file
Binary file not shown.
BIN
windows/quickjs/libquickjs32.dll
Normal file
BIN
windows/quickjs/libquickjs32.dll
Normal file
Binary file not shown.
BIN
windows/quickjs/libquickjs64.a
Normal file
BIN
windows/quickjs/libquickjs64.a
Normal file
Binary file not shown.
BIN
windows/quickjs/libquickjs64.dll
Normal file
BIN
windows/quickjs/libquickjs64.dll
Normal file
Binary file not shown.
100
windows/quickjs/quickjs/list.h
Normal file
100
windows/quickjs/quickjs/list.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Linux klist like system
|
||||
*
|
||||
* Copyright (c) 2016-2017 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef LIST_H
|
||||
#define LIST_H
|
||||
|
||||
#ifndef NULL
|
||||
#include <stddef.h>
|
||||
#endif
|
||||
|
||||
struct list_head {
|
||||
struct list_head *prev;
|
||||
struct list_head *next;
|
||||
};
|
||||
|
||||
#define LIST_HEAD_INIT(el) { &(el), &(el) }
|
||||
|
||||
/* return the pointer of type 'type *' containing 'el' as field 'member' */
|
||||
#define list_entry(el, type, member) \
|
||||
((type *)((uint8_t *)(el) - offsetof(type, member)))
|
||||
|
||||
static inline void init_list_head(struct list_head *head)
|
||||
{
|
||||
head->prev = head;
|
||||
head->next = head;
|
||||
}
|
||||
|
||||
/* insert 'el' between 'prev' and 'next' */
|
||||
static inline void __list_add(struct list_head *el,
|
||||
struct list_head *prev, struct list_head *next)
|
||||
{
|
||||
prev->next = el;
|
||||
el->prev = prev;
|
||||
el->next = next;
|
||||
next->prev = el;
|
||||
}
|
||||
|
||||
/* add 'el' at the head of the list 'head' (= after element head) */
|
||||
static inline void list_add(struct list_head *el, struct list_head *head)
|
||||
{
|
||||
__list_add(el, head, head->next);
|
||||
}
|
||||
|
||||
/* add 'el' at the end of the list 'head' (= before element head) */
|
||||
static inline void list_add_tail(struct list_head *el, struct list_head *head)
|
||||
{
|
||||
__list_add(el, head->prev, head);
|
||||
}
|
||||
|
||||
static inline void list_del(struct list_head *el)
|
||||
{
|
||||
struct list_head *prev, *next;
|
||||
prev = el->prev;
|
||||
next = el->next;
|
||||
prev->next = next;
|
||||
next->prev = prev;
|
||||
el->prev = NULL; /* fail safe */
|
||||
el->next = NULL; /* fail safe */
|
||||
}
|
||||
|
||||
static inline int list_empty(struct list_head *el)
|
||||
{
|
||||
return el->next == el;
|
||||
}
|
||||
|
||||
#define list_for_each(el, head) \
|
||||
for(el = (head)->next; el != (head); el = el->next)
|
||||
|
||||
#define list_for_each_safe(el, el1, head) \
|
||||
for(el = (head)->next, el1 = el->next; el != (head); \
|
||||
el = el1, el1 = el->next)
|
||||
|
||||
#define list_for_each_prev(el, head) \
|
||||
for(el = (head)->prev; el != (head); el = el->prev)
|
||||
|
||||
#define list_for_each_prev_safe(el, el1, head) \
|
||||
for(el = (head)->prev, el1 = el->prev; el != (head); \
|
||||
el = el1, el1 = el->prev)
|
||||
|
||||
#endif /* LIST_H */
|
58
windows/quickjs/quickjs/quickjs-libc.h
Normal file
58
windows/quickjs/quickjs/quickjs-libc.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* QuickJS C library
|
||||
*
|
||||
* Copyright (c) 2017-2018 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef QUICKJS_LIBC_H
|
||||
#define QUICKJS_LIBC_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "quickjs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name);
|
||||
JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name);
|
||||
void js_std_add_helpers(JSContext *ctx, int argc, char **argv);
|
||||
void js_std_loop(JSContext *ctx);
|
||||
void js_std_init_handlers(JSRuntime *rt);
|
||||
void js_std_free_handlers(JSRuntime *rt);
|
||||
void js_std_dump_error(JSContext *ctx);
|
||||
uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename);
|
||||
int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val,
|
||||
JS_BOOL use_realpath, JS_BOOL is_main);
|
||||
JSModuleDef *js_module_loader(JSContext *ctx,
|
||||
const char *module_name, void *opaque);
|
||||
void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len,
|
||||
int flags);
|
||||
void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise,
|
||||
JSValueConst reason,
|
||||
JS_BOOL is_handled, void *opaque);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" { */
|
||||
#endif
|
||||
|
||||
#endif /* QUICKJS_LIBC_H */
|
1031
windows/quickjs/quickjs/quickjs.h
Normal file
1031
windows/quickjs/quickjs/quickjs.h
Normal file
File diff suppressed because it is too large
Load Diff
1304
windows/quickjs/quickjspp.hpp
Normal file
1304
windows/quickjs/quickjspp.hpp
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user