From 6597f414cb0615bc196fc9dd8cf40cdf5d6cc5e2 Mon Sep 17 00:00:00 2001 From: boyan <17426470+boyan01@users.noreply.github.com> Date: Mon, 25 Oct 2021 18:12:29 +0800 Subject: [PATCH] implement windows --- .../pasteboard/.dart_tool/package_config.json | 50 +- .../.dart_tool/package_config_subset | 98 +-- packages/pasteboard/.dart_tool/version | 2 +- packages/pasteboard/.gitignore | 32 + packages/pasteboard/.idea/.gitignore | 3 - .../pasteboard/.idea/libraries/Dart_SDK.xml | 19 - .../.idea/libraries/Flutter_Plugins.xml | 9 - packages/pasteboard/.idea/modules.xml | 10 - .../example_lib_main_dart.xml | 6 - packages/pasteboard/.idea/vcs.xml | 6 - packages/pasteboard/.packages | 29 - packages/pasteboard/example/lib/main.dart | 28 +- packages/pasteboard/example/pubspec.lock | 14 +- .../pasteboard/example/windows/.gitignore | 19 + .../pasteboard/example/windows/CMakeLists.txt | 95 +++ .../example/windows/flutter/CMakeLists.txt | 103 ++++ .../flutter/generated_plugin_registrant.cc | 14 + .../flutter/generated_plugin_registrant.h | 15 + .../windows/flutter/generated_plugins.cmake | 16 + .../example/windows/runner/CMakeLists.txt | 17 + .../example/windows/runner/Runner.rc | 121 ++++ .../example/windows/runner/flutter_window.cpp | 61 ++ .../example/windows/runner/flutter_window.h | 33 + .../example/windows/runner/main.cpp | 43 ++ .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../windows/runner/runner.exe.manifest | 20 + .../example/windows/runner/utils.cpp | 64 ++ .../pasteboard/example/windows/runner/utils.h | 19 + .../example/windows/runner/win32_window.cpp | 245 ++++++++ .../example/windows/runner/win32_window.h | 98 +++ packages/pasteboard/lib/pasteboard.dart | 25 +- packages/pasteboard/pasteboard.iml | 22 - packages/pasteboard/pubspec.lock | 154 ----- packages/pasteboard/pubspec.yaml | 2 + packages/pasteboard/test/pasteboard_test.dart | 23 + packages/pasteboard/windows/.gitignore | 17 + packages/pasteboard/windows/CMakeLists.txt | 26 + .../include/pasteboard/pasteboard_plugin.h | 23 + .../pasteboard/windows/pasteboard_plugin.cpp | 367 ++++++++++++ packages/pasteboard/windows/strconv.h | 565 ++++++++++++++++++ 41 files changed, 2176 insertions(+), 353 deletions(-) create mode 100644 packages/pasteboard/.gitignore delete mode 100644 packages/pasteboard/.idea/.gitignore delete mode 100644 packages/pasteboard/.idea/libraries/Dart_SDK.xml delete mode 100644 packages/pasteboard/.idea/libraries/Flutter_Plugins.xml delete mode 100644 packages/pasteboard/.idea/modules.xml delete mode 100644 packages/pasteboard/.idea/runConfigurations/example_lib_main_dart.xml delete mode 100644 packages/pasteboard/.idea/vcs.xml delete mode 100644 packages/pasteboard/.packages create mode 100644 packages/pasteboard/example/windows/.gitignore create mode 100644 packages/pasteboard/example/windows/CMakeLists.txt create mode 100644 packages/pasteboard/example/windows/flutter/CMakeLists.txt create mode 100644 packages/pasteboard/example/windows/flutter/generated_plugin_registrant.cc create mode 100644 packages/pasteboard/example/windows/flutter/generated_plugin_registrant.h create mode 100644 packages/pasteboard/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/pasteboard/example/windows/runner/CMakeLists.txt create mode 100644 packages/pasteboard/example/windows/runner/Runner.rc create mode 100644 packages/pasteboard/example/windows/runner/flutter_window.cpp create mode 100644 packages/pasteboard/example/windows/runner/flutter_window.h create mode 100644 packages/pasteboard/example/windows/runner/main.cpp create mode 100644 packages/pasteboard/example/windows/runner/resource.h create mode 100644 packages/pasteboard/example/windows/runner/resources/app_icon.ico create mode 100644 packages/pasteboard/example/windows/runner/runner.exe.manifest create mode 100644 packages/pasteboard/example/windows/runner/utils.cpp create mode 100644 packages/pasteboard/example/windows/runner/utils.h create mode 100644 packages/pasteboard/example/windows/runner/win32_window.cpp create mode 100644 packages/pasteboard/example/windows/runner/win32_window.h delete mode 100644 packages/pasteboard/pasteboard.iml delete mode 100644 packages/pasteboard/pubspec.lock create mode 100644 packages/pasteboard/test/pasteboard_test.dart create mode 100644 packages/pasteboard/windows/.gitignore create mode 100644 packages/pasteboard/windows/CMakeLists.txt create mode 100644 packages/pasteboard/windows/include/pasteboard/pasteboard_plugin.h create mode 100644 packages/pasteboard/windows/pasteboard_plugin.cpp create mode 100644 packages/pasteboard/windows/strconv.h diff --git a/packages/pasteboard/.dart_tool/package_config.json b/packages/pasteboard/.dart_tool/package_config.json index d9a139bf..ee5e8360 100644 --- a/packages/pasteboard/.dart_tool/package_config.json +++ b/packages/pasteboard/.dart_tool/package_config.json @@ -3,133 +3,133 @@ "packages": [ { "name": "async", - "rootUri": "file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/async-2.7.0", + "rootUri": "file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/async-2.8.2", "packageUri": "lib/", "languageVersion": "2.12" }, { "name": "boolean_selector", - "rootUri": "file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0", + "rootUri": "file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/boolean_selector-2.1.0", "packageUri": "lib/", "languageVersion": "2.12" }, { "name": "characters", - "rootUri": "file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/characters-1.1.0", + "rootUri": "file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/characters-1.2.0", "packageUri": "lib/", "languageVersion": "2.12" }, { "name": "charcode", - "rootUri": "file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/charcode-1.3.1", + "rootUri": "file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/charcode-1.3.1", "packageUri": "lib/", "languageVersion": "2.12" }, { "name": "clock", - "rootUri": "file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0", + "rootUri": "file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/clock-1.1.0", "packageUri": "lib/", "languageVersion": "2.12" }, { "name": "collection", - "rootUri": "file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/collection-1.15.0", + "rootUri": "file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/collection-1.15.0", "packageUri": "lib/", "languageVersion": "2.12" }, { "name": "fake_async", - "rootUri": "file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/fake_async-1.2.0", + "rootUri": "file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/fake_async-1.2.0", "packageUri": "lib/", "languageVersion": "2.12" }, { "name": "flutter", - "rootUri": "file:///Users/YeungKC/flutter/packages/flutter", + "rootUri": "file:///C:/Users/yangbin/fvm/versions/master/packages/flutter", "packageUri": "lib/", "languageVersion": "2.12" }, { "name": "flutter_test", - "rootUri": "file:///Users/YeungKC/flutter/packages/flutter_test", + "rootUri": "file:///C:/Users/yangbin/fvm/versions/master/packages/flutter_test", "packageUri": "lib/", "languageVersion": "2.12" }, { "name": "matcher", - "rootUri": "file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.10", + "rootUri": "file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/matcher-0.12.11", "packageUri": "lib/", "languageVersion": "2.12" }, { "name": "meta", - "rootUri": "file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/meta-1.4.0", + "rootUri": "file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/meta-1.7.0", "packageUri": "lib/", "languageVersion": "2.12" }, { "name": "path", - "rootUri": "file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/path-1.8.0", + "rootUri": "file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/path-1.8.0", "packageUri": "lib/", "languageVersion": "2.12" }, { "name": "sky_engine", - "rootUri": "file:///Users/YeungKC/flutter/bin/cache/pkg/sky_engine", + "rootUri": "file:///C:/Users/yangbin/fvm/versions/master/bin/cache/pkg/sky_engine", "packageUri": "lib/", "languageVersion": "2.12" }, { "name": "source_span", - "rootUri": "file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.1", + "rootUri": "file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/source_span-1.8.1", "packageUri": "lib/", "languageVersion": "2.12" }, { "name": "stack_trace", - "rootUri": "file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0", + "rootUri": "file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/stack_trace-1.10.0", "packageUri": "lib/", "languageVersion": "2.12" }, { "name": "stream_channel", - "rootUri": "file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0", + "rootUri": "file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/stream_channel-2.1.0", "packageUri": "lib/", "languageVersion": "2.12" }, { "name": "string_scanner", - "rootUri": "file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0", + "rootUri": "file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/string_scanner-1.1.0", "packageUri": "lib/", "languageVersion": "2.12" }, { "name": "term_glyph", - "rootUri": "file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0", + "rootUri": "file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/term_glyph-1.2.0", "packageUri": "lib/", "languageVersion": "2.12" }, { "name": "test_api", - "rootUri": "file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.1", + "rootUri": "file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/test_api-0.4.3", "packageUri": "lib/", "languageVersion": "2.12" }, { "name": "typed_data", - "rootUri": "file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.0", + "rootUri": "file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/typed_data-1.3.0", "packageUri": "lib/", "languageVersion": "2.12" }, { "name": "vector_math", - "rootUri": "file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.0", + "rootUri": "file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/vector_math-2.1.1", "packageUri": "lib/", - "languageVersion": "2.12" + "languageVersion": "2.14" }, { "name": "very_good_analysis", - "rootUri": "file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/very_good_analysis-2.1.2", + "rootUri": "file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/very_good_analysis-2.1.2", "packageUri": "lib/", "languageVersion": "2.12" }, @@ -140,7 +140,7 @@ "languageVersion": "2.12" } ], - "generated": "2021-07-05T05:12:47.903598Z", + "generated": "2021-10-25T09:43:45.845068Z", "generator": "pub", - "generatorVersion": "2.14.0-269.0.dev" + "generatorVersion": "2.15.0-248.0.dev" } diff --git a/packages/pasteboard/.dart_tool/package_config_subset b/packages/pasteboard/.dart_tool/package_config_subset index 0d59a139..fcc8453c 100644 --- a/packages/pasteboard/.dart_tool/package_config_subset +++ b/packages/pasteboard/.dart_tool/package_config_subset @@ -1,93 +1,93 @@ -pasteboard -2.12 -file:///Users/YeungKC/FlutterProjects/flutter-plugins/packages/pasteboard/ -file:///Users/YeungKC/FlutterProjects/flutter-plugins/packages/pasteboard/lib/ async 2.12 -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/async-2.7.0/ -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/async-2.7.0/lib/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/async-2.8.2/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/async-2.8.2/lib/ boolean_selector 2.12 -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/ -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/lib/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/lib/ characters 2.12 -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/characters-1.1.0/ -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/characters-1.1.0/lib/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/characters-1.2.0/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/characters-1.2.0/lib/ charcode 2.12 -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/charcode-1.3.1/ -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/charcode-1.3.1/lib/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/charcode-1.3.1/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/charcode-1.3.1/lib/ clock 2.12 -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0/ -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0/lib/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/clock-1.1.0/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/clock-1.1.0/lib/ collection 2.12 -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/collection-1.15.0/ -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/collection-1.15.0/lib/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/collection-1.15.0/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/collection-1.15.0/lib/ fake_async 2.12 -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/fake_async-1.2.0/ -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/fake_async-1.2.0/lib/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/fake_async-1.2.0/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/fake_async-1.2.0/lib/ matcher 2.12 -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.10/ -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.10/lib/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/matcher-0.12.11/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/matcher-0.12.11/lib/ meta 2.12 -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/meta-1.4.0/ -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/meta-1.4.0/lib/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/meta-1.7.0/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/meta-1.7.0/lib/ path 2.12 -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/path-1.8.0/ -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/path-1.8.0/lib/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/path-1.8.0/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/path-1.8.0/lib/ source_span 2.12 -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.1/ -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.1/lib/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/source_span-1.8.1/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/source_span-1.8.1/lib/ stack_trace 2.12 -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/ -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/stack_trace-1.10.0/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib/ stream_channel 2.12 -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/ -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/lib/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/stream_channel-2.1.0/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/stream_channel-2.1.0/lib/ string_scanner 2.12 -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0/ -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0/lib/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/string_scanner-1.1.0/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/string_scanner-1.1.0/lib/ term_glyph 2.12 -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0/ -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0/lib/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/term_glyph-1.2.0/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/term_glyph-1.2.0/lib/ test_api 2.12 -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.1/ -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.1/lib/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/test_api-0.4.3/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/test_api-0.4.3/lib/ typed_data 2.12 -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.0/ -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.0/lib/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/typed_data-1.3.0/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/typed_data-1.3.0/lib/ vector_math -2.12 -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.0/ -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.0/lib/ +2.14 +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/vector_math-2.1.1/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/vector_math-2.1.1/lib/ very_good_analysis 2.12 -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/very_good_analysis-2.1.2/ -file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/very_good_analysis-2.1.2/lib/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/very_good_analysis-2.1.2/ +file:///C:/Users/yangbin/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/very_good_analysis-2.1.2/lib/ sky_engine 2.12 -file:///Users/YeungKC/flutter/bin/cache/pkg/sky_engine/ -file:///Users/YeungKC/flutter/bin/cache/pkg/sky_engine/lib/ +file:///C:/Users/yangbin/fvm/versions/master/bin/cache/pkg/sky_engine/ +file:///C:/Users/yangbin/fvm/versions/master/bin/cache/pkg/sky_engine/lib/ flutter 2.12 -file:///Users/YeungKC/flutter/packages/flutter/ -file:///Users/YeungKC/flutter/packages/flutter/lib/ +file:///C:/Users/yangbin/fvm/versions/master/packages/flutter/ +file:///C:/Users/yangbin/fvm/versions/master/packages/flutter/lib/ flutter_test 2.12 -file:///Users/YeungKC/flutter/packages/flutter_test/ -file:///Users/YeungKC/flutter/packages/flutter_test/lib/ +file:///C:/Users/yangbin/fvm/versions/master/packages/flutter_test/ +file:///C:/Users/yangbin/fvm/versions/master/packages/flutter_test/lib/ +pasteboard +2.12 +file:///C:/Users/yangbin/workspace/mixin/flutter-plugins/packages/pasteboard/ +file:///C:/Users/yangbin/workspace/mixin/flutter-plugins/packages/pasteboard/lib/ 2 diff --git a/packages/pasteboard/.dart_tool/version b/packages/pasteboard/.dart_tool/version index 39204ca9..40f09f9d 100644 --- a/packages/pasteboard/.dart_tool/version +++ b/packages/pasteboard/.dart_tool/version @@ -1 +1 @@ -2.3.0-17.0.pre.630 \ No newline at end of file +2.6.0-12.0.pre.458 \ No newline at end of file diff --git a/packages/pasteboard/.gitignore b/packages/pasteboard/.gitignore new file mode 100644 index 00000000..beebdaab --- /dev/null +++ b/packages/pasteboard/.gitignore @@ -0,0 +1,32 @@ +# 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 +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ + +.vscode + diff --git a/packages/pasteboard/.idea/.gitignore b/packages/pasteboard/.idea/.gitignore deleted file mode 100644 index 26d33521..00000000 --- a/packages/pasteboard/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/packages/pasteboard/.idea/libraries/Dart_SDK.xml b/packages/pasteboard/.idea/libraries/Dart_SDK.xml deleted file mode 100644 index c8a37540..00000000 --- a/packages/pasteboard/.idea/libraries/Dart_SDK.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/pasteboard/.idea/libraries/Flutter_Plugins.xml b/packages/pasteboard/.idea/libraries/Flutter_Plugins.xml deleted file mode 100644 index 53449dae..00000000 --- a/packages/pasteboard/.idea/libraries/Flutter_Plugins.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/packages/pasteboard/.idea/modules.xml b/packages/pasteboard/.idea/modules.xml deleted file mode 100644 index 82ec8255..00000000 --- a/packages/pasteboard/.idea/modules.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/packages/pasteboard/.idea/runConfigurations/example_lib_main_dart.xml b/packages/pasteboard/.idea/runConfigurations/example_lib_main_dart.xml deleted file mode 100644 index 5fd9159d..00000000 --- a/packages/pasteboard/.idea/runConfigurations/example_lib_main_dart.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/packages/pasteboard/.idea/vcs.xml b/packages/pasteboard/.idea/vcs.xml deleted file mode 100644 index 94a25f7f..00000000 --- a/packages/pasteboard/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/packages/pasteboard/.packages b/packages/pasteboard/.packages deleted file mode 100644 index 20a23093..00000000 --- a/packages/pasteboard/.packages +++ /dev/null @@ -1,29 +0,0 @@ -# This file is deprecated. Tools should instead consume -# `.dart_tools/package_config.json`. -# -# For more info see: https://dart.dev/go/dot-packages-deprecation -# -# Generated by pub on 2021-07-05 13:12:47.885095. -async:file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/async-2.7.0/lib/ -boolean_selector:file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/lib/ -characters:file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/characters-1.1.0/lib/ -charcode:file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/charcode-1.3.1/lib/ -clock:file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0/lib/ -collection:file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/collection-1.15.0/lib/ -fake_async:file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/fake_async-1.2.0/lib/ -flutter:file:///Users/YeungKC/flutter/packages/flutter/lib/ -flutter_test:file:///Users/YeungKC/flutter/packages/flutter_test/lib/ -matcher:file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.10/lib/ -meta:file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/meta-1.4.0/lib/ -path:file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/path-1.8.0/lib/ -sky_engine:file:///Users/YeungKC/flutter/bin/cache/pkg/sky_engine/lib/ -source_span:file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.1/lib/ -stack_trace:file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib/ -stream_channel:file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/lib/ -string_scanner:file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0/lib/ -term_glyph:file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0/lib/ -test_api:file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.1/lib/ -typed_data:file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.0/lib/ -vector_math:file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.0/lib/ -very_good_analysis:file:///Users/YeungKC/flutter/.pub-cache/hosted/pub.dartlang.org/very_good_analysis-2.1.2/lib/ -pasteboard:lib/ diff --git a/packages/pasteboard/example/lib/main.dart b/packages/pasteboard/example/lib/main.dart index f078b645..9d12c8ba 100644 --- a/packages/pasteboard/example/lib/main.dart +++ b/packages/pasteboard/example/lib/main.dart @@ -1,5 +1,6 @@ import 'dart:io'; import 'dart:typed_data'; +import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:pasteboard/pasteboard.dart'; @@ -16,6 +17,8 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State { + String _console = ""; + Uint8List? bytes; String? fileUrl; @@ -43,26 +46,29 @@ class _MyAppState extends State { ), MaterialButton( onPressed: () async { - final url = await Pasteboard.absoluteUrlString; - if (url?.startsWith('file') ?? false) { - var tryParse = Uri.tryParse(url!); - return setState(() { - fileUrl = tryParse!.toFilePath(); - this.bytes = null; - }); - } - final bytes = await Pasteboard.image; setState(() { fileUrl = null; this.bytes = bytes; + _console = "bytes: ${bytes?.length}"; }); }, child: const Text('paste image'), ), - Text('bytes: $bytes', maxLines: 1), - Text('fileUrl: $fileUrl', maxLines: 1), + TextButton( + onPressed: () async { + final files = await Pasteboard.files(); + setState(() { + _console = 'files: \n ${files.isEmpty ? 'empty' : ''}'; + for (final file in files) { + _console += '$file ${File(file).existsSync()}\n'; + } + }); + }, + child: const Text("Get files"), + ), + Text(' $_console'), if (bytes != null) Image.memory(bytes!), if (fileUrl != null) Image.file(File(fileUrl!)) ], diff --git a/packages/pasteboard/example/pubspec.lock b/packages/pasteboard/example/pubspec.lock index be49b4f9..2b9dfc15 100644 --- a/packages/pasteboard/example/pubspec.lock +++ b/packages/pasteboard/example/pubspec.lock @@ -7,7 +7,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.7.0" + version: "2.8.2" boolean_selector: dependency: transitive description: @@ -21,7 +21,7 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0" charcode: dependency: transitive description: @@ -80,14 +80,14 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10" + version: "0.12.11" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.4.0" + version: "1.7.0" pasteboard: dependency: "direct main" description: @@ -148,7 +148,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.1" + version: "0.4.3" typed_data: dependency: transitive description: @@ -162,7 +162,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.1" sdks: - dart: ">=2.12.0 <3.0.0" + dart: ">=2.14.0 <3.0.0" flutter: ">=1.20.0" diff --git a/packages/pasteboard/example/windows/.gitignore b/packages/pasteboard/example/windows/.gitignore new file mode 100644 index 00000000..5d57396c --- /dev/null +++ b/packages/pasteboard/example/windows/.gitignore @@ -0,0 +1,19 @@ +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/ + +cmake-build-* \ No newline at end of file diff --git a/packages/pasteboard/example/windows/CMakeLists.txt b/packages/pasteboard/example/windows/CMakeLists.txt new file mode 100644 index 00000000..4d4932fc --- /dev/null +++ b/packages/pasteboard/example/windows/CMakeLists.txt @@ -0,0 +1,95 @@ +cmake_minimum_required(VERSION 3.14) +project(pasteboard_example LANGUAGES CXX) + +set(BINARY_NAME "pasteboard_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 "$<$:_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 "$") +# 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) diff --git a/packages/pasteboard/example/windows/flutter/CMakeLists.txt b/packages/pasteboard/example/windows/flutter/CMakeLists.txt new file mode 100644 index 00000000..b2e4bd8d --- /dev/null +++ b/packages/pasteboard/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,103 @@ +cmake_minimum_required(VERSION 3.14) + +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" + "flutter_texture_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 + "core_implementations.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_engine.cc" + "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. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/packages/pasteboard/example/windows/flutter/generated_plugin_registrant.cc b/packages/pasteboard/example/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 00000000..7c2f20e1 --- /dev/null +++ b/packages/pasteboard/example/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,14 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include + +void RegisterPlugins(flutter::PluginRegistry* registry) { + PasteboardPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("PasteboardPlugin")); +} diff --git a/packages/pasteboard/example/windows/flutter/generated_plugin_registrant.h b/packages/pasteboard/example/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 00000000..dc139d85 --- /dev/null +++ b/packages/pasteboard/example/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/pasteboard/example/windows/flutter/generated_plugins.cmake b/packages/pasteboard/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 00000000..b0870296 --- /dev/null +++ b/packages/pasteboard/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,16 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + pasteboard +) + +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 $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) diff --git a/packages/pasteboard/example/windows/runner/CMakeLists.txt b/packages/pasteboard/example/windows/runner/CMakeLists.txt new file mode 100644 index 00000000..de2d8916 --- /dev/null +++ b/packages/pasteboard/example/windows/runner/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) +apply_standard_settings(${BINARY_NAME}) +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") +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) diff --git a/packages/pasteboard/example/windows/runner/Runner.rc b/packages/pasteboard/example/windows/runner/Runner.rc new file mode 100644 index 00000000..b284ecc8 --- /dev/null +++ b/packages/pasteboard/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// 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" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#ifdef FLUTTER_BUILD_NUMBER +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#else +#define VERSION_AS_NUMBER 1,0,0 +#endif + +#ifdef FLUTTER_BUILD_NAME +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "Demonstrates how to use the pasteboard plugin." "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "pasteboard_example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2021 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "pasteboard_example.exe" "\0" + VALUE "ProductName", "pasteboard_example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/packages/pasteboard/example/windows/runner/flutter_window.cpp b/packages/pasteboard/example/windows/runner/flutter_window.cpp new file mode 100644 index 00000000..b43b9095 --- /dev/null +++ b/packages/pasteboard/example/windows/runner/flutter_window.cpp @@ -0,0 +1,61 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/packages/pasteboard/example/windows/runner/flutter_window.h b/packages/pasteboard/example/windows/runner/flutter_window.h new file mode 100644 index 00000000..6da0652f --- /dev/null +++ b/packages/pasteboard/example/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/packages/pasteboard/example/windows/runner/main.cpp b/packages/pasteboard/example/windows/runner/main.cpp new file mode 100644 index 00000000..26a18bbf --- /dev/null +++ b/packages/pasteboard/example/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.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); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"pasteboard_example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/packages/pasteboard/example/windows/runner/resource.h b/packages/pasteboard/example/windows/runner/resource.h new file mode 100644 index 00000000..66a65d1e --- /dev/null +++ b/packages/pasteboard/example/windows/runner/resource.h @@ -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 diff --git a/packages/pasteboard/example/windows/runner/resources/app_icon.ico b/packages/pasteboard/example/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/packages/pasteboard/example/windows/runner/runner.exe.manifest b/packages/pasteboard/example/windows/runner/runner.exe.manifest new file mode 100644 index 00000000..c977c4a4 --- /dev/null +++ b/packages/pasteboard/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/packages/pasteboard/example/windows/runner/utils.cpp b/packages/pasteboard/example/windows/runner/utils.cpp new file mode 100644 index 00000000..d19bdbbc --- /dev/null +++ b/packages/pasteboard/example/windows/runner/utils.cpp @@ -0,0 +1,64 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +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(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr); + if (target_length == 0) { + return std::string(); + } + std::string utf8_string; + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/packages/pasteboard/example/windows/runner/utils.h b/packages/pasteboard/example/windows/runner/utils.h new file mode 100644 index 00000000..3879d547 --- /dev/null +++ b/packages/pasteboard/example/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/packages/pasteboard/example/windows/runner/win32_window.cpp b/packages/pasteboard/example/windows/runner/win32_window.cpp new file mode 100644 index 00000000..c10f08dc --- /dev/null +++ b/packages/pasteboard/example/windows/runner/win32_window.cpp @@ -0,0 +1,245 @@ +#include "win32_window.h" + +#include + +#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(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( + 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(origin.x), + static_cast(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); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// 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(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(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 { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(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 = GetClientArea(); + 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; + } + + 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( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/packages/pasteboard/example/windows/runner/win32_window.h b/packages/pasteboard/example/windows/runner/win32_window.h new file mode 100644 index 00000000..17ba4311 --- /dev/null +++ b/packages/pasteboard/example/windows/runner/win32_window.h @@ -0,0 +1,98 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// 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); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + 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. Subclasses should return false if setup fails. + virtual bool 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 // RUNNER_WIN32_WINDOW_H_ diff --git a/packages/pasteboard/lib/pasteboard.dart b/packages/pasteboard/lib/pasteboard.dart index 25c0e150..58b2bede 100644 --- a/packages/pasteboard/lib/pasteboard.dart +++ b/packages/pasteboard/lib/pasteboard.dart @@ -1,13 +1,29 @@ import 'dart:async'; +import 'dart:io'; import 'dart:typed_data'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; class Pasteboard { static const MethodChannel _channel = MethodChannel('pasteboard'); - static Future get image => - _channel.invokeMethod('image'); + static Future get image async { + final image = await _channel.invokeMethod('image'); + + if (image == null) { + return null; + } + if (Platform.isMacOS) { + return image as Uint8List; + } else if (Platform.isWindows) { + final file = File(image as String); + final bytes = await file.readAsBytes(); + await file.delete(); + return bytes; + } + return null; + } static Future get absoluteUrlString => _channel.invokeMethod('absoluteUrlString'); @@ -20,4 +36,9 @@ class Pasteboard { static Future writeUrl(String url) async => await _channel.invokeMethod('writeUrl', [url]); + + static Future> files() async { + final files = await _channel.invokeMethod('files'); + return files?.cast() ?? const []; + } } diff --git a/packages/pasteboard/pasteboard.iml b/packages/pasteboard/pasteboard.iml deleted file mode 100644 index 7ac84a92..00000000 --- a/packages/pasteboard/pasteboard.iml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/pasteboard/pubspec.lock b/packages/pasteboard/pubspec.lock deleted file mode 100644 index 7114b5ca..00000000 --- a/packages/pasteboard/pubspec.lock +++ /dev/null @@ -1,154 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - async: - dependency: transitive - description: - name: async - url: "https://pub.dartlang.org" - source: hosted - version: "2.7.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - characters: - dependency: transitive - description: - name: characters - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.1" - clock: - dependency: transitive - description: - name: clock - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - collection: - dependency: transitive - description: - name: collection - url: "https://pub.dartlang.org" - source: hosted - version: "1.15.0" - fake_async: - dependency: transitive - description: - name: fake_async - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - 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.dartlang.org" - source: hosted - version: "0.12.10" - meta: - dependency: transitive - description: - name: meta - url: "https://pub.dartlang.org" - source: hosted - version: "1.4.0" - path: - dependency: transitive - description: - name: path - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.0" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_span: - dependency: transitive - description: - name: source_span - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.1" - stack_trace: - dependency: transitive - description: - name: stack_trace - url: "https://pub.dartlang.org" - source: hosted - version: "1.10.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - test_api: - dependency: transitive - description: - name: test_api - url: "https://pub.dartlang.org" - source: hosted - version: "0.4.1" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0" - vector_math: - dependency: transitive - description: - name: vector_math - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - very_good_analysis: - dependency: "direct dev" - description: - name: very_good_analysis - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.2" -sdks: - dart: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" diff --git a/packages/pasteboard/pubspec.yaml b/packages/pasteboard/pubspec.yaml index bf2f78e8..5b6f4621 100644 --- a/packages/pasteboard/pubspec.yaml +++ b/packages/pasteboard/pubspec.yaml @@ -29,6 +29,8 @@ flutter: platforms: macos: pluginClass: PasteboardPlugin + windows: + pluginClass: PasteboardPlugin # To add assets to your plugin package, add an assets section, like this: # assets: diff --git a/packages/pasteboard/test/pasteboard_test.dart b/packages/pasteboard/test/pasteboard_test.dart new file mode 100644 index 00000000..4f1a93f5 --- /dev/null +++ b/packages/pasteboard/test/pasteboard_test.dart @@ -0,0 +1,23 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pasteboard/pasteboard.dart'; + +void main() { + const MethodChannel channel = MethodChannel('pasteboard'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + return '42'; + }); + }); + + tearDown(() { + channel.setMockMethodCallHandler(null); + }); + + test('getPlatformVersion', () async { + expect(await Pasteboard.platformVersion, '42'); + }); +} diff --git a/packages/pasteboard/windows/.gitignore b/packages/pasteboard/windows/.gitignore new file mode 100644 index 00000000..b3eb2be1 --- /dev/null +++ b/packages/pasteboard/windows/.gitignore @@ -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/ diff --git a/packages/pasteboard/windows/CMakeLists.txt b/packages/pasteboard/windows/CMakeLists.txt new file mode 100644 index 00000000..6829ed50 --- /dev/null +++ b/packages/pasteboard/windows/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.14) +set(PROJECT_NAME "pasteboard") +project(${PROJECT_NAME} LANGUAGES CXX) + +# This value is used when generating builds using this plugin, so it must +# not be changed +set(PLUGIN_NAME "pasteboard_plugin") + +add_library(${PLUGIN_NAME} SHARED + "pasteboard_plugin.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) + + + +# List of absolute paths to libraries that should be bundled with the plugin +set(pasteboard_bundled_libraries + "" + PARENT_SCOPE + ) diff --git a/packages/pasteboard/windows/include/pasteboard/pasteboard_plugin.h b/packages/pasteboard/windows/include/pasteboard/pasteboard_plugin.h new file mode 100644 index 00000000..67b75c02 --- /dev/null +++ b/packages/pasteboard/windows/include/pasteboard/pasteboard_plugin.h @@ -0,0 +1,23 @@ +#ifndef FLUTTER_PLUGIN_PASTEBOARD_PLUGIN_H_ +#define FLUTTER_PLUGIN_PASTEBOARD_PLUGIN_H_ + +#include + +#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 PasteboardPluginRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // FLUTTER_PLUGIN_PASTEBOARD_PLUGIN_H_ diff --git a/packages/pasteboard/windows/pasteboard_plugin.cpp b/packages/pasteboard/windows/pasteboard_plugin.cpp new file mode 100644 index 00000000..626f7755 --- /dev/null +++ b/packages/pasteboard/windows/pasteboard_plugin.cpp @@ -0,0 +1,367 @@ +#include "include/pasteboard/pasteboard_plugin.h" + +// This must be included before many other Windows headers. +#include + +#include + +#include +#include +#include + +#include +#include +#include "strconv.h" + +namespace { + +PBITMAPINFO CreateBitmapInfoStruct(HBITMAP hBmp) { + BITMAP bmp; + PBITMAPINFO pbmi; + WORD cClrBits; + + // Retrieve the bitmap color format, width, and height. + assert(GetObject(hBmp, sizeof(BITMAP), (LPSTR) &bmp)); + + // Convert the color format to a count of bits. + cClrBits = (WORD) (bmp.bmPlanes * bmp.bmBitsPixel); + if (cClrBits == 1) + cClrBits = 1; + else if (cClrBits <= 4) + cClrBits = 4; + else if (cClrBits <= 8) + cClrBits = 8; + else if (cClrBits <= 16) + cClrBits = 16; + else if (cClrBits <= 24) + cClrBits = 24; + else cClrBits = 32; + + // Allocate memory for the BITMAPINFO structure. (This structure + // contains a BITMAPINFOHEADER structure and an array of RGBQUAD + // data structures.) + + if (cClrBits < 24) + pbmi = (PBITMAPINFO) LocalAlloc(LPTR, + sizeof(BITMAPINFOHEADER) + + sizeof(RGBQUAD) * int(1 << cClrBits)); + + // There is no RGBQUAD array for these formats: 24-bit-per-pixel or 32-bit-per-pixel + + else + pbmi = (PBITMAPINFO) LocalAlloc(LPTR, + sizeof(BITMAPINFOHEADER)); + + // Initialize the fields in the BITMAPINFO structure. + + pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + pbmi->bmiHeader.biWidth = bmp.bmWidth; + pbmi->bmiHeader.biHeight = bmp.bmHeight; + pbmi->bmiHeader.biPlanes = bmp.bmPlanes; + pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel; + if (cClrBits < 24) + pbmi->bmiHeader.biClrUsed = (1 << cClrBits); + + // If the bitmap is not compressed, set the BI_RGB flag. + pbmi->bmiHeader.biCompression = BI_RGB; + + // Compute the number of bytes in the array of color + // indices and store the result in biSizeImage. + // The width must be DWORD aligned unless the bitmap is RLE + // compressed. + pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits + 31) & ~31) / 8 + * pbmi->bmiHeader.biHeight; + // Set biClrImportant to 0, indicating that all of the + // device colors are important. + pbmi->bmiHeader.biClrImportant = 0; + return pbmi; +} + +void CreateBMPFile(LPCTSTR pszFile, HBITMAP hBMP) { + HANDLE hf; // file handle + BITMAPFILEHEADER hdr; // bitmap file-header + PBITMAPINFOHEADER pbih; // bitmap info-header + LPBYTE lpBits; // memory pointer + DWORD dwTotal; // total count of bytes + DWORD cb; // incremental count of bytes + BYTE *hp; // byte pointer + DWORD dwTmp; + PBITMAPINFO pbi; + HDC hDC; + + hDC = CreateCompatibleDC(GetWindowDC(GetDesktopWindow())); + SelectObject(hDC, hBMP); + + pbi = CreateBitmapInfoStruct(hBMP); + + pbih = (PBITMAPINFOHEADER) pbi; + lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage); + + assert(lpBits); + + // Retrieve the color table (RGBQUAD array) and the bits + // (array of palette indices) from the DIB. + assert(GetDIBits(hDC, hBMP, 0, (WORD) pbih->biHeight, lpBits, pbi, + DIB_RGB_COLORS)); + + // Create the .BMP file. + hf = CreateFile(pszFile, + GENERIC_READ | GENERIC_WRITE, + (DWORD) 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + (HANDLE) NULL); + assert(hf != INVALID_HANDLE_VALUE); + + hdr.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M" + // Compute the size of the entire file. + hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) + + pbih->biSize + pbih->biClrUsed + * sizeof(RGBQUAD) + pbih->biSizeImage); + hdr.bfReserved1 = 0; + hdr.bfReserved2 = 0; + + // Compute the offset to the array of color indices. + hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + + pbih->biSize + pbih->biClrUsed + * sizeof(RGBQUAD); + + // Copy the BITMAPFILEHEADER into the .BMP file. + assert(WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER), + (LPDWORD) &dwTmp, NULL)); + + // Copy the BITMAPINFOHEADER and RGBQUAD array into the file. + assert(WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER) + + pbih->biClrUsed * sizeof(RGBQUAD), + (LPDWORD) &dwTmp, (NULL))); + + // Copy the array of color indices into the .BMP file. + dwTotal = cb = pbih->biSizeImage; + hp = lpBits; + assert(WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp, NULL)); + + // Close the .BMP file. + assert(CloseHandle(hf)); + + // Free memory. + GlobalFree((HGLOBAL) lpBits); +} + +void CreateBitmapHeaderWithColorDepth(LONG width, + LONG height, + WORD color_depth, + BITMAPINFOHEADER *hdr) { + // These values are shared with gfx::PlatformDevice. + hdr->biSize = sizeof(BITMAPINFOHEADER); + hdr->biWidth = width; + hdr->biHeight = -height; // Minus means top-down bitmap. + hdr->biPlanes = 1; + hdr->biBitCount = color_depth; + hdr->biCompression = BI_RGB; // No compression. + hdr->biSizeImage = 0; + hdr->biXPelsPerMeter = 1; + hdr->biYPelsPerMeter = 1; + hdr->biClrUsed = 0; + hdr->biClrImportant = 0; +} + +HBITMAP CreateHBitmapXRGB8888(int width, int height, HANDLE shared_section, void **data) { + if (width == 0 || height == 0) { + width = 1; + height = 1; + } + BITMAPINFOHEADER hdr = {0}; + CreateBitmapHeaderWithColorDepth(width, height, 32, &hdr); + HBITMAP hbitmap = CreateDIBSection(nullptr, reinterpret_cast(&hdr), 0, data, shared_section, 0); + return hbitmap; +} + +// A scoper to manage acquiring and automatically releasing the clipboard. +class ScopedClipboard { + public: + ScopedClipboard() : opened_(false) {} + + ~ScopedClipboard() { + if (opened_) + Release(); + } + + bool Acquire(HWND owner) { + const int kMaxAttemptsToOpenClipboard = 5; + + if (opened_) { + return false; + } + + for (int attempts = 0; attempts < kMaxAttemptsToOpenClipboard; ++attempts) { + if (::OpenClipboard(owner)) { + opened_ = true; + return true; + } + + // If we didn't manage to open the clipboard, sleep a bit and be hopeful. + ::Sleep(5); + } + + // We failed to acquire the clipboard. + return false; + } + + void Release() { + if (opened_) { + ::CloseClipboard(); + opened_ = false; + } + } + + private: + bool opened_; +}; + +class PasteboardPlugin : public flutter::Plugin { + public: + static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar); + + PasteboardPlugin(); + + virtual ~PasteboardPlugin(); + + private: + // Called when a method is called on this plugin's channel from Dart. + void HandleMethodCall( + const flutter::MethodCall &method_call, + std::unique_ptr> result); +}; + +// static +void PasteboardPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarWindows *registrar) { + auto channel = + std::make_unique>( + registrar->messenger(), "pasteboard", + &flutter::StandardMethodCodec::GetInstance()); + + auto plugin = std::make_unique(); + + channel->SetMethodCallHandler( + [plugin_pointer = plugin.get()](const auto &call, auto result) { + plugin_pointer->HandleMethodCall(call, std::move(result)); + }); + + registrar->AddPlugin(std::move(plugin)); +} + +PasteboardPlugin::PasteboardPlugin() {} + +PasteboardPlugin::~PasteboardPlugin() {} + +void PasteboardPlugin::HandleMethodCall( + const flutter::MethodCall &method_call, + std::unique_ptr> result) { + if (method_call.method_name() == "image") { + if (!IsClipboardFormatAvailable(CF_DIB)) { + result->Success(); + return; + } + ScopedClipboard clipboard; + + if (!clipboard.Acquire(nullptr)) { + result->Success(); + return; + } + // We use a DIB rather than a DDB here since ::GetObject() with the + // HBITMAP returned from ::GetClipboardData(CF_BITMAP) always reports a color + // depth of 32bpp. + auto *bitmap = static_cast(::GetClipboardData(CF_DIB)); + if (!bitmap) { + result->Success(); + return; + } + + int color_table_length = 0; + + // For more information on BITMAPINFOHEADER and biBitCount definition, + // see https://docs.microsoft.com/en-us/windows/win32/wmdm/-bitmapinfoheader + switch (bitmap->bmiHeader.biBitCount) { + case 1: + case 4: + case 8: + color_table_length = bitmap->bmiHeader.biClrUsed + ? int(bitmap->bmiHeader.biClrUsed) + : int(1 << bitmap->bmiHeader.biBitCount); + break; + case 16: + case 32: + if (bitmap->bmiHeader.biCompression == BI_BITFIELDS) + color_table_length = 3; + break; + case 24:break; + default:result->Success(); + return; + } + + const void *bitmap_bits = reinterpret_cast(bitmap) + + bitmap->bmiHeader.biSize + + color_table_length * sizeof(RGBQUAD); + + void *dst_bits; + auto dst_hbitmap = CreateHBitmapXRGB8888(bitmap->bmiHeader.biWidth, bitmap->bmiHeader.biHeight, + nullptr, &dst_bits); + + auto hdc = CreateCompatibleDC(nullptr); + auto old_hbitmap = static_cast(SelectObject(hdc, dst_hbitmap)); + ::SetDIBitsToDevice(hdc, 0, 0, bitmap->bmiHeader.biWidth, + bitmap->bmiHeader.biHeight, 0, 0, 0, + bitmap->bmiHeader.biHeight, bitmap_bits, bitmap, + DIB_RGB_COLORS); + SelectObject(hdc, old_hbitmap); + DeleteDC(hdc); + + TCHAR path[MAX_PATH]; + GetTempPath(MAX_PATH, path); + TCHAR name[MAX_PATH]; + GetTempFileName(path, L"pasteboard", false, name); + CreateBMPFile(name, dst_hbitmap); + + DeleteObject(dst_hbitmap); + + result->Success(flutter::EncodableValue(wide_to_utf8(name))); + } else if (method_call.method_name() == "files") { + if (!IsClipboardFormatAvailable(CF_HDROP)) { + result->Success(); + return; + } + if (!OpenClipboard(nullptr)) { + result->Error("0", "open clipboard failed"); + return; + } + auto handle = GetClipboardData(CF_HDROP); + flutter::EncodableList file_list; + if (handle) { + auto data = reinterpret_cast(GlobalLock(handle)); + if (data) { + auto files = DragQueryFile(data, 0xFFFFFFFF, nullptr, 0); + for (unsigned int i = 0; i < files; ++i) { + TCHAR filename[MAX_PATH]; + DragQueryFile(data, i, filename, sizeof(TCHAR) * MAX_PATH); + std::wstring wide_filename(filename); + file_list.emplace_back(wide_to_utf8(wide_filename)); + } + } + } + CloseClipboard(); + result->Success(flutter::EncodableValue(file_list)); + } else { + result->NotImplemented(); + } +} + +} // namespace + +void PasteboardPluginRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + PasteboardPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarManager::GetInstance() + ->GetRegistrar(registrar)); +} diff --git a/packages/pasteboard/windows/strconv.h b/packages/pasteboard/windows/strconv.h new file mode 100644 index 00000000..eddd0121 --- /dev/null +++ b/packages/pasteboard/windows/strconv.h @@ -0,0 +1,565 @@ +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2019-2021 JavaCommons +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. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +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 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. +----------------------------------------------------------------------------- + */ +/* strconv.h v1.8.10 */ +/* Last Modified: 2021/08/30 21:53 */ +#ifndef STRCONV_H +#define STRCONV_H + +#include +#include +#include +#include +#include + +#if __cplusplus >= 201103L && !defined(STRCONV_CPP98) +static inline std::wstring cp_to_wide(const std::string &s, UINT codepage) +{ + int in_length = (int)s.length(); + int out_length = MultiByteToWideChar(codepage, 0, s.c_str(), in_length, 0, 0); + std::wstring result(out_length, L'\0'); + if (out_length) + MultiByteToWideChar(codepage, 0, s.c_str(), in_length, &result[0], out_length); + return result; +} +static inline std::string wide_to_cp(const std::wstring &s, UINT codepage) +{ + int in_length = (int)s.length(); + int out_length = WideCharToMultiByte(codepage, 0, s.c_str(), in_length, 0, 0, 0, 0); + std::string result(out_length, '\0'); + if (out_length) + WideCharToMultiByte(codepage, 0, s.c_str(), in_length, &result[0], out_length, 0, 0); + return result; +} +#else /* __cplusplus < 201103L */ +static inline std::wstring cp_to_wide(const std::string &s, UINT codepage) +{ + int in_length = (int)s.length(); + int out_length = MultiByteToWideChar(codepage, 0, s.c_str(), in_length, 0, 0); + std::vector buffer(out_length); + if (out_length) + MultiByteToWideChar(codepage, 0, s.c_str(), in_length, &buffer[0], out_length); + std::wstring result(buffer.begin(), buffer.end()); + return result; +} +static inline std::string wide_to_cp(const std::wstring &s, UINT codepage) +{ + int in_length = (int)s.length(); + int out_length = WideCharToMultiByte(codepage, 0, s.c_str(), in_length, 0, 0, 0, 0); + std::vector buffer(out_length); + if (out_length) + WideCharToMultiByte(codepage, 0, s.c_str(), in_length, &buffer[0], out_length, 0, 0); + std::string result(buffer.begin(), buffer.end()); + return result; +} +#endif + +static inline std::string cp_to_utf8(const std::string &s, UINT codepage) +{ + if (codepage == CP_UTF8) + return s; + std::wstring wide = cp_to_wide(s, codepage); + return wide_to_cp(wide, CP_UTF8); +} +static inline std::string utf8_to_cp(const std::string &s, UINT codepage) +{ + if (codepage == CP_UTF8) + return s; + std::wstring wide = cp_to_wide(s, CP_UTF8); + return wide_to_cp(wide, codepage); +} + +static inline std::wstring ansi_to_wide(const std::string &s) +{ + return cp_to_wide(s, CP_ACP); +} +static inline std::string wide_to_ansi(const std::wstring &s) +{ + return wide_to_cp(s, CP_ACP); +} + +static inline std::wstring sjis_to_wide(const std::string &s) +{ + return cp_to_wide(s, 932); +} +static inline std::string wide_to_sjis(const std::wstring &s) +{ + return wide_to_cp(s, 932); +} + +static inline std::wstring utf8_to_wide(const std::string &s) +{ + return cp_to_wide(s, CP_UTF8); +} +static inline std::string wide_to_utf8(const std::wstring &s) +{ + return wide_to_cp(s, CP_UTF8); +} + +static inline std::string ansi_to_utf8(const std::string &s) +{ + return cp_to_utf8(s, CP_ACP); +} +static inline std::string utf8_to_ansi(const std::string &s) +{ + return utf8_to_cp(s, CP_ACP); +} + +static inline std::string sjis_to_utf8(const std::string &s) +{ + return cp_to_utf8(s, 932); +} +static inline std::string utf8_to_sjis(const std::string &s) +{ + return utf8_to_cp(s, 932); +} + +#ifdef __cpp_char8_t +static inline std::u8string utf8_to_char8(const std::string &s) +{ + return std::u8string(s.begin(), s.end()); +} +static inline std::string char8_to_utf8(const std::u8string &s) +{ + return std::string(s.begin(), s.end()); +} + +static inline std::wstring char8_to_wide(const std::u8string &s) +{ + return cp_to_wide(char8_to_utf8(s), CP_UTF8); +} +static inline std::u8string wide_to_char8(const std::wstring &s) +{ + return utf8_to_char8(wide_to_cp(s, CP_UTF8)); +} + +static inline std::u8string cp_to_char8(const std::string &s, UINT codepage) +{ + return utf8_to_char8(cp_to_utf8(s, codepage)); +} +static inline std::string char8_to_cp(const std::u8string &s, UINT codepage) +{ + return utf8_to_cp(char8_to_utf8(s), codepage); +} + +static inline std::u8string ansi_to_char8(const std::string &s) +{ + return cp_to_char8(s, CP_ACP); +} +static inline std::string char8_to_ansi(const std::u8string &s) +{ + return char8_to_cp(s, CP_ACP); +} + +static inline std::u8string sjis_to_char8(const std::string &s) +{ + return cp_to_char8(s, 932); +} +static inline std::string char8_to_sjis(const std::u8string &s) +{ + return char8_to_cp(s, 932); +} +#endif + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) +#endif + +static inline std::wstring vformat(const wchar_t *format, va_list args) +{ + int len = _vsnwprintf(0, 0, format, args); + if (len < 0) + return L""; + std::vector buffer(len + 1); + len = _vsnwprintf(&buffer[0], len, format, args); + if (len < 0) + return L""; + buffer[len] = L'\0'; + return &buffer[0]; +} +static inline std::string vformat(const char *format, va_list args) +{ + int len = _vsnprintf(0, 0, format, args); + if (len < 0) + return ""; + std::vector buffer(len + 1); + len = _vsnprintf(&buffer[0], len, format, args); + if (len < 0) + return ""; + buffer[len] = '\0'; + return &buffer[0]; +} +#ifdef __cpp_char8_t +static inline std::u8string vformat(const char8_t *format, va_list args) +{ + int len = _vsnprintf(0, 0, (const char *)format, args); + if (len < 0) + return u8""; + std::vector buffer(len + 1); + len = _vsnprintf(&buffer[0], len, (const char *)format, args); + if (len < 0) + return u8""; + buffer[len] = '\0'; + return (char8_t *)&buffer[0]; +} +#endif + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +static inline std::wstring format(const wchar_t *format, ...) +{ + va_list args; + va_start(args, format); + std::wstring s = vformat(format, args); + va_end(args); + return s; +} +static inline std::string format(const char *format, ...) +{ + va_list args; + va_start(args, format); + std::string s = vformat(format, args); + va_end(args); + return s; +} +#ifdef __cpp_char8_t +static inline std::u8string format(const char8_t *format, ...) +{ + va_list args; + va_start(args, format); + std::u8string s = vformat(format, args); + va_end(args); + return s; +} +#endif + +static inline void format(std::ostream &ostrm, const wchar_t *format, ...) +{ + va_list args; + va_start(args, format); + std::wstring s = vformat(format, args); + va_end(args); + ostrm << wide_to_utf8(s) << std::flush; +} +static inline void format(std::ostream &ostrm, const char *format, ...) +{ + va_list args; + va_start(args, format); + std::string s = vformat(format, args); + va_end(args); + ostrm << s << std::flush; +} +#ifdef __cpp_char8_t +static inline void format(std::ostream &ostrm, const char8_t *format, ...) +{ + va_list args; + va_start(args, format); + std::u8string s = vformat(format, args); + va_end(args); + ostrm << char8_to_utf8(s) << std::flush; +} +#endif + +static inline std::string formatA(const wchar_t *format, ...) +{ + va_list args; + va_start(args, format); + std::wstring s = vformat(format, args); + va_end(args); + return wide_to_ansi(s); +} +static inline std::string formatA(const char *format, ...) +{ + va_list args; + va_start(args, format); + std::string s = vformat(format, args); + va_end(args); + return utf8_to_ansi(s); +} +#ifdef __cpp_char8_t +static inline std::string formatA(const char8_t *format, ...) +{ + va_list args; + va_start(args, format); + std::u8string s = vformat(format, args); + va_end(args); + return char8_to_ansi(s); +} +#endif + +static inline void formatA(std::ostream &ostrm, const wchar_t *format, ...) +{ + va_list args; + va_start(args, format); + std::wstring s = vformat(format, args); + va_end(args); + ostrm << wide_to_ansi(s) << std::flush; +} +static inline void formatA(std::ostream &ostrm, const char *format, ...) +{ + va_list args; + va_start(args, format); + std::string s = vformat(format, args); + va_end(args); + ostrm << utf8_to_ansi(s) << std::flush; +} +#ifdef __cpp_char8_t +static inline void formatA(std::ostream &ostrm, const char8_t *format, ...) +{ + va_list args; + va_start(args, format); + std::u8string s = vformat(format, args); + va_end(args); + ostrm << char8_to_ansi(s) << std::flush; +} +#endif + +static inline void dbgmsg(const wchar_t *title, const wchar_t *format, ...) +{ + va_list args; + va_start(args, format); + std::wstring s = vformat(format, args); + va_end(args); + MessageBoxW(0, s.c_str(), title, MB_OK); +} +static inline void dbgmsg(const char *title, const char *format, ...) +{ + va_list args; + va_start(args, format); + std::string s = vformat(format, args); + va_end(args); + MessageBoxW(0, utf8_to_wide(s).c_str(), utf8_to_wide(title).c_str(), MB_OK); +} +#ifdef __cpp_char8_t +static inline void dbgmsg(const char8_t *title, const char8_t *format, ...) +{ + va_list args; + va_start(args, format); + std::u8string s = vformat(format, args); + va_end(args); + MessageBoxW(0, char8_to_wide(s).c_str(), char8_to_wide(title).c_str(), MB_OK); +} +#endif + +static inline HANDLE handle_for_ostream(std::ostream &ostrm) +{ + if (&ostrm == &std::cout) + { + return GetStdHandle(STD_OUTPUT_HANDLE); + } + else if (&ostrm == &std::cerr) + { + return GetStdHandle(STD_ERROR_HANDLE); + } + return INVALID_HANDLE_VALUE; +} +static inline void dbgout(std::ostream &ostrm, const wchar_t *format, ...) +{ + va_list args; + va_start(args, format); + std::wstring ws = vformat(format, args); + va_end(args); + HANDLE h = handle_for_ostream(ostrm); + if (h == INVALID_HANDLE_VALUE) + { + return; + } + DWORD dwNumberOfCharsWrite; + if (GetFileType(h) != FILE_TYPE_CHAR) + { + std::string s = wide_to_cp(ws, GetConsoleOutputCP()); + WriteFile(h, s.c_str(), (DWORD)s.size(), &dwNumberOfCharsWrite, NULL); + } + else + { + WriteConsoleW(h, + ws.c_str(), + (DWORD)ws.size(), + &dwNumberOfCharsWrite, + NULL); + } +} +static inline void dbgout(std::ostream &ostrm, const char *format, ...) +{ + va_list args; + va_start(args, format); + std::string s = vformat(format, args); + va_end(args); + HANDLE h = handle_for_ostream(ostrm); + if (h == INVALID_HANDLE_VALUE) + { + return; + } + DWORD dwNumberOfCharsWrite; + if (GetFileType(h) != FILE_TYPE_CHAR) + { + s = utf8_to_cp(s, GetConsoleOutputCP()); + WriteFile(h, s.c_str(), (DWORD)s.size(), &dwNumberOfCharsWrite, NULL); + } + else + { + std::wstring ws = utf8_to_wide(s); + WriteConsoleW(h, + ws.c_str(), + (DWORD)ws.size(), + &dwNumberOfCharsWrite, + NULL); + } +} +#ifdef __cpp_char8_t +static inline void dbgout(std::ostream &ostrm, const char8_t *format, ...) +{ + va_list args; + va_start(args, format); + std::u8string s = vformat(format, args); + va_end(args); + HANDLE h = handle_for_ostream(ostrm); + if (h == INVALID_HANDLE_VALUE) + { + return; + } + DWORD dwNumberOfCharsWrite; + if (GetFileType(h) != FILE_TYPE_CHAR) + { + std::string str = char8_to_cp(s, GetConsoleOutputCP()); + WriteFile(h, (const char *)str.c_str(), (DWORD)str.size(), &dwNumberOfCharsWrite, NULL); + } + else + { + std::wstring ws = char8_to_wide(s); + WriteConsoleW(h, + ws.c_str(), + (DWORD)ws.size(), + &dwNumberOfCharsWrite, + NULL); + } +} +#endif + +class unicode_ostream +{ + private: + std::ostream *m_ostrm; + UINT m_target_cp; + bool is_ascii(const std::string &s) + { + for (std::size_t i = 0; i < s.size(); i++) + { + unsigned char c = (unsigned char)s[i]; + if (c > 0x7f) + return false; + } + return true; + } + + public: + unicode_ostream(std::ostream &ostrm, UINT target_cp = CP_ACP) : m_ostrm(&ostrm), m_target_cp(target_cp) {} + std::ostream &stream() { return *m_ostrm; } + void stream(std::ostream &ostrm) { m_ostrm = &ostrm; } + UINT target_cp() { return m_target_cp; } + void target_cp(UINT cp) { m_target_cp = cp; } + template + unicode_ostream &operator<<(const T &x) + { + std::ostringstream oss; + oss << x; + std::string output = oss.str(); + if (is_ascii(output)) + { + (*m_ostrm) << x; + } + else + { + (*m_ostrm) << utf8_to_cp(output, m_target_cp); + } + return *this; + } + unicode_ostream &operator<<(const std::wstring &x) + { + (*m_ostrm) << wide_to_cp(x, m_target_cp); + return *this; + } + unicode_ostream &operator<<(const wchar_t *x) + { + (*m_ostrm) << wide_to_cp(x, m_target_cp); + return *this; + } + unicode_ostream &operator<<(const std::string &x) + { + (*m_ostrm) << utf8_to_cp(x, m_target_cp); + return *this; + } + unicode_ostream &operator<<(const char *x) + { + (*m_ostrm) << utf8_to_cp(x, m_target_cp); + return *this; + } +#ifdef __cpp_char8_t + unicode_ostream &operator<<(const std::u8string &x) + { + (*m_ostrm) << char8_to_cp(x, m_target_cp); + return *this; + } + unicode_ostream &operator<<(const char8_t *x) + { + (*m_ostrm) << char8_to_cp(x, m_target_cp); + return *this; + } +#endif + unicode_ostream &operator<<(std::ostream &(*pf)(std::ostream &)) // For manipulators... + { + (*m_ostrm) << pf; + return *this; + } + unicode_ostream &operator<<(std::basic_ios &(*pf)(std::basic_ios &)) // For manipulators... + { + (*m_ostrm) << pf; + return *this; + } +}; + +#define U8(X) ((const char *)u8##X) +#define WIDE(X) (L##X) + +#endif /* STRCONV_H */ \ No newline at end of file