From d434de92f3068fb09eb3e9c9aaea4ffca7ba3fdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hauke=20Kr=C3=BCger?= Date: Thu, 16 May 2024 09:45:36 +0200 Subject: [PATCH] Moved flutter code to github. There is now an AudYoFlo flutter pack (ayf_pack), a native pack (ayf_pack_native), an ffi host (ayf_ffihost) and a web host (ayf_webhost). You will find an example application project in the ayfstarter sub project --- flutter/ayf_ffihost/.gitignore | 32 + flutter/ayf_ffihost/.jvxprj.flutter | 0 flutter/ayf_ffihost/.metadata | 10 + flutter/ayf_ffihost/.pass.default | 0 flutter/ayf_ffihost/CHANGELOG.md | 3 + flutter/ayf_ffihost/CMakeLists.txt | 17 + flutter/ayf_ffihost/LICENSE | 1 + flutter/ayf_ffihost/README.md | 39 + flutter/ayf_ffihost/analysis_options.yaml | 4 + .../example/.ayf_ffihost_example.env.in | 3 + flutter/ayf_ffihost/example/.gitignore | 47 + .../ayfbinrender_icons/ayfbinrender-logo.png | Bin 0 -> 205019 bytes flutter/ayf_ffihost/example/lib/main.dart | 69 + .../example/lib/models/ayf_be_specific.dart | 5 + .../example/lib/models/ayf_ui_specific.dart | 178 ++ .../lib/models/ayf_ui_specific_widgets.dart | 112 ++ .../ayf_ffihost/example/test/widget_test.dart | 31 + .../ayf_ffihost/example/windows/.gitignore | 17 + .../example/windows/CMakeLists.txt | 101 + .../example/windows/flutter/CMakeLists.txt | 104 + .../example/windows/runner/CMakeLists.txt | 32 + .../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 + .../example/windows/runner/utils.h | 19 + .../example/windows/runner/win32_window.cpp | 245 +++ .../example/windows/runner/win32_window.h | 98 + flutter/ayf_ffihost/launch.json.in | 59 + flutter/ayf_ffihost/lib/ayf_ffihost.dart | 14 + .../lib/backend/ayf_backend_adapter.dart | 82 + .../backend/ayf_backend_adapter_native.dart | 1556 +++++++++++++++ .../lib/backend/ayf_backend_bridge.dart | 586 ++++++ .../ayf_backend_bridge_native_callbacks.dart | 187 ++ .../ayf_backend_bridge_native_helpers.dart | 72 + .../generated/ayf_ffi_gen_bind.dart | 1685 +++++++++++++++++ .../ayf_properties_from_native.dart | 472 +++++ .../systemstate/ayf_connected_processes.dart | 73 + .../lib/types/ayf_sequencer_types.dart | 28 + flutter/ayf_ffihost/pubspec.yaml.in | 83 + .../ayf_ffihost/test/ayf_ffihost_test.dart | 12 + flutter/ayf_pack/.gitignore | 32 + flutter/ayf_pack/.jvxprj.flutter | 0 flutter/ayf_pack/.metadata | 33 + flutter/ayf_pack/.pass.default | 0 flutter/ayf_pack/CHANGELOG.md | 3 + flutter/ayf_pack/CMakeLists.txt | 17 + flutter/ayf_pack/LICENSE | 1 + flutter/ayf_pack/README.md | 15 + flutter/ayf_pack/analysis_options.yaml | 10 + flutter/ayf_pack/doc/allocation-widgets.txt | 281 +++ flutter/ayf_pack/doc/cheat-sheet-backend.xlsx | Bin 0 -> 6368 bytes flutter/ayf_pack/doc/cheat-sheet.xlsx | Bin 0 -> 10850 bytes .../doc/wikipedia/backend_cache-compact.vsdx | Bin 0 -> 26251 bytes .../ayf_pack/doc/wikipedia/backend_cache.vsdx | Bin 0 -> 64192 bytes .../properties-flutter-multiplatform.vsdx | Bin 0 -> 19387 bytes .../ayf_pack/example/.ayf_pack_example.env.in | 3 + flutter/ayf_pack/example/.gitignore | 47 + flutter/ayf_pack/example/README.md | 16 + .../ayf_pack/example/analysis_options.yaml | 29 + .../ayfbinrender_icons/ayfbinrender-logo.png | Bin 0 -> 205019 bytes flutter/ayf_pack/example/lib/main.dart | 71 + .../example/lib/models/ayf_be_specific.dart | 5 + .../example/lib/models/ayf_ui_specific.dart | 190 ++ .../lib/models/ayf_ui_specific_widgets.dart | 274 +++ flutter/ayf_pack/example/pubspec.yaml | 112 ++ .../ayf_pack/example/test/widget_test.dart | 25 + flutter/ayf_pack/example/web/favicon.png | Bin 0 -> 917 bytes .../ayf_pack/example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../ayf_pack/example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../example/web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes .../example/web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes flutter/ayf_pack/example/web/index.html | 58 + flutter/ayf_pack/example/web/manifest.json | 35 + flutter/ayf_pack/example/windows/.gitignore | 17 + .../ayf_pack/example/windows/CMakeLists.txt | 101 + .../example/windows/flutter/CMakeLists.txt | 104 + .../example/windows/runner/CMakeLists.txt | 32 + .../ayf_pack/example/windows/runner/Runner.rc | 121 ++ .../example/windows/runner/flutter_window.cpp | 61 + .../example/windows/runner/flutter_window.h | 33 + .../ayf_pack/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 + .../ayf_pack/example/windows/runner/utils.cpp | 64 + .../ayf_pack/example/windows/runner/utils.h | 19 + .../example/windows/runner/win32_window.cpp | 245 +++ .../example/windows/runner/win32_window.h | 98 + flutter/ayf_pack/images/ayf-icons.vsdx | Bin 0 -> 100408 bytes .../images/ayf_icons/ayf-audio-settings.png | Bin 0 -> 1146 bytes .../ayf_pack/images/ayf_icons/ayf-audio.png | Bin 0 -> 1964 bytes .../ayf_pack/images/ayf_icons/ayf-brender.png | Bin 0 -> 20508 bytes .../ayf_pack/images/ayf_icons/ayf-comp.png | Bin 0 -> 345 bytes flutter/ayf_pack/images/ayf_icons/ayf-dbg.png | Bin 0 -> 1133 bytes .../images/ayf_icons/ayf-file-input.png | Bin 0 -> 8507 bytes .../ayf_pack/images/ayf_icons/ayf-hdrive.png | Bin 0 -> 4613 bytes .../ayf_icons/ayf-headtracker-settings.png | Bin 0 -> 24271 bytes .../images/ayf_icons/ayf-headtracker.png | Bin 0 -> 24271 bytes .../ayf_pack/images/ayf_icons/ayf-logo.svg | 1 + .../images/ayf_icons/ayf-logo.svg.orig | 102 + .../ayf_pack/images/ayf_icons/ayf-micsrc.png | Bin 0 -> 1309 bytes .../ayf_pack/images/ayf_icons/ayf-mixer.png | Bin 0 -> 1699 bytes .../ayf_pack/images/ayf_icons/ayf-plot.png | Bin 0 -> 35267 bytes .../ayf_pack/images/ayf_icons/ayf-remote.png | Bin 0 -> 3340 bytes flutter/ayf_pack/images/ayf_icons/ayf-sip.png | Bin 0 -> 2114 bytes .../ayf_pack/images/ayf_icons/ayf-soundi.png | Bin 0 -> 12664 bytes .../ayf_pack/images/ayf_icons/ayf-soundio.png | Bin 0 -> 18429 bytes .../ayf_pack/images/ayf_icons/ayf-soundo.png | Bin 0 -> 12784 bytes .../ayf_pack/images/ayf_icons/ayf-stream.png | Bin 0 -> 1115 bytes .../ayf_pack/images/ayf_icons/ayf-webrtc.png | Bin 0 -> 1724 bytes flutter/ayf_pack/images/ayf_splash/splash.png | Bin 0 -> 29802 bytes flutter/ayf_pack/launch.json.in | 59 + flutter/ayf_pack/lib/ayf_pack.dart | 10 + flutter/ayf_pack/lib/ayf_pack_common.dart | 104 + flutter/ayf_pack/lib/ayf_pack_local.dart | 1 + .../ayf_pack/lib/ayf_pack_method_channel.dart | 17 + .../lib/ayf_pack_platform_interface.dart | 29 + flutter/ayf_pack/lib/ayf_pack_web.dart | 33 + .../ayf_prop_list_combobox.dart | 205 ++ .../ayf_prop_textfield.dart | 142 ++ .../ayf_property_gridview.dart | 483 +++++ .../ayf_pack/lib/constants/ayf_constants.dart | 793 ++++++++ .../lib/constants/ayf_constants_audio.dart | 76 + .../constants/ayf_constants_properties.dart | 135 ++ .../lib/devices/ayf_devicelist_from_json.dart | 118 ++ .../ayf_pack/lib/dialogs/ayf_settings.dart | 150 ++ .../ayf_component_property_groups.dart | 122 ++ .../ayf_pack/lib/helpers/ayf_helper_lib.dart | 537 ++++++ ...f_multi_components_property_on_change.dart | 183 ++ .../ayf_one_component_property_on_change.dart | 140 ++ .../ayf_one_technology_devices_on_change.dart | 112 ++ .../helpers/ayf_translate_from_string.dart | 900 +++++++++ .../lib/helpers/web/ayf_helper_lib.dart | 88 + .../lib/interfaces/ayf_core_pack_if.dart | 3 + .../ayf_interface_backend_adapter_if.dart | 151 ++ .../interfaces/ayf_interface_bectrl_if.dart | 163 ++ .../interfaces/ayf_interface_fectrl_if.dart | 112 ++ .../lib/interfaces/ayf_interface_host.dart | 24 + .../interfaces/ayf_interface_properties.dart | 7 + .../lib/main-widgets/ayf_app_bar.dart | 227 +++ .../lib/main-widgets/ayf_app_widget.dart | 68 + .../ayf_audio_settings_widget.dart | 97 + .../lib/main-widgets/ayf_bottom_app_bar.dart | 6 + .../lib/main-widgets/ayf_bottom_content.dart | 17 + .../main-widgets/ayf_component_widget.dart | 205 ++ .../lib/main-widgets/ayf_debug_widget.dart | 122 ++ .../ayf_fileinput_control_widget.dart | 53 + .../main-widgets/ayf_main_inner_content.dart | 148 ++ .../lib/main-widgets/ayf_main_layout.dart | 126 ++ .../lib/main-widgets/ayf_main_widget.dart | 207 ++ .../lib/main-widgets/ayf_mixer_widget.dart | 34 + .../lib/main-widgets/ayf_play_pannel.dart | 128 ++ .../lib/main-widgets/ayf_upper_content.dart | 8 + .../lib/mixer-widgets/ayf_mixer_slider.dart | 557 ++++++ .../ayf_mixer_slider_with_size.dart | 108 ++ .../lib/models/ayf_backend_cache.dart | 1034 ++++++++++ .../lib/models/ayf_component_selection.dart | 39 + .../ayf_pack/lib/models/ayf_debug_model.dart | 100 + flutter/ayf_pack/lib/models/ayf_ui_model.dart | 201 ++ .../ayf_pack/lib/notifiers/ayf_notifiers.dart | 28 + .../lib/periodic/ayf_periodic_notifier.dart | 62 + .../lib/platform/ayf_platform_specific.dart | 20 + .../platform/web/ayf_platform_specific.dart | 72 + .../properties/ayf_properties_backend.dart | 338 ++++ .../properties/ayf_properties_from_json.dart | 728 +++++++ .../ayf_properties_from_string.dart | 196 ++ .../lib/properties/ayf_properties_local.dart | 132 ++ .../properties/ayf_properties_to_string.dart | 214 +++ .../lib/properties/web/ayf_properties.dart | 346 ++++ .../web/ayf_properties_from_json_simple.dart | 120 ++ .../lib/route-widgets/ayf_route_connect.dart | 385 ++++ flutter/ayf_pack/lib/routes/ayf_routes.dart | 29 + .../ayf_sequencer_status_from_json.dart | 29 + .../settings/ayf_settings_property_list.dart | 65 + .../ayf_settings_property_list_content.dart | 309 +++ .../simple-widgets/ayf_progress_property.dart | 68 + ...f_audio_device_active_inactive_sliver.dart | 173 ++ ...audio_device_channel_selection_widget.dart | 300 +++ .../lib/sub-widgets/ayf_active_textfield.dart | 127 ++ .../sub-widgets/ayf_component_combobox.dart | 170 ++ .../sub-widgets/ayf_custom_list_widget.dart | 91 + .../devices/ayf_device_selection_widget.dart | 93 + .../ayf_devices_active_inactive_widget.dart | 181 ++ .../ayf_file_input_active_devices.dart | 82 + .../ayf_file_input_active_devices_sliver.dart | 297 +++ ...ve_inactive_headtracker_device_sliver.dart | 117 ++ .../systemstate/ayf_connected_processes.dart | 148 ++ .../systemstate/ayf_selected_components.dart | 130 ++ flutter/ayf_pack/lib/types/ayf_bitfield.dart | 28 + .../lib/types/ayf_bitfield_common.dart | 169 ++ .../ayf_pack/lib/types/ayf_cache_types.dart | 76 + .../ayf_pack/lib/types/ayf_components.dart | 38 + .../ayf_pack/lib/types/ayf_model_types.dart | 116 ++ .../lib/types/ayf_sequencer_types.dart | 140 ++ flutter/ayf_pack/lib/types/ayf_types.dart | 61 + .../lib/types/web/ayf_bitfield_simple.dart | 107 ++ flutter/ayf_pack/pubspec.yaml | 113 ++ .../test/ayf_pack_method_channel_test.dart | 24 + flutter/ayf_pack/test/ayf_pack_test.dart | 28 + flutter/ayf_pack/windows/.gitignore | 17 + flutter/ayf_pack/windows/CMakeLists.txt | 53 + flutter/ayf_pack/windows/ayf_pack_plugin.cpp | 59 + flutter/ayf_pack/windows/ayf_pack_plugin.h | 32 + .../windows/ayf_pack_plugin_c_api.cpp | 12 + .../include/ayf_pack/ayf_pack_plugin_c_api.h | 23 + flutter/ayf_pack_native/.gitignore | 44 + flutter/ayf_pack_native/.jvxprj.flutter | 0 flutter/ayf_pack_native/README.md | 16 + .../ayf_pack_native/lib/ayf_pack_native.dart | 8 + .../lib/helpers/ayf_helper_lib.dart | 76 + .../lib/platform/ayf_platform_specific.dart | 248 +++ .../lib/properties/ayf_properties.dart | 425 +++++ .../ayf_properties_from_json_native.dart | 130 ++ .../ayf_file_input_core_widget.dart | 159 ++ .../lib/types/ayf_bitfield.dart | 120 ++ flutter/ayf_pack_native/pubspec.yaml | 97 + flutter/ayf_pack_native/test/widget_test.dart | 30 + flutter/ayf_pack_native/windows/.gitignore | 17 + .../ayf_pack_native/windows/CMakeLists.txt | 101 + .../windows/flutter/CMakeLists.txt | 104 + .../windows/runner/CMakeLists.txt | 39 + .../ayf_pack_native/windows/runner/Runner.rc | 121 ++ .../windows/runner/flutter_window.cpp | 61 + .../windows/runner/flutter_window.h | 33 + .../ayf_pack_native/windows/runner/main.cpp | 43 + .../ayf_pack_native/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../windows/runner/runner.exe.manifest | 20 + .../ayf_pack_native/windows/runner/utils.cpp | 64 + .../ayf_pack_native/windows/runner/utils.h | 19 + .../windows/runner/win32_window.cpp | 245 +++ .../windows/runner/win32_window.h | 98 + flutter/ayf_webhost/.gitignore | 44 + flutter/ayf_webhost/.jvxprj.flutter | 0 flutter/ayf_webhost/.pass.default | 0 flutter/ayf_webhost/CMakeLists.txt | 14 + flutter/ayf_webhost/LICENSE | 1 + flutter/ayf_webhost/README.md | 39 + .../doc/listErrorCodesWebHost.xlsx | Bin 0 -> 10934 bytes .../example/.ayf_webhost_example.env.in | 4 + flutter/ayf_webhost/example/.gitignore | 47 + flutter/ayf_webhost/example/README.md | 16 + .../ayfbinrender_icons/ayfbinrender-logo.png | Bin 0 -> 205019 bytes flutter/ayf_webhost/example/lib/main.dart | 70 + .../example/lib/models/ayf_be_specific.dart | 5 + .../example/lib/models/ayf_ui_specific.dart | 248 +++ .../lib/models/ayf_ui_specific_widgets.dart | 315 +++ flutter/ayf_webhost/example/pubspec.yaml | 102 + .../ayf_webhost/example/test/widget_test.dart | 33 + flutter/ayf_webhost/example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../example/web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes .../example/web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes flutter/ayf_webhost/example/web/index.html | 58 + flutter/ayf_webhost/example/web/manifest.json | 35 + .../ayf_webhost/example/windows/.gitignore | 17 + .../example/windows/CMakeLists.txt | 101 + .../example/windows/flutter/CMakeLists.txt | 104 + .../example/windows/runner/CMakeLists.txt | 32 + .../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 + .../example/windows/runner/utils.h | 19 + .../example/windows/runner/win32_window.cpp | 245 +++ .../example/windows/runner/win32_window.h | 98 + flutter/ayf_webhost/launch.json.in | 35 + .../lib/ayf_web_backend_constants.dart | 11 + .../lib/ayf_web_backend_helpers.dart | 439 +++++ .../ayf_webhost/lib/ayf_web_backend_host.dart | 6 + .../lib/ayf_web_backend_typedefs.dart | 3 + .../lib/backend/ayf_web_backend_adapter.dart | 1199 ++++++++++++ .../backend/ayf_web_backend_anomalies.dart | 24 + .../ayf_web_backend_message_processor.dart | 648 +++++++ flutter/ayf_webhost/pubspec.yaml.in | 59 + .../ayf_webhost/test/ayf_ffihost_test.dart | 14 + flutter/ayf_webhost/web/favicon.png | Bin 0 -> 917 bytes flutter/ayf_webhost/web/icons/Icon-192.png | Bin 0 -> 5292 bytes flutter/ayf_webhost/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes .../web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes flutter/ayf_webhost/web/index.html | 58 + flutter/ayf_webhost/web/manifest.json | 35 + 293 files changed, 30166 insertions(+) create mode 100644 flutter/ayf_ffihost/.gitignore create mode 100644 flutter/ayf_ffihost/.jvxprj.flutter create mode 100644 flutter/ayf_ffihost/.metadata create mode 100644 flutter/ayf_ffihost/.pass.default create mode 100644 flutter/ayf_ffihost/CHANGELOG.md create mode 100644 flutter/ayf_ffihost/CMakeLists.txt create mode 100644 flutter/ayf_ffihost/LICENSE create mode 100644 flutter/ayf_ffihost/README.md create mode 100644 flutter/ayf_ffihost/analysis_options.yaml create mode 100644 flutter/ayf_ffihost/example/.ayf_ffihost_example.env.in create mode 100644 flutter/ayf_ffihost/example/.gitignore create mode 100644 flutter/ayf_ffihost/example/images/ayfbinrender_icons/ayfbinrender-logo.png create mode 100644 flutter/ayf_ffihost/example/lib/main.dart create mode 100644 flutter/ayf_ffihost/example/lib/models/ayf_be_specific.dart create mode 100644 flutter/ayf_ffihost/example/lib/models/ayf_ui_specific.dart create mode 100644 flutter/ayf_ffihost/example/lib/models/ayf_ui_specific_widgets.dart create mode 100644 flutter/ayf_ffihost/example/test/widget_test.dart create mode 100644 flutter/ayf_ffihost/example/windows/.gitignore create mode 100644 flutter/ayf_ffihost/example/windows/CMakeLists.txt create mode 100644 flutter/ayf_ffihost/example/windows/flutter/CMakeLists.txt create mode 100644 flutter/ayf_ffihost/example/windows/runner/CMakeLists.txt create mode 100644 flutter/ayf_ffihost/example/windows/runner/Runner.rc create mode 100644 flutter/ayf_ffihost/example/windows/runner/flutter_window.cpp create mode 100644 flutter/ayf_ffihost/example/windows/runner/flutter_window.h create mode 100644 flutter/ayf_ffihost/example/windows/runner/main.cpp create mode 100644 flutter/ayf_ffihost/example/windows/runner/resource.h create mode 100644 flutter/ayf_ffihost/example/windows/runner/resources/app_icon.ico create mode 100644 flutter/ayf_ffihost/example/windows/runner/runner.exe.manifest create mode 100644 flutter/ayf_ffihost/example/windows/runner/utils.cpp create mode 100644 flutter/ayf_ffihost/example/windows/runner/utils.h create mode 100644 flutter/ayf_ffihost/example/windows/runner/win32_window.cpp create mode 100644 flutter/ayf_ffihost/example/windows/runner/win32_window.h create mode 100644 flutter/ayf_ffihost/launch.json.in create mode 100644 flutter/ayf_ffihost/lib/ayf_ffihost.dart create mode 100644 flutter/ayf_ffihost/lib/backend/ayf_backend_adapter.dart create mode 100644 flutter/ayf_ffihost/lib/backend/ayf_backend_adapter_native.dart create mode 100644 flutter/ayf_ffihost/lib/backend/ayf_backend_bridge.dart create mode 100644 flutter/ayf_ffihost/lib/backend/ayf_backend_bridge_native_callbacks.dart create mode 100644 flutter/ayf_ffihost/lib/backend/ayf_backend_bridge_native_helpers.dart create mode 100644 flutter/ayf_ffihost/lib/native-ffi/generated/ayf_ffi_gen_bind.dart create mode 100644 flutter/ayf_ffihost/lib/properties/ayf_properties_from_native.dart create mode 100644 flutter/ayf_ffihost/lib/systemstate/ayf_connected_processes.dart create mode 100644 flutter/ayf_ffihost/lib/types/ayf_sequencer_types.dart create mode 100644 flutter/ayf_ffihost/pubspec.yaml.in create mode 100644 flutter/ayf_ffihost/test/ayf_ffihost_test.dart create mode 100644 flutter/ayf_pack/.gitignore create mode 100644 flutter/ayf_pack/.jvxprj.flutter create mode 100644 flutter/ayf_pack/.metadata create mode 100644 flutter/ayf_pack/.pass.default create mode 100644 flutter/ayf_pack/CHANGELOG.md create mode 100644 flutter/ayf_pack/CMakeLists.txt create mode 100644 flutter/ayf_pack/LICENSE create mode 100644 flutter/ayf_pack/README.md create mode 100644 flutter/ayf_pack/analysis_options.yaml create mode 100644 flutter/ayf_pack/doc/allocation-widgets.txt create mode 100644 flutter/ayf_pack/doc/cheat-sheet-backend.xlsx create mode 100644 flutter/ayf_pack/doc/cheat-sheet.xlsx create mode 100644 flutter/ayf_pack/doc/wikipedia/backend_cache-compact.vsdx create mode 100644 flutter/ayf_pack/doc/wikipedia/backend_cache.vsdx create mode 100644 flutter/ayf_pack/doc/wikipedia/properties-flutter-multiplatform.vsdx create mode 100644 flutter/ayf_pack/example/.ayf_pack_example.env.in create mode 100644 flutter/ayf_pack/example/.gitignore create mode 100644 flutter/ayf_pack/example/README.md create mode 100644 flutter/ayf_pack/example/analysis_options.yaml create mode 100644 flutter/ayf_pack/example/images/ayfbinrender_icons/ayfbinrender-logo.png create mode 100644 flutter/ayf_pack/example/lib/main.dart create mode 100644 flutter/ayf_pack/example/lib/models/ayf_be_specific.dart create mode 100644 flutter/ayf_pack/example/lib/models/ayf_ui_specific.dart create mode 100644 flutter/ayf_pack/example/lib/models/ayf_ui_specific_widgets.dart create mode 100644 flutter/ayf_pack/example/pubspec.yaml create mode 100644 flutter/ayf_pack/example/test/widget_test.dart create mode 100644 flutter/ayf_pack/example/web/favicon.png create mode 100644 flutter/ayf_pack/example/web/icons/Icon-192.png create mode 100644 flutter/ayf_pack/example/web/icons/Icon-512.png create mode 100644 flutter/ayf_pack/example/web/icons/Icon-maskable-192.png create mode 100644 flutter/ayf_pack/example/web/icons/Icon-maskable-512.png create mode 100644 flutter/ayf_pack/example/web/index.html create mode 100644 flutter/ayf_pack/example/web/manifest.json create mode 100644 flutter/ayf_pack/example/windows/.gitignore create mode 100644 flutter/ayf_pack/example/windows/CMakeLists.txt create mode 100644 flutter/ayf_pack/example/windows/flutter/CMakeLists.txt create mode 100644 flutter/ayf_pack/example/windows/runner/CMakeLists.txt create mode 100644 flutter/ayf_pack/example/windows/runner/Runner.rc create mode 100644 flutter/ayf_pack/example/windows/runner/flutter_window.cpp create mode 100644 flutter/ayf_pack/example/windows/runner/flutter_window.h create mode 100644 flutter/ayf_pack/example/windows/runner/main.cpp create mode 100644 flutter/ayf_pack/example/windows/runner/resource.h create mode 100644 flutter/ayf_pack/example/windows/runner/resources/app_icon.ico create mode 100644 flutter/ayf_pack/example/windows/runner/runner.exe.manifest create mode 100644 flutter/ayf_pack/example/windows/runner/utils.cpp create mode 100644 flutter/ayf_pack/example/windows/runner/utils.h create mode 100644 flutter/ayf_pack/example/windows/runner/win32_window.cpp create mode 100644 flutter/ayf_pack/example/windows/runner/win32_window.h create mode 100644 flutter/ayf_pack/images/ayf-icons.vsdx create mode 100644 flutter/ayf_pack/images/ayf_icons/ayf-audio-settings.png create mode 100644 flutter/ayf_pack/images/ayf_icons/ayf-audio.png create mode 100644 flutter/ayf_pack/images/ayf_icons/ayf-brender.png create mode 100644 flutter/ayf_pack/images/ayf_icons/ayf-comp.png create mode 100644 flutter/ayf_pack/images/ayf_icons/ayf-dbg.png create mode 100644 flutter/ayf_pack/images/ayf_icons/ayf-file-input.png create mode 100644 flutter/ayf_pack/images/ayf_icons/ayf-hdrive.png create mode 100644 flutter/ayf_pack/images/ayf_icons/ayf-headtracker-settings.png create mode 100644 flutter/ayf_pack/images/ayf_icons/ayf-headtracker.png create mode 100644 flutter/ayf_pack/images/ayf_icons/ayf-logo.svg create mode 100644 flutter/ayf_pack/images/ayf_icons/ayf-logo.svg.orig create mode 100644 flutter/ayf_pack/images/ayf_icons/ayf-micsrc.png create mode 100644 flutter/ayf_pack/images/ayf_icons/ayf-mixer.png create mode 100644 flutter/ayf_pack/images/ayf_icons/ayf-plot.png create mode 100644 flutter/ayf_pack/images/ayf_icons/ayf-remote.png create mode 100644 flutter/ayf_pack/images/ayf_icons/ayf-sip.png create mode 100644 flutter/ayf_pack/images/ayf_icons/ayf-soundi.png create mode 100644 flutter/ayf_pack/images/ayf_icons/ayf-soundio.png create mode 100644 flutter/ayf_pack/images/ayf_icons/ayf-soundo.png create mode 100644 flutter/ayf_pack/images/ayf_icons/ayf-stream.png create mode 100644 flutter/ayf_pack/images/ayf_icons/ayf-webrtc.png create mode 100644 flutter/ayf_pack/images/ayf_splash/splash.png create mode 100644 flutter/ayf_pack/launch.json.in create mode 100644 flutter/ayf_pack/lib/ayf_pack.dart create mode 100644 flutter/ayf_pack/lib/ayf_pack_common.dart create mode 100644 flutter/ayf_pack/lib/ayf_pack_local.dart create mode 100644 flutter/ayf_pack/lib/ayf_pack_method_channel.dart create mode 100644 flutter/ayf_pack/lib/ayf_pack_platform_interface.dart create mode 100644 flutter/ayf_pack/lib/ayf_pack_web.dart create mode 100644 flutter/ayf_pack/lib/component-grid-widget/ayf_prop_list_combobox.dart create mode 100644 flutter/ayf_pack/lib/component-grid-widget/ayf_prop_textfield.dart create mode 100644 flutter/ayf_pack/lib/component-grid-widget/ayf_property_gridview.dart create mode 100644 flutter/ayf_pack/lib/constants/ayf_constants.dart create mode 100644 flutter/ayf_pack/lib/constants/ayf_constants_audio.dart create mode 100644 flutter/ayf_pack/lib/constants/ayf_constants_properties.dart create mode 100644 flutter/ayf_pack/lib/devices/ayf_devicelist_from_json.dart create mode 100644 flutter/ayf_pack/lib/dialogs/ayf_settings.dart create mode 100644 flutter/ayf_pack/lib/helpers/ayf_component_property_groups.dart create mode 100644 flutter/ayf_pack/lib/helpers/ayf_helper_lib.dart create mode 100644 flutter/ayf_pack/lib/helpers/ayf_multi_components_property_on_change.dart create mode 100644 flutter/ayf_pack/lib/helpers/ayf_one_component_property_on_change.dart create mode 100644 flutter/ayf_pack/lib/helpers/ayf_one_technology_devices_on_change.dart create mode 100644 flutter/ayf_pack/lib/helpers/ayf_translate_from_string.dart create mode 100644 flutter/ayf_pack/lib/helpers/web/ayf_helper_lib.dart create mode 100644 flutter/ayf_pack/lib/interfaces/ayf_core_pack_if.dart create mode 100644 flutter/ayf_pack/lib/interfaces/ayf_interface_backend_adapter_if.dart create mode 100644 flutter/ayf_pack/lib/interfaces/ayf_interface_bectrl_if.dart create mode 100644 flutter/ayf_pack/lib/interfaces/ayf_interface_fectrl_if.dart create mode 100644 flutter/ayf_pack/lib/interfaces/ayf_interface_host.dart create mode 100644 flutter/ayf_pack/lib/interfaces/ayf_interface_properties.dart create mode 100644 flutter/ayf_pack/lib/main-widgets/ayf_app_bar.dart create mode 100644 flutter/ayf_pack/lib/main-widgets/ayf_app_widget.dart create mode 100644 flutter/ayf_pack/lib/main-widgets/ayf_audio_settings_widget.dart create mode 100644 flutter/ayf_pack/lib/main-widgets/ayf_bottom_app_bar.dart create mode 100644 flutter/ayf_pack/lib/main-widgets/ayf_bottom_content.dart create mode 100644 flutter/ayf_pack/lib/main-widgets/ayf_component_widget.dart create mode 100644 flutter/ayf_pack/lib/main-widgets/ayf_debug_widget.dart create mode 100644 flutter/ayf_pack/lib/main-widgets/ayf_fileinput_control_widget.dart create mode 100644 flutter/ayf_pack/lib/main-widgets/ayf_main_inner_content.dart create mode 100644 flutter/ayf_pack/lib/main-widgets/ayf_main_layout.dart create mode 100644 flutter/ayf_pack/lib/main-widgets/ayf_main_widget.dart create mode 100644 flutter/ayf_pack/lib/main-widgets/ayf_mixer_widget.dart create mode 100644 flutter/ayf_pack/lib/main-widgets/ayf_play_pannel.dart create mode 100644 flutter/ayf_pack/lib/main-widgets/ayf_upper_content.dart create mode 100644 flutter/ayf_pack/lib/mixer-widgets/ayf_mixer_slider.dart create mode 100644 flutter/ayf_pack/lib/mixer-widgets/ayf_mixer_slider_with_size.dart create mode 100644 flutter/ayf_pack/lib/models/ayf_backend_cache.dart create mode 100644 flutter/ayf_pack/lib/models/ayf_component_selection.dart create mode 100644 flutter/ayf_pack/lib/models/ayf_debug_model.dart create mode 100644 flutter/ayf_pack/lib/models/ayf_ui_model.dart create mode 100644 flutter/ayf_pack/lib/notifiers/ayf_notifiers.dart create mode 100644 flutter/ayf_pack/lib/periodic/ayf_periodic_notifier.dart create mode 100644 flutter/ayf_pack/lib/platform/ayf_platform_specific.dart create mode 100644 flutter/ayf_pack/lib/platform/web/ayf_platform_specific.dart create mode 100644 flutter/ayf_pack/lib/properties/ayf_properties_backend.dart create mode 100644 flutter/ayf_pack/lib/properties/ayf_properties_from_json.dart create mode 100644 flutter/ayf_pack/lib/properties/ayf_properties_from_string.dart create mode 100644 flutter/ayf_pack/lib/properties/ayf_properties_local.dart create mode 100644 flutter/ayf_pack/lib/properties/ayf_properties_to_string.dart create mode 100644 flutter/ayf_pack/lib/properties/web/ayf_properties.dart create mode 100644 flutter/ayf_pack/lib/properties/web/ayf_properties_from_json_simple.dart create mode 100644 flutter/ayf_pack/lib/route-widgets/ayf_route_connect.dart create mode 100644 flutter/ayf_pack/lib/routes/ayf_routes.dart create mode 100644 flutter/ayf_pack/lib/sequencer/ayf_sequencer_status_from_json.dart create mode 100644 flutter/ayf_pack/lib/settings/ayf_settings_property_list.dart create mode 100644 flutter/ayf_pack/lib/settings/ayf_settings_property_list_content.dart create mode 100644 flutter/ayf_pack/lib/simple-widgets/ayf_progress_property.dart create mode 100644 flutter/ayf_pack/lib/sub-widgets/audio-devices/ayf_audio_device_active_inactive_sliver.dart create mode 100644 flutter/ayf_pack/lib/sub-widgets/audio-devices/ayf_audio_device_channel_selection_widget.dart create mode 100644 flutter/ayf_pack/lib/sub-widgets/ayf_active_textfield.dart create mode 100644 flutter/ayf_pack/lib/sub-widgets/ayf_component_combobox.dart create mode 100644 flutter/ayf_pack/lib/sub-widgets/ayf_custom_list_widget.dart create mode 100644 flutter/ayf_pack/lib/sub-widgets/devices/ayf_device_selection_widget.dart create mode 100644 flutter/ayf_pack/lib/sub-widgets/devices/ayf_devices_active_inactive_widget.dart create mode 100644 flutter/ayf_pack/lib/sub-widgets/file-input-devices/ayf_file_input_active_devices.dart create mode 100644 flutter/ayf_pack/lib/sub-widgets/file-input-devices/ayf_file_input_active_devices_sliver.dart create mode 100644 flutter/ayf_pack/lib/sub-widgets/headtracker-devices/ayf_active_inactive_headtracker_device_sliver.dart create mode 100644 flutter/ayf_pack/lib/systemstate/ayf_connected_processes.dart create mode 100644 flutter/ayf_pack/lib/systemstate/ayf_selected_components.dart create mode 100644 flutter/ayf_pack/lib/types/ayf_bitfield.dart create mode 100644 flutter/ayf_pack/lib/types/ayf_bitfield_common.dart create mode 100644 flutter/ayf_pack/lib/types/ayf_cache_types.dart create mode 100644 flutter/ayf_pack/lib/types/ayf_components.dart create mode 100644 flutter/ayf_pack/lib/types/ayf_model_types.dart create mode 100644 flutter/ayf_pack/lib/types/ayf_sequencer_types.dart create mode 100644 flutter/ayf_pack/lib/types/ayf_types.dart create mode 100644 flutter/ayf_pack/lib/types/web/ayf_bitfield_simple.dart create mode 100644 flutter/ayf_pack/pubspec.yaml create mode 100644 flutter/ayf_pack/test/ayf_pack_method_channel_test.dart create mode 100644 flutter/ayf_pack/test/ayf_pack_test.dart create mode 100644 flutter/ayf_pack/windows/.gitignore create mode 100644 flutter/ayf_pack/windows/CMakeLists.txt create mode 100644 flutter/ayf_pack/windows/ayf_pack_plugin.cpp create mode 100644 flutter/ayf_pack/windows/ayf_pack_plugin.h create mode 100644 flutter/ayf_pack/windows/ayf_pack_plugin_c_api.cpp create mode 100644 flutter/ayf_pack/windows/include/ayf_pack/ayf_pack_plugin_c_api.h create mode 100644 flutter/ayf_pack_native/.gitignore create mode 100644 flutter/ayf_pack_native/.jvxprj.flutter create mode 100644 flutter/ayf_pack_native/README.md create mode 100644 flutter/ayf_pack_native/lib/ayf_pack_native.dart create mode 100644 flutter/ayf_pack_native/lib/helpers/ayf_helper_lib.dart create mode 100644 flutter/ayf_pack_native/lib/platform/ayf_platform_specific.dart create mode 100644 flutter/ayf_pack_native/lib/properties/ayf_properties.dart create mode 100644 flutter/ayf_pack_native/lib/properties/ayf_properties_from_json_native.dart create mode 100644 flutter/ayf_pack_native/lib/sub-widgets/file-input-devices/ayf_file_input_core_widget.dart create mode 100644 flutter/ayf_pack_native/lib/types/ayf_bitfield.dart create mode 100644 flutter/ayf_pack_native/pubspec.yaml create mode 100644 flutter/ayf_pack_native/test/widget_test.dart create mode 100644 flutter/ayf_pack_native/windows/.gitignore create mode 100644 flutter/ayf_pack_native/windows/CMakeLists.txt create mode 100644 flutter/ayf_pack_native/windows/flutter/CMakeLists.txt create mode 100644 flutter/ayf_pack_native/windows/runner/CMakeLists.txt create mode 100644 flutter/ayf_pack_native/windows/runner/Runner.rc create mode 100644 flutter/ayf_pack_native/windows/runner/flutter_window.cpp create mode 100644 flutter/ayf_pack_native/windows/runner/flutter_window.h create mode 100644 flutter/ayf_pack_native/windows/runner/main.cpp create mode 100644 flutter/ayf_pack_native/windows/runner/resource.h create mode 100644 flutter/ayf_pack_native/windows/runner/resources/app_icon.ico create mode 100644 flutter/ayf_pack_native/windows/runner/runner.exe.manifest create mode 100644 flutter/ayf_pack_native/windows/runner/utils.cpp create mode 100644 flutter/ayf_pack_native/windows/runner/utils.h create mode 100644 flutter/ayf_pack_native/windows/runner/win32_window.cpp create mode 100644 flutter/ayf_pack_native/windows/runner/win32_window.h create mode 100644 flutter/ayf_webhost/.gitignore create mode 100644 flutter/ayf_webhost/.jvxprj.flutter create mode 100644 flutter/ayf_webhost/.pass.default create mode 100644 flutter/ayf_webhost/CMakeLists.txt create mode 100644 flutter/ayf_webhost/LICENSE create mode 100644 flutter/ayf_webhost/README.md create mode 100644 flutter/ayf_webhost/doc/listErrorCodesWebHost.xlsx create mode 100644 flutter/ayf_webhost/example/.ayf_webhost_example.env.in create mode 100644 flutter/ayf_webhost/example/.gitignore create mode 100644 flutter/ayf_webhost/example/README.md create mode 100644 flutter/ayf_webhost/example/images/ayfbinrender_icons/ayfbinrender-logo.png create mode 100644 flutter/ayf_webhost/example/lib/main.dart create mode 100644 flutter/ayf_webhost/example/lib/models/ayf_be_specific.dart create mode 100644 flutter/ayf_webhost/example/lib/models/ayf_ui_specific.dart create mode 100644 flutter/ayf_webhost/example/lib/models/ayf_ui_specific_widgets.dart create mode 100644 flutter/ayf_webhost/example/pubspec.yaml create mode 100644 flutter/ayf_webhost/example/test/widget_test.dart create mode 100644 flutter/ayf_webhost/example/web/favicon.png create mode 100644 flutter/ayf_webhost/example/web/icons/Icon-192.png create mode 100644 flutter/ayf_webhost/example/web/icons/Icon-512.png create mode 100644 flutter/ayf_webhost/example/web/icons/Icon-maskable-192.png create mode 100644 flutter/ayf_webhost/example/web/icons/Icon-maskable-512.png create mode 100644 flutter/ayf_webhost/example/web/index.html create mode 100644 flutter/ayf_webhost/example/web/manifest.json create mode 100644 flutter/ayf_webhost/example/windows/.gitignore create mode 100644 flutter/ayf_webhost/example/windows/CMakeLists.txt create mode 100644 flutter/ayf_webhost/example/windows/flutter/CMakeLists.txt create mode 100644 flutter/ayf_webhost/example/windows/runner/CMakeLists.txt create mode 100644 flutter/ayf_webhost/example/windows/runner/Runner.rc create mode 100644 flutter/ayf_webhost/example/windows/runner/flutter_window.cpp create mode 100644 flutter/ayf_webhost/example/windows/runner/flutter_window.h create mode 100644 flutter/ayf_webhost/example/windows/runner/main.cpp create mode 100644 flutter/ayf_webhost/example/windows/runner/resource.h create mode 100644 flutter/ayf_webhost/example/windows/runner/resources/app_icon.ico create mode 100644 flutter/ayf_webhost/example/windows/runner/runner.exe.manifest create mode 100644 flutter/ayf_webhost/example/windows/runner/utils.cpp create mode 100644 flutter/ayf_webhost/example/windows/runner/utils.h create mode 100644 flutter/ayf_webhost/example/windows/runner/win32_window.cpp create mode 100644 flutter/ayf_webhost/example/windows/runner/win32_window.h create mode 100644 flutter/ayf_webhost/launch.json.in create mode 100644 flutter/ayf_webhost/lib/ayf_web_backend_constants.dart create mode 100644 flutter/ayf_webhost/lib/ayf_web_backend_helpers.dart create mode 100644 flutter/ayf_webhost/lib/ayf_web_backend_host.dart create mode 100644 flutter/ayf_webhost/lib/ayf_web_backend_typedefs.dart create mode 100644 flutter/ayf_webhost/lib/backend/ayf_web_backend_adapter.dart create mode 100644 flutter/ayf_webhost/lib/backend/ayf_web_backend_anomalies.dart create mode 100644 flutter/ayf_webhost/lib/backend/ayf_web_backend_message_processor.dart create mode 100644 flutter/ayf_webhost/pubspec.yaml.in create mode 100644 flutter/ayf_webhost/test/ayf_ffihost_test.dart create mode 100644 flutter/ayf_webhost/web/favicon.png create mode 100644 flutter/ayf_webhost/web/icons/Icon-192.png create mode 100644 flutter/ayf_webhost/web/icons/Icon-512.png create mode 100644 flutter/ayf_webhost/web/icons/Icon-maskable-192.png create mode 100644 flutter/ayf_webhost/web/icons/Icon-maskable-512.png create mode 100644 flutter/ayf_webhost/web/index.html create mode 100644 flutter/ayf_webhost/web/manifest.json diff --git a/flutter/ayf_ffihost/.gitignore b/flutter/ayf_ffihost/.gitignore new file mode 100644 index 00000000..996d926f --- /dev/null +++ b/flutter/ayf_ffihost/.gitignore @@ -0,0 +1,32 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# 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/ + +pubspec.yaml diff --git a/flutter/ayf_ffihost/.jvxprj.flutter b/flutter/ayf_ffihost/.jvxprj.flutter new file mode 100644 index 00000000..e69de29b diff --git a/flutter/ayf_ffihost/.metadata b/flutter/ayf_ffihost/.metadata new file mode 100644 index 00000000..756df280 --- /dev/null +++ b/flutter/ayf_ffihost/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + channel: stable + +project_type: package diff --git a/flutter/ayf_ffihost/.pass.default b/flutter/ayf_ffihost/.pass.default new file mode 100644 index 00000000..e69de29b diff --git a/flutter/ayf_ffihost/CHANGELOG.md b/flutter/ayf_ffihost/CHANGELOG.md new file mode 100644 index 00000000..41cc7d81 --- /dev/null +++ b/flutter/ayf_ffihost/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/flutter/ayf_ffihost/CMakeLists.txt b/flutter/ayf_ffihost/CMakeLists.txt new file mode 100644 index 00000000..5e70fb4b --- /dev/null +++ b/flutter/ayf_ffihost/CMakeLists.txt @@ -0,0 +1,17 @@ +if(JVX_RELEASE_SDK) + + # Produce the right entries in the ffigen code lines + set(FFIGEN_HOST_HEADER_PATH "${CMAKE_INSTALL_PREFIX}/${INSTALL_PATH_INCLUDE_STATIC}/jvx-flutter-native-static/include/flutter_native_api.h") + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pubspec.yaml.in ${CMAKE_CURRENT_SOURCE_DIR}/pubspec.yaml @ONLY) + + # Configure for all tests + set(AYFBINRENDER_HOSTCONF_DLL "${CMAKE_INSTALL_PREFIX}/${INSTALL_PATH_BIN}/ayfbinrender-native-config_import.dll") + set(AYFBINRENDER_FFI_DLL "${CMAKE_INSTALL_PREFIX}/${INSTALL_PATH_BIN}/jvx-flutter-native-shared_import.dll") + set(AYFBINRENDER_FFI_PRELOAD_DLLS "${CMAKE_INSTALL_PREFIX}/${INSTALL_PATH_BIN}/zlib.dll,${CMAKE_INSTALL_PREFIX}/${INSTALL_PATH_BIN}/libfftw3-3.dll") + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/example/.ayf_ffihost_example.env.in ${CMAKE_CURRENT_SOURCE_DIR}/example/.ayf_ffihost_example.env @ONLY) + + set(AYF_FLUTTER_WORKDIR ${CMAKE_INSTALL_PREFIX}/${INSTALL_PATH_RUNTIME}) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/launch.json.in ${CMAKE_CURRENT_SOURCE_DIR}/.vscode/launch.json @ONLY) +else() + message("Entering folder for runtime build - nothing to be done here!") +endif() \ No newline at end of file diff --git a/flutter/ayf_ffihost/LICENSE b/flutter/ayf_ffihost/LICENSE new file mode 100644 index 00000000..ba75c69f --- /dev/null +++ b/flutter/ayf_ffihost/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/flutter/ayf_ffihost/README.md b/flutter/ayf_ffihost/README.md new file mode 100644 index 00000000..8b55e735 --- /dev/null +++ b/flutter/ayf_ffihost/README.md @@ -0,0 +1,39 @@ + + +TODO: Put a short description of the package here that helps potential users +know whether this package might be useful for them. + +## Features + +TODO: List what your package can do. Maybe include images, gifs, or videos. + +## Getting started + +TODO: List prerequisites and provide or point to information on how to +start using the package. + +## Usage + +TODO: Include short and useful examples for package users. Add longer examples +to `/example` folder. + +```dart +const like = 'sample'; +``` + +## Additional information + +TODO: Tell users more about the package: where to find more information, how to +contribute to the package, how to file issues, what response they can expect +from the package authors, and more. diff --git a/flutter/ayf_ffihost/analysis_options.yaml b/flutter/ayf_ffihost/analysis_options.yaml new file mode 100644 index 00000000..a5744c1c --- /dev/null +++ b/flutter/ayf_ffihost/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/flutter/ayf_ffihost/example/.ayf_ffihost_example.env.in b/flutter/ayf_ffihost/example/.ayf_ffihost_example.env.in new file mode 100644 index 00000000..4824a438 --- /dev/null +++ b/flutter/ayf_ffihost/example/.ayf_ffihost_example.env.in @@ -0,0 +1,3 @@ +AYF_BACKEND_FFI_SHARED_LIBRARY = '@AYFBINRENDER_FFI_DLL@' +AYF_BACKEND_PRELOAD_LIBRARIES = '@AYFBINRENDER_FFI_PRELOAD_DLLS@'; +AYF_BACKEND_EXECUTABLE_IN_BINFOLDER = "yes"; diff --git a/flutter/ayf_ffihost/example/.gitignore b/flutter/ayf_ffihost/example/.gitignore new file mode 100644 index 00000000..a8e938c0 --- /dev/null +++ b/flutter/ayf_ffihost/example/.gitignore @@ -0,0 +1,47 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/flutter/ayf_ffihost/example/images/ayfbinrender_icons/ayfbinrender-logo.png b/flutter/ayf_ffihost/example/images/ayfbinrender_icons/ayfbinrender-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..21c7a29e05dafc2fcdfad00fb02e2529fc5bb48e GIT binary patch literal 205019 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

G0^F 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 arguments) { + /* + if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) { + setWindowTitle('App title'); + setWindowMinSize(const Size(700, 500)); + setWindowMaxSize(Size.infinite); + } + */ + + // What is this good for in native windows?? It seems that native splash image + // does not work on platform windows! + // + // https://www.youtube.com/watch?v=dB0dOnc2k10 + // WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); + // FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); + // + + // Initialize the platform specific part here + AudYoFloPlatformSpecific platformSpec = allocatePlatformSpecific(); + Map configArgs = {}; + configArgs['corePack'] = ayfcore; + configArgs['host'] = ayfhost; + configArgs['cmdArgs'] = arguments; + platformSpec.configureSubSystem(configArgs); + + // *************************************************** + runApp(MultiProvider( + providers: [ + ChangeNotifierProvider( + create: (_) => AudYoFloPeriodicNotifier()), + ChangeNotifierProvider( + create: (_) => AudYoFloBackendCacheSpecific()), + ChangeNotifierProvider( + create: (_) => AudYoFloUiModelSpecificWithWidget()), + ChangeNotifierProvider( + create: (_) => AudYoFloDebugModel()), + /*ChangeNotifierProvider( + create: (_) => FernliveCacheModel()),*/ + ], + + // Here, we enter the main widget. We pass the backend adapter (here: ffi bridge) + // and the core pack which holds the Ayf components precompiled plugin + child: AudYoFloMainWidget( + platformSpecific: platformSpec, + ))); +} diff --git a/flutter/ayf_ffihost/example/lib/models/ayf_be_specific.dart b/flutter/ayf_ffihost/example/lib/models/ayf_be_specific.dart new file mode 100644 index 00000000..8ce6c354 --- /dev/null +++ b/flutter/ayf_ffihost/example/lib/models/ayf_be_specific.dart @@ -0,0 +1,5 @@ +import 'package:ayf_pack/ayf_pack.dart'; + +class AudYoFloBackendCacheSpecific extends AudYoFloBackendCache { + AudYoFloBackendCacheSpecific() : super() {} +} diff --git a/flutter/ayf_ffihost/example/lib/models/ayf_ui_specific.dart b/flutter/ayf_ffihost/example/lib/models/ayf_ui_specific.dart new file mode 100644 index 00000000..78bf1b2e --- /dev/null +++ b/flutter/ayf_ffihost/example/lib/models/ayf_ui_specific.dart @@ -0,0 +1,178 @@ +import 'package:flutter/material.dart'; +import 'package:ayf_pack/ayf_pack.dart'; +// import '../ayfpacks/ayf_pack.dart'; + +import 'package:ayf_pack/main-widgets/ayf_audio_settings_widget.dart'; + +class AudYoFloUiModelSpecific extends AudYoFloUiModel + with AudYoFloUiElementsTabBar { + // ========================================================================== + // We define some rendering parameters for the audio io widget. + // We use package specific icons here. + // ========================================================================== + AudYoFloAudioDeviceRenderSection soundIo = AudYoFloAudioDeviceRenderSection( + imageNameSoundIo: 'packages/ayf_pack/images/ayf_icons/ayf-soundio.png', + imageNameSoundI: 'packages/ayf_pack/images/ayf_icons/ayf-soundi.png', + imageNameSoundO: 'packages/ayf_pack/images/ayf_icons/ayf-soundo.png'); + + // Add a local property of type "string" + AudYoFloPropertyStringLocal propTestme = AudYoFloPropertyStringLocal( + 'Hallo Test', "/settings/testme", "Description"); + + AudYoFloUiModelSpecific() { + textApp = 'ayf_ffihost_example'; + imageNameApp = 'images/ayfbinrender_icons/ayfbinrender-logo.png'; + + // Define some specific configuration entities + widgetSpecificConfigurations['soundIo'] = soundIo; + + showAudioDevTech = false; + + // ===================================================================== + // Define the settings dialog entries + // ===================================================================== + } + + @override + void configureSystemParameters(AudYoFloCompileFlags flags) { + super.configureSystemParameters(flags); + AudYoFloSettingsConfigurationSection oneSection = + AudYoFloSettingsConfigurationSection(); + + oneSection = AudYoFloSettingsConfigurationSection(); + oneSection.nameEntry = 'Application'; + + AudYoFloOneComponentSettingsBackend addThisBeHost = + AudYoFloOneComponentSettingsBackend( + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_HOST, + slotid: 0, + slotsubid: 0), + 'Host Configuration', + [ + '/jvxLibHost/autostart' + + ///ext_interfaces/application_property_autostart' + ], + propTags: [ + 'Activate Autostart' + ], + propTooltips: [ + 'Activate Autostart feature to take effect when starting app the next time. When activated, audio processing will start immediately.' + ]); + + oneSection.addEntry(addThisBeHost); + + AudYoFloOneComponentSettingsBackend addThisBeAsio = + AudYoFloOneComponentSettingsBackend( + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_DEVICE, + slotid: 0, + slotsubid: 0), + 'ASIO Audio Device', + [ + '/JVX_GENW/system/sel_framesize', + '/JVX_GENW/system/sel_rate', + '/system/rate', + '/system/sel_framesize' + ], + propTags: [ + 'Select ASIO Buffersize', + 'Select ASIO Samplerate', + 'Samplerate', + 'Buffersize' + ], + propTooltips: [ + 'Tooltip: Select ASIO Buffersize', + 'Tooltip: Select ASIO Samplerate', + 'Tooltip: Samplerate', + 'Tooltip: Buffersize' + ]); + + oneSection.addEntry(addThisBeAsio); + + settingsConfigure.allSections[oneSection.nameEntry] = oneSection; + + // Here we may add an additional combobox in the component settings + // showAudioDevTech = true; + + // Create another section + // This is here only for demonstration and is not active at the moment! + if (false) { + oneSection = AudYoFloSettingsConfigurationSection(); + oneSection.nameEntry = 'Sip'; + + AudYoFloOneComponentSettingsBackend addThisBeSip = + AudYoFloOneComponentSettingsBackend( + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_DEVICE, + slotid: 3, + slotsubid: 0), + 'Sip Device', + [ + '/xyz', + '/xyz', + '/xyz' + ], + propTags: [ + '/xyz', + '/xyz', + '/xyz' + ], + propTooltips: [ + 'Tooltip: /xyz', + 'Tooltip: /xyz', + 'Tooltip: /xyz' + ]); + oneSection.addEntry(addThisBeSip); + + settingsConfigure.allSections[oneSection.nameEntry] = oneSection; + } + } + + @override + int reportSetProperty(AudYoFloPropertyContentLocal prop) { + bool notifyThis = false; + if (prop.descriptor == "/settings/testme") { + return jvxErrorType.JVX_NO_ERROR; + } + + if (notifyThis) { + notifyListeners(); + } + + return super.reportSetProperty(prop); + } + + @override + void filterListProperties( + String token, AudYoFloMultiComponentsPropertyOnChangeEntry entries) { + if (entries.settings.cpId == + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_DEVICE, + slotid: 0, + slotsubid: 0)) { + if (entries is AudYoFloMultiComponentsPropertyOnChangeEntryBackend) { + AudYoFloMultiComponentsPropertyOnChangeEntryBackend entriesBe = entries; + List? lst = entriesBe.bprops; + if (lst != null) { + if (token == 'Settings') { + lst = filterListProperties_settings(lst); + } + entriesBe.bprops = lst; + } + } + // var modLst = entries.; + } + super.filterListProperties('Settings', entries); + } + + List filterListProperties_settings( + List lst) { + lst = removeIfPresent( + lst, '/JVX_GENW/system/sel_framesize', ['/system/framesize']); + lst = removeIfPresent(lst, '/JVX_GENW/system/sel_rate', ['/system/rate']); + + return lst; + } +} diff --git a/flutter/ayf_ffihost/example/lib/models/ayf_ui_specific_widgets.dart b/flutter/ayf_ffihost/example/lib/models/ayf_ui_specific_widgets.dart new file mode 100644 index 00000000..eba205d8 --- /dev/null +++ b/flutter/ayf_ffihost/example/lib/models/ayf_ui_specific_widgets.dart @@ -0,0 +1,112 @@ +import 'package:flutter/material.dart'; +import 'package:ayf_pack/ayf_pack.dart'; +import 'ayf_ui_specific.dart'; + +import 'package:ayf_pack/main-widgets/ayf_audio_settings_widget.dart'; + +import 'package:flutter/foundation.dart' show kIsWeb; + +// ============================================================================ +// Callback to allocate an audio device sliver in the list of active/inactive +// devices lists +Widget? allocateAudioDeviceSliver(AudYoFloOneSelectionOptionWithId option, + bool fromActiveList, AudYoFloSingleSelectionComponent? reportW) => + AudYoFloOneDeviceSliver(option, fromActiveList, reportW); + +// ============================================================================ +// Callback to allocate a headtracker device sliver in the list of active/ +// inactibe devices list +Widget? allocateHeadtrackerDeviceSliver(AudYoFloOneSelectionOptionWithId option, + bool fromActiveList, AudYoFloSingleSelectionComponent? reportW) => + AudYoFloOneHeadtrackerDeviceSliver(option, fromActiveList, reportW); + +BottomAppBar? allocatorBottomAppBar() { + if (kIsWeb) { + return AudYoFloPlayConnectBottomAppBar(); + } else { + return AudYoFloBottomAppBar(); + } +} + +class AudYoFloPlayConnectBottomAppBar extends BottomAppBar { + AudYoFloPlayConnectBottomAppBar() + : super( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [AudYoFloPlaypannel(), AudYoFloPlaypannel()], + )); +} + +// ============================================================================ +// ============================================================================ +// This class extends the common UiModel part and adds application specific +// configuration entries. +// ============================================================================ +class AudYoFloUiModelSpecificWithWidget extends AudYoFloUiModelSpecific { + // ========================================================================== + // Define the core widget composition here: The function configures all application + // specific widgets here. Then, if all specific widgets were added, the base class + // call may be used to attach the application unspecific widgets - which are typically + // only the debug output and the component property viewer widget. + // ========================================================================== + + AudYoFloUiModelSpecificWithWidget() : super() { + allocateBottomAppBar = allocatorBottomAppBar; + } + + @override + void createTabsAndViews(Map entriesCfgWidgets) { + // Tabs are added to a list along multiple layers of the class hierarchie + // Since we dont know if we are the first here, we can not clear the list + // when entering the call. It is the task of the caller to run clear before + + // ====================================================================== + // Add the primary audio device widget + // ====================================================================== + String imageNameAudioIoPri = + 'packages/ayf_pack/images/ayf_icons/ayf-audio-settings.png'; + String textAudioIoPri = 'ASIO I/O'; + JvxComponentIdentification cpTpPriTech = widgetConfigCpId( + entriesCfgWidgets, + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_TECHNOLOGY, + slotid: 0, + slotsubid: 0), + 'priaudiot'); + + JvxComponentIdentification cpTpPriDev = widgetConfigCpId( + entriesCfgWidgets, + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_DEVICE, + slotid: cpTpPriTech.slotid, + slotsubid: 0), + 'priaudiod'); + + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Tooltip( + message: '${cpTpPriTech.txt}--${cpTpPriDev.txt}', + child: Column(children: [ + Image.asset(imageNameAudioIoPri, + filterQuality: FilterQuality.medium, + //'images/fernlive_icons/fernlive-mixer.png', + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(textAudioIoPri) + ])))); + theAllocatedTabViews.add(AudYoFloAudioSettingsWidget( + cpTpPriTech, + 'Audio Technologies', + cpTpPriDev, + 'Audio Devices', + 'soundIo', + (JvxComponentIdentification selCpId) => + AudYoFloAudioDeviceInputOutputChannelWidget(selCpId), + allocateAudioDeviceSliver)); + + // Here, the base class function is called. This call allows the base class + // to attach the "standard widgets" debug and components. If you do not call these functions + // the debug widgets will not be viisible. + super.createTabsAndViews(entriesCfgWidgets); + } +} diff --git a/flutter/ayf_ffihost/example/test/widget_test.dart b/flutter/ayf_ffihost/example/test/widget_test.dart new file mode 100644 index 00000000..71d6470b --- /dev/null +++ b/flutter/ayf_ffihost/example/test/widget_test.dart @@ -0,0 +1,31 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + print('No Test implemented at this point!'); + /* + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); + */ +} diff --git a/flutter/ayf_ffihost/example/windows/.gitignore b/flutter/ayf_ffihost/example/windows/.gitignore new file mode 100644 index 00000000..d492d0d9 --- /dev/null +++ b/flutter/ayf_ffihost/example/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/flutter/ayf_ffihost/example/windows/CMakeLists.txt b/flutter/ayf_ffihost/example/windows/CMakeLists.txt new file mode 100644 index 00000000..c2763964 --- /dev/null +++ b/flutter/ayf_ffihost/example/windows/CMakeLists.txt @@ -0,0 +1,101 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(ayf_ffihost_example LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "ayf_ffihost_example") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Define build configuration option. +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() +# Define settings for the Profile build mode. +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. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +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() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +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/flutter/ayf_ffihost/example/windows/flutter/CMakeLists.txt b/flutter/ayf_ffihost/example/windows/flutter/CMakeLists.txt new file mode 100644 index 00000000..930d2071 --- /dev/null +++ b/flutter/ayf_ffihost/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,104 @@ +# This file controls Flutter-level build steps. It should not be edited. +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/flutter/ayf_ffihost/example/windows/runner/CMakeLists.txt b/flutter/ayf_ffihost/example/windows/runner/CMakeLists.txt new file mode 100644 index 00000000..b9e550fb --- /dev/null +++ b/flutter/ayf_ffihost/example/windows/runner/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +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 the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/flutter/ayf_ffihost/example/windows/runner/Runner.rc b/flutter/ayf_ffihost/example/windows/runner/Runner.rc new file mode 100644 index 00000000..aecaa2b5 --- /dev/null +++ b/flutter/ayf_ffihost/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 +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#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", "example" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2023 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "example.exe" "\0" + VALUE "ProductName", "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/flutter/ayf_ffihost/example/windows/runner/flutter_window.cpp b/flutter/ayf_ffihost/example/windows/runner/flutter_window.cpp new file mode 100644 index 00000000..b43b9095 --- /dev/null +++ b/flutter/ayf_ffihost/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/flutter/ayf_ffihost/example/windows/runner/flutter_window.h b/flutter/ayf_ffihost/example/windows/runner/flutter_window.h new file mode 100644 index 00000000..6da0652f --- /dev/null +++ b/flutter/ayf_ffihost/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/flutter/ayf_ffihost/example/windows/runner/main.cpp b/flutter/ayf_ffihost/example/windows/runner/main.cpp new file mode 100644 index 00000000..ea92aa2c --- /dev/null +++ b/flutter/ayf_ffihost/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"ayf_pack_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/flutter/ayf_ffihost/example/windows/runner/resource.h b/flutter/ayf_ffihost/example/windows/runner/resource.h new file mode 100644 index 00000000..66a65d1e --- /dev/null +++ b/flutter/ayf_ffihost/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/flutter/ayf_ffihost/example/windows/runner/resources/app_icon.ico b/flutter/ayf_ffihost/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/flutter/ayf_ffihost/example/windows/runner/runner.exe.manifest b/flutter/ayf_ffihost/example/windows/runner/runner.exe.manifest new file mode 100644 index 00000000..a42ea768 --- /dev/null +++ b/flutter/ayf_ffihost/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/flutter/ayf_ffihost/example/windows/runner/utils.cpp b/flutter/ayf_ffihost/example/windows/runner/utils.cpp new file mode 100644 index 00000000..f5bf9fa0 --- /dev/null +++ b/flutter/ayf_ffihost/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); + std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return 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/flutter/ayf_ffihost/example/windows/runner/utils.h b/flutter/ayf_ffihost/example/windows/runner/utils.h new file mode 100644 index 00000000..3879d547 --- /dev/null +++ b/flutter/ayf_ffihost/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/flutter/ayf_ffihost/example/windows/runner/win32_window.cpp b/flutter/ayf_ffihost/example/windows/runner/win32_window.cpp new file mode 100644 index 00000000..c10f08dc --- /dev/null +++ b/flutter/ayf_ffihost/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/flutter/ayf_ffihost/example/windows/runner/win32_window.h b/flutter/ayf_ffihost/example/windows/runner/win32_window.h new file mode 100644 index 00000000..17ba4311 --- /dev/null +++ b/flutter/ayf_ffihost/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/flutter/ayf_ffihost/launch.json.in b/flutter/ayf_ffihost/launch.json.in new file mode 100644 index 00000000..a44a56d1 --- /dev/null +++ b/flutter/ayf_ffihost/launch.json.in @@ -0,0 +1,59 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "ayf_ffihost_example (debug)", + "request": "launch", + "type": "dart", + "program": "example/lib/main.dart", + "deviceId": "windows", + + // https://stackoverflow.com/questions/55004302/how-do-you-pass-arguments-from-command-line-to-main-in-flutter-dart + // https://dartcode.org/docs/launch-configuration/ + "args": [ + // Make sure that the sofa folder is in the runtime or the sdk folder - it often is the SDK output folder!! + "--dart-entrypoint-args", "--workdir", + "--dart-entrypoint-args", "@AYF_FLUTTER_WORKDIR@", + "--dart-entrypoint-args", "--preload", + "--dart-entrypoint-args", "@AYF_FLUTTER_WORKDIR@/bin/zlib.dll", + "--dart-entrypoint-args", "--preload", + "--dart-entrypoint-args", "@AYF_FLUTTER_WORKDIR@/bin/libfftw3-3.dll", + "--dart-entrypoint-args", "--jvxdir", + "--dart-entrypoint-args", "jvxComponents", + "--dart-entrypoint-args", "--config", + "--dart-entrypoint-args", "ayf_ffihost_example.jvx", + "--dart-entrypoint-args", "--textlog", + "--dart-entrypoint-args", "--verbose_dll", + "--dart-entrypoint-args", "--textloglev", + "--dart-entrypoint-args", "10" + ], + // "cwd": "C:\\jvxrt\\build.audio.all\\release\\runtime", <- this does not work since the interpreter can not find the main.dart script + "flutterMode": "debug", + }, + { + "name": "ayf_ffihost_example (debug-noargs)", + "request": "launch", + "type": "dart", + "program": "lib/main.dart", + "deviceId": "windows", + + // https://stackoverflow.com/questions/55004302/how-do-you-pass-arguments-from-command-line-to-main-in-flutter-dart + // https://dartcode.org/docs/launch-configuration/ + "args": [], + // "cwd": "C:\\jvxrt\\build.audio.all\\release\\runtime", <- this does not work since the interpreter can not find the main.dart script + "flutterMode": "debug", + }, + { + "name": "fernlive (profile mode)", + "request": "launch", + "type": "dart", + "args": [ + "--dart-define", "MY_VAR=foo" + ], + "flutterMode": "profile" + } + ] +} \ No newline at end of file diff --git a/flutter/ayf_ffihost/lib/ayf_ffihost.dart b/flutter/ayf_ffihost/lib/ayf_ffihost.dart new file mode 100644 index 00000000..5bfbfcee --- /dev/null +++ b/flutter/ayf_ffihost/lib/ayf_ffihost.dart @@ -0,0 +1,14 @@ +library ayf_ffihost; + +import 'backend/ayf_backend_bridge.dart'; +import 'package:ayf_pack/ayf_pack.dart'; +export 'backend/ayf_backend_adapter.dart'; +export 'backend/ayf_backend_bridge.dart'; + +/// A Calculator. +class Calculator { + /// Returns [value] plus 1. + int addOne(int value) => value + 1; +} + +AyfHost get ayfhost => AyfNativeAudioHost.instance; diff --git a/flutter/ayf_ffihost/lib/backend/ayf_backend_adapter.dart b/flutter/ayf_ffihost/lib/backend/ayf_backend_adapter.dart new file mode 100644 index 00000000..979e7c08 --- /dev/null +++ b/flutter/ayf_ffihost/lib/backend/ayf_backend_adapter.dart @@ -0,0 +1,82 @@ +import 'package:collection/collection.dart'; +import 'package:ayf_pack/ayf_pack.dart'; + +/* + * This is the base class for the backend adapter. It realizes the + * common functions required for every backend adapter. + */ //============================================================= +abstract class AudYoFloBackendAdapter { + // ========================================================== + // Interfaces for inheriting component: MUST BE IMPLEMENTED IN DERIVED CLASS!! + // ========================================================== + + // Translate the component identification struct into a string + String translateComponent(JvxComponentIdentification elm); + + // Creator for component property reference + dynamic createComponentPropertyRef( + JvxComponentIdentification cpId, dynamic optParam); + + // opposite of the creator + void destroyComponentPropertyReference( + JvxComponentIdentification cpId, dynamic removeme); + + // ========================================================== + // Member variables + // ========================================================== + + // Reference to the backend adapter (Interface only) + late AudYoFloBackendCacheBectrlIf theBeCache; + + // Map to hold all components + Map componentReferences = {}; + + // ========================================================== + // Initialize cross references + // ========================================================== + void initializeWithBackendCacheReference( + AudYoFloBackendCacheBectrlIf beCacheArg) { + theBeCache = beCacheArg; + } + + // ============================================================== + // Interface functions to be called from derived class + // ============================================================== + void reportSelectedComponent(JvxComponentIdentification cpId, + {dynamic optParam}) { + // Get the additional data as stored + + MapEntry? entryProps = componentReferences.entries + .firstWhereOrNull((element) => element.key == cpId); + if (entryProps == null) { + dynamic addMe = createComponentPropertyRef(cpId, optParam); + componentReferences[cpId] = addMe; + } else { + assert(false); + } + + // Translate component into a descriptive name in the BackendAdapter + String descr = translateComponent(cpId); + theBeCache.reportSelectedComponent(cpId, descr); + + // Invalidate the proper selection list in cache + // We need to report the new device at first and + // THEN validate the option listr + theBeCache.invalidateComponentListCache(cpId); + } + + void reportUnselectedComponent(JvxComponentIdentification cpId) { + // Invalidate the proper selection list in cache + theBeCache.invalidateComponentListCache(cpId); + + MapEntry? entry = componentReferences.entries + .firstWhereOrNull((element) => element.key == cpId); + if (entry != null) { + destroyComponentPropertyReference(cpId, entry.value); + componentReferences.removeWhere((key, value) => key == cpId); + } else { + assert(false); + } + return theBeCache.reportUnselectedComponent(cpId); + } +} diff --git a/flutter/ayf_ffihost/lib/backend/ayf_backend_adapter_native.dart b/flutter/ayf_ffihost/lib/backend/ayf_backend_adapter_native.dart new file mode 100644 index 00000000..958d57ec --- /dev/null +++ b/flutter/ayf_ffihost/lib/backend/ayf_backend_adapter_native.dart @@ -0,0 +1,1556 @@ +import 'dart:ffi'; +import 'dart:math'; +import 'dart:typed_data'; +import 'dart:async'; + +//import 'dart:html'; +import 'package:flutter/cupertino.dart'; + +import '../properties/ayf_properties_from_native.dart'; +import 'package:ffi/ffi.dart'; +import 'dart:convert'; +import 'package:collection/collection.dart'; +import 'package:fixnum/fixnum.dart' as fn; + +import 'package:ayf_pack/ayf_pack.dart'; +import 'package:ayf_pack_native/ayf_pack_native.dart'; + +import '../native-ffi/generated/ayf_ffi_gen_bind.dart'; +import '../systemstate/ayf_connected_processes.dart'; +import './ayf_backend_adapter.dart'; + +typedef Future shutdownFunction(); + +class AudYoFloNativePropertyHandle { + JvxComponentIdentification cpId = JvxComponentIdentification(); + AudioYoFloNative natLib; + Pointer propRef = nullptr; + Pointer opaqueHost; + + AudYoFloNativePropertyHandle( + {required this.cpId, + required this.propRef, + required this.natLib, + required this.opaqueHost}); +} + +class AudYoFloNativeHandles { + AudYoFloNativePropertyHandle? prop; + Pointer ident; + AudYoFloNativeHandles({required this.ident}); +} + +class AudYoFloBackendAdapterNative extends AudYoFloBackendAdapterIf + with AudYoFloBackendAdapter { + int cfg_gran_state_report = 5; + late AudioYoFloNative natLib; + Pointer opaque_host = nullptr; + + AudYoFloCompileFlags flags = AudYoFloCompileFlags(); + late AudYoFloBackendCacheBectrlIf theBeCacheNative; + shutdownFunction? shutdown; + + String latestError = ''; + + Completer? completeSequencer; + + // Retain a list of deferred actions to be run on certain conditions, e.g., + // to trigger shutdown of the app. + List queuedOperations = []; + + ayfBackendAccessEnum accBackend = ayfBackendAccessEnum.JVX_ACCESS_BACKEND_FFI; + //ayfBackendAccessEnum accBackend = + // ayfBackendAccessEnum.JVX_ACCESS_BACKEND_JSON; + // ============================================================== + + AudYoFloBackendAdapterNative(); + + @override + String get lastError => latestError; + + //! Return the currently detected compile flags + @override + AudYoFloCompileFlags compileFlags() { + return flags; + } + + void initializeNative( + AudioYoFloNative ptrNatlib, + Pointer opaque_host_hdl, + AudYoFloBackendCache theBeCacheArg, + shutdownFunction shutdownArg) { + natLib = ptrNatlib; + opaque_host = opaque_host_hdl; + + // Set cross references + theBeCacheArg.initializeWithBackendAdapterReference(this); + + // Extract interface interface + theBeCacheNative = theBeCacheArg; + + // Pass reference for backward cross referencing + super.initializeWithBackendCacheReference(theBeCacheNative); + shutdown = shutdownArg; + + // Find out how the backend has been compiled + Pointer dataFormatSpecPtr = calloc(); + Pointer numSFieldsBitFieldPtr = calloc(); + int res = natLib.ffi_get_compile_flags( + opaque_host_hdl, dataFormatSpecPtr, numSFieldsBitFieldPtr); + flags.compileNum32BitBitfield = numSFieldsBitFieldPtr.elementAt(0).value; + flags.compileDataTypeSpec = + jvxDataTypeSpecEEnum.fromInt(numSFieldsBitFieldPtr.value); + calloc.free(dataFormatSpecPtr); + calloc.free(numSFieldsBitFieldPtr); + assert(res == jvxErrorType.JVX_NO_ERROR); + + int reportSet = 1; + /* + * This can no longer be chosen, property changes should always be reported. + * HK, 24.02.2023 + * + AudYoFloBackendConfig cfg = theBeCacheArg.config; + int reportSet = 0; + if (cfg.reportPropertyOnSet) { + reportSet = 1; + } + */ + + int reportSyncDirect = 1; + + /* + * This can no longer be chosen, sync message should never be reported + * through the flutter code as this can not be realized in the plugin. + * Reporting direct means: always bypass Flutter code. + * HK, 24.02.2023 + * + if (cfg.reportSyncDirect) { + reportSyncDirect = 1; + } + */ + res = natLib.ffi_set_operation_flags( + opaque_host_hdl, reportSet, reportSyncDirect); + assert(res == jvxErrorType.JVX_NO_ERROR); + } + + // ========================================================= + @override + Future triggerStartSequencer() async { + int res = natLib.ffi_sequencer_start(opaque_host, cfg_gran_state_report); + if (res == jvxErrorType.JVX_NO_ERROR) { + // Run the first sequencer step immediately!! + res = natLib.ffi_sequencer_trigger(opaque_host); + } + return res; + } + + @override + Future triggerStopSequencer() async { + int res = natLib.ffi_sequencer_stop(opaque_host); + if (res == jvxErrorType.JVX_NO_ERROR) { + // Run the first sequencer step immediately!! + res = natLib.ffi_sequencer_trigger(opaque_host); + + // Wait for sequencer to complete + if (completeSequencer != null) { + await completeSequencer?.future; + + // Remove completer + completeSequencer = null; + } + } + return res; + } + + @override + Future triggerToggleSequencer() async { + jvxSequencerStatusEnum stat = await updateStatusSequencer(); + + if (stat == jvxSequencerStatusEnum.JVX_SEQUENCER_STATUS_NONE) { + triggerStartSequencer(); + } else { + triggerStopSequencer(); + } + return jvxErrorType.JVX_NO_ERROR; + } + + @override + Future updateStatusSequencer() async { + jvxSequencerStatusEnum stat = + jvxSequencerStatusEnum.JVX_SEQUENCER_STATUS_NONE; + if (opaque_host != nullptr) { + int statv = natLib.ffi_sequencer_status(opaque_host); + stat = jvxSequencerStatusEEnum.fromInt(statv); + } + return stat; + } + + // ============================================================ + + @override + Future transferTextCommand(String textArg, bool jsonReturn) async { + String retStr = ''; + int arg = 0; + if (jsonReturn) arg = 1; + Pointer ptrU8 = textArg.toNativeUtf8(); + Pointer cmd = ptrU8.cast(); + Pointer textRet = natLib.ffi_transfer_command(opaque_host, cmd, arg); + ptrU8 = textRet.cast(); + if (ptrU8 != null) { + retStr = ptrU8.toDartString(); + natLib.ffi_host_delete(textRet.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + } + return retStr; + } + + String translateComponent(JvxComponentIdentification elm) { + String ret = 'None'; + + // This function does not access the list of handles as it may request the + // description of a non-active component + Pointer cpId = calloc(1); + component_ident cpIdRef = cpId.ref; + cpIdRef.tp = elm.cpTp.index; + cpIdRef.slotid = elm.slotid; + cpIdRef.slotsubid = elm.slotsubid; + cpIdRef.uId = elm.uid; + + Pointer descrPtr = + natLib.ffi_component_description(opaque_host, cpId); + if (descrPtr != nullptr) { + Pointer dPtr = descrPtr.cast(); + String oneEntry = dPtr.toDartString(); + ret = oneEntry; + natLib.ffi_host_delete( + dPtr.cast(), ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + } + + calloc.free(cpId); + return ret; + } + + @override + int translateEnumString(String selection, String formatName, + AudYoFloCompileFlags flags) { + Pointer selPtr = selection.toNativeUtf8(); + Pointer formPtr = formatName.toNativeUtf8(); + return natLib.ffi_translate_enum_string( + opaque_host, formPtr.cast(), selPtr.cast()); + } + + /* + * This function is called by the sequencer controller to acknowledge shutdown. + * We do not need this in web connection but it is used in native host + */ ///========================================================================= + @override + jvxSequencerStatusEnum acknowledgeSequencerStop() { + jvxSequencerStatusEnum statOnLeave = + jvxSequencerStatusEnum.JVX_SEQUENCER_STATUS_NONE; + natLib.ffi_sequencer_ack_stop(opaque_host); + statOnLeave = jvxSequencerStatusEEnum + .fromInt(natLib.ffi_sequencer_status(opaque_host)); + return statOnLeave; + } + + @override + AudYoFloCommandQueueElement postSequencerStop() { + AudYoFloCommandQueueElement ret = + AudYoFloCommandQueueElement.AYF_COMMAND_QUEUE_NONE; + /* + if (queuedOperations + .contains(jvxFernliveCommandQueueElement.FERNLIVE_SHUTDOWN)) { + queuedOperations.removeWhere((element) => + element == jvxFernliveCommandQueueElement.FERNLIVE_SHUTDOWN); + ret = jvxFernliveCommandQueueElement.FERNLIVE_SHUTDOWN; + + } + */ + return ret; + } + + /* + void requestUpdateProperties( + JvxComponentIdentification cpId, String ident, bool contentOnly) { + List props = [ident]; + List propLst = str2PropList(ident); + if (propLst.length > 0) { + props = propLst; + } + + theBeCacheNative.invalidatePropertiesComponent(cpId, props, contentOnly); + } + */ + + AudYoFloNativePropertyHandle? requestNativePropertyReference( + JvxComponentIdentification cpId) { + AudYoFloNativePropertyHandle? theComp; + // asd + Pointer cpTpPtr = calloc(1); + component_ident idTpCp = cpTpPtr.ref; + idTpCp.tp = cpId.cpTp.index; + idTpCp.slotid = cpId.slotid; + idTpCp.slotsubid = cpId.slotsubid; + idTpCp.uId = cpId.uid; + + Pointer propHdl = + natLib.ffi_properties_request_handle(opaque_host, cpTpPtr); + if (propHdl != nullptr) { + theComp = AudYoFloNativePropertyHandle( + cpId: cpId, + propRef: propHdl, + natLib: natLib, + opaqueHost: opaque_host); + } + calloc.free(cpTpPtr); + return theComp; + } + + void returnNativePropertyReference( + JvxComponentIdentification cpId, AudYoFloNativePropertyHandle propRef) { + // This function deletes the propRef struct + natLib.ffi_properties_return_handle(opaque_host, propRef.propRef); + } + + @override + AudYoFloOneConnectedProcess allocateProcess(int uId) { + AudYoFloOneComponentInProcessNative inProcLocal = + AudYoFloOneComponentInProcessNative(); + AudYoFloOneConnectedProcess theProc = AudYoFloOneConnectedProcess(uId); + + Pointer hdl = + natLib.ffi_process_handle_for_uid(opaque_host, uId); + + if (hdl != nullptr) { + one_process procRef = hdl.ref; + if (procRef.name != nullptr) { + Pointer nmPtr = procRef.name.cast(); + theProc.nameProcess = nmPtr.toDartString(); + natLib.ffi_host_delete(nmPtr.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + theProc.catId = procRef.category; + } + + if (procRef.iterator != nullptr) { + AudYoFloOneComponentInProcess oneInvolved = + AudYoFloOneComponentInProcessNative(); + oneInvolved.fill(AudYoFloBackendSpecificNative( + natLib, opaque_host, procRef.iterator)); + theProc.involved = oneInvolved; + } + natLib.ffi_host_delete( + hdl.cast(), ffiDeleteDatatype.JVX_DELETE_DATATYPE_ONE_PROCESS); + } + return theProc; + } + + @override + int processSeqEvent( + jvxSequencerEventType ev, jvxSequencerStatusEnum seqStat) { + return theBeCacheNative.reportSequencerEvent(ev, seqStat); + } + + void addProcess(int uId) { + return theBeCacheNative.addProcess(uId); + } + + void remProcess(int uId) { + return theBeCacheNative.remProcess(uId); + } + + @override + String debugPrintProcess(int uId) { + return theBeCacheNative.debugPrintProcess(uId); + } + + void requestUpdateProcessStatus() async { + await theBeCacheNative.triggerUpdateProcessStatus(); + } + + @override + Future triggerUpdateProcessStatus() async { + int errCode = jvxErrorType.JVX_NO_ERROR; + + errCode = await Future.delayed(Duration.zero, () async { + int errCode = jvxErrorType.JVX_NO_ERROR; + if (accBackend == ayfBackendAccessEnum.JVX_ACCESS_BACKEND_FFI) { + // Native readout of the property descriptors + Pointer procReadyPtr = + natLib.ffi_process_system_ready(opaque_host); + if (procReadyPtr != nullptr) { + process_ready procReadyRef = procReadyPtr.ref; + bool processesReadyNew = + (procReadyRef.err == jvxErrorType.JVX_NO_ERROR); + String reasonIfNot = ''; + if (procReadyRef.reason_if_not != nullptr) { + reasonIfNot = + procReadyRef.reason_if_not.cast().toDartString(); + natLib.ffi_host_delete(procReadyRef.reason_if_not.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + } + natLib.ffi_host_delete(procReadyPtr.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_PROCESS_READY); + theBeCacheNative.updateProcessStatusCompleteNotify( + processesReadyNew, reasonIfNot); + } else { + errCode = jvxErrorType.JVX_ERROR_INTERNAL; + } + } else { + assert(false); + } + + return errCode; + }); + return errCode; + } + + @override + dynamic createComponentPropertyRef( + JvxComponentIdentification cpId, dynamic optParam /* ignored */) { + Pointer cpTpPtr = calloc(1); + component_ident idTpCp = cpTpPtr.ref; + idTpCp.tp = cpId.cpTp.index; + idTpCp.slotid = cpId.slotid; + idTpCp.slotsubid = cpId.slotsubid; + idTpCp.uId = cpId.uid; + + // calloc.free(cpTpPtr); + AudYoFloNativeHandles newHandlePair = AudYoFloNativeHandles(ident: cpTpPtr); + newHandlePair.prop = requestNativePropertyReference(cpId); + return newHandlePair; + } + + void destroyComponentPropertyReference( + JvxComponentIdentification cpId, dynamic removeme) { + AudYoFloNativeHandles storedHandlePair = removeme; + if (storedHandlePair.prop != null) { + // Release property reference only if it is not null + AudYoFloNativePropertyHandle propRef = storedHandlePair.prop!; + returnNativePropertyReference(cpId, propRef); + } + + // But always release the ident handle + calloc.free(storedHandlePair.ident); + } + + void reportStateSwitchComponent( + JvxComponentIdentification cpId, jvxStateSwitchEnum ss) { + return theBeCacheNative.reportStateSwitchComponent(cpId, ss); + } + + // Function called on event + void reportUpdateStatusComponent(JvxComponentIdentification cpId) { + return theBeCacheNative.reportUpdateStatusComponent(cpId); + } + + // ================================================================= + + // Translate component type enum to component class + @override + jvxComponentTypeClassEnum lookupComponentClass(JvxComponentTypeEnum tp) { + jvxComponentTypeClassEnum cpClass = + jvxComponentTypeClassEnum.JVX_COMPONENT_TYPE_NONE; + int cpClassId = natLib.ffi_lookup_component_class(opaque_host, tp.index); + if (cpClassId >= jvxComponentTypeClassEnum.JVX_COMPONENT_TYPE_NONE.index) { + cpClass = jvxComponentTypeClassEEnum.fromInt(cpClassId); + } + return cpClass; + } + + // Here is a good explanation of the async/await functionality: + // https://stackoverflow.com/questions/53249859/async-await-not-waiting-before-future-dart + @override + Future triggerUpdateAvailComponents(JvxComponentIdentification cpId, + List updateMe) async { + //List updateMe = []; + updateMe.clear(); + + // This is the core function call to update the status of a component + Future updateComponents = Future(() async { + // Convert the component identification into a string + int errCode = jvxErrorType.JVX_NO_ERROR; + String address = 'unknown'; + Pointer strPtr = natLib.ffi_encode_component_identification( + cpId.cpTp.index, cpId.slotid, cpId.slotsubid); + if (strPtr.address != 0) { + Pointer ptrU8 = strPtr.cast(); + address = ptrU8.toDartString(); + natLib.ffi_host_delete(strPtr.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + } + + // Generate a text command + String cmd = 'show(' + address + ');'; + int stat = jvxState.JVX_STATE_NONE; + + // Request command on backend (immediate emit) + String retText = await transferTextCommand(cmd, true); + + // Process the returned result + Map valueMap = json.decode(retText); + + // Extract the relevant information + MapEntry? entry_ecode = valueMap.entries + .firstWhereOrNull((element) => element.key == 'return_code'); + if (entry_ecode != null) { + errCode = jvxErrorTypeEInt.fromStringSingle(entry_ecode.value); + } + + // Next, return the load part for this command + MapEntry? entry_load = valueMap.entries + .firstWhereOrNull((element) => element.key == 'all_components'); + if (entry_load != null) { + for (Map singleSection in entry_load.value) { + String name = 'Unknown'; + String state = 'none'; + + MapEntry? singleEntryNm = singleSection.entries + .firstWhereOrNull((element) => element.key == 'description'); + if (singleEntryNm != null) { + name = singleEntryNm.value; + } + MapEntry? singleEntryState = singleSection.entries + .firstWhereOrNull((element) => element.key == 'state'); + if (singleEntryState != null) { + state = singleEntryState.value; + stat = jvxStateEInt.fromStringSingle(state); + } + AudYoFloOneComponentOptions newCom = + AudYoFloOneComponentOptions(name: name, stat: stat); + updateMe.add(newCom); + } + } + // theBeCache.updateAvailableComponentList(cpId, availComps); + + return errCode; + }); + return updateComponents; + } + + // =========================================================================== + // ACTIVATE COMPONENT ACTIVATE COMPONENT ACTIVATE COMPONENT ACTIVATE COMPONENT + // =========================================================================== + + @override + Future triggerActivateComponent( + JvxComponentIdentification cpId, int id, bool select) async { + int errCode = jvxErrorType.JVX_NO_ERROR; + jvxComponentTypeClassEnum cpCls = lookupComponentClass(cpId.cpTp); + if (accBackend == ayfBackendAccessEnum.JVX_ACCESS_BACKEND_FFI) { + Pointer ptrId = nullptr; + if (select) { + // Select currently selected component + switch (cpCls) { + case jvxComponentTypeClassEnum.JVX_COMPONENT_TYPE_DEVICE: + ptrId = natLib.ffi_select_component( + opaque_host, cpId.cpTp.index, cpId.slotid, cpId.slotsubid, id); + break; + case jvxComponentTypeClassEnum.JVX_COMPONENT_TYPE_NODE: + case jvxComponentTypeClassEnum.JVX_COMPONENT_TYPE_TECHNOLOGY: + ptrId = natLib.ffi_select_component( + opaque_host, cpId.cpTp.index, cpId.slotid, cpId.slotsubid, id); + break; + default: + assert(false); + } + } else { + // The required pointer must be taken from the list of native handles + MapEntry? entryProps = componentReferences.entries + .firstWhereOrNull((element) => element.key == cpId); + if (entryProps != null) { + ptrId = entryProps.value.ident; + } + } + + // Step 2: Activate selected component + if (ptrId != nullptr) { + errCode = natLib.ffi_activate_selected_component(opaque_host, ptrId); + } + if (!select) { + // If we have temporarily allocated a component ident pointer delete it here + natLib.ffi_host_delete(ptrId.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_ONE_COMPONENT_IDENT); + } + } else { + // JSON CONTROL + + String address = 'unknown'; + Pointer strPtr = natLib.ffi_encode_component_identification( + cpId.cpTp.index, cpId.slotid, cpId.slotsubid); + if (strPtr.address != 0) { + Pointer ptrU8 = strPtr.cast(); + address = ptrU8.toDartString(); + natLib.ffi_host_delete(strPtr.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + } + String cmd = 'act(' + address + ', activate, ' + id.toString() + ');'; + String retText = await transferTextCommand(cmd, true); + Map valueMap = json.decode(retText); + + MapEntry? entry_ecode = valueMap.entries + .firstWhereOrNull((element) => element.key == 'return_code'); + if (entry_ecode != null) { + errCode = jvxErrorTypeEInt.fromStringSingle(entry_ecode.value); + } + } + + return errCode; + } + + // =========================================================================== + // DEACTIVATE COMPONENT DEACTIVATE COMPONENT DEACTIVATE COMPONENT DEACTIVATE COMPONENT + // =========================================================================== + + @override + Future triggerDeactivateComponent( + JvxComponentIdentification cpId, bool unselect) async { + int errCode = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + jvxComponentTypeClassEnum cpCls = lookupComponentClass(cpId.cpTp); + if (accBackend == ayfBackendAccessEnum.JVX_ACCESS_BACKEND_FFI) { + // Get the native component ident from the list + Pointer ptrId = nullptr; + MapEntry? entryProps = componentReferences.entries + .firstWhereOrNull((element) => element.key == cpId); + if (entryProps != null) { + ptrId = entryProps.value.ident; + + if (ptrId != nullptr) { + errCode = + natLib.ffi_deactivate_selected_component(opaque_host, ptrId); + } + + // If we check the errorcode we will never get rid of selected devices + // if (errCode == jvxErrorType.JVX_NO_ERROR) { + if (unselect) { + errCode = natLib.ffi_unselect_selected_component(opaque_host, ptrId); + } + //} + + // Note that the component_ident handle is stored within the list and will be removed + // once the unselected component is reported back via the report callback! + } // if (entryProps != null) + } else { + // JSON CONTROL + assert(false); + String address = 'unknown'; + Pointer strPtr = natLib.ffi_encode_component_identification( + cpId.cpTp.index, cpId.slotid, cpId.slotsubid); + if (strPtr.address != 0) { + Pointer ptrU8 = strPtr.cast(); + address = ptrU8.toDartString(); + natLib.ffi_host_delete(strPtr.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + } + String cmd = 'act(' + address + ', deactivate);'; + String retText = await transferTextCommand(cmd, true); + Map valueMap = json.decode(retText); + + MapEntry? entry_ecode = valueMap.entries + .firstWhereOrNull((element) => element.key == 'return_code'); + if (entry_ecode != null) { + errCode = jvxErrorTypeEInt.fromStringSingle(entry_ecode.value); + } + } + + return errCode; + } + + @override + Future triggerGetPropertyContentComponent( + JvxComponentIdentification cpId, List propDescrLst, + {AyfPropertyReportLevel reportArg = + AyfPropertyReportLevel.AYF_FRONTEND_REPORT_SINGLE_PROPERTY}) { + Future getPropertyComponent = Future(() async { + int errCode = jvxErrorType.JVX_NO_ERROR; + if (accBackend == ayfBackendAccessEnum.JVX_ACCESS_BACKEND_FFI) { + MapEntry? entry = componentReferences.entries + .firstWhereOrNull((element) => element.key == cpId); + if (entry != null) { + AudYoFloNativeHandles oneNativeHandlePair = entry.value; + AudYoFloNativePropertyHandle? propRef = oneNativeHandlePair.prop; + if (propRef != null) { + for (var elmS in propDescrLst) { + AudYoFloPropertyContainer? propUpdate = + theBeCacheNative.referencePropertyInCache(cpId, elmS); + if (propUpdate != null) { + if (propUpdate is AudYoFloPropertyContentBackend) { + int errUpdate = AudYoFloPropertyContentFromNative + .updatePropertyContentFromNative( + propUpdate, elmS, propRef); + if (errUpdate == jvxErrorType.JVX_ERROR_POSTPONE) { + assert(false); + } + } else { + assert(false); + } + } + } + } else { + errCode = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + } + } else { + errCode = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + } + } else { + String address = 'unknown'; + Pointer strPtr = natLib.ffi_encode_component_identification( + cpId.cpTp.index, cpId.slotid, cpId.slotsubid); + if (strPtr.address != 0) { + Pointer ptrU8 = strPtr.cast(); + address = ptrU8.toDartString(); + natLib.ffi_host_delete(strPtr.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + } + String propDescr = ""; + if (propDescrLst.length > 1) { + propDescr = "["; + for (int i = 0; i < propDescrLst.length; i++) { + propDescr += propDescrLst[i]; + if (i != propDescrLst.length - 1) { + propDescr += ", "; + } + } + propDescr += "]"; + } else { + if (propDescrLst.length == 1) { + propDescr = propDescrLst[0]; + } + } + if (propDescr.isNotEmpty) { + String cmd = 'act(' + address + ', get_property, ' + propDescr + ');'; + String retText = await transferTextCommand(cmd, true); + Map valueMap = json.decode(retText); + + String? errStr = getStringEntryValueMap(valueMap, 'return_code'); + if (errStr != null) { + errCode = jvxErrorTypeEInt.fromStringSingle(errStr); + } + + Map? subSec = getMapValueMap(valueMap, 'properties'); + if (subSec != null) { + // Multiple fields returned + for (var elmS in propDescrLst) { + Map? subSubSec = getMapValueMap(subSec, elmS); + if (subSubSec != null) { + // Update the field here! + AudYoFloPropertyContainer? propUpdate = + theBeCacheNative.referencePropertyInCache(cpId, elmS); + if (propUpdate != null) { + if (propUpdate is AudYoFloPropertyContentBackend) { + int errUpdate = AudYoFloPropertyContentFromJson + .updatePropertyContentFromJsonMap( + propUpdate, subSubSec, this); + if (errUpdate == jvxErrorType.JVX_ERROR_POSTPONE) { + assert(false); + } + } else { + assert(false); + } + } + } + } + } else { + if (propDescrLst.length == 1) { + // Single field returned + String elmS = propDescr; + AudYoFloPropertyContainer? propUpdate = + theBeCacheNative.referencePropertyInCache(cpId, elmS); + if (propUpdate != null) { + if (propUpdate is AudYoFloPropertyContentBackend) { + AudYoFloPropertyContentFromJson + .updatePropertyContentFromJsonMap(propUpdate, valueMap, this); + } + } + } + } + } else { + errCode = jvxErrorType.JVX_ERROR_INVALID_ARGUMENT; + } + } + theBeCacheNative.updatePropertyCacheCompleteNotify(cpId, + report: reportArg); + + return errCode; + }); + return getPropertyComponent; + } + + @override + Future triggerGetPropertyDescriptionComponent( + JvxComponentIdentification cpId, List propDescrLst, + {AyfPropertyReportLevel reportArg = + AyfPropertyReportLevel.AYF_FRONTEND_REPORT_NO_REPORT, int numDigits = 2}) async { + int errCode = jvxErrorType.JVX_NO_ERROR; + + errCode = await Future.delayed(Duration.zero, () async { + if (accBackend == ayfBackendAccessEnum.JVX_ACCESS_BACKEND_FFI) { + // Native readout of the property descriptors + + // Find entry in component cache list + MapEntry? entry = componentReferences.entries + .firstWhereOrNull((element) => element.key == cpId); + if (entry != null) { + // If this component is in cache list go ahead with the property reference + AudYoFloNativeHandles oneHandlePair = entry.value; + AudYoFloNativePropertyHandle? propRef = oneHandlePair.prop; + + if (propRef != null) { + // Update all properties in provided string list + for (var elmS in propDescrLst) { + // Try to use existing container in cache + AudYoFloPropertyContainer? propFromCache = + theBeCacheNative.referencePropertyInCache(cpId, elmS); + if (propFromCache != null) { + if (propFromCache is AudYoFloPropertyContentBackend) { + // An element already exists: update the property content + int resL = AudYoFloPropertyContentFromNative + .updatePropertyDescriptorFromNative( + this, propFromCache, propRef, flags, true); + if (resL != jvxErrorType.JVX_NO_ERROR) { + String t1 = cpId.txt; + String t2 = propFromCache.descriptor; + String t3 = jvxErrorTypeEInt.toStringSingle(resL); + print( + "Failed to update single property with descriptor $t2 on component <$t1>, reason: <$t3>."); + // assert(false); + } + } else { + assert(false); + } + } else { + // Create new component and receive descriptor + AudYoFloPropertyContentBackend updateProp = + AudYoFloPropertyContentFromNative.createPropertyFromNative( + this, elmS, propRef, flags, numDigits); + + /* + int resL = updateProp.descriptorFromJsonMap(this, elmS); + if (resL == jvxErrorType.JVX_NO_ERROR) { + */ + int resL = theBeCacheNative.updateEntrySinglePropertyCache( + cpId, updateProp); + assert(resL == jvxErrorType.JVX_NO_ERROR); + } + } + } + } + } else { + String address = 'unknown'; + + Pointer strPtr = natLib.ffi_encode_component_identification( + cpId.cpTp.index, cpId.slotid, cpId.slotsubid); + if (strPtr.address != 0) { + Pointer ptrU8 = strPtr.cast(); + address = ptrU8.toDartString(); + natLib.ffi_host_delete(strPtr.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + } + + String propDescr = ""; + if (propDescrLst.length > 1) { + propDescr = "["; + for (int i = 0; i < propDescrLst.length; i++) { + propDescr += propDescrLst[i]; + if (i != propDescrLst.length - 1) { + propDescr += ", "; + } + } + propDescr += "]"; + } else { + if (propDescrLst.length == 1) { + propDescr = propDescrLst[0]; + } + } + if (propDescr.isNotEmpty) { + String cmd = + 'show(' + address + ', properties, fullp|x, ' + propDescr + ');'; + String retText = await transferTextCommand(cmd, true); + Map valueMap = json.decode(retText); + + errCode = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + String? errCodeExprPtr = + getStringEntryValueMap(valueMap, 'return_code'); + if (errCodeExprPtr != null) { + // Convert error code + String errCodeExpr = errCodeExprPtr; + errCode = jvxErrorTypeEInt.fromStringSingle(errCodeExpr); + } + + Map? subSec = getMapValueMap(valueMap, 'properties'); + if (subSec != null) { + for (var elmS in propDescrLst) { + Map? subSubSec = getMapValueMap(subSec, elmS); + if (subSubSec == null) { + assert(false); + } else { + AudYoFloPropertyContainer? propFromCache = + theBeCacheNative.referencePropertyInCache(cpId, elmS); + if (propFromCache != null) { + if (propFromCache is AudYoFloPropertyContentBackend) { + // An element already exists: update the property content + int resL = AudYoFloPropertyContentFromJson + .updatePropertyDescriptionFromJson(cpId, this, + propFromCache, elmS, subSubSec, flags, true, numDigits); + assert(resL == jvxErrorType.JVX_NO_ERROR); + } else { + assert(false); + } + } else { + AudYoFloPropertyContentBackend updateProp = + AudYoFloPropertyContentFromJson.createPropertyFromJsonMap( + cpId, this, elmS, subSubSec, flags, numDigits); + + /* + int resL = updateProp.descriptorFromJsonMap(this, elmS); + if (resL == jvxErrorType.JVX_NO_ERROR) { + */ + int resL = theBeCacheNative.updateEntrySinglePropertyCache( + cpId, updateProp); + if (resL != jvxErrorType.JVX_NO_ERROR) { + assert(false); + } + } + } + } + } + } else { + errCode = jvxErrorType.JVX_ERROR_INVALID_ARGUMENT; + } + } + if ((errCode == jvxErrorType.JVX_NO_ERROR) && + ((reportArg == + AyfPropertyReportLevel.AYF_FRONTEND_REPORT_SINGLE_PROPERTY) || + (reportArg == + AyfPropertyReportLevel + .AYF_FRONTEND_REPORT_COMPONENT_PROPERTY))) { + theBeCacheNative.updatePropertyCacheCompleteNotify(cpId, + report: reportArg); + } + return errCode; + }); + return errCode; + } + + @override + Future triggerSetProperties( + JvxComponentIdentification cpId, List propContentsArg, + {bool invalidateProperty = true, + int offset = 0, + int num = -1, + bool preventCallMultipleTimes = false, + AyfPropertyReportLevel reportArg = + AyfPropertyReportLevel.AYF_BACKEND_REPORT_COMPONENT_PROPERTY}) async { + // List of properties + List propContents = []; + AyfBackendReportPropertySetEnum repEnum = AyfBackendReportPropertySetEnum + .AYF_BACKEND_REPORT_SET_PROPERTY_INACTIVE; + + // We may reduce the number of "sets" to be issued + if (preventCallMultipleTimes) { + //print( + // 'Entering with true'); + + for (var propDescr in propContentsArg) { + AudYoFloPropertyContainer? propFromCache = + theBeCacheNative.referencePropertyInCache(cpId, propDescr); + if (propFromCache != null) { + if (propFromCache is AudYoFloPropertyContentBackend) { + if (propFromCache.setInProgress == + jvxPropertyProgressStates.JVX_PROPERTY_PROGRESS_NONE) { + propFromCache.setInProgress = + jvxPropertyProgressStates.JVX_PROPERTY_PROGRESS_WAIT; + propContents.add(propDescr); + } else { + //print( + // 'Set for property <$propDescr> is skipped due to previous pending operation'); + } + } else { + assert(false); + } + } + } + if (propContents.isEmpty) { + //print( + // 'Leaving with true'); + return jvxErrorType.JVX_NO_ERROR; + } else { + //print( + // 'Running with true'); + } + } else { + propContents = propContentsArg; + } + // ============================================================== + + if ((reportArg == + AyfPropertyReportLevel.AYF_BACKEND_REPORT_COMPONENT_PROPERTY) || + (reportArg == + AyfPropertyReportLevel + .AYF_BACKEND_REPORT_COMPONENT_PROPERTY_COLLECT)) { + repEnum = AyfBackendReportPropertySetEnum + .AYF_BACKEND_REPORT_SET_PROPERTY_ACTIVE; + } + + // We need to run the update in a delayed manner. Otherwise we could end up + // in calling this function in the build thread and the notify causes an exception + // since it is not allowed to run notify within the build thread!!! + // We may allow the delay to be larger to reduce the number of calls to the backend + // at this point! + return await Future.delayed( + Duration.zero /*Duration(milliseconds: 100)*/, () async { + int errCode = jvxErrorType.JVX_NO_ERROR; + if (accBackend == ayfBackendAccessEnum.JVX_ACCESS_BACKEND_FFI) { + MapEntry? entry = componentReferences.entries + .firstWhereOrNull((element) => element.key == cpId); + if (entry != null) { + // If this component is in cache list go ahead with the property reference + AudYoFloNativeHandles oneHandlePair = entry.value; + AudYoFloNativePropertyHandle? propRef = oneHandlePair.prop; + + if (propRef != null) { + if (reportArg == + AyfPropertyReportLevel + .AYF_BACKEND_REPORT_COMPONENT_PROPERTY_COLLECT) { + errCode = natLib.ffi_properties_set_collect_start( + opaque_host, propRef.propRef); + } + + for (var propDescr in propContents) { + AudYoFloPropertyContainer? propFromCache = + theBeCacheNative.referencePropertyInCache(cpId, propDescr); + if (propFromCache != null) { + if (propFromCache is AudYoFloPropertyContentBackend) { + if (propFromCache is AudYoFloPropertyValueInRangeNative) { + AudYoFloPropertyValueInRangeNative contVal = propFromCache; + + Pointer ptrSet = contVal.fldPtr.cast(); + assert(ptrSet != nullptr); + + int address = ptrSet.address; + address += contVal.szNativeElement * offset; + ptrSet = Pointer.fromAddress(address); + + // Limit the number of elements due to offset + int numLocal = contVal.fldSz - offset; + + // Limit the number of elements due to specification + if (num > 0) { + numLocal = min(num, numLocal); + } + + errCode = natLib.ffi_properties_set_value_range( + opaque_host, + propRef.propRef, + propFromCache.descriptor.toNativeUtf8().cast(), + 1, + /* contentOnly */ + offset, + ptrSet, + numLocal, + contVal.minVal, + contVal.maxVal, + repEnum.index); + } else if (propFromCache + is AudYoFloPropertyMultiContentNative) { + AudYoFloPropertyMultiContentNative contMulti = + propFromCache; + + Pointer ptrSet = contMulti.fldPtr.cast(); + if (ptrSet != nullptr) { + int address = ptrSet.address; + address += contMulti.szNativeElement * offset; + ptrSet = Pointer.fromAddress(address); + + // Limit the number of elements due to offset + int numLocal = contMulti.fldSz - offset; + + // Limit the number of elements due to specification + if (num > 0) { + numLocal = min(num, numLocal); + } + + errCode = natLib.ffi_properties_set_content_property_fld( + opaque_host, + propRef.propRef, + propFromCache.descriptor.toNativeUtf8().cast(), + 1, + /* contentOnly */ + offset, + ptrSet, + contMulti.jvxFormat.index, + numLocal, + repEnum.index); + } else { + print( + 'Requesting property <${propFromCache.descriptor}> of type <${propFromCache.dataType.name}}> not possible due to nullptr.'); + } + } else if (propFromCache + is AudYoFloPropertySelectionListBackend) { + AudYoFloPropertySelectionListBackend contSel = + propFromCache as AudYoFloPropertySelectionListBackend; + + if (contSel.selection is AudYoFloBitFieldNative) { + AudYoFloBitFieldNative sel = + contSel.selection as AudYoFloBitFieldNative; + + Pointer targetPtr = sel.raw; + int address = targetPtr.address; + address += offset * + sel.compileNum32BitBitfield * + sizeOf(); + targetPtr = Pointer.fromAddress(address); + + int numLocal = sel.numEntries; + if (num > 0) { + numLocal = min(num, numLocal); + } + + errCode = natLib.ffi_properties_set_selection_list( + opaque_host, + propRef.propRef, + propFromCache.descriptor.toNativeUtf8().cast(), + 1, + offset, + /* the offset may even be beyond 0 if property can deal with it */ + targetPtr, + numLocal, + sel.compileNum32BitBitfield, + repEnum.index); + } else { + assert(false); + } + } else if (propFromCache + is AudYoFloPropertySingleStringBackend) { + AudYoFloPropertySingleStringBackend contStr = propFromCache; + errCode = natLib.ffi_properties_set_single_string( + opaque_host, + propRef.propRef, + propFromCache.descriptor.toNativeUtf8().cast(), + contStr.value.toNativeUtf8().cast(), + repEnum.index); + } else { + print( + 'Setting property <${propFromCache.descriptor}> of type <${propFromCache.dataType.name}}> is currently not supported.'); + } + if (propFromCache.setInProgress == + jvxPropertyProgressStates.JVX_PROPERTY_PROGRESS_WAIT) { + propFromCache.setInProgress = + jvxPropertyProgressStates.JVX_PROPERTY_PROGRESS_NONE; + } + + } else { + assert(false); + } + } else { + // Property not found + } + } + +// If we use reportPropertyOnSet this will not be required + if (invalidateProperty) { + theBeCacheNative.invalidatePropertiesComponent( + cpId, propContents, true); + } + if (reportArg == + AyfPropertyReportLevel + .AYF_BACKEND_REPORT_COMPONENT_PROPERTY_COLLECT) { + errCode = natLib.ffi_properties_set_collect_stop( + opaque_host, propRef.propRef); + } + + if (reportArg != + AyfPropertyReportLevel.AYF_BACKEND_REPORT_COMPONENT_PROPERTY) { + // If we use reportPropertyOnSet this will not be required + theBeCacheNative.updatePropertyCacheCompleteNotify(cpId, + report: reportArg); + // theBeCacheNative.updatePropertiesInCacheComplete(report: report); + } + } + } + } + return errCode; + }); + } + + // Trigger backend to update the list of properties in cache + @override + Future triggerUpdatePropertyListComponent( + JvxComponentIdentification cpId, + {AyfPropertyReportLevel reportArg = AyfPropertyReportLevel + .AYF_FRONTEND_REPORT_COMPONENT_PROPERTY}) async { + // The update property list in cache is typically executed with a delay + // since here is the point where we have to request each single property. + // We are only allowed to notify the listeners if the result is not within + // the build function. + return await Future.delayed(Duration.zero, () async { + int errCode = jvxErrorType.JVX_NO_ERROR; + if (accBackend == ayfBackendAccessEnum.JVX_ACCESS_BACKEND_FFI) { + MapEntry? entry = componentReferences.entries + .firstWhereOrNull((element) => element.key == cpId); + if (entry != null) { + // If this component is in cache list go ahead with the property reference + AudYoFloNativeHandles oneHandlePair = entry.value; + AudYoFloNativePropertyHandle? propRef = oneHandlePair.prop; + if (propRef != null) { + List lstProps = []; + int num = natLib.ffi_properties_number_properties( + propRef.opaqueHost, propRef.propRef); + for (int idx = 0; idx < num; idx++) { + Pointer propDescrPtr = propRef.natLib + .ffi_properties_descriptor_property_lin( + propRef.opaqueHost, + propRef.propRef, + idx, + jvxPropertyDescriptorEnum + .JVX_PROPERTY_DESCRIPTOR_FULL_PLUS); + if (propDescrPtr != nullptr) { + one_property_full_plus propDescrRef = propDescrPtr.ref; + lstProps.add( + propDescrRef.full.descriptor.cast().toDartString()); + propRef.natLib.ffi_host_delete( + propDescrPtr.cast(), + ffiDeleteDatatype + .JVX_DELETE_DATATYPE_ONE_PROPERTY_DESCRIPTOR_FULL_PLUS); + } + } + theBeCacheNative.updatePropertyCacheListCompleteNotify( + cpId, lstProps, + report: reportArg); + } else { + errCode = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + } + } else { + errCode = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + } + } else { + String address = 'unknown'; + + Pointer strPtr = natLib.ffi_encode_component_identification( + cpId.cpTp.index, cpId.slotid, cpId.slotsubid); + if (strPtr.address != 0) { + Pointer ptrU8 = strPtr.cast(); + address = ptrU8.toDartString(); + natLib.ffi_host_delete(strPtr.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + } + + String propDescr = "/*"; + String cmd = + 'show(' + address + ', properties, list, ' + propDescr + ');'; + String retText = await transferTextCommand(cmd, true); + Map valueMap = json.decode(retText); + + errCode = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + String? errCodeExprPtr = + getStringEntryValueMap(valueMap, 'return_code'); + if (errCodeExprPtr != null) { + // Convert error code + String errCodeExpr = errCodeExprPtr; + errCode = jvxErrorTypeEInt.fromStringSingle(errCodeExpr); + } + if (errCode == jvxErrorType.JVX_NO_ERROR) { + List? subSec = getMapValueList(valueMap, 'properties'); + if (subSec != null) { + List lstProps = []; + for (var elmS in subSec) lstProps.add(elmS); + theBeCacheNative.updatePropertyCacheListCompleteNotify( + cpId, lstProps); + } + } + } + + return errCode; + }); + } + + @override + Future triggerUpdateDeviceList(JvxComponentIdentification cpId) async { + // The update property list in cache is typically executed with a delay + // since here is the point where we have to request each single property. + // We are only allowed to notify the listeners if the result is not within + // the build function. + return await Future.delayed(Duration.zero, () async { + int errCode = jvxErrorType.JVX_NO_ERROR; + + if (accBackend == ayfBackendAccessEnum.JVX_ACCESS_BACKEND_FFI) { + // Get the native opaque handle for the component + MapEntry? entry = componentReferences.entries + .firstWhereOrNull((element) => element.key == cpId); + if (entry != null) { + // If we found it + AudYoFloNativeHandles oneNativeHandlePair = entry.value; + Pointer cpPtr = oneNativeHandlePair.ident; + component_ident cpRref = cpPtr.ref; + // New device list + List devices = []; + + // Obtain the number of components + int num = natLib.ffi_component_number_devices(opaque_host, cpPtr); + for (int idx = 0; idx < num; idx++) { + AudYoFloOneDeviceSelectionOption newDevice = + AudYoFloOneDeviceSelectionOption(); + + // Native call to update + Pointer devCapPtr = natLib + .ffi_component_capabilities_device(opaque_host, cpPtr, idx); + if (devCapPtr != nullptr) { + device_capabilities devCapRef = devCapPtr.ref; + + // Fill in the values as received + newDevice.caps.fld = fn.Int32(devCapRef.caps); + newDevice.flow = + jvxDeviceDataFlowTypeEEnum.fromInt(devCapRef.flow); + newDevice.description = + devCapRef.description.cast().toDartString(); + newDevice.descriptor = + devCapRef.descriptor.cast().toDartString(); + newDevice.flags.fld = fn.Int32(devCapRef.flags); + newDevice.selectable = (devCapRef.selectable != 0); + newDevice.optionIdx = idx; + newDevice.state.fld = fn.Int32(devCapRef.stat); + + // newDevice.state = devCapRef. + + // The device is always technology + 1 + newDevice.devIdent.cpTp = + JvxComponentTypeEEnum.fromInt(cpRref.tp + 1); + + // The slotid value is always helpful to identify the technology + newDevice.devIdent.slotid = cpRref.slotid; + newDevice.devIdent.slotsubid = devCapRef.slotsubid; + devices.add(newDevice); + + if (devCapRef.description != nullptr) { + natLib.ffi_host_delete(devCapRef.description.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + } + + if (devCapRef.descriptor != nullptr) { + natLib.ffi_host_delete(devCapRef.descriptor.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + } + // Release the memory + natLib.ffi_host_delete(devCapPtr.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_DEVICE_CAPS); + + // Update the list and notify all!! + } + } + + theBeCacheNative.updateDeviceListCacheCompleteNotify(cpId, devices); + } else { + errCode = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + } + } else { + assert( + false); // was implemented but not tested, reactivate comment if desired + /* + // if (accBackend == ayfBackendAccessEnum.JVX_ACCESS_BACKEND_FFI) + String address = 'unknown'; + + Pointer strPtr = natLib.ffi_encode_component_identification( + cpId.cpTp.index, cpId.slotid, cpId.slotsubid); + if (strPtr.address != 0) { + Pointer ptrU8 = strPtr.cast(); + address = ptrU8.toDartString(); + natLib.ffi_host_delete(strPtr.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + } + + String propDescr = "/ *"; // <- added " " + String cmd = 'show(' + address + ', devices);'; + String retText = transferTextCommand(cmd, true); + Map valueMap = json.decode(retText); + + errCode = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + String? errCodeExprPtr = + helpers.getStringEntryValueMap(valueMap, 'return_code'); + if (errCodeExprPtr != null) { + // Convert error code + String errCodeExpr = errCodeExprPtr; + errCode = jvxErrorTypeEInt.fromStringSingle(errCodeExpr); + } + if (errCode == jvxErrorType.JVX_NO_ERROR) { + List? subSec = helpers.getMapValueList(valueMap, 'properties'); + if (subSec != null) { + List lstProps = []; + for (var elmS in subSec) lstProps.add(elmS); + theBeCacheNative.updatePropertyCacheListCompleteNotify(cpId, lstProps); + } + } + + */ + } + + return errCode; + }); + } + + @override + Future triggerUpdateComponentList( + JvxComponentIdentification cpId, bool isDevice) async { + // The update property list in cache is typically executed with a delay + // since here is the point where we have to request each single property. + // We are only allowed to notify the listeners if the result is not within + // the build function. + return await Future.delayed(Duration.zero, () async { + int errCode = jvxErrorType.JVX_NO_ERROR; + + if (accBackend == ayfBackendAccessEnum.JVX_ACCESS_BACKEND_FFI) { + // For this command we do not need a specific component ident + Pointer cpIdPtr = calloc(1); + component_ident cpIdRef = cpIdPtr.ref; + cpIdRef.tp = cpId.cpTp.index; + cpIdRef.slotid = cpId.slotid; + + // Sub id and uid do not have any impact + cpIdRef.slotsubid = cpId.slotsubid; + cpIdRef.uId = cpId.uid; + + // New device list + List options = []; + + // Get the slot population for this component + List ssids = []; + Pointer sslistPtr = + natLib.ffi_component_slot_allocation(opaque_host, cpIdPtr); + if (sslistPtr != nullptr) { + ss_list sslistRef = sslistPtr.ref; + Int32List? lst; + if (sslistRef.slots != nullptr) { + lst = sslistRef.slots.asTypedList(sslistRef.num); + } + if (sslistRef.subslots != nullptr) { + lst = sslistRef.subslots.asTypedList(sslistRef.num); + } + if (lst != null) { + for (var sgl in lst) { + ssids.add(sgl); + } + } + } + + // Obtain the number of components + int num = natLib.ffi_component_number_components(opaque_host, cpIdPtr); + for (int idx = 0; idx < num; idx++) { + AudYoFloOneComponentSelectionOption newOption = + AudYoFloOneComponentSelectionOption(); + + // Find the associated sots + for (int sidx = 0; sidx < ssids.length; sidx++) { + if (ssids[sidx] == idx) newOption.sids.add(sidx); + } + + // Native call to update entry + Pointer optPtr = + natLib.ffi_component_selection_option(opaque_host, cpIdPtr, idx); + if (optPtr != nullptr) { + selection_option optRef = optPtr.ref; + + // Fill in the values as received + newOption.description = + optRef.description.cast().toDartString(); + newOption.descriptor = + optRef.descriptor.cast().toDartString(); + + // Stil missing: the slots + newOption.optionIdx = idx; + + // The device is always technology + 1 + options.add(newOption); + + if (optRef.description != nullptr) { + natLib.ffi_host_delete(optRef.description.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + } + + if (optRef.descriptor != nullptr) { + natLib.ffi_host_delete(optRef.descriptor.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + } + // Release the memory + natLib.ffi_host_delete(optPtr.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_SELECTION_OPTION); + } + } + + calloc.free(cpIdPtr); + + // Update the list and notify all!! + theBeCacheNative.updateComponentListCacheCompleteNotify( + cpId, options, isDevice); + } else { + assert( + false); // was implemented but not tested, reactivate comment if desired + /* + // if (accBackend == ayfBackendAccessEnum.JVX_ACCESS_BACKEND_FFI) + String address = 'unknown'; + + Pointer strPtr = natLib.ffi_encode_component_identification( + cpId.cpTp.index, cpId.slotid, cpId.slotsubid); + if (strPtr.address != 0) { + Pointer ptrU8 = strPtr.cast(); + address = ptrU8.toDartString(); + natLib.ffi_host_delete(strPtr.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + } + + String propDescr = "/ *"; <- added " " + String cmd = 'show(' + address + ', options);'; + String retText = transferTextCommand(cmd, true); + Map valueMap = json.decode(retText); + + errCode = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + String? errCodeExprPtr = + helpers.getStringEntryValueMap(valueMap, 'return_code'); + if (errCodeExprPtr != null) { + // Convert error code + String errCodeExpr = errCodeExprPtr; + errCode = jvxErrorTypeEInt.fromStringSingle(errCodeExpr); + } + if (errCode == jvxErrorType.JVX_NO_ERROR) { + List? subSec = helpers.getMapValueList(valueMap, 'properties'); + if (subSec != null) { + List lstProps = []; + for (var elmS in subSec) lstProps.add(elmS); + theBeCacheNative.updatePropertyCacheListCompleteNotify(cpId, lstProps); + } + } + */ + } + + return errCode; + }); + } + + // ===================================================================== + // Api call to save current setup in config file + // ===================================================================== + + @override + Future triggerSaveConfig() async { + return await Future.delayed(Duration.zero, () async { + int errCode = jvxErrorType.JVX_NO_ERROR; + + if (accBackend == ayfBackendAccessEnum.JVX_ACCESS_BACKEND_FFI) { + errCode = natLib.ffi_config_save(opaque_host); + } else { + assert(false); + } + return errCode; + }); + } + + @override + void reportPropertyListSetBackend( + JvxComponentIdentification cpId, String propLst) { + theBeCacheNative.reportPropertyListSetBackend(cpId, propLst); + } + + @override + Future triggerClose() async { + if (shutdown != null) { + await shutdown!(); + } + } +} diff --git a/flutter/ayf_ffihost/lib/backend/ayf_backend_bridge.dart b/flutter/ayf_ffihost/lib/backend/ayf_backend_bridge.dart new file mode 100644 index 00000000..c270a4e9 --- /dev/null +++ b/flutter/ayf_ffihost/lib/backend/ayf_backend_bridge.dart @@ -0,0 +1,586 @@ +import 'package:flutter/material.dart'; +import 'package:ffi/ffi.dart'; +import 'dart:ffi' as ffi; +import 'dart:io' show File, Platform; +import '../native-ffi/generated/ayf_ffi_gen_bind.dart'; +import 'ayf_backend_bridge_native_callbacks.dart'; +import 'ayf_backend_adapter_native.dart'; +import 'ayf_backend_bridge_native_helpers.dart'; +import '../types/ayf_sequencer_types.dart'; +import 'package:ayf_pack/ayf_pack.dart'; +import 'package:collection/collection.dart'; +import 'dart:async'; + +class AyfNativeAudioHost extends AyfHost { + // Allocate the audio host with instance Native Audio Host + static AyfHost _instance = AyfNativeAudioHost(); + static AyfHost get instance => _instance; + + @override + String descriptionHost() { + return 'AyfNativeAudioHost'; + } + + @override + Future initialize(AudYoFloBackendCache beCache, + AudYoFloDebugModel? dbgModel, Map args) async { + List cmdArgsLst = []; + String libraryPath = "not-found"; + int executableInBinFolder = 0; + + var elm = + args.entries.firstWhereOrNull((element) => element.key == 'cmdArgs'); + if (elm != null) { + if (elm.value is List) { + cmdArgsLst = elm.value as List; + } + } + + elm = args.entries.firstWhereOrNull((element) => element.key == 'libPath'); + if (elm != null) { + if (elm.value is String) { + libraryPath = elm.value as String; + } + } + + elm = args.entries.firstWhereOrNull((element) => element.key == 'exeInBin'); + if (elm != null) { + if (elm.value is int) { + executableInBinFolder = elm.value as int; + } + } + + return _bridge.initialize( + cmdArgsLst, beCache, dbgModel, libraryPath, executableInBinFolder); + } + + @override + void terminate() => _bridge.terminate(); + void periodicTriggerSlow() => _bridge.periodic_trigger_slow(); + + // Hold the global instance + AudioFlowBackendBridge_ _bridge = AudioFlowBackendBridge_(); + // AudioFlowBackendBridge_ get beBridge => _instance; + AudYoFloCompileFlags get compFlags => _bridge.theBeAdapter.compileFlags(); + + @override + String get lastError => _bridge.theBeAdapter.lastError; +} + +// ========================================================== +// Derive this class from the Callback part and come back via +// overriden callbacks +// ========================================================== +class AudioFlowBackendBridge_ extends AudioFlowBackendBridgeCallbacks { +// ========================================================== + // The dynamic library which is connected via ffi + late ffi.DynamicLibrary dylib; + + // ========================================================== + // Copies version of the runtime arguments + List cmdArgs = []; + + // Global debug switch + // late FernliveBackendCache theBackendCache; + AudYoFloBackendAdapterNative theBeAdapter = AudYoFloBackendAdapterNative(); + AudYoFloDebugModel? theDebugModel; + + // =================================================== + // The bridge is a singleton that exists only once. + // The singleton functionality is indicated by the "factory" + // keyword - every additional call to the constructor returns the + // same object. + // =================================================== + + AudioFlowBackendBridge_._privateConstructor() { + print("Creating ."); + } + static final AudioFlowBackendBridge_ _instance = + AudioFlowBackendBridge_._privateConstructor(); + factory AudioFlowBackendBridge_() { + return _instance; + } + + // ================================================================== + // Startup the host bridge interface in ffi domain + // ================================================================== + + Future initialize( + List cmdArgsLst, + AudYoFloBackendCache beCache, + AudYoFloDebugModel? dbgModel, + String libraryPath, + int executableInBinFolder) async { + int result = 0; + + cmdArgs = cmdArgsLst; + //theBackendCache = beCache; + theDebugModel = dbgModel; + + if (!File(libraryPath).existsSync()) { + assert(false); + } + + debugPrint('Loading backend library <$libraryPath>'); + dylib = ffi.DynamicLibrary.open(libraryPath); + natLib = AudioYoFloNative(dylib); + + // =========================================================== + // Initialize the native functions to control the application + // =========================================================== + + // Install the methodCall callback that reports specific aspects that + // are not covered by flutter by default. + // WindowsAyfNative.initialize(_fernNativeCallback); + // =========================================================== + + // Initialize the DART API on the native side. In there, all + // function to control dart part from within the native code are passed + // the parameter NativeApi.initializeApiDLData + final initializeApi = dylib.lookupFunction< + ffi.IntPtr Function(ffi.Pointer), + int Function(ffi.Pointer)>("InitDartApiDL"); + initializeApi(ffi.NativeApi.initializeApiDLData); + + // Install the native port + final int nativePort = installNativePort(); + + // Allocate the backend handle + retOpaqueHandle retVal = natLib!.ffi_allocate_backend_handle(); + + // Store the host handle + opaque_host = retVal.opaque_hdl; + + // Cross-link the required system required + theBeAdapter.initializeNative( + natLib!, opaque_host, beCache, triggeredClose); + theDebugModel?.initialize(beCache); + + // ==================================================================== + // Provide a list of command line arguments + // ==================================================================== + List lstArgv = []; + String runExec = Platform.resolvedExecutable; //;Platform.script.path; + lstArgv.add(runExec); + int numEntries = cmdArgsLst.length; + for (var i = 0; i < numEntries; i++) { + print("Run Arg [ ${i} ] = ${cmdArgsLst[i]}"); + lstArgv.add(cmdArgsLst[i]); + } + + // Copy into ffi C domain + numEntries = lstArgv.length; + ffi.Pointer> lstArgvC = + calloc>(numEntries); + for (var i = 0; i < numEntries; i++) { + ffi.Pointer ptrU8 = lstArgv[i].toNativeUtf8(); + lstArgvC[i] = ptrU8.cast(); + } + + // Allocate the struct and fill it with synchronous callback references + ffi.Pointer pointers = calloc(1); + fill_static_callbacks(pointers); + + // Call the initialize function and pass the callback struct + result = natLib!.ffi_host_initialize(opaque_host, lstArgvC, numEntries, + pointers, nativePort, executableInBinFolder); + + // Free list of arguments + calloc.free(lstArgvC); + + // =========================================================== + // Initialize the native part + // =========================================================== + + // The following sets the min/max pixel size and the maximized + // start of the window + // initializeNative(); + + // =========================================================== + return true; + } + + // ============================================================ + // Terminate the ffi backend. I think this function is not called + // since there is just not termination signal + // ============================================================ + void terminate() { + int result = 0; + if (opaque_host != ffi.nullptr) { + result = natLib!.ffi_host_terminate(opaque_host); + result = natLib!.ffi_deallocate_backend_handle(opaque_host); + opaque_host = ffi.nullptr; + } + if (dylib != null) { + // DynamicLibrary.close() <- does not exist at the moment :-( + } + } + + // =========================================================== + // Some native function implementations + // =========================================================== + // + //Run functions to setup window resolution etc + /* + Future initializeNative() async { + Size size = await DesktopWindow.getWindowSize(); + print(size); + await DesktopWindow.setWindowSize(Size(500, 500)); + + await DesktopWindow.setMinWindowSize(Size(800, 800)); + await DesktopWindow.setMaxWindowSize(Size(800, 800)); + + await DesktopWindow.resetMaxWindowSize(); + await DesktopWindow.toggleFullScreen(); + bool isFullScreen = await DesktopWindow.getFullScreen(); + await DesktopWindow.setFullScreen(true); + isFullScreen = await DesktopWindow.getFullScreen(); + await DesktopWindow.setFullScreen(true); + } +*/ + Future triggeredClose() async { + jvxSequencerStatusEnum statusSeq = + jvxSequencerStatusEnum.JVX_SEQUENCER_STATUS_NONE; + + statusSeq = await theBeAdapter.updateStatusSequencer(); + + if (statusSeq != jvxSequencerStatusEnum.JVX_SEQUENCER_STATUS_NONE) { + assert(theBeAdapter.completeSequencer != null); + + // Wait for sequencer to complete + await theBeAdapter.triggerStopSequencer(); + + // Wait for sequencer to complete + await theBeAdapter.completeSequencer?.future; + + // Remove completer + theBeAdapter.completeSequencer = null; + } + + // Finalize the shutdown + finalizeShutdown(); + } + + // Callback in which the close event is reported to the flutter host in windows + /* + Future _fernNativeCallback(MethodCall methodCall) async { + String command = methodCall.method; + var args = methodCall.arguments; + Map valueMap = json.decode(args); + if (command == 'trigger_wm_close') { + jvxSequencerStatusEnum statusSeq = + jvxSequencerStatusEnum.JVX_SEQUENCER_STATUS_NONE; + + statusSeq = theBeAdapter.statusSequencer(); + + if (statusSeq != jvxSequencerStatusEnum.JVX_SEQUENCER_STATUS_NONE) { + theBeAdapter.queuedOperations + .add(jvxFernliveCommandQueueElement.FERNLIVE_SHUTDOWN); + theBeAdapter.triggerStopSequencer(); + } else { + finalizeShutdown(); + } + + // At the moment, I have no clue how to pass the result back to + // the caller. Somehow, there is no docs on this call direction. + return 123; + } + } + */ + + void finalizeShutdown() { + terminate(); + // WindowsAyfNative.acknowldge_wm_close('wm_close_ok'); + } + + Future runAutoStart() async { + String focusProperty = '/jvxLibHost/autostart'; + + ///ext_interfaces/application_property_autostart'; + JvxComponentIdentification cpId = JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_HOST, slotid: 0, slotsubid: 0); + var prop = theBeAdapter.theBeCacheNative + .referenceValidPropertiesComponents(cpId, [focusProperty]); + if (prop == null) { + await theBeAdapter.theBeCacheNative + .triggerUpdatePropertiesComponent(cpId, [focusProperty]); + prop = theBeAdapter.theBeCacheNative + .referenceValidPropertiesComponents(cpId, [focusProperty]); + } + + if (prop != null) { + if (prop.length == 1) { + var propStart = prop[0]; + if (propStart is AudYoFloPropertyMultiContentNative) { + if (propStart.fld[0] != 0) { + theBeAdapter.triggerStartSequencer(); + } + } + } + } + } + + //======================================================================== + // Overriden callback functions to report messages + //======================================================================== + void processAsyncCommand( + jvxReportCommandRequestEnum reqCom, + ffi.Pointer load_fld, + int sz_fld, + bool async, + jvxReportCommandBroadcastTypeEnum bcEnum) { + int uId = 0; + String dbgTxt; + bool dbgOut = false; + bool? dbgOutL = theDebugModel?.dbgOut; + if (dbgOutL != null) dbgOut = dbgOutL; + + JvxComponentIdentification cpId = + AudioFlowBackendBridgeHelpers.decodeCpIdReqCommand(natLib!, load_fld); + jvxStateSwitchEnum ss = jvxStateSwitchEnum.JVX_STATE_SWITCH_NONE; + + // ================================================================= + // ================================================================= + String txt = ''; + String bcText = bcEnum.txt; + if (dbgOut) { + if (async) { + txt = + 'Incoming ASYNC command requested <${reqCom.txt}> from component <${cpId.txt}> - $bcText'; + } else { + txt = + 'Incoming SYNC command requested <${reqCom.txt}> from component <${cpId.txt}> - $bcText'; + } + theDebugModel?.addLineOut(txt); + } + // ================================================================= + // ================================================================= + + switch (reqCom) { + case jvxReportCommandRequestEnum.JVX_REPORT_COMMAND_REQUEST_UNSPECIFIC: + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_UPDATE_AVAILABLE_COMPONENT_LIST: + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_UPDATE_STATUS_COMPONENT_LIST: + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_UPDATE_STATUS_COMPONENT: + theBeAdapter.reportUpdateStatusComponent(cpId); + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_SYSTEM_STATUS_CHANGED: + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_TRIGGER_SEQUENCER_IMMEDIATE: + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_UPDATE_ALL_PROPERTIES: + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_RESCHEDULE_MAIN: + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_BORN_SUBDEVICE: + ffi.Pointer ptrIdent = + natLib!.ffi_req_command_decode_ident_allocate_char_array(load_fld); + if (ptrIdent.address != 0) { + ffi.Pointer ptrU8 = ptrIdent.cast(); + String ident = ptrU8.toDartString(); + natLib!.ffi_host_delete(ptrIdent.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + if (dbgOut) { + theDebugModel?.addLineOut( + 'Component was born: ${ident} component <${cpId.txt}>'); + } + } + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_DIED_SUBDEVICE: + ffi.Pointer ptrIdent = + natLib!.ffi_req_command_decode_ident_allocate_char_array(load_fld); + if (ptrIdent.address != 0) { + ffi.Pointer ptrU8 = ptrIdent.cast(); + String ident = ptrU8.toDartString(); + natLib!.ffi_host_delete(ptrIdent.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + if (dbgOut) { + theDebugModel?.addLineOut( + 'Component about to die: ${ident} component <${cpId.txt}>'); + } + } + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_BORN_COMPONENT: + ffi.Pointer ptrIdent = + natLib!.ffi_req_command_decode_ident_allocate_char_array(load_fld); + if (ptrIdent.address != 0) { + ffi.Pointer ptrU8 = ptrIdent.cast(); + String ident = ptrU8.toDartString(); + natLib!.ffi_host_delete(ptrIdent.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + if (dbgOut) { + theDebugModel?.addLineOut( + 'Component was born: ${ident} component <${cpId.txt}>'); + } + } + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_DIED_COMPONENT: + ffi.Pointer ptrIdent = + natLib!.ffi_req_command_decode_ident_allocate_char_array(load_fld); + if (ptrIdent.address != 0) { + ffi.Pointer ptrU8 = ptrIdent.cast(); + String ident = ptrU8.toDartString(); + natLib!.ffi_host_delete(ptrIdent.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + if (dbgOut) { + theDebugModel?.addLineOut( + 'Component was born: ${ident} component <${cpId.txt}>'); + } + } + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_PROCESS_CONNECTED: + uId = decodeUId(natLib!, load_fld); + if (dbgOut) { + theDebugModel?.addLineOut('Process connected with uid ${uId}'); + } + theBeAdapter.addProcess(uId); + theBeAdapter.requestUpdateProcessStatus(); + + dbgTxt = theBeAdapter.debugPrintProcess(uId); + theDebugModel?.addLineOutConvert(dbgTxt); + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_PROCESS_TO_BE_DISCONNECTED: + uId = decodeUId(natLib!, load_fld); + if (dbgOut) { + theDebugModel + ?.addLineOut('Process to be disconnected with uid ${uId}'); + } + theBeAdapter.remProcess(uId); + break; + case jvxReportCommandRequestEnum.JVX_REPORT_COMMAND_REQUEST_TEST_CHAIN: + uId = decodeUId(natLib!, load_fld); + if (dbgOut) { + theDebugModel?.addLineOut('Tested chain with uid ${uId}'); + } + theBeAdapter.requestUpdateProcessStatus(); + break; + + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_COMPONENT_STATESWITCH: + ss = decodeSswitch(natLib!, load_fld); + + if (dbgOut) { + theDebugModel?.addLineOut( + 'Component State Switch -> ${ss.txt} component <${cpId.txt}>'); + } + switch (ss) { + case jvxStateSwitchEnum.JVX_STATE_SWITCH_SELECT: + theBeAdapter.reportSelectedComponent(cpId); + break; + case jvxStateSwitchEnum.JVX_STATE_SWITCH_UNSELECT: + theBeAdapter.reportUnselectedComponent(cpId); + break; + default: + + // Indicate that the state changed + theBeAdapter.reportStateSwitchComponent(cpId, ss); + break; + } + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_CONFIGURATION_COMPLETE: + runAutoStart(); + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_SEQUENCER_EVENT: + if (opaque_host != ffi.nullptr) { + var evPtr = natLib!.ffi_req_command_decode_sequencer_event(load_fld); + int status = natLib!.ffi_sequencer_status(opaque_host); + jvxSequencerStatusEnum seqStat = + jvxSequencerStatusEEnum.fromInt(status); + jvxSequencerEventType ev = + jvxSequencerEventTypeNative.decodeSequencerEventType(evPtr); + + one_sequencer_event evRef = evPtr.ref; + ffi.Pointer dPtr = evRef.description.cast(); + + natLib!.ffi_host_delete(dPtr.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + natLib!.ffi_host_delete(evPtr.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_ONE_SEQ_EVENT); + + if ((ev.eventMask & + jvxSequencerEventBitfield + .JVX_SEQUENCER_EVENT_PROCESS_STARTUP_COMPLETE) != + 0) { + // This event indicates a JVX_SEQUENCER_EVENT_PROCESS_STARTUP_COMPLETE + //Replace completer if there is an old one + theBeAdapter.completeSequencer = Completer(); + } + + if ((ev.eventMask & + jvxSequencerEventBitfield + .JVX_SEQUENCER_EVENT_PROCESS_TERMINATED) != + 0) { + // This event indicates a JVX_SEQUENCER_EVENT_PROCESS_TERMINATED + // Signal the completer + theBeAdapter.completeSequencer?.complete(null); + } + + int errCode = theBeAdapter.processSeqEvent(ev, seqStat); + if (errCode != jvxErrorType.JVX_NO_ERROR) { + print('Error reported while reporting sequencer event!'); + } + /* + if (whattodo == jvxFernliveCommandQueueElement.FERNLIVE_SHUTDOWN) { + finalizeShutdown(); + } + */ + } else { + print('Nullptr in call'); + } + // If the + //theBeAdapter.triggerUpdateProcessStatus(); + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_SEQUENCER_CALLBACK: + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_PROCESS_DISCONNECT_COMPLETE: + theBeAdapter.requestUpdateProcessStatus(); + break; + + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_UPDATE_PROPERTY: + ffi.Pointer ptrIdent = + natLib!.ffi_req_command_decode_ident_allocate_char_array(load_fld); + if (ptrIdent.address != 0) { + ffi.Pointer ptrU8 = ptrIdent.cast(); + String propLst = ptrU8.toDartString(); + natLib!.ffi_host_delete(ptrIdent.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + theBeAdapter.reportPropertyListSetBackend(cpId, propLst); + } + break; + } + } + + void periodic_trigger_slow() { + jvxSequencerStatusEnum seq_stat = + jvxSequencerStatusEnum.JVX_SEQUENCER_STATUS_NONE; + if (opaque_host != ffi.nullptr) { + seq_stat = jvxSequencerStatusEEnum + .fromInt(natLib!.ffi_sequencer_status(opaque_host)); + + if (seq_stat != jvxSequencerStatusEnum.JVX_SEQUENCER_STATUS_NONE) { + natLib!.ffi_sequencer_trigger(opaque_host); + } + natLib!.ffi_periodic_trigger(opaque_host); + } + } +} diff --git a/flutter/ayf_ffihost/lib/backend/ayf_backend_bridge_native_callbacks.dart b/flutter/ayf_ffihost/lib/backend/ayf_backend_bridge_native_callbacks.dart new file mode 100644 index 00000000..0e966e63 --- /dev/null +++ b/flutter/ayf_ffihost/lib/backend/ayf_backend_bridge_native_callbacks.dart @@ -0,0 +1,187 @@ +/* + * Module top implement synchronous and asynchronous callbacks. Sync callbacks + * are endpoints within a native ffi call and end up in a static function. + * Async callbacks are triggered via the flutter native port API and will be + * triggered in a queued approach. + */ + +import 'dart:ffi'; +import 'dart:isolate'; +import '../native-ffi/generated/ayf_ffi_gen_bind.dart'; +import 'ayf_backend_bridge_native_helpers.dart'; +import 'ayf_backend_bridge.dart'; +import 'package:ayf_pack/ayf_pack.dart'; +import 'package:ayf_pack_native/ayf_pack_native.dart'; + +// ========================================================================= +// Definition of natove callback for sync messages +// ========================================================================= +typedef callback_tp = Int32 Function(Pointer); +typedef callback_report = Int32 Function(Pointer); + +// ========================================================================= +// Class to concentrate all callbacks in the ferncast native bridge component +// ========================================================================= +abstract class AudioFlowBackendBridgeCallbacks + extends AudioFlowBackendBridgeHelpers { +// Generrated part of the ffi native library + AudioYoFloNative? natLib; + + // Pointer to address the host handle + Pointer opaque_host = nullptr; + + void fill_static_callbacks(Pointer pointers) { + func_pointers func_ptrs_assign = pointers.ref; + + func_ptrs_assign.cb_sync = Pointer.fromFunction( + static_sync_callback, jvxErrorType.JVX_ERROR_INTERNAL); + } + + int installNativePort() { + // Install a native port to report to the DART side + // Good thing: requestExecuteCallback is within class context + // and can be invoked from every thread + final interactiveCppRequests = ReceivePort()..listen(asyncCallback); + int nativePort = interactiveCppRequests.sendPort.nativePort; + return nativePort; + } + + // ============================================================ + // Intermediate callback functions as given by the Flutter system. + // We get access to the bridge object by accessing the main bridge singleton + // ============================================================ + static int static_sync_callback(Pointer ptr) { + // Access to the main bridge only via global reference since we are not in build tree + AudioFlowBackendBridge_ inClass = new AudioFlowBackendBridge_(); + + // Return to bridge component + assert(false); + return 0; + //inClass.sync_callback(ptr); + } + + // =================================================================== + // Async callback as triggered via the native port + // =================================================================== + void asyncCallback(dynamic message) { + final int cbk_address = message; + final cbks = Pointer.fromAddress(cbk_address); + report_callback cbkref = cbks.ref; + + int id1 = cbkref.callback_id; + int id2 = cbkref.callback_subid; + int res = jvxErrorType.JVX_NO_ERROR; + + switch (id1) { + case ffiCallbackIds.JVX_FFI_CALLBACK_ASYNC_REPORT_TEXT: + printFormattedTextMessage(id2, cbkref.load_fld); + natLib!.ffi_host_delete( + cbkref.load_fld, ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + break; + + case ffiCallbackIds.JVX_FFI_CALLBACK_ASYNC_REPORT_COMMAND_REQUEST: + + // This should not popup here + assert(false); + break; + + case ffiCallbackIds.JVX_FFI_CALLBACK_REQUEST_COMMAND: + + // Decode the broadcast type to filtert out rescheduled commands + jvxReportCommandBroadcastTypeEnum bcEnum = + decodeBroadcastType(natLib!, cbkref.load_fld); + + // Forward the async message. Re-scheduled messages + // will be filtered out by the backend function! + res = natLib! + .ffi_host_request_command_fwd(opaque_host, cbkref.load_fld, 1); + + // Local processing of the request + if (res == jvxErrorType.JVX_NO_ERROR) { + jvxReportCommandRequestEnum reqCom = decodeReqCommand(id2); + + processAsyncCommand( + reqCom, cbkref.load_fld, cbkref.sz_fld, true, bcEnum); + } + + // This function deletes the "load_fld" object + natLib!.ffi_host_delete(cbkref.load_fld, + ffiDeleteDatatype.JVX_DELETE_DATATYPE_REQUEST_COMMAND); + break; + + default: + print('Uncaught asynchronous callback.'); + print(StackTrace.current); + } + final delv = Pointer.fromAddress(cbk_address); + natLib!.ffi_host_delete( + delv, ffiDeleteDatatype.JVX_DELETE_DATATYPE_ASYNC_CALLBACK); + } + + // ============================================================ + // Final mapping goal to forward synchronous and asynchronous callbacks + // ============================================================ + int sync_callback(Pointer cbks) { + report_callback cbkref = cbks.ref; + + int id1 = cbkref.callback_id; + int id2 = cbkref.callback_subid; + int res = jvxErrorType.JVX_NO_ERROR; + + switch (id1) { + case ffiCallbackIds.JVX_FFI_CALLBACK_SYNC_REPORT_INTERNALS_CHANGED: + break; + + case ffiCallbackIds.JVX_FFI_CALLBACK_REQUEST_COMMAND: + + // Forward the sync call to backend library + res = natLib! + .ffi_host_request_command_fwd(opaque_host, cbkref.load_fld, 0); + + // The following are tasks that must be run from within the + // UI host and within the main thread! + jvxReportCommandRequestEnum reqTp = + decodeRequestType(natLib!, cbkref.load_fld); + switch (reqTp) { + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_TRIGGER_SEQUENCER_IMMEDIATE: + + // Here, we need to run the sequener step IMMEDIATELY! + natLib!.ffi_sequencer_trigger(opaque_host); + break; + + default: + break; + } + + //natLib.ffi_comm + + if (res == jvxErrorType.JVX_NO_ERROR) { + // Sync calls must be re-scheduled to enter the UI to keep everything in order + res = natLib!.ffi_host_request_command_reschedule(cbkref.load_fld); + /* + jvxReportCommandRequestEnum reqCom = decodeReqCommand(id2); + process_request_command( + reqCom, cbkref.load_fld, cbkref.sz_fld, false); + */ + } + break; + + default: + res = jvxErrorType.JVX_ERROR_UNSUPPORTED; + print('Uncaught synchronous callback.'); + print(StackTrace.current); + } + return res; + } + + // ======================================================================= + // Virtual abstract member functions to be re-defined by derived classes + // ======================================================================= + void processAsyncCommand( + jvxReportCommandRequestEnum reqCom, + Pointer load_fld, + int sz_fld, + bool async, + jvxReportCommandBroadcastTypeEnum bcEnum); +} diff --git a/flutter/ayf_ffihost/lib/backend/ayf_backend_bridge_native_helpers.dart b/flutter/ayf_ffihost/lib/backend/ayf_backend_bridge_native_helpers.dart new file mode 100644 index 00000000..3ca93f95 --- /dev/null +++ b/flutter/ayf_ffihost/lib/backend/ayf_backend_bridge_native_helpers.dart @@ -0,0 +1,72 @@ +// Module to provide some native to flutter conversion helpers +import 'package:ffi/ffi.dart'; +import 'dart:ffi'; + +import 'package:ayf_pack/ayf_pack.dart'; +import '../native-ffi/generated/ayf_ffi_gen_bind.dart'; + +// Abstract class with conversion functions +abstract class AudioFlowBackendBridgeHelpers { + // Decode a component identification (type, slot, subslot, uid) + static JvxComponentIdentification decodeCpIdReqCommand( + AudioYoFloNative natLib, Pointer load_fld) { + Pointer cpIdPtr = calloc(1); + + int resLoc = natLib.ffi_req_command_decode_origin(load_fld, cpIdPtr); + + var ret = JvxComponentIdentification(); + component_ident cpIdRef = cpIdPtr.ref; + + ret.cpTp = JvxComponentTypeEEnum.fromInt(cpIdRef.tp); + ret.slotid = cpIdRef.slotid; + ret.slotsubid = cpIdRef.slotsubid; + ret.uid = cpIdRef.uId; + calloc.free(cpIdPtr); + + return ret; + } + + // Function to decode uId for processes + int decodeUId(AudioYoFloNative natLib, Pointer load_fld) { + Pointer uIdPtr = calloc(1); + int resLoc = natLib.ffi_req_command_decode_uid(load_fld, uIdPtr); + int uId = uIdPtr.value; + calloc.free(uIdPtr); + return uId; + } + +// Function to decode state switches + jvxStateSwitchEnum decodeSswitch( + AudioYoFloNative natLib, Pointer load_fld) { + Pointer ssPtr = calloc(1); + int resLoc = natLib.ffi_req_command_decode_sswitch(load_fld, ssPtr); + int ssi = ssPtr.value; + calloc.free(ssPtr); + return jvxStateSwitchEEnum.fromInt(ssi); + } + + // Function to decode state switches + jvxReportCommandRequestEnum decodeRequestType( + AudioYoFloNative natLib, Pointer load_fld) { + Pointer reqPtr = calloc(1); + int resLoc = natLib.ffi_req_command_decode_request_type(load_fld, reqPtr); + int reqi = reqPtr.value; + calloc.free(reqPtr); + return jvxReportCommandRequestEEnum.fromInt(reqi); + } + + // Function to decode command requests + jvxReportCommandRequestEnum decodeReqCommand(int rci) { + return jvxReportCommandRequestEEnum.fromInt(rci); + } + + // Function to decode command requests + jvxReportCommandBroadcastTypeEnum decodeBroadcastType( + AudioYoFloNative natLib, Pointer load_fld) { + Pointer bcPtr = calloc(1); + int resLoc = natLib.ffi_req_command_decode_broadcast(load_fld, bcPtr); + int bcId = bcPtr.value; + calloc.free(bcPtr); + return jvxReportCommandBroadcastTypeEEnum.fromInt(bcId); + } +} diff --git a/flutter/ayf_ffihost/lib/native-ffi/generated/ayf_ffi_gen_bind.dart b/flutter/ayf_ffihost/lib/native-ffi/generated/ayf_ffi_gen_bind.dart new file mode 100644 index 00000000..7d2cf003 --- /dev/null +++ b/flutter/ayf_ffihost/lib/native-ffi/generated/ayf_ffi_gen_bind.dart @@ -0,0 +1,1685 @@ +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `package:ffigen`. +import 'dart:ffi' as ffi; + +/// Bindings to allow access to fernlive native backend. +class AudioYoFloNative { + /// Holds the symbol lookup function. + final ffi.Pointer Function(String symbolName) + _lookup; + + /// The symbols are looked up in [dynamicLibrary]. + AudioYoFloNative(ffi.DynamicLibrary dynamicLibrary) + : _lookup = dynamicLibrary.lookup; + + /// The symbols are looked up with [lookup]. + AudioYoFloNative.fromLookup( + ffi.Pointer Function(String symbolName) + lookup) + : _lookup = lookup; + + retOpaqueHandle ffi_allocate_backend_handle() { + return _ffi_allocate_backend_handle(); + } + + late final _ffi_allocate_backend_handlePtr = + _lookup>( + 'ffi_allocate_backend_handle'); + late final _ffi_allocate_backend_handle = + _ffi_allocate_backend_handlePtr.asFunction(); + + int ffi_deallocate_backend_handle( + ffi.Pointer opaque_hdl, + ) { + return _ffi_deallocate_backend_handle( + opaque_hdl, + ); + } + + late final _ffi_deallocate_backend_handlePtr = + _lookup)>>( + 'ffi_deallocate_backend_handle'); + late final _ffi_deallocate_backend_handle = _ffi_deallocate_backend_handlePtr + .asFunction)>(); + + int ffi_host_initialize( + ffi.Pointer opaque_hdl, + ffi.Pointer> argv, + int argc, + ffi.Pointer funcs, + int dart_port, + int executableInBinFolder, + ) { + return _ffi_host_initialize( + opaque_hdl, + argv, + argc, + funcs, + dart_port, + executableInBinFolder, + ); + } + + late final _ffi_host_initializePtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer, + ffi.Pointer>, + ffi.Int32, + ffi.Pointer, + ffi.Int64, + ffi.Int32)>>('ffi_host_initialize'); + late final _ffi_host_initialize = _ffi_host_initializePtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer>, + int, ffi.Pointer, int, int)>(); + + int ffi_host_terminate( + ffi.Pointer opaque_hdl, + ) { + return _ffi_host_terminate( + opaque_hdl, + ); + } + + late final _ffi_host_terminatePtr = + _lookup)>>( + 'ffi_host_terminate'); + late final _ffi_host_terminate = + _ffi_host_terminatePtr.asFunction)>(); + + int ffi_req_command_decode_origin( + ffi.Pointer hdl_class, + ffi.Pointer retCp, + ) { + return _ffi_req_command_decode_origin( + hdl_class, + retCp, + ); + } + + late final _ffi_req_command_decode_originPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer, + ffi.Pointer)>>('ffi_req_command_decode_origin'); + late final _ffi_req_command_decode_origin = + _ffi_req_command_decode_originPtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer)>(); + + int ffi_req_command_decode_sswitch( + ffi.Pointer hdl_class, + ffi.Pointer sswitchRet, + ) { + return _ffi_req_command_decode_sswitch( + hdl_class, + sswitchRet, + ); + } + + late final _ffi_req_command_decode_sswitchPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer, + ffi.Pointer)>>('ffi_req_command_decode_sswitch'); + late final _ffi_req_command_decode_sswitch = + _ffi_req_command_decode_sswitchPtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer)>(); + + int ffi_req_command_decode_broadcast( + ffi.Pointer hdl_class, + ffi.Pointer sswitchRet, + ) { + return _ffi_req_command_decode_broadcast( + hdl_class, + sswitchRet, + ); + } + + late final _ffi_req_command_decode_broadcastPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer, + ffi.Pointer)>>('ffi_req_command_decode_broadcast'); + late final _ffi_req_command_decode_broadcast = + _ffi_req_command_decode_broadcastPtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer)>(); + + int ffi_req_command_decode_uid( + ffi.Pointer hdl_class, + ffi.Pointer uid, + ) { + return _ffi_req_command_decode_uid( + hdl_class, + uid, + ); + } + + late final _ffi_req_command_decode_uidPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer, + ffi.Pointer)>>('ffi_req_command_decode_uid'); + late final _ffi_req_command_decode_uid = + _ffi_req_command_decode_uidPtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer)>(); + + int ffi_req_command_decode_request_type( + ffi.Pointer hdl_class, + ffi.Pointer reqRetPtr, + ) { + return _ffi_req_command_decode_request_type( + hdl_class, + reqRetPtr, + ); + } + + late final _ffi_req_command_decode_request_typePtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer, + ffi.Pointer)>>('ffi_req_command_decode_request_type'); + late final _ffi_req_command_decode_request_type = + _ffi_req_command_decode_request_typePtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer)>(); + + ffi.Pointer ffi_req_command_decode_ident_allocate_char_array( + ffi.Pointer hdl_class, + ) { + return _ffi_req_command_decode_ident_allocate_char_array( + hdl_class, + ); + } + + late final _ffi_req_command_decode_ident_allocate_char_arrayPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer)>>( + 'ffi_req_command_decode_ident_allocate_char_array'); + late final _ffi_req_command_decode_ident_allocate_char_array = + _ffi_req_command_decode_ident_allocate_char_arrayPtr + .asFunction Function(ffi.Pointer)>(); + + ffi.Pointer ffi_req_command_decode_sequencer_event( + ffi.Pointer hdl_class, + ) { + return _ffi_req_command_decode_sequencer_event( + hdl_class, + ); + } + + late final _ffi_req_command_decode_sequencer_eventPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer)>>( + 'ffi_req_command_decode_sequencer_event'); + late final _ffi_req_command_decode_sequencer_event = + _ffi_req_command_decode_sequencer_eventPtr.asFunction< + ffi.Pointer Function(ffi.Pointer)>(); + + ffi.Pointer ffi_process_handle_for_uid( + ffi.Pointer opaque_hdl, + int uId, + ) { + return _ffi_process_handle_for_uid( + opaque_hdl, + uId, + ); + } + + late final _ffi_process_handle_for_uidPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Int32)>>('ffi_process_handle_for_uid'); + late final _ffi_process_handle_for_uid = + _ffi_process_handle_for_uidPtr.asFunction< + ffi.Pointer Function(ffi.Pointer, int)>(); + + int ffi_process_decode_iterator_context( + ffi.Pointer opaque_hdl, + ffi.Pointer it_handle, + ) { + return _ffi_process_decode_iterator_context( + opaque_hdl, + it_handle, + ); + } + + late final _ffi_process_decode_iterator_contextPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer, + ffi.Pointer)>>('ffi_process_decode_iterator_context'); + late final _ffi_process_decode_iterator_context = + _ffi_process_decode_iterator_contextPtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer)>(); + + ffi.Pointer ffi_process_iterator_next( + ffi.Pointer opaque_hdl, + ffi.Pointer it_handle, + int idNext, + ) { + return _ffi_process_iterator_next( + opaque_hdl, + it_handle, + idNext, + ); + } + + late final _ffi_process_iterator_nextPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer, ffi.Int64)>>('ffi_process_iterator_next'); + late final _ffi_process_iterator_next = + _ffi_process_iterator_nextPtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer, int)>(); + + ffi.Pointer ffi_process_decode_iterator_component( + ffi.Pointer opaque_hdl, + ffi.Pointer it_handle, + ) { + return _ffi_process_decode_iterator_component( + opaque_hdl, + it_handle, + ); + } + + late final _ffi_process_decode_iterator_componentPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer)>>('ffi_process_decode_iterator_component'); + late final _ffi_process_decode_iterator_component = + _ffi_process_decode_iterator_componentPtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer)>(); + + ffi.Pointer ffi_process_decode_iterator_module( + ffi.Pointer opaque_hdl, + ffi.Pointer it_handle, + ) { + return _ffi_process_decode_iterator_module( + opaque_hdl, + it_handle, + ); + } + + late final _ffi_process_decode_iterator_modulePtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer)>>('ffi_process_decode_iterator_module'); + late final _ffi_process_decode_iterator_module = + _ffi_process_decode_iterator_modulePtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer)>(); + + ffi.Pointer ffi_process_decode_iterator_connector( + ffi.Pointer opaque_hdl, + ffi.Pointer it_handle, + ) { + return _ffi_process_decode_iterator_connector( + opaque_hdl, + it_handle, + ); + } + + late final _ffi_process_decode_iterator_connectorPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer)>>('ffi_process_decode_iterator_connector'); + late final _ffi_process_decode_iterator_connector = + _ffi_process_decode_iterator_connectorPtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer)>(); + + ffi.Pointer ffi_process_system_ready( + ffi.Pointer opaque_hdl, + ) { + return _ffi_process_system_ready( + opaque_hdl, + ); + } + + late final _ffi_process_system_readyPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer)>>('ffi_process_system_ready'); + late final _ffi_process_system_ready = _ffi_process_system_readyPtr + .asFunction Function(ffi.Pointer)>(); + + int ffi_sequencer_status( + ffi.Pointer opaque_hdl, + ) { + return _ffi_sequencer_status( + opaque_hdl, + ); + } + + late final _ffi_sequencer_statusPtr = + _lookup)>>( + 'ffi_sequencer_status'); + late final _ffi_sequencer_status = _ffi_sequencer_statusPtr + .asFunction)>(); + + int ffi_sequencer_start( + ffi.Pointer opaque_hdl, + int granularity_state_report, + ) { + return _ffi_sequencer_start( + opaque_hdl, + granularity_state_report, + ); + } + + late final _ffi_sequencer_startPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer, ffi.Int32)>>('ffi_sequencer_start'); + late final _ffi_sequencer_start = _ffi_sequencer_startPtr + .asFunction, int)>(); + + int ffi_sequencer_stop( + ffi.Pointer opaque_hdl, + ) { + return _ffi_sequencer_stop( + opaque_hdl, + ); + } + + late final _ffi_sequencer_stopPtr = + _lookup)>>( + 'ffi_sequencer_stop'); + late final _ffi_sequencer_stop = + _ffi_sequencer_stopPtr.asFunction)>(); + + int ffi_sequencer_ack_stop( + ffi.Pointer opaque_hdl, + ) { + return _ffi_sequencer_ack_stop( + opaque_hdl, + ); + } + + late final _ffi_sequencer_ack_stopPtr = + _lookup)>>( + 'ffi_sequencer_ack_stop'); + late final _ffi_sequencer_ack_stop = _ffi_sequencer_ack_stopPtr + .asFunction)>(); + + int ffi_sequencer_trigger( + ffi.Pointer opaque_hdl, + ) { + return _ffi_sequencer_trigger( + opaque_hdl, + ); + } + + late final _ffi_sequencer_triggerPtr = + _lookup)>>( + 'ffi_sequencer_trigger'); + late final _ffi_sequencer_trigger = _ffi_sequencer_triggerPtr + .asFunction)>(); + + int ffi_periodic_trigger( + ffi.Pointer opaque_hdl, + ) { + return _ffi_periodic_trigger( + opaque_hdl, + ); + } + + late final _ffi_periodic_triggerPtr = + _lookup)>>( + 'ffi_periodic_trigger'); + late final _ffi_periodic_trigger = _ffi_periodic_triggerPtr + .asFunction)>(); + + ffi.Pointer ffi_properties_request_handle( + ffi.Pointer opaque_hdl, + ffi.Pointer cpIdArg, + ) { + return _ffi_properties_request_handle( + opaque_hdl, + cpIdArg, + ); + } + + late final _ffi_properties_request_handlePtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer)>>('ffi_properties_request_handle'); + late final _ffi_properties_request_handle = + _ffi_properties_request_handlePtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer)>(); + + int ffi_properties_return_handle( + ffi.Pointer opaque_hdl, + ffi.Pointer prop_hdl, + ) { + return _ffi_properties_return_handle( + opaque_hdl, + prop_hdl, + ); + } + + late final _ffi_properties_return_handlePtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer, + ffi.Pointer)>>('ffi_properties_return_handle'); + late final _ffi_properties_return_handle = + _ffi_properties_return_handlePtr.asFunction< + int Function( + ffi.Pointer, ffi.Pointer)>(); + + int ffi_properties_number_properties( + ffi.Pointer opaque_hdl, + ffi.Pointer prop_hdl, + ) { + return _ffi_properties_number_properties( + opaque_hdl, + prop_hdl, + ); + } + + late final _ffi_properties_number_propertiesPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer, ffi.Pointer)>>( + 'ffi_properties_number_properties'); + late final _ffi_properties_number_properties = + _ffi_properties_number_propertiesPtr.asFunction< + int Function( + ffi.Pointer, ffi.Pointer)>(); + + ffi.Pointer ffi_properties_descriptor_property_lin( + ffi.Pointer opaque_hdl, + ffi.Pointer opaque_prop, + int id_prop, + int descr_depth, + ) { + return _ffi_properties_descriptor_property_lin( + opaque_hdl, + opaque_prop, + id_prop, + descr_depth, + ); + } + + late final _ffi_properties_descriptor_property_linPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Int32, + ffi.Int32)>>('ffi_properties_descriptor_property_lin'); + late final _ffi_properties_descriptor_property_lin = + _ffi_properties_descriptor_property_linPtr.asFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer, int, int)>(); + + ffi.Pointer ffi_properties_descriptor_property_descr( + ffi.Pointer opaque_hdl, + ffi.Pointer opaque_prop, + ffi.Pointer descror, + int descr_depth, + ) { + return _ffi_properties_descriptor_property_descr( + opaque_hdl, + opaque_prop, + descror, + descr_depth, + ); + } + + late final _ffi_properties_descriptor_property_descrPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Int32)>>('ffi_properties_descriptor_property_descr'); + late final _ffi_properties_descriptor_property_descr = + _ffi_properties_descriptor_property_descrPtr.asFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer, ffi.Pointer, int)>(); + + ffi.Pointer ffi_properties_get_string_list( + ffi.Pointer opaque_hdl, + ffi.Pointer opaque_prop, + ffi.Pointer desror, + int contentOnly, + int offset, + ) { + return _ffi_properties_get_string_list( + opaque_hdl, + opaque_prop, + desror, + contentOnly, + offset, + ); + } + + late final _ffi_properties_get_string_listPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Int32, + ffi.Int32)>>('ffi_properties_get_string_list'); + late final _ffi_properties_get_string_list = + _ffi_properties_get_string_listPtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + int, + int)>(); + + ffi.Pointer ffi_properties_get_selection_list( + ffi.Pointer opaque_hdl, + ffi.Pointer opaque_prop, + ffi.Pointer desror, + int contentOnly, + int offset, + ffi.Pointer fillSelected, + ffi.Pointer fillSelectable, + ffi.Pointer fillExclusive, + int numEntries, + int numFill, + ) { + return _ffi_properties_get_selection_list( + opaque_hdl, + opaque_prop, + desror, + contentOnly, + offset, + fillSelected, + fillSelectable, + fillExclusive, + numEntries, + numFill, + ); + } + + late final _ffi_properties_get_selection_listPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Int32, + ffi.Int32, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Uint64, + ffi.Uint64)>>('ffi_properties_get_selection_list'); + late final _ffi_properties_get_selection_list = + _ffi_properties_get_selection_listPtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + int, + int, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + int, + int)>(); + + int ffi_properties_get_content_property_fld( + ffi.Pointer opaque_hdl, + ffi.Pointer opaque_prop, + ffi.Pointer desror, + int contentOnly, + int offset, + ffi.Pointer fld, + int format, + int numEntries, + ) { + return _ffi_properties_get_content_property_fld( + opaque_hdl, + opaque_prop, + desror, + contentOnly, + offset, + fld, + format, + numEntries, + ); + } + + late final _ffi_properties_get_content_property_fldPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Int32, + ffi.Int32, + ffi.Pointer, + ffi.Int32, + ffi.Uint64)>>('ffi_properties_get_content_property_fld'); + late final _ffi_properties_get_content_property_fld = + _ffi_properties_get_content_property_fldPtr.asFunction< + int Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + int, + int, + ffi.Pointer, + int, + int)>(); + + ffi.Pointer ffi_properties_get_string( + ffi.Pointer opaque_hdl, + ffi.Pointer opaque_prop, + ffi.Pointer desror, + ) { + return _ffi_properties_get_string( + opaque_hdl, + opaque_prop, + desror, + ); + } + + late final _ffi_properties_get_stringPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer)>>('ffi_properties_get_string'); + late final _ffi_properties_get_string = + _ffi_properties_get_stringPtr.asFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer, ffi.Pointer)>(); + + ffi.Pointer ffi_properties_get_value_in_range( + ffi.Pointer opaque_hdl, + ffi.Pointer opaque_prop, + ffi.Pointer desror, + int contentOnly, + int offset, + ffi.Pointer fld, + int numEntries, + ) { + return _ffi_properties_get_value_in_range( + opaque_hdl, + opaque_prop, + desror, + contentOnly, + offset, + fld, + numEntries, + ); + } + + late final _ffi_properties_get_value_in_rangePtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Int32, + ffi.Uint64, + ffi.Pointer, + ffi.Uint64)>>('ffi_properties_get_value_in_range'); + late final _ffi_properties_get_value_in_range = + _ffi_properties_get_value_in_rangePtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + int, + int, + ffi.Pointer, + int)>(); + + int ffi_properties_set_content_property_fld( + ffi.Pointer opaque_hdl, + ffi.Pointer opaque_prop, + ffi.Pointer desror, + int contentOnly, + int offset, + ffi.Pointer fld, + int format, + int numEntries, + int reportSetMode, + ) { + return _ffi_properties_set_content_property_fld( + opaque_hdl, + opaque_prop, + desror, + contentOnly, + offset, + fld, + format, + numEntries, + reportSetMode, + ); + } + + late final _ffi_properties_set_content_property_fldPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Int32, + ffi.Int32, + ffi.Pointer, + ffi.Int32, + ffi.Uint64, + ffi.Int32)>>('ffi_properties_set_content_property_fld'); + late final _ffi_properties_set_content_property_fld = + _ffi_properties_set_content_property_fldPtr.asFunction< + int Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + int, + int, + ffi.Pointer, + int, + int, + int)>(); + + int ffi_properties_set_selection_list( + ffi.Pointer opaque_hdl, + ffi.Pointer opaque_prop, + ffi.Pointer desror, + int contentOnly, + int offset, + ffi.Pointer passSelected, + int numEntries, + int numOneEntry, + int reportSetMode, + ) { + return _ffi_properties_set_selection_list( + opaque_hdl, + opaque_prop, + desror, + contentOnly, + offset, + passSelected, + numEntries, + numOneEntry, + reportSetMode, + ); + } + + late final _ffi_properties_set_selection_listPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Int32, + ffi.Int32, + ffi.Pointer, + ffi.Uint64, + ffi.Uint64, + ffi.Int32)>>('ffi_properties_set_selection_list'); + late final _ffi_properties_set_selection_list = + _ffi_properties_set_selection_listPtr.asFunction< + int Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + int, + int, + ffi.Pointer, + int, + int, + int)>(); + + int ffi_properties_set_value_range( + ffi.Pointer opaque_hdl, + ffi.Pointer opaque_prop, + ffi.Pointer desror, + int contentOnly, + int offset, + ffi.Pointer fld, + int numEntries, + double minVal, + double maxVal, + int reportSetMode, + ) { + return _ffi_properties_set_value_range( + opaque_hdl, + opaque_prop, + desror, + contentOnly, + offset, + fld, + numEntries, + minVal, + maxVal, + reportSetMode, + ); + } + + late final _ffi_properties_set_value_rangePtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Int32, + ffi.Int32, + ffi.Pointer, + ffi.Uint64, + ffi.Double, + ffi.Double, + ffi.Int32)>>('ffi_properties_set_value_range'); + late final _ffi_properties_set_value_range = + _ffi_properties_set_value_rangePtr.asFunction< + int Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + int, + int, + ffi.Pointer, + int, + double, + double, + int)>(); + + int ffi_properties_set_single_string( + ffi.Pointer opaque_hdl, + ffi.Pointer opaque_prop, + ffi.Pointer desror, + ffi.Pointer txt, + int reportSetMode, + ) { + return _ffi_properties_set_single_string( + opaque_hdl, + opaque_prop, + desror, + txt, + reportSetMode, + ); + } + + late final _ffi_properties_set_single_stringPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Int32)>>('ffi_properties_set_single_string'); + late final _ffi_properties_set_single_string = + _ffi_properties_set_single_stringPtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer, ffi.Pointer, int)>(); + + int ffi_properties_set_collect_start( + ffi.Pointer opaque_hdl, + ffi.Pointer prop_hdl, + ) { + return _ffi_properties_set_collect_start( + opaque_hdl, + prop_hdl, + ); + } + + late final _ffi_properties_set_collect_startPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer, ffi.Pointer)>>( + 'ffi_properties_set_collect_start'); + late final _ffi_properties_set_collect_start = + _ffi_properties_set_collect_startPtr.asFunction< + int Function( + ffi.Pointer, ffi.Pointer)>(); + + int ffi_properties_set_collect_stop( + ffi.Pointer opaque_hdl, + ffi.Pointer prop_hdl, + ) { + return _ffi_properties_set_collect_stop( + opaque_hdl, + prop_hdl, + ); + } + + late final _ffi_properties_set_collect_stopPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer, ffi.Pointer)>>( + 'ffi_properties_set_collect_stop'); + late final _ffi_properties_set_collect_stop = + _ffi_properties_set_collect_stopPtr.asFunction< + int Function( + ffi.Pointer, ffi.Pointer)>(); + + int ffi_properties_string_to_array( + ffi.Pointer txt, + ffi.Pointer fldBuffer, + int offset, + int numElements, + int formatElements, + ) { + return _ffi_properties_string_to_array( + txt, + fldBuffer, + offset, + numElements, + formatElements, + ); + } + + late final _ffi_properties_string_to_arrayPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer, + ffi.Pointer, + ffi.Int32, + ffi.Int32, + ffi.Int32)>>('ffi_properties_string_to_array'); + late final _ffi_properties_string_to_array = + _ffi_properties_string_to_arrayPtr.asFunction< + int Function( + ffi.Pointer, ffi.Pointer, int, int, int)>(); + + ffi.Pointer ffi_component_description( + ffi.Pointer opaque_hdl, + ffi.Pointer address, + ) { + return _ffi_component_description( + opaque_hdl, + address, + ); + } + + late final _ffi_component_descriptionPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer)>>('ffi_component_description'); + late final _ffi_component_description = + _ffi_component_descriptionPtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer)>(); + + int ffi_component_number_components( + ffi.Pointer opaque_hdl, + ffi.Pointer address, + ) { + return _ffi_component_number_components( + opaque_hdl, + address, + ); + } + + late final _ffi_component_number_componentsPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer, ffi.Pointer)>>( + 'ffi_component_number_components'); + late final _ffi_component_number_components = + _ffi_component_number_componentsPtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer)>(); + + int ffi_component_number_devices( + ffi.Pointer opaque_hdl, + ffi.Pointer address, + ) { + return _ffi_component_number_devices( + opaque_hdl, + address, + ); + } + + late final _ffi_component_number_devicesPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer, + ffi.Pointer)>>('ffi_component_number_devices'); + late final _ffi_component_number_devices = + _ffi_component_number_devicesPtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer)>(); + + ffi.Pointer ffi_component_capabilities_device( + ffi.Pointer opaque_hdl, + ffi.Pointer address, + int idx, + ) { + return _ffi_component_capabilities_device( + opaque_hdl, + address, + idx, + ); + } + + late final _ffi_component_capabilities_devicePtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Int32)>>('ffi_component_capabilities_device'); + late final _ffi_component_capabilities_device = + _ffi_component_capabilities_devicePtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer, int)>(); + + ffi.Pointer ffi_component_selection_option( + ffi.Pointer opaque_hdl, + ffi.Pointer address, + int idx, + ) { + return _ffi_component_selection_option( + opaque_hdl, + address, + idx, + ); + } + + late final _ffi_component_selection_optionPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Int32)>>('ffi_component_selection_option'); + late final _ffi_component_selection_option = + _ffi_component_selection_optionPtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer, int)>(); + + ffi.Pointer ffi_component_slot_allocation( + ffi.Pointer opaque_hdl, + ffi.Pointer address, + ) { + return _ffi_component_slot_allocation( + opaque_hdl, + address, + ); + } + + late final _ffi_component_slot_allocationPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer)>>('ffi_component_slot_allocation'); + late final _ffi_component_slot_allocation = + _ffi_component_slot_allocationPtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer)>(); + + ffi.Pointer ffi_select_component( + ffi.Pointer opaque_hdl, + int cpTp, + int slotid, + int slotSubId, + int idx, + ) { + return _ffi_select_component( + opaque_hdl, + cpTp, + slotid, + slotSubId, + idx, + ); + } + + late final _ffi_select_componentPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Int32, + ffi.Int32, + ffi.Int32, + ffi.Uint64)>>('ffi_select_component'); + late final _ffi_select_component = _ffi_select_componentPtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, int, int, int, int)>(); + + int ffi_activate_selected_component( + ffi.Pointer opaque_hdl, + ffi.Pointer address, + ) { + return _ffi_activate_selected_component( + opaque_hdl, + address, + ); + } + + late final _ffi_activate_selected_componentPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer, ffi.Pointer)>>( + 'ffi_activate_selected_component'); + late final _ffi_activate_selected_component = + _ffi_activate_selected_componentPtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer)>(); + + int ffi_deactivate_selected_component( + ffi.Pointer opaque_hdl, + ffi.Pointer address, + ) { + return _ffi_deactivate_selected_component( + opaque_hdl, + address, + ); + } + + late final _ffi_deactivate_selected_componentPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer, ffi.Pointer)>>( + 'ffi_deactivate_selected_component'); + late final _ffi_deactivate_selected_component = + _ffi_deactivate_selected_componentPtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer)>(); + + int ffi_unselect_selected_component( + ffi.Pointer opaque_hdl, + ffi.Pointer address, + ) { + return _ffi_unselect_selected_component( + opaque_hdl, + address, + ); + } + + late final _ffi_unselect_selected_componentPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer, ffi.Pointer)>>( + 'ffi_unselect_selected_component'); + late final _ffi_unselect_selected_component = + _ffi_unselect_selected_componentPtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer)>(); + + int ffi_config_save( + ffi.Pointer opaque_hdl, + ) { + return _ffi_config_save( + opaque_hdl, + ); + } + + late final _ffi_config_savePtr = + _lookup)>>( + 'ffi_config_save'); + late final _ffi_config_save = + _ffi_config_savePtr.asFunction)>(); + + int ffi_get_compile_flags( + ffi.Pointer opaque_hdl, + ffi.Pointer dataTpFormat, + ffi.Pointer num32BitWordsSelection, + ) { + return _ffi_get_compile_flags( + opaque_hdl, + dataTpFormat, + num32BitWordsSelection, + ); + } + + late final _ffi_get_compile_flagsPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>>('ffi_get_compile_flags'); + late final _ffi_get_compile_flags = _ffi_get_compile_flagsPtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); + + int ffi_set_operation_flags( + ffi.Pointer opaque_hdl, + int reportSet, + int reportSyncDirectArg, + ) { + return _ffi_set_operation_flags( + opaque_hdl, + reportSet, + reportSyncDirectArg, + ); + } + + late final _ffi_set_operation_flagsPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer, ffi.Int32, + ffi.Int32)>>('ffi_set_operation_flags'); + late final _ffi_set_operation_flags = _ffi_set_operation_flagsPtr + .asFunction, int, int)>(); + + int ffi_translate_enum_string( + ffi.Pointer opaque_hdl, + ffi.Pointer typeName, + ffi.Pointer typeSelect, + ) { + return _ffi_translate_enum_string( + opaque_hdl, + typeName, + typeSelect, + ); + } + + late final _ffi_translate_enum_stringPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>>('ffi_translate_enum_string'); + late final _ffi_translate_enum_string = + _ffi_translate_enum_stringPtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); + + int ffi_lookup_component_class( + ffi.Pointer opaque_hdl, + int cpType, + ) { + return _ffi_lookup_component_class( + opaque_hdl, + cpType, + ); + } + + late final _ffi_lookup_component_classPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer, ffi.Int32)>>('ffi_lookup_component_class'); + late final _ffi_lookup_component_class = _ffi_lookup_component_classPtr + .asFunction, int)>(); + + int ffi_preload_dll( + ffi.Pointer> dllhandle, + ffi.Pointer dllPath, + ) { + return _ffi_preload_dll( + dllhandle, + dllPath, + ); + } + + late final _ffi_preload_dllPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer>, + ffi.Pointer)>>('ffi_preload_dll'); + late final _ffi_preload_dll = _ffi_preload_dllPtr.asFunction< + int Function( + ffi.Pointer>, ffi.Pointer)>(); + + /// ! Sync and asynchronous callback function to re-enter the host + int ffi_host_request_command_fwd( + ffi.Pointer opaque_hdl, + ffi.Pointer request_command, + int async1, + ) { + return _ffi_host_request_command_fwd( + opaque_hdl, + request_command, + async1, + ); + } + + late final _ffi_host_request_command_fwdPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer, ffi.Pointer, + ffi.Int32)>>('ffi_host_request_command_fwd'); + late final _ffi_host_request_command_fwd = + _ffi_host_request_command_fwdPtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer, int)>(); + + int ffi_host_request_command_reschedule( + ffi.Pointer request_command, + ) { + return _ffi_host_request_command_reschedule( + request_command, + ); + } + + late final _ffi_host_request_command_reschedulePtr = + _lookup)>>( + 'ffi_host_request_command_reschedule'); + late final _ffi_host_request_command_reschedule = + _ffi_host_request_command_reschedulePtr + .asFunction)>(); + + ffi.Pointer ffi_transfer_command( + ffi.Pointer opaque_hdl, + ffi.Pointer cmd, + int json_out, + ) { + return _ffi_transfer_command( + opaque_hdl, + cmd, + json_out, + ); + } + + late final _ffi_transfer_commandPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer, ffi.Int32)>>('ffi_transfer_command'); + late final _ffi_transfer_command = _ffi_transfer_commandPtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer, int)>(); + + ffi.Pointer ffi_encode_component_identification( + int cpTp, + int slotid, + int slotsubid, + ) { + return _ffi_encode_component_identification( + cpTp, + slotid, + slotsubid, + ); + } + + late final _ffi_encode_component_identificationPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Int32, ffi.Int32, + ffi.Int32)>>('ffi_encode_component_identification'); + late final _ffi_encode_component_identification = + _ffi_encode_component_identificationPtr + .asFunction Function(int, int, int)>(); + + int ffi_last_error() { + return _ffi_last_error(); + } + + late final _ffi_last_errorPtr = + _lookup>('ffi_last_error'); + late final _ffi_last_error = _ffi_last_errorPtr.asFunction(); + + /// ! Delete a buffer. The tp argument specifies the type + int ffi_host_delete( + ffi.Pointer ptr, + int tp, + ) { + return _ffi_host_delete( + ptr, + tp, + ); + } + + late final _ffi_host_deletePtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer, ffi.Int32)>>('ffi_host_delete'); + late final _ffi_host_delete = _ffi_host_deletePtr + .asFunction, int)>(); + + /// ! Test function to run a test command via ffi bridge + int ffi_simple_call() { + return _ffi_simple_call(); + } + + late final _ffi_simple_callPtr = + _lookup>('ffi_simple_call'); + late final _ffi_simple_call = + _ffi_simple_callPtr.asFunction(); +} + +abstract class ffiCallbackIds { + static const int JVX_FFI_CALLBACK_ASYNC_REPORT_TEXT = 0; + static const int JVX_FFI_CALLBACK_SYNC_REPORT_INTERNALS_CHANGED = 1; + static const int JVX_FFI_CALLBACK_ASYNC_REPORT_COMMAND_REQUEST = 2; + static const int JVX_FFI_CALLBACK_REQUEST_COMMAND = 3; +} + +abstract class ffiDeleteDatatype { + static const int JVX_DELETE_DATATYPE_ASYNC_CALLBACK = 0; + static const int JVX_DELETE_DATATYPE_CHAR_ARRAY = 1; + static const int JVX_DELETE_DATATYPE_INTERNAL_CHANGED = 2; + static const int JVX_DELETE_DATATYPE_REQUEST_COMMAND = 3; + static const int JVX_DELETE_DATATYPE_ONE_PROCESS = 4; + static const int JVX_DELETE_DATATYPE_ONE_PROP_HANDLER = 5; + static const int JVX_DELETE_DATATYPE_ONE_SEQ_EVENT = 6; + static const int JVX_DELETE_DATATYPE_ONE_COMPONENT_IDENT = 7; + static const int JVX_DELETE_DATATYPE_ONE_PROPERTY_DESCRIPTOR_FULL_PLUS = 8; + static const int JVX_DELETE_DATATYPE_PROCESS_READY = 9; + static const int JVX_DELETE_DATATYPE_PROPERTY_STRING_LIST = 10; + static const int JVX_DELETE_DATATYPE_DEVICE_CAPS = 11; + static const int JVX_DELETE_DATATYPE_SELECTION_OPTION = 12; + static const int JVX_DELETE_DATATYPE_SS_LIST = 13; + static const int JVX_DELETE_DATATYPE_VALUE_IN_RANGE = 14; +} + +class one_property_min extends ffi.Struct { + @ffi.Int32() + external int valid_parts; + + @ffi.Int32() + external int category; + + @ffi.Int32() + external int globalIdx; +} + +class one_property_core extends ffi.Struct { + external one_property_min pmin; + + @ffi.Int32() + external int format; + + @ffi.Int32() + external int num; + + @ffi.Int32() + external int accessType; + + @ffi.Int32() + external int decTp; + + @ffi.Int32() + external int ctxt; +} + +class one_property_control extends ffi.Struct { + external one_property_core core; + + @ffi.Int32() + external int allowedStateMask; + + @ffi.Int32() + external int allowedThreadingMask; + + @ffi.Int32() + external int isValid; + + @ffi.Int32() + external int invalidateOnStateSwitch; + + @ffi.Int32() + external int invalidateOnTest; + + @ffi.Int32() + external int installable; +} + +class one_property_full extends ffi.Struct { + external one_property_control ctrl; + + external ffi.Pointer name; + + external ffi.Pointer description; + + external ffi.Pointer descriptor; +} + +class one_property_full_plus extends ffi.Struct { + external one_property_full full; + + @ffi.Uint64() + external int accessFlags; + + @ffi.Uint32() + external int configModeFlags; +} + +class component_ident extends ffi.Struct { + @ffi.Int32() + external int tp; + + @ffi.Uint64() + external int slotid; + + @ffi.Uint64() + external int slotsubid; + + @ffi.Uint64() + external int uId; +} + +class report_internal_changed extends ffi.Struct { + external component_ident from; + + external component_ident to; + + @ffi.Int32() + external int cat; + + @ffi.Uint64() + external int propId; + + @ffi.Int32() + external int onlyContent; + + @ffi.Uint64() + external int offset_start; + + @ffi.Uint64() + external int numElements; + + @ffi.Int32() + external int callpurpose; +} + +class retOpaqueHandle extends ffi.Struct { + @ffi.Int32() + external int err_code; + + external ffi.Pointer opaque_hdl; +} + +class report_callback extends ffi.Struct { + @ffi.Int32() + external int callback_id; + + @ffi.Int32() + external int callback_subid; + + external ffi.Pointer load_fld; + + @ffi.Int64() + external int sz_fld; +} + +class one_process extends ffi.Struct { + @ffi.Int32() + external int uId; + + external ffi.Pointer name; + + @ffi.Int32() + external int category; + + external ffi.Pointer iterator; +} + +class one_property_comp extends ffi.Struct { + external component_ident cpId; + + external ffi.Pointer obj_ptr; + + external ffi.Pointer prop_ptr; +} + +class one_sequencer_event extends ffi.Struct { + @ffi.Int32() + external int event_mask; + + external ffi.Pointer description; + + @ffi.Int32() + external int sequenceId; + + @ffi.Int32() + external int stepId; + + @ffi.Int32() + external int qtp; + + @ffi.Int32() + external int setp; + + @ffi.Int32() + external int fId; + + @ffi.Int32() + external int seq_state_id; + + @ffi.Int32() + external int statSeq; + + @ffi.Int32() + external int indicateFirstError; +} + +class process_ready extends ffi.Struct { + @ffi.Int32() + external int err; + + external ffi.Pointer reason_if_not; +} + +class func_pointers extends ffi.Struct { + external ffi.Pointer< + ffi.NativeFunction)>> + cb_sync; +} + +class one_property_string_list extends ffi.Struct { + @ffi.Int32() + external int nEntries; + + external ffi.Pointer> pEntries; +} + +class device_capabilities extends ffi.Struct { + external ffi.Pointer description; + + external ffi.Pointer descriptor; + + @ffi.Uint64() + external int caps; + + @ffi.Int32() + external int flow; + + @ffi.Uint32() + external int flags; + + @ffi.Int32() + external int selectable; + + @ffi.Int32() + external int slotsubid; + + @ffi.Int32() + external int stat; +} + +class selection_option extends ffi.Struct { + external ffi.Pointer description; + + external ffi.Pointer descriptor; +} + +class ss_list extends ffi.Struct { + external ffi.Pointer slots; + + external ffi.Pointer subslots; + + @ffi.Int32() + external int num; +} + +class one_property_value_in_range extends ffi.Struct { + @ffi.Double() + external double minVal; + + @ffi.Double() + external double maxVal; +} + +typedef jvxUInt32 = ffi.Uint32; diff --git a/flutter/ayf_ffihost/lib/properties/ayf_properties_from_native.dart b/flutter/ayf_ffihost/lib/properties/ayf_properties_from_native.dart new file mode 100644 index 00000000..c3e66ff3 --- /dev/null +++ b/flutter/ayf_ffihost/lib/properties/ayf_properties_from_native.dart @@ -0,0 +1,472 @@ +import 'dart:typed_data'; +import 'dart:ffi'; +import 'dart:math'; +import 'package:ffi/ffi.dart'; +import 'package:fixnum/fixnum.dart' as fn; +import '../native-ffi/generated/ayf_ffi_gen_bind.dart'; +import 'package:ayf_pack/ayf_pack.dart'; +import 'package:ayf_pack_native/ayf_pack_native.dart'; + +import '../backend/ayf_backend_adapter_native.dart'; + +class AudYoFloPropertyContentFromNative { + // ======================================================================== + // ======================================================================== + + static AudYoFloPropertyContentBackend createPropertyFromNative( + AudYoFloBackendAdapterIf ref, + String descriptor, + AudYoFloNativePropertyHandle propRef, + AudYoFloCompileFlags flags, int numDigits) { + // ================================================================= + // Get the descriptor from the ffi backend + // ================================================================= + + Pointer descror = descriptor.toNativeUtf8(); + Pointer propDescrPtr = propRef.natLib + .ffi_properties_descriptor_property_descr( + propRef.opaqueHost, + propRef.propRef, + descror.cast(), + jvxPropertyDescriptorEnum.JVX_PROPERTY_DESCRIPTOR_FULL_PLUS); + + // A nullptr indicates an error + if (propDescrPtr == nullptr) { + // Create a property container anyway and set the error flag + AudYoFloPropertyContentBackend retVal = + AudYoFloPropertyContentBackend(propRef.cpId, descriptor: descriptor); + retVal.last_error = propRef.natLib.ffi_last_error(); + + retVal.invalidateDescriptor(true); + return retVal; + } + + // Successful descriptor return + one_property_full_plus propDescrRef = propDescrPtr.ref; + jvxDataFormatEnum format = + jvxDataFormatEEnum.fromInt(propDescrRef.full.ctrl.core.format); + + // Allocate the variable + AudYoFloPropertyContentBackend retVal = allocatePropertyContent( + propRef.cpId, format, propDescrRef.full.ctrl.core.num, flags, numDigits); + + updatePropertyDescriptorFromNative(ref, retVal, propRef, flags, false, + ffiPtr: propDescrPtr); + + return retVal; + } + + // ======================================================================== + // ======================================================================== + + static int updatePropertyDescriptorFromNative( + AudYoFloBackendAdapterIf ref, + AudYoFloPropertyContentBackend prop, + AudYoFloNativePropertyHandle propRef, + AudYoFloCompileFlags flags, + bool checkFormatNum, + {Pointer? ffiPtr = null}) { + // ================================================================= + // Get the descriptor from the ffi backend + // ================================================================= + Pointer propDescrPtr = nullptr; + prop.last_error = jvxErrorType.JVX_NO_ERROR; + + if (ffiPtr != null) { + propDescrPtr = ffiPtr; + } else { + Pointer descror = prop.descriptor.toNativeUtf8(); + propDescrPtr = propRef.natLib.ffi_properties_descriptor_property_descr( + propRef.opaqueHost, + propRef.propRef, + descror.cast(), + jvxPropertyDescriptorEnum.JVX_PROPERTY_DESCRIPTOR_FULL_PLUS); + } + + // A nullptr indicates an error + if (propDescrPtr == nullptr) { + // Create a property container anyway and set the error flag + prop.last_error = propRef.natLib.ffi_last_error(); + } else { + // Do not forget to set the success to "NO ERROR"! + + // Successful descriptor return + one_property_full_plus propDescrRef = propDescrPtr.ref; + + // checkFormatNum checks if an update is required compared to what is already allocated + // This affects only field entries where the field of this property may + // vary from update to update. It is only necessary to call the check part + // if the property was created before. If checkFormatNum is false, this is + // due to the fact that the property has very recently be created. + if (checkFormatNum) { + jvxDataFormatEnum format = + jvxDataFormatEEnum.fromInt(propDescrRef.full.ctrl.core.format); + + // Changing the dataformat is not supported + if (prop.jvxFormat != format) { + String t1 = prop.jvxFormat.toString(); + String t2 = format.toString(); + String t3 = prop.descriptor; + print( + 'Error when rereading property description for property <$t3>: format has changed: old: $t1, new: $t2'); + } + + // Changing the number of entries, however, is ok and often used! + // Check if the variable has been allocated with a different configuration + if (prop.num != propDescrRef.full.ctrl.core.num) { + // Dealloc + if (prop is AudYoFloPropertyMultiContentNative) { + prop.rebuild(propDescrRef.full.ctrl.core.num); + } + if (prop is AudYoFloPropertySelectionListNative) { + prop.resize(propDescrRef.full.ctrl.core.num, true, false); + } + } + } + + // Min descr + prop.category = jvxPropertyCategoryTypeEEnum + .fromInt(propDescrRef.full.ctrl.core.pmin.category); + prop.globalIdx = propDescrRef.full.ctrl.core.pmin.globalIdx; + + // Core desc + /* + * Set in the alloc function call + retVal.format = + jvxDataFormatEEnum.fromInt(propDescrRef.full.ctrl.core.format); + retVal.num = propDescrRef.full.ctrl.core.num; + */ + prop.decoderHintType = jvxPropertyDecoderHintTypeEEnum + .fromInt(propDescrRef.full.ctrl.core.decTp); + prop.ctxt = + jvxPropertyContexteEEnum.fromInt(propDescrRef.full.ctrl.core.ctxt); + prop.accessType = jvxPropertyAccessTypeEEnum + .fromInt(propDescrRef.full.ctrl.core.accessType); + + // Control desc + prop.isValid = (propDescrRef.full.ctrl.isValid != 0); + prop.reqInvalidateOnStateSwitch = jvxPropertyInvalidateTypeEEnum + .fromInt(propDescrRef.full.ctrl.invalidateOnStateSwitch); + prop.reqInvalidateOnTest = jvxPropertyInvalidateTypeEEnum + .fromInt(propDescrRef.full.ctrl.invalidateOnTest); + prop.allowedStateMask.fld = + fn.Int32(propDescrRef.full.ctrl.allowedStateMask); + prop.installable = (propDescrRef.full.ctrl.installable != 0); + prop.allowedThreadingMask.fld = + fn.Int32(propDescrRef.full.ctrl.allowedThreadingMask); + + // Full desc + prop.name = propDescrRef.full.name.cast().toDartString(); + prop.description = + propDescrRef.full.description.cast().toDartString(); + prop.descriptor = + propDescrRef.full.descriptor.cast().toDartString(); + + prop.accessFlags.fld = fn.Int64(propDescrRef.accessFlags); + prop.configFlags.fld = fn.Int32(propDescrRef.configModeFlags); + + propRef.natLib.ffi_host_delete(propDescrRef.full.name.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + propRef.natLib.ffi_host_delete(propDescrRef.full.description.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + propRef.natLib.ffi_host_delete(propDescrRef.full.descriptor.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + + if (ffiPtr == null) { + propRef.natLib.ffi_host_delete( + propDescrPtr.cast(), + ffiDeleteDatatype + .JVX_DELETE_DATATYPE_ONE_PROPERTY_DESCRIPTOR_FULL_PLUS); + } + } + + prop.validateDescriptor(); + return prop.last_error; + } + + // ======================================================================== + // ======================================================================== + + static int updatePropertyContentFromNative( + AudYoFloPropertyContentBackend prop, + String descriptor, + AudYoFloNativePropertyHandle propRef, + {int contentOnly = 0, + int offset = 0, + int numEntriesLimit = -1}) { + prop.last_error = jvxErrorType.JVX_NO_ERROR; + + // Check that descriptor is ready + if (!prop.checkValidDescriptor()) { + prop.last_error = jvxErrorType.JVX_ERROR_NOT_READY; + } else { + // Descriptor is ready + switch (prop.jvxFormat) { + case jvxDataFormatEnum.JVX_DATAFORMAT_STRING_LIST: + + // Datatype string list, check that variable is of right format + if (prop is AudYoFloPropertyMultiStringBackend) { + // Cast it directly + AudYoFloPropertyMultiStringBackend propStrLst = prop; + + // Return the ffi struct from backend + Pointer retVal = propRef.natLib + .ffi_properties_get_string_list( + propRef.opaqueHost, + propRef.propRef, + prop.descriptor.toNativeUtf8().cast(), + contentOnly, + offset); + if (retVal != nullptr) { + // It is a valid string list + one_property_string_list retValRef = retVal.ref; + + // It seems there is no resize function in the List<> + propStrLst.entries.clear(); + + // Copy all string from the list + for (int i = 0; i < retValRef.nEntries; i++) { + Pointer ptrStr = retValRef.pEntries[i]; + if (ptrStr != nullptr) { + propStrLst.entries.add(ptrStr.cast().toDartString()); + } else { + propStrLst.entries.add(''); + } + } + int errCodeLocal = propRef.natLib.ffi_host_delete( + retVal.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_PROPERTY_STRING_LIST); + assert(errCodeLocal == jvxErrorType.JVX_NO_ERROR); + } + } else { + prop.last_error = jvxErrorType.JVX_ERROR_INTERNAL; + } + break; + + case jvxDataFormatEnum.JVX_DATAFORMAT_SELECTION_LIST: + + // Datatype string list, check that variable is of right format + if (prop is AudYoFloPropertySelectionListBackend) { + // Cast it directly + AudYoFloPropertySelectionListBackend propSelLst = + prop as AudYoFloPropertySelectionListBackend; + + int numEntries = propSelLst.parpropms.num - offset; + if (numEntriesLimit > 0) { + numEntries = min(numEntries, numEntriesLimit); + } + if (numEntries > 0) { + Pointer retVal = nullptr; + if ((propSelLst.selection is AudYoFloBitFieldNative) && + (propSelLst.selectable is AudYoFloBitFieldNative) && + (propSelLst.exclusive is AudYoFloBitFieldNative)) { + AudYoFloBitFieldNative sel = + propSelLst.selection as AudYoFloBitFieldNative; + AudYoFloBitFieldNative sela = + propSelLst.selectable as AudYoFloBitFieldNative; + AudYoFloBitFieldNative excl = + propSelLst.exclusive as AudYoFloBitFieldNative; + + if (sel.raw != nullptr) { + Pointer targetPtr = sel.raw; + int address = targetPtr.address; + address += + offset * sel.compileNum32BitBitfield * sizeOf(); + targetPtr = Pointer.fromAddress(address); + + // Return the ffi struct from backend + retVal = propRef.natLib.ffi_properties_get_selection_list( + propRef.opaqueHost, + propRef.propRef, + prop.descriptor.toNativeUtf8().cast(), + contentOnly, + offset, + targetPtr, + sela.raw, + excl.raw, + numEntries, + propSelLst.compileNum32BitBitfield); + } + if (retVal == nullptr) { + prop.last_error = propRef.natLib.ffi_last_error(); + } else { + // It is a valid string list + one_property_string_list retValRef = retVal.ref; + + // It seems there is no resize function in the List<> + propSelLst.parpropms.entries.clear(); + + // Copy all string from the list + for (int i = 0; i < retValRef.nEntries; i++) { + Pointer ptrStr = retValRef.pEntries[i]; + if (ptrStr != nullptr) { + propSelLst.parpropms.entries + .add(ptrStr.cast().toDartString()); + } else { + propSelLst.parpropms.entries.add(''); + } + } + + int errCodeLocal = propRef.natLib.ffi_host_delete( + retVal.cast(), + ffiDeleteDatatype + .JVX_DELETE_DATATYPE_PROPERTY_STRING_LIST); + assert(errCodeLocal == jvxErrorType.JVX_NO_ERROR); + } + } else { + String nm = prop.descriptor; + print( + 'Property with descriptor $nm: Error reading field for property, field contains nullptr'); + prop.last_error = jvxErrorType.JVX_ERROR_INVALID_ARGUMENT; + } + } else { + String nm = prop.descriptor; + print( + 'Property with descriptor $nm: Error reading field for property, field has zero length'); + prop.last_error = jvxErrorType.JVX_ERROR_INVALID_ARGUMENT; + } + } + + break; + case jvxDataFormatEnum.JVX_DATAFORMAT_STRING: + if (prop is AudYoFloPropertySingleStringBackend) { + // Cast it directly + AudYoFloPropertySingleStringBackend propString = prop; + + // Return the ffi struct from backend + Pointer retVal = propRef.natLib.ffi_properties_get_string( + propRef.opaqueHost, + propRef.propRef, + prop.descriptor.toNativeUtf8().cast()); + if (retVal == nullptr) { + prop.last_error = propRef.natLib.ffi_last_error(); + } else { + // It is a valid string list + + propString.value = retVal.cast().toDartString(); + + int errCodeLocal = propRef.natLib.ffi_host_delete( + retVal.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + assert(errCodeLocal == jvxErrorType.JVX_NO_ERROR); + } + } + break; + + case jvxDataFormatEnum.JVX_DATAFORMAT_DATA: + case jvxDataFormatEnum.JVX_DATAFORMAT_64BIT_LE: + case jvxDataFormatEnum.JVX_DATAFORMAT_32BIT_LE: + case jvxDataFormatEnum.JVX_DATAFORMAT_16BIT_LE: + case jvxDataFormatEnum.JVX_DATAFORMAT_8BIT: + case jvxDataFormatEnum.JVX_DATAFORMAT_U64BIT_LE: + case jvxDataFormatEnum.JVX_DATAFORMAT_U32BIT_LE: + case jvxDataFormatEnum.JVX_DATAFORMAT_U16BIT_LE: + case jvxDataFormatEnum.JVX_DATAFORMAT_U8BIT: + case jvxDataFormatEnum.JVX_DATAFORMAT_SIZE: + + // ========================================================= + // Trying to cover all operations with one code fragment + // ========================================================= + if (prop is AudYoFloPropertyMultiContentNative) { + // Transfer all or less elements + int numEntries = prop.fldSz; + numEntries -= offset; + if (numEntriesLimit > 0) { + numEntries = min(numEntries, numEntriesLimit); + } + + if (numEntries > 0) { + if (prop.fldPtr != nullptr) { + int address = prop.fldPtr.address; + address += offset * prop.szNativeElement; + Pointer targetPtr = Pointer.fromAddress(address); + + prop.last_error = propRef.natLib + .ffi_properties_get_content_property_fld( + propRef.opaqueHost, + propRef.propRef, + prop.descriptor.toNativeUtf8().cast(), + contentOnly, + offset, + targetPtr, + prop.jvxFormat.index, + numEntries); + } else { + String nm = prop.descriptor; + print( + 'Property with descriptor $nm: Error reading field for property, field contains nullptr'); + prop.last_error = jvxErrorType.JVX_ERROR_INVALID_ARGUMENT; + } + } else { + String nm = prop.descriptor; + print( + 'Property with descriptor $nm: Error reading field for property, field has zero length'); + prop.last_error = jvxErrorType.JVX_ERROR_INVALID_ARGUMENT; + } + } + break; + + case jvxDataFormatEnum.JVX_DATAFORMAT_VALUE_IN_RANGE: + + // Datatype string list, check that variable is of right format + if (prop is AudYoFloPropertyValueInRangeNative) { + // Cast it directly + AudYoFloPropertyValueInRangeNative propValRange = prop; + + int numEntries = prop.fldSz; + numEntries -= offset; + if (numEntriesLimit > 0) { + numEntries = min(numEntries, numEntriesLimit); + } + + if (numEntries > 0) { + if (propValRange.fldPtr != nullptr) { + Pointer targetPtr = propValRange.fldPtr.cast(); + int address = targetPtr.address; + address += offset * propValRange.szNativeElement; + targetPtr = Pointer.fromAddress(address); + + // Return the ffi struct from backend + Pointer retVal = propRef.natLib + .ffi_properties_get_value_in_range( + propRef.opaqueHost, + propRef.propRef, + prop.descriptor.toNativeUtf8().cast(), + contentOnly, + offset, + targetPtr, + numEntries); + + prop.last_error = propRef.natLib.ffi_last_error(); + + if (contentOnly != 0) { + // It is a valid string list + one_property_value_in_range retValRef = retVal.ref; + propValRange.minVal = retValRef.minVal; + propValRange.maxVal = retValRef.maxVal; + + int errCodeLocal = propRef.natLib.ffi_host_delete( + retVal.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_VALUE_IN_RANGE); + assert(errCodeLocal == jvxErrorType.JVX_NO_ERROR); + } + } + } + } + + break; + default: + prop.last_error = jvxErrorType.JVX_ERROR_INVALID_FORMAT; + break; + } + } + + if (prop.last_error == jvxErrorType.JVX_NO_ERROR) { + prop.validateContent(); + } else { + prop.invalidateContent(true); + } + + return prop.last_error; + } +} diff --git a/flutter/ayf_ffihost/lib/systemstate/ayf_connected_processes.dart b/flutter/ayf_ffihost/lib/systemstate/ayf_connected_processes.dart new file mode 100644 index 00000000..b10ef116 --- /dev/null +++ b/flutter/ayf_ffihost/lib/systemstate/ayf_connected_processes.dart @@ -0,0 +1,73 @@ +import 'package:collection/collection.dart'; + +import 'package:ffi/ffi.dart'; +import 'dart:ffi'; +import '../native-ffi/generated/ayf_ffi_gen_bind.dart'; +import 'package:ayf_pack/ayf_pack.dart'; + +class AudYoFloBackendSpecificNative { + AudioYoFloNative natLib; + Pointer opaque_hdl; + Pointer itRef; + AudYoFloBackendSpecificNative(this.natLib, this.opaque_hdl, this.itRef); +} + +class AudYoFloOneComponentInProcessNative + extends AudYoFloOneComponentInProcess { + @override + void fill(dynamic beSpecific) { + AudYoFloBackendSpecificNative spec = + beSpecific as AudYoFloBackendSpecificNative; + var ret = JvxComponentIdentification(); + var numBranches = spec.natLib + .ffi_process_decode_iterator_context(spec.opaque_hdl, spec.itRef); + Pointer idPtr = spec.natLib + .ffi_process_decode_iterator_component(spec.opaque_hdl, spec.itRef); + Pointer modPtr = spec.natLib + .ffi_process_decode_iterator_module(spec.opaque_hdl, spec.itRef); + Pointer cnamePtr = spec.natLib + .ffi_process_decode_iterator_connector(spec.opaque_hdl, spec.itRef); + + if ((idPtr != nullptr) && (modPtr != nullptr)) { + nameConnector = cnamePtr.cast().toDartString(); + spec.natLib.ffi_host_delete(cnamePtr.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + + // Decode Module name + nameModule = modPtr.cast().toDartString(); + spec.natLib.ffi_host_delete(modPtr.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + + // Decode component ident + component_ident cpIdRef = idPtr.ref; + ret.cpTp = JvxComponentTypeEEnum.fromInt(cpIdRef.tp); + ret.slotid = cpIdRef.slotid; + ret.slotsubid = cpIdRef.slotsubid; + ret.uid = cpIdRef.uId; + spec.natLib.ffi_host_delete(idPtr.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_ONE_COMPONENT_IDENT); + + cpId = ret; + + Pointer descrPtr = + spec.natLib.ffi_component_description(spec.opaque_hdl, idPtr); + if (descrPtr != nullptr) { + nameComponent = (descrPtr.cast()).toDartString(); + spec.natLib.ffi_host_delete(descrPtr.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + } + } + + for (int ii = 0; ii < numBranches; ii++) { + Pointer itNext = spec.natLib + .ffi_process_iterator_next(spec.opaque_hdl, spec.itRef, ii); + if (itNext != nullptr) { + AudYoFloOneComponentInProcess connectTo = + AudYoFloOneComponentInProcessNative(); + connectTo.fill(AudYoFloBackendSpecificNative( + spec.natLib, spec.opaque_hdl, itNext)); + attach(connectTo); + } + } + } +} diff --git a/flutter/ayf_ffihost/lib/types/ayf_sequencer_types.dart b/flutter/ayf_ffihost/lib/types/ayf_sequencer_types.dart new file mode 100644 index 00000000..2eff5ec5 --- /dev/null +++ b/flutter/ayf_ffihost/lib/types/ayf_sequencer_types.dart @@ -0,0 +1,28 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; +import '../native-ffi/generated/ayf_ffi_gen_bind.dart'; +import 'package:ayf_pack/ayf_pack.dart'; + +class jvxSequencerEventTypeNative { + static jvxSequencerEventType decodeSequencerEventType( + Pointer evPtr) { + jvxSequencerEventType retVal = jvxSequencerEventType(); + one_sequencer_event ev = evPtr.ref; + + retVal.eventMask = ev.event_mask; + retVal.fId = ev.fId; + retVal.indicateFirstError = (ev.indicateFirstError != 0); + retVal.qtp = jvxSequencerQueueTypeEEnum.fromInt(ev.qtp); + retVal.seq_state_id = ev.seq_state_id; + retVal.sequenceId = ev.sequenceId; + retVal.setp = jvxSequencerElementTypeEEnum.fromInt(ev.setp); + retVal.stepId = ev.stepId; + retVal.statSeq = jvxSequencerStatusEEnum.fromInt(ev.statSeq); + + Pointer dPtr = ev.description; + Pointer ptrU8 = dPtr.cast(); + retVal.description = ptrU8.toDartString(); + + return retVal; + } +} diff --git a/flutter/ayf_ffihost/pubspec.yaml.in b/flutter/ayf_ffihost/pubspec.yaml.in new file mode 100644 index 00000000..4d30fb74 --- /dev/null +++ b/flutter/ayf_ffihost/pubspec.yaml.in @@ -0,0 +1,83 @@ +name: ayf_ffihost +description: A new Flutter package project. +version: 0.0.1 +homepage: + +environment: + sdk: ">=2.17.1 <3.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + + collection: ^1.16.0 + + ayf_pack: + path: '../ayf_pack' + +dev_dependencies: + flutter_test: + sdk: flutter + + flutter_lints: ^2.0.0 + + ffigen: ^4.1.2 + +# This is the command to specify which file is source for binding generation +ffigen: + name: AudioYoFloNative + description: Bindings to allow access to fernlive native backend. + output: 'lib/native-ffi/generated/ayf_ffi_gen_bind.dart' + headers: + entry-points: + - '@FFIGEN_HOST_HEADER_PATH@' + + typedef-map: + 'size_t': 'Int64' + 'jvxCBitField': 'Uint64' + 'jvxCBitField64': 'Uint64' + 'jvxCBitField32': 'Uint32' + 'jvxCBitField16': 'Uint16' + 'jvxSize': 'Uint64' + 'jvxInt32': 'Int32' + 'jvxInt64': 'Int64' + compiler-opts: + "-DJVX_REDEFINE_DATATYPES_FFI" + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # To add assets to your package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/flutter/ayf_ffihost/test/ayf_ffihost_test.dart b/flutter/ayf_ffihost/test/ayf_ffihost_test.dart new file mode 100644 index 00000000..34324885 --- /dev/null +++ b/flutter/ayf_ffihost/test/ayf_ffihost_test.dart @@ -0,0 +1,12 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:ayf_ffihost/ayf_ffihost.dart'; + +void main() { + test('adds one to input values', () { + final calculator = Calculator(); + expect(calculator.addOne(2), 3); + expect(calculator.addOne(-7), -6); + expect(calculator.addOne(0), 1); + }); +} diff --git a/flutter/ayf_pack/.gitignore b/flutter/ayf_pack/.gitignore new file mode 100644 index 00000000..08cf8f3b --- /dev/null +++ b/flutter/ayf_pack/.gitignore @@ -0,0 +1,32 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# 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/ + +example/build diff --git a/flutter/ayf_pack/.jvxprj.flutter b/flutter/ayf_pack/.jvxprj.flutter new file mode 100644 index 00000000..e69de29b diff --git a/flutter/ayf_pack/.metadata b/flutter/ayf_pack/.metadata new file mode 100644 index 00000000..ae96e93b --- /dev/null +++ b/flutter/ayf_pack/.metadata @@ -0,0 +1,33 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled. + +version: + revision: ffccd96b62ee8cec7740dab303538c5fc26ac543 + channel: stable + +project_type: plugin + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543 + base_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543 + - platform: web + create_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543 + base_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543 + - platform: windows + create_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543 + base_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/flutter/ayf_pack/.pass.default b/flutter/ayf_pack/.pass.default new file mode 100644 index 00000000..e69de29b diff --git a/flutter/ayf_pack/CHANGELOG.md b/flutter/ayf_pack/CHANGELOG.md new file mode 100644 index 00000000..41cc7d81 --- /dev/null +++ b/flutter/ayf_pack/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/flutter/ayf_pack/CMakeLists.txt b/flutter/ayf_pack/CMakeLists.txt new file mode 100644 index 00000000..b58f8981 --- /dev/null +++ b/flutter/ayf_pack/CMakeLists.txt @@ -0,0 +1,17 @@ +## Why only in the SDK build? Since the path to the dlls is set in the .env files which are only built in the SDK build! + +if(JVX_RELEASE_SDK) + # set(AYFBINRENDER_FFI_DLL ${CMAKE_CURRENT_BINARY_DIR}/sources/jvxLibraries/ayfbinrender-native/Debug/ayfbinrender-native.dll) + # set(AYFBINRENDER_FFI_DLL "${CMAKE_INSTALL_PREFIX}/${INSTALL_PATH_BIN}/ayfbinrender-native_import.dll") + set(AYFBINRENDER_HOSTCONF_DLL "${CMAKE_INSTALL_PREFIX}/${INSTALL_PATH_BIN}/ayfbinrender-native-config_import.dll") + set(AYFBINRENDER_FFI_DLL "${CMAKE_INSTALL_PREFIX}/${INSTALL_PATH_BIN}/jvx-flutter-native-shared_import.dll") + set(AYFBINRENDER_FFI_PRELOAD_DLLS "${CMAKE_INSTALL_PREFIX}/${INSTALL_PATH_BIN}/zlib.dll,${CMAKE_INSTALL_PREFIX}/${INSTALL_PATH_BIN}/libfftw3-3.dll") + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/example/.ayf_pack_example.env.in ${CMAKE_CURRENT_SOURCE_DIR}/example/.ayf_pack_example.env @ONLY) + + set(AYF_FLUTTER_WORKDIR ${CMAKE_INSTALL_PREFIX}/${INSTALL_PATH_RUNTIME}) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/launch.json.in ${CMAKE_CURRENT_SOURCE_DIR}/.vscode/launch.json @ONLY) + +else() + message("Entering folder for runtime build - nothing to be done here!") +endif() + diff --git a/flutter/ayf_pack/LICENSE b/flutter/ayf_pack/LICENSE new file mode 100644 index 00000000..ba75c69f --- /dev/null +++ b/flutter/ayf_pack/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/flutter/ayf_pack/README.md b/flutter/ayf_pack/README.md new file mode 100644 index 00000000..fb893823 --- /dev/null +++ b/flutter/ayf_pack/README.md @@ -0,0 +1,15 @@ +# ayf_pack + +A new Flutter plugin project. + +## Getting Started + +This project is a starting point for a Flutter +[plug-in package](https://flutter.dev/developing-packages/), +a specialized package that includes platform-specific implementation code for +Android and/or iOS. + +For help getting started with Flutter development, view the +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. + diff --git a/flutter/ayf_pack/analysis_options.yaml b/flutter/ayf_pack/analysis_options.yaml new file mode 100644 index 00000000..07013646 --- /dev/null +++ b/flutter/ayf_pack/analysis_options.yaml @@ -0,0 +1,10 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options + +## The class hierarchie with mixins by default has limits. This is eplained here! +## https://amandeepkochhar.medium.com/flutter-dart-the-class-cant-be-used-as-a-mixin-because-it-extends-a-class-other-than-object-a53eb5f214b0 +analyzer: + errors: + mixin_inherits_from_not_object: ignore \ No newline at end of file diff --git a/flutter/ayf_pack/doc/allocation-widgets.txt b/flutter/ayf_pack/doc/allocation-widgets.txt new file mode 100644 index 00000000..f10783dc --- /dev/null +++ b/flutter/ayf_pack/doc/allocation-widgets.txt @@ -0,0 +1,281 @@ +@override + void createTabsAndViews() { + theAllocatedTabs.clear(); + theAllocatedTabViews.clear(); + // Alternative: Icon + // import 'dart:io'; + // ImageIcon(FileImage(File( + //'O:/jvx-projects/fernlive/images/fernlive_icons/fernlive-mixer.png'))), + //width: 50, + //height: 50), + + + import 'package:ayf_pack/main-widgets/ayf_mixer_widget.dart'; + if (entryMixer) { + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Column(children: [ + Image.asset(imageNameMixer, + filterQuality: FilterQuality.medium, + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(textMixer) + ]))); + + theAllocatedTabViews.add(AudYoFloMixerWidget(imageNameMixerCore)); + // 'images/fernlive_icons/fernlive-mixer.png' + } + + + + + AudYoFloOneUiSection streaming = AudYoFloOneUiSection( + true, 'packages/ayf_pack/images/ayf_icons/ayf-stream.png', 'Stream'); + + if (streaming.show) { + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Column(children: [ + Image.asset(streaming.imageName, + filterQuality: FilterQuality.medium, + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(streaming.textShow) + ]))); + + theAllocatedTabViews.add(Container( + color: Colors.orange, + )); + } + + + + + AudYoFloOneUiSection sip = AudYoFloOneUiSection( + true, 'packages/ayf_pack/images/ayf_icons/ayf-sip.png', 'Sip-Phone'); + if (sip.show) { + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Column(children: [ + Image.asset(sip.imageName, + filterQuality: FilterQuality.medium, + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(sip.textShow) + ]))); + + theAllocatedTabViews.add(Container( + color: Colors.orange, + )); + } + + + AudYoFloOneUiSection webrtc = AudYoFloOneUiSection( + true, 'packages/ayf_pack/images/ayf_icons/ayf-webrtc.png', 'Web RTC'); + + if (webrtc.show) { + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Column(children: [ + Image.asset(webrtc.imageName, + filterQuality: FilterQuality.medium, + fit: BoxFit.fill, + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(webrtc.textShow) + ]))); + + theAllocatedTabViews.add(Container( + color: Colors.green, + )); + } + + + + AudYoFloOneUiSection hdrive = AudYoFloOneUiSection( + true, 'packages/ayf_pack/images/ayf_icons/ayf-hdrive.png', 'File I/O'); + + if (hdrive.show) { + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Column(children: [ + Image.asset(hdrive.imageName, + filterQuality: FilterQuality.medium, + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(hdrive.textShow) + ]))); + + theAllocatedTabViews.add(Container( + color: Colors.blue, + child: TextFormField( + initialValue: 'Hallo', + ))); + } + + AudYoFloOneUiSection remote = AudYoFloOneUiSection(true, + 'packages/ayf_pack/images/ayf_icons/ayf-remote.png', 'Remote Control'); + + if (remote.show) { + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Column(children: [ + Image.asset(remote.imageName, + filterQuality: FilterQuality.medium, + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(remote.textShow) + ]))); + + theAllocatedTabViews.add(Container( + color: Colors.blue, + child: TextFormField( + initialValue: 'Hallo', + ))); + } + + if (binauralRenderer.show) { + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Column(children: [ + Image.asset(binauralRenderer.imageName, + filterQuality: FilterQuality.medium, + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(binauralRenderer.textShow) + ]))); + + theAllocatedTabViews.add(AudYoFloBinRenderWidget()); + } + + + + +String imageNameAudioIoPri = + 'packages/ayf_pack/images/ayf_icons/ayf-audio-settings.png'; + String textAudioIoPri = 'ASIO I/O'; + JvxComponentIdentification cpTpPriTech = JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_TECHNOLOGY, + slotid: 0, + slotsubid: 0); + JvxComponentIdentification cpTpPriDev = JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_DEVICE, + slotid: 0, + slotsubid: 0); + + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Column(children: [ + Image.asset(imageNameAudioIoPri, + filterQuality: FilterQuality.medium, + //'images/fernlive_icons/fernlive-mixer.png', + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(textAudioIoPri) + ]))); + + theAllocatedTabViews + .add(AudYoFloAudioSettingsWidget(cpTpPriTech, cpTpPriDev)); + + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Column(children: [ + Image.asset(imageNameAudioIoPri, + filterQuality: FilterQuality.medium, + //'images/fernlive_icons/fernlive-mixer.png', + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(textAudioIoPri) + ]))); + + theAllocatedTabViews + .add(AudYoFloAudioSettingsWidget(cpTpPriTech, cpTpPriDev)); + + + + + +String imageNameAudioIoSec = + 'packages/ayf_pack/images/ayf_icons/ayf-audio-settings.png'; + String textAudioIoSec = 'WASAPI I/O'; + JvxComponentIdentification cpTpSecTech = JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_TECHNOLOGY, + slotid: 3, + slotsubid: 0); + JvxComponentIdentification cpTpSecDev = JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_DEVICE, + slotid: 3, + slotsubid: 0); + if (entryAudioIoSec) { + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Column(children: [ + Image.asset(imageNameAudioIoSec, + filterQuality: FilterQuality.medium, + //'images/fernlive_icons/fernlive-mixer.png', + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(textAudioIoSec) + ]))); + + theAllocatedTabViews + .add(AudYoFloAudioSettingsWidget(cpTpSecTech, cpTpSecDev)); + } + + + + + + AudYoFloOneUiSection plotter = AudYoFloOneUiSection(true, + 'packages/ayf_pack/images/ayf_icons/ayf-plot.png', 'Signal Plotter'); + + + if (plotter.show) { + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Column(children: [ + Image.asset(plotter.imageName, + filterQuality: FilterQuality.medium, + //'images/fernlive_icons/fernlive-mixer.png', + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(plotter.textShow) + ]))); + + theAllocatedTabViews.add(Container( + color: Colors.blue, + child: TextFormField( + initialValue: 'Plot here', + ))); + } + + if (debug.show) { + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Column(children: [ + Image.asset(debug.imageName, + filterQuality: FilterQuality.medium, + //'images/fernlive_icons/fernlive-mixer.png', + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(debug.textShow) + ]))); + + theAllocatedTabViews.add(AudYoFloDebugWidget()); + } + + if (components.show) { + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Column(children: [ + Image.asset(components.imageName, + filterQuality: FilterQuality.medium, + //'images/fernlive_icons/fernlive-mixer.png', + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(components.textShow) + ]))); + + theAllocatedTabViews.add(AudYoFloComponentWidget()); + } + } \ No newline at end of file diff --git a/flutter/ayf_pack/doc/cheat-sheet-backend.xlsx b/flutter/ayf_pack/doc/cheat-sheet-backend.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..2e73fe259c8c674c12e8d4443287a3282a318852 GIT binary patch literal 6368 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 NF0Z@nB1?4t_Aj6I4~+l- literal 0 HcmV?d00001 diff --git a/flutter/ayf_pack/doc/cheat-sheet.xlsx b/flutter/ayf_pack/doc/cheat-sheet.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..da11817b6ced0ce2b1401273fc70ada58db47670 GIT binary patch literal 10850 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

G0^F z+!b;x{`8l|KeCAsf71RcuG&JILijK`f~zaO1yty@kx zja;WemTZh@iTX31z^DoG8v1NGJo2y+6((2jlt8Nw zPq9-sJ#mTw4cjqcDWq#rN4!;w6(VF;BVdA6BMSuKKK=^sbs)$hR_;Nqe1)ZyuD^p! zK2Q?MfJ&i~UP}*AbQG^Gho@FrWmn2n3o@Vhdq>_^Sb#b=GHylG9K(ZfUX1c;7n~RW zh3Iov4~HR}@|qD{uN$lFO>>tD4Wgpi!3>RSY}ioA>C)J)MM9RxxoVbX)8f5bQi#?T zS4pTl1Z&QEO5}c_)l*>7$RpD?aTu8X##i=qJTgAr{zcVZPT?BkWG=~Yzvm$x?4MV= z+9BxibUoYagYf%4AM@ld)9j$<`L^StLo2(Yxh|^PpVa;Gk5tEa#iZ~&USA_%())-RiSmJkX=c**_)-?gT;;2Ib&G`B`|{Kc80H%*P8 z#Spx%P$b#(k+Gm?=E7B^X4c)eC%=QU>0S4JrHf=3{EX^N5t3+xVOR%f!dm6G#v90X zzw>ARh5)CUCD;5K1jwH&bCa@Jp=3)6-yA=L{h(^&F0C)RVn9AcnO^G4W*z_28(3L{ zQpbdsM)HyqWT~&PZ0|`1jC3@BKUk{OIp|bvU8s3tA^3SD_CqmAf4?y24H2j_{pX-l~N`rCo$6=jGG-1-WN^P8C6_eF%q?&0%gya%M#6un-pLO zVjDsMD}(}Y>Ujk<-6_5)q?BX;Mdn98jtH!#4zxxUy52E_fxE$U8*9UizmQIiZTIe^ zkA-Yq^%AxRpsqO;^K0y=Ltvv|rb0ZOJ#ShQOj|`*F9wjNV1^|?Bd?hC?&cVPI$Ts9 zLZN`I!`t&5!~aL^Mr8L zycfaLsjDwQwxp9@y=>~k`lqqKZQ*G;-+dhEqPDr&ij1cI}RUK~yLrI=i zR6&ES;)v{ylIV8)8nEoRE*qxE?Zj%ndd}(Oy6ZmjlGhaqrW?^2cxzrK6s&J99$vy{q9eW73=?WQ4>iA0;L=c< zEALWG-}p#h8g+EePG_JxJOnb--`0EMUk*~sC=n$bu9=4{Y-VoXYpXCaR1I;!Uv{2* z|6Hi=`T0k0ycl1BR`?a@0%V!Mgc1szt`XVXx{;E#`LYsSs>1~fGzelE;sEi9TdJw~ zn0~UOA9dDPnU6Z9z@L$h=zswKgo-nmz=wT4IQov@2RiG!2rveYeyE}R-hTht!o^6t z4~e37I*hD^q??S<+)$ATr{F6Gndtf~mqK03b@pz9u^|LxN5-NrZo=v;V_y*c1?5$M zik20eU*-=$7Alb(XO>YNZ=jD_03Ce1`kad&rtF@9_1q@}u7#YdKglpocCS3;5Hm*b zHuuR+$cnC8SJ<5*4Ij;B8voN9d&U1SE!cdnPOaVi@cj~hY@fH(gEWd^lf#t1ct*%u z&Py9SeTn$Utg0^BvFM-wfK#eH-*$g&`z9XHC3fGBXC=?;b3$|(sDv;pVefPyxOj}5C-7> zgB)~e$}L(gRp!hGY;Q8IKFshemQwiE$O!;p=xydHY+R-b(q#vAOxhn!ZaH- z5DXw79ETnx@@LG25rVbs7qf~0^};TWTXye^zn*7NGnjdW0zrcjf_xRkQ=)6%Fha8*?eP^r4lCggEQ5eB0t&d zDaPNP%_{f)xqpoln%J$M;oR)`!U&vaANG7iY&l46?i4_hU@%EzfpzBYe@+{{P7X?) zI`ud@iL*Q4=Rc6UAw1-?x(nR9V^p6SuH$FA?R(F*SLPLxHTH%Er8wL+K>M(FQ^f(8 zD~|T7`XT7J3Bt$q4Q$nkTYOjFWi6u@<-0>5Fj&k&6l&@**sVVI5q;77Gsl`s zu>XT8(PGCH*vyZmFdV8=pPQTe3$ufr-ujL5cSFvrB1@(9tCUeY*omwiL3crPOoF5B>bsET*C0n|ZJ*d|Rlaq>CN1J2S4>g%4S4FKeQ(#V?{qi@m0(c{ zJ6|!^elUk=yDCzWfs0Rz_y!(55tX~)r1Fs z@`j&ZVUBFGRH!lz(@pnmDzRpOUl}s5CWLXb(KP!T%Xo!RCciIZ_v&T#`C7_JgG&>_ z`mG?!=ii6hHpOYM_f?%`#F^R-!LJwjU+V6tCtJB1YGyg%JxMDTj`9-8(pduE{b|^v z7aaqd@KaU7Adv+EyaEP3dtg%|63l(FUzEF@L@Z_xc0Ol+&V2$xZ+e#1>xB-6cTHha zZ||%|^5|(Xpg!N-j!=q^*L78R+9@Wt5zm%DK@NF~qF468jf3Aap9jeRkW|X`ZB&yu z*^U!^+N!V`ZpmjupBB$jeCOT-Es!^MNY##b z4~3SFWlbmrz`AmNk2T$G^tT6vc>)w|4ccRKSh;2L7o{O87>T{pN^YBdY@9i6wpTVY z1mCZUZe;7Gm;awg(bltq3>H_}AW{UNR|b!Oiq$%~l`_~jFY`Mzhf&M*m4vtB&W~8Q zav2R}R&}7)iB*3h1*ZA$ALbsxepZEoW`+WBrD2j2Gl4GNspOt-ju<*uHm$%g9jU*& z@H%p*ruHlJ!UA{0M+6pG6p0+IKMAkF$@p92x;~LVn|0N6(4wTTOm{jeX|tvT%N#x- z4cT_{_Dh=9L{!{^3A~Wk<_zwP>>)Z+wR$3DgmMwQD8D1c?08Q~k1u-)nL~)bHCIF( z^JBQ9`0Ms7zojOzA9H7LzYwh@{L5QtN%PnK3*gE$kVj2J=|&?9ufSs&aenBQ+Cig9-^L!i>ARen*A6a9+qm7uKAhig`{cnBH;^JS&~+kP|nuFNoKP` zXl!UlSld(pJ?M+vThDFN{3twwaK3v?;w}DS=n|%~2;0yo0 zCkk}3m$_U0p^K&jSvLw9A1{MLun#_E5IS^VhQx>v(J*sFI&H_nng?7$12dYfUPH1b zHdrfPpclyAoJ+d6NBB7kHGanX%8T<(3Z@U>))7p7FRN(%0)HsPOOAlA`qsevWFpZzQpxOHiKodv#;`&XJ1LQldd$V|HpO`)Utqv?^UawJLo-BE>;;B) z$%VrfBS+#*JH9;At^VlCkv&`UPccI9X6S57Slah7l9(`K$=jG1$xXFu0@JrOHqWiw z+%?eGKi!{&z&FiKT|CeoG-5ehZxM}IjW;mFd~O6;@$0cF->OA0lhCzNtT}OW#NR%e zlRc#Rfal%S7d#S;_*tH(MSh=EJ8cH!YhRiT@Laoi(U2A84VyUfKcAcM=QV5Wm;Ev0 zEa8J1x*t=zL>&m5tx>6gaDg_pf=2A*H^$=7*#4a%h_{haR`oyPhd2 zoAd56OH3sF{Of`lo({bg4PS;eUOgEb`mSL+ctN z*48b5Lx2vnoGa574Jk$se7iUFG9F(}VV}JW?z2TA(cNymX`8(qqRD-oBH@kcrmw+F z93||6&jI*=MaT1N{jEQt@8P=RX0l@QUP&@*`#H^`JS*r!&0)2d0hxUh;eV%vu{Y(C zlts$eXAWL41R#p+1U}ti7wg>ZAN)kNPPU>{!YJe%ms=b;-j?UBwJ#wz>)$Kg3M(Cu zeHp41c)z<@YwJIddmb$J{(vC&+XIP`_K`w9Z?%X&L2EVk!o~e1Qt#OD&}2BcHjiMK zjIIer0k}yj9jZg)?z)F!ZgidXSpEYT))4=qMWdlTIlW2z8s4PfuhOh z;=E*nGBA4NL?ORs-!kZ=MZvZXhmQe#xcj``q>2Mk2ijHLeTGtj>#;#k+ypN6cb|A& z3Ec@WZdH>j;0$DM$R#O?&|anS5rvgTC*2ERNYc7!3|WX{!EylPZh{-fAOIvh7tXM5 z!_fKefIgBS>k>)DYPRL8j%b^Uss$JsaP4k7z9!Q3J#A5SQU^}+7I(qL?W;G2+%9)% zK$kVcbS^DVx59`xKH-rQVIAq`p`WDVN=91mWJdA`&+Tw7_7iUHdYoPqL26OD-WF|| zTEA#M2x;5%jGdm&s`9#yaE1NPJx>!OZm=oG5fUURsh68^kCb+}DQ{aM%!}>2lVJmW z4;E_0Meak{_?M$H+R*Xqx@Y$hAUTLt={TaV`dzo+g_SnVYav-+mB;uq$c{(sNZNgv zZ(g6(C+X{agVLLyS0G5!6LaoV8_>Xb3zZ8TRN_jZyCzvk7S>$Mo~hvM4wjiRuAZNC zQ709n|Gw zI;k)~vL#^Xj-1ewvB`PC)fiG-HKL-}T2KNi=kyq-Q0kaK6_L-Y1y-C}ZPI;~_cfio zk^Bak0ckGMi1%26NL}T;beda{akY&J7+FtoT&vvFsk46s_e90uc)d-m08X&k@1(P~ zM|NgrK^fs`(xX$9eLUO*4ffu^(lpVfC4yL*XyGsBi~9pHsvUVAj9x?L806ruXYWMuVQ`M1a-w*rg3*vCny1vpu2GlKBcgkMVt;yr2CAP z*lcu7RBk{1e3R&9crMV_O+Yp?*y=U=FD*Iy@5IYv10pe}_DZp!uz$;^=$OByF3S}2 zMI-qe6t%VRQSk?G@j0Z_VPe#z=#SdGfH0FS_-mwVyZ8wy4@r)<&EMG@@Kf;lwaN67 zEQ)#ha z0Mu`kx|2VvG>3J&oD~pDclx&V{<@fHmp&c zmbFWf-#Aq>RhReQIh~b}?3Zi5WWOxu%zKdcu5!eTuy8M5ee7(XwJ&K>zs$91*$&>!n2)xiGzIM?IU3cGWTv* zqMG%+!n;?295j%BGbtk=5xo${_N2sX1DR%eZ8oPil%si*3N>^75iKbRzkSxLsIQRa zU2yK>u(qZC8*EaoJ~K&f%SAaQ-D89K;BCRUiz6%c9+Y5=W;8DB8w#T^jHS1mz&qvZ4llXgPd0ITO`_C) z;JGG`tWmw2@?>z^8jv#Ag%f8P>~v_ahF@O4VDC{|92LyGa(CyjsJlnF;|r|ZSjTcV zXq8t9OlB5}_O<9iaO30( z{?nA#@upwoB&TcwPI6tl%Gud-`N}R%_KBlJ&|cSo=*A9NQ(91|t4s=1hCe90Yr}oe1`UFz8{L)Ki8~?(q(>kgiLEHV#0r-%o6|-Nrt?G}dXH?2T^KLd*dQGw} zC@MHR4Q3-yt8HH%7%LFqmA^?Jrau$wqGZmYa*Uxv=1=(?9i-1NJ$IHm)BW*YTcmh`)vmSja@k zOBSv#1m8u*qPYPG`{iB^;bo2k&sghe-gXEjru#LXU$%ukPxjO zV9gT(&X3OfiE;`2s8EvztumzyXS8zog|Sbev4mwXmNXI2e>#mE9v+VI2VjC++3JRvwOGAEMFyQ@gkbXs6Gn;jSa=RvGeHoU(*(Hq~+ve6{r#la+ zF;=QoZ$2V2<{;aG7NXVp-G%D5_VC!OjreG}DJhs67&qczzVL2#_)Z)7U8C#LkpHJ? z*O?O^%r1=GrC$D=I#!;+rZRGi8FBdbk=~u{9|H*nAhHAxLjI;P`81m&v?HI6{ZM^< z#KXFLCtOy0gofF%@Hv@0Q^JLh!%shd!)TjlKwQ_?RV@bpg%Sg#- zGk%-|dPgIjQvD=qTEl;9u%cMaE_kOl?)}OV|J#;$+C$%-{1ZV|JIIObngxB;r^+T~ zwgXseHsgJ`jJV4g3GO{GghlZRv~ZFibpDw5EYxfQ`z2xXV6*>2H_v6BUC{SO1X9oJ zPpyE;l)-TyF#+tCWNep&bL3?-SH!>RMmBjf)l}@int-=@Z9eFqcf%X1;Cwtoy8Q(z zt+ls~?nlks+3rD`(q6QmjbzD(;G9&z$7ub&kCS}Fl}od3gGQ*cMR$`%!`zU2y)Z28 zmrfL?^o&|4+MK+~YkCS@cUy&q`9n75y8(uXYqGTz20UO6C)OYs;u6D;GU%x`Y_EiqgKVeA6zoEkoJF$w%gzBOZu^J4yQe30mr^tt<>3-CVe4^ zB~VtFpC~7bUFmJYz+@H@)I|^l?;K6l))y!?BW8z$thp+eeMpuvPOV{odmh8|AkGR7 zzD2kp#;4D%`QPT!)SsXK1U}`&Y+t{GtThda+70yes7_7H@Y*W>D=Y}9oIGWp$6^~l z?LGcV$(N<#y8M+twNROrgEC31YG`bn{ll$?{L$<;waaH>>iQOqv>vM&V7MfCZ|)^( zvEm{+o9N9Gyh`$36vdr8p1NtyTMh-;`8LVIbFKGhkn#px&X28-O|=E>Fx_U&9p+T( zFH=b;Ad4fGmR}pYaK-0{pI_&hTfHRDJ@Jauk zs&Qiz1bg&hDmF$!qcQGtr0*!5qguZ(MSi6O@Cf_EgNqKuusH66kgB^xK< zcSM-~r^>Rd;TSM!2AqzHl+txCYYI-gMW1a zQ$0P9oV(la=g)TB*5}Wk{qy_^7t8A^x5SV->9|x=`+AVtIv>tB_D1mnjv>xQb?oN% z@>`oFC(FE#d6syT*|y>Ahgy9lZl^9`n;g6tb#BC2-Znk;E?n#;sukwhy}znQ@xY7; zd@06ocFlqo{~tN1h!Ax~fzJ|o`R#1Ci2rG^kVRA<(r)$7I{VXdyHKJ8Wy zr^?(l$D1Ye^gd-^ZAL^)*GH(+x62$)B4TBC;NQc|8|r0V+>%X$svGgd;{j5)te>4` zlMU%X?%q`|N1^}4>COD8W!=&?<2NeuJ7R&W9R7Z@_b7VL7E;u6MhQ8rYOGu~yPbi% zCRigQg5gXzRm|nrlCmFgA!>F%fW*br)L%xdnkJ5gbkWShVmdGUWtpTql>tWjgZB}> zn@WU&DKiz^-2NRL=l@99jU-%7@bf50c(v0zdWw;q67jK{+j?df`1wg5qt$1MApl`T z((zcs6XQ~ZAw`BeGN($C6#}Xj$5grxD+LAqX%N)ksEc|+!kFcRwME=Ai^Teyt4Xxz zls9-EDZA0`7}thFFUL+7EINsH-A5%%ReY5`SfdW3wSbwlfN3B21KtN}Wu5lTR$8st zXD!;6#Z`-|&R#l?&9~O6fWu6au>09&bXlW4G9^`|!50*c)NQr+9bI|#h7B_*hMO0zF^b&Oa~det`401Zne+j_ z@Jj~1B{9F6&agYF`2Nt1RfOij&z}!M9EMugRDu;7`geI#z~8uQ2x|<{Zlmgx!d)+K zd{kIDoM&v=(X`8RMKT3HaEak9U)xZv+E2DW(j(lZTMv$<4DZGfGx8Rn%fJ2B6i(7o z9^t?RJ!&Z9S^phzL<_UJCU78B1PbcFer!PYd6{CXE86XDAs)X*{xNmj&YhOY#+PJO zwVhp6@hjp>#?LzAbL`U#TS;+lr~1-_-CMLi+Y6V85*%51TR|AM+dF~$kTrTC+38vM zzUKn6wp7pkq@TPO7gz#F6q2A*Oh&Vl?z_=y$zL67Y3s>&nfokAR*22q&QHeyzHxtGk0{ZXN*dvO~n6GBBm z%#il#CK2}ieWNMr@w5@nua#^}r1-qVzIMwowDrJADq>D2esIMh$GDEKkDkH(g(l|f zRKr<1dxOmsRltmIbZB;DMA|O4!Jw&lcK9e0bQM53-0@(1yt9GB)zg%CIu1Mq61%8|2UM!6ZRVQ zXKlA};0&`JHwj8s&aCNqZzh5>$?XEsCmGm8!b8ozXXsgXpQC$bzDLV3PI*ze&x?0V zI(tZZn$K`Dc4LvwM;*pxpalCsBc~EhWbpHH0F6`6CC@kKxU8mc4H6baonuZ|w@I}v za~jEyR&%(%;c3mfu4*U}nEb)&brqBLP$xN>o#r~xVe4V{CDRtr!?7-$D!3S4=uJv+ zX>*NTl0Y#7%uqzy|1|m#$V6p?wocqnQ@M?>SSUd$A(tmW=GOc(hbb9*;2048ayfrT ziqrd07t%5&qg>fnqa~Ga&PExghR?hJbjVc}_PuGiiDDyY%lH}w)J;3i&cUq7xlN|e zj+-+Lgavh`g6O$$lDJ>z#>nF$vlU+h3(;Hh zSA6#hvwCOWuA%VTl@B+I)2-*{aRqgDtH!~Zn9m=*A{DmDeB|X1@ zQ3MuW(eFelb@^tf)pd@}SSx0n7WdSam`u)V9-T7@CTut!PFtox-+vNP&Nk|3z3a_Wor0hs=MvWyF-Suii87{j!LkeV_csa zE4{$s0?*uTD_#$!gVveLV>gUB#QI+ za+4t-H_x2;>rfX0QAf&U;pAXjIK}tVH3*6wAC%m1LUP(@ZT0|v$1H`lC3@2d|0U!c z`hC!w-;Ct9gi2eaGqr#-*hx7ZEBCIYr=y-^KHTMYESsi2d3zNZyu#6q^Gj42>sPc` zI@%&u^pR$^N47rpVpcN&iHy3Vf8e&Lq=D$^*B3D+LbCu}_xZn2Gu`ce%Mjx? zrOZ*Opg+^p$=VU@OcE0vJg)HP)vJ%ghhEl+hTTARgHmwa?a;Th)1* zMyV@Dymb3`NNZMgsS^qHzSTZwEqe7~^sBx@BKw_nknk(xSQW8OEG0$q)Tq9AvpfF$FrY z38{PK3HBu16C!oWHbZlk@w{>8-?SHwghtYXb}PsG0(w zPm!2F+dvvP1pG92$LVr^xSMeGt2*N65y#6HJ3fuGj;2v<{dr0DO#$a8bKw-njPU!y zWr5RNY__aoPg2b8YXf|hQ$(}-1UY1;+Px*is%z?&!jsyMT(h&b9d5V1GqP_Q#~dU+ z;6ep9Z;6M)Mej4$v!p{cP}!J9UL5Q&3$93n@!|WvgLnB|xwyRba7PYI{v7!z=Q7`n zjKgrzy>Gw?2?Y2K0`E4HJAhQYWT1&Y%DpbMk%-cSj6qzt@o^)Tl5h(M;v3V2k7@~o zth>O3*MopWbXw_H!ZRnwnWp&Aw&dV5EA*Zi1OvWMDce;YL z^^?aA;!Mc7_i;6nB;qeq{mhvW2epy=#{L#DkMODDb^lGmaeCkbHGr>`SkT@Ht*5#p z@b|eVlb2Mf@>#DL9f?t9SBVci7AR=qFtg5ApL!~B63b|Z$IG`ZhX&z0Ubx0aeW+j8 zOZ-K6(PnQ$4&j^XJ>>fn1ufnWLwr?%@Xcf9Z_h5C7b?7Mczgz$3y%9VLFjwwMt#1l zTs6VXY(;ocef^gVeMA3_uW@Pc@P+v%7|{-FPS@d_;6@7nH&mbn05MQ+|Bx=qTX zAUA#Tmw?=n_WZpu8Z z2Ld_wG-56$D+p&`HkM&l=SPZ3rAVWDq3}n)1q!^9sh7;?B{(o>IfO1#3;EI{CR7oj z!s3Y(Sj=V0~4+_SK2sDsPLoZks~a!orub7ZB9v-+yej0|@J1LQFx=XHH^PPVLD{!@ZG zB!r8PitMwvj?;yn*`YB~1fZlC*kGQdQN*i}6B+temTAyL80-flYaOyyVGLZO*>@kO|UAS_68mvYe+ zO5rqUMCUeOh~B91{nnJL*gm0M#)YPg6U9&jhG zGaTJUp^RYAh7&G;Fri49ZGeSf<{GlIQR0qxsKimC%Mui`-&`@a1bAG5U$(>r=z8@` zY0@bKW@vKEBtW$er%$fg@8>&{FkA(~3l!LY)VqSpppknk&m%DlMX7J`mmhnI6HU-}^k660YFx4k% zM*svXk^g-S2YtN5!&EcIBz{Ot55!^{OOu9(1o)=Db9va*t%AN9z(OL^>M09%uF z2r2#h?(IxWla$Xss8Jvo1sIW|)G3;!ZeGBtlS+=F#> ze!bYR;Re@EzcH#K;BDW8jJ|T$v!2S;amcN|NJ&XKdwYAOTlPu>zn>mR!MBaB(eoka z9e~?b6fuu;GBa~cQq5@F?p#>E&bVr_r$B^f3?&QDevn)t#5+z%FSWR1#=Cqya83bk z0xP~&H{@1Y=pezU>4=b@SVQza@iapJDPH-L5Vwpq8e4P>uPrxBvR8F|6XXbHXJ&_Co;*uXY?hplZCh8bo;$jmqZJu{i!P}tSXNNoSvrZ0g*G|kxMzXKq2^y zt>vjB+6DxK%X{jmNnz_zv^je?lWS7=Y8Do|yT#i1U;v8-z|7}EentVEvzC4V(Fuw0_B;Hnk>V@=vd5Nl@PK%Fird@RpJ?nZn zOha`YB@2ubXbS<9-V#E7pwE4`?WtQVlgFK)H@^~M^;%P3Zw|2!-#za_hcfxFC4Mg8 z{jGSKbVN{sq-Yqra6NJjH{GWX{ky867%;1t77B|PPR-fnV(*b~6&f27n~Luc-zeBA zX_}{MtB{fZD?_GLj4Jn;N3Vr9jUkinWx?NrhJ*a>Ig0q%0R;}JldtN?_f>BM_6lfW zsSRdzzsS*dhr9nUte8dM$I$14QF7Qttk{J1rt9B=uT6DN9XW@K<1FTVehH(YPqyCY zeo#_`<9kPNmhR>iCEt5s(V_-pbvVy`p5{gZr%DnWzr)Mj?4q`_GLj!EUwC7{E4f4M z#W%@lG$wsmT{T#R!f2)+=lded9-O8TW$gB}Uk$X0vq)=7$C8y5Ie@ z5Y7syEH$P=9XH*Mh@{454f|e4$XJx$U)%0U7i)#Kxfr@<50~o$_lv4Z*2uTQUU__c8Q?;~o@!$&8D7S5eeJ1PZ zSP}Kx_9dIua&_XJyV3{-OHxJ%E39&Ag+H^aNuahg%Zn6u5z{Xz_b{Qf&9!^eKDBzN zuPTf{f70H~M`cn3tFWv*d5*5%ULJqEq_v)Hse&douvNuTBRO@Wfy~IibbhIZ@6^t|HEBW*M&_GS_1@y3`+di)JYrS4i6z3r7Ar5p zur0^t;$;U~kDOar76XpG5~6%(pq|ppTE|bt`t?YBlR>~S>jW0wF?W$BIkXQyRQbS0 zToz_8t^le6xn|4qw*DlEmvs*f4(@3-DP|#@g*7~peS>T&%!2xetR8Z68lg#NSmb?! zNX+Nx2L*Y=;L^2*Nbmg2v({95(KRUw(Ipz%;js7q`$o4SGeu;m60vQT;8)ZYUoj>X zZ0l~=k>%GGHNJyqz0rq-b42AP*Or=LxXwMI1dUTKO;%|(;#9s+nhfnWw>MH%a$$t_ zBGvlRS}jbR&WazWBb>vz%8^`ufWj1C=@c;GYR`g`1|!iJ1hq5Xk=8m>9r8uFdb@S! zm;5~sxe%=RH#e*Z6Vb-S<=fn{X5yG4rn&L1sT6uGPWiy^pjwp!M@}!^sqwl6H^WN5sVJbz`{J{`Nmd40? z3KYkV%jIZQkNvH+=``4x&~#S*2(?cb_&CXZRqufW=h|0wq;+#BB<9R?&)+M3Pr}-~ zvT$>BLlF=C=nXUaQkQGm+xHCt#^(KL?+5-$Tvx2U>I5O{p`-akZJj%^@&D0UHKf9g$9u+Zqy~G z>GosTiWY@Ue4NEA1)SYWJ2bf*qU(r!;TL6ZxOE(NAJJplP2}gYY*_M1al#oI8++vv z8Ts#obu6W#_)i%pj)@Oe7q)XwCr$FkY%JlFdA}h zaJY&g8X3xfC@RbEQzrc#V^Q^JjPn>_PuIczk5xznDW%kf8yyunOt*ZM_{i!8wv6MO-rG%6l!C!mO#jO>B^0kJ zL=SS6F(a4cckU5=m%3tsu19?k%mf`~tGYu`ih5AWI%U&k)~j^Pmp_?it!T?$310nx zqs5YcE=AtsPCX$mg>g#{v2wFH{g*JGmi#HMJB5{M&(6=h@W>1PZwjPEUFieN4Tmjn z*pF^=$4<#!5{+wVZkBVk*AUHV+}t-6hTU~-r+~Cjzrr83{e7caO?uCsJ$#;>_X~&W zJKt1X2=1G9B{cm4u!UGCebgh;M?t&C4LA8x^Rm}M*4ha8)G))~)unMr$~U^_Py6uj zVQu$b@Ax{EXhD}`KbOEdFlh?TZFG3FLY6&^<8jSY{MLa8pz*aV1&>->)pU$^Z%L!W zW@1B{rb(IP#tk&8T})uf3EGWtJn4uDynw>A!b9}Tr~HbrwljO>xZ6r&Cf$fn-2_}e z6k486Ty!qT(bA0* z4+Xc7?hECg!EIlr3JBo&qOOk38!W9w-b*2b19RKjjHf=Dk>`oY)8g;)%m>a5hb^3P z5b0AHN{ekwmEL3~#ZRip&Snl#FlyE3W^&-hm@}{}2__M&2+l{r0#+33qLudE_!|ft z0Xmw^-$s9kQ3J%#W65Vr;6L@P-qwhcS=+JAW$73>DqI9-GgbxrHD86}NbMudGGhRpRsE+CGuSG6f?i4Ia7|%=Sxy(PIUAor0Vp%iOXfSXSgB;F>DEKcpINn|#kyGR2?X z=hCz+m#FG=aj3W>2t@YgpsKB(Usyx^^_5rAf1Q52{}`GTg+MbotW@OQ238ws2AMmYtCinDJnRRLfOSz2u$#TJEch*>W}r-%liyvZ7!Et)ZH zj2|Mi+VD+Yr|c*F>{UWi-z;a*GC@s9s<2W>@>7HKmiSi;`(Z7uwyVI=n+IVBTW#Y^JuV26J zV7CcFRRC%l4mcaI^`V~uII2+mQw`x&8#6f=z@{Fnc!4pVGk`9nvBdKN19K$yU1zC@ zv-4_gsiLB&%uS2hW3#e4?*OPFyHUP6r`R9wm|GNl2%unEFF~3{Z-x%IDkr;k;;iRo zehJd1s^hUlaS+EMUJ4$pycqXEz$&-ic8a~jYjCWs^0sGG{_t)Tl9VB9iW+`W3@e)G zfomH&^{gHhchxxO#%Zo=_!td5d(tQf#}v{bAR~T(XPzDUuW)fDQ7bvBb{r?O<$3nO z%gSitJU9!U?dPcyWTwEv!usZ!Z#%UV_DR1^J*59b`?3Ph)fO{7{e?LFmD#sT{b9K# zs&eSjUr5VvK5DeF?=ofTHrYojpi19*kGE6QzF~uE$~9~#ks?8RU%Y=iw+Bpn;_+`* z9eSo{9#l$5y7#&rZBjdyX!59MsvaK_!eabx7Ye12DzVndDUq@?ourB}fZQk;(l^`Wdn*CNX1HLqxA9S<5+b8@*!l3Yc8a!~d8Q@gAY z?c66_92MHGMM?!@^ZwqJH>qo8;)tSj$_>`c-jtVL>d(vnR%sM!BO}^K%6xYtar>T8 zd8V~eWl2t2aNVOUm#gFA3K-3lWzSQs7l`Mg>q{qj0`=MROa~zK3pSdJmohm3fhtKz{QGKKm9Tk!!Xr zKD*W9v96LIa!pVV3%sgwdqn*jC&F5XsdqhN14C2qW0$>{#>Zk3M1Y~2(%X_Q8{0}0 zEIh`<)EU&U5*VLC_dw4lU8I+zd>BN~|N5gK4jUGZCzdm>{%qJ4gjk2G<))+FM(SzE zo;~%@gol)D)lJd-7xr_HBa`tD`u~}Ti?-C)8ZKMn-qx*GqM_KZS*Pt^uU{n`HlkQg zwg{oMc)F=#(W9UIs8@f1iH!1cpLJ+i=TDw%=r~?m%+_b}+)I0*e|<=pvVnuRA4$LC zY17eFAV?{rpuOqvlz!Kz!K)i7gc6GH2rm|~d(LZ-w>PK{8t4v1{0$Igig8r;Y>Gln zNpt1rN&@R=Hu}cy&inRTcpwAuSq!Bh3=H^Zwe+WSD4mmerXeTWPlx}ik9&SEgVpsz zNumgH4T7_tf}i>K$0W!tHx&sHhLPb{JNSmVm^&!lwpwmpCR&!R%lyUCtq%q3fi@EF zHZQ5x>#%FrNkM4DtZss~ym=e7<8);Q23XDSmCskU$#sLu@vC)!clY+r&P~NP_Wkua zfV5lAE3z2{IWO`UuNFc4e5+nTdym`^6Y<0mEv@`6R@pK{>!`lwQWrZHY?Lb|`0vk( zQW5u_t3!~;c7E4k1e#K_=zuft-rE1H>O-Ur^&P6rvWGslV(0<^7xk2oHB!2~Q|aJ| z8{fIJMjs9~Rgzj~`C7}a=544{X79-F*gjJ^kr1~LMODujji2>l)qgcZamGC0OgMbp zzC7_NBS$gGM@@fA6b_g-9(ha?_y3QF6b$}l#(|XVm<296!*mWJ1fMAc((XNPqN=G6 zhEbl`2DjrXg&h>xDjmG}l$xS?YLUJoJmhBP;TGS}}znKb%)z9^?5Cvx(uwVIq$ zDDv$R23#--zZ^t$91%i2EXn6Yl{Cr`drF&CZokP3pAoH#$W~~_iQ;HJ> zr)Jwd2Lhb*`RV>CbP3SKQ0V&J#3>4GvA3Ub$0_t(diAKbWj{;-2c{JDW5%T_JO>}t zUw`EJ?ef?&n6Yej$@xFSd!WVk|9HYzO<5ruFdItd&{D8kZJ3ikvfi)d=lkP`S2R_Q&im@meTV zjLx~Wbw>)?X1)PpQPM~z_~p7R_mbk|-S6l1tRIu6r0|=lnHwGXm=H{%lu|_q@#Oj) z4*h}Sa4`J)&q^yiFr$=KPvimJ=YBr^rX)}ke)H-qM&LJ*cWqD3K;5sRcP%ZbMg0-^ z^?bT%-TuRmGDKiYID7u2I|VTFU83k+oWCz2sjAi zDLh=~W@2(vCOKVY3LnDXiSmv?!R!INyH2)fDD0c%G5!0)p#p2S@-9C*$sdGRIll$l zzCko_yjF=bzWEkUyrCsG@@%&m*~d;mtcZA!Nww(Oct#dE2zohW+ok;IglED$uz^@O zF8}RE;cs|zlI-M9W*&M?T{tSEMagG2{crZgxN>VF-BX!C$3j4t0dXi9MJ_Gg zsqEEFNB5z-spQcwU-ML~f~+(Ww{I`chp?njBOda2sL~;KvT~OP#MZgvrM?yPysLGDT)x?PQr5miC(m-eOJIeIjW3NcSQ`m0u%^XEO_hmZ{ z@_nB)z}23lLmp_yp|xHthW6i|CC+dHZ**-c_JIELOZ+eGc{u}-p});XYEX)B&18EI zUu*1uuPD(sw`tqr1!IP_&a@tIB;C*jn@ILy9Wgi5Xd4Pnt{JsRHsdl8o0n&4X~^59Kn7V&a0QExta23#}#|B?&&P5>P%rl zVn<&1i6d-B$uEPtonwd{-8zss=jhX7>-g}X_%Nznz?iopOzX%boyXS<=}GJGIyAxL ztVz)=$=3lA-4~e7voY*A>7#1<9&)~cZ|S&anQ|$|kWP=iA$?hVt_AyEzb$A6J(J#b z(b~9UnI}f&+sXC9r(XZpyWIN~vl*AGak2ug>qp1nJprYp)!ObH^hZA1BQs8*SFs}U zY-wLS7<}C6@@Btf@O4`381ZOtopxtl$&yt>8&u6961B~py|ML&fFSxpdw8fy}8=vbI@VtpkGq0r()wL&`Vb5Z0OEM#> zxG(hv(u%cpvaKz3I{heh)bx>N@yT^L3dRqbKp!u!PZr8KDx7iO^(n$E!f7FE`e0~L zz^r;SMa3%%mVp3vfwtnm|HOeT52yVx<-nciGv<@Ql;kIcO)x{#HF@}}EBDfl*`lp+ z=Et?2w&nGQUD9^takYUq++}-h#y-A~2=Q zov>zk^Yr~ks{q3Xv}ncp-iY?wb*UK~zA?RenkQm#-Y-Z#jnJO(Si zxLnIzI>IAgJ`uL>>Yo44k~D`)wp@O6;XePau%}{i4wx0S?z%YI%GEE#K_z|qm20%} zyI0a%*ZMk~1_Tb6Ufnx$eIbZ+p@g_qpRzNa`v2&9?|7>J_kSEIM`avkWQNkP!y$ys zWRtxqI$23%95Y2^ydq?a?Cl`y7#U^faIEa??7hE_r}z8w`TgFf_oqK!w{EZVcwUcd z+^_p}zaFK9d@8YzHL`VCo&4qd(rCW?;{_502BqLcN9s)XyxQZ{XO08ayy>)K+4Loy z$&R+J{ieplt2{)#p0DQ8U4>io_tI=#2=R`z6b>Cp_nu@8FPT7$f8l$tMT6a7O6;+l z(~+#fg-tDStiM{n^^*y8(d7s#S22dQT!$HsODD=hLyLo5ZXQ}Swcc4b!>X-zB^({J z_e|u=8$EukF1y)$xb=wX&$2-;yEHDxguRCw$9PoczV_UJ){eSsd;M+?nRcx}%SO>J zz-s)*pKfvFl(;ri@oj?ivDhoJckdGN9Z|7P@$#}L+V~z*Nf?K11?Xbe5_@wv6@tIb zc=FeDOp{FUcQ#|-UJr+c@~_WsZqNMqEW2K5?%;K>6}={KgTWf6!Q$Apbdf~v$8YDx zu_gg7hAzdX1L+D1j-7*<{arCl{~YTnvZ~31TqDU%1=~B3eR76_bNN-1!x}CfH>ejS zOY!prWH&AUxF^2nA`B{?7mC=9(vFnZ?2F_Dmj#(o+%GwzO0nh-d$L7Pq#@JpG+k-F z!X}ut@LZcUzAyNBd0%h(@!i$0N?che7hwlsCCHD=WQ;fv^sQ9~DPl;tsjBGTS zZ(w%4;0rYM<-DCt4_YdUfK?4v0@CuX=+KadeAa2*`nJjD&&S~Oy|HF@;%CU`m+S5e zRRikjttvdW4+U%VWOSK`Rm=6AC|BI{?aid8CDG=s3G7_&_>GlK!& zE4T=gP45eppDg^|FPDau#p7CM6@_hiBp(#)rCE@3?cfK9pze@}gV?wG^tOCiuru(w zL5G3@CMBEHJb%YFlXfHl@N(TuQU}e)1j_^&ge@y2Q@bmZW1A5f`NU zF}%&?XU5vAI1!WW`3Gslk$6yDswyo+Wp$zTr{FI$%|(g=nt$DLU9pXM4GZ(gO;v75 z(@IXFgJO3d<#rGF9kh_%?baIww~lX1M-~^wKl1aBMxf9uhSB{Rclr>kj+1au%p5Wru) z-VtineSMX>{#+vw5%NeXs!eBp3BONP){Mk%v+cW|qC!|uioXnrGG{F;*8`~f>{Z!sf7aT7+w3VuWJ zG#y<;%C`A=fiG;Sucawv<9H5}{aQ9os$0*yskG}E5k|rPEUC>=F= z=wI+8p`4mS*jqFx?ShF28sB-r;<>f3m`ekqtX3MYu1xKac;h!5`8 zp@~&XIA`Ls$H?NYB4lnRagJzgr3bI6nl9tT3Bgkgx=2xnms#~2Imh)!ud`nZ$C|l1 zq==LrqUJHBO;bqYx-??-uwy0v+fOV7vS9LDJ3qDIqCNMmDhuzU1p+;IHgs=5}K74Me`3) zoKL^)r!Ca=49I5vQlOQabBJGTee-%=M2g9x<9N8B_VV?Z{_97@HT$kcm-O?{`tiDZ zM)RQq&jqTMBZ5{!rX}!>>f`c&A?_LxeGWM+)C~PXa>vkxOUc(AoJf{s3zGU}P z<Hi{n=c)oI}eI|ADK3}i&t$&1kG+#_iN&v zweB}K>!68e{5cj7DVJJ3yCt9iQ#(^-%a_d@9eZok$RvdS3s+vrHvk!6h`}nfp^M ze`RmimzIZO9qfPlN|H9V6=CuAyo;2vJczcySLraT_sN4WTJC;F>d8+APo5$fIUC~I zjs#WWckJt4+CH?9Nd93VK?#oOKV`kuV+FEV+2oamm$%0RZJNQd8Q!11hYWFZM@LG? z-TE{(+poV!j!J6wzxG@$Ob@K<2C0jehz0opfUOqRW`5y5>OXqXF#T2#ws712w88>c z@_fl^QSvt^$7QMgxU7~JJ@KbN!|%{9e%#%rO!ejq+{wxo6P1CPSSw!6USU`-7!Pk5DbQ{z4RJ zC16*$k7UI^b_dr=k%bO)yga&o6yajbPC3Jeo zE*7TI-f5}1F+_K@LBO)w@|UGO<;6h)$s2auJ6y=&3%=M9R@YdbK(Z^7pYB$BcvUOP zh%3|zr?~(WwHXr;}s>&x<`1jufKuUn-#_v)oHM^;)72@ZsFhbvxT^m~^WjB}eIt z2{S0C>`y};6oNMPPyE>R@%UYG43x-(h*Y)@H!+g08CT-3K-YZ+-nMn`f+Izf!w;{i zG=NrV13C|egJID@j_+m%>>oVFv8Zf!rRALgfEmN)r`ooqA`DA>`ynZ!%)xa-^5k&k z`CNg%x9ipz;lPPbZmP?-BAJgVk=@{J;=K>MUQvC@!-KtFSSIz8z!fx=zj=X==%o$- z;G?>)Ocn zix+_7J|0CWE?sZD)ydiE;ObzG!ZV)f0OFZg7>6@FH z8lD;}E<*1njRD+O>+M1Ny6gKrrJ;Scg<4eYPh%V_LAwmGszTxOy$a%#oW)Cvf53y) zCz|d@H>fwB{Oz_0JW>nyW*G}t;Py&uBCSapFUKHCfPp~$i>_|yK1~B{lW^T^#?F^gtc&66FO!!yHqyl;Bq|b2 zP80{@FX6>%CGhb|SXP@KoI^;Y$`nR+Lc{u7KLMTa+_{xl?c3qjt*0?7t4=*Qexi)Q zEzrZM+HVs{2~^j(6}$fW-E|cPf;a>G5~xPO*tV`u_HJ%_4-C#kXo}(eP&YTyl0jDe zB~2(_*3J&^X`gAz=2N|}|Li<#SR{y=^irbs2Ncq8&HFd72lCL-*CHe%=8~YEz4H6Q zSpp8B>T-2)t95G%|9txfY6f@IkRGQhzobK@rN@?|!5Qr82p!(9LZA0K``djT9!4&R zxjA_$6e!$)e$%B+lX8cMWkT#I60NcnkQQ>hZ{33CIBNSowm&2kDKb>Jk-2btVae+902fRGm(nB8;SsQ})!%{nGA$ElVjA?|V z*j)&aK#3coMhtG)?SPCuxw-C?UJEiqn9ProSVEuMxhCR9;xE=`DUY_-)|P$(EL6Zi zu*+QZkETCqbQ1q+g``WFXw{%xyl%YdUi^IGfI6S_c8O?AFF;p8GP6hc$!jSRQPu>V zF5F*{v|-8#t`pg(NDHH4y<~oDOlBDH>?jLo=Zd*akDA2Uw9bPyuC0r z+3n`N*)=q|hE^R~9?W7<7`9Oh)@tvHI}RFeiH;6Sb3TKbnN72!*JcUZUVN_7*vvdV zOL3%a;vgx&LR3gn~cdORs{EOI1e<9LCGK1@5=Nx#Vy|GsU$o`$D{9m0jXGY zvZ>-s1M8JxJp*N%M#c+uJAui@V?w*kASA78SDM_KcG}kOVpUdFOdl?qX~NC&yzqWK zIj*M}0TQL9ncVST1-ryi}gRl_>DZRS^7j=DRia7VnaitH7`ASz6mXavo@? zGR%Pd;HTPYME0CzN&3YD1;+sRn8kCHRAks7YvsKP8OcU5qv-yAg5EZIzZ$yHdr3GA zTG<$vJ=A>u?%TM&dcl8}tH07Vx1DX=M9mvPL37P6)gzIhs}Q({V#Lw-@m7xueCQZy znJ3b5_-mrv)cXp=66Uca8?-4hywuLkb9wuEQX@jrdoG_RK$1v3$(47+$V*-P;ri@_ zNI;y59^B*YMA0X{7TYHK~#@bO$tz zP^x=d2pSbe@UPcW5GX&}nlz8A*;MwIv?^WY?(*s!CY%e<6Bx+#s|ggEfsp?_=liw| zdJ@)^Ha$X{{f!(>)A$$av;2Tzcy1(53+W0fsZlAYqzLjH=!8nNFoRh`wsA>6O!2<& z=>9Ea+z zZLT9l9fq=;2A9>b)zCdHaSJKe;7%{#v?x>0x}H@%V#qXwCJ z$iyQSS1%|bUa7$-x`L16=H8-<@}Tnf_NMYP5K*T9D5uT03Lx!Jwntx4o$568rf`C& zKKN!Ah2n6eC^O&1C!qJI0}O(RxW)P``BHfiI>y9<;CtgxU9+CDg|!IXsbl2uKum>W z>|O;{#j&HrAf|WxkJp$tzJHVSG?xt-d4-?5rEF1w2*N zsk1x5y-FamTA}+!o8V_ou{QjMof;+BG2aR2Cq~G7$Wd8v+N0+#XPZF zb2ZR4hL5qg%_^6Zt#GH6f|j(9W&!^Oj=~XTg8>S{JyLIwe=d@odO!gW$)LDojoQe@ z2%GXcfVW6)!U&Hj^nI3r!_S7JBcdNoW*?F!o_kueOKwh9CpbD*8;R4=ZG80|q%OO) z8oTE%-UbvAL8WdL_#b?ly)HG^<$}V>5Wx0C2z!?ELV&!Z}HE;QZ%6RhV~9c;jex(&gu=yr4HH%$@5x|peWJt3C{*J-vM}IZlp9eV#c~6-=RDT(NP%~GCF5*1RBwYVrCRqwg z$aD03?2M&`8o^2NwpsGAbzttNr_oWjv(Jir?d9s_K6Vokc}vptX(9~PmCs55k-kAH z?^?UaV;TXmkd<0{^ryeE<6XVeP-J?R>%^=V*W0r&$l z&smQ6nFDA`RE1g2RB|3~hxau?u?8h6{4k!&M;9r9k;oVG3lTw69}j$t;jQs~D$%mf zpYK&qc*s4~Bo44DJ&S4s^ z#ae%)y)||Rz=8`$c&7GAe8^CBt--Fi?hO*wKB2_cXbC!cw0-5|DFVw03&wV|5MWv$ zEQ0ajh4RK&u4J`=xcs{Hg^f$TlN52h4DS z6l9No`6wQ3FY+QgoE1^*aNMZrns7ia4+r%|kvrbd&F2`{VFqf}SO53gDHbv-(jkV_ zF?(hLP5K3_%YAF+O7vTh>Z#r~{)y_P!+?t$#=0cP3ehm;Ge(fpSNx02WHPb`4zw<%;m9G*Hv1E@$nL-QY?sTt&O z9z{jyfnb7L`x&8EvD0Z}#|ue{hUJZ-oVeZTS<;eA^aPp;8`IK0VB zu_tgQ9d3i!C6DVfiLS14&K4O#U7W*+K12N%X-RAG%r*GpAO`*U;g(BcO$Lbl87 zv0Sqg$_Yj`?v%d3&uvn*X(7g!2}J-pjCsrVuRsSG5atZtqV9ltI;a7F+;4jUAjEk& zGqEEL1)q74-jh7mB#&9J+xc#w9IVZKPrA`-?K-RMey{TP;K`DALfM(s3|H$CQflX5 z75U=*#SgnCOH~H7O=EP$L;u@r;AL7VNpPC2Wn7k^zd1dT%Wjke@oy-1>|0vrK!!6b z@ES8>sj2W{%M&KRa7iz|1#^X@Hc!|&a@QB!O_Jb|xDU9CAK z*^?g@RXot~HZFmt=)jM=J)Ld!b|HKKJRScK-q0ZcE?)00{P;!&D&SXT=Pdp2SZ`sk z7vwXZW{;TOnf2Euza(Fdm8980fEk*8uZrA!^@==1=mGl4iG6hAzL!nm=E_F8#G28v z49tnxW+l<-mzL+(FanH1b)4JGoeR#yYlwfIn@u`g(6{?Ps}Cr-JRZ=DsF^PWCJibN zfRE`C#GBClpBe=acB0iz9C>#Mj``g|?tKcXzD4Wd0VBO{g{R)@Mmh+F+&b~Rm?__@ z`Q8a}Y`p$uwFR^VCcl&jKE<3&CUA{*v~I9J;wExBSZ!y5=q56%wplgrUU)vwcSbz> z417em==-}pVlpZ&ex3ny?=dl8aT6j^~T4)h{%0TyFZm`3^pH0qU7iF zDp+m?4|#aGn;OU%Q|6YmCnjujaW#vvn#{Qrz3m+*KCD+JH45n#NW5g&Hu5$o~|^Drq> zC;%7P~mUzI@Z%Hu?s4e8=oF=k}AE9eC_6SJTY5a|P%F#u<3$T%#+mzw< z!QsCk*A(g%0#j#rbEmRwT_;q|MTwv0=oRQx_{xY!gF~-b|KZvCGzf0ddTe^&L@dJlNi0yc7%631Zg3j zaKbNe`RF%7V3OoP{Ac|h7^QyfH`M~d5nk21EF_sjAPz=}vwKY16o=6ZA{-kVpvptl z&%&T>wi4Oscu;)K4N3)2l^NR8TYZJg>2c1r)DK zE!(B{;*xyT#$G9A;kgza=-h@TNH0cR=FXy2K>d%-G8 z%_dZjq;W=Xnm^flQcPQi9UE9`^_Rc+gd?^UiMzF#h!7%G-kVU&?pCYq{(>sC=)z~v zv_Q$hNA%CzdsbVpjWW<&=7d__VSrZ(SC{i)g&rr(w1AY3n;oO8iXED5mcX9f&?oSZ{BPn?IE9plFX*D|9GB>czmcKEg5Jq zRmE6>MS+DHGJHP;7tV*N+0$axC4}p_nqpJc7Q0-Rqg#Buf@-NB3*8CXjg$Y|D#l35 zG1>2?{u5RcWD!A>6EMwNJ7+%u@c*&jObhhx4wnh*rulvJ0}D%G#%<3o&6wXs#_2G% zq%p(43a?mOge|7Ea%7Nfa9SRngj+_)jiU*XqFMa+> z;>o}v%k-#b!VIO5*xxMopGF~&5(A#H33*DLIX&RqZbJa(i3YDele31u;dy!U#O4Vs zEqVGDsd{<(8!3CTDEE>*Iai2o18%yh!L)K;h1#YYjCF%SG+E%-P?c&EadozhMu3&j zQx@3!b!MZxyl#Hrxt+DhP1SoWKr#{PfGK**`xmTTtw0xru@RIn6xN(JZQ8&nrp0Bh z;(1?qg6dRuNH%E9ZQ-p^eK*@%fqo~?T9IL+A6+=#l_yypdpwg*KYGs;x=K%~sqhdr z+NLTKfAQ#+WKu*l%X@&dKOEcptX#K^<$x?^P;R^h&D}E+!pGCuIH+9P%j$9 z?cqn#81S1uK>YPw4M0}^o5YBegKa?E7Ps?Hkm13c4F)fxXE^)V`F!4%R!_5$EbKR6 z6oTeCl3vG70xD{LWhEO!qPD~Rw3_UGE?k0XVsmhXWcPz>7#yJzv1K3P2J{b0D6Y9A z(c@9d7OuZ+qaFng0fZc<Y)E@TR1AW>^l?QyF%GKfB4($q7P=Aal z1JqX?$cCF;pzTAero+9f0&C495Acvb!A&KG11_Q;#Zm!CH23fKd%?*Sxlxr5R|Qq5 zO6geBJkNV)v;iGNrlG$SnCTiQ-`W$$KK2|wWJBMgt3D6pP!C#Ca=@kZ3^~t$)adB? z#KlC)yD6QfsB@KE1q-0koxF*Bpycaretjs1T4{gwg#Ne_&vyw>1?Zx#Q`$LUpP$S* z<`JWV#}X1sG2dn9>~z$Ow;;^|dZWn4EwC{2D@BeMM_n}32)Re0HIiU)UZ6BB%3Nii zLx|P#EGn&Tf^5)}Gi&fGV$DmJIiT#_s~>ut*0Zop1^>C0bK28)iDEMy#?Tg6nlZ0E zBPNFP&7znKHime|GDGwqRm-{6{b%ude206X z1JF4-644&zGw1Sd@2X!=)n&v{#em>-v6cLw@X7rAe)e3nnlF|$ad6skE@w?NA7t-}+{$?Q0_ICt{XI}6gSEO^&cw3$DY8Glonsy| zpr&X@y&!5I1}5t)koL)tBJ=b$OYYo2rc5xGH)4R!xoX#)gu1 zql$jWRPXFK9)D)o-yOM)@T8;rri z;P?j|1oYO=NEd^~8(x^Ma%+S^0}r9TCy=f&BPonjP=)v&ro!uUR5+{q43K0|NnW#i zY(XYcU2awq?ma&>Lkom5gSU)C&@3_LO%tmEpldd zc+qnH={svj!hU6KmW0l>@-$uVcR7syc51^O`D1n>hmyJgq%2({cVC9PhEKh##B zbjOlzq}WZGgj}tVIOnDL1&DdEZc*qpU4V1kmr?gwzS#`Zfp=VzB_zBd4k_MCg2R)R z^*501x$iL(s=&v5Eu82lc3&!*p2@J|crXJ|Z8gfAp@x5-11Mz=`vC7ExM@xg!-68p-9GGXorq87A}l1*Y`nc3!*!`@GP|VnL5q*f`EFQRf-L&IQ>Q|D}Kr76@{lIm~*N?CG;2qP5i`rU%O46bL&$_TmM3zAPN3c z8x%gqwQFtw@74s4+yaTgVizhQU~+m{BT(g79c_R}R-~uQjDC;2GaH>UVfkx*^sZ0Jzm&XUA&85 z6_nZSey^NTkdn&m(i5#mbvy2mY+cuwCPltsXx=Y#3Sxs2zHx2dZQmce9}c*RVg6c$ zy-JwmqGh*P#Ou=|^j~X;Xq&1ZZEWUQvxiT83K3ZKgs8G0K9l>qFZFv}bxw?F;{^4b z!rwW&GR3(zirG}~;zg5B%>#zLDi}vDyU_~N=1y!#OxUn}U+DXoiZpiqG>)wNjcp!! z3Un8nK)sJ1W#s|{q*`A76ylM%fTVaOE3BApjjOY{VlnnB`Mk|ITF89M!t1@;9Yph- zWudq-n_qfAiwRRxB?hH-zkgHcxoF}$l@V9}J*e|H5XJ_^ZW!SsiS&<%4>|0z7SCNy zZu(8Ode-mEIejddbu zeErYY0-Jg>ZarSY-qk05>GP=rT(_!N_Q#>g73J?K>Ds|GDm+6xEk^%i#n6}<@dd(-Fa{asMkpMETvm`Aq z#N~CmB%qXkiS8ZAdhUxWbl9AE4~q|#s{AADY;4=A&P-v|GUP7-7bH6(k1tcaYI-S- z@rknl_X--%5>nbd@|Zt>5dR59?qv&l_>lJUh3(Jzg%OBA%CvP?ss2B2#W9sH1Zybf zFkr%T-kGV~-Y-2aHGj*|uwB%(vdb3>Ct%ppVZJoOj^h9*hOVm#C{D39=j<-o*xkpj zW&pM_eMgaHSGBFYckTF+&B$K_9zg`KK_yJM-H$b$I;DOZW5)Zh=@APoFQbdY|5((W;ajh!uYgJDLD8<}z!`G=Iun&}F7VaY~+&;c1~7 zVm6{@2rCUZ2^4ze(zRI+)ZH^@Ft;%+k4pve^cJK40&y{qhQ62AfbS&>h>`bq`gq`5 z;|_VRsVAkmIdl)hWXYjsxqmP|D?fkp+1$r271eCNMAZ~84v;vLs42jVOSr3fU{i*)%A`nZn8o<-#B>;SWlLvt8hK0E+2L+tQ4su ziCtB}ugaMQ;81dNhBTD?c`%9FJ7+zhu^<|=f3@MC)+s{IHE37_6yH+^$)S|sbt52W zZB$bnj+Y%U^tUICwND!JI{MO2SW>j7;HI42JzF=Q3C(l)FYXO=lk*EV9u)zZnR`Nt zs>Uk~Z;gQIA>w29hL;D2_io_~8s_$*L=gwUTls}fc+Qjj49Py05gPsuMjF!H{lc`6 z7Jm6u?o}iKR4a6^*S|`Q$v&6|N%kp(wM|l*yi|t_xTMS2#_ge@UgvZ5NCy)v`df34 zg?qxm=LIr8(3fl?o{PegRuW@oEfWJf&ibos1;hyCahf?yGPFX}kw-Sgq~|r9(X^v) zSCcR}+dvWNb-a_Q2cZ91*#PusRPwvi@b;kI+JkahGcef)pfB=ne0vV^)qs@db0mYF zGI59D=u#!(kmlG~KoQ@1C|F=PWCLao?jr}Ev&ZMZN-nsLJ-tAPI-<0R%-jP0l?n+- zN(oFa*#yD(!}vD9NK#IJy7b)deP$7+N#hD}9(A3Tw#xE&?D!zGggv+??M7uN+M zWeekzuVqs@Zvq(~x^rJ@RS-3ZGea^i>nS{?Dp?0oy$TD@4RG0~!t{iR>m}qNV4nYNU?R4z?Uct zj8^O#*0Er1a7xT4?U3RfjxPjQj`scwXt73yJsr;K45E<=K7gC8Yk;Xy$(}Q%?J9Qs zIyJH}MOiD+btP|It49Y6>&i$`%;&FcY3GYo^3FtM`sPaPvvqoZaQ+T@KKFaH3f<4$ zl;_F^l&vk1j+-<4qzqP@$|#3-Di_)QI01e~gWN5Iz=M{d9uRdgqaL1+kk#Vm`kJqC z5(4*ZkeGdx`fonx(RI7W4v@m^oPsi^bSWqv*FHxT(=P@CH0_sR9tNcXpw$SGIfjHR zJ2V?Wff^SL%MIpJPYFlyB-VOT2)y42Q*T)xK)rJwPSGHaif%b8LAw|^(m@-$$@N$i zev{ee9Uz(4-cpZFBunsz;ozT#If^Edghf7qXme+ z02dStbAJCE94=CBzUyU^?|xbh7L6N_uL0Yk-(tojBM=Le8z%lrEr&EVfq>LWg^}@^YMHt0W9sDs z9b}4bJ#@28=1SBr(va3szy{8yhUnlqrcdRU5M>Uc&m~viA>}fyE5L$^{}xk^Wb0m6%saj>e@xPt5a1uxN}4R5>8IbmjqsZDSbZ5-*wWQRhv{ z__*Q1kkqUa>ko)+XKjU(Us;R+E;AKAZET#_1tfEaJ8sSA3>}Zx$c4y1@-%-D*O77> z9G_GJ^2uOGyQ6?zPhi2bY0O9TKR@IswP646N(7bo(TyGIc3pp(V=(WWqWlJQ zry(+A?Bje@rk|3)ZBD+J?x*B{@6?RXd%rh122UlX)_*!N-ml$y7b3N5qQR+8rou_| zvCUJ3%}?3V?vjh}@IWcUsFBXBn0|YSe$pB_`k=~1CQZiQCaR0{Y3;z(uAL-rXmfC< ziR@f5mvqWvrGrI?`ZE438_Jpy!RkQ(nIkhB(f;7ECm6FkP{kYAcmhHUb$Q3m4dt^p zL}qW~?dr@J1P#U<)juZ>)dpS7rHck2By{w!>*U+qp!d;AkGBIo|BU0k)_zzztLwfg zs7vnGW_DiKNpXxCFSvGvy4?&lUM{#LK~X$9J#u!}WR2kJY=vrmkZH?lLxn^$ZGJy{-k;D_uhwX*+`^r0*?crV-N z((3g!RM)~&gEd?3owMC%i|Q5pxviZ{lNx7;peqOUsMIzTyBWfRcjrFKiVUAjjPFNF z7iAg{t5I|=t1afKv7yBLp0rM#dD((9o98v}gS#HANX<5aj;)qh%KKD(+;U*#U$=A| z8WYe~>#Pa?UIQ?LXqSqOC9-IaZH1PD+WBK2!wAiWSq06w(&VR4eLv9_M=V_(FT4rt ztUb3fV0$6f58P1WHj@bwIT^?%EB(LOkcCLZof<$oPd$&|dTc@O*na!lEmqkY2}sjd zA@PnskYHeamjJEkZ)5HB3vKb*4@8KV%I?EKYUP@189PdIc_df(E% zZKhg+3M7lBb4t!A+K`p^YaOM(@WW7Qq(?p*?#m0B@^s%zSgIf;Ca9i&Q!kEPgp`jK z57r%to>l^KQvpKOdv*pyzcPcz=es~ZU?a?0`$wBt(5h50&s9Rc@NtEtPg&4iI{>&| zb}puZX@Z5`dXXP*7AN{1qWk7wt8si6knDZ$D95;-TNwBiXDa!>JD+%r1^QHZII}4; zyAJ{q5sQ|qEP^(9M*i6bsLSYO_xahhye}XKo{gGyLHd#{L2mOjsc2iWsAt2r=^^cF z%50c7R^Y#`;PaDp;$K1@<>aqc!y19#YG-n0qC=oQ_B1%6P`L!Lf3`|3=YJvcz;&G# zPJ^Q6j)hPcV8v^vO#v2dVQ0F0Avq7nVp576u$~;$U}oQq(#wz+d8wm?4eGer_LnHR z{o&AqQno@DAVH&-;OCy8?tNm3;6^DK?;Hc!J;dkwlm)IHx;9&F7@3mBTWJEt_UY%}uso9qVh-<5NhCru z3?j_#U&hjp-qnJ#S332?J^hibk9_ROA<*vOEE-9=nx^WPQ9BOkD|2F1fvie|O;9Xa z|5CRL7@amVSUdqgRs$3ubg`&vP?Equ2BC8JH!y2A7v#X7_u zlcN=l8DOx!&-P%|fizK7{NS!bQidK2>1S(AICE@YAA0g$`Y&23L)oh*(6DqbuM1cs zM6X~d5+5+nRigiKhX!Hiw3HYB&8noFcRpKRs_p+nHXP*^PY`?4&qJ4oQF*om3Ep-{ zg}x`7a;pk*EXmmWT`fUL6hgBA%9@2kT=5`lD&KeLRaxB}0}}(y7^d#?V5Q+Da~uvRVRG-skS>N> zfmjk;_;nf5OW&&eesK0_F`&i5`UeJo;jBS=)Uh3k>BFHxU{eQl9pE#er^c0g@Q_BG zKMyQ^(15?R&3KeO&^apFRLh=mqz3AdN7A zyf66&V06+p494X2p1b9m@j~uU41XM`!tr0?S8M3O(ec0_JrL2yynUqnKSd{-&2@hT zqG*6vSN)=zgcX6xDx~BCaV{FBVgsa$r^bHFw0<4D2C;ByMF4zWGw`XX)fT;IZ?ML3 z`uU&rGFs$$2CH$eFwS@K*2;I^$RIscIQ=I7%SjA}zT3)**wH)!6@Gx~xS0#`Go0C& z9Ho^T$X>qz?KFQVlm{apG4V8d`OnS;Z7T5u4;?+8(mqz{12)DIl<~MnUj&q8VHI)uyoc^z-nG3E;tlNlb{4N`)leP5XdE@+kr0Q+%nh?qK2N)Uh@F zPaX>myaJr-rAC;bLA%x<^!t?5l})(=1eOxSs#<4P8VJ1UM-Ax$IKdmoNlh__0NgcE zPEVa1%gGO6c}5m(8X8GER7!uPv{w zyAlf418KtiFYPy=wfLVyw96B#o)nd6Uv%?g8uyU>{>+YncjTTabS2%_K=+$(cP6GR z*s;i$R{QiF5o>Hvsd6r|^{?U~C^z)|PYqa#Co2S#r8lqrCStDCV@+vTg#m9(`z7U>1j5>n>h-{{6QYZ56HSNgA5%q`^6R$8>P-ObAR=v5L-A#yo~RiXp+EM z{sGW*{!|$it`l!6|61ALP-QXQaDU~(qubJ>B?dVS(2f!)E_n5rAQlW}yx6YtHe4xD z4@QEk&4nCqfimSjsC`@we8S+Zgp}463bQDS5}!(7WkJZwR%hPNL${})rk|ia0pk4B zn>Q|RM5yp#?4L}8hn&fLlrV;4m%{uF@wC2#wCg7L8WGulDupG6Ql{iG??EujpNlYr za?#&5G24&8aK^lv99q{?Jf6~@KQZ-oEv3Vtl+6nOk>Qr8VsVESM~<0 zo4mn+o;j}cm2QU->_K2y*li5&RtZ!h5O=(xSrs8I*4<^H<@Di^;A+!M8GjflxL=5w zW(zM6cOfX*22In;#8{o&`|VEcGPyDUnSH=rKY`a{+s?-If{x4ky|cZJ6P7-Hg!d5s zG+u9IXmxUUGJ!o2KE3uD3ACZN^U80d{$1qo4wC$?M}EcuY+?imSh3P#gQRTgdD>&g z=T`)(ozm&@Q5OlVSc$~hzYD~rrc>9a;;y!T!?oQ=ojut(Q89=c_8|xGRJRa7Vj6O48Md3DkPX218hZ!Yx)HQ8@KY5 zj&J+TvI~}odl5JX3rQ1uSUEw!`jSojizM}r5MNv-^l}WWgFS|xd7#6ga#^)>$36U` zpyYq*gdbs_jlteRLe6fEUc{%WEG63yANq``AvkRO`v||U5;PuqP$K)(ga)PY1_;_O zgTEp@0=T<-GdGWUX6(lJGPQd3bUgmA zvVv9|da!W_3Y1}?3h}WQghhTFysZQpD!(|4)~^CkB$^7RElJe_pqH5LA`Kds;0w?#L4S0HF7V!bqZ>@&xLyGbLx$mo?;Poj2d;u|d->Vt=}Jk4zezM`i~KDuYe zM(~`%hvxwUGX1pGZsxjZY4Goo@J2t7G9&X_t^}A9_Ft|hX@A%*?3{NWd@^a8r3KJJ z(FgprDlOH8mfC)RR>o`-QVd12)*?2$z`P5slYR9N9up10Nr-cGx>Nx>!xP5-2nlv- zh7aaJbp5w{JOo4#6>MLxUuevf_TL6|M?a|hC-9QAtsi4@b)kGW%yigR1>%CUSAi(& zGe}+EZ42%(L7*Fl6^x2NV0du$MQ#uF=zsFUk7N22s6p*Lb;4e-Xf@qCarKzJN6LvP6YoN&VHmv|fxB4&8~Ft= z-9=-GL;~%1<6XAQ*$v>efMBS5(Y00Qhh8(V?^wvBf*$M8Cf+3d^rzq}@8Wsy_fzME zNUZFjkq5xwJ#O!e-uHv~zs2r4|C?;+wVk~B+Yll5(5Y2dg;rks5Dcr94352CY_} zPNY@$ih)(5c7%DrYW|W$ z0&trUSbWC}8*r7SnP*n!)*_ip30HAHydXA&-+%8bk@D?^>4l54&>9(pKaME7j$wD&7c7|*kK)TW>;OT;*tErEYB|I` zDz`X9XT^B@bsIg$vBHpip1-Ra%oh^^cFP9*)5X)(16+6BcPJN&<~D%F9{Qi*pC}HT zs0BETAYbZvI2(=m>8U=`oRwDLXjU0uS1*Oa*sR-jG)p?yvDLGO0w_{KL01%wJG*qb zUx4U{O|TGEgUk@^pGGhfELL)6L0Z%^z!xF804=OLhZ_ZD)hswo}ecYD|Y znxA>*j2~Uf)bbx19 zfSPlMbmMZ005sXaahl%v1J4|SVstP4Jis*<+E#pF9;XV%GcUE;89*)%ZcFMX?ts*c zY5nN)MT2!f?D09S+QCP$)WS0Ot)Zc~y>X|qT%B9O*f9VB!S?GIydptRdDSk>bAf!4 zQd0>lt8Pww(|W75oYaFjo65HydN6A?_BON%1FkgA<-pU)Q?S~pS)S@1;3r5~zj15s zpjHwtWyXeZXRjR}f3XnR;D0OH=pmiJz$G8(UZIBf!9Mwl49EI8JN% z(#$RCrvPNc;<(0U!M0@svu_skSb>wW_T=ug1hO1b$Ur%^zseY>3I~E=Oe~}KB%v*n zn<3REF|}VMAk5cacpffgvV6%*6H%5apW6qZWK;Oe)J5kk0WJwdVesA0f9Jsdv=DSZ zWM8R}+qqnfROb}f<E9#k1V5x&%MuRy{qfC4b74!K6eTSejKFVWBXdzIX>j3 z$0N>JTJ#ETd&T_sbZp8Mi_{N)Dz#Y~Sn_0ivq7so^iK%)3@6S8k!iua@aqvjc-8JE zsC)RY=FfdV|Faz=6$1(b+8R+$TP65{YV|0ZpCR(**Mdeg$al z_vObOwUQ=!XOKDE?Zl>L(?aeAyIpJe0p2P+gdVW!&puz2<+Dikh}G}0WeM6g-`oMx zz+FU2bgE~34YRZh?|VYc)?N9xIdp+D8u~X|)zfQ(4f|3kh-Z)0QFPoirb+gInRX zIJ#ofXd7zoV1YS-Ry1<=^kfn&MoyQdI&Qm0GehRJ=g8Y1H zaQiE)Z}gXng+b=Iybb#^2(1fu-qMDv4>7|H^sJvVj?k_!Id+`@Q$;>9vF^Rmd<3nR zmS#!YzSLa4H0}Xj*g+jEv^CQ>8$xOd2DgtbSFBhu(@m9Eb2@IzdK#+XV>(Yzo4nw- zBCv%_j|O4Na!Fn(0_-&3$}`@c0efqy0H12K-xBCJ1u45)LTei@-LM;|rQO#Ra z-?y%2hD+RYg5D}DP@t|>BZ{Cx7}vzC+_U_Y@&DNR?s%%-_x~48IW0M|vf?O3RwYUH zY@sC~vl2-N*&L(n^llLuB_n$)d!|A}M#_k?_d2%Ub$fsN{2rg+??vPS zoG$mSS06K|JkkemRoUc7wTPaXOyN(eOI#0Y;9pZLD!QgoC7)NpKC3h+ar^j7euU!q ze6d=D+7@q&6K~7(J)4rCd8#aGh-z{U*(WdC!=?_8RoLyQ1Q z>Hqt7xpD+=XKSu|YtVF|4t8_;NeDLm!Op!k@Rr*ezUo&0SUir`CEp2p3>~g!O{RJ} zvd`-u%G)_Egbz=Zzun0Y{@`gfhYA1KB=p}kv88yb`BQA1RD6bgFcNXfR2-VVSTy2y zKo=Vr0l9h}I>_h3Hvc@-Icp(C098oBjkp+$59jKX7QsFK(6%E=xK+S;S6LvTAvDOE ziFoD0Jnw|iA&ttH`ZqDRs2$@k53w}iQAop8m!Z+ zb3gPL1P|*jg8O#9%%RklS~2CH!`cy-tNKhS_KhjTJKt~~9h3+?w5>Ox9CBpn|1*7z zuM2dl8wwo+M0cC9tn<$y+C?GpxGN||I+jQ;pLHlNWGR--dWFcU-X>5|hXZoRYS$BDIi9Yyk@`uq&}t+NfvhoSS5 zE7=mBs+C+MO>6czziUgc?xl_HsrWSeU@nqW8Ju)_CSuvpNsAFO^Uw%_`m-AIViWHH z^~c*NYa;Lsr=4{Sj(ux*>Z#-q)J1EwYLlD?1sO=2+(T~RqgFE^p=F*@(ncTsbAN+n z#@qYpgNs%65ApLt!LqeyI?NwR+?zQ+M#%?8r@?g-qIMhF{ym7KP$r&Thgymw->Trs z!dOV6h0Zb&`xB=eLq(kT<1+OL4_MgspH@q^^rNf4#-6xqoFAeXhLiIz+PL?!Cka$u zoq{qUbn6E~LU$T-nqCZ8tda<+-qXk5lAl3PzMQ7%tEQKERGdNy>bYZ`A`Qp=ZJP1UNHd zPlA4OKbju0-PKLnF!Hr{R({H7sO^gZV$45vYL&uRV2gqlZw$~zHrrpi0RhSSh0VnO z3BWt9Tl`lKK@rs}I9cTVCOr>^?%%=!>tY%6czf|i@ z5Deg~)B4l2*mXvM0a$Zn}H z{rz(EfWY`3O7(fww0y?fOW9P7x##?SYwY__@9U&OchmfovPM(XzF+)Szg87)4u|r& z^9wpy2ZKjG#8Zv$qXiB{JzW~jXKf9NRjyCn4FU2+-H(pmKC1M0TSR94j-J9L=DMmK z4tPI@cv3CLHMzV>azy?yIvy7Df_a(q#e5Bn8Ahrd6fA%pW6P)qm+3zE7CE#kL+2vMOLKPxVs`v9Q1Wmh*noC7z+I zvE#%Ru|FFq)I zoV_ajFEq2HU)`1HD^Xrt)A~jo0_On3$>ZxO=hca!+O(xNLvGapR-f)h$=M@91DxLE z&lfQcW3|RM+d?lF246pV_1Qa?_RLjEP1LsbWV(hJ3Ed*c;89Z^pop@i1@2!r#pMqLS?(a34Ro6}8dtm1YbC~(NFda$|E zx1A>d(Cwz7FY9v5KM3T#85mwwjtYd1pqZg%yjX?2BFuDjR|T`zg@ z9Rkj_pRM~4bGS3e74ZO?ow5yFliU4n>UwkBBui#74HvdmM!Q5rzwP0S9V72Dr^K_Q zqBHC~O3|v!Gv}Y-_b#CJTQFX)EQnXF5ScMvtKxkSiUjW>3ijRJ1v~065$ac{&9Spz z(=UDTZiyP>H3GXGs+o+W&Dtejp7B*_f$iui@6|0kEFNBAQDL2j>wh(JIjosyD0}R< zEwZC%m2{I|7{}G^XunOb7`wL593+q zIZdJno1~qAM6Q7FdAE*4`f3BHi(7q=So!{&E0>&fPiN5kkDE8uF_;Iv6aF*2NsYNn zXQ-1kUWk1Yr9~Gz-wwPuDOhkmMgZ~JThH_>N!-Ax-Z)x+TY10)m#0E9Uy!7#%LOYi z*ZmXBGZR?Z`gR}!Cdc2NC=42BVNcJti(q3fz%yAy;+uj?m-59s9ib1h-BPo#K>maU z6yVoO+gT2jbygK5IzG$w!Jn2(x(8PN@gI{#8AofOp;LEb|bm@V3 zKo}Py4yC3E@~McDA-3hX6p>7zpijj~Rtt@-K_)AUo}CH$s5IQ!rwR1|skblF*Gu!n z)G>}qgadRf{3~I}2`bj1Zi}yy#r{grQ)Y-xz(2j}Zb`!?g3fM_3)&q=WDoV@7d-EwVQih%nvUza zp>-FqCaE;PGSTVD{qcdHj_+_|PTjfJorsKxJW?lWf!TQn+^kMH{gWL{`~r>jF!7BZ zymHKO?RIY4OBnE-t(p8RD?jzKRA(K9q_F%07T#xZZb}oX%=in>@SXhRZF=+`7tVLt zbt#VM;fhsE8{hB*m)=%7=W=f+mZ=V`hjV7eY6 z0;FHVX5=VIQvB=3hZkc*3qG$<*U{T37uG|cKrx6X?$RG~yu~C0hS|AEjIK|YD3jfW zo^4#nVKZJAH5}=B8Mme%_5`79&<{8+2~GPeJDUh{JL+~By$5YSIsQEn zIK=G7-Gf(Z&^F(7LYcJxndb)8!U1*H5&v6#-^4%X6zCZ zkIOaK7F6m~oY-e^EI>_oo%IFBN^wx7ouJY}C^bc+kjNm!{tqpm> zsgOsihHl2L=j^+`zbR`Xh;bU3p_-lEMh@#BqV0Q^9ZLtquh2j2lo1hGF;J;rt6o!F zEYZ9H=XY9R-s98gX0__2llP(;uV!R;t_>r%ElV3$NJ zyvHZ2zqFX55s$1Fo=FsU6Bgy#uASHt{a^vrd^#Ci9)z}LWi9vd6Xo2{{s{;3;))hZ z^PQO|n}3daojcrl<$;s*HreX6m5Q#_ym2Ovx97M0S;U42U6)ZVtdp^<{dGqVo71s3 z2TceP5WKlFg5pt6K3hOQPucCXLQxUF}~hD)s7_+hW)`lI+BNd9avC?<7)e|lj4s` z`?ma6Pxx*O&vpvkkaKNR0lu-tFZz+79GJUG)Q|U7ZK{Pnus9x{((0z3xZ%z;8yJG8ysY0*1 z)C0{v=Wrw-|Ja!Ey&jzpc0vZ0rdihAGv%+c`;vZlQaXbKt#-UK6V-cd(#dDgOsrbY z^}`a?6u@zy{z|;eeYVPWZqLrFrQlU3aNDRk=k(q*_F@C60>q^~JTjWcv9>%|zji{Q zy{2_f=+)bDyT)xMg<@RBzuuXAC{vi%&CV3D>u3>*cFCIlOYuJ`S$YiSd>b(k;+}5u zyTnh8t65QK-Qqm*9B<`MiUnq(>Q7zT@$_SdG*H8FQGa6ga+=}gFepxIFtu5bKhNx# zBKjV$!Su3S4@hp)C)-0Wc;~pe+;v7Dig}TICN%h6@%j~yY^BQ9tToEIGF&BnX^fJK zA`Qg`iN4jw^{TMyyf+x2F$(TD0@HO%c`pGsxjq50sCA$?R0G=dgiiCM?M$2otq4T+ zB8{el5PrR~Kri{ccanl|3zv0pWf4O-*L5;O2Vwekkk>$_wu_~|4Do0F|JeE&b|bbd zJU>B7EebZBia!hi!I5@o@C=bGSuG8*5SC??J~4aE$z5{@CcnP-9p;>l>t6j1*^|f7 zWG+Xz*)V)<{y=I$gXW?@%YgOH$|Cx3&g!1-iVPTnBRTF7A#CALkC}XnE7|Ot1v)o5Mf8-9fZRQbIw z9oe)k1L2{x(90(cMVUu*fGa9;A9&vO^ExPn^}=+R;T+^k9x9e*4J*4>7VRExY!l_Z z_4?}mhU=3%cyArM<-iijD3#_Pqn2ksZjM4usk6pLIz73!$)u9N); zOma{WBD!?tr+qh;Cc1>_S=XybL*YfBS5JzjseeLM!l#6~gs%zB$qxpj5_}wHk2+D-l;f{<7ODvG$rglPO)jUrm&Wdd5r&^ZoOAxLBd0u*u?*^- zc-QxO4tMqmjFsp@wGTxsiG9vO6bmU`eiL*qR(sP6rAL?ETMi`Q=Lj-txEYmmmbf_; zE6exl(Pk(QNR*PLegLiQ>cjbOA@Ll?!t)4{v*)=S>l$uPj`%%l9ceG`H|`C5*Tu^! zAugQhxY?PBeb>mHON8+BYzxjZm&$_2>1K<3#Vj|-ip@~LERK1|P z`KCvg24Q?&HPwwj#jom#Gp-O7IMp|35K76A|2+Rq1vk9{1q4q|>!YDi3=jM7B@>9I zm(IyV1%%O4f9EWZe{t!X8V)~xCz?=J{%7&Ed(Q%WxOx>E@mA#$oimf&E}PTubEAh% zm?Su-ZQff_$OJPvXyGjhoc1ZNlZksB*j9xV?%&s#K6;*=P{>?%j?kS4QzK9BPumZz zIEyfFM3PP_SSO(0?s7oRBFxLO_ExhGpD;_D0ItiL6N*u%y3AQTzIE&x{`xM}I>^2v z_S*E*WWH`^WzH?(acMoygMytEICV;Ch$*(??NEgt+sO;D_;ZBIc=!D-VMQ>lSB#Dn z)R2HrXonJF__WP7g>NUno&I+A+ZQy3#GX);ZtodavUyK74D*{&ft2uL)(~cYV1l8Yb*V=!h+q(fkpQ8Cj60y zCX@MF_s^V?>NS~6W$tpWaitWJsH_?9H-1k6-rone9eCR);ovlC_VvcKh>0&U%J`+MX4l{$-4dG%m>Jn zkx{8jY|yzp?M)gbP%DD6#d>`uH1slcx1~K^ zwY;ac7Hxv$nSGSQy{A$88?-V$!3T;c-biu-2MN;+6{qo0af8O{!4tpfd9(^%edbZH zu%`*hgOvEbh*n2k7k)X(^7r=Da-zeGIE_|F6EyjjR)+RxwNsNIoumq^{{gPu3pTjL z11%a^^O3uTm*-P;$0%t%lF3PSqvynKacZS3?1{jZ$OlDW%tDkn#~+Bf*R1#SXlooC zi%E{kMDzMfBJRM5J3H;<0+b4`KD8R?QPigIDn<9B{?-oCs^9D)ZZ9_%=^o7Y|9HT2492e}RE&jP zNBl@IW~*8|3+P?p&aZ>UO(EKw)VH}&f&){ZY(n1+=h;d43{6b%K+;dy30GnRLh(-S zR1=)&%W()Nwp^-xc65u}v8tb3HZG2xw5tMRPe!;Bb#SHIHhj_@K3hlVgNi0GR81+? z{pK_@n>s-#tb#I`T$>iAsZ2(p;C?r1E>F?M+Ijn? zLYr!)r6%mNw!WO(L8+ziSuK^&JUlCd)Q3*u(A7f#AlZ>!1{!ZR&V-JrPN~O^p-*{LqYVCM6`m!eNwo}bSseOwA9|1&!X7!^P1~bJp zro}Mkq=RYr73%pzC4pP(`|V7V@aNraRx00p$y%h1*3Wo#e_UAfJ!lLqS%5+rE(DpZ(4P=3eMQBg&O1p9Q2r{5%IQ(v!P)N%QgnI*yJDbp5obQ*hDA#r zYW?tX3Gzyzp3{!I=W|i#ne=6!HZ1+8EXB*P(Z}KnyCBmqiP!R2L@coKl?%9+CgeSG z<1mA{`8v#!ALeqhK-vb&cbI-L!bN2`4wDVuAMI`r_fzA0(Pi3k$|w=*cqFcH zaf`Oc9f(dp41jAJl*Y?RqizW&=jq=E#HkL{*EDFM*(-oUIM)H*G0f!$2yyZ%o6Rl9 z$W44lk}O{lnD=ySdKoSX9Fqm657=(d@Y(l%jgxHKCF8+&v?Uv}2b@tseuwivdYU|nk)VqP9 zuy#okTAK;}n$@^M+i*iXi}XFzu6qVjOs{_|=TXQ2Yq$f8{dCqV9u*XTDLhw#zYw&9 zSYfANYTNJVqg4=E$3PQZ{R_FHGCN8s%A|iLSqM{$&{33*AfjysAoh&Rrh)asaHzReA*gFb?(rCM;kU{^iydv) zFg3G zwxNz|{Wh`M(l_HQ)CnHqe&mFkd0Cf?mg< zv2W{d=$vc6NcBxlM9J`Kt>Yg@`h*XV9G>a?aS;CDt{|_t05YHXme92MhYRo>gF#oF z|Fw9ykKyoOCn)GIWj0Odz=rUbKaUxZa$)po7Yf$V;7;yz`*mA(Rra4wnbr536+fg%5n_~|GYaJcrgELpyT=e7p*@_{@kl~9W8SGq+|Z&y-Zd7 zZgO!%YpBSzs|Ty>|0++@eopV)AC6A(S0YY4-kidbi@)>T!2N>0c-p4W!g-ahj>uhd zJ8acwPVC|85)8?AS)l=R(~;6M+uYS?IohyN@6M7Y67?+{%9BFz`4p@5}Quy2YJT?jrCBjQiIH?T>CWqd+G61ZYjk; zMtl2n7Mz-*muvf2@GJ?wJoZ;kfB!ZKae52;bq|L0Ka8Uij1hfj|F-b9{#*~mw)tf9s5oHoxzq{pWSEnEjbow zBNEK*WKL-*OKs24zA`8Nyz$cLE@Aaj8kK4Al_SJ%YYR8Nl@C zY++W%zwjDQpB>Oy+!0hh^z$D9j$HkU;I?sDkxton7j6rFiEG93g%qyS)@)N=zWg?A zr<~1bv4ccQ7xmZu65f8W8((ApXfu*tq0g-eU4dzFfh| zMFXmAMGm@_^NiCA&nX3;HxL>6(BV^=Gv{>hPwcdlA)7M&HTOZ=#k#=q1JvNEeKSNEP3F1xF{GZqw?Y2&H79E4L1n26J2367@{ilJ{^jO0EY?Mk<~$awf` z4osfBsF?sJ9+KHhi$8R421LZ$y9s==04ZgNyE?dJp~aqkQeU8}dCp}nC9wP*b^Nfg zh5u23mPG5-F)Fv8X>&|+)0KIJj=g6W>w-^TP1C2uoEXxq)PU$V>&@xP?|zR3_@I4} z?)Vyhvd`=H&slh3D}=S1U9--0SnEDbpw;?TvJ`b_j7C)9n?V{)+^Lzh-k(u1YnGqO zj%4&z^6Vy@U2uhjqkePK%ho z<&a&enD0bC-l?uQ;JoRDZd<;VC{XlOw{sstTX{moCxfb1+1nv(}ePqa8>Mt{+?D8k3NHlDebxx00v}u2S{fvIGqe zZ-ZoNc(a&*bnuOb(5*OEBUwp;>USuc#W*EBexq$KS!V;&) z!MCeqG)eKG4zu`9j!21aqMO0>;mQM*IXz*sl7IL9_iwi@EGArp)>$)Jhq>>t0O210 z1cBAR`$yT7LXLRgk@T%`WulCCoo~F&@JJ_&g*D3v5FR><4m=C_GVGD-Z0{`WN>+^> zHX6%-HV-zQg^V~8GM0baZtUdb;*e#t2NMAX+e^_CZDm?~Zg;TWA0lnzA^&~(-@h(C zpcc)0%LipF$TesBXR)S(o^Oby0-kqyuL$TWl1gsb9Qf^1eXN>IhlPp^n(-99UEIIo z`r?`RCVRI_JriN??i4*24#+v$5`4w{+Nx*5VAyQU{v;VGy8CUzDNoZ}SB0=9{<#$i zUmjgqm3^xZ1{x^8i-r|zfj$T-=x14loc9y`jt@y`rJ6{=Cj`s2h8*M{xn*p=z;;;Kx>7ZQ}ZuNOTu%U$X*$|%abw0$fxjSgca zJ8hN~SoHm6#PrpC$%1Yvc<11u3+g!d2j66lEtbjx2Yre1y` z!5s&8IsZf8>5+(*gMSzG_pjR}Nj&2kc<#e<+%NDYi6EdNqSX;k2VP&NYu5KheuZ0w z5>HB(^MFG?n9HRXo?o$>_~s~eZkF>Afee#M$qQ+l!i49FB`Wthr>XL^#KXmX4u|<` z$>m3$(nL}5{R)Qh3xyXa@3^|$>+0MLvr^O!k5xqWq(?YyK;skAjq=_0v|gQU74@J9 z0r;@wR_ci?V)8NSOk%o#f=l)8+AUqzEPg5FtSL)eFHFl=Yw7AWn*qIf|Cntn+mn~(dS>DFHOq#A3H#UXzGPmfpJr0g)U3e+pSn6F!&4Pw zvf*6yWzr~n?XgugN&GSobu{lsfo-JS&+z%3;21@q)_)=i)H=zq7;pQt&^qo)iOXTV z;BL`bVU8s>y4^gtoQs}K+0}NPU!4FC9d72PN8FL4^CG{GS)ly4@L|voOrW^HDta+YRr|7PG^38#LaP`knitV{rx-7HOM`2 z6LiOawQ*2vyU8Wubv}ihp&Tr*adq*@tx-S25-?c;hDUT`y1SGlba7#-q6wsr26^D= zouX9vJ!Pbig&mknv>w+WxUrW#nbk3UIA6!SRF@v?=sbKo=i%Uuurmo_4cDz3r*!CV9N=%^dEPOMQSJ-sqmNQ4cTFVx;O)6yde%>ozF<+!K0fO%2h$; z?)0M{_kEQ8)lc*bT!FzX)@OM0*|TPkL02BGobXj-B{sa%-!^&|u-nol@A;kN6eCeP zU+nkD!gTKgqedP&OSVqSSB-MImHLOq+<05Qv?alADSoB(Cuu!O@`^H_KBx$6L|4X! zDhLKD5x809J-aqbq}J8HF=rb+v^|jHf$|3%x>C^DdY-GvL20Ik@`cOTtnE1GOHI5x;(Mtntxtu#MHr-m)UQELyg>va{;tb-C-S?~me@3(=IGEBDLZ&l)RH zD;G=|u$CH~q4jWQp=v1FR^3<{dR{x?#03`wP+atPfi(;r$TRZkdO&nzFJ%3-^u7J# zo)(d)Wv3D%w}Zo;F4=ce@y=J>H)q$sQE=$xT*~ecIrDMYvw~Tg{H}Fv;C&v#)BQ)O z00x!(!)JZq0tfTCG|I{dj80J!?-B*FxS}iZ&*tjDMmP$He!{RgdGAg-ny0cd1#9uq zL!jDeWi>nsce8S`KM^NJejHa~{ch!EJzr`yM);spa_np{ct2%?>F9zkaO7vS>hiCz zhGAF_CwFuF7g!1QVe=wpEIPc06q&rAH77;2_22dVw@Q_!ou>5|Cp+3FZI!5_zOKTr zaI&|3)(M+GPUx1}ZnJs<9M>{`x1`cZ?YS>Ts4tzKZU3% z0AG;WN`u)3NA9M0F3Ic>9kr|_z=^G%U#Lw~1_9hug?JTZdKU^~(M!aS!qlUg`bJNQ z??kAVhi2h}R}=GnN=^e1%>189Ou+@q-ou)++GysUMH$W*CWA+4RT?n%7BP`O5|iz= zaDCFOz~K3#)=ikL0j^y+U9aU}pbl=KdDz39VSE_f-X*bHUVrZYyMPli<9F2F$UdYw zI#W%ta_p=ENCe$Iq;C(t1YUp;g{Y7=Jd0sXHvqj03?yT>aN16dTATb`3D|O99=a?**?&q(syx`N(^ZN#0QOLCTYA@BT;h?0k+NPtQouxlpQ1^r=R9Lt7#)9?ARd?wnRF z_k#$A9){`@kd0j7DetuhP;1GA1P1?bAacN!7RDuxS0q#!iC7PuDdNMTb?27q10Zxl z5}MZyU!@d2Dee@l$5&?vsMszaxdlQJLO37H3^kT7LiY+`f>9lB)6sRMhCUEhla$@ z>?$`yqa7tm=T*>;Hf35e`YhIO_B}yH8?v1G9U9#7Wpy=9oJu?TBu5>43+LbuM4)V(nuJIYRtDJ3V{q~59_uoFNJzYK~8#< zccDRSkXv*&P(GSG4ch5-3CdPqAi~sE(PzgREWXkv8_2wv+tQov*c(V+!p#1&;HgZj zwQq$k(K}Or7uLWDd)zgu8Pke3Me$R6Mo0Z53w($Z3-lfxi(zhE0a*5bPbU0irz%Eg z0hEKS&GiwPf>RW`Mb=bOtB2bcKyf`IwMj=7uUs0`GTkLf}@`%ibdZ! z7yCARg3XoM&V!Y#^js+ioDatr)aOgy>&cpRQ{tBO6@SuQY#Jt7Ov$Il~f$o^r}U|d?JhAz$iEN zi_>-Pzy^vXt`g~9e01`#M%)zPRqJd%#k#TR9gXtQ$HA~1yxjlr4V46lrbAy_0JNP3 zSR>CleS;OD(On;YfoG}u(sbemzoToQ0ZtC#uzGWjo^#LfMXAV)SA)68-pLyB&JAuJ z#n1GKExqY;Jb4&G;myASE5LsF<0F#ce>>|_HdS6pSB4Ki_65&&hjgl#*17`)A2)vI zP+_A8_N<>ebuYcUb3jR1GhEI8{*iLw4)-l&cd!)FU}w+HP?=93F@v+T9tD^d68n4h zkmOgNJ3xZf-{3%e)kvBw|Cvw2G+4W3qt8nTE6Phn5{BoQEZ$Amf+$g=!urF$R0Fux zp>$sZfDm{xhwo)*Wx-3x5N1}UetwI!}zsFdryPEqPbaYGJ6w5qSO3 z$znO|)E=u$aoLOiUj!DwQjC(d_lOFE84AD`r-I#}_~`*M7|no~g9(e+GyuC*Q@H}G z#0(`Xc^I{WMTL>sL{uwm8HNTYd;*L;=*ks>3>^tadgw1LQy!VcWq7s6Fi65M2Cg#2 z&-RogKD)+k%@NUj4+aCzcyHc<$B_mZY0nkrJ4lTZKHPwaT!MAx^W7=7g`0$*oIHWw zi#UDoJG+2h6x^4#6En@jJ4McPrM@-@R1_Zq#rf}_T)GB*R&*V#ut&%e|{gORKfM< zJ40k#;@~)D%9eRVn83&QN?{gxsvr8=VGbghfOyt#_mkF-<9sX0-rh8Fc~xBr$Qrqp zzN@4%TU7Y(HDg#D1g_aX5W|)s5&mEl@V z`P0_pDMSbB#`^u#++?SNH;%(}WI5-mu2}f?@UuCrcV=wvr9M5yOGt0QY;}lU(NCae z$?A@<{tl6_Z~7}IWYv+eUh=YLxU+myZE&aT_?>^ZKAHo8cA=EL*eKP9D)|kKj%N#O z=?5OJ%?Gv5@d zQ#>6D6^FIM*D{x?hLcKSd8B(8UnvpPFM$#dgLniJ+YC?i#@4afQ2gpeTp4tf*G z5=HqAg}&XxPGAa_syvLQ{U9M$e-Aik0GZ-^*`jpizM>;eK^6q6dNAm3rrG1)g&XAPgos^Z~P8#t=a zO1FdZhLS3t(@{6Y@KfD4f|xg{f{Ti@hsD`_2Gh0_y@R|m!eup|%`FnaH!vd{c|52I zXxb_;8`>B2_hSt*nrXR(=l+%cZ>tR`Y+eSCg)1>ReG8QWIEX5gquQA2&N~W%TXqgLGA_+iJ&imUX(&UG&cQo%(gGnmMJjf8QtSUknQOyR6^cq~vI$C-*i5pC}r zIHMi}$+R6)dUK01zxm1tDMifzr?$RU(~$Y4GaUK1X=1(e3?G-Lqtq{*?}>4uqOk&jScmw{wN z%i%jHvV|a5`?CH)I9gDy%RKepJ7u>ZFNJIe{Ku3RtF+_{Embj-kt|{H$$cs{1)PWq zcCAPqi!K1?hb#3d6&Kxb-(H(~Aj z=)U?^h-JFEGZ5$7<*!k`$x5P8o^;lGO$mRFnNIAEW?OI38D-qzmSG&0&rcW8R%p#P zx`Xc(xOsw6+pMH}Gr*=Q6os2V)Wx}4O6OmG{;YK?mVJgJjNo%q?2q9#_b$rgAXS9W zQ7=c}sr*Hw!7M1Z^#adc(>Ub>!dn33+Dvo3sBABoPVLHH9Po~BU~blvvgT*Y0R<4` zyJkVT@A;QRiKY0o|J@nIPTbTb@e`Ir#slRZnGMmWd`Tbhx02zu+cu#TFUoHii1M$* zHpwzIa@aURPGRNP{&gHU_xXahj=l)iAZ#t6@?`bFF4`BM>7UW^C|*G_ctS_*l7`>n zpB^o!WXeEvnYvjHxBH$yeH9AKL_)ode0vbnP*~V!S%eZMj$c+&PJs|rL^ffZ1ZO7> z|9?goo@8c7=vK;`%<61-Lj}BH*%w8hJ3g0t^YM7H^NwU7lb}YJkOKQTx{VL9bnD>N z(XkH9p5>)0_Yg~PD-kxEtSi00eGru-;o zLJ14y0(#hZW$F`b;3*1C=UStl{#nt#)!-Ffwm5`0_& zD36fV4cD6^)t0{drm}AXz~aV7uTJ?{#jLDOgFs9mg56xRtqO9w=siK^YxWrnYb&xr zi3jNhe51z}p~9wpe|cOhv-%r&!^~gb@ju=Xr~*fwBqLK`aDUs4SBg&#LzFUKSfIJS z9$Qro36vHx(l;_n7*0uqn)hqn&&*eUR z`#jTuKqOu#U~!dy^}X2e@#Qrw8JSzi;_6$*79L$#p~l*I=BD``sHF+LJV3-3fEH2w zM8Q;-8ZCm7Gg*b+C#aqF*04+yeD>$hQ5vj+;G?#EOiuuXD)8xs6uxUQ^fr40MV{vI zmZ*Y?A!-)0 zJ<1K`nM>9R3pXvm2SLLi2X20Q&ora~CXwYhlC~k^!J1}EZ+MBg7n*r4nw|W*H>Iwg z5v9ZS_1VFi5p@Y!`Zv<@A#g-ZU6@==Ro8ihJIRZueq4EJmre=jF)^_CqPLLUldsn{ z{km5fi?vdL7zBmJWwqL{Op(VK<2jP{Z&-g8TyiEpB2i`6agbzK=dH zO2Lg~o#Rv69{#YT3t%$GZOUti?Er%nco8R1R3II)nN1C*CYNl9+Dzh42T+7+5LV=N z70ySwxR^rp8o3G6N!}U`(Dff$n@zO38xKeGG00VlnytG8bZy0!O&j5$`2n!6FD1I@^-e$fk0JeQY1unX{d4Z` zQs!tmpSA}!fVa6bWTZQ3A6nKiO_xitXpF=R-YpD4j{%Vgb+1QZa`R$9+yKL)43^Bc z%Q6wts`oW$5!5NPZG#c zItJD!j(j}A9TRWjK#syht3>@1n5dhzRTWS)1Hev<{m_fsPeuBQ%unx{3IU8s?0(8; zksiWfwD0wd06rn|0sm11<(bakEV_po&cI2d3y7}Ppp2{Os@CBaQ6~mW;wr%4DyVI- z?Xq}0Km~;Jx;9ZfnKT9mbDI+ z$ItG?CZ^GS9p7`+Y?PjS4-L8*#%Chz(_*hUR4qNR+Q9cB1y=x!nz11iwUu=+DPD{zE z2P$luxZ~pdup|g0Z=hF`N*2Lap>m4#GfGpNe4V|xwz_9>HxPz^!ns<{7hxvAD)<^EEplKa*~&5o1T7ItTrztCpPdC4ri{D3 zb4>4+a-F;eb!j#vJWNojOUIsYKDthNaZ1_9g(w*nC!7 z&5yf}l)Tk$IxmR2I6(!*;+i>|^cJj%d#)cqcH+v3A3MYO%HzO&UO<+-roMjzeR($C zmOgqIDF6zKt>EF^nz&QS*kxBPOjPKefz<7~G6I2m_p4{K$G-vd!vfJp4xfu;-%;@M zh&q{?K29a~V`TPnB8^ac<{qfNyVSOU<(&wIJ7PwldY+cH`h4^8qiVP_$_>|WmsFoSzh$w(`P4!DG+48gC zw;W}uG}dhZSG%bXBJ`9k*;j#f*>rJi_VVY(=><$K96ksPHxXI*d=~LKQQVYjMcgpl zDU^08Qn{$yaF4^Ekki^vgQ@K*iRmTVE~kE(%i;6axt7NCKh6aCWgUp+FQQmoUPFyB z2V4f9%T|)QuOfFlW4_e(aG4QT2ltgzlMva>gsIH|462&@+$iE>NmFX%?<(nbEN?K+ z%{P4z8A^%37@3XM!2K~D_1OaaSr=F}+wwOhf(k0+Kwf|SDAgF{{lTBKj1~~6j}IyC zF*XS1?oWQe7a%$Y92qmvhJVDS-cyw(RLQWFJh|5OWiE*?!1aF}PgbKm=0*m`{Tfk- z)XkR=t^yapyR#feGMI$q6B1KLP1r3~ap^Joj{x5SAVm^;^k@gf%E}yls5tTEX&G5e zLU8hggxk*Q@t)fTw=#bjU4WSP_%jzuUx{H|yqvi1orK42A0`Hk44VPOy;PAohOx%u z&A9|c7*S|PPUSOKz+b;AJLD@Zla%oZ5eJ1ucDcyb#y_s#iMd@~9RGus`GcI2}=H z;Dsy8xMo3mWdE+b$rH!i#!Z&gnyPp>&X=rEl3kd@?LltL9p+)6y2wpRH_Fbd>e?D1E z4}7>Wc*uj~58~v#Jk%0EA^YPSQ|otNVBa-qg(}C}ZtpSkLisKLh0oU)LGd!HD@Z%e z;#8GMmzV{cHxuaCcwWsqf@FLj^+|l}2BBd$#FZj=ZV6fx9q#{tzROA$e0Bu zbI#jTtLW64zekW~riHOnt8@}(8iyH+PI*7vv)$CDog#uVxcq(78Tky zfuP#-)P9RyufDG>k^!}d@o1$ttjLAZH@5e1X&JqqJ~EhPnl+$id`F>%3NG`ZozGb$S;RPd z>>ZZ5)_~?$KN_{6C`qmf9IBY2H+L~u-HYrHdW}$KY`sD)D z9;MAvNwphbD}PTxV*MUNw#vn0SM&#dTQM8im(-;y}@oZbtj7l zG^F9hcTM0Y#)@ZUe|{(EJUmJP^YTT zbxf~$w%GWi5oIv|Rw@Rj$Fvwll0a}VeS3M|EGL4biu(Rn@%`gd?IQTh2;;Q>Y{DW$ zVlT~re=3=&>u%_V5MERbPQCpG&+UIt zF0s|2zwU#enf*P44hsvQd|NKOvP>ED@lpwA!B4I2$7?qj;Lyu(3ZslX#wa-mj{{8Ww~n z2lJbi;Q;^F9-7e}M9Qm>dZU1CN6!!gRWQIbQA-@JN~oLn(9PkiAu}QBEI_M?#7B1R z%twXKMZFMOHj-Zla6G0utH7a@Jw2m7t1C+9@UbOc_-NK($1t=W;O?sKbG!!!ns9*r{s2J2fAL9V9AZ zIQc32*CKusvu4{*?f4oDvwRZsX;EqD^WWqx-MW(neC12mf+LG9dQ86_6234~-9Vnz;^X)ZAkpZ*G|u!Zoxs38Z01cgF;@>!PFRDmDMxgQ0q-)pe=IWUvm0!)6x8O*8s!~bPg0cHGEB{PX};6-E>wo^2p2bx6Xbj+ zD)1gbqHTDk+LkA&0o|*AA&2hcuKn|xU&9Lvq_vPCcikDUhASk8sB7^gD|EhEc^%cJ zuf13lMW1cpw?toZYSYGmakW_{UxBve0xPvxVzcai#ZKHU8` z`Lu+0dL^o&x164o_*-U5iuQKK8lze{2(ulNrj}^W>U&r1T=K*JQ(ru6#yIT6vAm0R z$G7nfG%zL$M@h2W;<&8nU4b-VYd}E5yTu~ve4cV$-by=UcJ1m~yv!Je^}WZpWykUx z?|i7pfT`vuuwn(ygM0OPVFY8_tp=669K3E&BUVc}3>A z3^OKq8*2mNw@IL2EH&NMNS^@PrO zfeuL9YlfP8OSX*fv6?UD7}4y~b+MmZsj_G7x;7V~o`nZqq1*qFi{#C=@cA+Kvw)^= z6$PH_z_$iH>R2qgpBhlX^4bS6rC_PFd_G_A+V=j6eQ$9UkJQ)T=J5>-G!_H6y)=z} zYTlJNuzsvr0nWL=V|ErVM^di|7DvrL>~Z!VA3~VgzIxnv#qa;Mb)`{FWLdZngoqN& zQ4rY|6`@h$5tLywjX>)Wg>hOLoraLpC@8Wi8YT%O>jqO2sVUG1r@>;k`O}XK4#|6RM+pEldAXLz4yD{{k|&Z=>gg)#H8pK=h648wkznQ zdQtJKLq6|ow(5&@UoB=o^Q;jS~}M{s1^JkV2*+Mh4Q* z*^(_1JJGz20_h4@Pp)B?<&N-k<@*Ll6N+u`J2NFpuQ<&}h+hO<9ipvqBwj)WDZ}{U zM50|6iKII17NPLkp44 zB7*F#XUie4bsuf_Ay9wzRxZ=6ekpO?Ztv|g>UotcPH?jAb{YJmWQhyvrW5IxK&$ts zX%Dj>5sW~C5abs_A}xt!*+#j?!j(_4xfdi^F1r@4BbVgNSl89dIUz1Zaq~5j1^qtN zi}7)sy8}uvB%VWvlbrdl9Zt7&CK7?_)e0FL>EtAm!d#0@S`KC#f$>Afd9V!BgrE4? zwW5SBn`dO^dZi0&{E5;+(HM;dFQiGCOCQ?%jYm!?K`&2j*K#r2^X$RcsjzkH3l&fI z6JCF0tG?8Qzen#&KfQRd%-rU? ziwZLkQ1w1G*FAVfs#V4Vs48>RDfXUgrJG11S-d{EzpC}I`BaYNlheIrTh8+j?<^|T zEN?Oj4qBT>Ty?*}mx_bFWU#hi5vd4?qMi!rt*Y9>d;^DhFVX?`CK~MhjS)W?V4Wok zf5lbqr7)PMCq5_Ug-=iJ`A3myK~F<$6^aA>)oMWJUN4uFt2&J`!;q3ay6Tq{X7Zt* zz00NGO`3cigiFf)Hz6taBTHT&@}3=!jWlZXbBD^g6uIMqPWF23f&(6-v|F=*9;!uE z_cl_ES7BrtiOsj9cd_{a)(tnEsfL8-;GPUjQ1@x0sZ<5=C9vRc9$=YMKvxUpou>mx zp5G@L;IL+BJ>-V&%cLQb(nyj3IFqdg;brdx2Ha&kcxtu%TSnM9>(`eCdnPu}9zzH9 zt=0&Dv6t^1sLh;ccjNhNbQ)`~LNHG-w5I7$Gae=(XQd5sM+J8`{z-Z8r1jh3rB}ey zpp;E(IhkPEM9Ts5*Nxo#3Bv7a?h(w|dmMQQWSo@qo?7K%V7Pi05rRX?@?ECNQ@E$V zK*J61_nH>}2alw8iUzQx7G#)pybR=vp@jZ;J(X`q&52;JfGI;7jfPQ)0?h_i4-ckGr-%~h-jZb>o?KR zKpsI|1cjg#j}KneZzuSusNsPMbHts?R^gUp8pan`i6Q_#aB}Ml!f~;Ej$4VhETI2J zyoV98XG6BSaJxfHzbU!kio zwxRW7)KZLp0Y$3GR=xOUabO4YW;QatAmit0E2=4W2esCygM~T84m99G0%Wmk==_M> z+bm?ymu+T$#>Gz4GdM&|!ko}{LcZy5p49Z7Gqj5ocQmy|E-}=MAYi?*OUUHx?FU?r zXf=J|0EmUroTVncDUbQrsd1^^@xKYzjBxG{lDOyN=YLekKWI2#zU#Pk70{hZK=i z+q~jC{p;>u8`1y+dHi#HmAKE1IBKLd zK~4QhBOl%*`U&?Z<8|-|5q}4S(hACz%92LpW%F=a+nc)U;JVwJiB37NbDUVBN)Tr!qKRtJFe;;z1Oo3w-MxO6ctKTZju>5Cn!z) z%#)d@_oR45JS14=(g7u`RKgme6pQZyS#tz}ek^ITN1Qlg8Ra?^vdw)`BTIo` zm(pDa1Gd|iWtz4WMVYOYZ^_m+s`IRSg*;?#8rPDrgqV!Pra-j-pLq)aP-9C4{K1!& zTDh;*QV$SRhaU(tHrDJcJgB|y>@`S&+^(-gE}31s2|ws?q;pZ!`D$1u&DofHPk^8P zr`v%*OQ({ arguments) { + /* + if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) { + setWindowTitle('App title'); + setWindowMinSize(const Size(700, 500)); + setWindowMaxSize(Size.infinite); + } + */ + + // What is this good for in native windows?? It seems that native splash image + // does not work on platform windows! + // + // https://www.youtube.com/watch?v=dB0dOnc2k10 + // WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); + // FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); + // + + // Initialize the platform specific part here + AudYoFloPlatformSpecific platformSpec = allocatePlatformSpecific(); + Map configArgs = {}; + + // The core pack allows to configure the ffi backend. It pre-loads dlls and the actual host + configArgs['corePack'] = ayfcore; + configArgs['host'] = ayfhost; + configArgs['cmdArgs'] = arguments; + platformSpec.configureSubSystem(configArgs); + + // *************************************************** + runApp(MultiProvider( + providers: [ + ChangeNotifierProvider( + create: (_) => AudYoFloPeriodicNotifier()), + ChangeNotifierProvider( + create: (_) => AudYoFloBackendCacheSpecific()), + ChangeNotifierProvider( + create: (_) => AudYoFloUiModelSpecificWithWidget()), + ChangeNotifierProvider( + create: (_) => AudYoFloDebugModel()), + /*ChangeNotifierProvider( + create: (_) => FernliveCacheModel()),*/ + ], + + // Here, we enter the main widget. We pass the backend adapter (here: ffi bridge) + // and the core pack which holds the Ayf components precompiled plugin + child: AudYoFloMainWidget( + platformSpecific: platformSpec, + ))); +} diff --git a/flutter/ayf_pack/example/lib/models/ayf_be_specific.dart b/flutter/ayf_pack/example/lib/models/ayf_be_specific.dart new file mode 100644 index 00000000..8ce6c354 --- /dev/null +++ b/flutter/ayf_pack/example/lib/models/ayf_be_specific.dart @@ -0,0 +1,5 @@ +import 'package:ayf_pack/ayf_pack.dart'; + +class AudYoFloBackendCacheSpecific extends AudYoFloBackendCache { + AudYoFloBackendCacheSpecific() : super() {} +} diff --git a/flutter/ayf_pack/example/lib/models/ayf_ui_specific.dart b/flutter/ayf_pack/example/lib/models/ayf_ui_specific.dart new file mode 100644 index 00000000..932d1281 --- /dev/null +++ b/flutter/ayf_pack/example/lib/models/ayf_ui_specific.dart @@ -0,0 +1,190 @@ +import 'package:flutter/material.dart'; +import 'package:ayf_pack/ayf_pack.dart'; +import 'package:ayf_pack_native/ayf_pack_native.dart' + if (dart.library.html) 'package:ayf_pack/ayf_pack_web.dart'; + +import 'package:ayf_pack/main-widgets/ayf_audio_settings_widget.dart'; + +class AudYoFloUiModelSpecific extends AudYoFloUiModel + with AudYoFloUiElementsTabBar { + // ========================================================================== + // We define some rendering parameters for the audio io widget. + // We use package specific icons here. + // ========================================================================== + AudYoFloAudioDeviceRenderSection soundIo = AudYoFloAudioDeviceRenderSection( + imageNameSoundIo: 'packages/ayf_pack/images/ayf_icons/ayf-soundio.png', + imageNameSoundI: 'packages/ayf_pack/images/ayf_icons/ayf-soundi.png', + imageNameSoundO: 'packages/ayf_pack/images/ayf_icons/ayf-soundo.png'); + + AudYoFloAudioDeviceFileInputSection fileinput = + AudYoFloAudioDeviceFileInputSection(sizeIcon: 55); + + AudYoFloHeadtrackerDeviceRenderSection headtracker = + AudYoFloHeadtrackerDeviceRenderSection( + imageNameHeadtracker: + 'packages/ayf_pack/images/ayf_icons/ayf-headtracker.png'); + + // Add a local property of type "string" + AudYoFloPropertyStringLocal propTestme = AudYoFloPropertyStringLocal( + 'Hallo Test', "/settings/testme", "Description"); + + AudYoFloUiModelSpecific() { + textApp = 'ayfPackExample'; + imageNameApp = 'images/ayfbinrender_icons/ayfbinrender-logo.png'; + + // Define some specific configuration entities + widgetSpecificConfigurations['soundIo'] = soundIo; + widgetSpecificConfigurations['headtracker'] = headtracker; + widgetSpecificConfigurations['fileinput'] = fileinput; + + showAudioDevTech = false; + // initRoute = "/connect"; + + // ===================================================================== + // Define the settings dialog entries + // ===================================================================== + } + + @override + void configureSystemParameters(AudYoFloCompileFlags flags) { + super.configureSystemParameters(flags); + AudYoFloSettingsConfigurationSection oneSection = + AudYoFloSettingsConfigurationSection(); + + oneSection = AudYoFloSettingsConfigurationSection(); + oneSection.nameEntry = 'Application'; + + AudYoFloOneComponentSettingsBackend addThisBeHost = + AudYoFloOneComponentSettingsBackend( + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_HOST, + slotid: 0, + slotsubid: 0), + 'Host Configuration', + [ + '/jvxLibHost/autostart' + + ///ext_interfaces/application_property_autostart' + ], + propTags: [ + 'Activate Autostart' + ], + propTooltips: [ + 'Activate Autostart feature to take effect when starting app the next time. When activated, audio processing will start immediately.' + ]); + + oneSection.addEntry(addThisBeHost); + + AudYoFloOneComponentSettingsBackend addThisBeAsio = + AudYoFloOneComponentSettingsBackend( + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_DEVICE, + slotid: 0, + slotsubid: 0), + 'ASIO Audio Device', + [ + '/JVX_GENW/system/sel_framesize', + '/JVX_GENW/system/sel_rate', + '/system/rate', + '/system/sel_framesize' + ], + propTags: [ + 'Select ASIO Buffersize', + 'Select ASIO Samplerate', + 'Samplerate', + 'Buffersize' + ], + propTooltips: [ + 'Tooltip: Select ASIO Buffersize', + 'Tooltip: Select ASIO Samplerate', + 'Tooltip: Samplerate', + 'Tooltip: Buffersize' + ]); + + oneSection.addEntry(addThisBeAsio); + + settingsConfigure.allSections[oneSection.nameEntry] = oneSection; + + // Here we may add an additional combobox in the component settings + // showAudioDevTech = true; + + // Create another section + // This is here only for demonstration and is not active at the moment! + if (false) { + oneSection = AudYoFloSettingsConfigurationSection(); + oneSection.nameEntry = 'Sip'; + + AudYoFloOneComponentSettingsBackend addThisBeSip = + AudYoFloOneComponentSettingsBackend( + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_DEVICE, + slotid: 3, + slotsubid: 0), + 'Sip Device', + [ + '/xyz', + '/xyz', + '/xyz' + ], + propTags: [ + '/xyz', + '/xyz', + '/xyz' + ], + propTooltips: [ + 'Tooltip: /xyz', + 'Tooltip: /xyz', + 'Tooltip: /xyz' + ]); + oneSection.addEntry(addThisBeSip); + + settingsConfigure.allSections[oneSection.nameEntry] = oneSection; + } + } + + @override + int reportSetProperty(AudYoFloPropertyContentLocal prop) { + bool notifyThis = false; + if (prop.descriptor == "/settings/testme") { + return jvxErrorType.JVX_NO_ERROR; + } + + if (notifyThis) { + notifyListeners(); + } + + return super.reportSetProperty(prop); + } + + @override + void filterListProperties( + String token, AudYoFloMultiComponentsPropertyOnChangeEntry entries) { + if (entries.settings.cpId == + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_DEVICE, + slotid: 0, + slotsubid: 0)) { + if (entries is AudYoFloMultiComponentsPropertyOnChangeEntryBackend) { + AudYoFloMultiComponentsPropertyOnChangeEntryBackend entriesBe = entries; + List? lst = entriesBe.bprops; + if (lst != null) { + if (token == 'Settings') { + lst = filterListProperties_settings(lst); + } + entriesBe.bprops = lst; + } + } + // var modLst = entries.; + } + super.filterListProperties('Settings', entries); + } + + List filterListProperties_settings( + List lst) { + lst = removeIfPresent( + lst, '/JVX_GENW/system/sel_framesize', ['/system/framesize']); + lst = removeIfPresent(lst, '/JVX_GENW/system/sel_rate', ['/system/rate']); + + return lst; + } +} diff --git a/flutter/ayf_pack/example/lib/models/ayf_ui_specific_widgets.dart b/flutter/ayf_pack/example/lib/models/ayf_ui_specific_widgets.dart new file mode 100644 index 00000000..e45e45d8 --- /dev/null +++ b/flutter/ayf_pack/example/lib/models/ayf_ui_specific_widgets.dart @@ -0,0 +1,274 @@ +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; +import 'package:ayf_pack/ayf_pack.dart'; +import 'ayf_ui_specific.dart'; + +import 'package:ayf_pack/main-widgets/ayf_audio_settings_widget.dart'; + +import 'package:flutter/foundation.dart' show kIsWeb; + +// ============================================================================ +// Callback to allocate an audio device sliver in the list of active/inactive +// devices lists +Widget? allocateAudioDeviceSliver(AudYoFloOneSelectionOptionWithId option, + bool fromActiveList, AudYoFloSingleSelectionComponent? reportW) => + AudYoFloOneDeviceSliver(option, fromActiveList, reportW); + +// ============================================================================ +// Callback to allocate a headtracker device sliver in the list of active/ +// inactibe devices list +Widget? allocateHeadtrackerDeviceSliver(AudYoFloOneSelectionOptionWithId option, + bool fromActiveList, AudYoFloSingleSelectionComponent? reportW) => + AudYoFloOneHeadtrackerDeviceSliver(option, fromActiveList, reportW); + +BottomAppBar? allocatorBottomAppBar() { + if (kIsWeb) { + return AudYoFloPlayConnectBottomAppBar(); + } else { + return AudYoFloBottomAppBar(); + } +} + +class AudYoFloPlayConnectBottomAppBar extends BottomAppBar { + AudYoFloPlayConnectBottomAppBar() + : super( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [AudYoFloPlaypannel(), AudYoFloPlaypannel()], + )); +} + +// ============================================================================ +// ============================================================================ +// This class extends the common UiModel part and adds application specific +// configuration entries. +// ============================================================================ +class AudYoFloUiModelSpecificWithWidget extends AudYoFloUiModelSpecific { + // ========================================================================== + // Define the core widget composition here: The function configures all application + // specific widgets here. Then, if all specific widgets were added, the base class + // call may be used to attach the application unspecific widgets - which are typically + // only the debug output and the component property viewer widget. + // ========================================================================== + + AudYoFloUiModelSpecificWithWidget() : super() { + allocateBottomAppBar = allocatorBottomAppBar; + } + + @override + void createTabsAndViews(Map entriesCfgWidgets) { + // ====================================================================== + // Add the primary audio device widget + // ====================================================================== + String imageNameAudioIoPri = + 'packages/ayf_pack/images/ayf_icons/ayf-audio-settings.png'; + String textAudioIoPri = 'ASIO I/O'; + JvxComponentIdentification cpTpPriTech = widgetConfigCpId( + entriesCfgWidgets, + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_TECHNOLOGY, + slotid: 0, + slotsubid: 0), + 'priaudiot'); + + JvxComponentIdentification cpTpPriDev = widgetConfigCpId( + entriesCfgWidgets, + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_DEVICE, + slotid: cpTpPriTech.slotid, + slotsubid: 0), + 'priaudiod'); + + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Tooltip( + message: '${cpTpPriTech.txt}--${cpTpPriDev.txt}', + child: Column(children: [ + Image.asset(imageNameAudioIoPri, + filterQuality: FilterQuality.medium, + //'images/fernlive_icons/fernlive-mixer.png', + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(textAudioIoPri) + ])))); + theAllocatedTabViews.add(AudYoFloAudioSettingsWidget( + cpTpPriTech, + 'Audio Technologies', + cpTpPriDev, + 'Audio Devices', + 'soundIo', + (JvxComponentIdentification selCpId) => + AudYoFloAudioDeviceInputOutputChannelWidget(selCpId), + allocateAudioDeviceSliver)); + + // ====================================================================== + // Add the secondary audio device widget + // ====================================================================== + String imageNameAudioIoSec = + 'packages/ayf_pack/images/ayf_icons/ayf-audio-settings.png'; + String textAudioIoSec = 'WASAPI I/O'; + JvxComponentIdentification cpTpSecTech = widgetConfigCpId( + entriesCfgWidgets, + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_TECHNOLOGY, + slotid: 3, + slotsubid: 0), + 'secaudiot'); + + JvxComponentIdentification cpTpSecDev = widgetConfigCpId( + entriesCfgWidgets, + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_DEVICE, + slotid: cpTpSecTech.slotid, + slotsubid: 0), + 'secaudiod'); + + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Tooltip( + message: '${cpTpSecTech.txt}--${cpTpSecDev.txt}', + child: Column(children: [ + Image.asset(imageNameAudioIoSec, + filterQuality: FilterQuality.medium, + //'images/fernlive_icons/fernlive-mixer.png', + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(textAudioIoSec) + ])))); + theAllocatedTabViews.add(AudYoFloAudioSettingsWidget( + cpTpSecTech, + 'Audio Technologies', + cpTpSecDev, + 'Audio Devices', + 'soundIo', + (JvxComponentIdentification selCpId) => + AudYoFloAudioDeviceInputOutputChannelWidget(selCpId), + allocateAudioDeviceSliver)); + + // ====================================================================== + // Add the headtracker device widget [RS-232] + // ====================================================================== + String imageNameHeadTracker = + 'packages/ayf_pack/images/ayf_icons/ayf-headtracker-settings.png'; + String textAudioIoHt = 'H-Tracker Rs232'; + JvxComponentIdentification cpTpHtTech = widgetConfigCpId( + entriesCfgWidgets, + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_CUSTOM_TECHNOLOGY, + slotid: 0, + slotsubid: 0), + 'headtrackrs232t'); + JvxComponentIdentification cpTpHtDev = widgetConfigCpId( + entriesCfgWidgets, + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_CUSTOM_DEVICE, + slotid: cpTpHtTech.slotid, + slotsubid: 0), + 'headtrackrs232d'); + ; + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Tooltip( + message: '${cpTpHtTech.txt}--${cpTpHtTech.txt}', + child: Column(children: [ + Image.asset(imageNameHeadTracker, + filterQuality: FilterQuality.medium, + //'images/fernlive_icons/fernlive-mixer.png', + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(textAudioIoHt) + ])))); + theAllocatedTabViews.add(AudYoFloAudioSettingsWidget( + cpTpHtTech, + 'Head Tracker Technologies', + cpTpHtDev, + 'Rs232 Head Tracker Devices', + 'headtracker', + (JvxComponentIdentification selCpId) => null, + allocateHeadtrackerDeviceSliver)); + + // ====================================================================== + // Add the headtracker device widget [ART-UDP] + // ====================================================================== + textAudioIoHt = 'ART Tracker'; + cpTpHtTech = widgetConfigCpId( + entriesCfgWidgets, + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_CUSTOM_TECHNOLOGY, + slotid: 1, + slotsubid: 0), + 'headtrackartt'); + cpTpHtDev = widgetConfigCpId( + entriesCfgWidgets, + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_CUSTOM_DEVICE, + slotid: cpTpHtTech.slotid, + slotsubid: 0), + 'headtrackartd'); + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Tooltip( + message: '${cpTpHtTech.txt}--${cpTpHtTech.txt}', + child: Column(children: [ + Image.asset(imageNameHeadTracker, + filterQuality: FilterQuality.medium, + //'images/fernlive_icons/fernlive-mixer.png', + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(textAudioIoHt) + ])))); + theAllocatedTabViews.add(AudYoFloAudioSettingsWidget( + cpTpHtTech, + 'Head Tracker Technologies', + cpTpHtDev, + 'ART Head Tracker Devices', + 'headtracker', + (JvxComponentIdentification selCpId) => null, + allocateHeadtrackerDeviceSliver)); + + // ====================================================================== + // Add the file input widget + // ====================================================================== + String imageNameFileIO = + 'packages/ayf_pack/images/ayf_icons/ayf-file-input.png'; + String textFileIo = 'File Input'; + + JvxComponentIdentification cpTpFIoTech = widgetConfigCpId( + entriesCfgWidgets, + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_TECHNOLOGY, + slotid: 1, + slotsubid: 0), + 'fileiot'); + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Tooltip( + message: cpTpHtTech.txt, + child: Column(children: [ + Image.asset(imageNameFileIO, + filterQuality: FilterQuality.medium, + //'images/fernlive_icons/fernlive-mixer.png', + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(textFileIo) + ])))); + theAllocatedTabViews.add(AudYoFloFileInputControlWidget( + cpTpFIoTech, + "Drag Input File Here", + )); + + /*AudYoFloAudioSettingsWidget( + cpTpFIoTech, + 'File I/O Technologies', + cpTpFIoDev, + 'File I/O Devices', + 'headtracker', + (JvxComponentIdentification selCpId) => null, + allocateHeadtrackerDeviceSliver));*/ + + // Here, the base class function is called. This call allows the base class + // to attach the "standard widgets" debug and components. If you do not call these functions + // the debug widgets will not be viisible. + super.createTabsAndViews(entriesCfgWidgets); + } +} diff --git a/flutter/ayf_pack/example/pubspec.yaml b/flutter/ayf_pack/example/pubspec.yaml new file mode 100644 index 00000000..b711a68b --- /dev/null +++ b/flutter/ayf_pack/example/pubspec.yaml @@ -0,0 +1,112 @@ +name: ayf_pack_example +description: Demonstrates how to use the ayf_pack plugin. + +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +environment: + sdk: ">=2.18.0 <3.0.0" + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + ayf_pack: + # When depending on this package from a real application you should use: + # ayf_pack: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + + ayf_pack_native: + path: '../../ayf_pack_native' + + ayfcorepack: + path: '../../../sources/sub-projects/ayfstarter/flutter/starter_core_pack' + + ayf_ffihost: + path: '../../ayf_ffihost' + + ayf_webhost: + path: '../../ayf_webhost' + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + + provider: ^6.0.3 + collection: ^1.16.0 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + + # It seems that this here does not work on platform windows + flutter_native_splash: ^2.2.18 + +# Run: +# flutter clean +# flutter pub get +# flutter pub run flutter_native_splash:create +flutter_native_splash: + color: "#ff0000" + image: images/ayf_splash/splash.png + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + assets: + - images/ayfbinrender_icons/ayfbinrender-logo.png + - .ayf_pack_example.env + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/flutter/ayf_pack/example/test/widget_test.dart b/flutter/ayf_pack/example/test/widget_test.dart new file mode 100644 index 00000000..c2269c17 --- /dev/null +++ b/flutter/ayf_pack/example/test/widget_test.dart @@ -0,0 +1,25 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('Verify Platform version', (WidgetTester tester) async { + // Build our app and trigger a frame. + // await tester.pumpWidget(const MyApp()); + + // Verify that platform version is retrieved. + expect( + find.byWidgetPredicate( + (Widget widget) => + widget is Text && widget.data!.startsWith('Running on:'), + ), + findsOneWidget, + ); + }); +} diff --git a/flutter/ayf_pack/example/web/favicon.png b/flutter/ayf_pack/example/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/flutter/ayf_pack/example/web/icons/Icon-192.png b/flutter/ayf_pack/example/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/flutter/ayf_pack/example/web/icons/Icon-512.png b/flutter/ayf_pack/example/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 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 literal 0 HcmV?d00001 diff --git a/flutter/ayf_pack/example/web/icons/Icon-maskable-192.png b/flutter/ayf_pack/example/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9b4d76e525556d5d89141648c724331630325d GIT binary patch literal 5594 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 X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! literal 0 HcmV?d00001 diff --git a/flutter/ayf_pack/example/web/icons/Icon-maskable-512.png b/flutter/ayf_pack/example/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..d69c56691fbdb0b7efa65097c7cc1edac12a6d3e GIT binary patch literal 20998 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 MPgg&ebxsLQ025IeI{*Lx literal 0 HcmV?d00001 diff --git a/flutter/ayf_pack/example/web/index.html b/flutter/ayf_pack/example/web/index.html new file mode 100644 index 00000000..ac6ef206 --- /dev/null +++ b/flutter/ayf_pack/example/web/index.html @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + ayf_pack_example + + + + + + + + + + diff --git a/flutter/ayf_pack/example/web/manifest.json b/flutter/ayf_pack/example/web/manifest.json new file mode 100644 index 00000000..79e9d89c --- /dev/null +++ b/flutter/ayf_pack/example/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "ayf_pack_example", + "short_name": "ayf_pack_example", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "Demonstrates how to use the ayf_pack plugin.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/flutter/ayf_pack/example/windows/.gitignore b/flutter/ayf_pack/example/windows/.gitignore new file mode 100644 index 00000000..d492d0d9 --- /dev/null +++ b/flutter/ayf_pack/example/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/flutter/ayf_pack/example/windows/CMakeLists.txt b/flutter/ayf_pack/example/windows/CMakeLists.txt new file mode 100644 index 00000000..187fd006 --- /dev/null +++ b/flutter/ayf_pack/example/windows/CMakeLists.txt @@ -0,0 +1,101 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(ayf_pack_example LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "ayf_pack_example") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Define build configuration option. +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() +# Define settings for the Profile build mode. +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. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +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() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +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/flutter/ayf_pack/example/windows/flutter/CMakeLists.txt b/flutter/ayf_pack/example/windows/flutter/CMakeLists.txt new file mode 100644 index 00000000..930d2071 --- /dev/null +++ b/flutter/ayf_pack/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,104 @@ +# This file controls Flutter-level build steps. It should not be edited. +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/flutter/ayf_pack/example/windows/runner/CMakeLists.txt b/flutter/ayf_pack/example/windows/runner/CMakeLists.txt new file mode 100644 index 00000000..b9e550fb --- /dev/null +++ b/flutter/ayf_pack/example/windows/runner/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +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 the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/flutter/ayf_pack/example/windows/runner/Runner.rc b/flutter/ayf_pack/example/windows/runner/Runner.rc new file mode 100644 index 00000000..55967073 --- /dev/null +++ b/flutter/ayf_pack/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", "ayf_pack_example" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "ayf_pack_example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2022 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "ayf_pack_example.exe" "\0" + VALUE "ProductName", "ayf_pack_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/flutter/ayf_pack/example/windows/runner/flutter_window.cpp b/flutter/ayf_pack/example/windows/runner/flutter_window.cpp new file mode 100644 index 00000000..b43b9095 --- /dev/null +++ b/flutter/ayf_pack/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/flutter/ayf_pack/example/windows/runner/flutter_window.h b/flutter/ayf_pack/example/windows/runner/flutter_window.h new file mode 100644 index 00000000..6da0652f --- /dev/null +++ b/flutter/ayf_pack/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/flutter/ayf_pack/example/windows/runner/main.cpp b/flutter/ayf_pack/example/windows/runner/main.cpp new file mode 100644 index 00000000..ea92aa2c --- /dev/null +++ b/flutter/ayf_pack/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"ayf_pack_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/flutter/ayf_pack/example/windows/runner/resource.h b/flutter/ayf_pack/example/windows/runner/resource.h new file mode 100644 index 00000000..66a65d1e --- /dev/null +++ b/flutter/ayf_pack/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/flutter/ayf_pack/example/windows/runner/resources/app_icon.ico b/flutter/ayf_pack/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/flutter/ayf_pack/example/windows/runner/runner.exe.manifest b/flutter/ayf_pack/example/windows/runner/runner.exe.manifest new file mode 100644 index 00000000..c977c4a4 --- /dev/null +++ b/flutter/ayf_pack/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/flutter/ayf_pack/example/windows/runner/utils.cpp b/flutter/ayf_pack/example/windows/runner/utils.cpp new file mode 100644 index 00000000..f5bf9fa0 --- /dev/null +++ b/flutter/ayf_pack/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); + std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return 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/flutter/ayf_pack/example/windows/runner/utils.h b/flutter/ayf_pack/example/windows/runner/utils.h new file mode 100644 index 00000000..3879d547 --- /dev/null +++ b/flutter/ayf_pack/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/flutter/ayf_pack/example/windows/runner/win32_window.cpp b/flutter/ayf_pack/example/windows/runner/win32_window.cpp new file mode 100644 index 00000000..c10f08dc --- /dev/null +++ b/flutter/ayf_pack/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/flutter/ayf_pack/example/windows/runner/win32_window.h b/flutter/ayf_pack/example/windows/runner/win32_window.h new file mode 100644 index 00000000..17ba4311 --- /dev/null +++ b/flutter/ayf_pack/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/flutter/ayf_pack/images/ayf-icons.vsdx b/flutter/ayf_pack/images/ayf-icons.vsdx new file mode 100644 index 0000000000000000000000000000000000000000..8e7bbf12fb665fc652ed80bd6daf1828ea168f6f GIT binary patch literal 100408 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 F{|6#VtH1yN literal 0 HcmV?d00001 diff --git a/flutter/ayf_pack/images/ayf_icons/ayf-audio-settings.png b/flutter/ayf_pack/images/ayf_icons/ayf-audio-settings.png new file mode 100644 index 0000000000000000000000000000000000000000..4acc16fd3f8f59e39fe8f0e26560c749dccac0b4 GIT binary patch literal 1146 zcmV-=1cm#FP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1P@6>K~!i%?V2r2 zG%*l{`R5=&2na4D5DW=H5rQQFLy$-$5{bKWNF+EC41tg!5eOs_i3Ep4B9Z0ZCf!VD zr@c-$eBz_xxnyUbZ9BW&w!3=+#+W~Y7|h}0}1Z5;dG z-rfw{%gaj}J3c=4+=GJy8$UZcGq10&y`ts%au9>5pwVd9#>Pgkq=8>yWo5;lke8R2 zUX^k?QW1mw`275|S5Io(Z-Oh|Bxx}iqucG8R;yKO$iD<#{p;(i*BrTxA;h2r3g6$~ zuT|`5x7+q=KRi5`$Hzyry}d1ZF_xB=%+1Y>d47I2CnqOnZEa2T0J=Bsf8Nd*VvvJ- zGgL8702-%p(MTjCIkU5~=IQCFDo^9~Lkv>Tcw9AJ5e6liq}Fvlx+M2;oA4wwZU`~x z`2PMDM_P=IOu9=z*UpFsmA4qL5FlEF+Xw-Xj}=5vS>6t9`~g*5NTYB7z9`V z(82&>+yfdn29n@c4JVO%K;t^ZAkGlK_d?xKYXMv--aRmPat~-+4OpS^azi7o#;pY~+R+1JXG-I0;XMWm7VbDw z-CBT;sz``C4vnh?^C~SHqc3+HsjjzydyRLA&~UDCbK�v@mg`xZVZ?dH|j>=zfaE z%>|E-v<^Jua>tS4dK>&6L;-X^MdRkePiXAEO>w;qoDF(FYz3robHNgj)`15J?l@9h zZ-b9&v1vr(=7KSR))5=+Q(RBxk7id_SG`^S0YKw60jr_m;o+)|si~>pf*l?(V7sG;S{M@9$=8Y)q^JjE;_0>u-%~ z3+DodKE?2ToF-2^4!nmN92^wOcOXfvYYXQ)DJbaT;=;;WHy6yAu>^*OhN^o}1C(SlD9I8^vVsRnvLQ;c0ZOtNlw=7dS-}G(*${s!*@=mXpg@`hjI=7OGrD@VIyh<>iOVFM*u8I7AuNj5-9mf&sJz+XxhZ_x31Blh9fp>cD; z-+xdCzAfR;B78rkpX+S^eEGse?LHaA(6~)N9jX5+kGZwL|BD&;0gF2mw*PbPhX4Qo M07*qoM6N<$f?Jvm-2eap literal 0 HcmV?d00001 diff --git a/flutter/ayf_pack/images/ayf_icons/ayf-audio.png b/flutter/ayf_pack/images/ayf_icons/ayf-audio.png new file mode 100644 index 0000000000000000000000000000000000000000..6acfe65b45bc6eca56fecb6955590c7a8b9cdc13 GIT binary patch literal 1964 zcmeAS@N?(olHy`uVBq!ia0y~yV6tLhU{vQ|1Bxto@lpavF%}28J29*~C-V}>VM%xN zb!1@J*w6hZkrl}2EbxddW?+bM17SvyDb7Gic1KSa$B>G+w>J#e&59Ig34DJ?`$74O z3)~f-q&OnmnDtv4b6z~_osj00=A~}o*~zfz-;Vfo*R>fOmYV+g@$vDmudl-y8tT8i zy}do!%BJkd?#Si~j0|y#B@7KwPV5XTRB6U2l{x;k#%_P>>8C}fXB=O)Ga77hq1D`Z z@9GbwulLRtuwpo%-NMHZ;QfMuVe24byte3j{j;Wyd;008(+vB4i?_2aWM`+%wNF{o zx?K6XpJ(oT`}(;3_5b-8{_p%fKeMW)rsmG2hZ~jeot1hQ|0i-Q==RzyAOL literal 0 HcmV?d00001 diff --git a/flutter/ayf_pack/images/ayf_icons/ayf-brender.png b/flutter/ayf_pack/images/ayf_icons/ayf-brender.png new file mode 100644 index 0000000000000000000000000000000000000000..52f00441ad4a12d38bcd1fa16aea59f6d4ec12dc GIT binary patch literal 20508 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 literal 0 HcmV?d00001 diff --git a/flutter/ayf_pack/images/ayf_icons/ayf-comp.png b/flutter/ayf_pack/images/ayf_icons/ayf-comp.png new file mode 100644 index 0000000000000000000000000000000000000000..d76ba31869b43b83aca63a0fb069c3a8f311b899 GIT binary patch literal 345 zcmeAS@N?(olHy`uVBq!ia0vp^`arD6!2~3a`KNXRDaPU;cPEB*=VV?2IV|apzK#qG z8~eHcB(eheoCO|{#S9F5he4R}c>anMpx`@C7srqY_qWpxg_;!vTy-Vf({+NXdU(Pg zFvvA7exfl=M%jJCmMu>>rlg$ByV5z~21D8*?Q+4mgM9Z7$vu{vQ7H3pinE#5%2liM z6dV6;z1E)dYhV7XWgMJu?0ql4ztM2yt>^X9)W}Qai9hn3xff5!{bP}n@WS%>b@43* zTzt|yww1;P+w0{?uL!c&OW&G#ds^1Zh};zursOzCTwXdfE0yj@HO%j>MjWo4>nks+=t= pfAiS+oXvmo+aDi38@urvqn}dQLx%SnhCrV)c)I$ztaD0e0sw#@h{pf` literal 0 HcmV?d00001 diff --git a/flutter/ayf_pack/images/ayf_icons/ayf-dbg.png b/flutter/ayf_pack/images/ayf_icons/ayf-dbg.png new file mode 100644 index 0000000000000000000000000000000000000000..5a719fbb1d6b1e8bd5b000ba583b1b8d924d2979 GIT binary patch literal 1133 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 Sguovj5Xx@rUD-RG&;KWkzv3|f literal 0 HcmV?d00001 diff --git a/flutter/ayf_pack/images/ayf_icons/ayf-hdrive.png b/flutter/ayf_pack/images/ayf_icons/ayf-hdrive.png new file mode 100644 index 0000000000000000000000000000000000000000..abf8ee6062cf51ca332a61c9fcf6a9d50802cd78 GIT binary patch literal 4613 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 S^}xSw5T88(yE(fEfBR2aUQJ>E literal 0 HcmV?d00001 diff --git a/flutter/ayf_pack/images/ayf_icons/ayf-headtracker-settings.png b/flutter/ayf_pack/images/ayf_icons/ayf-headtracker-settings.png new file mode 100644 index 0000000000000000000000000000000000000000..fa836fb6f36ed28db4bfb38e0bbebd3c36eaf28b GIT binary patch literal 24271 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 \ No newline at end of file diff --git a/flutter/ayf_pack/images/ayf_icons/ayf-logo.svg.orig b/flutter/ayf_pack/images/ayf_icons/ayf-logo.svg.orig new file mode 100644 index 00000000..eef0b395 --- /dev/null +++ b/flutter/ayf_pack/images/ayf_icons/ayf-logo.svg.orig @@ -0,0 +1,102 @@ + + + + + + + + + + Zeichenblatt-1 + + + + Logo-Ferncast-2018-Horizontal + Created with Sketch. + + Logo + + Fill-1 + + + + Fill-3 + + + + Fill-5 + + + + Fill-7 + + + + Fill-9 + + + + Fill-12 + + + + Fill-14 + + + + Fill-16 + + + + Fill-18 + + + + + + diff --git a/flutter/ayf_pack/images/ayf_icons/ayf-micsrc.png b/flutter/ayf_pack/images/ayf_icons/ayf-micsrc.png new file mode 100644 index 0000000000000000000000000000000000000000..d15b87b4830a509aba336e4e1a2d12156a7a2618 GIT binary patch literal 1309 zcmeAS@N?(olHy`uVBq!ia0vp^g+QFo!2~2j4(!MQQjEnx?oJHr&dIz4a#+$GeH|GX zHuiJ>Nn{1`ISV`@iy0XB4ude`@%$AjKsB2@T^vIy;@-~DE%{_1(xyJqL!}4=ge*H< zlnN(L_DFg97&!6nYk!gAMvy5;5s&$O%idT)i za)gSywq5hj;Jl<~Cv>dKOUti&(TwxYuUr;il5iyPNX3#zC41w7&lpTQt*U%XnR9tY zOfU1YI+ybdo)&j=%)Ts4*cxS+r!uLa>!3ibhC^8D9gidBUGL@@l^ogo_px@H5N>a`g4}!JPT?=VvZ5p0Q`z{lKk_oO+9oCrt5^my@d~nlvNW_F2ez zjk)KZ7tbkWTN2{de^6s?9E+i(E!(q@e#@)LeqVt~7MEDf^)sAf#C#aYE}REVTj}|}nnMflm3 zIjevX_u*ZoEZ;lT-0jiRULAOT#?_6K2Ex`)vV%NknHZeeHWvzaaG2?e}6YK zP-G7u`xf6Y`Tq99jZ3W_OuQI9ssH-Z%lGCo#kC(!+njheb9tZQ>2I%n)FyY|uiki$ zUvbX0>5~_=l&a60H!ou0gO4947Wyx6PCm*%HGI18p_fHFpIo)r5&Qr4ZE0^&1*yl| zQsnMu|8cg+ebcIST73Q8@MXNfNEPno)~{Xf#`{p@W^edogV|^A*!Fy~Nk3|E=N_j? zUC6%jrCbk1u1uCHsIE7tS@4R_+c8;rZ`QsG{SJQ8#%ZnHa!`{&<19d)HjYd7yZ)+x*}!PC3R>gbFuxu>?ot*^Wg zU%++b;k9EPazP(Do@->z-e7)i7DL>1S;1=66~Py#F)~cym3;ajCMEsYD#2&#^f}*( z%(?lAXYI<5W&1eJWxaXoeJhX2-=%;nHC;-iK{n-v-gd3Fq!aU=Tbt|&>*vqp5S%lu zM6xUCMROqIvPr-H{wuI}pkwpM(ktMeg~e)tRR1j*evZ-gix$~s@H+b}XK=n~T)Dx# zBXZ`Cc9l)R4gaEcvnwt7`ES}2n~!s+DFpKUPm>ZnwMBY*$ed{l*1K!(Hj{o(F-J7V zqkiI+IsclTwJEB%Ip)>{_C?7|b~?Of!iwPOzslDiOndj&04>`il>i{-`pb(nzBRA> z@75%AjrYL){a(LIZ+&~ut=aJXSoz9hD5b{7*C9sJJ{^fT9`s)wmiocj3Y>ke%Jlzd ZD6lAcZY!zF1}xGTJYD@<);T3K0RX?cc~}4d literal 0 HcmV?d00001 diff --git a/flutter/ayf_pack/images/ayf_icons/ayf-mixer.png b/flutter/ayf_pack/images/ayf_icons/ayf-mixer.png new file mode 100644 index 0000000000000000000000000000000000000000..76e69bc56220a14bc80f870027758f339ff33284 GIT binary patch literal 1699 zcmbVNdpy%?7~fKEwM{48HkH;mA2pep%WC)xJ8g)X+pw4}w8ZFFSenb$AxUg?x*&AU zICU^&hbS>ZC)a+2)KUr2a%n-;*W^KXBgKc4sbKA-pdywCUZywB%-Q}?*yffG(P|a zLpFXjcnkN&K^RQimhMdPiVb|1V|UnapJB4OXQ4PT{Sdy#OKghO#rEIdiq#}i8BB?n zUU?bqdgeB!9=2cC?7*f=W}b8G6WfxnAEH?x&ZT?Q>)4EHim|Tn6klPQq3aY zjH=Z`n$HXH1KFd02(*DrS;TgBRFq|I2M`Uv^d@S1)}!Oe8Cr{&wNdPq4*+k9ki-=F zS)wkWjoongrs5IXGUA_4Jl6yS1jsI3&T~j;CX!>2h(dP4e(K#9F7<^uqv5XEsf#1M zUz4&0dR?#2ddT^XxpVl#gyD{BI|~+_;Azx6waUdFiq;}()XGYkj3{0hY0rMyTN0o(wZqP*NvS;Q))LlpeH;i|w>3v5oke9%Ryf3~ zb?XwotWWG4E8jeRRO3y{(xPvoIDx0W1Jo5Dn6#k&2#n41XpJ_wRXP?y_l!s$Ag;tp zyhBM+Q^UZhXoMn^yFY2sy-}j@E-rp8R78out*j0>^4P( zNzkZMOQtjo+&hbm^>+HB?T;SqJ^J(9Wh^HlSlDkPHsu@;$&i^uIwnnWB2hvgVB$)& zaJ#|gZOb{R4faO5mePH9e2m`u`>*_jPFtMLLTet48MF5&uSgIoYbuC^bP>->NSVYR zh`9EY?*rNV=+i20Qh8O<3_psA)?>r(ej0k#iFYL0@Um>ioi{JbV6*W6Nwz z$9NeV911@o-WK$*&Pou$MNWfEi-HjbE?gI?TOR1rUO*c#us*{-Pm2oi)bvO5f1c$6QWNqoH?!Ml`e@T0G+Pzrc{A ztJ88rYbmRmVH7v3^jqFC)G@|mi$QQ8Hau!3G%6DXN+ebVD>y-Q@#ZEG4<{xf-2Qso zbBM!9?Y+_ZZODV;<8wz!TOU4jfks}^m7i23G~qVL<&=M@Crd($=;%Hz<*Ky=di&$3?!-_TCifrzeAh8Dp{OhG;iG77K4u zx>S0O%*D8Scl$lXWuG(Rq=CM0$j!N?lRUw`P>qC z$4(|^37+V|i)a1VK9fr2{cc`|HT!DuCS+|a*Pd~B3cshm!V8k3bE>(uGjH+j{bfDn z_QxW_CKSr2i5WKYw(^sNff+}kcfLV{`xWwo2AKPP6h=pyTU}kv@POIq?G8DPAzY30 zP0i3^HY3h?JT(_;+p*2+N3z9rqvU$=)!ClM>t0a>+D%kb)+ZDqT}`;)GGGjSEHFCN K-C63$7W@sRhA)!< literal 0 HcmV?d00001 diff --git a/flutter/ayf_pack/images/ayf_icons/ayf-plot.png b/flutter/ayf_pack/images/ayf_icons/ayf-plot.png new file mode 100644 index 0000000000000000000000000000000000000000..ef9d29c706e9891cbe35a0cc3b3fd76170352d61 GIT binary patch literal 35267 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 literal 0 HcmV?d00001 diff --git a/flutter/ayf_pack/images/ayf_icons/ayf-remote.png b/flutter/ayf_pack/images/ayf_icons/ayf-remote.png new file mode 100644 index 0000000000000000000000000000000000000000..c18ec24b08cc28ce28a6448ff38020e894b970e3 GIT binary patch literal 3340 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 s#}w978&gV!Z literal 0 HcmV?d00001 diff --git a/flutter/ayf_pack/images/ayf_icons/ayf-soundi.png b/flutter/ayf_pack/images/ayf_icons/ayf-soundi.png new file mode 100644 index 0000000000000000000000000000000000000000..f30e0dea99e3a39caad4560fbca2140a0b96f808 GIT binary patch literal 12664 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 Y%b#_BD?7OoIEid>aoceF@4No-@4lWgfB*mh literal 0 HcmV?d00001 diff --git a/flutter/ayf_pack/images/ayf_icons/ayf-stream.png b/flutter/ayf_pack/images/ayf_icons/ayf-stream.png new file mode 100644 index 0000000000000000000000000000000000000000..13021dc354819bbcd88fb2b7767d3f22cb8cedd7 GIT binary patch literal 1115 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 literal 0 HcmV?d00001 diff --git a/flutter/ayf_pack/launch.json.in b/flutter/ayf_pack/launch.json.in new file mode 100644 index 00000000..dd8e3313 --- /dev/null +++ b/flutter/ayf_pack/launch.json.in @@ -0,0 +1,59 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "ayf_pack_example (debug)", + "request": "launch", + "type": "dart", + "program": "example/lib/main.dart", + "deviceId": "windows", + + // https://stackoverflow.com/questions/55004302/how-do-you-pass-arguments-from-command-line-to-main-in-flutter-dart + // https://dartcode.org/docs/launch-configuration/ + "args": [ + // Make sure that the sofa folder is in the runtime or the sdk folder - it often is the SDK output folder!! + "--dart-entrypoint-args", "--workdir", + "--dart-entrypoint-args", "@AYF_FLUTTER_WORKDIR@", + "--dart-entrypoint-args", "--preload", + "--dart-entrypoint-args", "@AYF_FLUTTER_WORKDIR@/bin/zlib.dll", + "--dart-entrypoint-args", "--preload", + "--dart-entrypoint-args", "@AYF_FLUTTER_WORKDIR@/bin/libfftw3-3.dll", + "--dart-entrypoint-args", "--jvxdir", + "--dart-entrypoint-args", "jvxComponents", + "--dart-entrypoint-args", "--config", + "--dart-entrypoint-args", "ayfstarter.jvx", + "--dart-entrypoint-args", "--textlog", + "--dart-entrypoint-args", "--verbose_dll", + "--dart-entrypoint-args", "--textloglev", + "--dart-entrypoint-args", "10" + ], + // "cwd": "C:\\jvxrt\\build.audio.all\\release\\runtime", <- this does not work since the interpreter can not find the main.dart script + "flutterMode": "debug", + }, + { + "name": "ayf_pack_example (debug-noargs)", + "request": "launch", + "type": "dart", + "program": "lib/main.dart", + "deviceId": "windows", + + // https://stackoverflow.com/questions/55004302/how-do-you-pass-arguments-from-command-line-to-main-in-flutter-dart + // https://dartcode.org/docs/launch-configuration/ + "args": [], + // "cwd": "C:\\jvxrt\\build.audio.all\\release\\runtime", <- this does not work since the interpreter can not find the main.dart script + "flutterMode": "debug", + }, + { + "name": "fernlive (profile mode)", + "request": "launch", + "type": "dart", + "args": [ + "--dart-define", "MY_VAR=foo" + ], + "flutterMode": "profile" + } + ] +} \ No newline at end of file diff --git a/flutter/ayf_pack/lib/ayf_pack.dart b/flutter/ayf_pack/lib/ayf_pack.dart new file mode 100644 index 00000000..6b5d2bb4 --- /dev/null +++ b/flutter/ayf_pack/lib/ayf_pack.dart @@ -0,0 +1,10 @@ +import 'ayf_pack_platform_interface.dart'; +export 'ayf_pack_common.dart'; +export 'package:ayf_pack_native/ayf_pack_native.dart' + if (dart.library.html) 'ayf_pack_web.dart'; + +class AyfPack { + Future getPlatformVersion() { + return AyfPackPlatform.instance.getPlatformVersion(); + } +} diff --git a/flutter/ayf_pack/lib/ayf_pack_common.dart b/flutter/ayf_pack/lib/ayf_pack_common.dart new file mode 100644 index 00000000..7f3bfc62 --- /dev/null +++ b/flutter/ayf_pack/lib/ayf_pack_common.dart @@ -0,0 +1,104 @@ +library ayf_pack_common; + +// ============================================================================ + +export 'periodic/ayf_periodic_notifier.dart'; +export 'notifiers/ayf_notifiers.dart'; +export 'mixer-widgets/ayf_mixer_slider_with_size.dart'; +export 'mixer-widgets/ayf_mixer_slider.dart'; + +export 'properties/ayf_properties_local.dart'; +export 'properties/ayf_properties_backend.dart'; +export 'properties/ayf_properties_from_string.dart'; +export 'properties/ayf_properties_to_string.dart'; +export 'properties/ayf_properties_from_json.dart'; + +export 'platform/ayf_platform_specific.dart'; + +export 'types/ayf_bitfield_common.dart'; +export 'types/ayf_bitfield.dart'; +export 'types/ayf_sequencer_types.dart'; + +export 'systemstate/ayf_connected_processes.dart'; + +export 'types/ayf_components.dart'; +export 'types/ayf_types.dart'; +export 'types/ayf_cache_types.dart'; +export 'types/ayf_model_types.dart'; + +export 'systemstate/ayf_selected_components.dart'; + +export 'constants/ayf_constants.dart'; +export 'constants/ayf_constants_audio.dart'; +export 'constants/ayf_constants_properties.dart'; + +export 'interfaces/ayf_interface_bectrl_if.dart'; +export 'interfaces/ayf_interface_fectrl_if.dart'; +export 'interfaces/ayf_interface_backend_adapter_if.dart'; +export 'interfaces/ayf_interface_properties.dart'; +export 'interfaces/ayf_interface_host.dart'; +export 'interfaces/ayf_core_pack_if.dart'; + +// export 'interfaces/ayf_interface_host.dart'; + +export 'models/ayf_component_selection.dart'; +export 'models/ayf_backend_cache.dart'; +export 'models/ayf_debug_model.dart'; +export 'models/ayf_ui_model.dart'; + +export 'helpers/ayf_multi_components_property_on_change.dart'; +export 'helpers/ayf_helper_lib.dart'; +export 'helpers/ayf_component_property_groups.dart'; +export 'helpers/ayf_one_component_property_on_change.dart'; +export 'helpers/ayf_one_technology_devices_on_change.dart'; +export 'helpers/ayf_translate_from_string.dart'; + +export 'devices/ayf_devicelist_from_json.dart'; +export 'sequencer/ayf_sequencer_status_from_json.dart'; + +export 'component-grid-widget/ayf_prop_list_combobox.dart'; +export 'component-grid-widget/ayf_prop_textfield.dart'; +export 'component-grid-widget/ayf_property_gridview.dart'; + +export 'dialogs/ayf_settings.dart'; +export 'settings/ayf_settings_property_list.dart'; +export 'settings/ayf_settings_property_list_content.dart'; + +export 'simple-widgets/ayf_progress_property.dart'; + +// ============================================================================= +// Sub widgets +// ============================================================================= +export 'sub-widgets/ayf_active_textfield.dart'; +export 'sub-widgets/ayf_component_combobox.dart'; +export 'sub-widgets/devices/ayf_device_selection_widget.dart'; + +export 'sub-widgets/devices/ayf_devices_active_inactive_widget.dart'; + +export 'sub-widgets/audio-devices/ayf_audio_device_active_inactive_sliver.dart'; +export 'sub-widgets/audio-devices/ayf_audio_device_channel_selection_widget.dart'; + +export 'sub-widgets/file-input-devices/ayf_file_input_active_devices_sliver.dart'; +export 'sub-widgets/file-input-devices/ayf_file_input_active_devices.dart'; + +export 'sub-widgets/headtracker-devices/ayf_active_inactive_headtracker_device_sliver.dart'; +// ============================================================================= +// ============================================================================= + +export 'main-widgets/ayf_play_pannel.dart'; + +export 'main-widgets/ayf_debug_widget.dart'; +export 'main-widgets/ayf_component_widget.dart'; +export 'main-widgets/ayf_upper_content.dart'; +export 'main-widgets/ayf_bottom_content.dart'; +export 'main-widgets/ayf_app_bar.dart'; +export 'main-widgets/ayf_bottom_app_bar.dart'; + +export 'main-widgets/ayf_main_layout.dart'; +export 'main-widgets/ayf_main_widget.dart'; +export 'main-widgets/ayf_app_widget.dart'; +export 'main-widgets/ayf_main_inner_content.dart'; + +export 'main-widgets/ayf_fileinput_control_widget.dart'; + +export 'route-widgets/ayf_route_connect.dart'; diff --git a/flutter/ayf_pack/lib/ayf_pack_local.dart b/flutter/ayf_pack/lib/ayf_pack_local.dart new file mode 100644 index 00000000..d8fe768c --- /dev/null +++ b/flutter/ayf_pack/lib/ayf_pack_local.dart @@ -0,0 +1 @@ +export 'ayf_pack.dart'; diff --git a/flutter/ayf_pack/lib/ayf_pack_method_channel.dart b/flutter/ayf_pack/lib/ayf_pack_method_channel.dart new file mode 100644 index 00000000..12a5006f --- /dev/null +++ b/flutter/ayf_pack/lib/ayf_pack_method_channel.dart @@ -0,0 +1,17 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +import 'ayf_pack_platform_interface.dart'; + +/// An implementation of [AyfPackPlatform] that uses method channels. +class MethodChannelAyfPack extends AyfPackPlatform { + /// The method channel used to interact with the native platform. + @visibleForTesting + final methodChannel = const MethodChannel('ayf_pack'); + + @override + Future getPlatformVersion() async { + final version = await methodChannel.invokeMethod('getPlatformVersion'); + return version; + } +} diff --git a/flutter/ayf_pack/lib/ayf_pack_platform_interface.dart b/flutter/ayf_pack/lib/ayf_pack_platform_interface.dart new file mode 100644 index 00000000..e2b9b3a6 --- /dev/null +++ b/flutter/ayf_pack/lib/ayf_pack_platform_interface.dart @@ -0,0 +1,29 @@ +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'ayf_pack_method_channel.dart'; + +abstract class AyfPackPlatform extends PlatformInterface { + /// Constructs a AyfPackPlatform. + AyfPackPlatform() : super(token: _token); + + static final Object _token = Object(); + + static AyfPackPlatform _instance = MethodChannelAyfPack(); + + /// The default instance of [AyfPackPlatform] to use. + /// + /// Defaults to [MethodChannelAyfPack]. + static AyfPackPlatform get instance => _instance; + + /// Platform-specific implementations should set this with their own + /// platform-specific class that extends [AyfPackPlatform] when + /// they register themselves. + static set instance(AyfPackPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + Future getPlatformVersion() { + throw UnimplementedError('platformVersion() has not been implemented.'); + } +} diff --git a/flutter/ayf_pack/lib/ayf_pack_web.dart b/flutter/ayf_pack/lib/ayf_pack_web.dart new file mode 100644 index 00000000..c05b68bf --- /dev/null +++ b/flutter/ayf_pack/lib/ayf_pack_web.dart @@ -0,0 +1,33 @@ +library ayf_pack_web; + +export 'properties/web/ayf_properties_from_json_simple.dart'; +export 'types/web/ayf_bitfield_simple.dart'; +export 'properties/web/ayf_properties.dart'; +export 'platform/web/ayf_platform_specific.dart'; + +export 'helpers/web/ayf_helper_lib.dart'; + +// In order to *not* need this ignore, consider extracting the "web" version +// of your plugin as a separate package, instead of inlining it in the same +// package as the core of your plugin. +// ignore: avoid_web_libraries_in_flutter +import 'dart:html' as html show window; +import 'package:flutter_web_plugins/flutter_web_plugins.dart'; +import 'ayf_pack_platform_interface.dart'; + +/// A web implementation of the AyfPackPlatform of the AyfPack plugin. +class AyfPackWeb extends AyfPackPlatform { + /// Constructs a AyfPackWeb + AyfPackWeb(); + + static void registerWith(Registrar registrar) { + AyfPackPlatform.instance = AyfPackWeb(); + } + + /// Returns a [String] containing the version of the platform. + @override + Future getPlatformVersion() async { + final version = html.window.navigator.userAgent; + return version; + } +} diff --git a/flutter/ayf_pack/lib/component-grid-widget/ayf_prop_list_combobox.dart b/flutter/ayf_pack/lib/component-grid-widget/ayf_prop_list_combobox.dart new file mode 100644 index 00000000..daa6bb33 --- /dev/null +++ b/flutter/ayf_pack/lib/component-grid-widget/ayf_prop_list_combobox.dart @@ -0,0 +1,205 @@ +import 'package:flutter/material.dart'; + +import 'package:collection/collection.dart'; +import 'package:provider/provider.dart'; +import '../ayf_pack_local.dart'; + +class AudYoFloPropLstSelEntry { + String txt = 'not-set'; + bool isSel = false; + int posi = -1; +} + +// =========================================================================== +// Combobox widget to show a single selection list property. The property +// can be a backend property OR a frontend property. +// The set-operation may report change of property OR change of component +// in backend cache. +// =========================================================================== +class AudYoFloPropListComboBoxWidget extends StatefulWidget { + final AudYoFloPropertySelectionListBackend? selLstBe; + final AudYoFloPropertyMultiStringBackend? strLstBe; + final AudYoFloPropertySelectionListLocal? selLstLo; + final AyfPropertyReportLevel reportSet; + final bool invalidatePropOnSet; + final double height; + final int numCharsMax = 25; + final String prefix; + + /* This combobox can be used to control backend and local + properties. There should always be either selLstBe or + selLstLo. It constructs the combobox with all options and + then waits for a selection. + In reportSet the report type is specified.*/ + AudYoFloPropListComboBoxWidget( + {this.selLstBe, + this.selLstLo, + this.strLstBe, + this.reportSet = + AyfPropertyReportLevel.AYF_BACKEND_REPORT_COMPONENT_PROPERTY_COLLECT, + this.invalidatePropOnSet = true, + this.height = 40, + this.prefix = ' '}); + + @override + State createState() { + return _AudYoFloPropSelectionListComboBox(); + } +} + +// =========================================================================== + +class _AudYoFloPropSelectionListComboBox + extends State { + AudYoFloBackendCache? theBeCache; + AudYoFloUiModel? theUiModel; + + @override + Widget build(BuildContext context) { + // This function is called if the grid view becomes visible - + // maybe it is kind of a resize procedure. + int posi = 0; + + List entriesCombo = []; + AudYoFloPropLstSelEntry? sel; + + // Get model contexts + theBeCache = Provider.of(context, listen: false); + theUiModel = Provider.of(context, listen: false); + + String noSelectionHint = 'No Select'; + + if (widget.selLstBe != null) { + // Backend property + for (var sgl in widget.selLstBe!.parpropms.entries) { + AudYoFloPropLstSelEntry newEntry = AudYoFloPropLstSelEntry(); + newEntry.txt = sgl; + newEntry.posi = posi; + newEntry.isSel = widget.selLstBe!.selection.bitTest(posi); + if (newEntry.isSel) { + if (sel == null) { + sel = newEntry; + } + } + entriesCombo.add(newEntry); + posi++; + } + } else if (widget.selLstLo != null) { + // Frontend property + for (var sgl in widget.selLstLo!.entries) { + AudYoFloPropLstSelEntry newEntry = AudYoFloPropLstSelEntry(); + newEntry.txt = sgl; + newEntry.posi = posi; + newEntry.isSel = widget.selLstLo!.selection.bitTest(posi); + if (newEntry.isSel) { + if (sel == null) { + sel = newEntry; + } + } + entriesCombo.add(newEntry); + posi++; + } + } else if (widget.strLstBe != null) { + // Frontend property + for (var sgl in widget.strLstBe!.entries) { + AudYoFloPropLstSelEntry newEntry = AudYoFloPropLstSelEntry(); + newEntry.txt = sgl; + newEntry.posi = posi; + newEntry.isSel = false; + if (sel == null) { + sel = newEntry; + } + entriesCombo.add(newEntry); + posi++; + } + } else { + // No property + // assert(false); + } + + return SizedBox( + height: widget.height, + child: Container( + height: widget.height, + decoration: BoxDecoration( + border: Border.all( + color: Colors.black, + ), + borderRadius: BorderRadius.all(Radius.circular(10)), + color: Colors.white, + ), + + //color: Colors.white, + child: Center( + child: DropdownButtonFormField( + items: entriesCombo + .map>( + (AudYoFloPropLstSelEntry value) { + String txt = value.txt; + txt = limitString(txt, widget.numCharsMax); + txt = widget.prefix + txt; + Widget cW = Text(txt); + if (value.isSel) { + cW = Text(txt, + style: TextStyle(fontWeight: FontWeight.bold)); + } + return DropdownMenuItem( + value: value, + child: cW, + ); + }).toList(), + value: sel, + isExpanded: true, + isDense: true, + /* + HK: This does not work: the OutlineInputBorder hides the text if + the selection is not clicked. + + decoration: InputDecoration( + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(25.0), + borderSide: BorderSide(color: Colors.pinkAccent)), + + filled: true, + fillColor: Colors.white, + //border: OutlineInputBorder( + // gapPadding: 12, borderRadius: BorderRadius.circular(12)), + ),*/ + onChanged: (AudYoFloPropLstSelEntry? newValue) { + // This triggers setProperty and reports change + if (theBeCache != null) { + if (newValue != null) { + AudYoFloPropLstSelEntry? entry = entriesCombo + .firstWhereOrNull((element) => element == newValue); + if (entry != null) { + if (widget.selLstBe != null) { + // Run and apply user input + applyChangeSelectionList( + widget.selLstBe!.selection, + newValue.posi, + widget.selLstBe!.parpropms.decoderHintType); + + JvxComponentIdentification cpId = + widget.selLstBe!.parpropms.assCpIdent; + List propContents = [ + widget.selLstBe!.parpropms.descriptor + ]; + + // We need to propagate the updated property to the + // higher level widget to refresh the property in cache + theBeCache!.triggerSetProperties(cpId, propContents, + report: widget.reportSet, + invalidateProperty: widget.invalidatePropOnSet); + } else if (widget.selLstLo != null) { + applyChangeSelectionList(widget.selLstLo!.selection, + newValue.posi, widget.selLstLo!.decTp); + theUiModel!.reportSetProperty(widget.selLstLo!); + } + } + } + } + }), + )), + ); + } +} diff --git a/flutter/ayf_pack/lib/component-grid-widget/ayf_prop_textfield.dart b/flutter/ayf_pack/lib/component-grid-widget/ayf_prop_textfield.dart new file mode 100644 index 00000000..a4a060e0 --- /dev/null +++ b/flutter/ayf_pack/lib/component-grid-widget/ayf_prop_textfield.dart @@ -0,0 +1,142 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import '../ayf_pack_local.dart'; + +// =========================================================================== +// Simple text field to edit a property. The property may be a backend property +// OR a frontend property +// =========================================================================== +class AudYoFloSimpleTextField extends StatefulWidget { + final AudYoFloPropertyContainer? propBe; + final AudYoFloPropertyContentLocal? propLo; + final AyfPropertyReportLevel reportSet; + final bool invalidatePropOnSet; + final double height; + final int numCharsMax = 32; + final String hintText; + AudYoFloSimpleTextField( + {this.propBe, + this.propLo, + this.reportSet = + AyfPropertyReportLevel.AYF_BACKEND_REPORT_COMPONENT_PROPERTY_COLLECT, + this.invalidatePropOnSet = true, + this.height = 45, + this.hintText = ''}); + @override + State createState() { + return _AudYoFloSimpleTextFieldStates(); + } +} + +// =========================================================================== + +class _AudYoFloSimpleTextFieldStates extends State { + final TextEditingController _controllerIn = TextEditingController(); + AudYoFloBackendCache? theBeCache; + AudYoFloUiModel? theUiModel; + @override + Widget build(BuildContext context) { + // Get both models for backend and frontend properties + theBeCache = Provider.of(context, listen: false); + theUiModel = Provider.of(context, listen: false); + + // We need to set the value, not just the text! + // https://stackoverflow.com/questions/51127241/how-do-you-change-the-value-inside-of-a-textfield-flutter + String textOnCreate = 'not-set'; + if (widget.propBe != null) { + // Backend property + textOnCreate = widget.propBe.toString(); + } else if (widget.propLo != null) { + // local frontend property + textOnCreate = widget.propLo.toString(); + } else { + // No property + } + + textOnCreate = limitString(textOnCreate, widget.numCharsMax); + + _controllerIn.value = TextEditingValue( + text: textOnCreate, + selection: TextSelection.fromPosition( + TextPosition(offset: textOnCreate.length), + )); + InputDecoration? inDec; + if (widget.hintText.isNotEmpty) { + inDec = InputDecoration( + hintText: widget.hintText, + border: OutlineInputBorder(), + alignLabelWithHint: true); + } + + return SizedBox( + height: widget.height, + child: TextFormField( + controller: _controllerIn, + keyboardType: TextInputType.text, + maxLines: 1, + minLines: 1, + decoration: InputDecoration( + + // Here I have tested a lot. If the text is too wide, it disappears! + // We can avoid this by re-defining the default EdgeInsets. We need the left border inset however, to look nice. + // isDense: true, + contentPadding: const EdgeInsets.fromLTRB(5, 0, 0, 0), + filled: true, + fillColor: Colors.white, + border: OutlineInputBorder( + gapPadding: 12, borderRadius: BorderRadius.circular(12)), + ), + onEditingComplete: () { + String text = _controllerIn.text; + bool triggerSet = false; + if (widget.propBe != null) { + if (theBeCache != null) { + JvxComponentIdentification cpId = widget.propBe!.assCpIdent; + + if (widget.propBe != null) { + if (widget.propBe is AudYoFloPropertySingleStringBackend) { + AudYoFloPropertySingleStringBackend propStr = + widget.propBe as AudYoFloPropertySingleStringBackend; + propStr.value = _controllerIn.text; + triggerSet = true; + } else if (widget.propBe + is AudYoFloPropertyMultiContentBackend) { + AudYoFloPropertyMultiContentBackend propM = + widget.propBe as AudYoFloPropertyMultiContentBackend; + bool suc = + propertyValueMultiFromString(propM, _controllerIn.text); + if (suc == true) { + triggerSet = true; + } + } else { + print( + 'Invalid property type for descriptor ${widget.propBe!.descriptor} in AudYoFloSimpleTextField.'); + } + } else { + print('Invalid null property in AudYoFloSimpleTextField.'); + } + if (triggerSet) { + List lst = [widget.propBe!.descriptor]; + + // Trigger set_property and notify all widgets to listen for all + // properties related to this component + theBeCache!.triggerSetProperties(cpId, lst, + invalidateProperty: widget.invalidatePropOnSet, + report: widget.reportSet); + } + } + } else if (widget.propLo != null) { + AudYoFloPropertyContentLocal prop = widget.propLo!; + if (prop is AudYoFloPropertyStringLocal) { + // Copy text to property + prop.value = text; + } else if (prop is AudYoFloPropertyValue) { + // Copy text to property + prop.value = double.parse(text); + } + theUiModel!.reportSetProperty(prop); + } + }, + )); + } +} diff --git a/flutter/ayf_pack/lib/component-grid-widget/ayf_property_gridview.dart b/flutter/ayf_pack/lib/component-grid-widget/ayf_property_gridview.dart new file mode 100644 index 00000000..cc208432 --- /dev/null +++ b/flutter/ayf_pack/lib/component-grid-widget/ayf_property_gridview.dart @@ -0,0 +1,483 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:clipboard/clipboard.dart'; +import 'package:fixnum/fixnum.dart' as fn; + +import '../ayf_pack_local.dart'; + +class AudYoFloPropertyGridWidget extends StatefulWidget { + AudYoFloPropertyGridWidget(); + + @override + State createState() { + return _AudYoFloPropertyGridWidgetStates(); + } +} + +class _AudYoFloPropertyGridWidgetStates + extends State { + ScrollController _myController = ScrollController(); + // int lastIdBackendCache = 0; + List allProperties = []; + + @override + Widget build(BuildContext context) { + // + // outside selector is to be triggered on a different component election in dbg model + // + + return Selector( + selector: (context, dbgModel) { + return dbgModel.ssUpdateCpId; + }, + shouldRebuild: (previous, next) => (next != previous) ? true : false, + builder: (context, notifier, child) { + // ====================================================================== + // This function is called whenever the selected component has changed + // The selected component is stored in the debug model. Therefore we use + // a provider to listen to the debug model + // ====================================================================== + AudYoFloDebugModel theDbgModel = + Provider.of(context, listen: false); + + // Obtain currently active component + JvxComponentIdentification cpId = theDbgModel.idSelectCp; + + // Inside selector is to be triggered by a state change in the + // backend cache, e.g., when the component state changed + return Selector( + selector: (context, beCache) { + // if property is not there, return a -1 + int retVal = -1; + + // We are waiting for an update of the properties + retVal = beCache.updateIdComponentInCache(cpId); + + // Return the current id + return retVal; + }, + + // Return true if the id is a different one than it was before + shouldRebuild: (previous, next) => + (next != previous) ? true : false, + + // The builder + builder: (context, notifier, child) { + // ====================================================================== + // The builder needs to wait for an update of the prorties. Therefore + // we use a Future builder + // ====================================================================== + Future triggerPropertyList( + JvxComponentIdentification tp) async { + int errCode = jvxErrorType.JVX_NO_ERROR; + List? props; + // print(tp.txt); + + // First part: get all existing properties + List? lst; + lst = theDbgModel.be + .referencePropertyListInCache(theDbgModel.idSelectCp); + if (lst == null) { + errCode = await theDbgModel.be + .triggerUpdatePropertyListComponent( + theDbgModel.idSelectCp); + if (errCode == jvxErrorType.JVX_NO_ERROR) { + lst = theDbgModel.be + .referencePropertyListInCache(theDbgModel.idSelectCp); + } + } + + if (errCode == jvxErrorType.JVX_NO_ERROR) { + if (lst != null) { + if (lst.isEmpty) { + allProperties = []; + } else { + props = theDbgModel.be + .referenceValidPropertiesComponents( + theDbgModel.idSelectCp, lst); + if (props == null) { + errCode = await theDbgModel.be + .triggerUpdatePropertiesComponent( + theDbgModel.idSelectCp, lst); + if (errCode == jvxErrorType.JVX_NO_ERROR) { + props = theDbgModel.be + .referenceValidPropertiesComponents( + theDbgModel.idSelectCp, lst); + } + } + } + } + } + if (props == null) { + allProperties = []; + } else { + allProperties = props; + } + + return errCode; + } + + // Then, run the future builder + return FutureBuilder( + future: triggerPropertyList(theDbgModel.idSelectCp), + builder: + (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + // The future has been completed. Check the errorcode + int errCode = jvxErrorType.JVX_ERROR_INTERNAL; + int? errCodePtr = snapshot.data; + if (errCodePtr != null) { + errCode = errCodePtr; + } + + if (errCode == jvxErrorType.JVX_NO_ERROR) { + // Looks good, future operated as desired + return GridView.builder( + //scrollDirection: Axis.vertical, + gridDelegate: + SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 350, + mainAxisExtent: 100, + crossAxisSpacing: 20, + mainAxisSpacing: 20), + itemBuilder: (context, index) { + return AudYoFloPropertySingleWidget( + pContentShow: allProperties, idProp: index); + }, + itemCount: allProperties.length, + controller: _myController, + shrinkWrap: true); + //return Container( + // color: Colors.green, child: Center(child: Text('Success'))); + } + + String errTxt = + jvxErrorTypeEInt.toStringSingle(errCode); + return Container( + + // Display an error message + color: Colors.red, + child: Center(child: Text('Error = $errTxt'))); + } + + // Still waiting here + return Container( + color: Colors.grey, + child: Center(child: Text('Waiting'))); + }); + }); + }); + } +} + +// ============================================================ + +// Widget to show a single property. It is stateful since it stores the +// "showRealtime" status. +class AudYoFloPropertySingleWidget extends StatefulWidget { + final List pContentShow; + final int idProp; + AudYoFloPropertySingleWidget( + {required this.pContentShow, required this.idProp}); + + @override + State createState() { + return _AudYoFloPropertySingleWidgetState(); + } +} + +class _AudYoFloPropertySingleWidgetState + extends State { + @override + Widget build(BuildContext context) { + AudYoFloPropertyContainer? propDescr; + if (widget.idProp < widget.pContentShow.length) { + AudYoFloPropertyContainer? ref = + widget.pContentShow.elementAt(widget.idProp); + if (ref != null) { + propDescr = ref; + } + } + return AudYoFloPropertyShow(propDescr: propDescr); + } +} + +// ============================================================= + +class AudYoFloPropertyShow extends StatefulWidget { + final AudYoFloPropertyContainer? propDescr; + AudYoFloPropertyShow({required this.propDescr}); + + @override + State createState() { + return _AudYoFloPropertyShowState(); + } +} + +class _AudYoFloPropertyShowState extends State { + bool updateRt = false; + bool requirePropUpdate = false; + @override + Widget build(BuildContext context) { + return Selector( + selector: (context, periodic) { + return periodic.cntSlow; + }, + // Return true if the id is a different one than it was before + shouldRebuild: (previous, next) { + if (((previous != next) && updateRt)) { + if (!requirePropUpdate) { + print('Update property with <${widget.propDescr!.descriptor}'); + requirePropUpdate = true; + return true; + } + } + return false; + }, builder: (context, notifier, child) { + String description = 'not-found'; + String ttip = 'not-found'; + String currentValueText = 'not-found'; + Widget? secondaryWidget; + + if (widget.propDescr != null) { + description = widget.propDescr!.description; + currentValueText = widget.propDescr!.toString(); + if (widget.propDescr is AudYoFloPropertyContentBackend) { + AudYoFloPropertyContentBackend propNat = + widget.propDescr! as AudYoFloPropertyContentBackend; + ttip = propNat.descriptor + + '-' + + propNat.toHelperString() + + ': ' + + propNat.origin; + + if (requirePropUpdate) { + AudYoFloBackendCache theBeCache = + Provider.of(context, listen: false); + + JvxComponentIdentification cpId = widget.propDescr!.assCpIdent; + List props = [widget.propDescr!.descriptor]; + updateSingleProperty(theBeCache, cpId, props); + } + secondaryWidget = AudYoFloPropertyDetailView(propNat); + } else { + assert(false); + } + } + + Widget contentWidget = Text(currentValueText); + + if (widget.propDescr != null) { + switch (widget.propDescr!.jvxFormat) { + case jvxDataFormatEnum.JVX_DATAFORMAT_SELECTION_LIST: + if (widget.propDescr! is AudYoFloPropertySelectionListBackend) { + AudYoFloPropertySelectionListBackend selLst = + widget.propDescr! as AudYoFloPropertySelectionListBackend; + if (selLst.parpropms.num == 1) { + contentWidget = AudYoFloPropListComboBoxWidget( + selLstBe: selLst, height: 40); + } else { + contentWidget = AudYoFloSimpleTextField( + propBe: selLst.parpropms, height: 40); + } + } + break; + + case jvxDataFormatEnum.JVX_DATAFORMAT_STRING_LIST: + if (widget.propDescr! is AudYoFloPropertyMultiStringBackend) { + AudYoFloPropertyMultiStringBackend strLst = + widget.propDescr! as AudYoFloPropertyMultiStringBackend; + contentWidget = + AudYoFloPropListComboBoxWidget(strLstBe: strLst, height: 35); + } + break; + case jvxDataFormatEnum.JVX_DATAFORMAT_DATA: + /* + case jvxDataFormatEnum.JVX_DATAFORMAT_DOUBLE: + case jvxDataFormatEnum.JVX_DATAFORMAT_FLOAT: + */ + case jvxDataFormatEnum.JVX_DATAFORMAT_64BIT_LE: + case jvxDataFormatEnum.JVX_DATAFORMAT_32BIT_LE: + case jvxDataFormatEnum.JVX_DATAFORMAT_16BIT_LE: + case jvxDataFormatEnum.JVX_DATAFORMAT_8BIT: + case jvxDataFormatEnum.JVX_DATAFORMAT_U64BIT_LE: + case jvxDataFormatEnum.JVX_DATAFORMAT_U32BIT_LE: + case jvxDataFormatEnum.JVX_DATAFORMAT_U16BIT_LE: + case jvxDataFormatEnum.JVX_DATAFORMAT_U8BIT: + case jvxDataFormatEnum.JVX_DATAFORMAT_SIZE: + case jvxDataFormatEnum.JVX_DATAFORMAT_STRING: + case jvxDataFormatEnum.JVX_DATAFORMAT_VALUE_IN_RANGE: + contentWidget = + AudYoFloSimpleTextField(propBe: widget.propDescr!, height: 35); + break; + default: + break; + } + } + + return GestureDetector( + onSecondaryTap: () { + String txt = ''; + if (widget.propDescr != null) { + txt = widget.propDescr!.descriptor; + } + FlutterClipboard.copy(txt) + .then((value) => print('Copied $txt to clipboard')); + }, + onDoubleTap: () { + String txt = ''; + if (widget.propDescr != null) { + txt = widget.propDescr!.toString(); + } + FlutterClipboard.copy(txt) + .then((value) => print('Copied $txt to clipboard')); + }, + child: Container( + color: Colors.lightBlue, + child: DecoratedBox( + decoration: BoxDecoration( + color: Colors.grey[350], + border: Border.all( + color: Colors.grey, + width: 4, + ), + borderRadius: BorderRadius.circular(15)), + child: Column(children: [ + Container( + height: 100, + //padding: EdgeInsets.all(3), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Flexible( + flex: 1, + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Flexible( + flex: 1, child: Text(description)), + Flexible(flex: 1, child: secondaryWidget!) + ]), + ), + Flexible( + flex: 1, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Flexible( + flex: 5, + child: Tooltip( + message: ttip, + child: contentWidget)), + Flexible( + flex: 1, + child: Checkbox( + value: updateRt, + onChanged: (value) { + setState(() { + updateRt = value!; + }); + }, + )) + ], + )) + ]), + )), + ]))), + ); + }); + } + + void updateSingleProperty(AudYoFloBackendCache theBeCache, + JvxComponentIdentification cpId, List props) async { + theBeCache.invalidatePropertiesComponent(cpId, props, true); + await theBeCache.triggerUpdatePropertiesComponent(cpId, props); + setState(() { + requirePropUpdate = false; + }); + } +} + +class AudYoFloPropertyDetailView extends StatelessWidget { + final AudYoFloPropertyContentBackend prop; + + AudYoFloPropertyDetailView(this.prop); + + @override + Widget build(BuildContext context) { + // TODO: implement build + String txtAllowedState = ""; + if (prop.allowedStateMask.and( + prop.allowedStateMask.fld, fn.Int32(jvxState.JVX_STATE_INIT)) != + 0) { + txtAllowedState += "i"; + } else { + txtAllowedState += "-"; + } + if (prop.allowedStateMask.and( + prop.allowedStateMask.fld, fn.Int32(jvxState.JVX_STATE_SELECTED)) != + 0) { + txtAllowedState += "s"; + } else { + txtAllowedState += "-"; + } + if (prop.allowedStateMask.and( + prop.allowedStateMask.fld, fn.Int32(jvxState.JVX_STATE_ACTIVE)) != + 0) { + txtAllowedState += "a"; + } else { + txtAllowedState += "-"; + } + if (prop.allowedStateMask.and( + prop.allowedStateMask.fld, fn.Int32(jvxState.JVX_STATE_PREPARED)) != + 0) { + txtAllowedState += "p"; + } else { + txtAllowedState += "-"; + } + + if (prop.allowedStateMask.and(prop.allowedStateMask.fld, + fn.Int32(jvxState.JVX_STATE_PROCESSING)) != + 0) { + txtAllowedState += "r"; + } else { + txtAllowedState += "-"; + } + + String txtAccess = ""; + switch (prop.accessType) { + case jvxPropertyAccessTypeEnum.JVX_PROPERTY_ACCESS_FULL_READ_AND_WRITE: + txtAccess = "fu"; + break; + case jvxPropertyAccessTypeEnum.JVX_PROPERTY_ACCESS_READ_AND_WRITE_CONTENT: + txtAccess = "rwc"; + break; + case jvxPropertyAccessTypeEnum.JVX_PROPERTY_ACCESS_READ_ONLY: + txtAccess = "ro"; + break; + case jvxPropertyAccessTypeEnum.JVX_PROPERTY_ACCESS_READ_ONLY_ON_START: + txtAccess = "ros"; + break; + case jvxPropertyAccessTypeEnum.JVX_PROPERTY_ACCESS_WRITE_ONLY: + txtAccess = "wo"; + break; + } + return Row(mainAxisAlignment: MainAxisAlignment.end, children: [ + Flexible( + flex: 5, + child: Tooltip( + message: "Allowed State Mask", + child: Container( + color: Colors.green, child: Text(txtAllowedState)))), + Spacer(flex: 1), + Flexible( + flex: 5, + child: Tooltip( + message: "Property Access Type", + child: Container(color: Colors.blue, child: Text(txtAccess)))) + ]); + } +} diff --git a/flutter/ayf_pack/lib/constants/ayf_constants.dart b/flutter/ayf_pack/lib/constants/ayf_constants.dart new file mode 100644 index 00000000..b904d809 --- /dev/null +++ b/flutter/ayf_pack/lib/constants/ayf_constants.dart @@ -0,0 +1,793 @@ +import 'package:flutter/foundation.dart'; + +enum jvxDataTypeSpecEnum { JVX_DATA_TYPE_SPEC_DOUBLE, JVX_DATA_TYPE_SPEC_FLOAT } + +extension jvxDataTypeSpecEEnum on jvxDataTypeSpecEnum { + static String get formatName => "jvxDataTypeSpec"; + String get txt => describeEnum(this); + static jvxDataTypeSpecEnum fromInt(int val) { + jvxDataTypeSpecEnum ss = jvxDataTypeSpecEnum.JVX_DATA_TYPE_SPEC_DOUBLE; + if (val < jvxDataTypeSpecEnum.values.length) { + ss = jvxDataTypeSpecEnum.values[val]; + } + return ss; + } + + static jvxDataTypeSpecEnum fromString(String val) { + jvxDataTypeSpecEnum ss = jvxDataTypeSpecEnum.JVX_DATA_TYPE_SPEC_DOUBLE; + if (val == 'dbl') { + ss = jvxDataTypeSpecEnum.JVX_DATA_TYPE_SPEC_DOUBLE; + } else { + ss = jvxDataTypeSpecEnum.JVX_DATA_TYPE_SPEC_FLOAT; + } + return ss; + } +} + +enum jvxComponentTypeClassEnum { + JVX_COMPONENT_TYPE_NONE, + JVX_COMPONENT_TYPE_TECHNOLOGY, + JVX_COMPONENT_TYPE_NODE, + JVX_COMPONENT_TYPE_HOST, + JVX_COMPONENT_TYPE_CODEC, + JVX_COMPONENT_TYPE_TOOL, + JVX_COMPONENT_TYPE_SIMPLE, + JVX_COMPONENT_TYPE_DEVICE, + JVX_COMPONENT_TYPE_PROCESS +} + +extension jvxComponentTypeClassEEnum on jvxComponentTypeClassEnum { + static String get formatName => "jvxComponentTypeClass"; + String get txt => describeEnum(this); + static jvxComponentTypeClassEnum fromInt(int val) { + jvxComponentTypeClassEnum ss = + jvxComponentTypeClassEnum.JVX_COMPONENT_TYPE_NONE; + if (val < jvxComponentTypeClassEnum.values.length) { + ss = jvxComponentTypeClassEnum.values[val]; + } + return ss; + } +} + +// ==================================================================== +// ==================================================================== + +abstract class jvxReportPriority { + static const int JVX_REPORT_PRIORITY_NONE = 0; + static const int JVX_REPORT_PRIORITY_ERROR = 1; + static const int JVX_REPORT_PRIORITY_WARNING = 2; + static const int JVX_REPORT_PRIORITY_SUCCESS = 3; + static const int JVX_REPORT_PRIORITY_INFO = 4; + static const int JVX_REPORT_PRIORITY_VERBOSE = 5; + static const int JVX_REPORT_PRIORITY_DEBUG = 6; + static const int JVX_REPORT_PRIORITY_LIMIT = 7; +} + +// enum ffiCallbackIds { JVX_FFI_CALLBACK_REPORT_TEXT } + +abstract class jvxPropertyCategoryType { + static const int JVX_PROPERTY_CATEGORY_UNKNOWN = 0; + static const int JVX_PROPERTY_CATEGORY_PREDEFINED = 1; + static const int JVX_PROPERTY_CATEGORY_UNSPECIFIC = 2; +} + +// =========================================================== +/* +abstract class jvxDataFormat { + static const int JVX_DATAFORMAT_NONE = 0; + static const int JVX_DATAFORMAT_DATA = 1; + static const int JVX_DATAFORMAT_16BIT_LE = 2; + static const int JVX_DATAFORMAT_32BIT_LE = 3; + static const int JVX_DATAFORMAT_64BIT_LE = 4; + static const int JVX_DATAFORMAT_8BIT = 5; + static const int JVX_DATAFORMAT_SIZE = 6; + static const int JVX_DATAFORMAT_STRING = 7; + static const int JVX_DATAFORMAT_STRING_LIST = 8; + static const int JVX_DATAFORMAT_SELECTION_LIST = 9; + static const int JVX_DATAFORMAT_VALUE_IN_RANGE = 10; + static const int JVX_DATAFORMAT_U16BIT_LE = 11; + static const int JVX_DATAFORMAT_U32BIT_LE = 12; + static const int JVX_DATAFORMAT_U64BIT_LE = 13; + static const int JVX_DATAFORMAT_U8BIT = 14; + static const int JVX_DATAFORMAT_FLOAT = 15; + static const int JVX_DATAFORMAT_DOUBLE = 16; + static const int JVX_DATAFORMAT_BYTE = 17; + + static const int JVX_DATAFORMAT_HANDLE = 18; + static const int JVX_DATAFORMAT_POINTER = 19; + static const int JVX_DATAFORMAT_CALLBACK = 20; + static const int JVX_DATAFORMAT_INTERFACE = 21; +} +*/ +enum jvxDataFormatEnum { + JVX_DATAFORMAT_NONE, + JVX_DATAFORMAT_DATA, + JVX_DATAFORMAT_16BIT_LE, + JVX_DATAFORMAT_32BIT_LE, + JVX_DATAFORMAT_64BIT_LE, + JVX_DATAFORMAT_8BIT, + JVX_DATAFORMAT_SIZE, + JVX_DATAFORMAT_STRING, + JVX_DATAFORMAT_STRING_LIST, + JVX_DATAFORMAT_SELECTION_LIST, + JVX_DATAFORMAT_VALUE_IN_RANGE, + JVX_DATAFORMAT_U16BIT_LE, + JVX_DATAFORMAT_U32BIT_LE, + JVX_DATAFORMAT_U64BIT_LE, + JVX_DATAFORMAT_U8BIT, + JVX_DATAFORMAT_FLOAT, + JVX_DATAFORMAT_DOUBLE, + JVX_DATAFORMAT_BYTE, + + JVX_DATAFORMAT_HANDLE, + JVX_DATAFORMAT_POINTER, + JVX_DATAFORMAT_CALLBACK, + JVX_DATAFORMAT_INTERFACE, + JVX_DATAFORMAT_TEXT_LOG +} + +extension jvxDataFormatEEnum on jvxDataFormatEnum { + static String get formatName => "jvxDataFormat"; + String get txt => describeEnum(this); + static jvxDataFormatEnum fromInt(int val) { + jvxDataFormatEnum ss = jvxDataFormatEnum.JVX_DATAFORMAT_NONE; + if (val < jvxDataFormatEnum.values.length) { + ss = jvxDataFormatEnum.values[val]; + } + return ss; + } +} + +// =========================================================== +abstract class jvxPropertyCallPurpose { + static const int JVX_PROPERTY_CALL_PURPOSE_NONE_SPECIFIC = 0; + static const int JVX_PROPERTY_CALL_PURPOSE_INTERNAL_PASS = 1; + static const int JVX_PROPERTY_CALL_PURPOSE_TAKE_OVER = 2; + static const int JVX_PROPERTY_CALL_PURPOSE_PROPERTY_POOL = 3; + static const int JVX_PROPERTY_CALL_PURPOSE_SIZE_ONLY = 4; + static const int JVX_PROPERTY_CALL_PURPOSE_GET_ACCESS_DATA = 5; + /*JVX_PROPERTY_CALL_PURPOSE_COMPONENT_RECONFIGURE = 4*/ +} + +abstract class jvxCommandRequestFlags { + static const int JVX_REPORT_REQUEST_UPDATE_WINDOW_SHIFT = 0; + static const int JVX_REPORT_REQUEST_UPDATE_PROPERTY_VIEWER_SHIFT = 1; + static const int JVX_REPORT_REQUEST_UPDATE_PROPERTY_VIEWER_FULL_SHIFT = 2; + static const int JVX_REPORT_REQUEST_TRY_TRIGGER_STOP_SEQUENCER_PROCESS_SHIFT = + 3; + static const int JVX_REPORT_REQUEST_TRY_TRIGGER_START_SEQUENCER_SHIFT = 4; + static const int JVX_REPORT_REQUEST_UPDATE_DATACONNECTIONS_SHIFT_DEP = 5; + static const int JVX_REPORT_REQUEST_USER_MESSAGE_SHIFT = 6; + static const int JVX_REPORT_REQUEST_UPDATE_PROPERTIES_SHIFT_DEP = 7; + static const int JVX_REPORT_REQUEST_TEST_CHAIN_MASTER_SHIFT = 8; + static const int JVX_REPORT_REQUEST_RECONFIGURE_SHIFT = 9; + static const int JVX_REPORT_REQUEST_REACTIVATE_SHIFT = 10; + static const int + JVX_REPORT_REQUEST_TRY_TRIGGER_STOP_SEQUENCER_SUBROUTINE_SHIFT = 11; + static const int JVX_REPORT_REQUEST_COMMAND_RETRIGGER_SEQUENCER_SHIFT_DEP = + 12; +} + +/* +abstract class jvxReportCommandRequest { + static const int JVX_REPORT_COMMAND_REQUEST_UNSPECIFIC = 0; + static const int JVX_REPORT_COMMAND_REQUEST_UPDATE_AVAILABLE_COMPONENT_LIST = + 1; + static const int JVX_REPORT_COMMAND_REQUEST_UPDATE_STATUS_COMPONENT_LIST = 2; + static const int JVX_REPORT_COMMAND_REQUEST_UPDATE_STATUS_COMPONENT = 3; + static const int JVX_REPORT_COMMAND_REQUEST_SYSTEM_STATUS_CHANGED = 4; + static const int JVX_REPORT_COMMAND_REQUEST_TRIGGER_SEQUENCER_IMMEDIATE = 5; + + static const int JVX_REPORT_COMMAND_REQUEST_UPDATE_PROPERTIES = 6; + + static const int JVX_REPORT_COMMAND_REQUEST_RESCHEDULE_MAIN = 7; + + static const int JVX_REPORT_COMMAND_REQUEST_REPORT_BORN_SUBDEVICE = 8; + static const int JVX_REPORT_COMMAND_REQUEST_REPORT_DIED_SUBDEVICE = 9; + + static const int JVX_REPORT_COMMAND_REQUEST_REPORT_BORN_COMPONENT = 10; + static const int JVX_REPORT_COMMAND_REQUEST_REPORT_DIED_COMPONENT = 11; + + static const int JVX_REPORT_COMMAND_REQUEST_REPORT_PROCESS_CONNECTED = 12; + static const int + JVX_REPORT_COMMAND_REQUEST_REPORT_PROCESS_TO_BE_DISCONNECTED = 13; + static const int JVX_REPORT_COMMAND_REQUEST_TEST_CHAIN = 14; + static const int JVX_REPORT_COMMAND_REQUEST_REPORT_COMPONENT_STATESWITCH = 15; + + static const int JVX_REPORT_COMMAND_REQUEST_REPORT_CONFIGURATION_COMPLETE = + 16; + + static const int JVX_REPORT_COMMAND_REQUEST_REPORT_SEQUENCER_EVENT = 17; + static const int JVX_REPORT_COMMAND_REQUEST_REPORT_SEQUENCER_CALLBACK = 18; + + static const int + JVX_REPORT_COMMAND_REQUEST_REPORT_PROCESS_DISCONNECT_COMPLETE = 19; + + static const int JVX_REPORT_COMMAND_REQUEST_UPDATE_PROPERTY_CONTENT = 20; + static const int + JVX_REPORT_COMMAND_REQUEST_UPDATE_PROPERTY_DESCRIPTOR_CONTENT = 21; +} +*/ + +enum jvxReportCommandRequestEnum { + JVX_REPORT_COMMAND_REQUEST_UNSPECIFIC, + JVX_REPORT_COMMAND_REQUEST_UPDATE_AVAILABLE_COMPONENT_LIST, + JVX_REPORT_COMMAND_REQUEST_UPDATE_STATUS_COMPONENT_LIST, + JVX_REPORT_COMMAND_REQUEST_UPDATE_STATUS_COMPONENT, + JVX_REPORT_COMMAND_REQUEST_SYSTEM_STATUS_CHANGED, + + JVX_REPORT_COMMAND_REQUEST_TRIGGER_SEQUENCER_IMMEDIATE, + JVX_REPORT_COMMAND_REQUEST_UPDATE_ALL_PROPERTIES, + JVX_REPORT_COMMAND_REQUEST_RESCHEDULE_MAIN, + JVX_REPORT_COMMAND_REQUEST_REPORT_BORN_SUBDEVICE, + JVX_REPORT_COMMAND_REQUEST_REPORT_DIED_SUBDEVICE, + + JVX_REPORT_COMMAND_REQUEST_REPORT_BORN_COMPONENT, + JVX_REPORT_COMMAND_REQUEST_REPORT_DIED_COMPONENT, + JVX_REPORT_COMMAND_REQUEST_REPORT_PROCESS_CONNECTED, + JVX_REPORT_COMMAND_REQUEST_REPORT_PROCESS_TO_BE_DISCONNECTED, + JVX_REPORT_COMMAND_REQUEST_TEST_CHAIN, + + JVX_REPORT_COMMAND_REQUEST_REPORT_COMPONENT_STATESWITCH, + JVX_REPORT_COMMAND_REQUEST_REPORT_CONFIGURATION_COMPLETE, + JVX_REPORT_COMMAND_REQUEST_REPORT_SEQUENCER_EVENT, + JVX_REPORT_COMMAND_REQUEST_REPORT_SEQUENCER_CALLBACK, + JVX_REPORT_COMMAND_REQUEST_REPORT_PROCESS_DISCONNECT_COMPLETE, + + JVX_REPORT_COMMAND_REQUEST_UPDATE_PROPERTY, + JVX_REPORT_COMMAND_REQUEST_REPORT_TEST_SUCCESS, + JVX_REPORT_COMMAND_REQUEST_TEST_CHAIN_RUN +} + +extension jvxReportCommandRequestEEnum on jvxReportCommandRequestEnum { + String get txt => describeEnum(this); + static jvxReportCommandRequestEnum fromInt(int val) { + jvxReportCommandRequestEnum ss = + jvxReportCommandRequestEnum.JVX_REPORT_COMMAND_REQUEST_UNSPECIFIC; + assert(val < jvxReportCommandRequestEnum.values.length); + if (val < jvxReportCommandRequestEnum.values.length) { + ss = jvxReportCommandRequestEnum.values[val]; + } + return ss; + } +} + +// =========================================================================== + +enum jvxReportCommandDataTypeEnum { + JVX_REPORT_COMMAND_TYPE_BASE, + JVX_REPORT_COMMAND_TYPE_SCHEDULE, + JVX_REPORT_COMMAND_TYPE_IDENT, + JVX_REPORT_COMMAND_TYPE_UID, + JVX_REPORT_COMMAND_TYPE_SS, + JVX_REPORT_COMMAND_TYPE_SEQ +} + +extension jvxReportCommandDataTypeEEnum on jvxReportCommandDataTypeEnum { + String get txt => describeEnum(this); + static jvxReportCommandDataTypeEnum fromInt(int val) { + jvxReportCommandDataTypeEnum dt = + jvxReportCommandDataTypeEnum.JVX_REPORT_COMMAND_TYPE_BASE; + assert(val < jvxReportCommandDataTypeEnum.values.length); + if (val < jvxReportCommandDataTypeEnum.values.length) { + dt = jvxReportCommandDataTypeEnum.values[val]; + } + return dt; + } +} +// =========================================================================== + +enum jvxReportCommandBroadcastTypeEnum { + JVX_REPORT_COMMAND_BROADCAST_NONE, + JVX_REPORT_COMMAND_BROADCAST_NO_FURTHER, + JVX_REPORT_COMMAND_BROADCAST_AUTOMATION, + JVX_REPORT_COMMAND_BROADCAST_RESCHEDULED +} + +extension jvxReportCommandBroadcastTypeEEnum + on jvxReportCommandBroadcastTypeEnum { + String get txt => describeEnum(this); + static jvxReportCommandBroadcastTypeEnum fromInt(int val) { + jvxReportCommandBroadcastTypeEnum bc = + jvxReportCommandBroadcastTypeEnum.JVX_REPORT_COMMAND_BROADCAST_NONE; + if (val < jvxReportCommandBroadcastTypeEnum.values.length) { + bc = jvxReportCommandBroadcastTypeEnum.values[val]; + } + return bc; + } +} + +abstract class jvxSize { + static const int JVX_SIZE_UNSELECTED = -1; + static const int JVX_SIZE_DONTCARE = -2; + static const int JVX_SIZE_SLOT_RETAIN = -3; + static const int JVX_SIZE_SLOT_OFF_SYSTEM = -4; + static const int JVX_SIZE_STATE_INCREMENT = -5; + static const int JVX_SIZE_STATE_DECREMENT = -6; +} + +// =========================================================================== + +enum jvxStateShiftEnum { + JVX_STATE_NONE, + JVX_STATE_INIT, + JVX_STATE_SELECTED, + JVX_STATE_ACTIVE, + JVX_STATE_PREPARED, + JVX_STATE_PROCESSING, + JVX_STATE_COMPLETE +} + +abstract class jvxState { + static const int JVX_STATE_NONE = 0x1; + static const int JVX_STATE_INIT = 0x2; + static const int JVX_STATE_SELECTED = 0x4; + static const int JVX_STATE_ACTIVE = 0x8; + static const int JVX_STATE_PREPARED = 0x10; + static const int JVX_STATE_PROCESSING = 0x20; + static const int JVX_STATE_COMPLETE = 0x40; +} + +extension jvxStateEInt on jvxState { + static int fromStringSingle(String value) { + int retVal = jvxState.JVX_STATE_NONE; + switch (value) { + case 'selected': + retVal = jvxState.JVX_STATE_SELECTED; + break; + case 'active': + retVal = jvxState.JVX_STATE_ACTIVE; + break; + case 'prepared': + retVal = jvxState.JVX_STATE_PREPARED; + break; + case 'processing': + retVal = jvxState.JVX_STATE_PROCESSING; + break; + } + return retVal; + } +} +// =========================================================================== + +abstract class jvxErrorType { + static const int JVX_NO_ERROR = 0; + static const int JVX_ERROR_UNKNOWN = 1; + static const int JVX_ERROR_INVALID_ARGUMENT = 2; + static const int JVX_ERROR_WRONG_STATE = 3; + static const int JVX_ERROR_ID_OUT_OF_BOUNDS = 4; + static const int JVX_ERROR_UNSUPPORTED = 5; + static const int JVX_ERROR_ELEMENT_NOT_FOUND = 6; + static const int JVX_ERROR_DUPLICATE_ENTRY = 7; + static const int JVX_ERROR_OPEN_FILE_FAILED = 8; + static const int JVX_ERROR_UNEXPECTED = 9; + static const int JVX_ERROR_NOT_READY = 10; + static const int JVX_ERROR_COMPONENT_BUSY = 11; + static const int JVX_ERROR_BUFFER_OVERFLOW = 12; + static const int JVX_ERROR_BUFFER_UNDERRUN = 13; + static const int JVX_ERROR_WRONG_STATE_SUBMODULE = 14; + static const int JVX_ERROR_MESSAGE_QUEUE_FULL = 15; + static const int JVX_ERROR_PARSE_ERROR = 16; + static const int JVX_ERROR_WRONG_SECTION_TYPE = 17; + static const int JVX_ERROR_INTERNAL = 18; + static const int JVX_ERROR_EMPTY_LIST = 19; + static const int JVX_ERROR_CALL_SUB_COMPONENT_FAILED = 20; + static const int JVX_ERROR_THREADING_MISMATCH = 21; + static const int JVX_ERROR_SYSTEMCALL_FAILED = 22; + static const int JVX_ERROR_SIZE_MISMATCH = 23; + static const int JVX_ERROR_NO_ACCESS = 24; + static const int JVX_ERROR_INVALID_SETTING = 25; + static const int JVX_ERROR_TIMEOUT = 26; + static const int JVX_ERROR_ABORT = 27; + static const int JVX_ERROR_POSTPONE = 28; + static const int JVX_ERROR_END_OF_FILE = 29; + static const int JVX_ERROR_STANDARD_EXCEPTION = 30; + static const int JVX_ERROR_SEQUENCER_TOOK_CONTROL = 31; + static const int JVX_ERROR_THREAD_ONCE_MORE = 32; + static const int JVX_ERROR_PROCESS_COMPLETE = 33; + static const int JVX_ERROR_DSP = 34; + static const int JVX_ERROR_ALREADY_IN_USE = 35; + static const int JVX_ERROR_PROTOCOL_ERROR = 36; + static const int JVX_ERROR_NOT_ENOUGH_INPUT_DATA = 37; + static const int JVX_ERROR_NOT_ENOUGH_OUTPUT_SPACE = 38; + static const int JVX_ERROR_COMPROMISE = 39; + static const int JVX_ERROR_REMOTE = 40; + static const int JVX_ERROR_ALLOCATION = 41; + static const int JVX_ERROR_INVALID_FORMAT = 42; + static const int JVX_ERROR_REQUEST_CALL_AGAIN = 43; + static const int JVX_ERROR_NOT_IMPLEMENTED = 44; +} + +extension jvxErrorTypeEInt on jvxErrorType { + static int fromStringSingle(String value) { + int retVal = jvxErrorType.JVX_ERROR_UNEXPECTED; + + switch (value) { + case 'JVX_NO_ERROR': + retVal = jvxErrorType.JVX_NO_ERROR; + break; + case 'JVX_ERROR_UNKNOWN': + retVal = jvxErrorType.JVX_ERROR_UNKNOWN; + break; + case 'JVX_ERROR_INVALID_ARGUMENT': + retVal = jvxErrorType.JVX_ERROR_INVALID_ARGUMENT; + break; + case 'JVX_ERROR_WRONG_STATE': + retVal = jvxErrorType.JVX_ERROR_WRONG_STATE; + break; + case 'JVX_ERROR_ID_OUT_OF_BOUNDS': + retVal = jvxErrorType.JVX_ERROR_ID_OUT_OF_BOUNDS; + break; + case 'JVX_ERROR_UNSUPPORTED': + retVal = jvxErrorType.JVX_ERROR_UNSUPPORTED; + break; + case 'JVX_ERROR_ELEMENT_NOT_FOUND': + retVal = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + break; + case 'JVX_ERROR_DUPLICATE_ENTRY': + retVal = jvxErrorType.JVX_ERROR_DUPLICATE_ENTRY; + break; + case 'JVX_ERROR_OPEN_FILE_FAILED': + retVal = jvxErrorType.JVX_ERROR_OPEN_FILE_FAILED; + break; + case 'JVX_ERROR_UNEXPECTED': + retVal = jvxErrorType.JVX_ERROR_UNEXPECTED; + break; + case 'JVX_ERROR_NOT_READY': + retVal = jvxErrorType.JVX_ERROR_NOT_READY; + break; + case 'JVX_ERROR_COMPONENT_BUSY': + retVal = jvxErrorType.JVX_ERROR_COMPONENT_BUSY; + break; + case 'JVX_ERROR_BUFFER_OVERFLOW': + retVal = jvxErrorType.JVX_ERROR_BUFFER_OVERFLOW; + break; + case 'JVX_ERROR_BUFFER_UNDERRUN': + retVal = jvxErrorType.JVX_ERROR_BUFFER_UNDERRUN; + break; + case 'JVX_ERROR_WRONG_STATE_SUBMODULE': + retVal = jvxErrorType.JVX_ERROR_WRONG_STATE_SUBMODULE; + break; + case 'JVX_ERROR_MESSAGE_QUEUE_FULL': + retVal = jvxErrorType.JVX_ERROR_MESSAGE_QUEUE_FULL; + break; + case 'JVX_ERROR_PARSE_ERROR': + retVal = jvxErrorType.JVX_ERROR_PARSE_ERROR; + break; + case 'JVX_ERROR_WRONG_SECTION_TYPE': + retVal = jvxErrorType.JVX_ERROR_WRONG_SECTION_TYPE; + break; + case 'JVX_ERROR_INTERNAL': + retVal = jvxErrorType.JVX_ERROR_INTERNAL; + break; + case 'JVX_ERROR_EMPTY_LIST': + retVal = jvxErrorType.JVX_ERROR_EMPTY_LIST; + break; + case 'JVX_ERROR_CALL_SUB_COMPONENT_FAILED': + retVal = jvxErrorType.JVX_ERROR_CALL_SUB_COMPONENT_FAILED; + break; + case 'JVX_ERROR_THREADING_MISMATCH': + retVal = jvxErrorType.JVX_ERROR_THREADING_MISMATCH; + break; + case 'JVX_ERROR_SYSTEMCALL_FAILED': + retVal = jvxErrorType.JVX_ERROR_SYSTEMCALL_FAILED; + break; + case 'JVX_ERROR_SIZE_MISMATCH': + retVal = jvxErrorType.JVX_ERROR_SIZE_MISMATCH; + break; + case 'JVX_ERROR_NO_ACCESS': + retVal = jvxErrorType.JVX_ERROR_NO_ACCESS; + break; + case 'JVX_ERROR_INVALID_SETTING': + retVal = jvxErrorType.JVX_ERROR_INVALID_SETTING; + break; + case 'JVX_ERROR_TIMEOUT': + retVal = jvxErrorType.JVX_ERROR_TIMEOUT; + break; + case 'JVX_ERROR_ABORT': + retVal = jvxErrorType.JVX_ERROR_ABORT; + break; + case 'JVX_ERROR_POSTPONE': + retVal = jvxErrorType.JVX_ERROR_POSTPONE; + break; + case 'JVX_ERROR_END_OF_FILE': + retVal = jvxErrorType.JVX_ERROR_END_OF_FILE; + break; + case 'JVX_ERROR_STANDARD_EXCEPTION': + retVal = jvxErrorType.JVX_ERROR_STANDARD_EXCEPTION; + break; + case 'JVX_ERROR_SEQUENCER_TOOK_CONTROL': + retVal = jvxErrorType.JVX_ERROR_SEQUENCER_TOOK_CONTROL; + break; + case 'JVX_ERROR_THREAD_ONCE_MORE': + retVal = jvxErrorType.JVX_ERROR_THREAD_ONCE_MORE; + break; + case 'JVX_ERROR_PROCESS_COMPLETE': + retVal = jvxErrorType.JVX_ERROR_PROCESS_COMPLETE; + break; + case 'JVX_ERROR_DSP': + retVal = jvxErrorType.JVX_ERROR_DSP; + break; + case 'JVX_ERROR_ALREADY_IN_USE': + retVal = jvxErrorType.JVX_ERROR_ALREADY_IN_USE; + break; + case 'JVX_ERROR_PROTOCOL_ERROR': + retVal = jvxErrorType.JVX_ERROR_PROTOCOL_ERROR; + break; + case 'JVX_ERROR_NOT_ENOUGH_INPUT_DATA': + retVal = jvxErrorType.JVX_ERROR_NOT_ENOUGH_INPUT_DATA; + break; + case 'JVX_ERROR_NOT_ENOUGH_OUTPUT_SPACE': + retVal = jvxErrorType.JVX_ERROR_NOT_ENOUGH_OUTPUT_SPACE; + break; + case 'JVX_ERROR_COMPROMISE': + retVal = jvxErrorType.JVX_ERROR_COMPROMISE; + break; + case 'JVX_ERROR_REMOTE': + retVal = jvxErrorType.JVX_ERROR_REMOTE; + break; + case 'JVX_ERROR_ALLOCATION': + retVal = jvxErrorType.JVX_ERROR_ALLOCATION; + break; + case 'JVX_ERROR_INVALID_FORMAT': + retVal = jvxErrorType.JVX_ERROR_INVALID_FORMAT; + break; + case 'JVX_ERROR_REQUEST_CALL_AGAIN': + retVal = jvxErrorType.JVX_ERROR_REQUEST_CALL_AGAIN; + break; + case 'JVX_ERROR_NOT_IMPLEMENTED': + retVal = jvxErrorType.JVX_ERROR_NOT_IMPLEMENTED; + break; + } + return retVal; + } + + static String toStringSingle(int errCode) { + String retVal = "JVX_ERROR_UNEXPECTED"; + + switch (errCode) { + case jvxErrorType.JVX_NO_ERROR: + retVal = 'JVX_NO_ERROR'; + break; + case jvxErrorType.JVX_ERROR_UNKNOWN: + retVal = 'JVX_ERROR_UNKNOWN'; + break; + case jvxErrorType.JVX_ERROR_INVALID_ARGUMENT: + retVal = 'JVX_ERROR_INVALID_ARGUMENT'; + break; + case jvxErrorType.JVX_ERROR_WRONG_STATE: + retVal = 'JVX_ERROR_WRONG_STATE'; + break; + case jvxErrorType.JVX_ERROR_ID_OUT_OF_BOUNDS: + retVal = 'JVX_ERROR_ID_OUT_OF_BOUNDS'; + break; + case jvxErrorType.JVX_ERROR_UNSUPPORTED: + retVal = 'JVX_ERROR_UNSUPPORTED'; + break; + case jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND: + retVal = 'JVX_ERROR_ELEMENT_NOT_FOUND'; + break; + case jvxErrorType.JVX_ERROR_DUPLICATE_ENTRY: + retVal = 'JVX_ERROR_DUPLICATE_ENTRY'; + break; + case jvxErrorType.JVX_ERROR_OPEN_FILE_FAILED: + retVal = 'JVX_ERROR_OPEN_FILE_FAILED'; + break; + case jvxErrorType.JVX_ERROR_UNEXPECTED: + retVal = 'JVX_ERROR_UNEXPECTED'; + break; + case jvxErrorType.JVX_ERROR_NOT_READY: + retVal = 'JVX_ERROR_NOT_READY'; + break; + case jvxErrorType.JVX_ERROR_COMPONENT_BUSY: + retVal = 'JVX_ERROR_COMPONENT_BUSY'; + break; + case jvxErrorType.JVX_ERROR_BUFFER_OVERFLOW: + retVal = 'JVX_ERROR_BUFFER_OVERFLOW'; + break; + case jvxErrorType.JVX_ERROR_BUFFER_UNDERRUN: + retVal = 'JVX_ERROR_BUFFER_UNDERRUN'; + break; + case jvxErrorType.JVX_ERROR_WRONG_STATE_SUBMODULE: + retVal = 'JVX_ERROR_WRONG_STATE_SUBMODULE'; + break; + case jvxErrorType.JVX_ERROR_MESSAGE_QUEUE_FULL: + retVal = 'JVX_ERROR_MESSAGE_QUEUE_FULL'; + break; + case jvxErrorType.JVX_ERROR_PARSE_ERROR: + retVal = 'JVX_ERROR_PARSE_ERROR'; + break; + case jvxErrorType.JVX_ERROR_WRONG_SECTION_TYPE: + retVal = 'JVX_ERROR_WRONG_SECTION_TYPE'; + break; + case jvxErrorType.JVX_ERROR_INTERNAL: + retVal = 'JVX_ERROR_INTERNAL'; + break; + case jvxErrorType.JVX_ERROR_EMPTY_LIST: + retVal = 'JVX_ERROR_EMPTY_LIST'; + break; + case jvxErrorType.JVX_ERROR_CALL_SUB_COMPONENT_FAILED: + retVal = 'JVX_ERROR_CALL_SUB_COMPONENT_FAILED'; + break; + case jvxErrorType.JVX_ERROR_THREADING_MISMATCH: + retVal = 'JVX_ERROR_THREADING_MISMATCH'; + break; + case jvxErrorType.JVX_ERROR_SYSTEMCALL_FAILED: + retVal = 'JVX_ERROR_SYSTEMCALL_FAILED'; + break; + case jvxErrorType.JVX_ERROR_SIZE_MISMATCH: + retVal = 'JVX_ERROR_SIZE_MISMATCH'; + break; + case jvxErrorType.JVX_ERROR_NO_ACCESS: + retVal = 'JVX_ERROR_NO_ACCESS'; + break; + case jvxErrorType.JVX_ERROR_INVALID_SETTING: + retVal = 'JVX_ERROR_INVALID_SETTING'; + break; + case jvxErrorType.JVX_ERROR_TIMEOUT: + retVal = 'JVX_ERROR_TIMEOUT'; + break; + case jvxErrorType.JVX_ERROR_ABORT: + retVal = 'JVX_ERROR_ABORT'; + break; + case jvxErrorType.JVX_ERROR_POSTPONE: + retVal = 'JVX_ERROR_POSTPONE'; + break; + case jvxErrorType.JVX_ERROR_END_OF_FILE: + retVal = 'JVX_ERROR_END_OF_FILE'; + break; + case jvxErrorType.JVX_ERROR_STANDARD_EXCEPTION: + retVal = 'JVX_ERROR_STANDARD_EXCEPTION'; + break; + case jvxErrorType.JVX_ERROR_SEQUENCER_TOOK_CONTROL: + retVal = 'JVX_ERROR_SEQUENCER_TOOK_CONTROL'; + break; + case jvxErrorType.JVX_ERROR_THREAD_ONCE_MORE: + retVal = 'JVX_ERROR_THREAD_ONCE_MORE'; + break; + case jvxErrorType.JVX_ERROR_PROCESS_COMPLETE: + retVal = 'JVX_ERROR_PROCESS_COMPLETE'; + break; + case jvxErrorType.JVX_ERROR_DSP: + retVal = 'JVX_ERROR_DSP'; + break; + case jvxErrorType.JVX_ERROR_ALREADY_IN_USE: + retVal = 'JVX_ERROR_ALREADY_IN_USE'; + break; + case jvxErrorType.JVX_ERROR_PROTOCOL_ERROR: + retVal = 'JVX_ERROR_PROTOCOL_ERROR'; + break; + case jvxErrorType.JVX_ERROR_NOT_ENOUGH_INPUT_DATA: + retVal = 'JVX_ERROR_NOT_ENOUGH_INPUT_DATA'; + break; + case jvxErrorType.JVX_ERROR_NOT_ENOUGH_OUTPUT_SPACE: + retVal = 'JVX_ERROR_NOT_ENOUGH_OUTPUT_SPACE'; + break; + case jvxErrorType.JVX_ERROR_COMPROMISE: + retVal = 'JVX_ERROR_COMPROMISE'; + break; + case jvxErrorType.JVX_ERROR_REMOTE: + retVal = 'JVX_ERROR_REMOTE'; + break; + case jvxErrorType.JVX_ERROR_ALLOCATION: + retVal = 'JVX_ERROR_ALLOCATION'; + break; + case jvxErrorType.JVX_ERROR_INVALID_FORMAT: + retVal = 'JVX_ERROR_INVALID_FORMAT'; + break; + case jvxErrorType.JVX_ERROR_REQUEST_CALL_AGAIN: + retVal = 'JVX_ERROR_REQUEST_CALL_AGAIN'; + break; + case jvxErrorType.JVX_ERROR_NOT_IMPLEMENTED: + retVal = 'JVX_ERROR_NOT_IMPLEMENTED'; + break; + } + return retVal; + } +} +// ================================================================= + +enum jvxStateSwitchEnum { + JVX_STATE_SWITCH_NONE, + JVX_STATE_SWITCH_INIT, + JVX_STATE_SWITCH_SELECT, + JVX_STATE_SWITCH_ACTIVATE, + JVX_STATE_SWITCH_PREPARE, + JVX_STATE_SWITCH_START, + JVX_STATE_SWITCH_STOP, + JVX_STATE_SWITCH_POSTPROCESS, + JVX_STATE_SWITCH_DEACTIVATE, + JVX_STATE_SWITCH_UNSELECT, + JVX_STATE_SWITCH_TERMINATE +} + +extension jvxStateSwitchEEnum on jvxStateSwitchEnum { + String get txt => describeEnum(this); + static jvxStateSwitchEnum fromInt(int val) { + jvxStateSwitchEnum ss = jvxStateSwitchEnum.JVX_STATE_SWITCH_NONE; + if (val < jvxStateSwitchEnum.values.length) { + ss = jvxStateSwitchEnum.values[val]; + } + return ss; + } +} + +// ========================================================================== + +abstract class jvxPropertyDescriptorEnum { + static const int JVX_PROPERTY_DESCRIPTOR_NONE = 0; + static const int JVX_PROPERTY_DESCRIPTOR_MIN = 1; + static const int JVX_PROPERTY_DESCRIPTOR_CORE = 2; + static const int JVX_PROPERTY_DESCRIPTOR_CONTROL = 3; + static const int JVX_PROPERTY_DESCRIPTOR_FULL = 4; + static const int JVX_PROPERTY_DESCRIPTOR_FULL_PLUS = 5; +} + +// ========================================================================== + +enum ayfBackendAccessEnum { JVX_ACCESS_BACKEND_FFI, JVX_ACCESS_BACKEND_JSON } + +enum jvxDeviceCapabilityTypeShift { + JVX_DEVICE_CAPABILITY_UNKNOWN_SHIFT, + JVX_DEVICE_CAPABILITY_INPUT_SHIFT, + JVX_DEVICE_CAPABILITY_OUTPUT_SHIFT, + JVX_DEVICE_CAPABILITY_DUPLEX_SHIFT, + JVX_DEVICE_CAPABILITY_OTHER_SHIFT, + JVX_DEVICE_CAPABILITY_LIMIT_SHIFT +} + +// ========================================================================== + +enum jvxDeviceDataFlowTypeEnum { + JVX_DEVICE_DATAFLOW_UNKNOWN, + JVX_DEVICE_DATAFLOW_ACTIVE, + JVX_DEVICE_DATAFLOW_PASSIVE, + JVX_DEVICE_DATAFLOW_OFFLINE, + JVX_DEVICE_DATAFLOW_CONTROL, + JVX_DEVICE_DATAFLOW_LIMIT +} + +extension jvxDeviceDataFlowTypeEEnum on jvxDeviceDataFlowTypeEnum { + String get txt => describeEnum(this); + static jvxDeviceDataFlowTypeEnum fromInt(int val) { + jvxDeviceDataFlowTypeEnum ss = + jvxDeviceDataFlowTypeEnum.JVX_DEVICE_DATAFLOW_UNKNOWN; + if (val < jvxDeviceDataFlowTypeEnum.values.length) { + ss = jvxDeviceDataFlowTypeEnum.values[val]; + } + return ss; + } +} + +// ========================================================================== + +enum AyfPropertyReportLevel { + // Nothing is reported via notifyListeners + AYF_FRONTEND_REPORT_NO_REPORT, + + // Property ssUpdate is increased and reported via notifyListener + AYF_FRONTEND_REPORT_SINGLE_PROPERTY, + + // Property ssUpdate and component ssUpdate are increased and reported via notifyListener + AYF_FRONTEND_REPORT_COMPONENT_PROPERTY, + + // Report is via backend. This will inform all connected listeners + AYF_BACKEND_REPORT_COMPONENT_PROPERTY, + + // Same as AYF_BACKEND_REPORT_COMPONENT_PROPERTY but it collects all set operations + // and reports once ALL properties have been set - not after each property + AYF_BACKEND_REPORT_COMPONENT_PROPERTY_COLLECT +} + +/* +abstract class AyfFrontendReportLevelEnum { + static const int AYF_FRONTEND_REPORT_NOTHING = 0; + static const int AYF_FRONTEND_REPORT_SINGLE_PROPERTY = 0x1; + static const int AYF_FRONTEND_REPORT_PROPERTY_CACHE_COMPONENT = 0x2; +} +*/ + +enum AyfBackendReportPropertySetEnum { + AYF_BACKEND_REPORT_SET_PROPERTY_ACTIVE, + AYF_BACKEND_REPORT_SET_PROPERTY_INACTIVE +} diff --git a/flutter/ayf_pack/lib/constants/ayf_constants_audio.dart b/flutter/ayf_pack/lib/constants/ayf_constants_audio.dart new file mode 100644 index 00000000..4fde36e3 --- /dev/null +++ b/flutter/ayf_pack/lib/constants/ayf_constants_audio.dart @@ -0,0 +1,76 @@ +import 'package:flutter/foundation.dart'; + +enum JvxComponentTypeEnum { + // NOT AUDIO SPECIFIC + JVX_COMPONENT_UNKNOWN, + JVX_COMPONENT_SIGNAL_PROCESSING_TECHNOLOGY, + JVX_COMPONENT_SIGNAL_PROCESSING_DEVICE, + JVX_COMPONENT_SIGNAL_PROCESSING_NODE, + JVX_COMPONENT_AUDIO_TECHNOLOGY, + JVX_COMPONENT_AUDIO_DEVICE, + JVX_COMPONENT_AUDIO_NODE, + JVX_COMPONENT_VIDEO_TECHNOLOGY, + JVX_COMPONENT_VIDEO_DEVICE, + JVX_COMPONENT_VIDEO_NODE, + JVX_COMPONENT_APPLICATION_CONTROLLER_TECHNOLOGY, + JVX_COMPONENT_APPLICATION_CONTROLLER_DEVICE, + JVX_COMPONENT_APPLICATION_CONTROLLER_NODE, + JVX_COMPONENT_CUSTOM_TECHNOLOGY, + JVX_COMPONENT_CUSTOM_DEVICE, + JVX_COMPONENT_CUSTOM_NODE, + JVX_COMPONENT_EXTERNAL_TECHNOLOGY, + JVX_COMPONENT_EXTERNAL_DEVICE, + JVX_COMPONENT_EXTERNAL_NODE, + JVX_COMPONENT_DYNAMIC_NODE, + JVX_COMPONENT_PACKAGE, + JVX_COMPONENT_MIN_HOST, + JVX_COMPONENT_FACTORY_HOST, + JVX_COMPONENT_HOST, + JVX_COMPONENT_EVENTLOOP, + JVX_COMPONENT_DATALOGGER, + JVX_COMPONENT_DATALOGREADER, + JVX_COMPONENT_CONNECTION, + JVX_COMPONENT_CONFIG_PROCESSOR, + JVX_COMPONENT_EXTERNAL_CALL, + JVX_COMPONENT_THREADCONTROLLER, + JVX_COMPONENT_THREADS, + JVX_COMPONENT_SHARED_MEMORY, + JVX_COMPONENT_TEXT2SPEECH, + JVX_COMPONENT_SYSTEM_TEXT_LOG, + JVX_COMPONENT_LOCAL_TEXT_LOG, + JVX_COMPONENT_CRYPT, + JVX_COMPONENT_WEBSERVER, + JVX_COMPONENT_REMOTE_CALL, + JVX_COMPONENT_PACKETFILTER_RULE, + JVX_COMPONENT_LOGREMOTEHANDLER, + + // AUDIO SPECIFIC + JVX_COMPONENT_AUDIO_CODEC, + JVX_COMPONENT_AUDIO_ENCODER, + JVX_COMPONENT_AUDIO_DECODER, + JVX_COMPONENT_RT_AUDIO_FILE_READER, + JVX_COMPONENT_RT_AUDIO_FILE_WRITER, + JVX_COMPONENT_RESAMPLER, + JVX_COMPONENT_DATACONVERTER, + + // NOT AUDIO SPECIFIC + JVX_COMPONENT_PROCESSING_GROUP, + JVX_COMPONENT_PROCESSING_PROCESS, + JVX_COMPONENT_OFF_HOST, + JVX_COMPONENT_INTERCEPTOR, + JVX_COMPONENT_SYSTEM_AUTOMATION +} + +// Enum handling does not work with generic functions/methods +// https://stackoverflow.com/questions/59362403/dart-constrain-a-generic-type-t-to-an-enum + +extension JvxComponentTypeEEnum on JvxComponentTypeEnum { + String get txt => describeEnum(this); + static JvxComponentTypeEnum fromInt(int val) { + JvxComponentTypeEnum ss = JvxComponentTypeEnum.values[0]; + if (val < JvxComponentTypeEnum.values.length) { + ss = JvxComponentTypeEnum.values[val]; + } + return ss; + } +} diff --git a/flutter/ayf_pack/lib/constants/ayf_constants_properties.dart b/flutter/ayf_pack/lib/constants/ayf_constants_properties.dart new file mode 100644 index 00000000..b4be39d5 --- /dev/null +++ b/flutter/ayf_pack/lib/constants/ayf_constants_properties.dart @@ -0,0 +1,135 @@ +import 'package:flutter/foundation.dart'; + +// =========================================================== + +enum jvxPropertyAccessTypeEnum { + JVX_PROPERTY_ACCESS_NONE, + JVX_PROPERTY_ACCESS_FULL_READ_AND_WRITE, + JVX_PROPERTY_ACCESS_READ_AND_WRITE_CONTENT, + JVX_PROPERTY_ACCESS_READ_ONLY, + JVX_PROPERTY_ACCESS_READ_ONLY_ON_START, + JVX_PROPERTY_ACCESS_WRITE_ONLY +} + +extension jvxPropertyAccessTypeEEnum on jvxPropertyAccessTypeEnum { + static String get formatName => "jvxPropertyAccessType"; + String get txt => describeEnum(this); + static jvxPropertyAccessTypeEnum fromInt(int val) { + jvxPropertyAccessTypeEnum ss = + jvxPropertyAccessTypeEnum.JVX_PROPERTY_ACCESS_NONE; + if ((val >= 0) && (val < jvxPropertyAccessTypeEnum.values.length)) { + ss = jvxPropertyAccessTypeEnum.values[val]; + } + return ss; + } +} + +// =========================================================== + +enum jvxPropertyCategoryTypeEnum { + JVX_PROPERTY_CATEGORY_UNKNOWN, + JVX_PROPERTY_CATEGORY_PREDEFINED, + JVX_PROPERTY_CATEGORY_UNSPECIFIC +} + +extension jvxPropertyCategoryTypeEEnum on jvxPropertyCategoryTypeEnum { + static String get formatName => "jvxPropertyCategoryType"; + String get txt => describeEnum(this); + static jvxPropertyCategoryTypeEnum fromInt(int val) { + jvxPropertyCategoryTypeEnum ss = + jvxPropertyCategoryTypeEnum.JVX_PROPERTY_CATEGORY_UNKNOWN; + if ((val >= 0) && (val < jvxPropertyCategoryTypeEnum.values.length)) { + ss = jvxPropertyCategoryTypeEnum.values[val]; + } + return ss; + } +} + +// =========================================================== + +enum jvxPropertyInvalidateTypeEnum { + JVX_PROPERTY_INVALIDATE_INACTIVE, + JVX_PROPERTY_INVALIDATE_CONTENT, + JVX_PROPERTY_INVALIDATE_DESCRIPTOR_CONTENT, + JVX_PROPERTY_INVALIDATE_LIMIT +} + +extension jvxPropertyInvalidateTypeEEnum on jvxPropertyInvalidateTypeEnum { + static String get formatName => "jvxPropertyInvalidateType"; + String get txt => describeEnum(this); + static jvxPropertyInvalidateTypeEnum fromInt(int val) { + jvxPropertyInvalidateTypeEnum ss = + jvxPropertyInvalidateTypeEnum.JVX_PROPERTY_INVALIDATE_INACTIVE; + if ((val >= 0) && (val < jvxPropertyInvalidateTypeEnum.values.length)) { + ss = jvxPropertyInvalidateTypeEnum.values[val]; + } + return ss; + } +} + +// =========================================================== + +enum jvxPropertyDecoderHintTypeEnum { + JVX_PROPERTY_DECODER_NONE, + JVX_PROPERTY_DECODER_FILENAME_OPEN, + JVX_PROPERTY_DECODER_DIRECTORY_SELECT, + JVX_PROPERTY_DECODER_FILENAME_SAVE, + JVX_PROPERTY_DECODER_IP_ADDRESS, + JVX_PROPERTY_DECODER_PLOT_ARRAY, + JVX_PROPERTY_DECODER_BITFIELD, + JVX_PROPERTY_DECODER_SINGLE_SELECTION, + JVX_PROPERTY_DECODER_MULTI_SELECTION, + JVX_PROPERTY_DECODER_FORMAT_IDX, + JVX_PROPERTY_DECODER_PROGRESSBAR, + JVX_PROPERTY_DECODER_INPUT_FILE_LIST, + JVX_PROPERTY_DECODER_OUTPUT_FILE_LIST, + JVX_PROPERTY_DECODER_MULTI_SELECTION_CHANGE_ORDER, + JVX_PROPERTY_DECODER_MULTI_CHANNEL_CIRCULAR_BUFFER, + JVX_PROPERTY_DECODER_MULTI_CHANNEL_SWITCH_BUFFER, + JVX_PROPERTY_DECODER_COMMAND, + JVX_PROPERTY_DECODER_SIMPLE_ONOFF, + JVX_PROPERTY_DECODER_VALUE_OR_DONTCARE, + JVX_PROPERTY_DECODER_SUBFORMAT_IDX, + JVX_PROPERTY_DECODER_DATAFLOW_IDX, + JVX_PROPERTY_DECODER_ENUM_TYPE, + JVX_PROPERTY_DECODER_PROPERTY_EXTENDER_INTERFACE, + JVX_PROPERTY_DECODER_LOCAL_TEXT_LOG, +} + +extension jvxPropertyDecoderHintTypeEEnum on jvxPropertyDecoderHintTypeEnum { + static String get formatName => "jvxPropertyDecoderHintType"; + String get txt => describeEnum(this); + static jvxPropertyDecoderHintTypeEnum fromInt(int val) { + jvxPropertyDecoderHintTypeEnum ss = + jvxPropertyDecoderHintTypeEnum.JVX_PROPERTY_DECODER_NONE; + if ((val >= 0) && (val < jvxPropertyDecoderHintTypeEnum.values.length)) { + ss = jvxPropertyDecoderHintTypeEnum.values[val]; + } + return ss; + } +} + +// =========================================================== + +enum jvxPropertyContextEnum { + JVX_PROPERTY_CONTEXT_UNKNOWN, + JVX_PROPERTY_CONTEXT_PARAMETER, + JVX_PROPERTY_CONTEXT_INFO, + JVX_PROPERTY_CONTEXT_RESULT, + JVX_PROPERTY_CONTEXT_COMMAND, + JVX_PROPERTY_CONTEXT_VIEWBUFFER, + JVX_PROPERTY_CONTEXT_LIMIT +} + +extension jvxPropertyContexteEEnum on jvxPropertyContextEnum { + static String get formatName => "jvxPropertyContext"; + String get txt => describeEnum(this); + static jvxPropertyContextEnum fromInt(int val) { + jvxPropertyContextEnum ss = + jvxPropertyContextEnum.JVX_PROPERTY_CONTEXT_UNKNOWN; + if ((val >= 0) && (val < jvxPropertyContextEnum.values.length)) { + ss = jvxPropertyContextEnum.values[val]; + } + return ss; + } +} diff --git a/flutter/ayf_pack/lib/devices/ayf_devicelist_from_json.dart b/flutter/ayf_pack/lib/devices/ayf_devicelist_from_json.dart new file mode 100644 index 00000000..da07e164 --- /dev/null +++ b/flutter/ayf_pack/lib/devices/ayf_devicelist_from_json.dart @@ -0,0 +1,118 @@ +import 'package:fixnum/fixnum.dart' as fn; +import 'package:ayf_pack/ayf_pack.dart'; + +class AudYoFloDevicelistFromJson { + static int updateDeviceList( + Map jsonMap, + JvxComponentIdentification cpId, + AudYoFloBackendCacheBectrlIf theBeCache, + AudYoFloBackendTranslator trans) { + int retVal = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + String? errCodeExprPtr = getStringEntryValueMap(jsonMap, 'return_code'); + if (errCodeExprPtr != null) { + // Convert error code + String errCodeExpr = errCodeExprPtr; + retVal = jvxErrorTypeEInt.fromStringSingle(errCodeExpr); + } + if (retVal == jvxErrorType.JVX_NO_ERROR) { + var subSec = getMapValueList(jsonMap, 'devices'); + if (subSec != null) { + List devices = []; + int idx = 0; + for (var elmD in subSec) { + AudYoFloOneDeviceSelectionOption newDevice = + AudYoFloOneDeviceSelectionOption(); + + newDevice.optionIdx = idx; + idx++; + + String? tmp = extractStringFromJson(elmD, 'description'); + if (tmp != null) { + newDevice.description = tmp; + tmp = null; + } + + tmp = extractStringFromJson(elmD, 'descriptor'); + if (tmp != null) { + newDevice.descriptor = tmp; + tmp = null; + } + + newDevice.devIdent.cpTp = + JvxComponentTypeEEnum.fromInt(cpId.cpTp.index + 1); + newDevice.devIdent.slotid = cpId.slotid; + newDevice.devIdent.slotsubid = -1; + + tmp = extractStringFromJson(elmD, 'state'); + if (tmp != null) { + newDevice.state.fld = fn.Int32( + AudYoFloStringTranslator.translateEnumString(tmp, 'jvxState', trans.compileFlags())); + tmp = null; + } + + var subSubSec = getMapValueMap(elmD, 'device_caps'); + if (subSubSec != null) { + newDevice.selectable = false; + tmp = extractStringFromJson(subSubSec, 'sel'); + if (tmp != null) { + if (tmp == '*') { + newDevice.selectable = true; + } + tmp = null; + } + + newDevice.singleton = false; + tmp = extractStringFromJson(subSubSec, 'single'); + if (tmp != null) { + if (tmp == '*') { + newDevice.singleton = true; + } + tmp = null; + } + + newDevice.flow = + jvxDeviceDataFlowTypeEnum.JVX_DEVICE_DATAFLOW_UNKNOWN; + tmp = extractStringFromJson(subSubSec, 'flow_type'); + if (tmp != null) { + newDevice.flow = jvxDeviceDataFlowTypeEEnum.fromInt( + AudYoFloStringTranslator.translateEnumString( + tmp, 'jvxDeviceDataFlowType', trans.compileFlags())); + tmp = null; + } + + newDevice.caps.fld = fn.Int32(0); + tmp = extractStringFromJson(subSubSec, 'caps'); + if (tmp != null) { + newDevice.caps.fld = fn.Int32( + AudYoFloStringTranslator.translateEnumString( + tmp, 'jvxDeviceCapabilityType', trans.compileFlags())); + tmp = null; + } + + tmp = extractStringFromJson(subSubSec, 'flags'); + if (tmp != null) { + resultParseInt32 res = resultParseInt32(); + newDevice.flags.fld = fn.Int32(hexStringToFnInt32(res, tmp)); + tmp = null; + } + + var elmSSId = getMapValueList(elmD, 'slots'); + if (elmSSId != null) { + if (elmSSId.isNotEmpty) { + if (elmSSId[0] is int) { + // I can not remember any case in which a device is in differnt slots.. + // Need to keep this in mind!! + newDevice.devIdent.slotsubid = elmSSId[0]; + } + } + } + } + devices.add(newDevice); + } + // Update the list and notify all!! + theBeCache.updateDeviceListCacheCompleteNotify(cpId, devices); + } + } + return retVal; + } +} diff --git a/flutter/ayf_pack/lib/dialogs/ayf_settings.dart b/flutter/ayf_pack/lib/dialogs/ayf_settings.dart new file mode 100644 index 00000000..ee4b1ce9 --- /dev/null +++ b/flutter/ayf_pack/lib/dialogs/ayf_settings.dart @@ -0,0 +1,150 @@ +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import '../ayf_pack_local.dart'; + +// Audio settings dialog: only purpose is to start the dialog and to set the proper size +class AudYoFloSettingsDialog extends StatelessWidget { + @override + Widget build(BuildContext context) { + final Size size = MediaQuery.of(context).size; + return SimpleDialog( + insetPadding: EdgeInsets.zero, + titlePadding: EdgeInsets.zero, + contentPadding: EdgeInsets.zero, + children: [AudYoFloSettingsInnerTabs(size.width, size.height)]); + } +} + +// =========================================================================== + +class AudYoFloSettingsInnerTabs extends StatefulWidget { + final double width; + final double height; + AudYoFloSettingsInnerTabs(this.width, this.height); + + @override + State createState() { + return _AudYoFloSettingsInnerTabsState(); + } +} + +class _AudYoFloSettingsInnerTabsState extends State + with TickerProviderStateMixin { + late TabController tabController; + int idxTab = 0; + @override + void initState() { + // TODO: implement initState + super.initState(); + } + + @override + Widget build(BuildContext context) { + AudYoFloBackendCache theBeCache = + Provider.of(context, listen: false); + AudYoFloUiModel theUiModel = + Provider.of(context, listen: false); + + double innerWidth = widget.width * 0.9; + double innerHeight = widget.height * 0.9; + innerWidth = min(innerWidth, theUiModel.maxWidthSettings); + innerHeight = min(innerHeight, theUiModel.maxHeightSettings); + + return Consumer( + builder: (context, notifier, child) { + SettingsUiElementsTabBar elementTabs = SettingsUiElementsTabBar(); + elementTabs.createTabsAndViews(theUiModel); + + tabController = TabController( + initialIndex: idxTab, + length: elementTabs.theAllocatedTabViews.length, + vsync: this, + ); + return Container( + width: innerWidth, + height: innerHeight, + child: NotificationListener( + onNotification: (SizeChangedLayoutNotification notification) { + return true; + }, + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Container( + child: SingleChildScrollView( + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SizedBox( + width: tabController.length * + theUiModel + .navButtonAreaWidthSettings + + theUiModel + .navButtonAreaWidthSpaceSettings, + height: + theUiModel.navButtonAreaHeightSettings, + child: TabBar( + onTap: (value) { + idxTab = value; + }, + labelColor: Colors + .black, //Color.fromRGBO(4, 2, 46, 1), + labelStyle: TextStyle( + fontStyle: FontStyle.normal, + fontWeight: FontWeight.bold), + unselectedLabelStyle: TextStyle( + fontStyle: FontStyle.italic, + fontWeight: FontWeight.normal), + // labelStyle: theme.textTheme.headline1, + indicatorColor: + Color.fromRGBO(48, 28, 46, 10), + unselectedLabelColor: Colors.grey, + controller: tabController, + tabs: elementTabs.theAllocatedTabs, + )) + ]), + )), + Container( + height: innerHeight - + theUiModel.navButtonAreaHeightSettings, + child: TabBarView( + controller: tabController, + children: elementTabs.theAllocatedTabViews), + ) + ])))); + }, + ); + } +} + +// ========================================================================== + +class SettingsUiElementsTabBar { + List theAllocatedTabs = []; + List theAllocatedTabViews = []; + void createTabsAndViews(AudYoFloUiModel uimodel) { + theAllocatedTabs.clear(); + + for (var elm in uimodel.settingsConfigure.allSections.entries) { + if (elm.value.nameImage.isNotEmpty) { + theAllocatedTabs.add( + Column(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ + Image.asset(elm.value.nameImage, + filterQuality: FilterQuality.medium, + width: uimodel.navButtonSizeSettings, + height: uimodel.navButtonSizeSettings), + Text(elm.value.nameEntry) + ])); + } else { + theAllocatedTabs.add(Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [Text(elm.value.nameEntry)])); + } + + theAllocatedTabViews.add(AudYoFloSettingsPropertyList(elm.value)); + } + } +} diff --git a/flutter/ayf_pack/lib/helpers/ayf_component_property_groups.dart b/flutter/ayf_pack/lib/helpers/ayf_component_property_groups.dart new file mode 100644 index 00000000..3edff3cf --- /dev/null +++ b/flutter/ayf_pack/lib/helpers/ayf_component_property_groups.dart @@ -0,0 +1,122 @@ +import 'package:collection/collection.dart'; +import '../ayf_pack_local.dart'; + +/* One property defintion in group: valid descriptor, null prop ref at first */ +class AudYoFloPropertyOnChangeOneGroupOneProp { + final String propertyDescr; + AudYoFloPropertyContainer? prop; + AudYoFloPropertyOnChangeOneGroupOneProp(this.propertyDescr); +} + +/* One property group to combine multiples properties */ +class AudYoFloPropertyOnChangeOneGroup { + List propsInGroup = []; + AudYoFloPropertyOnChangeOneGroup(List props) { + for (var elm in props) { + AudYoFloPropertyOnChangeOneGroupOneProp oneProp = + AudYoFloPropertyOnChangeOneGroupOneProp(elm); + propsInGroup.add(oneProp); + } + } + + AudYoFloPropertySingleStringBackend? extractPropString(String tag) { + AudYoFloPropertySingleStringBackend? retVal; + + var elm = extractPropContent(tag); + if (elm != null) { + if (elm is AudYoFloPropertySingleStringBackend) { + retVal = elm; + } + } + return retVal; + } + + AudYoFloPropertyContainer? extractPropContent(String tag) { + AudYoFloPropertyContainer? retVal; + var elm = propsInGroup + .firstWhereOrNull((element) => element.propertyDescr == tag); + if (elm != null) { + retVal = elm.prop; + } + return retVal; + } + + AudYoFloPropertySelectionListBackend? extractPropSelectionList(String tag) { + AudYoFloPropertySelectionListBackend? retVal; + var elm = extractPropContent(tag); + if (elm != null) { + if (elm is AudYoFloPropertySelectionListBackend) { + retVal = elm as AudYoFloPropertySelectionListBackend; + } + } + return retVal; + } + + AudYoFloPropertyValueInRangeBackend? extractPropValueInRange(String tag) { + AudYoFloPropertyValueInRangeBackend? retVal; + var elm = extractPropContent(tag); + if (elm != null) { + if (elm is AudYoFloPropertyValueInRangeBackend) { + retVal = elm as AudYoFloPropertyValueInRangeBackend; + } + } + return retVal; + } + + AudYoFloPropertyMultiContentBackend? extractPropMultiContent(String tag) { + AudYoFloPropertyMultiContentBackend? retVal; + var elm = extractPropContent(tag); + if (elm != null) { + if (elm is AudYoFloPropertyMultiContentBackend) { + retVal = elm as AudYoFloPropertyMultiContentBackend; + } + } + return retVal; + } + + AudYoFloPropertyMultiStringBackend? extractPropMultiString(String tag) { + AudYoFloPropertyMultiStringBackend? retVal; + var elm = extractPropContent(tag); + if (elm != null) { + if (elm is AudYoFloPropertyMultiStringBackend) { + retVal = elm; + } + } + return retVal; + } +} + +/* Multiple groups combined **/ +class AudYoFloPropertyOnChangeGroups { + Map theGroups = {}; + List propsAllGroups = []; + + void createList() { + propsAllGroups.clear(); + for (var elmG in theGroups.entries) { + for (var elm in elmG.value.propsInGroup) { + var fElm = propsAllGroups.firstWhereOrNull((str) { + return (str == elm.propertyDescr); + }); + if (fElm == null) { + // Add element only if not in the list yet + propsAllGroups.add(elm.propertyDescr); + } + } + + // Here we have a groupt which contains only unique properties + } + } + + void reconstructProperyReferences(List props) { + for (var elmG in theGroups.entries) { + for (var elm in elmG.value.propsInGroup) { + // Property references may be used in multiple groups or multiple times in single group! + // It is always a REFERNCE! + AudYoFloPropertyContainer? propRef = props.firstWhereOrNull( + (element) => element.descriptor == elm.propertyDescr); + elm.prop = propRef; + } + } + } +} diff --git a/flutter/ayf_pack/lib/helpers/ayf_helper_lib.dart b/flutter/ayf_pack/lib/helpers/ayf_helper_lib.dart new file mode 100644 index 00000000..688c9d83 --- /dev/null +++ b/flutter/ayf_pack/lib/helpers/ayf_helper_lib.dart @@ -0,0 +1,537 @@ +import 'dart:math'; +import 'package:fixnum/fixnum.dart' as fn; +import 'package:collection/collection.dart'; +import 'package:flutter/foundation.dart'; +import '../ayf_pack_local.dart'; +import 'dart:typed_data'; + +int hexStringToFnInt32(resultParseInt32 result, String inStr) { + fn.Int32 ret = fn.Int32(0); + List lstStr = inStr.split('0x'); + if (lstStr.length == 2) { + if (lstStr[0].isEmpty) { + String str1 = lstStr[1]; + + int sz = str1.length; + while (sz > 0) { + String token = str1; + if (sz > 8) { + token = str1.substring(str1.length - 8, str1.length); + str1 = str1.substring(0, str1.length - 8); + } else { + token = str1; + str1 = ''; + } + ret = ret << 32; + int val = int.parse(token, radix: 16); + ret |= fn.Int64(val); + sz = str1.length; + } + result.val = ret; + return jvxErrorType.JVX_NO_ERROR; + } + } + result.error = true; + return jvxErrorType.JVX_ERROR_PARSE_ERROR; +} + +class resultParseInt64 { + fn.Int64 val = fn.Int64(0); + bool error = false; +} + +class resultParseInt32 { + fn.Int32 val = fn.Int32(0); + bool error = false; +} + +String? getStringEntryValueMap(Map valueMap, String nm) { + String? retVal; + MapEntry? entry = + valueMap.entries.firstWhereOrNull((element) => element.key == nm); + if (entry != null) { + if (entry.value is String) { + retVal = entry.value; + } + } + return retVal; +} + +int getIntEntryValueMap(Map valueMap, String nm) { + int retVal = -1; + MapEntry? entry = + valueMap.entries.firstWhereOrNull((element) => element.key == nm); + if (entry != null) { + if (entry.value is int) { + retVal = entry.value as int; + } else if (entry.value is double) { + double val = entry.value as double; + retVal = val.toInt(); + } + } + return retVal; +} + +List? getListValueMap(Map valueMap, String nm) { + List? retVal; + MapEntry? entry = + valueMap.entries.firstWhereOrNull((element) => element.key == nm); + if (entry != null) { + if (entry.value is List) { + retVal = entry.value; + } + } + return retVal; +} + +Map? getMapValueMap(Map valueMap, String nm) { + Map? retVal; + MapEntry? entry = + valueMap.entries.firstWhereOrNull((element) => element.key == nm); + if (entry != null) { + if (entry.value is Map) { + retVal = entry.value; + } + } + return retVal; +} + +List? getMapValueList(Map valueMap, String nm) { + List? retVal; + MapEntry? entry = + valueMap.entries.firstWhereOrNull((element) => element.key == nm); + if (entry != null) { + if (entry.value is List) { + retVal = entry.value; + } + } + return retVal; +} + +List str2PropList(String ident) { + List onRet = []; + List parts = ident.split('['); + if (parts.length == 2) { + parts = parts[1].split(']'); + if (parts.length == 2) { + onRet = parts[0].split(','); + } + } else { + onRet.add(parts[0]); + } + + return onRet; +} + +String intValueList2String(List lst) { + String txt = ''; + for (int val in lst) { + if (!txt.isEmpty) { + txt += ', '; + } + txt += val.toString(); + } + txt = '[' + txt + ']'; + return txt; +} + +void applyChangeSelectionList( + AudYoFloBitField selection, int posi, jvxPropertyDecoderHintTypeEnum decTp, + {int offset = 0}) { + switch (decTp) { + case jvxPropertyDecoderHintTypeEnum.JVX_PROPERTY_DECODER_MULTI_SELECTION: + case jvxPropertyDecoderHintTypeEnum + .JVX_PROPERTY_DECODER_MULTI_SELECTION_CHANGE_ORDER: + if (selection.bitTest(posi, offset: offset)) { + selection.bitClear(posi, offset: offset); + } else { + selection.bitSet(posi, offset: offset); + } + break; + default: + selection.bitZSet(posi, offset: offset); + } +} + +void incrementSelectionList(AudYoFloBitField selection, int numOptions, + jvxPropertyDecoderHintTypeEnum decTp, + {int offset = 0}) { + int curPosition = selection.firstSet(numOptions, offset: offset); + if (curPosition < 0) { + curPosition = 0; + } else { + curPosition++; + if (curPosition == numOptions) { + curPosition = 0; + } + } + selection.bitZSet(curPosition, offset: offset); +} + +// ======================================================================= + +enum ayfChannelTypeEnum { + JVX_GENERIC_WRAPPER_CHANNEL_TYPE_NONE, + JVX_GENERIC_WRAPPER_CHANNEL_TYPE_DEVICE, + JVX_GENERIC_WRAPPER_CHANNEL_TYPE_FILE, + JVX_GENERIC_WRAPPER_CHANNEL_TYPE_EXTERNAL, + JVX_GENERIC_WRAPPER_CHANNEL_TYPE_DUMMY +} + +class AudYoFloChannelTypeClass { + ayfChannelTypeEnum classtype = + ayfChannelTypeEnum.JVX_GENERIC_WRAPPER_CHANNEL_TYPE_NONE; + String name = 'Unknwon channel name'; + int idHwList = -1; + + void reset(int id) { + classtype = ayfChannelTypeEnum.JVX_GENERIC_WRAPPER_CHANNEL_TYPE_NONE; + name = 'Unknown channel name'; + idHwList = id; + } +} + +void decodeInputOutputChannel( + String description, AudYoFloChannelTypeClass clsOnReturn, int idLst) { + clsOnReturn.reset(idLst); + var tokens = description.split('::'); + clsOnReturn.name = description; + + if (tokens.length == 3) { + // Channel name in correct format! + switch (tokens[0]) { + case 'hw': + clsOnReturn.classtype = + ayfChannelTypeEnum.JVX_GENERIC_WRAPPER_CHANNEL_TYPE_DEVICE; + break; + case 'f': + clsOnReturn.classtype = + ayfChannelTypeEnum.JVX_GENERIC_WRAPPER_CHANNEL_TYPE_FILE; + break; + case 'e': + clsOnReturn.classtype = + ayfChannelTypeEnum.JVX_GENERIC_WRAPPER_CHANNEL_TYPE_EXTERNAL; + break; + case 'd': + clsOnReturn.classtype = + ayfChannelTypeEnum.JVX_GENERIC_WRAPPER_CHANNEL_TYPE_DUMMY; + break; + } + + clsOnReturn.idHwList = int.parse(tokens[1]); + clsOnReturn.name = tokens[2]; + } +} + +// ======================================================================== + +class AudYoFloDeviceChannel { + bool complies = false; + String deviceName = 'Unknown device'; + AudYoFloChannelTypeClass channelName = AudYoFloChannelTypeClass(); + + void reset(int idLst, String token) { + deviceName = ''; + channelName.reset(idLst); + complies = false; + } +} + +void decodeDeviceChannel( + int idLst, String token, AudYoFloDeviceChannel retVal) { + retVal.reset(idLst, token); + var lst = token.split('--'); + if (lst.length == 2) { + retVal.deviceName = lst[0]; + retVal.complies = true; + + decodeInputOutputChannel(lst[1], retVal.channelName, idLst); + int idx = retVal.deviceName.indexOf(RegExp(r'<*>')); + if (idx != -1) { + retVal.deviceName = + retVal.deviceName.substring(1, retVal.deviceName.length - 1); + } + } else { + retVal.channelName.name = token; + retVal.complies = false; + } +} + +String limitString(String txt, int numCharsMax) { + String retVal = txt; + int numCharsUse = numCharsMax - 4; + numCharsUse = max(2, numCharsUse); + int numCharsStart = numCharsUse ~/ 2; + int numCharsStop = numCharsUse - numCharsStart; + if (txt.length > numCharsMax) { + retVal = txt.substring(0, numCharsStart - 1) + + "...." + + txt.substring(txt.length - numCharsStop - 1); + } + + return retVal; +} + +AudYoFloPropertyContentBackend? pList2Elm( + List lst, String descr) { + return lst.firstWhereOrNull((element) { + if (element != null) { + if (element.descriptor == descr) { + return true; + } + } + return false; + }); +} + +bool setSinglePropertyValueMulti( + AudYoFloPropertyContainer mc, int posi, T val) { + bool retVal = false; + if (mc is AudYoFloPropertyMultiContentBackend) { + AudYoFloPropertyMultiContentBackend pp = + mc as AudYoFloPropertyMultiContentBackend; + if (pp.fld != null) { + if (posi < pp.fldSz) { + if (val is double) { + pp.fld![posi] = val; + retVal = true; + } + } + } + } else if (mc is AudYoFloPropertyMultiContentBackend) { + AudYoFloPropertyMultiContentBackend pp = + mc as AudYoFloPropertyMultiContentBackend; + if (pp.fld != null) { + if (posi < pp.fldSz) { + if (val is double) { + pp.fld![posi] = val; + retVal = true; + } + } + } + } + return retVal; +} + +// Return a value from a property list (call by reference, +// check the AudYoFloValRef type) +bool getSinglePropertyValueMulti( + AudYoFloPropertyContainer mc, int posi, AudYoFloCallByReference val) { + bool retVal = false; + if (mc is AudYoFloPropertyMultiContentBackend) { + AudYoFloPropertyMultiContentBackend pp = + mc as AudYoFloPropertyMultiContentBackend; + if (pp.fld != null) { + if (posi < pp.fldSz) { + if (val is AudYoFloCallByReference) { + AudYoFloCallByReference tVal = + val as AudYoFloCallByReference; + tVal.value = pp.fld![posi]; + retVal = true; + } + } + } + } else if (mc is AudYoFloPropertyMultiContentBackend) { + AudYoFloPropertyMultiContentBackend pp = + mc as AudYoFloPropertyMultiContentBackend; + if (pp.fld != null) { + if (posi < pp.fldSz) { + if (val is AudYoFloCallByReference) { + AudYoFloCallByReference tVal = + val as AudYoFloCallByReference; + tVal.value = pp.fld![posi]; + retVal = true; + } + } + } + } + return retVal; +} + +bool propertyValueMultiFromString( + AudYoFloPropertyMultiContentBackend prop, String txt, + {int offset = 0, int num = -1}) { + bool suc = false; + int idx = 0; + List lstEntries = str2PropList(txt); + int cntMax = lstEntries.length; + cntMax = min(cntMax, prop.fldSz - offset); + + if (num > 0) { + cntMax = min(cntMax, num); + } + + if (prop is AudYoFloPropertyMultiContentBackend) { + AudYoFloPropertyMultiContentBackend propM = prop; + if (propM.fld != null) { + for (idx = 0; idx < cntMax; idx++) { + double val = double.parse(lstEntries[idx]); + propM.fld![idx] = val; + suc = true; + } + } + } else if (prop is AudYoFloPropertyMultiContentBackend) { + AudYoFloPropertyMultiContentBackend propM = prop; + if (propM.fld != null) { + for (idx = 0; idx < cntMax; idx++) { + double val = double.parse(lstEntries[idx]); + propM.fld![idx] = val; + suc = true; + } + } + + // ================================================================ + } else if (prop is AudYoFloPropertyMultiContentBackend) { + AudYoFloPropertyMultiContentBackend propM = prop; + if (propM.fld != null) { + for (idx = 0; idx < cntMax; idx++) { + int val = int.parse(lstEntries[idx]); + propM.fld![idx] = val; + suc = true; + } + } + } else if (prop is AudYoFloPropertyMultiContentBackend) { + AudYoFloPropertyMultiContentBackend propM = prop; + if (propM.fld != null) { + for (idx = 0; idx < cntMax; idx++) { + int val = int.parse(lstEntries[idx]); + propM.fld![idx] = val; + suc = true; + } + } + } else if (prop is AudYoFloPropertyMultiContentBackend) { + AudYoFloPropertyMultiContentBackend propM = prop; + if (propM.fld != null) { + for (idx = 0; idx < cntMax; idx++) { + int val = int.parse(lstEntries[idx]); + propM.fld![idx] = val; + suc = true; + } + } + } else if (prop is AudYoFloPropertyMultiContentBackend) { + AudYoFloPropertyMultiContentBackend propM = prop; + if (propM.fld != null) { + for (idx = 0; idx < cntMax; idx++) { + int val = int.parse(lstEntries[idx]); + propM.fld![idx] = val; + suc = true; + } + } + + // ======================================================================= + } else if (prop is AudYoFloPropertyMultiContentBackend) { + AudYoFloPropertyMultiContentBackend propM = prop; + if (propM.fld != null) { + for (idx = 0; idx < cntMax; idx++) { + int val = int.parse(lstEntries[idx]); + propM.fld![idx] = val; + suc = true; + } + } + } else if (prop is AudYoFloPropertyMultiContentBackend) { + AudYoFloPropertyMultiContentBackend propM = prop; + if (propM.fld != null) { + for (idx = 0; idx < cntMax; idx++) { + int val = int.parse(lstEntries[idx]); + propM.fld![idx] = val; + suc = true; + } + } + } else if (prop is AudYoFloPropertyMultiContentBackend) { + AudYoFloPropertyMultiContentBackend propM = prop; + if (propM.fld != null) { + for (idx = 0; idx < cntMax; idx++) { + int val = int.parse(lstEntries[idx]); + propM.fld![idx] = val; + suc = true; + } + } + } else if (prop is AudYoFloPropertyMultiContentBackend) { + AudYoFloPropertyMultiContentBackend propM = prop; + if (propM.fld != null) { + for (idx = 0; idx < cntMax; idx++) { + int val = int.parse(lstEntries[idx]); + propM.fld![idx] = val; + suc = true; + } + } + } + return suc; +} + +String? extractStringFromJson(Map jsonMap, String nm) { + String? retStrPtr = getStringEntryValueMap(jsonMap, nm); + return retStrPtr; +} + +Future?> + checkAndUpdateComponentOptions( + AudYoFloBackendCache be, JvxComponentIdentification tp) async { + List? lst; + + // Two attempts + for (int idx = 0; idx < 2; idx++) { + // This call checks that the technology has been registered. + // If you are not allowed to call this function the call throws + // an exeption with the error reason + try { + lst = be.referenceComponentListInCache(tp); + } on AudYoFloException catch (e) { + // This exception may occur if the technology has been unselected in the + // backend but the info about this has not yet been forwarded to the frontend. + // In this case the list of devices is returned as empty. + String txt = jvxErrorTypeEInt.toStringSingle(e.errCode); + String cp = tp.txt; + print( + 'Error reported when trying to get reference to the devices list for technology $cp: <$txt>'); + + // Return an empty list + return lst; + } + + // the next call may be used without check safely + if (lst == null) { + int errCode = await be.triggerUpdateComponentList(tp); + if (errCode != jvxErrorType.JVX_NO_ERROR) { + assert(false); + } + } + } + + return lst; +} + +void dbgPrint(String output, {String intro = '', int stackTraceLevel = 1}) { + String strace = ''; + String introtoken = ''; + + var dbgPosi = StackTrace.current.toString(); + List entries = dbgPosi.split('\n'); + if (stackTraceLevel < entries.length) { + strace = entries[stackTraceLevel]; + } + + if (strace.isNotEmpty && intro.isNotEmpty) { + introtoken = '$strace -- $intro: '; + } else { + if (intro.isNotEmpty) { + introtoken = '$intro: '; + } else { + introtoken = '$strace -- '; + } + } + debugPrint('$introtoken $output'); +} + +JvxComponentIdentification widgetConfigCpId( + Map entriesCfgWidgets, + JvxComponentIdentification defCp, + String token) { + var elmBinW = entriesCfgWidgets.entries + .firstWhereOrNull((element) => element.key == token); + if (elmBinW != null) { + defCp = AudYoFloStringTranslator.componentIdentificationFromString( + elmBinW.value); + } + return defCp; +} diff --git a/flutter/ayf_pack/lib/helpers/ayf_multi_components_property_on_change.dart b/flutter/ayf_pack/lib/helpers/ayf_multi_components_property_on_change.dart new file mode 100644 index 00000000..2dabec19 --- /dev/null +++ b/flutter/ayf_pack/lib/helpers/ayf_multi_components_property_on_change.dart @@ -0,0 +1,183 @@ +import 'package:flutter/material.dart'; +import 'package:collection/collection.dart'; +import 'package:provider/provider.dart'; +import '../ayf_pack_local.dart'; + +class AudYoFloMultiComponentsPropertyOnChangeEntry { + AudYoFloOneComponentSettings settings; + AudYoFloMultiComponentsPropertyOnChangeEntry(this.settings); +} + +class AudYoFloMultiComponentsPropertyOnChangeEntryBackend + extends AudYoFloMultiComponentsPropertyOnChangeEntry { + AudYoFloMultiComponentsPropertyOnChangeEntryBackend( + AudYoFloOneComponentSettings settings, this.cpId) + : super(settings); + JvxComponentIdentification cpId; + List? bprops; +} + +class AudYoFloMultiComponentsPropertyOnChangeEntryLocal + extends AudYoFloMultiComponentsPropertyOnChangeEntry { + AudYoFloMultiComponentsPropertyOnChangeEntryLocal( + AudYoFloOneComponentSettings settings) + : super(settings); + + List? lprops; +} + +abstract class AudYoFloMultiComponentsPropertyOnChange { + // ================================================ + // Member variables to configure behavior + // ================================================ + + // Single property descriptor to "listen" for in selector to update + // build. If this tag is not set, the component is observed with not specific + // property. + // String? propDescriptorUpdate; + + // Specify the component to which the properties belongs that are handled in + // this class. + List cpsId = []; + bool configured = false; + + // ================================================= + // Abstract callbacks + Widget innerWidget(BuildContext context); + AudYoFloOneComponentSettings? requestedPropertiesList( + JvxComponentIdentification cpId); + // ================================================= + + List ids = []; + int latestValue = -1; + + // ================================================= + + List latestResult = []; + + void setComponentConsider(JvxComponentIdentification cpId) { + configured = true; + JvxComponentIdentification? elmInList = + cpsId.firstWhereOrNull((element) => element == cpId); + if (elmInList == null) { + cpsId.add(cpId); + ids.add(-1); + } + } + + void resetMultiPropertySettings() { + configured = false; + ids = []; + latestValue = -1; + } + + // ================================================= + // Basic functionality + // ================================================ + Widget localBuild(BuildContext context) { + // Get link to backend cache + AudYoFloBackendCache theBeCache = + Provider.of(context, listen: false); + + // Here we start the selector & future builder code + return Selector( + selector: (context, beCache) { + // if property is not there, return a -1 + + int cnt = 0; + + // Check here if any of the components has seen an update + // There is no option to listen to a single component or + // a dedicated property - I do not think this is necessary + // since the invalidated properties are in the cash and only + // those require an update + for (var elm in cpsId) { + int retVal = beCache.updateIdComponentInCache(elm); + if (retVal != ids[cnt]) { + latestValue++; + } + ids[cnt] = retVal; + cnt++; + } + + // Return the current id + return latestValue; + }, + + // Return true if the id is a different one than it was before + shouldRebuild: (previous, next) => (next != previous) ? true : false, + + // The builder + builder: (context, notifier, child) { + Future triggerPropertyLists() async { + int errCode = jvxErrorType.JVX_NO_ERROR; + + latestResult.clear(); + List> waitFutures = []; + List + waitElements = []; + + for (var elm in cpsId) { + AudYoFloOneComponentSettings? propList = + requestedPropertiesList(elm); + + // Skip this component if there is no property selected + if (propList != null) { + AudYoFloOneComponentSettings pList = propList; + + if (pList is AudYoFloOneComponentSettingsLocal) { + AudYoFloMultiComponentsPropertyOnChangeEntryLocal nextProp = + AudYoFloMultiComponentsPropertyOnChangeEntryLocal(pList); + nextProp.lprops = pList.directProps; + + // Use this as it is + latestResult.add(nextProp); + } else { + if (pList is AudYoFloOneComponentSettingsBackend) { + AudYoFloOneComponentSettingsBackend pListBackend = pList; + AudYoFloMultiComponentsPropertyOnChangeEntryBackend + nextProp = + AudYoFloMultiComponentsPropertyOnChangeEntryBackend( + pList, elm); + nextProp.bprops = + theBeCache.referenceValidPropertiesComponents( + nextProp.cpId, pListBackend.propsDescriptors); + if (nextProp.bprops == null) { + waitFutures.add( + theBeCache.triggerUpdatePropertiesComponent( + nextProp.cpId, pListBackend.propsDescriptors)); + waitElements.add(nextProp); + } + latestResult.add(nextProp); + } + } + } + } + + if (waitFutures.isNotEmpty) { + int cnt = 0; + List retVals = await Future.wait(waitFutures); + for (var elmR in retVals) { + if (errCode == jvxErrorType.JVX_NO_ERROR) { + AudYoFloOneComponentSettingsBackend pListBackend = + waitElements[cnt].settings + as AudYoFloOneComponentSettingsBackend; + waitElements[cnt].bprops = + theBeCache.referenceValidPropertiesComponents( + waitElements[cnt].cpId, + pListBackend.propsDescriptors); + } + } + } + + return errCode; + } + + return FutureBuilder( + future: triggerPropertyLists(), + builder: (BuildContext context, AsyncSnapshot snapshot) { + return innerWidget(context); + }); + }); + } +} diff --git a/flutter/ayf_pack/lib/helpers/ayf_one_component_property_on_change.dart b/flutter/ayf_pack/lib/helpers/ayf_one_component_property_on_change.dart new file mode 100644 index 00000000..6010af00 --- /dev/null +++ b/flutter/ayf_pack/lib/helpers/ayf_one_component_property_on_change.dart @@ -0,0 +1,140 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import '../ayf_pack_local.dart'; + +abstract class AudYoFloOneComponentPropertyOnChange { + // ================================================ + // Member variables to configure behavior + // ================================================ + + // Single property descriptor to "listen" for in selector to update + // build. If this tag is not set, the component is observed with not specific + // property. + String? propDescriptorUpdate; + + bool dbgOutput = false; + + // Specify the component to which the properties belongs that are handled in + // this class. + JvxComponentIdentification cpId = JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_UNKNOWN, + slotid: 0, + slotsubid: 0); + + // ================================================= + // Abstract callbacks + Widget innerWidget(BuildContext context); + List requestedPropertiesList(); + void reconstructProperties(List props); + // ================================================= + + // ================================================= + // Basic functionality + // ================================================ + Widget localBuild(BuildContext context) { + // Get link to backend cache + AudYoFloBackendCache theBeCache = + Provider.of(context, listen: false); + + // Here we start the selector & future builder code + return Selector(selector: (context, beCache) { + // if property is not there, return a -1 + int retVal = -1; + + // Either check for a specific property OR the component + // Note that the call to triggerPropertySet call must be invoked + // accordingly: + // report: AyfBackendReportLevelEnum.AYF_BACKEND_REPORT_SINGLE_PROPERTY + // to report update of a single property, + // report: AyfBackendReportLevelEnum.AYF_BACKEND_REPORT_PROPERTY_CACHE_COMPONENT + // to report update of a component + if (propDescriptorUpdate != null) { + if (dbgOutput) { + String txt = cpId.txt; + print( + 'Running Provider Selector on component $txt - property $propDescriptorUpdate.'); + } + + retVal = beCache.updateIdPropertyComponentInCache( + cpId, propDescriptorUpdate!); + } else { + // We are waiting for an update of the properties + if (dbgOutput) { + String txt = cpId.txt; + print('Running Provider Selector on component $txt.'); + } + retVal = beCache.updateIdComponentInCache(cpId); + } + + if (dbgOutput) { + print('Update selector update id $retVal.'); + } + // Return the current id + return retVal; + }, + + // Return true if the id is a different one than it was before + shouldRebuild: (previous, next) { + if (next != previous) { + if (dbgOutput) { + print('Should rebuild is positive.'); + } + return true; + } + if (dbgOutput) { + print('Should rebuild is negative.'); + } + return false; + }, + + // The builder + builder: (context, notifier, child) { + Future triggerPropertyLists(JvxComponentIdentification cpTp) async { + int errCode = jvxErrorType.JVX_NO_ERROR; + List? props; + List lst = requestedPropertiesList(); + + if (dbgOutput) { + print('Property update on properties:'); + for (var elms in lst) { + print('- $elms'); + } + } + + props = theBeCache.referenceValidPropertiesComponents(cpTp, lst); + if (props == null) { + if (dbgOutput) { + print('Not all properties valid, running update routine.'); + } + + // No need to invoke a listener - we wait for the operation to complete + errCode = await theBeCache.triggerUpdatePropertiesComponent(cpTp, lst, + report: AyfPropertyReportLevel.AYF_FRONTEND_REPORT_NO_REPORT); + if (errCode == jvxErrorType.JVX_NO_ERROR) { + props = theBeCache.referenceValidPropertiesComponents(cpTp, lst); + } else { + cpId.reset(); + } + } + + //inChannels = null; + //outChannels = null; + if (props != null) { + reconstructProperties(props); + } + + return errCode; + } + + return FutureBuilder( + future: triggerPropertyLists(cpId), + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (dbgOutput) { + print('Running function .'); + } + + return innerWidget(context); + }); + }); + } +} diff --git a/flutter/ayf_pack/lib/helpers/ayf_one_technology_devices_on_change.dart b/flutter/ayf_pack/lib/helpers/ayf_one_technology_devices_on_change.dart new file mode 100644 index 00000000..86a40b5f --- /dev/null +++ b/flutter/ayf_pack/lib/helpers/ayf_one_technology_devices_on_change.dart @@ -0,0 +1,112 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import '../ayf_pack_local.dart'; + +abstract class AudYoFloOneTechnologyDevicesOnChange { + // ================================================ + // Member variables to configure behavior + // ================================================ + + bool dbgOutput = false; + + // Specify the component to which the properties belongs that are handled in + // this class. + JvxComponentIdentification cpId = JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_UNKNOWN, + slotid: 0, + slotsubid: 0); + + // ================================================= + // Abstract callbacks + Widget innerWidget(BuildContext context); + void updateDevices(List devs); + // ================================================= + + // ================================================= + // Basic functionality + // ================================================ + Widget localBuild(BuildContext context) { + // Get link to backend cache + AudYoFloBackendCache theBeCache = + Provider.of(context, listen: false); + + // Here we start the selector & future builder code + return Selector(selector: (context, beCache) { + // if property is not there, return a -1 + int retVal = -1; + + // We are waiting for an update of the properties + if (dbgOutput) { + String txt = cpId.txt; + print('Running Provider Selector on component $txt.'); + } + + retVal = beCache.updateIdDeviceListInCache(cpId); + if (dbgOutput) { + print('Update selector update id $retVal.'); + } + // Return the current id + return retVal; + }, + // Return true if the id is a different one than it was before + shouldRebuild: (previous, next) { + if (next != previous) { + if (dbgOutput) { + print('Should rebuild is positive.'); + } + return true; + } + if (dbgOutput) { + print('Should rebuild is negative.'); + } + return false; + }, + + // The builder + builder: (context, notifier, child) { + Future triggerDevicesLists(JvxComponentIdentification cpTp) async { + int errCode = jvxErrorType.JVX_NO_ERROR; + List? devs; + + devs = theBeCache.referenceDeviceListInCache(cpTp); + if (devs == null) { + if (dbgOutput) { + print('Devices not valid, running update routine.'); + } + + // No need to invoke a listener - we wait for the operation to complete + errCode = await theBeCache.triggerUpdateDeviceList(cpTp); + if (errCode == jvxErrorType.JVX_NO_ERROR) { + devs = theBeCache.referenceDeviceListInCache(cpTp); + } else { + if (errCode == jvxErrorType.JVX_ERROR_NOT_READY) { + dbgPrint('Backend not ready yet, coming back later!'); + } else { + dbgPrint( + 'Backend returns error, deactivating widget main function!'); + cpId.reset(); + } + } + } + + //inChannels = null; + //outChannels = null; + if (devs != null) { + updateDevices(devs); + } + + return errCode; + } + + return FutureBuilder( + future: triggerDevicesLists(cpId), + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (dbgOutput) { + print('Running function .'); + } + + return innerWidget(context); + }); + }); + } +} diff --git a/flutter/ayf_pack/lib/helpers/ayf_translate_from_string.dart b/flutter/ayf_pack/lib/helpers/ayf_translate_from_string.dart new file mode 100644 index 00000000..7ee3f513 --- /dev/null +++ b/flutter/ayf_pack/lib/helpers/ayf_translate_from_string.dart @@ -0,0 +1,900 @@ +import '../ayf_pack_local.dart'; + +class AudYoFloStringTranslator { + static int translateEnumString( + String selection, String formatName, AudYoFloCompileFlags flags) { + int retVal = -1; + if (formatName == "jvxDataFormat") { + retVal = jvxDataFormatEnum.JVX_DATAFORMAT_NONE.index; + String tp = 'double'; + if (flags != null) { + if (flags.compileDataTypeSpec == + jvxDataTypeSpecEnum.JVX_DATA_TYPE_SPEC_FLOAT) { + tp = 'single'; + } + } + tp = 'jvxData' + '(' + tp + ')'; + if (selection == tp) { + retVal = jvxDataFormatEnum.JVX_DATAFORMAT_DATA.index; + } else { + switch (selection) { + case 'none': + retVal = jvxDataFormatEnum.JVX_DATAFORMAT_NONE.index; + break; + case 'jvxData': + retVal = jvxDataFormatEnum.JVX_DATAFORMAT_DATA.index; + break; + + case '16 Bit le': + retVal = jvxDataFormatEnum.JVX_DATAFORMAT_16BIT_LE.index; + break; + + case '32 Bit le': + retVal = jvxDataFormatEnum.JVX_DATAFORMAT_32BIT_LE.index; + break; + + case '64 Bit le': + retVal = jvxDataFormatEnum.JVX_DATAFORMAT_64BIT_LE.index; + break; + + case '8 Bit': + retVal = jvxDataFormatEnum.JVX_DATAFORMAT_8BIT.index; + break; + + case 'size': + retVal = jvxDataFormatEnum.JVX_DATAFORMAT_SIZE.index; + break; + + case 'string': + retVal = jvxDataFormatEnum.JVX_DATAFORMAT_STRING.index; + break; + + case 'string list': + retVal = jvxDataFormatEnum.JVX_DATAFORMAT_STRING_LIST.index; + break; + + case 'selection list': + retVal = jvxDataFormatEnum.JVX_DATAFORMAT_SELECTION_LIST.index; + break; + + case 'value in range': + retVal = jvxDataFormatEnum.JVX_DATAFORMAT_VALUE_IN_RANGE.index; + break; + + case 'u16 Bit le': + retVal = jvxDataFormatEnum.JVX_DATAFORMAT_U16BIT_LE.index; + break; + + case 'u32 Bit le': + retVal = jvxDataFormatEnum.JVX_DATAFORMAT_U32BIT_LE.index; + break; + + case 'u64 Bit le': + retVal = jvxDataFormatEnum.JVX_DATAFORMAT_U64BIT_LE.index; + break; + + case 'u8 Bit': + retVal = jvxDataFormatEnum.JVX_DATAFORMAT_U8BIT.index; + break; + + case 'float': + retVal = jvxDataFormatEnum.JVX_DATAFORMAT_FLOAT.index; + break; + + case 'double': + retVal = jvxDataFormatEnum.JVX_DATAFORMAT_DOUBLE.index; + break; + + case 'byte': + retVal = jvxDataFormatEnum.JVX_DATAFORMAT_BYTE.index; + break; + + case 'handle': + retVal = jvxDataFormatEnum.JVX_DATAFORMAT_HANDLE.index; + break; + + case 'pointer': + retVal = jvxDataFormatEnum.JVX_DATAFORMAT_POINTER.index; + break; + + case 'callback': + retVal = jvxDataFormatEnum.JVX_DATAFORMAT_CALLBACK.index; + break; + + case 'interface': + retVal = jvxDataFormatEnum.JVX_DATAFORMAT_INTERFACE.index; + break; + + default: + print( + 'Warning: For datatype <$formatName>, a selection value of <$selection> is not handled!'); + + break; + } + } + } else if (formatName == "jvxPropertyCategoryType") { + retVal = jvxPropertyCategoryType.JVX_PROPERTY_CATEGORY_UNKNOWN; + switch (selection) { + case 'unknown': + retVal = jvxPropertyCategoryType.JVX_PROPERTY_CATEGORY_UNKNOWN; + break; + case 'predefined': + retVal = jvxPropertyCategoryType.JVX_PROPERTY_CATEGORY_PREDEFINED; + break; + case 'unspecific': + retVal = jvxPropertyCategoryType.JVX_PROPERTY_CATEGORY_UNSPECIFIC; + break; + default: + print( + 'Warning: For datatype <$formatName>, a selection value of <$selection> is not handled!'); + + break; + } + } else if (formatName == 'jvxPropertyDecoderHintType') { + { + retVal = jvxPropertyDecoderHintTypeEnum.JVX_PROPERTY_DECODER_NONE.index; + switch (selection) { + case 'none': + retVal = + jvxPropertyDecoderHintTypeEnum.JVX_PROPERTY_DECODER_NONE.index; + break; + case 'file name (open)': + retVal = jvxPropertyDecoderHintTypeEnum + .JVX_PROPERTY_DECODER_FILENAME_OPEN.index; + break; + + case 'directory (select)': + retVal = jvxPropertyDecoderHintTypeEnum + .JVX_PROPERTY_DECODER_DIRECTORY_SELECT.index; + break; + + case 'file name (save)': + retVal = jvxPropertyDecoderHintTypeEnum + .JVX_PROPERTY_DECODER_FILENAME_SAVE.index; + break; + + case 'ip address': + retVal = jvxPropertyDecoderHintTypeEnum + .JVX_PROPERTY_DECODER_IP_ADDRESS.index; + break; + + case 'plot array': + retVal = jvxPropertyDecoderHintTypeEnum + .JVX_PROPERTY_DECODER_PLOT_ARRAY.index; + break; + + case 'bit field': + retVal = jvxPropertyDecoderHintTypeEnum + .JVX_PROPERTY_DECODER_BITFIELD.index; + break; + + case 'single selection': + retVal = jvxPropertyDecoderHintTypeEnum + .JVX_PROPERTY_DECODER_SINGLE_SELECTION.index; + break; + + case 'multi selection': + retVal = jvxPropertyDecoderHintTypeEnum + .JVX_PROPERTY_DECODER_MULTI_SELECTION.index; + break; + + case 'format (index)': + retVal = jvxPropertyDecoderHintTypeEnum + .JVX_PROPERTY_DECODER_FORMAT_IDX.index; + break; + + case 'progress bar': + retVal = jvxPropertyDecoderHintTypeEnum + .JVX_PROPERTY_DECODER_PROGRESSBAR.index; + break; + + case 'input file list': + retVal = jvxPropertyDecoderHintTypeEnum + .JVX_PROPERTY_DECODER_INPUT_FILE_LIST.index; + break; + + case 'output file list': + retVal = jvxPropertyDecoderHintTypeEnum + .JVX_PROPERTY_DECODER_OUTPUT_FILE_LIST.index; + break; + + case 'multi selection change order': + retVal = jvxPropertyDecoderHintTypeEnum + .JVX_PROPERTY_DECODER_MULTI_SELECTION_CHANGE_ORDER.index; + break; + + case 'multi channel circ buffer': + retVal = jvxPropertyDecoderHintTypeEnum + .JVX_PROPERTY_DECODER_MULTI_CHANNEL_CIRCULAR_BUFFER.index; + break; + + case 'multi channel switch buffer': + retVal = jvxPropertyDecoderHintTypeEnum + .JVX_PROPERTY_DECODER_MULTI_CHANNEL_SWITCH_BUFFER.index; + break; + + case 'command': + retVal = jvxPropertyDecoderHintTypeEnum + .JVX_PROPERTY_DECODER_COMMAND.index; + break; + + case 'simple onoff': + retVal = jvxPropertyDecoderHintTypeEnum + .JVX_PROPERTY_DECODER_SIMPLE_ONOFF.index; + break; + + case 'value or dont care': + retVal = jvxPropertyDecoderHintTypeEnum + .JVX_PROPERTY_DECODER_VALUE_OR_DONTCARE.index; + break; + + case 'subformat (index)': + retVal = jvxPropertyDecoderHintTypeEnum + .JVX_PROPERTY_DECODER_SUBFORMAT_IDX.index; + break; + + case 'dataflow (index)': + retVal = jvxPropertyDecoderHintTypeEnum + .JVX_PROPERTY_DECODER_DATAFLOW_IDX.index; + break; + + case 'enum type': + retVal = jvxPropertyDecoderHintTypeEnum + .JVX_PROPERTY_DECODER_ENUM_TYPE.index; + break; + + case 'property extender': + retVal = jvxPropertyDecoderHintTypeEnum + .JVX_PROPERTY_DECODER_PROPERTY_EXTENDER_INTERFACE.index; + break; + + case 'local text log': + retVal = jvxPropertyDecoderHintTypeEnum + .JVX_PROPERTY_DECODER_LOCAL_TEXT_LOG.index; + break; + + default: + print( + 'Warning: For datatype <$formatName>, a selection value of <$selection> is not handled!'); + + break; + } + } + } else if (formatName == 'jvxPropertyAccessType') { + retVal = jvxPropertyAccessTypeEnum.JVX_PROPERTY_ACCESS_NONE.index; + switch (selection) { + case 'none': + retVal = jvxPropertyAccessTypeEnum.JVX_PROPERTY_ACCESS_NONE.index; + break; + case 'full read + write': + retVal = jvxPropertyAccessTypeEnum + .JVX_PROPERTY_ACCESS_FULL_READ_AND_WRITE.index; + break; + + case 'read + write': + retVal = jvxPropertyAccessTypeEnum + .JVX_PROPERTY_ACCESS_READ_AND_WRITE_CONTENT.index; + break; + + case 'read': + retVal = + jvxPropertyAccessTypeEnum.JVX_PROPERTY_ACCESS_READ_ONLY.index; + break; + + case 'reados': + retVal = jvxPropertyAccessTypeEnum + .JVX_PROPERTY_ACCESS_READ_ONLY_ON_START.index; + break; + + case 'write': + retVal = + jvxPropertyAccessTypeEnum.JVX_PROPERTY_ACCESS_WRITE_ONLY.index; + break; + default: + print( + 'Warning: For datatype <$formatName>, a selection value of <$selection> is not handled!'); + + break; + } + } else if (formatName == "jvxState") { + retVal = 0; + + // We may correct here in the future: If multiples states are combined in the text expression. + // It seems that this is currently not in use. + if (selection == 'none') { + retVal |= jvxState.JVX_STATE_NONE; + } + if (selection == 'selected') { + retVal |= jvxState.JVX_STATE_SELECTED; + } + if (selection == 'active') { + retVal |= jvxState.JVX_STATE_ACTIVE; + } + if (selection == 'prepared') { + retVal |= jvxState.JVX_STATE_PREPARED; + } + if (selection == 'prepared') { + retVal |= jvxState.JVX_STATE_PREPARED; + } + if (selection == 'processing') { + retVal |= jvxState.JVX_STATE_PROCESSING; + } + if (selection == 'complete') { + retVal |= jvxState.JVX_STATE_COMPLETE; + } + + // A return value of "0" indicates a problem! + assert(retVal != 0); + } else if (formatName == "jvxDeviceDataFlowType") { + retVal = jvxDeviceDataFlowTypeEnum.JVX_DEVICE_DATAFLOW_UNKNOWN.index; + switch (selection) { + case 'unknown': + retVal = jvxDeviceDataFlowTypeEnum.JVX_DEVICE_DATAFLOW_UNKNOWN.index; + break; + case 'active': + retVal = jvxDeviceDataFlowTypeEnum.JVX_DEVICE_DATAFLOW_ACTIVE.index; + break; + + case 'passive': + retVal = jvxDeviceDataFlowTypeEnum.JVX_DEVICE_DATAFLOW_PASSIVE.index; + break; + + case 'offline': + retVal = jvxDeviceDataFlowTypeEnum.JVX_DEVICE_DATAFLOW_OFFLINE.index; + break; + + case 'control': + retVal = jvxDeviceDataFlowTypeEnum.JVX_DEVICE_DATAFLOW_CONTROL.index; + break; + default: + print( + 'Warning: For datatype <$formatName>, a selection value of <$selection> is not handled!'); + break; + } + } else if (formatName == "jvxDeviceCapabilityType") { + retVal = 0; + var tokens = selection.split('|'); + for (var elmT in tokens) { + if (elmT == 'Unknown') { + retVal |= 1 << + jvxDeviceCapabilityTypeShift + .JVX_DEVICE_CAPABILITY_UNKNOWN_SHIFT.index; + break; + } + if (elmT == 'Input') { + retVal |= 1 << + jvxDeviceCapabilityTypeShift + .JVX_DEVICE_CAPABILITY_INPUT_SHIFT.index; + break; + } + if (elmT == 'Output') { + retVal |= 1 << + jvxDeviceCapabilityTypeShift + .JVX_DEVICE_CAPABILITY_OUTPUT_SHIFT.index; + break; + } + if (elmT == 'Duplex') { + retVal |= 1 << + jvxDeviceCapabilityTypeShift + .JVX_DEVICE_CAPABILITY_DUPLEX_SHIFT.index; + break; + } + if (elmT == 'Other') { + retVal |= 1 << + jvxDeviceCapabilityTypeShift + .JVX_DEVICE_CAPABILITY_OTHER_SHIFT.index; + break; + } + } + } else if (formatName == "jvxReportCommandRequest") { + // cmdreq-unspecific + retVal = jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_UNSPECIFIC.index; + switch (selection) { + case 'unspecific': + retVal = jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_UNSPECIFIC.index; + break; + + case 'cmdreq-upd-cp-lst': + retVal = jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_UPDATE_AVAILABLE_COMPONENT_LIST.index; + break; + + case 'cmdreq-upd-stat-cp-lst': + retVal = jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_UPDATE_STATUS_COMPONENT_LIST.index; + break; + + case 'cmdreq-upd-stat-cp': + retVal = jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_UPDATE_STATUS_COMPONENT.index; + break; + + case 'cmdreq-sys-stat': + retVal = jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_SYSTEM_STATUS_CHANGED.index; + break; + + case 'cmdreq-trig-seq-imm': + retVal = jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_TRIGGER_SEQUENCER_IMMEDIATE.index; + break; + + case 'cmdreq-upd-props': + retVal = jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_UPDATE_ALL_PROPERTIES.index; + break; + + case 'cmdreq-resched-main': + retVal = jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_RESCHEDULE_MAIN.index; + break; + + case 'cmdreq-born-sub-dev': + retVal = jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_BORN_SUBDEVICE.index; + break; + + case 'cmdreq-died-sub-dev': + retVal = jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_DIED_SUBDEVICE.index; + break; + + case 'cmdreq-born-comp': + retVal = jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_BORN_COMPONENT.index; + break; + + case 'cmdreq-died-comp': + retVal = jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_DIED_COMPONENT.index; + break; + + case 'cmdreq-proc-connect': + retVal = jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_PROCESS_CONNECTED.index; + break; + + case 'cmdreq-proc-discon': + retVal = jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_PROCESS_TO_BE_DISCONNECTED + .index; + break; + + case 'cmdreq-req-test-chain': + retVal = jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_TEST_CHAIN.index; + break; + + case 'cmdreq-rep-cp-sswitch': + retVal = jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_COMPONENT_STATESWITCH.index; + break; + + case 'cmdreq-config-complete': + retVal = jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_CONFIGURATION_COMPLETE.index; + break; + + case 'cmdreq-rep-seq-evt': + retVal = jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_SEQUENCER_EVENT.index; + break; + + case 'cmdreq-rep-seq_cb': + retVal = jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_SEQUENCER_CALLBACK.index; + break; + + case 'cmdreq-proc-dis-cmplt': + retVal = jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_PROCESS_DISCONNECT_COMPLETE + .index; + break; + + case 'cmdreq-req-upd-prop': + retVal = jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_UPDATE_PROPERTY.index; + break; + + case 'cmdreq-proc-test': + retVal = jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_TEST_SUCCESS.index; + break; + case 'cmdreq-req-test-chain-run': + retVal = jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_TEST_CHAIN_RUN.index; + break; + default: + print( + 'Warning: For datatype <$formatName>, a selection value of <$selection> is not handled!'); + break; + } + } else if (formatName == "jvxReportCommandDataType") { + retVal = jvxReportCommandDataTypeEnum + .JVX_REPORT_COMMAND_TYPE_BASE.index; // 'cmdreqtp-base' + switch (selection) { + case 'cmdreqtp-base': + retVal = + jvxReportCommandDataTypeEnum.JVX_REPORT_COMMAND_TYPE_BASE.index; + break; + + case 'cmdreqtp-sched': + retVal = jvxReportCommandDataTypeEnum + .JVX_REPORT_COMMAND_TYPE_SCHEDULE.index; + break; + + case 'cmdreqtp-ident': + retVal = + jvxReportCommandDataTypeEnum.JVX_REPORT_COMMAND_TYPE_IDENT.index; + break; + + case 'cmdreqtp-uid': + retVal = + jvxReportCommandDataTypeEnum.JVX_REPORT_COMMAND_TYPE_UID.index; + break; + + case 'cmdreqtp-ss': + retVal = + jvxReportCommandDataTypeEnum.JVX_REPORT_COMMAND_TYPE_SS.index; + break; + case 'cmdreqtp-seq': + retVal = + jvxReportCommandDataTypeEnum.JVX_REPORT_COMMAND_TYPE_SEQ.index; + break; + default: + print( + 'Warning: For datatype <$formatName>, a selection value of <$selection> is not handled!'); + break; + } + } else if (formatName == 'jvxStateSwitch') { + retVal = jvxStateSwitchEnum.JVX_STATE_SWITCH_NONE.index; + switch (selection) { + case 'none': + retVal = jvxStateSwitchEnum.JVX_STATE_SWITCH_NONE.index; + break; + + case 'initialize': + retVal = jvxStateSwitchEnum.JVX_STATE_SWITCH_INIT.index; + break; + + case 'select': + retVal = jvxStateSwitchEnum.JVX_STATE_SWITCH_SELECT.index; + break; + + case 'activate': + retVal = jvxStateSwitchEnum.JVX_STATE_SWITCH_ACTIVATE.index; + break; + + case 'prepare': + retVal = jvxStateSwitchEnum.JVX_STATE_SWITCH_PREPARE.index; + break; + + case 'start': + retVal = jvxStateSwitchEnum.JVX_STATE_SWITCH_START.index; + break; + + case 'stop': + retVal = jvxStateSwitchEnum.JVX_STATE_SWITCH_STOP.index; + break; + + case 'postprocess': + retVal = jvxStateSwitchEnum.JVX_STATE_SWITCH_POSTPROCESS.index; + break; + + case 'deactivate': + retVal = jvxStateSwitchEnum.JVX_STATE_SWITCH_DEACTIVATE.index; + break; + + case 'unselect': + retVal = jvxStateSwitchEnum.JVX_STATE_SWITCH_UNSELECT.index; + break; + + case 'terminate': + retVal = jvxStateSwitchEnum.JVX_STATE_SWITCH_TERMINATE.index; + break; + + default: + print( + 'Warning: For datatype <$formatName>, a selection value of <$selection> is not handled!'); + break; + } + } else if (formatName == 'jvxSequencerQueueType') { + retVal = jvxSequencerQueueTypeEnum.JVX_SEQUENCER_QUEUE_TYPE_NONE.index; + switch (selection) { + case 'none': + retVal = + jvxSequencerQueueTypeEnum.JVX_SEQUENCER_QUEUE_TYPE_NONE.index; + break; + case 'queue_type_run': + retVal = jvxSequencerQueueTypeEnum.JVX_SEQUENCER_QUEUE_TYPE_RUN.index; + break; + case 'queue_type_leave': + retVal = + jvxSequencerQueueTypeEnum.JVX_SEQUENCER_QUEUE_TYPE_LEAVE.index; + break; + default: + print( + 'Warning: For datatype <$formatName>, a selection value of <$selection> is not handled!'); + break; + } + } else if (formatName == 'jvxSequencerElementType') { + retVal = + jvxSequencerElementTypeEnum.JVX_SEQUENCER_TYPE_COMMAND_NONE.index; + switch (selection) { + case 'none': + retVal = + jvxSequencerElementTypeEnum.JVX_SEQUENCER_TYPE_COMMAND_NONE.index; + break; + + case 'activate': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_COMMAND_COMPONENT_ACTIVATE.index; + break; + + case 'prepare': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_COMMAND_COMPONENT_PREPARE.index; + break; + + case 'start': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_COMMAND_COMPONENT_START.index; + break; + + case 'stop': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_COMMAND_COMPONENT_STOP.index; + break; + + case 'post process': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_COMMAND_COMPONENT_POSTPROCESS.index; + break; + + case 'deactivate': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_COMMAND_COMPONENT_DEACTIVATE.index; + break; + + case 'condition wait': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_CONDITION_WAIT.index; + break; + + case 'condition jump': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_CONDITION_JUMP.index; + break; + + case 'jump': + retVal = jvxSequencerElementTypeEnum.JVX_SEQUENCER_TYPE_JUMP.index; + break; + + case 'command': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_COMMAND_SPECIFIC.index; + break; + + case 'wait until stop': + retVal = + jvxSequencerElementTypeEnum.JVX_SEQUENCER_TYPE_WAIT_FOREVER.index; + break; + + case 'relative jump': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_WAIT_CONDITION_RELATIVE_JUMP.index; + break; + + case 'break': + retVal = jvxSequencerElementTypeEnum.JVX_SEQUENCER_TYPE_BREAK.index; + break; + + case 'request viewer update': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_REQUEST_UPDATE_VIEWER.index; + break; + + case 'start processing loop': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_REQUEST_START_IN_PROCESSING_LOOP.index; + break; + + case 'stop processing loop': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_REQUEST_STOP_IN_PROCESSING_LOOP.index; + break; + + case 'callback': + retVal = + jvxSequencerElementTypeEnum.JVX_SEQUENCER_TYPE_CALLBACK.index; + break; + + case 'proc prepare': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_COMMAND_PROCESS_PREPARE.index; + break; + + case 'proc start': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_COMMAND_PROCESS_START.index; + break; + + case 'proc stop': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_COMMAND_PROCESS_STOP.index; + break; + + case 'proc post process': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_COMMAND_PROCESS_POSTPROCESS.index; + break; + + case 'output text': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_COMMAND_OUTPUT_TEXT.index; + break; + + case 'call seq': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_COMMAND_CALL_SEQUENCE.index; + break; + + case 'switch state': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_COMMAND_SWITCH_STATE.index; + break; + + case 'reset state': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_COMMAND_RESET_STATE.index; + break; + + case 'proc test': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_COMMAND_PROCESS_TEST.index; + break; + + case '': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_COMMAND_INTERCEPT.index; + break; + + case 'nop': + retVal = + jvxSequencerElementTypeEnum.JVX_SEQUENCER_TYPE_COMMAND_NOP.index; + break; + + case 'set_property': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_COMMAND_SET_PROPERTY.index; + break; + + case 'error': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_COMMAND_INVOKE_ERROR.index; + break; + + case 'fata_error': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_COMMAND_INVOKE_FATAL_ERROR.index; + break; + + case 'uncond wait': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_UNCONDITION_WAIT.index; + break; + + case 'wait_tasks': + retVal = jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_WAIT_AND_RUN_TASKS.index; + break; + default: + print( + 'Warning: For datatype <$formatName>, a selection value of <$selection> is not handled!'); + break; + } + } else if (formatName == 'jvxSequencerStatus') { + retVal = jvxSequencerStatusEnum.JVX_SEQUENCER_STATUS_NONE.index; + switch (selection) { + case 'none': + retVal = jvxSequencerStatusEnum.JVX_SEQUENCER_STATUS_NONE.index; + break; + + case 'startup': + retVal = jvxSequencerStatusEnum.JVX_SEQUENCER_STATUS_STARTUP.index; + break; + + case 'in operation': + retVal = + jvxSequencerStatusEnum.JVX_SEQUENCER_STATUS_IN_OPERATION.index; + break; + + case 'waiting': + retVal = jvxSequencerStatusEnum.JVX_SEQUENCER_STATUS_WAITING.index; + break; + + case 'wants stop process': + retVal = jvxSequencerStatusEnum + .JVX_SEQUENCER_STATUS_WANTS_TO_STOP_PROCESS.index; + break; + + case 'wants stop sequence': + retVal = jvxSequencerStatusEnum + .JVX_SEQUENCER_STATUS_WANTS_TO_STOP_SEQUENCE.index; + break; + + case 'shutdown': + retVal = jvxSequencerStatusEnum + .JVX_SEQUENCER_STATUS_SHUTDOWN_IN_PROGRESS.index; + break; + + case 'error': + retVal = jvxSequencerStatusEnum.JVX_SEQUENCER_STATUS_ERROR.index; + break; + + case 'shutdown complete': + retVal = jvxSequencerStatusEnum + .JVX_SEQUENCER_STATUS_SHUTDOWN_COMPLETE.index; + break; + default: + print( + 'Warning: For datatype <$formatName>, a selection value of <$selection> is not handled!'); + break; + } + } else { + assert(false); + } + return retVal; + } + + static JvxComponentIdentification componentIdentificationFromString( + String cpIdStr) { + JvxComponentIdentification cpId = JvxComponentIdentification(); + var lst = cpIdStr.split('<'); + cpId.cpTp = componentTypeFromString(lst[0]); + if (cpId.cpTp != JvxComponentTypeEnum.JVX_COMPONENT_UNKNOWN) { + cpId.slotid = 0; + cpId.slotsubid = 0; + } + if (lst.length > 1) { + lst = lst[1].split('>'); + lst = lst[0].split(','); + + // If comma does not work, try - + if (lst.length == 1) { + lst = lst[0].split('-'); + } + cpId.slotsubid = 0; + cpId.slotid = int.parse(lst[0]); + if (lst.length > 1) { + cpId.slotsubid = int.parse(lst[1]); + } + } + + cpId.uid = 0; + return cpId; + } + + static JvxComponentTypeEnum componentTypeFromString(String inStr) { + bool foundit = false; + JvxComponentTypeEnum cpTp = JvxComponentTypeEnum.JVX_COMPONENT_UNKNOWN; + + for (int i = JvxComponentTypeEnum.JVX_COMPONENT_UNKNOWN.index; + i < JvxComponentTypeEnum.values.length; + i++) { + JvxComponentTypeEnum cpTpTest = JvxComponentTypeEEnum.fromInt(i); + String str = cpTpTest.txt; + if (inStr == str) { + foundit = true; + cpTp = cpTpTest; + break; + } + } + if (!foundit) { + print('Unable to find component type < ${inStr} >.'); + } + return cpTp; + } +} diff --git a/flutter/ayf_pack/lib/helpers/web/ayf_helper_lib.dart b/flutter/ayf_pack/lib/helpers/web/ayf_helper_lib.dart new file mode 100644 index 00000000..9961790b --- /dev/null +++ b/flutter/ayf_pack/lib/helpers/web/ayf_helper_lib.dart @@ -0,0 +1,88 @@ +/* +void printFormattedTextMessage(int id2, Pointer load_fld) { + Pointer cstr = load_fld.cast(); + String txt = cstr.toDartString(); + String typeMess = ""; + switch (id2) { + case jvxReportPriority.JVX_REPORT_PRIORITY_DEBUG: + typeMess = "DEBUG: "; + break; + case jvxReportPriority.JVX_REPORT_PRIORITY_ERROR: + typeMess = "ERROR: "; + break; + case jvxReportPriority.JVX_REPORT_PRIORITY_INFO: + typeMess = "INFO: "; + break; + case jvxReportPriority.JVX_REPORT_PRIORITY_SUCCESS: + typeMess = "SUCCESS: "; + break; + case jvxReportPriority.JVX_REPORT_PRIORITY_VERBOSE: + typeMess = "VERBOSE: "; + break; + case jvxReportPriority.JVX_REPORT_PRIORITY_WARNING: + typeMess = "WARNING: "; + break; + } + print("${typeMess}${txt}"); +} + +bool setSinglePropertyValueMulti( + AudYoFloPropertyContainer mc, int posi, T val) { + bool retVal = false; + if (mc is AudYoFloPropertyMultiContentNative) { + AudYoFloPropertyMultiContentNative pp = mc; + if (pp.fld != null) { + if (posi < pp.fldSz) { + if (val is double) { + pp.fld![posi] = val; + retVal = true; + } + } + } + } else if (mc is AudYoFloPropertyMultiContentNative) { + AudYoFloPropertyMultiContentNative pp = mc; + if (pp.fld != null) { + if (posi < pp.fldSz) { + if (val is double) { + pp.fld![posi] = val; + retVal = true; + } + } + } + } + return retVal; +} + +// Return a value from a property list (call by reference, +// check the AudYoFloValRef type) +bool getSinglePropertyValueMulti( + AudYoFloPropertyContainer mc, int posi, AudYoFloCallByReference val) { + bool retVal = false; + if (mc is AudYoFloPropertyMultiContentNative) { + AudYoFloPropertyMultiContentNative pp = mc; + if (pp.fld != null) { + if (posi < pp.fldSz) { + if (val is AudYoFloCallByReference) { + AudYoFloCallByReference tVal = + val as AudYoFloCallByReference; + tVal.value = pp.fld![posi]; + retVal = true; + } + } + } + } else if (mc is AudYoFloPropertyMultiContentNative) { + AudYoFloPropertyMultiContentNative pp = mc; + if (pp.fld != null) { + if (posi < pp.fldSz) { + if (val is AudYoFloCallByReference) { + AudYoFloCallByReference tVal = + val as AudYoFloCallByReference; + tVal.value = pp.fld![posi]; + retVal = true; + } + } + } + } + return retVal; +} +*/ \ No newline at end of file diff --git a/flutter/ayf_pack/lib/interfaces/ayf_core_pack_if.dart b/flutter/ayf_pack/lib/interfaces/ayf_core_pack_if.dart new file mode 100644 index 00000000..e0f015d7 --- /dev/null +++ b/flutter/ayf_pack/lib/interfaces/ayf_core_pack_if.dart @@ -0,0 +1,3 @@ +abstract class AyfCorePackIf { + Future?> getEntryPoints(); +} \ No newline at end of file diff --git a/flutter/ayf_pack/lib/interfaces/ayf_interface_backend_adapter_if.dart b/flutter/ayf_pack/lib/interfaces/ayf_interface_backend_adapter_if.dart new file mode 100644 index 00000000..3c44c2dd --- /dev/null +++ b/flutter/ayf_pack/lib/interfaces/ayf_interface_backend_adapter_if.dart @@ -0,0 +1,151 @@ +import '../ayf_pack_local.dart'; + +abstract class AudYoFloBackendTranslator { + //String translateComponent(jvxComponentIdentification elm); + // Translate an enum string into an int (and from there into a real enum). + // We indeed involve the backend for this since the lookup is stored in the + // backend. However, in the future the enum lookup should NOT involve a web + // connection if using a web control. + int translateEnumString(String selection, String formatName, + AudYoFloCompileFlags flags); + + // Obtain compile falgs + AudYoFloCompileFlags compileFlags(); + +} + +/* + * Interface to address the backend from GUI or backend cache + */ //======================================================== +abstract class AudYoFloBackendAdapterIf extends AudYoFloBackendTranslator { + // ================================== + //// Interface invoked from application + // ================================== + + // Trigger start of the sequencer + Future triggerStartSequencer(); + + // Trigger stop of the sequencer + Future triggerStopSequencer(); + + // If sequencer is stopped, start the sequencer, + // if sequencer is started, stop the sequencer + Future triggerToggleSequencer(); + + // Obtain the current sequencer status + Future updateStatusSequencer(); + + // ================================== + + // Transfer a single text command. The result may be returned delayed. + Future transferTextCommand(String textArg, bool jsonOut); + + // ================================== + + // Forward of cache update functions towards the backend + // https://stackoverflow.com/questions/53249859/async-await-not-waiting-before-future-dart + Future triggerUpdateAvailComponents(JvxComponentIdentification cpId, + List updateMe); + + // =============================================================================== + // Change component states + // =============================================================================== + + // Forward of cache update functions towards the backend + Future triggerActivateComponent( + JvxComponentIdentification cpId, int id, bool select); + + // Forward of cache update functions towards the backend + Future triggerDeactivateComponent( + JvxComponentIdentification cpId, bool unselect); + + // =============================================================================== + + // Trigger call to read out the current process status. This call will be completed + // with an update of the cache value and a notify + Future triggerUpdateProcessStatus(); + + // =============================================================================== + + // Forward of cache update functions towards the backend + Future triggerUpdateDeviceList(JvxComponentIdentification cpId); + + // Forward of cache update functions towards the backend + Future triggerUpdateComponentList( + JvxComponentIdentification cpId, bool isDevice); + + // Forward of cache update functions towards the backend + Future triggerSaveConfig(); + + void reportPropertyListSetBackend( + JvxComponentIdentification cpId, String propLst); + // !! OPTIMIZE THOSE OUT!! + + //========================================================================= + // These functions are run whenever the sequencer has completed. Then, we need to + // check an action queue to find pending requests + //========================================================================= + + // Acknowledge the sequencer stop event. In the end, the stop must be + // acknowledged to reset the state in the backend. We may change this. + // We do not need this in web connection but it is used in native host + jvxSequencerStatusEnum acknowledgeSequencerStop(); + + // Immediate request of the sequencer status. We need this to find out if the seq + // status has changed + // jvxSequencerStatusEnum requestSequencerStatus_(); + + // This step is run after the command to stop the sequencer + // has been triggered, the end of sequencer was acknowledged. + // We may run steps from a queue, therefore, this function returns + // jvxFernliveCommandQueueElement elements until there is none left. + AudYoFloCommandQueueElement postSequencerStop(); + + //========================================================================= + + // Immediate request of the process - CAN WE OPTIMIZE THIS OUT? + AudYoFloOneConnectedProcess allocateProcess(int uId); + + String debugPrintProcess(int uId); + + Future triggerClose(); + + //void reportUpdateProcessStatus(); + // !! OPTIMIZE THOSE OUT!! + + // ============================================================================ + // ============================================================================ + + // Translate component type to a component type class + jvxComponentTypeClassEnum lookupComponentClass(JvxComponentTypeEnum tp); + + // Forward of cache update functions towards the backend + Future triggerGetPropertyDescriptionComponent( + JvxComponentIdentification cpId, List propDescr, + {AyfPropertyReportLevel reportArg = + AyfPropertyReportLevel.AYF_FRONTEND_REPORT_COMPONENT_PROPERTY}); + + // Forward of cache update functions towards the backend + Future triggerGetPropertyContentComponent( + JvxComponentIdentification cpId, List propDescr, + {AyfPropertyReportLevel reportArg = + AyfPropertyReportLevel.AYF_FRONTEND_REPORT_SINGLE_PROPERTY}); + + // Forward of cache update functions towards the backend + Future triggerSetProperties( + JvxComponentIdentification cpId, List propContents, + {bool invalidateProperty = true, + int offset = 0, + int num = -1, + bool preventCallMultipleTimes = false, + AyfPropertyReportLevel reportArg = + AyfPropertyReportLevel.AYF_BACKEND_REPORT_COMPONENT_PROPERTY}); + + // Forward of cache update functions towards the backend + Future triggerUpdatePropertyListComponent( + JvxComponentIdentification cpId, + {AyfPropertyReportLevel reportArg = + AyfPropertyReportLevel.AYF_FRONTEND_REPORT_COMPONENT_PROPERTY}); + + String get lastError; +} diff --git a/flutter/ayf_pack/lib/interfaces/ayf_interface_bectrl_if.dart b/flutter/ayf_pack/lib/interfaces/ayf_interface_bectrl_if.dart new file mode 100644 index 00000000..b5d2c66d --- /dev/null +++ b/flutter/ayf_pack/lib/interfaces/ayf_interface_bectrl_if.dart @@ -0,0 +1,163 @@ +import '../ayf_pack_local.dart'; + +abstract class AudYoFloBackendCacheBectrlIf { + // ==================================================================== + // ==================================================================== + // Report a new component that pops up in the system due to a select + // operation. + void reportSelectedComponent(JvxComponentIdentification cpId, String descr, + {AudYoFloDirectPropertiesIf? directPropRef}); + + // Report a component that has been removed from the system due to an + // unselect operation + void reportUnselectedComponent(JvxComponentIdentification cpId); + + // Function to invalidate the component list in cache. This function is + // principally called if a component that matches the type/id is selected + // or unselected. In particular, it is important to invalidate since the + // location with slots and subslots is affected. + void invalidateComponentListCache(JvxComponentIdentification cpId); + + // ========================================================================== + // We had 2 interfaces at this point earlier + // ========================================================================== + + // Function to add a new process uId. This function is called from backend + // if a new process is reported asynchronously. + void addProcess(int uId); + + // Function to remove a process uId. This function is called from backend + // if a removed process is reported asynchronously. + void remProcess(int uId); + + // ==================================================================== + + // Function to update the process status. The process status allows to + // decide if processing may be started. The backend will forward this + // request in async manner and generate a notify on the backend cache. + // This function can be addressed from frontend as well as the backend + Future triggerUpdateProcessStatus(); + + // Function to conclude the update the status of the system. This function + // notifies all listeners + void updateProcessStatusCompleteNotify(bool procStatus, String reasonIfNot); + + // ==================================================================== + // ==================================================================== + + // Report if the state of a component has changed. + void reportStateSwitchComponent( + JvxComponentIdentification cpId, jvxStateSwitchEnum ss); + + // Report that the status of a component has changed. A status change is + // reported if the event + // comes from the connected backend. This event is reported if the INTERNAL + // status of a component has changed, it is independent of the component + // state switch. + void reportUpdateStatusComponent(JvxComponentIdentification cpId); + + // Report a sequencer event. This report comes from the backend adapter + // directly and may be enter from a websocket connection or an async local + // event. + int reportSequencerEvent( + jvxSequencerEventType ev, jvxSequencerStatusEnum seqStat); + + // ==================================================================== + // ==================================================================== + + List? referenceValidPropertiesComponents( + JvxComponentIdentification cpId, List props); + + // Properties can + Future triggerUpdatePropertiesComponent( + JvxComponentIdentification cpId, List propDescr, + {AyfPropertyReportLevel report = + AyfPropertyReportLevel.AYF_FRONTEND_REPORT_SINGLE_PROPERTY, + bool forceDescriptor = false, + bool forceContent = false}); + + // ==================================================================== + // ==================================================================== + + // Update one property entry in the property cache. This call will only + // update the content, it does NOT invoke a notify event but it increases the + // update id of the property. + int updateEntrySinglePropertyCache( + JvxComponentIdentification cpId, AudYoFloPropertyContentBackend newProp); + + // Acknowledge an update of the latest property interaction. It may increase + // the component specific update id (argument report) and it calls notify + // to push a signal with an updated backend cache. + int updatePropertyCacheCompleteNotify(JvxComponentIdentification cpId, + {AyfPropertyReportLevel report = + AyfPropertyReportLevel.AYF_FRONTEND_REPORT_SINGLE_PROPERTY}); + + // Get a reference to a property from the backend to refresh the actual + // content. The caller of this function is responsible to inform the app via + // emission of notify events + AudYoFloPropertyContainer? referencePropertyInCache( + JvxComponentIdentification cpId, String descriptor); + + // ==================================================================== + // ==================================================================== + // Function to report a new version of the property list in cache. + // This call will trigger the notify function. + int updatePropertyCacheListCompleteNotify( + JvxComponentIdentification cpId, List lst, + {AyfPropertyReportLevel report = + AyfPropertyReportLevel.AYF_FRONTEND_REPORT_COMPONENT_PROPERTY}); + + // Function to invalidate the list of properties as given in the argument + // props. The properties may be invalidated regarding the content of the + // property (lite version) or the overall property. This function does + // not trigger a notify. Properties can be invaliodated from frontend as well + // as from backend. Typically, an invalidation ends with a call to + // to trigger a notify. + void invalidatePropertiesComponent( + JvxComponentIdentification cpId, List props, bool contentOnly, + {bool invalidateAll = false}); + + // ==================================================================== + // ==================================================================== + + // Function to update the device list in cache and notify the listeners. + // The device list previously stored in cache is replaced by the passed + // list. Note that a device list is obtained for a technology, the cpId + // is specified by means of the type and the slot id. + int updateDeviceListCacheCompleteNotify(JvxComponentIdentification cpId, + List options); + + // Function to update the list of selectable components and notify the + // listeners. The list of components depends on the component type for + // technologies and nodes and on the slotid for the device components. + // A previously stored list is replaced by the call. + int updateComponentListCacheCompleteNotify(JvxComponentIdentification cpId, + List options, bool isDevice); + + // ==================================================================== + // ==================================================================== + + // Function to report list of properties that have recently been modified + // This call reuires the config to enable report on every property set + void reportPropertyListSetBackend( + JvxComponentIdentification cpId, String propLst); + + // ==================================================================== + // ==================================================================== + + void triggerClose(); + + // If some calls this function, the host needs to restart!! + void reportSystemStateRestart(); + + // More kind of helper functions + + // Function to check if a component is of type "technology". + bool checkComponentIdentTechnology(JvxComponentIdentification cpId); + + // Function to check if a component is of type "device". + bool checkComponentIdentDevice(JvxComponentIdentification cpId); + + // Function to create a string to represent a process. + String debugPrintProcess(int uId); +} diff --git a/flutter/ayf_pack/lib/interfaces/ayf_interface_fectrl_if.dart b/flutter/ayf_pack/lib/interfaces/ayf_interface_fectrl_if.dart new file mode 100644 index 00000000..063137d0 --- /dev/null +++ b/flutter/ayf_pack/lib/interfaces/ayf_interface_fectrl_if.dart @@ -0,0 +1,112 @@ +import '../ayf_pack_local.dart'; + +abstract class AudYoFloBackendCacheFectrlIf { + // Explicitely set a property to be invalid + void invalidatePropertiesComponent( + JvxComponentIdentification cpId, List props, bool contentOnly, + {bool invalidateAll = false}); + + // Return the list of properties with valid content + List? referenceValidPropertiesComponents( + JvxComponentIdentification cpId, List props); + + // Update one or more properties to be stored in cache. + Future triggerUpdatePropertiesComponent( + JvxComponentIdentification cpId, List propDescr, + {AyfPropertyReportLevel report = + AyfPropertyReportLevel.AYF_FRONTEND_REPORT_SINGLE_PROPERTY, + bool forceDescriptor = false, + bool forceContent = false}); + + // Send a list of new property values to backend. The properties will be + // invalidated on set since a new value must be read for read-back before + // exposing to UI + Future triggerSetProperties( + JvxComponentIdentification cpId, List propContents, + {bool invalidateProperty = true, + int offset = 0, + int num = -1, + bool preventCallMultipleTimes = false, + AyfPropertyReportLevel report = + AyfPropertyReportLevel.AYF_FRONTEND_REPORT_NO_REPORT}); + + // ========================================================================= + + void triggerStartSequencer(); + + // Trigger stop of the sequencer + void triggerStopSequencer(); + + void triggerToggleSequencer(); + + Future transferTextCommand(String textArg, bool jsonOut); + + // Get a list of strings to list all properties for a specific component + // The list is returned as null if there is no valid property list + // If it is null, you can call to create a + // list + List? referencePropertyListInCache(JvxComponentIdentification cpId); + + // Trigger an update of the property list in cache. + Future triggerUpdatePropertyListComponent( + JvxComponentIdentification cpId); + +// ========================================================================== + // We had 2 interfaces at this point earlier + // ========================================================================== + + // ================================================================ + // HELPER FUNCTIONS + void findComponentsMatch(List lst, int processId); + + void availProcessDescriptions(List lst); + + String descriptionComponent(JvxComponentIdentification cpTp); + + // ======================================================================= + // Activate a new component specified by the id. + // The activated component will be reported via the "report" callbacks + Future triggerActivateComponent(JvxComponentIdentification cpId, int id, + {bool selectBeforeActivate = true}); + + Future triggerDeactivateComponent(JvxComponentIdentification cpId, + {bool unselect = true}); + + // ======================================================================= + + // Function to update the process status. The backend will drive this call + // in async manner and generate a notify on the backend cache. + Future triggerUpdateProcessStatus(); + + // ======================================================================= + + int updateIdComponentInCache(JvxComponentIdentification cpId); + + int updateIdPropertyComponentInCache( + JvxComponentIdentification cpId, String propDescr); + + int updateIdDeviceListInCache(JvxComponentIdentification cpId); + + // ========================================================================= + + // Function to receive a device list from cache. If no device list present, you can call + // triggerUpdateDeviceList. + List? referenceDeviceListInCache( + JvxComponentIdentification cpId); + + // Function to trigger a device list readout to cache + Future triggerUpdateDeviceList(JvxComponentIdentification cpId); + + // ========================================================================= + + // Function to receive a componnt list from cache. If the component list is null + // you can trigger an update using the function triggerUpdateComponentList. + List? referenceComponentListInCache( + JvxComponentIdentification cpId); + + // Function to update the component list in cache + Future triggerUpdateComponentList(JvxComponentIdentification cpId); + + // ========================================================================= + Future triggerSaveConfig(); +} diff --git a/flutter/ayf_pack/lib/interfaces/ayf_interface_host.dart b/flutter/ayf_pack/lib/interfaces/ayf_interface_host.dart new file mode 100644 index 00000000..26badf44 --- /dev/null +++ b/flutter/ayf_pack/lib/interfaces/ayf_interface_host.dart @@ -0,0 +1,24 @@ +import '../ayf_pack_local.dart'; + +abstract class AyfHost { + // The initialize function may test some things and return in a delayed way. Therefore, we + // need to use a future here. Also note that specific host parameters must be specified in + // argument args + Future initialize(AudYoFloBackendCache beCache, + AudYoFloDebugModel? dbgModel, Map args); + + // Return a string to specify which host adapter is in use. + String descriptionHost(); + + // Terminate the host + void terminate(); + + // This must be called from the main thread from time to time in case the native host is in use + void periodicTriggerSlow(); + + // Return the comple flags to address properties in a correct way. + AudYoFloCompileFlags get compFlags; + + // Return last error that occurred + String get lastError; +} diff --git a/flutter/ayf_pack/lib/interfaces/ayf_interface_properties.dart b/flutter/ayf_pack/lib/interfaces/ayf_interface_properties.dart new file mode 100644 index 00000000..7af473bf --- /dev/null +++ b/flutter/ayf_pack/lib/interfaces/ayf_interface_properties.dart @@ -0,0 +1,7 @@ +import '../ayf_pack_local.dart'; + +abstract class AudYoFloDirectPropertiesIf { + int setProperty(AudYoFloPropertyContainer prop); + int getProperty(AudYoFloPropertyContainer prop); + int getPropertyDescriptors(List propsOnReturn); +} diff --git a/flutter/ayf_pack/lib/main-widgets/ayf_app_bar.dart b/flutter/ayf_pack/lib/main-widgets/ayf_app_bar.dart new file mode 100644 index 00000000..32e86a65 --- /dev/null +++ b/flutter/ayf_pack/lib/main-widgets/ayf_app_bar.dart @@ -0,0 +1,227 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'dart:async'; +import '../ayf_pack_local.dart'; + +/* + * + * Hint on how to create a "really customized appbar": + * https://dev.to/pazozzoo/creating-a-custom-appbar-in-flutter-572k + */ +class AudYoFloAppBar extends StatefulWidget with PreferredSizeWidget { + final String titletxt; + final String logoPath; + final double widthScale; + final double height; + final double overallScale; + final String routeNavigatorSettingsTag; + + @override + final Size preferredSize; + + const AudYoFloAppBar(BuildContext context, this.titletxt, this.logoPath, + this.widthScale, this.overallScale, this.height, theBeCache, + {Key? key, this.routeNavigatorSettingsTag = ''}) + : preferredSize = const Size.fromHeight(50.0), + super(key: key); + + @override + _MyAppBarState createState() => _MyAppBarState(); +} + +class _MyAppBarState extends State { + Color col = Colors.black; + @override + Widget build(BuildContext context) { + AudYoFloBackendCache theBeCache = + Provider.of(context, listen: false); + + //theBEBridge.triggeredClose(); + //return true; + + return AppBar( + backgroundColor: Colors.white, + title: Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ + Flexible( + flex: 1, + /*child: DecoratedBox( + decoration: BoxDecoration( + // color: Colors.blueGrey, + border: Border.all(), + borderRadius: BorderRadius.circular(5), + /* + boxShadow: [ + BoxShadow( + color: Colors.white, + offset: Offset(-2, -2), + blurRadius: 3), + BoxShadow( + color: Colors.black, + offset: Offset(10, 10), + blurRadius: 8) + ]*/ + ),*/ + child: + Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ + GestureDetector( + onTap: () { + theBeCache.triggerSaveConfig(); + + setState(() { + col = Colors.red; + }); + + Timer wait1Sec = Timer(const Duration(seconds: 1), () { + setState(() { + col = Colors.black; + }); + }); + }, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Icon(Icons.file_copy_outlined, color: col))), + GestureDetector( + onTap: () { + debugPrint('Help triggered'); + }, + child: const Padding( + padding: EdgeInsets.all(8.0), + child: Icon(Icons.help, color: Colors.black))) + ]), + ), + Flexible( + flex: 4, + child: + Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ + Text( + widget.titletxt, + style: const TextStyle(color: Colors.black), + ), + Image.asset( + widget.logoPath, + filterQuality: FilterQuality.medium, + fit: BoxFit.fill, + width: widget.widthScale * widget.height * widget.overallScale, + height: widget.height * widget.overallScale, + ), + GestureDetector( + onTap: () { + if (widget.routeNavigatorSettingsTag.isNotEmpty) { + Navigator.pushNamed( + context, widget.routeNavigatorSettingsTag); + } else { + showDialog( + context: context, + builder: (context) { + return AudYoFloSettingsDialog(); + }); + } + debugPrint('Settings'); + }, + child: const Icon(Icons.menu, color: Colors.black)) + ]), + ) + ]), + toolbarHeight: widget.height, + ); + } +} + +/* +class AudYoFloAppBar extends AppBar { + final String titletxt; + final String logoPath; + final double widthScale; + final double height; + final double overallScale; + final Color col; + + AudYoFloAppBar(BuildContext context, this.titletxt, this.logoPath, + this.widthScale, this.overallScale, this.height, theBeCache, this.col, + {String routeNavigatorSettingsTag = ''}) + : super( + backgroundColor: Colors.white, + title: + Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ + Flexible( + flex: 1, + /*child: DecoratedBox( + decoration: BoxDecoration( + // color: Colors.blueGrey, + border: Border.all(), + borderRadius: BorderRadius.circular(5), + /* + boxShadow: [ + BoxShadow( + color: Colors.white, + offset: Offset(-2, -2), + blurRadius: 3), + BoxShadow( + color: Colors.black, + offset: Offset(10, 10), + blurRadius: 8) + ]*/ + ),*/ + child: Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + GestureDetector( + onTap: () { + theBeCache.triggerSaveConfig(); + }, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: + Icon(Icons.file_copy_outlined, color: col))), + GestureDetector( + onTap: () { + print('File Save'); + }, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Icon(Icons.help, color: Colors.black))) + ]), + ), + ), + Flexible( + flex: 4, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text( + titletxt, + style: TextStyle(color: Colors.black), + ), + Container( + // color: Colors.grey, + //child: SvgPicture.asset(logoPath), + child: Image.asset( + logoPath, + filterQuality: FilterQuality.medium, + fit: BoxFit.fill, + width: widthScale * height * overallScale, + height: height * overallScale, + )), + GestureDetector( + onTap: () { + if (routeNavigatorSettingsTag.isNotEmpty) { + Navigator.pushNamed( + context, routeNavigatorSettingsTag); + } else { + showDialog( + context: context, + builder: (context) { + return AudYoFloSettingsDialog(); + }); + } + print('Settings'); + }, + child: Icon(Icons.menu, color: Colors.black)) + ]), + ) + ]), + toolbarHeight: height, + ); +} +*/ diff --git a/flutter/ayf_pack/lib/main-widgets/ayf_app_widget.dart b/flutter/ayf_pack/lib/main-widgets/ayf_app_widget.dart new file mode 100644 index 00000000..16de14f3 --- /dev/null +++ b/flutter/ayf_pack/lib/main-widgets/ayf_app_widget.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import './ayf_main_layout.dart'; +import '../ayf_pack_local.dart'; + +class AudYoFloMainAppWidget extends StatefulWidget { + AudYoFloMainAppWidget(); + @override + _AudYoFloMainAppWidgetStates createState() => _AudYoFloMainAppWidgetStates(); +} + +class _AudYoFloMainAppWidgetStates extends State + with AudYoFloBackendCacheCallback { + String myCbTag = 'Main'; + + BuildContext? latestContext; + AudYoFloBackendCache? beCache; + AudYoFloUiModel? uiModel; + // FocusNode _myFocusNode = FocusNode(); + + @override + void reportEvent(String reasonHint) { + if (uiModel != null) { + if (uiModel!.initRoute != "/") { + if (beCache != null) { + // Clear the backend cache!! + beCache!.componentOptionMap.fullClear(); + beCache!.deviceOptionMap.fullClear(); + beCache!.componentSection.fullClear(); + beCache!.processSection.fullClear(); + beCache!.sequencerSection.fullClear(); + beCache!.typeClassList.clear(); + beCache!.httpConnect.isConnected = false; + } + Navigator.pushNamed(context, uiModel!.initRoute); + } + } + } + + @override + Widget build(BuildContext context) { + beCache = Provider.of(context, listen: false); + uiModel = Provider.of(context, listen: false); + beCache!.requestSystemDisconnect = this; + latestContext = context; + + // ======================================================= + + final Size size = MediaQuery.of(context).size; + return AudYoFloMainLayout(size: size); + } + + @override + void initState() { + super.initState(); + //AudioFlowBackendBridge bridge = AudioFlowBackendBridge(); + } + + @override + void didUpdateWidget(AudYoFloMainAppWidget oldWidget) { + super.didUpdateWidget(oldWidget); + } + + @override + void dispose() { + super.dispose(); + } +} diff --git a/flutter/ayf_pack/lib/main-widgets/ayf_audio_settings_widget.dart b/flutter/ayf_pack/lib/main-widgets/ayf_audio_settings_widget.dart new file mode 100644 index 00000000..aa9f1926 --- /dev/null +++ b/flutter/ayf_pack/lib/main-widgets/ayf_audio_settings_widget.dart @@ -0,0 +1,97 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import '../ayf_pack_local.dart'; + +class AudYoFloAudioSettingsWidget extends StatefulWidget { + const AudYoFloAudioSettingsWidget( + this.identT, + this.tagTechnologies, + this.identD, + this.tagDevices, + this.deviceTypeTag, + this.callbackDeviceWidget, + this.callbackActiveInactiveDeviceSliver); + final JvxComponentIdentification identT; + final JvxComponentIdentification identD; + final String tagTechnologies; + final String tagDevices; + final String deviceTypeTag; + + final Widget? Function(JvxComponentIdentification selCpId) + callbackDeviceWidget; + final Widget? Function( + AudYoFloOneSelectionOptionWithId option, + bool fromActiveList, + AudYoFloSingleSelectionComponent?) callbackActiveInactiveDeviceSliver; + @override + _AudYoFloAudioSettingsWidgetState createState() => + _AudYoFloAudioSettingsWidgetState(); +} + +class _AudYoFloAudioSettingsWidgetState + extends State { + AudYoFloStringWithId dropdownValuePrimDev = AudYoFloStringWithId('None', -1); + AudYoFloBackendCache? theBeCache; + AudYoFloBackendCacheFectrlIf? beCacheFe; + AudYoFloUiModel? theUiModel; + + @override + Widget build(BuildContext context) { + theBeCache = Provider.of(context, listen: false); + theUiModel = Provider.of(context, listen: false); + + // obtain the frontend interface + beCacheFe = theBeCache!.cacheToFrontend; + + return Selector( + selector: (context, beModel) => theBeCache!.componentSection.ssUpdateId, + shouldRebuild: (previous, next) => (next != previous) ? true : false, + builder: (context, notifier, child) { + if (theUiModel!.showAudioDevTech) { + // ================================================================ + return Container( + color: Colors.white, + child: Column(children: [ + Flexible( + flex: 0, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Flexible( + flex: 1, child: Text(widget.tagTechnologies)), + Flexible( + flex: 1, + child: AudYoFloComponentSelectionComboBoxWidget( + widget.identT), + ), + Flexible(flex: 1, child: Text(widget.tagDevices)), + Flexible( + flex: 1, + child: AudYoFloComponentSelectionComboBoxWidget( + widget.identD), + ) + ], + ), + )), + Flexible( + flex: 10, + child: AudYoFloDeviceSelectionWidget( + widget.identT, + widget.tagDevices, + widget.callbackDeviceWidget, + widget.callbackActiveInactiveDeviceSliver), + ) + ])); + } + + // No additional information: show only audio device lists + return AudYoFloDeviceSelectionWidget( + widget.identT, + widget.tagDevices, + widget.callbackDeviceWidget, + widget.callbackActiveInactiveDeviceSliver); + }); + } +} diff --git a/flutter/ayf_pack/lib/main-widgets/ayf_bottom_app_bar.dart b/flutter/ayf_pack/lib/main-widgets/ayf_bottom_app_bar.dart new file mode 100644 index 00000000..4bb8793e --- /dev/null +++ b/flutter/ayf_pack/lib/main-widgets/ayf_bottom_app_bar.dart @@ -0,0 +1,6 @@ +import 'package:flutter/material.dart'; +import './ayf_play_pannel.dart'; + +class AudYoFloBottomAppBar extends BottomAppBar { + AudYoFloBottomAppBar() : super(child: AudYoFloPlaypannel()); +} diff --git a/flutter/ayf_pack/lib/main-widgets/ayf_bottom_content.dart b/flutter/ayf_pack/lib/main-widgets/ayf_bottom_content.dart new file mode 100644 index 00000000..d3e1a30f --- /dev/null +++ b/flutter/ayf_pack/lib/main-widgets/ayf_bottom_content.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + +class AudYoFloBottomContent extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Center( + child: Row(children: [ + ButtonBar(children: [ + ElevatedButton(child: Container(color: Colors.red), onPressed: () {}) + ]), + Container(width: 120, height: 120, color: Colors.red), + Container(color: Colors.green), + Container(color: Colors.blue) + ]), + ); + } +} diff --git a/flutter/ayf_pack/lib/main-widgets/ayf_component_widget.dart b/flutter/ayf_pack/lib/main-widgets/ayf_component_widget.dart new file mode 100644 index 00000000..46169e33 --- /dev/null +++ b/flutter/ayf_pack/lib/main-widgets/ayf_component_widget.dart @@ -0,0 +1,205 @@ +import 'package:provider/provider.dart'; +import 'package:flutter/material.dart'; +import '../ayf_pack_local.dart'; + +class AudYoFloComponentWidget extends StatefulWidget { + const AudYoFloComponentWidget({Key? key}) : super(key: key); + + @override + _AudYoFloComponentWidgetState createState() => + _AudYoFloComponentWidgetState(); +} + +class _AudYoFloComponentWidgetState extends State { + String dropdownValueProcess = 'None'; + + @override + Widget build(BuildContext context) { + AudYoFloBackendCache theBeCache = + Provider.of(context, listen: false); + AudYoFloDebugModel theDbgModel = + Provider.of(context, listen: false); + JvxComponentIdentification cpTpSelected = JvxComponentIdentification(); + + // ===================================================== + // Find the list of processes to be exposed in combo box + // ===================================================== + List? entriesProcs = ['None']; + theBeCache.availProcessDescriptions(entriesProcs); + //dropdownValueProcess = 'None'; + dropdownValueProcess = entriesProcs.elementAt(theDbgModel.idSelectProc + 1); + + return Selector( + selector: (context, beModel) => theBeCache.componentSection.ssUpdateId, + //shouldRebuild: (previous, next) => next ? false : true, + shouldRebuild: (previous, next) => (next != previous) ? true : false, + builder: (context, notifier, child) { + //FernLiveBackendBridge theBridge = new FernLiveBackendBridge(); + return Container( + color: Colors.white, + child: Column(children: [ + Flexible( + flex: 0, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Flexible(flex: 1, child: Text('Processes')), + Flexible( + flex: 4, + child: DropdownButton( + value: dropdownValueProcess, + icon: const Icon(Icons.arrow_downward), + isExpanded: true, + iconSize: 24, + elevation: 16, + style: const TextStyle(color: Colors.deepPurple), + underline: Container( + height: 2, + color: Colors.deepPurpleAccent, + ), + onChanged: (String? newValue) { + /* + theBeModel.updatePropertyViewer( + theDbgModel.idSelectCp + + )*/ + setState(() { + theDbgModel.idSelectCp = + JvxComponentIdentification(); + // theComponent = theDbgModel.idSelectCp + + theDbgModel.selectNewComponent( + theDbgModel.idSelectCp); + theDbgModel.idSelectProc = + entriesProcs.indexWhere( + (element) => element == newValue); + if (theDbgModel.idSelectProc >= 0) { + theDbgModel.idSelectProc--; + } + }); + }, + items: entriesProcs.map>( + (String value) { + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + ), + ), + Flexible(flex: 1, child: Text('Components')), + Flexible( + flex: 4, child: AudYoFloDropdownComponents()), + ])), + Flexible( + flex: 1, + child: AudYoFloPropertyGridWidget(), + ) + ])); + }); + //Container(color: Colors.yellow) + //]))) + } +} + +// =================================================================== +/* + * This would almost be realizable as a stateful widget BUT it is not since + * we need to trigger an update on itself when clicking another component. + */ +class AudYoFloDropdownComponents extends StatefulWidget { + AudYoFloDropdownComponents(); + + @override + State createState() { + return _AudYoFloDropdownWidgetStates(); + } +} + +class _AudYoFloDropdownWidgetStates extends State { + @override + Widget build(BuildContext context) { + AudYoFloBackendCache theBeCache = + Provider.of(context, listen: false); + AudYoFloDebugModel theDbgModel = + Provider.of(context, listen: false); + + List lstComponents = []; + lstComponents.clear(); + + // Add the "none" component + lstComponents.add(JvxComponentIdentification()); + theBeCache.findComponentsMatch(lstComponents, theDbgModel.idSelectProc); + + // If the component selected before is no longer available, here we have a mismatch + // Therefore, we check and correct the slection before we show it + theDbgModel.verifyComponentSelection(lstComponents); + + return LayoutBuilder(builder: (context, constraints) { + return SizedBox( + width: constraints.maxWidth, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Flexible( + flex: 4, + child: DropdownButton( + value: theDbgModel.idSelectCp, + icon: const Icon(Icons.arrow_downward), + isExpanded: true, + iconSize: 24, + elevation: 16, + style: const TextStyle(color: Colors.deepPurple), + underline: Container( + height: 2, + color: Colors.deepPurpleAccent, + ), + onChanged: (JvxComponentIdentification? newValue) { + if (newValue != null) { + // The setState is to update this combobox, the operation on the dbgModel + // will trigger the provider on the dbgModel to update the grid widget with all + // the properties + setState(() { + theDbgModel.selectNewComponent(newValue); + }); + } + }, + items: lstComponents + .map>( + (JvxComponentIdentification value) { + String showText = theBeCache.descriptionComponent(value); + return DropdownMenuItem( + value: value, + child: Text(showText), + ); + }).toList(), + ), + ), + Flexible( + flex: 1, + child: ElevatedButton( + style: ButtonStyle(backgroundColor: + MaterialStateProperty.resolveWith( + (Set states) { + if (states.contains(MaterialState.pressed)) + return Colors.grey; + return Colors.black; // Use the component's default. + }, + )), + child: const Text('Refresh'), + // color: Colors.black,q + //child: Icon(Icons.clear), + onPressed: () { + setState(() { + theBeCache.invalidatePropertiesComponent( + theDbgModel.idSelectCp, [], false, + invalidateAll: true); + theDbgModel.selectNewComponent(theDbgModel.idSelectCp); + }); + }), + ) + ], + )); + }); + } +} diff --git a/flutter/ayf_pack/lib/main-widgets/ayf_debug_widget.dart b/flutter/ayf_pack/lib/main-widgets/ayf_debug_widget.dart new file mode 100644 index 00000000..43017b06 --- /dev/null +++ b/flutter/ayf_pack/lib/main-widgets/ayf_debug_widget.dart @@ -0,0 +1,122 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import '../ayf_pack_local.dart'; + +class AudYoFloDebugWidget extends StatefulWidget { + const AudYoFloDebugWidget({Key? key}) : super(key: key); + + @override + _AudYoFloDebugWidgetState createState() => _AudYoFloDebugWidgetState(); +} + +class _AudYoFloDebugWidgetState extends State { + List lstText = ['a', 'b', 'c']; + + // Important stuff here: we need two controllers, one for the multiline + // textfield and one for the scroll function + final TextEditingController _controllerOut = TextEditingController(); + final ScrollController _scrollController = ScrollController(); + + @override + Widget build(BuildContext context) { + AudYoFloBackendCache theBeCache = + Provider.of(context, listen: false); + AudYoFloDebugModel theDbgModel = + Provider.of(context, listen: false); + + return LayoutBuilder(builder: (_, constraints) { + double h1 = 40; + double h2 = constraints.maxHeight - 40; + AudYoFloDebugModel theDbgModel = + Provider.of(context, listen: false); + + return Column(children: [ + Container( + height: h1, + width: constraints.maxWidth, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text('Debug'), + Checkbox( + checkColor: Colors.white, + activeColor: Colors.black, + //fillColor: MaterialStateProperty.resolveWith(getColor), + value: theDbgModel.dbgOut, + onChanged: (bool? value) { + setState(() { + theDbgModel.dbgOut = value!; + }); + }), + Text('Functionname'), + Checkbox( + checkColor: Colors.white, + activeColor: Colors.black, + value: theDbgModel.funcOut, + onChanged: (bool? value) { + setState(() { + theDbgModel.funcOut = value!; + }); + }), + ElevatedButton( + style: ButtonStyle(backgroundColor: + MaterialStateProperty.resolveWith( + (Set states) { + if (states.contains(MaterialState.pressed)) + return Colors.grey; + return Colors.black; // Use the component's default. + }, + )), + child: Text('Clear'), + // color: Colors.black,q + //child: Icon(Icons.clear), + onPressed: () { + theDbgModel.clearAll(); + }), + Text('Command'), + Container( + width: constraints.maxHeight / 2, + child: FocusScope(child: AudYoFloActiveTextField( + onEditingComplete: (String text) async { + // Return json expression but convert for display! + String transferResult = + await theBeCache.transferTextCommand(text, false); + theDbgModel.addLineOutConvert(transferResult); + }))), + ])), + Container( + height: h2, + child: Consumer(builder: (_, value, __) { + String txt = ''; + for (final txtLine in value.out_debug) { + txt += txtLine + '\n'; + } + _controllerOut.text = txt; + return DecoratedBox( + decoration: BoxDecoration( + color: Colors.grey[350], + border: Border.all( + color: Colors.grey, + width: 4, + ), + borderRadius: BorderRadius.circular(15)), + child: Padding( + padding: EdgeInsets.all(20), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + reverse: true, + controller: _scrollController, + child: TextField( + style: TextStyle(fontSize: 12), + keyboardType: TextInputType.text, + maxLines: null, + readOnly: true, + controller: _controllerOut, + )), + )); + }), + ) + ]); + }); + } +} diff --git a/flutter/ayf_pack/lib/main-widgets/ayf_fileinput_control_widget.dart b/flutter/ayf_pack/lib/main-widgets/ayf_fileinput_control_widget.dart new file mode 100644 index 00000000..6c5120c0 --- /dev/null +++ b/flutter/ayf_pack/lib/main-widgets/ayf_fileinput_control_widget.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import '../ayf_pack_local.dart'; +import 'package:provider/provider.dart'; +import 'package:collection/collection.dart'; + +class AudYoFloAudioDeviceFileInputSection { + double sizeIcon = 55; + AudYoFloAudioDeviceFileInputSection({required this.sizeIcon}); +} + +class AudYoFloFileInputControlWidget extends StatelessWidget { + const AudYoFloFileInputControlWidget(this.identT, this.textShowDrag, + {Key? key}) + : super(key: key); + final String textShowDrag; + final JvxComponentIdentification identT; + + @override + Widget build(BuildContext context) { + AudYoFloUiModel theUiModel = + Provider.of(context, listen: false); + + // Extract and forward the iconSize from the model + double sizeIcon = 55; + MapEntry? elm = theUiModel + .widgetSpecificConfigurations.entries + .firstWhereOrNull((element) => element.key == 'fileinput'); + if (elm != null) { + if (elm.value is AudYoFloAudioDeviceFileInputSection) { + sizeIcon = elm.value.sizeIcon; + } + } + + // File drop widget is massively depending on platform + Widget? fileDropWidget; + if (theUiModel.platformSpec != null) { + fileDropWidget = theUiModel.platformSpec! + .allocateFileDropWidget(identT, textShowDrag, sizeIcon); + } + return Consumer(builder: (context, notifier, child) { + return Column( + children: [ + Flexible( + flex: 1, + child: Padding( + padding: const EdgeInsets.all(8.0), child: fileDropWidget), + ), + Flexible(flex: 1, child: AudYoFloFileInputActiveDevicesWidget(identT)) + ], + ); + }); + } +} diff --git a/flutter/ayf_pack/lib/main-widgets/ayf_main_inner_content.dart b/flutter/ayf_pack/lib/main-widgets/ayf_main_inner_content.dart new file mode 100644 index 00000000..76d7eba9 --- /dev/null +++ b/flutter/ayf_pack/lib/main-widgets/ayf_main_inner_content.dart @@ -0,0 +1,148 @@ +import 'package:flutter/material.dart'; +//import 'package:flutter/src/scheduler/ticker.dart'; +import 'package:provider/provider.dart'; +import '../ayf_pack_local.dart'; + +class AudYoFloMainInnerContent extends StatefulWidget { + @override + _AudYoFloMainInnerContentStates createState() => + _AudYoFloMainInnerContentStates(); +} + +class _AudYoFloMainInnerContentStates extends State + with TickerProviderStateMixin { + late TabController tabController; + + @override + void initState() { + super.initState(); + tabController = TabController( + initialIndex: 0, + length: 6, + vsync: this, + ); + } + + @override + Widget build(BuildContext context) { + final Size size = MediaQuery.of(context).size; + + AudYoFloUiModel theModel = + Provider.of(context, listen: false); + // print('$size'); + + // Let us create the widgets to show up in the main window + + // + // The TabController must be re-created since the length parameter is final. + // We therefore copy the index to the initial Index + // https://stackoverflow.com/questions/46180919/pattern-for-updating-tabcontroller-length-from-child-widget + // + + // FIlter out the widget specific arguments + var args = theModel.platformSpec!.cmdArgs; + List argsFilteredFwd = []; + List argsFilteredWidgets = []; + bool foundEntry = false; + for (var arg in args) { + if (foundEntry) { + argsFilteredWidgets.add(arg); + } else { + if (arg == "--widget") { + foundEntry = true; + } else { + argsFilteredFwd.add(arg); + } + } + } + + Map entriesCfgWidgets = {}; + for (var elm in argsFilteredWidgets) { + var lstElms = elm.split('='); + if (lstElms.length == 2) { + entriesCfgWidgets[lstElms[0]] = lstElms[1]; + } + } + + theModel.platformSpec!.cmdArgs = argsFilteredFwd; + + // Clear the list before rebuilding it + theModel.clearTabAndViews(); + + // Rebuild it + theModel.createTabsAndViews(entriesCfgWidgets); + // var subWidgets = CreateUiElementsTabBar(); + // subWidgets.createTabsAndViews(theModel); + + int index = tabController.index; + if (index >= theModel.allocatedTabViews.length) { + index = 0; + } + tabController = TabController( + initialIndex: tabController.index, + length: theModel.allocatedTabViews.length, + vsync: this, + ); + return NotificationListener( + onNotification: (SizeChangedLayoutNotification notification) { + return true; + }, + child: Center( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Container( + child: SingleChildScrollView( + child: Column(children: [ + SizedBox( + height: tabController.length * + theModel.navButtonAreaHeightMain + + theModel.navButtonAreaHeightSpaceMain, + width: theModel.navButtonAreaWidthMain, + child: RotatedBox( + quarterTurns: 1, + child: TabBar( + labelColor: + Colors.black, //Color.fromRGBO(4, 2, 46, 1), + labelStyle: const TextStyle( + fontStyle: FontStyle.normal, + fontWeight: FontWeight.bold), + unselectedLabelStyle: const TextStyle( + fontStyle: FontStyle.italic, + fontWeight: FontWeight.normal), + // labelStyle: theme.textTheme.headline1, + indicatorColor: Color.fromRGBO(48, 28, 46, 10), + unselectedLabelColor: Colors.grey, + controller: tabController, + tabs: theModel.allocatedTabs, + ))) + ]), + )), + Container( + width: size.width - theModel.navButtonAreaWidthMain, + child: TabBarView( + + // https://stackoverflow.com/questions/51518393/disable-swiping-tabs-in-tabbar-flutter + physics: const NeverScrollableScrollPhysics(), + controller: tabController, + children: theModel.allocatedTabViews), + ) + ]))); + } +} + +/* +class FernliveTickerProvider implements TickerProvider { + @override + Ticker createTicker(TickerCallback onTick) { + // TODO: implement createTicker + throw UnimplementedError(); + } + + void onTickerReport(Duration elapsed) {} +} +*/ + + + diff --git a/flutter/ayf_pack/lib/main-widgets/ayf_main_layout.dart b/flutter/ayf_pack/lib/main-widgets/ayf_main_layout.dart new file mode 100644 index 00000000..55d6474d --- /dev/null +++ b/flutter/ayf_pack/lib/main-widgets/ayf_main_layout.dart @@ -0,0 +1,126 @@ +import 'package:flutter/services.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import '../ayf_pack_local.dart'; + +import './ayf_app_bar.dart'; +import './ayf_bottom_app_bar.dart'; +import './ayf_upper_content.dart'; +import './ayf_bottom_content.dart'; +import './ayf_main_inner_content.dart'; + +// https://medium.com/@dvargahali/dart-2-conditional-imports-update-16147a776aa8 +// We would need conditional imports which are not supported. Hence, do it +// manually. + +class AudYoFloMainLayout extends StatelessWidget { + final Size size; + + //late FocusNode focusNode; + AudYoFloMainLayout({required this.size /*, required this.focusNode*/}); + + @override + Widget build(BuildContext context) { + AudYoFloUiModel theUiModel = + Provider.of(context, listen: false); + AudYoFloBackendCache theBeCache = + Provider.of(context, listen: false); + + List cwidgets = []; + Widget? upper; + Widget inner; + Widget? bottom; + + double computeSizeInner = 20; + + PreferredSizeWidget? myAppBar; + + Widget? myBottomBar; + if (theUiModel.addUpperNavBar) { + myAppBar = AudYoFloAppBar( + context, + theUiModel.textApp, + theUiModel.imageNameApp, + theUiModel.imageWScaleApp, + theUiModel.imageOAScaleApp, + theUiModel.heightUpperNavBar, + theBeCache); + + computeSizeInner += theUiModel.heightUpperNavBar; + } + + if (theUiModel.addBottomNavBar) { + if (theUiModel.allocateBottomAppBar != null) { + myBottomBar = theUiModel.allocateBottomAppBar!(); + } else { + myBottomBar = AudYoFloBottomAppBar(); + } + computeSizeInner += theUiModel.heightBottomNavBar; + } + // ===================================================== + + if (theUiModel.addUpper) { + upper = Container( + width: size.width, + height: theUiModel.heightUpper, + child: LayoutBuilder(builder: (context, constraints) { + return AudYoFloUpperContent(); + })); + computeSizeInner += theUiModel.heightUpper; + } + + if (theUiModel.addBottom) { + bottom = Container( + width: size.width, + height: theUiModel.heightBottom, + child: LayoutBuilder(builder: (context, constraints) { + return AudYoFloBottomContent(); + })); + computeSizeInner += theUiModel.heightBottom; + } + + inner = Container( + width: size.width, + height: size.height - computeSizeInner, + child: LayoutBuilder(builder: (context, constraints) { + return AudYoFloMainInnerContent(); + })); + + if (upper != null) cwidgets.add(upper); + cwidgets.add(inner); + if (bottom != null) cwidgets.add(bottom); + + // Here, we have platform specific code!! + if (theUiModel.platformSpec != null) { + theUiModel.platformSpec!.prepareClose(context); + } + + return Scaffold( + appBar: myAppBar, + bottomNavigationBar: myBottomBar, + body: Shortcuts( + shortcuts: const { + SingleActivator(LogicalKeyboardKey.space, control: true): + SpaceIntent(), + }, + child: Actions( + actions: { + SpaceIntent: CallbackAction(onInvoke: (intent) { + theBeCache.triggerToggleSequencer(); + }) + }, + child: FocusScope( + autofocus: true, + child: Container( + child: Column( + children: cwidgets, + )), + ), + ), + )); + } +} + +class SpaceIntent extends Intent { + const SpaceIntent(); +} diff --git a/flutter/ayf_pack/lib/main-widgets/ayf_main_widget.dart b/flutter/ayf_pack/lib/main-widgets/ayf_main_widget.dart new file mode 100644 index 00000000..88741c23 --- /dev/null +++ b/flutter/ayf_pack/lib/main-widgets/ayf_main_widget.dart @@ -0,0 +1,207 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:path/path.dart' as path; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'dart:io' show Platform; +import '../ayf_pack_local.dart'; +// import 'package:ayfcorepack/ayfcorepack.dart'; +import 'package:collection/collection.dart'; + +import '../routes/ayf_routes.dart'; + +//============================================================ +// Define a callback that is used to hookup functions on a periodic basis +//============================================================ + +//============================================================ +// Create the main FernLive Widget +// This widget is stateful, hence, check out the stateful part. +//============================================================ + +class AudYoFloMainWidget extends StatefulWidget { + final AudYoFloPlatformSpecific platformSpecific; + + // https://stackoverflow.com/questions/54279223/flutter-default-assignment-of-list-parameter-in-a-constructor + const AudYoFloMainWidget({Key? key, required this.platformSpecific}) + : super(key: key); + + // Return the stateful part + @override + _AudYoFloMainWidgetStates createState() => _AudYoFloMainWidgetStates(); +} + +//Stateful part of this widget +class _AudYoFloMainWidgetStates extends State + with WidgetsBindingObserver + implements AudYoFloPeriodicCallbacks { +// ====================================================================== + + // WindowsFernNative pltf_inst = WindowsFernNative(); + + // String _windowSize = 'Unknown'; + // This is the main timer with a fast and a slow callback + // In each callback, a notifier is used to inform everyone interested about + // the callback + AudYoFloHostTimer? hTimer; + + // This is the configuration module for the algorithm + + // This object must be constructed here since it is used in initState + // AudioFlowBackendBridge theBEBridge = AudioFlowBackendBridge(); + AyfHost? theBeAdapter; //AyfNativeAudioHost.instance; + + // This is to grant access to the periodic notifiers - but don't listen, we are the only one to write it + AudYoFloPeriodicNotifier? notifierPeriodic; + AudYoFloBackendCache? notifierBeCache; + AudYoFloDebugModel? notifierDbgModel; + AudYoFloUiModel? uiModel; + + // ======================================================== + // Build function to create the widget. Note: The initState + // function is always called before the Buid function. + // ======================================================== + @override + Widget build(BuildContext context) { + // Access to the notifier object + notifierPeriodic = + Provider.of(context, listen: false); + notifierBeCache = Provider.of(context, listen: false); + notifierDbgModel = Provider.of(context, listen: false); + uiModel = Provider.of(context, listen: false); + + /* + if (updateDueToRestart) { + if (uiModel!.initRoute != "/") { + Navigator.pushNamed(context, uiModel!.initRoute); + } + } + */ + + // If we pass by at this positon for the first time adopt the audioflow + // handles and store in states + if (theBeAdapter == null) { + // Attach the platform specific part in uiModel + assert(uiModel != null); + uiModel!.platformSpec = widget.platformSpecific; + theBeAdapter = widget.platformSpecific.referenceHost(); + + /* + * Here, we run the main component initialization functions in an async function. + * + * We need to delay the initialization to allow all stateful widgets to + * connect to the bridge within the initState function - and the order of initState + * is unpredictable + * https://oleksandrkirichenko.com/blog/delayed-code-execution-in-flutter/ + * + * + */ + + if (uiModel!.initRoute == "/") { + // initializeSubSystem starts the backend engine. We might couple it immediately OR we + // may expect some input before connecting - depending on the routes. If we start with the + // main route, we immediately couple the backend. + uiModel!.platformSpec! + .initializeSubSystem(notifierBeCache, uiModel, notifierDbgModel); + } + + // ================================================================= + // Start the class to realize the periodic callbacks. It receives the + // two callbacks as functional arguments + hTimer = AudYoFloHostTimer(callbacks: this); + + // Actually start the timers + hTimer?.initTimer(); + } + + /* + switch(uiModel.startRoute) + { + case '' + } + */ + return MaterialApp( + initialRoute: uiModel!.initRoute, + onGenerateRoute: AudYoFloRouteGenerator.generateRoute, + ); + } + + // ======================================================== + // Initialization function to initialize the states + // ======================================================== + @override + void initState() { + super.initState(); + + // Add this object as an observer for events - do not know exactly which + // events right now. I think it is for didChangeAppLifecycleState + WidgetsBinding.instance.addObserver(this); + + /* + * I have no idea why the shutdown tool can not live here. If I + * try to open a dialog from this class it just does not show. + * I think it is because this class is not connected to the widget tree + * as the MaterialApp is only the child of this instance. + * Therefore I have moved this part into the ayf_main_layout widget! + * + FlutterWindowClose.setWindowShouldCloseHandler(() async { + theBEBridge.triggeredClose(); + return true; + }); + */ + } + + // ======================================================== + // The opposite of the init_state function called at end of lifetime + // ======================================================== + @override + void dispose() { + // stop the timer + hTimer?.termTimer(); + + // Terminate the native bridge + theBeAdapter?.terminate(); + + super.dispose(); + } + + // ======================================================== + // This function is important to detect lifecycle changes + // At the moment, it does not even detect the "stop" button :-( + // ======================================================== + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + switch (state) { + case AppLifecycleState.resumed: + print("Hallo1"); + break; + case AppLifecycleState.paused: + print("Hallo2"); + break; + default: + break; + } + } + + // ======================================================== + // Callbacks for the fast and the slow callback + // ======================================================== + @override + int reportFast() { + notifierPeriodic?.fast = true; + notifierPeriodic?.cntFast++; + notifierPeriodic?.notify(); + return uiModel!.fastRefreshPerMsecs.value.toInt(); + } + + @override + int reportSlow() { + theBeAdapter?.periodicTriggerSlow(); + notifierPeriodic?.fast = false; + notifierPeriodic?.cntSlow++; + notifierPeriodic?.notify(); + return uiModel!.slowRefreshPerMsecs.value.toInt(); + //print("Hello slow"); + } + + // Run the system init routines in an async call! +} diff --git a/flutter/ayf_pack/lib/main-widgets/ayf_mixer_widget.dart b/flutter/ayf_pack/lib/main-widgets/ayf_mixer_widget.dart new file mode 100644 index 00000000..dafdb89c --- /dev/null +++ b/flutter/ayf_pack/lib/main-widgets/ayf_mixer_widget.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import '../ayf_pack_local.dart'; + +class AudYoFloMixerWidget extends StatelessWidget { + final String fNameMixerImage; + AudYoFloMixerWidget(this.fNameMixerImage); + @override + Widget build(BuildContext context) { + AudYoFloUiModel theModel = + Provider.of(context, listen: false); + return Container( + child: Row(children: [ + Container( + child: Expanded( + child: Column(children: [ + Expanded( + child: Row( + children: [ + AudYoFloMixerSliderWithSize( + orientation: Axis.vertical, + longerSide: 400, + shorterSide: 75) + ], + ), + ), + Padding(padding: EdgeInsets.all(16.0)) + ]), + ), + ), + Container(color: Colors.green) + ])); + } +} diff --git a/flutter/ayf_pack/lib/main-widgets/ayf_play_pannel.dart b/flutter/ayf_pack/lib/main-widgets/ayf_play_pannel.dart new file mode 100644 index 00000000..0e4e609d --- /dev/null +++ b/flutter/ayf_pack/lib/main-widgets/ayf_play_pannel.dart @@ -0,0 +1,128 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import '../ayf_pack_local.dart'; + +class AudYoFloPlaypannel extends StatelessWidget { + @override + Widget build(BuildContext context) { + AudYoFloUiModel theUiModel = + Provider.of(context, listen: false); + AudYoFloBackendCache theBeCache = + Provider.of(context, listen: false); + return SizedBox( + height: theUiModel.heightBottomNavBar, + child: Container( + color: Colors.grey, + child: Container( + height: theUiModel.heightBottomNavBar * 0.8, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + DecoratedBox( + decoration: BoxDecoration( + color: Colors.blueGrey, + border: Border.all(), + borderRadius: BorderRadius.circular(5), + boxShadow: [ + BoxShadow( + color: Colors.white, + offset: Offset(-2, -2), + blurRadius: 3), + BoxShadow( + color: Colors.black, + offset: Offset(10, 10), + blurRadius: 8) + ]), + child: Padding( + padding: EdgeInsets.all(5), + child: Row(children: [ + SizedBox( + width: theUiModel.heightBottomNavBar, + height: theUiModel.heightBottomNavBar / 2, + child: FittedBox( + child: FloatingActionButton( + heroTag: UniqueKey(), + child: Icon(Icons.fiber_manual_record, + size: theUiModel.heightBottomNavBar / + 2), + onPressed: () {}, + backgroundColor: Colors.black))), + SizedBox( + width: theUiModel.heightBottomNavBar * 2, + height: theUiModel.heightBottomNavBar * 0.7, + child: FittedBox( + child: Selector( + selector: (context, notifyHandle) { + return notifyHandle + .sequencerSection.ssUpdateId; + }, + //shouldRebuild: (previous, next) => next ? false : true, + shouldRebuild: (previous, next) => + (next != previous) ? true : false, + builder: (context, notifier, child) { + jvxSequencerStatusEnum stat = theBeCache + .sequencerSection.seqStatus; + + if (stat == + jvxSequencerStatusEnum + .JVX_SEQUENCER_STATUS_NONE) { + if (theBeCache.sequencerSection + .processesReady) { + return FloatingActionButton( + heroTag: UniqueKey(), + child: Icon(Icons.play_arrow, + size: theUiModel + .heightBottomNavBar / + 2), + onPressed: () { + theBeCache + .triggerStartSequencer(); + }, + backgroundColor: Colors.black); + } else { + return Tooltip( + message: theBeCache + .sequencerSection + .reasonIfNot, + child: FloatingActionButton( + heroTag: UniqueKey(), + child: Icon(Icons.cancel, + size: theUiModel + .heightBottomNavBar / + 2), + onPressed: () {}, + backgroundColor: + Colors.black)); + } + } else { + return FloatingActionButton( + heroTag: UniqueKey(), + child: Icon(Icons.stop, + size: theUiModel + .heightBottomNavBar / + 2), + onPressed: () { + theBeCache + .triggerStopSequencer(); + }, + backgroundColor: Colors.black); + } + }))), + SizedBox( + width: theUiModel.heightBottomNavBar, + height: theUiModel.heightBottomNavBar / 2, + child: FittedBox( + child: FloatingActionButton( + heroTag: UniqueKey(), + child: Icon(Icons.fiber_manual_record, + size: theUiModel.heightBottomNavBar / + 2), + onPressed: () {}, + backgroundColor: Colors.black))), + ]))) + ], + ), + ), + )); + } +} diff --git a/flutter/ayf_pack/lib/main-widgets/ayf_upper_content.dart b/flutter/ayf_pack/lib/main-widgets/ayf_upper_content.dart new file mode 100644 index 00000000..cb97caf8 --- /dev/null +++ b/flutter/ayf_pack/lib/main-widgets/ayf_upper_content.dart @@ -0,0 +1,8 @@ +import 'package:flutter/material.dart'; + +class AudYoFloUpperContent extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Container(color: Colors.lightBlue); + } +} diff --git a/flutter/ayf_pack/lib/mixer-widgets/ayf_mixer_slider.dart b/flutter/ayf_pack/lib/mixer-widgets/ayf_mixer_slider.dart new file mode 100644 index 00000000..47d20819 --- /dev/null +++ b/flutter/ayf_pack/lib/mixer-widgets/ayf_mixer_slider.dart @@ -0,0 +1,557 @@ +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:another_xlider/another_xlider.dart'; +import 'package:provider/provider.dart'; +import '../ayf_pack_local.dart'; + +/* + List labels = List.generate(11, (idx) { + return AyfLabelPosition(idx.toString(), idx / 10); + }); + List positionMarksShort = List.generate(101, (idx) { + return idx / 100; + }); + List positionMarksLong = List.generate(11, (idx) { + return idx / 10; + }); + */ +class AudYoFloMixerSlider extends StatefulWidget { + final double currentGainValue; + final Axis orientation; + final int numChans; + final Color backgroundColor; + final IconData iconDat; + final int numCnt; + final double ftSizeMax; + final double widthMaxLine; + final String title; + final List? labels; + final List? positionMarksShort; + final List? positionMarksLong; + + final void Function(double gainVal)? onSliderMoved; + final void Function(double gainVal)? onSliderReleased; + final void Function(List avrgAndMax, int idIdent)? onUpdatePeriodic; + final String Function(String proposed)? onSliderTooltipFormat; + + final List idIdent; + + AudYoFloMixerSlider( + {this.orientation = Axis.vertical, + this.currentGainValue = 100, + this.backgroundColor = Colors.white, + this.numChans = 1, + this.iconDat = Icons.mic, + this.numCnt = 100, + this.ftSizeMax = 16, + this.widthMaxLine = 6, + this.labels, + this.positionMarksShort, + this.positionMarksLong, + this.onSliderMoved, + this.onSliderReleased, + this.onUpdatePeriodic, + this.onSliderTooltipFormat, + this.idIdent = const [], + this.title = ""}) { + // print('Gain on Reset: $currentGainValue'); + } + + @override + _AudYoFloMixerSliderState createState() => _AudYoFloMixerSliderState(); +} + +class _AudYoFloMixerSliderState extends State { + double gainShow = 0; + bool mouseOverShow = false; + int oldHashCodeWidget = -1; + List currentAvrgValues = []; + List currentMaxValues = []; + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + // Take over the latest gain only if a new widget was created since + // there may be another value in the backend + if (widget.hashCode != oldHashCodeWidget) { + gainShow = widget.currentGainValue; + oldHashCodeWidget = widget.hashCode; + if (widget.numChans != currentAvrgValues.length) { + currentAvrgValues = List.filled(widget.numChans, 0); + currentMaxValues = List.filled(widget.numChans, 0); + } + // print('Reset gain'); + } + + // Use the layout builder to find out the requested size + return LayoutBuilder(builder: (context, constraints) { + bool disableTooltip = true; + double longerSide = constraints.maxHeight; + double shorterSide = constraints.maxWidth; + bool rtl = true; + + if (widget.onSliderTooltipFormat != null) { + disableTooltip = false; + } + + if (widget.orientation == Axis.horizontal) { + rtl = false; + longerSide = constraints.maxWidth; + shorterSide = constraints.maxHeight; + } + + double handlerWidth = shorterSide * 3 / 4; + double handlerHeight = shorterSide / 2; + double iconSize = handlerHeight; + if (widget.orientation == Axis.horizontal) { + handlerHeight = shorterSide * 3 / 4; + handlerWidth = shorterSide / 2; + iconSize = handlerWidth; + } + + Widget? hatchWidget; + double opacitySlider = 0.2; // do not show + if (mouseOverShow) { + opacitySlider = 1; + hatchWidget = Icon(widget.iconDat, size: iconSize * 0.75); + } else { + hatchWidget = Container(color: Colors.white.withOpacity(0)); + } + + return MouseRegion( + onEnter: (event) { + if (mouseOverShow == false) { + setState(() { + mouseOverShow = true; + }); + } + }, + onExit: (event) { + if (mouseOverShow == true) { + setState(() { + mouseOverShow = false; + }); + } + }, + child: Stack(fit: StackFit.expand, children: [ + Container( + decoration: BoxDecoration( + color: widget.backgroundColor, + borderRadius: BorderRadius.all(Radius.circular(10)), + border: Border.all(color: Colors.black, width: 2))), + Selector( + selector: (context, perNotify) => perNotify.fast, + //shouldRebuild: (previous, next) => next ? false : true, + shouldRebuild: (previous, next) => next ? true : false, + builder: (context, notifier, child) { + if (widget.onUpdatePeriodic != null) { + for (int idx = 0; idx < currentAvrgValues.length; idx++) { + int idIdent = -1; + if (idx < widget.idIdent.length) { + idIdent = widget.idIdent[idx]; + } + List valExchg = [ + currentAvrgValues[idx], + currentMaxValues[idx] + ]; + + // here we get the current level values from the caller + widget.onUpdatePeriodic!(valExchg, idIdent); + currentAvrgValues[idx] = valExchg[0]; + currentMaxValues[idx] = valExchg[1]; + } + } + //FernLiveBackendBridge theBridge = new FernLiveBackendBridge(); + return CustomPaint( + //size: Size(200, 200), + painter: SliderPainter( + widget.numChans, + widget.numCnt, + currentAvrgValues, + currentMaxValues, + widget.orientation, + widget.ftSizeMax, + widget.labels, + widget.positionMarksShort, + widget.positionMarksLong, + widget.widthMaxLine, + title: widget.title), + ); + }), + FlutterSlider( + tooltip: FlutterSliderTooltip( + disabled: disableTooltip, format: widget.onSliderTooltipFormat), + // numberFormat: intl.NumberFormat(), + handlerWidth: handlerWidth, + handlerHeight: handlerHeight, + values: [gainShow], + max: widget.numCnt.toDouble(), + min: 0, + axis: widget.orientation, + rtl: rtl, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + shape: BoxShape.rectangle, + // color: Colors.yellow, + //border: Border.all(width: 3, color: Colors.red), + ), + trackBar: FlutterSliderTrackBar( + //activeTrackBarHeight: trackHeight, + //inactiveTrackBarHeight: trackHeight, + activeDisabledTrackBarColor: Colors.white, + inactiveDisabledTrackBarColor: Colors.white, + inactiveTrackBar: BoxDecoration( + borderRadius: BorderRadius.circular(5), + shape: BoxShape.rectangle, + color: Colors.white.withOpacity(0), + //border: Border.all(width: 1, color: Colors.black), + ), + activeTrackBar: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: Colors.white.withOpacity(0), + shape: BoxShape.rectangle, + //border: Border.all(width: 1, color: Colors.black), + ), + ), + handler: FlutterSliderHandler( + //opacity: opacitySlider, + decoration: BoxDecoration( + color: Colors.white.withOpacity(opacitySlider), + shape: BoxShape.rectangle, + border: Border.all( + width: 1, + color: Colors.black.withOpacity(opacitySlider))), + child: hatchWidget), + rightHandler: FlutterSliderHandler( + child: Icon( + Icons.chevron_left, + color: Colors.red, + size: 24, + ), + ), + /* + * We do not use the hatchmark: it was almost impossible to place it cleanly + hatchMark: FlutterSliderHatchMark( + linesDistanceFromTrackBar: 0, // 0 means lower bound + labelsDistanceFromTrackBar: + shiftLabs, // 0 means that labels are in middle + displayLines: false, + // density: 0.5, // means 50 lines, from 0 to 100 percent + // labels: hatchLabels, + ),*/ + onDragCompleted: (handlerIndex, lowerValue, upperValue) { + // print('New value on Release: $lowerValue'); + if (widget.onSliderReleased != null) { + widget.onSliderReleased!(lowerValue); + } + setState(() { + gainShow = lowerValue; + }); + }, + onDragging: (int x, lowerValue, upperValue) { + // print('New value on Move: $lowerValue'); + if (widget.onSliderMoved != null) { + widget.onSliderMoved!(lowerValue); + } + setState(() { + gainShow = lowerValue; + }); + }, + ) + ]), + ); + }); + } +} + +// ========================================================================== +// Painter for main are +// ========================================================================== +class SliderPainter extends CustomPainter { + final List currentAvrgValue; + final List currentMaxValue; + Random randInst = Random(); + Axis orientation; + final double ftSizeMax; + final int numChans; + final int numCnt; + final widthMaxLine; + final String title; + + List? labels; + List? positionMarksShort; + List? positionMarksLong; + + SliderPainter( + this.numChans, + this.numCnt, + this.currentAvrgValue, + this.currentMaxValue, + this.orientation, + this.ftSizeMax, + this.labels, + this.positionMarksShort, + this.positionMarksLong, + this.widthMaxLine, + {this.title = ""}); + + @override + void paint(Canvas canvas, Size size) { + Offset p1; + Offset p2; + Offset offs = Offset(0, 0); + + double longAxisSize = size.height; + double shortAxisSize = size.width; + double widthLevelWidget = size.width / 4; + if (orientation == Axis.horizontal) { + widthLevelWidget = size.height / 4; + longAxisSize = size.width; + shortAxisSize = size.height; + } + double deltaInner = longAxisSize / 20; + double ftSize = widthLevelWidget; + ftSize = min(ftSize, ftSizeMax); + // currentValue = 0.5; + + double shiftLine = widthLevelWidget / numChans; + double devDist = shiftLine / 2 * 0.9; + double offsetLevelWidget = -shiftLine * (numChans - 1) * 0.5; + + for (int idxChan = 0; idxChan < numChans; idxChan++) { + AlignmentGeometry alignStart = Alignment.bottomCenter; + AlignmentGeometry alignStop = Alignment.topCenter; + + Rect myRect = Rect.fromLTRB( + shortAxisSize / 2 - devDist + offsetLevelWidget, + deltaInner, + shortAxisSize / 2 + devDist + offsetLevelWidget, + longAxisSize - deltaInner, + ); + + double val = 0; + if (idxChan < currentAvrgValue.length) { + val = currentAvrgValue[idxChan] / numCnt; + } + val = min(1, val); + + if (orientation == Axis.vertical) { + val = 1 - val; + } + + Rect myRectCover = Rect.fromLTRB( + shortAxisSize / 2 - devDist + offsetLevelWidget, + deltaInner, + shortAxisSize / 2 + devDist + offsetLevelWidget, + deltaInner + (val) * (longAxisSize - 2 * deltaInner)); + + if (orientation == Axis.horizontal) { + alignStart = Alignment.centerLeft; + alignStop = Alignment.centerRight; + myRect = xySwapRect(myRect, false, 0); + myRectCover = xySwapRect(myRectCover, true, longAxisSize); + } + + // The two painters with gradient bgrd and black foreground + final paintG = Paint() + //..color = Colors.red + ..shader = LinearGradient( + colors: [Colors.green, Colors.red], + begin: alignStart, + end: alignStop, + // transform: GradientTransform + ).createShader(myRect); + final paintC = Paint()..color = Colors.black; + + // Draw both rects + canvas.drawRect(myRect, paintG); + canvas.drawRect(myRectCover, paintC); + + val = 0; + if (idxChan < currentMaxValue.length) { + val = currentMaxValue[idxChan] / numCnt; + } + val = min(1, val); + if (orientation == Axis.vertical) { + val = 1 - val; + } + + myRect = Rect.fromLTRB( + shortAxisSize / 2 - devDist + offsetLevelWidget, + deltaInner + + (val) * (longAxisSize - 2 * deltaInner) - + widthMaxLine / 2, + shortAxisSize / 2 + devDist + offsetLevelWidget, + deltaInner + + (val) * (longAxisSize - 2 * deltaInner) + + widthMaxLine / 2); + if (orientation == Axis.horizontal) { + myRect = xySwapRect(myRect, false, 0); + } + canvas.drawRect(myRect, paintG); + offsetLevelWidget += shiftLine; + } + + double startPosiMarker = shortAxisSize * 2 / 3; + double stopPosiMarker = shortAxisSize * 3 / 4; + + startPosiMarker = shortAxisSize / 2 - devDist / 2; + stopPosiMarker = shortAxisSize / 2 + devDist / 2; + if (positionMarksShort != null) { + for (var elm in positionMarksShort!) { + final paint = Paint() + ..color = Colors.white + ..strokeWidth = 0.3; + p1 = Offset(startPosiMarker, + deltaInner + (longAxisSize - 2 * deltaInner) * elm / numCnt); + p2 = Offset(stopPosiMarker, + deltaInner + (longAxisSize - 2 * deltaInner) * elm / numCnt); + if (orientation == Axis.horizontal) { + p1 = xySwapOffset(p1); + p2 = xySwapOffset(p2); + } + canvas.drawLine(p1, p2, paint); + } + } + + stopPosiMarker = shortAxisSize * 5 / 6; + startPosiMarker = shortAxisSize / 2 - devDist * 0.8; + stopPosiMarker = shortAxisSize / 2 + devDist * 0.8; + + if (positionMarksLong != null) { + for (var elm in positionMarksLong!) { + final paint = Paint() + ..color = Colors.white + ..strokeWidth = 0.8; + + p1 = Offset(startPosiMarker, + deltaInner + (longAxisSize - 2 * deltaInner) * elm / numCnt); + p2 = Offset(stopPosiMarker, + deltaInner + (longAxisSize - 2 * deltaInner) * elm / numCnt); + if (orientation == Axis.horizontal) { + p1 = xySwapOffset(p1); + p2 = xySwapOffset(p2); + } + canvas.drawLine(p1, p2, paint); + } + } + + // ============================================================== + // Draw text labels + // ============================================================== + double offetLabels = shortAxisSize * 1 / 6; + if (title.isNotEmpty) { + offs = Offset( + offetLabels, deltaInner + 0.5 * (longAxisSize - 2 * deltaInner)); + if (orientation == Axis.horizontal) { + offs = xySwapOffset(offs); + } + localDrawText(title, canvas, offs, ftSize, fontStyle: FontStyle.normal); + } + offetLabels = shortAxisSize * 3 / 4; + if (labels != null) { + for (var elml in labels!) { + double posiEff = elml.posi / numCnt.toDouble(); + if (orientation != Axis.horizontal) { + posiEff = (1 - posiEff); + } + offs = Offset(offetLabels, + deltaInner + posiEff * (longAxisSize - 2 * deltaInner)); + + if (orientation == Axis.horizontal) { + offs = xySwapOffset(offs); + } + localDrawText(elml.txt, canvas, offs, ftSize); + } + } + } + + Offset xySwapOffset(Offset val) { + return Offset(val.dy, val.dx); + } + + Rect xySwapRect(Rect val, bool lrSwap, double longAxisSize) { + if (lrSwap) + return Rect.fromLTRB( + val.bottom, val.left, longAxisSize - val.top, val.right); + return Rect.fromLTRB(val.top, val.left, val.bottom, val.right); + } + + @override + bool shouldRepaint(SliderPainter old) { + // currentValue = randInst.nextDouble(); + for (int idx = 0; idx < numChans; idx++) { + if (old.currentAvrgValue[idx] != currentAvrgValue[idx]) { + return true; + } + + if (old.currentMaxValue[idx] != currentMaxValue[idx]) { + return true; + } + } + + return true; + } + + void localDrawText(String lab, Canvas canvas, Offset offs, double ftSize, + {FontStyle fontStyle = FontStyle.italic, double rotate = 0}) { + Paint pt = Paint(); + pt.color = Colors.white; + double ww = 0; + double hh = 0; + Color colText = Colors.black; + + FontWeight ftW = FontWeight.normal; + + // https://stackoverflow.com/questions/49408146/drawing-text-in-a-specific-spot-using-flutter + canvas.save(); + //canvas.translate(100.0, 100.0); + canvas.rotate(rotate); + + // https://stackoverflow.com/questions/68984666/the-argument-type-textdirection-cant-be-assigned-to-the-parameter-type-textd + TextSpan span1 = TextSpan( + style: new TextStyle( + color: colText, + fontWeight: ftW, + fontStyle: fontStyle, + fontSize: ftSize), + text: lab); + TextPainter tp1 = new TextPainter( + text: span1, + textAlign: TextAlign.center, + textDirection: TextDirection.ltr); + tp1.layout(); + + ww = tp1.width; + hh = tp1.height; + + double xx = 0; + double yy = 0; + xx = offs.dx - ww / 2; + yy = offs.dy - hh / 2; + + Offset drawHere = Offset(xx, yy); + //Rect textBox = Rect.fromLTWH(xx, offs.dy, ww, hh).inflate(ref.deflateInner); + //canvas.drawRect(textBox, pt); + + // double yy = offs.dy + ref.deflateInner / 2; + //offs = Offset(xx, yy); + tp1.paint(canvas, drawHere); + canvas.restore(); + } +} + +class AyfLabelPosition { + String txt; + double posi; + AyfLabelPosition(this.txt, this.posi); +} diff --git a/flutter/ayf_pack/lib/mixer-widgets/ayf_mixer_slider_with_size.dart b/flutter/ayf_pack/lib/mixer-widgets/ayf_mixer_slider_with_size.dart new file mode 100644 index 00000000..f9cb36a1 --- /dev/null +++ b/flutter/ayf_pack/lib/mixer-widgets/ayf_mixer_slider_with_size.dart @@ -0,0 +1,108 @@ +import 'package:flutter/material.dart'; +import 'ayf_mixer_slider.dart'; + +class AudYoFloMixerSliderWithSize extends StatelessWidget { + final double longerSide; + final double shorterSide; + + final double currentGainValue; + final Axis orientation; + final int numChans; + final Color backgroundColor; + final IconData iconDat; + final int numCnt; + final double gainShow; + final double ftSizeMax; + final String title; + final List? labels; + final List? positionMarksShort; + final List? positionMarksLong; + final List idIdent; + + final void Function(double gainVal)? onSliderMoved; + final void Function(double gainVal)? onSliderReleased; + final void Function(List avrgMax, int idIdent)? onUpdatePeriodic; + final String Function(String proposed)? onSliderTooltipFormat; + + AudYoFloMixerSliderWithSize( + {this.orientation = Axis.vertical, + this.longerSide = 400, + this.shorterSide = 75, + this.currentGainValue = 10, + this.backgroundColor = Colors.white, + this.numChans = 1, + this.iconDat = Icons.mic, + this.numCnt = 100, + this.gainShow = 100, + this.ftSizeMax = 16, + this.labels, + this.positionMarksShort, + this.positionMarksLong, + this.onSliderMoved, + this.onSliderReleased, + this.onUpdatePeriodic, + this.onSliderTooltipFormat, + this.idIdent = const [], + this.title = ""}); + + @override + Widget build(BuildContext context) { + double width = shorterSide; + double height = longerSide; + + if (orientation == Axis.horizontal) { + width = longerSide; + height = shorterSide; + } + if (shorterSide > 0) { + return SizedBox( + width: width, + height: height, + child: AudYoFloMixerSlider( + orientation: orientation, + currentGainValue: currentGainValue, + backgroundColor: backgroundColor, + numChans: numChans, + iconDat: iconDat, + numCnt: numCnt, + ftSizeMax: ftSizeMax, + labels: labels, + positionMarksShort: positionMarksShort, + positionMarksLong: positionMarksLong, + onSliderMoved: onSliderMoved, + onSliderReleased: onSliderReleased, + onUpdatePeriodic: onUpdatePeriodic, + onSliderTooltipFormat: onSliderTooltipFormat, + idIdent: idIdent, + title: title, + )); + } else { + return LayoutBuilder(builder: (context, constraints) { + Axis axis = Axis.vertical; + if (constraints.maxWidth > constraints.maxHeight) { + axis = Axis.horizontal; + } + return SizedBox( + width: constraints.maxWidth, + height: constraints.maxHeight, + child: AudYoFloMixerSlider( + orientation: orientation, + currentGainValue: currentGainValue, + backgroundColor: backgroundColor, + numChans: numChans, + iconDat: iconDat, + numCnt: numCnt, + ftSizeMax: ftSizeMax, + labels: labels, + positionMarksShort: positionMarksShort, + positionMarksLong: positionMarksLong, + onSliderMoved: onSliderMoved, + onSliderReleased: onSliderReleased, + onUpdatePeriodic: onUpdatePeriodic, + onSliderTooltipFormat: onSliderTooltipFormat, + idIdent: idIdent, + title: title)); + }); + } + } +} diff --git a/flutter/ayf_pack/lib/models/ayf_backend_cache.dart b/flutter/ayf_pack/lib/models/ayf_backend_cache.dart new file mode 100644 index 00000000..baa6b998 --- /dev/null +++ b/flutter/ayf_pack/lib/models/ayf_backend_cache.dart @@ -0,0 +1,1034 @@ +import 'package:collection/collection.dart'; +import 'package:flutter/foundation.dart'; +import 'package:ayf_pack/helpers/ayf_helper_lib.dart' as helpers; +import '../ayf_pack_local.dart'; + +// ================================================ +// ================================================ +// The actual cache +// ================================================ +// ================================================ +class AudYoFloHttpConnect { + bool isConnected = false; +} + +abstract class AudYoFloBackendCacheCallback { + void reportEvent(String reasonHint); +} + +abstract class AudYoFloBackendCache + with ChangeNotifier + implements AudYoFloBackendCacheBectrlIf, AudYoFloBackendCacheFectrlIf { + AudYoFloBackendAdapterIf? _be; + + AudYoFloBackendCacheCallback? requestSystemDisconnect; + + AudYoFloHttpConnect httpConnect = AudYoFloHttpConnect(); + // ====================================================================== + // Initialization function + // ====================================================================== + + void initializeWithBackendAdapterReference( + AudYoFloBackendAdapterIf beAdapterArg) { + processSection.theRegisteredProcesses.initialize(beAdapterArg); + componentSection.theSelectedComponents.initialize(beAdapterArg); + + // Store reference to backend adapter + _be = beAdapterArg; + } + + // Return reference to backward adapter if required + AudYoFloBackendAdapterIf? get backendAdapterIf => _be; + + // ======================================================================== + // Member functions + // ====================================================================== + + // Trigger notification in case the cache has changed + void triggerNotify() { + notifyListeners(); + } + + // ======================================================================== + // Helper functions + // ====================================================================== + AudYoFloOneSelectedComponent? findSelectedComponent( + JvxComponentIdentification cpId) { + AudYoFloOneSelectedComponent? theSelected; + MapEntry? entry = componentSection + .theSelectedComponents.selectedComponents.entries + .firstWhereOrNull((element) => element.key == cpId); + if (entry != null) { + theSelected = entry.value; + } + return theSelected; + } + + // ====================================================================== + // Dedicated caches for all sub elements + // ====================================================================== + + // Hold a structure to a cache for all components + final AudYoFloComponentCache componentSection = AudYoFloComponentCache(); + + // Hold a structure for all processes + final AudYoFloProcessesCache processSection = AudYoFloProcessesCache(); + + // Hold a structure for all sequencer related infos + final AudYoFloSequencerStateCache sequencerSection = + AudYoFloSequencerStateCache(); + + // Holds the type class list + Map> + typeClassList = {}; + + // Hold a system state id. If the system state id increases we need to restart! + int ssUpdateId = 1; + + // Cache to hold the device options for each technology + final AudYoFloDeviceSelectionMapCache deviceOptionMap = + AudYoFloDeviceSelectionMapCache(); + + // Cache to hold all component selection options + final AudYoFloComponentSelectionMapCache componentOptionMap = + AudYoFloComponentSelectionMapCache(); + + // ====================================================================== + // Function to expose the interfaces + // ====================================================================== + + AudYoFloBackendCacheFectrlIf get cacheToFrontend => this; + AudYoFloBackendCacheBectrlIf get cacheToBackend => this; + + // ================================================================ + // ================================================================ + // INTERFACE FUNCTIONS INTERFACE FUNCTIONS INTERFACE FUNCTIONS INTERFACE FUNCTIONS + // ================================================================ + // ================================================================ + + // Functions to add components - reported when being selected + @override + void reportSelectedComponent(JvxComponentIdentification cpId, String descr, + {AudYoFloDirectPropertiesIf? directPropRef}) { + // Add an entry in the list of selected components + componentSection.theSelectedComponents.addComponent(cpId, descr); + componentSection.ssUpdateId++; + + // Store typeclass list + jvxComponentTypeClassEnum cpClass = + backendAdapterIf!.lookupComponentClass(cpId.cpTp); + if (cpClass != jvxComponentTypeClassEnum.JVX_COMPONENT_TYPE_NONE) { + MapEntry>? + cls = typeClassList.entries + .firstWhereOrNull((element) => element.key == cpClass); + if (cls != null) { + // There should be no such entry!! + JvxComponentIdentification? lelm = + cls.value.firstWhereOrNull((element) => element == cpId); + assert(lelm == null); + cls.value.add(cpId); + } else { + List lst = [cpId]; + typeClassList[cpClass] = lst; + } + } + // componentOptionMap + triggerNotify(); + } + + @override + void reportUnselectedComponent(JvxComponentIdentification cpId) { + AudYoFloOneSelectedComponent? actComponent = findSelectedComponent(cpId); + if (actComponent != null) { + for (var sElm in actComponent.propertyCache.cachedProperties.entries) { + AudYoFloPropertyContainer theCProp = sElm.value; + theCProp.dispose(); + } + actComponent.propertyCache.availProperties = null; + } + componentSection.theSelectedComponents.removeComponent(cpId); + componentSection.ssUpdateId++; + + // ========================================== + // Remove the device list in cache + if (checkComponentIdentTechnology(cpId)) { + deviceOptionMap.selectionOptionCacheMap.removeWhere((key, value) => + (key.cpTp == cpId.cpTp) && (key.slotid == cpId.slotid)); + } + + // =========================================== + // Remove the component cache list for this component type + if (checkComponentIdentDevice(cpId)) { + componentOptionMap.selectionOptionCacheMap.removeWhere((key, value) => + (key.cpTp == cpId.cpTp) && (key.slotid == cpId.slotid)); + } else { + componentOptionMap.selectionOptionCacheMap + .removeWhere((key, value) => (key.cpTp == cpId.cpTp)); + } + + // Cleanup this component in component class list + bool completeOne = false; + jvxComponentTypeClassEnum rmMe = + jvxComponentTypeClassEnum.JVX_COMPONENT_TYPE_NONE; + for (var elmTp in typeClassList.entries) { + for (var elmTpTp in elmTp.value) { + if (elmTpTp == cpId) { + completeOne = true; + break; + } + } + if (completeOne) { + elmTp.value.remove(cpId); + completeOne = false; + if (elmTp.value.isEmpty) { + rmMe = elmTp.key; + } + break; + } + } + if (rmMe != jvxComponentTypeClassEnum.JVX_COMPONENT_TYPE_NONE) { + typeClassList.remove(rmMe); + } + + // Post latest change + triggerNotify(); + } + + @override + void invalidateComponentListCache(JvxComponentIdentification cpId) { + if (checkComponentIdentDevice(cpId)) { + // If device, we need to compare tp and slotid + MapEntry< + JvxComponentIdentification, + AudYoFloOneSelectionOptionEntry< + AudYoFloOneComponentSelectionOption>>? entry = componentOptionMap + .selectionOptionCacheMap.entries + .firstWhereOrNull((element) => + (element.key.cpTp == cpId.cpTp) && + (element.key.slotid == cpId.slotid)); + if (entry != null) { + // There was a device list before, just update it + entry.value.options.optionList.clear(); + entry.value.options.valid = false; + entry.value.options.ssUpdateId += 1; + } + } else { + MapEntry< + JvxComponentIdentification, + AudYoFloOneSelectionOptionEntry< + AudYoFloOneComponentSelectionOption>>? entry = componentOptionMap + .selectionOptionCacheMap.entries + .firstWhereOrNull((element) => (element.key.cpTp == cpId.cpTp)); + if (entry != null) { + // There was a device list before, just update it + entry.value.options.optionList.clear(); + entry.value.options.valid = false; + entry.value.options.ssUpdateId += 1; + } + } + } + // fectrl + + @override + void invalidatePropertiesComponent( + JvxComponentIdentification cpId, List props, bool contentOnly, + {bool invalidateAll = false}) { + // Find the component in list of selected components + + AudYoFloOneSelectedComponent? actComponent = findSelectedComponent(cpId); + if (actComponent != null) { + // Browse through list of specified properties + if (invalidateAll) { + props.clear(); + for (var elmProp in actComponent.propertyCache.cachedProperties.entries) + props.add(elmProp.value.descriptor); + // + } + for (String elmPS in props) { + String theProp = elmPS; + bool contentOnlyThisProp = contentOnly; + List elements = theProp.split('+'); + if (elements.length == 2) { + contentOnlyThisProp = false; + theProp = elements[0]; + } + AudYoFloPropertyContainer? propContent = + actComponent.propertyCache.findCachedProperty(theProp); + if (propContent != null) { + if (!contentOnlyThisProp) { + propContent.cache_status.bitClear(jvxPropertyCacheStatusFlagShifts + .JVX_PROPERTY_DESCRIPTOR_VALID.index); + propContent.cache_status.bitSet(jvxPropertyCacheStatusFlagShifts + .JVX_PROPERTY_DESCRIPTOR_INVALID.index); + } + propContent.cache_status.bitClear(jvxPropertyCacheStatusFlagShifts + .JVX_PROPERTY_CONTENT_VALID.index); + propContent.cache_status.bitSet(jvxPropertyCacheStatusFlagShifts + .JVX_PROPERTY_CONTENT_INVALID.index); + propContent.ssUpdateId++; + } + } + } + } + + @override + List? referencePropertyListInCache(JvxComponentIdentification cpId) { + List? retVal; + AudYoFloOneSelectedComponent? actComponent = findSelectedComponent(cpId); + if (actComponent != null) { + retVal = actComponent.propertyCache.availProperties; + } + return retVal; + } + + @override + List? referenceValidPropertiesComponents( + JvxComponentIdentification cpId, List props) { + List? retVal; + List collect = []; + bool foundAll = true; + + // If + AudYoFloOneSelectedComponent? actComp = findSelectedComponent(cpId); + if (actComp != null) { + for (var elmS in props) { + AudYoFloPropertyContainer? propRef = + actComp.propertyCache.findCachedProperty(elmS); + if (propRef != null) { + // We accept also error to return a value in list + if (propRef.checkInvalidContent()) { + foundAll = false; + break; + } else { + collect.add(propRef); + } + } else { + foundAll = false; + break; + } + } + } + + if (foundAll) { + retVal = collect; + } + + return retVal; + } + + @override + Future triggerUpdatePropertiesComponent( + JvxComponentIdentification cpId, List propDescrArg, + {AyfPropertyReportLevel report = + AyfPropertyReportLevel.AYF_FRONTEND_REPORT_SINGLE_PROPERTY, + bool forceDescriptor = false, + bool forceContent = false}) async { + int errCode = jvxErrorType.JVX_NO_ERROR; + List propsDescr = []; + List propsContent = []; + + AudYoFloOneSelectedComponent? actComp = findSelectedComponent(cpId); + if (actComp != null) { + for (var elmS in propDescrArg) { + AudYoFloPropertyContainer? propRef = + actComp.propertyCache.findCachedProperty(elmS); + if (propRef != null) { + if (!propRef.checkValidDescriptor() || forceDescriptor) { + propsDescr.add(elmS); + propsContent.add(elmS); + } else { + if (!propRef.checkValidContent() || forceContent) { + propsContent.add(elmS); + } + } + } else { + propsDescr.add(elmS); + propsContent.add(elmS); + } + } + if (propsDescr.isNotEmpty) { + errCode = await backendAdapterIf! + .triggerGetPropertyDescriptionComponent(cpId, propsDescr, + reportArg: report); + } + if (errCode == jvxErrorType.JVX_NO_ERROR) { + errCode = await backendAdapterIf!.triggerGetPropertyContentComponent( + cpId, propsContent, + reportArg: report); + } + } else { + errCode = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + } + return errCode; + } + + @override + Future triggerSetProperties( + JvxComponentIdentification cpId, List propContents, + {bool invalidateProperty = true, + int offset = 0, + int num = -1, + bool preventCallMultipleTimes = false, + AyfPropertyReportLevel report = + AyfPropertyReportLevel.AYF_BACKEND_REPORT_COMPONENT_PROPERTY}) async { + int errorCode = jvxErrorType.JVX_NO_ERROR; + errorCode = await backendAdapterIf!.triggerSetProperties(cpId, propContents, + invalidateProperty: invalidateProperty, + offset: offset, + num: num, + preventCallMultipleTimes: preventCallMultipleTimes, + reportArg: report); + + return errorCode; + } + + // Check that call addresses a technology (in case we request a devices list) + @override + bool checkComponentIdentTechnology(JvxComponentIdentification cpId) { + bool isTechnology = false; + // Check that the component is a technology + MapEntry>? + elmTech = typeClassList.entries.firstWhereOrNull((element) => + element.key == + jvxComponentTypeClassEnum.JVX_COMPONENT_TYPE_TECHNOLOGY); + if (elmTech != null) { + JvxComponentIdentification? elmCp = + elmTech.value.firstWhereOrNull((element) => element == cpId); + if (elmCp != null) { + isTechnology = true; + } + } + return isTechnology; + } + + // Check that call addresses a technology (in case we request a devices list) + @override + bool checkComponentIdentDevice(JvxComponentIdentification cpId) { + bool isDevice = false; + // Check that the component is a technology + MapEntry>? + elmDev = typeClassList.entries.firstWhereOrNull((element) => + element.key == jvxComponentTypeClassEnum.JVX_COMPONENT_TYPE_DEVICE); + if (elmDev != null) { + JvxComponentIdentification? elmCp = + elmDev.value.firstWhereOrNull((element) => element == cpId); + if (elmCp != null) { + isDevice = true; + } + } + return isDevice; + } + + // ======================================================================== + // ======================================================================== + // ======================================================================== + // ======================================================================== + // ======================================================================== + // ======================================================================== + + // ================================================================ + // Functions to report new processes and removed processes + @override + void addProcess(int uId) { + processSection.theRegisteredProcesses.addProcess(uId); + componentSection.ssUpdateId++; + triggerNotify(); + } + + @override + void remProcess(int uId) { + processSection.theRegisteredProcesses.removeProcess(uId); + componentSection.ssUpdateId++; + triggerNotify(); + } + + @override + void updateProcessStatusCompleteNotify( + bool processesReadyNew, String reasonIfNot) { + sequencerSection.processesReady = processesReadyNew; + sequencerSection.reasonIfNot = reasonIfNot; + sequencerSection.ssUpdateId++; + triggerNotify(); + } + + @override + Future triggerUpdateProcessStatus() async { + int errCode = await backendAdapterIf!.triggerUpdateProcessStatus(); + return errCode; + } + + // Update the state if it changes (typically "active") + @override + void reportStateSwitchComponent( + JvxComponentIdentification cpId, jvxStateSwitchEnum ss) { + if (ss == jvxStateSwitchEnum.JVX_STATE_SWITCH_SELECT) {} + componentSection.theSelectedComponents.updateComponent(cpId); + + // If a component is activated, load the component selection lists + // that depends on this component + if ((ss == jvxStateSwitchEnum.JVX_STATE_SWITCH_ACTIVATE) || + (ss == jvxStateSwitchEnum.JVX_STATE_SWITCH_DEACTIVATE)) { + AudYoFloOneSelectedComponent? theComponentHere = + findSelectedComponent(cpId); + if (theComponentHere != null) { + bool foundOne = true; + while (foundOne) { + foundOne = false; + for (var elm + in theComponentHere.propertyCache.cachedProperties.entries) { + // Invalidate + if (elm.value.invalidateOnStateSwitch(ss)) { + // If true is returned the single property was also disposed + foundOne = true; + elm.value.dispose(); + theComponentHere.propertyCache.cachedProperties.remove(elm.key); + break; + } + } + } + } + + triggerNotify(); + } else { + debugPrint('New stateswitch triggered.'); + } + } + + @override + void reportUpdateStatusComponent(JvxComponentIdentification cpId) { + // We end up here mostly if a device associated to a technology has been selected/unselected + + // Check if the component is a technology + if (checkComponentIdentTechnology(cpId)) { + // Find the device list + MapEntry? + entry = deviceOptionMap.selectionOptionCacheMap.entries + .firstWhereOrNull((element) => + (element.key.cpTp == cpId.cpTp) && + (element.key.slotid == cpId.slotid)); + if (entry != null) { + entry.value.options.optionList.clear(); + entry.value.options.valid = false; + entry.value.options.ssUpdateId += 1; + } + } + triggerNotify(); + } + + // ================================================================ + // Process a single sequencer even + @override + int reportSequencerEvent( + jvxSequencerEventType ev, jvxSequencerStatusEnum seqStat) { + int errCode = jvxErrorType.JVX_NO_ERROR; + String maskStr = ev.eventMask.toRadixString(16); + debugPrint('Sequencer reports < $ev.description>, event mask = 0x$maskStr'); + + if ((ev.eventMask & + jvxSequencerEventBitfield + .JVX_SEQUENCER_EVENT_SUCCESSFUL_COMPLETION_STEP) != + 0) { + if (ev.setp == + jvxSequencerElementTypeEnum + .JVX_SEQUENCER_TYPE_REQUEST_UPDATE_VIEWER) { + // What shall we do here? + } + } + // The sequencer may have been shutdown to close the program. If so, + // we need to continue shutdown process + if ((ev.eventMask & + jvxSequencerEventBitfield.JVX_SEQUENCER_EVENT_PROCESS_TERMINATED) != + 0) { + // Then, update the seq status + // seqStat = ev.seq_state_id!.immediateSequencerStatus(); + seqStat = ev.statSeq; + + // Here, we have seen the PROCESS_TERMINATED event + // First acknowledge the stop, then it returns the status AFTER acknowledge + seqStat = backendAdapterIf!.acknowledgeSequencerStop(); + + // Finally check for elements in queue + var retVal = AudYoFloCommandQueueElement.AYF_COMMAND_QUEUE_NONE; + do { + retVal = backendAdapterIf!.postSequencerStop(); + + // We can run any action when the sequencer shuts down! + switch (retVal) { + default: + break; + } + } while (retVal != AudYoFloCommandQueueElement.AYF_COMMAND_QUEUE_NONE); + } + + if (sequencerSection.seqStatus != seqStat) { + sequencerSection.seqStatus = seqStat; + sequencerSection.ssUpdateId++; + + // This trigger will run the notify + backendAdapterIf!.triggerUpdateProcessStatus(); + // triggerNotify(); + } + return errCode; + } + + @override + String debugPrintProcess(int uId) { + return processSection.theRegisteredProcesses.debugPrintProcess(uId); + } + + @override + int updateIdComponentInCache(JvxComponentIdentification cpId) { + int retVal = -1; + AudYoFloOneSelectedComponent? cpElm = findSelectedComponent(cpId); + if (cpElm != null) { + retVal = cpElm.propertyCache.ssPropertyCache; + } + return retVal; + } + + @override + int updateIdPropertyComponentInCache( + JvxComponentIdentification cpId, String descriptor) { + int retVal = -1; + AudYoFloPropertyContainer? propCont; + AudYoFloOneSelectedComponent? cpElm = findSelectedComponent(cpId); + if (cpElm != null) { + propCont = cpElm.propertyCache.findCachedProperty(descriptor); + if (propCont != null) { + retVal = propCont.ssUpdateId; + } + } + return retVal; + } + + @override + int updateEntrySinglePropertyCache( + JvxComponentIdentification cpId, AudYoFloPropertyContentBackend newProp) { + int res = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + AudYoFloOneSelectedComponent? cpElm = findSelectedComponent(cpId); + if (cpElm != null) { + res = jvxErrorType.JVX_NO_ERROR; + AudYoFloPropertyContainer? propCont = + cpElm.propertyCache.findCachedProperty(newProp.descriptor); + if (propCont != null) { + // Exchange this property + propCont = newProp; + propCont.ssUpdateId++; + } else { + // Attach this property + cpElm.propertyCache.cachedProperties[newProp.descriptor] = newProp; + } + } + + return res; + } + + @override + int updatePropertyCacheCompleteNotify(JvxComponentIdentification cpId, + {AyfPropertyReportLevel report = + AyfPropertyReportLevel.AYF_FRONTEND_REPORT_SINGLE_PROPERTY}) { + int res = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + AudYoFloOneSelectedComponent? cpElm = findSelectedComponent(cpId); + if (cpElm != null) { + res = jvxErrorType.JVX_NO_ERROR; + + // We may update also the property cache id for the current component + if (report == + AyfPropertyReportLevel.AYF_FRONTEND_REPORT_COMPONENT_PROPERTY) { + cpElm.propertyCache.ssPropertyCache++; + } + if ((report == + AyfPropertyReportLevel.AYF_FRONTEND_REPORT_COMPONENT_PROPERTY) || + (report == + AyfPropertyReportLevel.AYF_FRONTEND_REPORT_SINGLE_PROPERTY)) { + triggerNotify(); + } + } + return res; + } + + @override + void findComponentsMatch( + List lst, int processId) { + // Collect all data from active components + if (processId >= 0) { + if (processId < + processSection + .theRegisteredProcesses.reportedProcesses.entries.length) { + var entry = processSection + .theRegisteredProcesses.reportedProcesses.entries + .elementAt(processId); + AudYoFloOneConnectedProcess con = entry.value; + con.involved?.findComponentsMatch( + lst, componentSection.theSelectedComponents.selectedComponents); + } else { + processId = -1; + } + } + if (processId == -1) { + componentSection.theSelectedComponents.selectedComponents.forEach((k, v) { + bool addElement = true; + JvxComponentIdentification cpId = k; + for (var elm in lst) { + if (elm == cpId) { + addElement = false; + break; + } + } + + if (addElement) { + lst.add(cpId); + } + }); + } + } + + @override + void availProcessDescriptions(List lst) { + processSection.theRegisteredProcesses.reportedProcesses.forEach((k, v) { + AudYoFloOneConnectedProcess vProc = v; + String oneEntry = ''; + var uId = vProc.uId; + var catId = vProc.catId; + oneEntry = vProc.nameProcess; + oneEntry += '<$uId>'; + oneEntry += '[$catId]'; + lst.add(oneEntry); + }); + } + + @override + String descriptionComponent(JvxComponentIdentification cpTp) { + String retVal = 'Unknown'; + AudYoFloOneSelectedComponent? comp = findSelectedComponent(cpTp); + if (comp != null) { + retVal = comp.description; + } + return retVal; + } + + @override + Future triggerActivateComponent(JvxComponentIdentification cpId, int id, + {bool selectBeforeActivate = true}) async { + int errCode = await backendAdapterIf! + .triggerActivateComponent(cpId, id, selectBeforeActivate); + + // All following messages will be reported by events + return errCode; + } + + @override + Future triggerDeactivateComponent(JvxComponentIdentification cpId, + {bool unselect = true}) async { + int errCode = + await backendAdapterIf!.triggerDeactivateComponent(cpId, unselect); + return errCode; + } + + // This function returns a list of the requested properties from cache. + // Each entry in the cache is evaluated if is valid. If any is not in cache + // the function returns a null list. + @override + AudYoFloPropertyContainer? referencePropertyInCache( + JvxComponentIdentification cpId, String theProp) { + AudYoFloPropertyContainer? retVal; + AudYoFloOneSelectedComponent? actComponent = findSelectedComponent(cpId); + if (actComponent != null) { + retVal = actComponent.propertyCache.findCachedProperty(theProp); + } + return retVal; + } + + // ====================================================================== + + // Property list functions + @override + Future triggerUpdatePropertyListComponent( + JvxComponentIdentification cpId) async { + int errCode = jvxErrorType.JVX_ERROR_INVALID_ARGUMENT; + if (cpId.cpTp != JvxComponentTypeEnum.JVX_COMPONENT_UNKNOWN) + errCode = + await backendAdapterIf!.triggerUpdatePropertyListComponent(cpId); + return errCode; + } + + @override + int updatePropertyCacheListCompleteNotify( + JvxComponentIdentification cpId, List lst, + {AyfPropertyReportLevel report = + AyfPropertyReportLevel.AYF_FRONTEND_REPORT_COMPONENT_PROPERTY}) { + int errCode = jvxErrorType.JVX_NO_ERROR; + AudYoFloOneSelectedComponent? actComponent = findSelectedComponent(cpId); + if (actComponent != null) { + actComponent.propertyCache.availProperties = lst; + updatePropertyCacheCompleteNotify(cpId, report: report); + } else { + errCode = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + } + return errCode; + } + + // Get the device list as stored in cache. If the device list is not + // available return an empty (null) list + @override + List? referenceDeviceListInCache( + JvxComponentIdentification cpId) { + // Check that the type is really a registered technology + List? options; + if (!checkComponentIdentTechnology(cpId)) { + /* + throw AudYoFloException(jvxErrorType.JVX_ERROR_INVALID_SETTING, + 'Function to obtain devices called on a component type other than a technology.');*/ + return null; + } + // Here, we have found the technology in list of ids in the technology list + + // Find the device list as stored for this technology + // A technology is defined by comp type and slotid + MapEntry>? + entry = deviceOptionMap.selectionOptionCacheMap.entries + .firstWhereOrNull((element) => + (element.key.cpTp == cpId.cpTp) && + (element.key.slotid == cpId.slotid)); + if (entry != null) { + if (entry.value.options.valid) { + // We have identified the right technology and return the VALID device list + options = entry.value.options.optionList; + } + // else: device list is invalid and can not be returned + } + + return options; + } + + // Refresh devices list in cache + @override + Future triggerUpdateDeviceList(JvxComponentIdentification cpId) async { + int errCode = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + if (cpId.cpTp != JvxComponentTypeEnum.JVX_COMPONENT_UNKNOWN) { + if (backendAdapterIf == null) { + errCode = jvxErrorType.JVX_ERROR_NOT_READY; + } else { + errCode = await backendAdapterIf!.triggerUpdateDeviceList(cpId); + } + } + return errCode; + } + + // From backend adapter, provide new devices list and inform all listeners + // Since the notify is triggered, this call must come in asyncronously + @override + int updateDeviceListCacheCompleteNotify(JvxComponentIdentification cpId, + List devices) { + int errCode = jvxErrorType.JVX_NO_ERROR; + MapEntry>? + entry = deviceOptionMap.selectionOptionCacheMap.entries + .firstWhereOrNull((element) => + (element.key.cpTp == cpId.cpTp) && + (element.key.slotid == cpId.slotid)); + if (entry != null) { + // There was a device list before, just update it + entry.value.options.optionList = devices; + entry.value.options.valid = true; + entry.value.options.ssUpdateId += 1; + } else { + // This is the first time to create a device list + AudYoFloOneSelectionOptionEntry + newEntry = + AudYoFloOneSelectionOptionEntry(); + newEntry.options.optionList = devices; + newEntry.options.valid = true; + newEntry.options.ssUpdateId = 1; + deviceOptionMap.selectionOptionCacheMap[cpId] = newEntry; + } + + if (errCode == jvxErrorType.JVX_NO_ERROR) { + triggerNotify(); + } + return errCode; + } + + @override + int updateIdDeviceListInCache(JvxComponentIdentification cpId) { + int retVal = -1; + MapEntry>? + entry = deviceOptionMap.selectionOptionCacheMap.entries + .firstWhereOrNull((element) => + (element.key.cpTp == cpId.cpTp) && + (element.key.slotid == cpId.slotid)); + if (entry != null) { + retVal = entry.value.options.ssUpdateId; + } + return retVal; + } + + // Return a selection list from cache. If none exists return an empty list + @override + List? referenceComponentListInCache( + JvxComponentIdentification cpId) { + // Check that the type is really a registered technology + List? options; + if (checkComponentIdentDevice(cpId)) { + // It is a device, hence, we select option based on type and slotid + MapEntry< + JvxComponentIdentification, + AudYoFloOneSelectionOptionEntry< + AudYoFloOneComponentSelectionOption>>? entry = componentOptionMap + .selectionOptionCacheMap.entries + .firstWhereOrNull((element) => + (element.key.cpTp == cpId.cpTp) && + (element.key.slotid == cpId.slotid)); + if (entry != null) { + if (entry.value.options.valid) { + // We have identified the right technology and return the VALID device list + options = entry.value.options.optionList; + } + // else: device list is invalid and can not be returned + } + } else { + // Technology or node, only type is of relevance + MapEntry< + JvxComponentIdentification, + AudYoFloOneSelectionOptionEntry< + AudYoFloOneComponentSelectionOption>>? entry = componentOptionMap + .selectionOptionCacheMap.entries + .firstWhereOrNull((element) => (element.key.cpTp == cpId.cpTp)); + if (entry != null) { + if (entry.value.options.valid) { + // We have identified the right technology and return the VALID device list + options = entry.value.options.optionList; + } + // else: device list is invalid and can not be returned + } + } + + return options; + } + + // Trigger an update of the selection list in the backend adapter + @override + Future triggerUpdateComponentList( + JvxComponentIdentification cpId) async { + bool isDevice = checkComponentIdentDevice(cpId); + int errCode = + await backendAdapterIf!.triggerUpdateComponentList(cpId, isDevice); + return errCode; + } + +// From backend adapter, provide new devices list and inform all listeners + // Since the notify is triggered, this call must come in asyncronously + @override + int updateComponentListCacheCompleteNotify(JvxComponentIdentification cpId, + List options, bool isDevice) { + int errCode = jvxErrorType.JVX_NO_ERROR; + if (isDevice) { + // If device, we need to compare tp and slotid + MapEntry< + JvxComponentIdentification, + AudYoFloOneSelectionOptionEntry< + AudYoFloOneComponentSelectionOption>>? entry = componentOptionMap + .selectionOptionCacheMap.entries + .firstWhereOrNull((element) => + (element.key.cpTp == cpId.cpTp) && + (element.key.slotid == cpId.slotid)); + if (entry != null) { + // There was a device list before, just update it + entry.value.options.optionList = options; + entry.value.options.valid = true; + entry.value.options.ssUpdateId += 1; + } else { + // This is the first time to create a device list + AudYoFloOneSelectionOptionEntry + newEntry = AudYoFloOneSelectionOptionEntry< + AudYoFloOneComponentSelectionOption>(); + newEntry.options.optionList = options; + newEntry.options.valid = true; + newEntry.options.ssUpdateId = 1; + componentOptionMap.selectionOptionCacheMap[cpId] = newEntry; + } + } else { + // Node or technology: only use the type + MapEntry< + JvxComponentIdentification, + AudYoFloOneSelectionOptionEntry< + AudYoFloOneComponentSelectionOption>>? entry = componentOptionMap + .selectionOptionCacheMap.entries + .firstWhereOrNull((element) => (element.key.cpTp == cpId.cpTp)); + if (entry != null) { + // There was a device list before, just update it + entry.value.options.optionList = options; + entry.value.options.valid = true; + entry.value.options.ssUpdateId += 1; + } else { + // This is the first time to create a device list + AudYoFloOneSelectionOptionEntry + newEntry = AudYoFloOneSelectionOptionEntry< + AudYoFloOneComponentSelectionOption>(); + newEntry.options.optionList = options; + newEntry.options.valid = true; + newEntry.options.ssUpdateId = 1; + componentOptionMap.selectionOptionCacheMap[cpId] = newEntry; + } + } + + if (errCode == jvxErrorType.JVX_NO_ERROR) { + triggerNotify(); + } + return errCode; + } + + @override + void triggerStartSequencer() { + backendAdapterIf!.triggerStartSequencer(); + } + + // Trigger stop of the sequencer + @override + void triggerStopSequencer() { + backendAdapterIf!.triggerStopSequencer(); + } + + @override + void triggerToggleSequencer() { + backendAdapterIf!.triggerStopSequencer(); + } + + @override + Future transferTextCommand(String textArg, bool jsonOut) { + return backendAdapterIf!.transferTextCommand(textArg, jsonOut); + } + + @override + Future triggerSaveConfig() async { + return await backendAdapterIf!.triggerSaveConfig(); + } + + @override + void reportPropertyListSetBackend( + JvxComponentIdentification cpId, String propLst) { + // print('Report property set <$propLst>'); + List props = helpers.str2PropList(propLst); + + // Invalidate properties + invalidatePropertiesComponent(cpId, props, true); + updatePropertyCacheCompleteNotify(cpId, + report: AyfPropertyReportLevel.AYF_FRONTEND_REPORT_COMPONENT_PROPERTY); + } + + @override + Future triggerClose() async { + if (backendAdapterIf != null) { + await backendAdapterIf!.triggerClose(); + } + } + + @override + void reportSystemStateRestart() { + ssUpdateId++; + // Navigator.pushNamed(context, '/second'); + triggerNotify(); + } +} diff --git a/flutter/ayf_pack/lib/models/ayf_component_selection.dart b/flutter/ayf_pack/lib/models/ayf_component_selection.dart new file mode 100644 index 00000000..291c86c7 --- /dev/null +++ b/flutter/ayf_pack/lib/models/ayf_component_selection.dart @@ -0,0 +1,39 @@ +import '../ayf_pack_local.dart'; + +class AudYoFloOneSelectionOptionBase { + String description = 'not-set'; + String descriptor = 'not-set'; + int optionIdx = -1; +} + +// Class tp hold the status of a single device to be shown in +// device list. +class AudYoFloOneDeviceSelectionOption extends AudYoFloOneSelectionOptionBase { + jvxDeviceDataFlowTypeEnum flow = + jvxDeviceDataFlowTypeEnum.JVX_DEVICE_DATAFLOW_UNKNOWN; + AudYoFloCBitField32 caps = AudYoFloCBitField32(); + AudYoFloCBitField32 flags = AudYoFloCBitField32(); + bool selectable = false; + JvxComponentIdentification devIdent = JvxComponentIdentification(); + AudYoFloCBitField32 state = AudYoFloCBitField32(); + bool singleton = false; +} + +class AudYoFloOneComponentSelectionOption + extends AudYoFloOneSelectionOptionBase { + List sids = []; +} + +class AudYoFloOneSelectionOptionCache extends AudYoFloStateCache { + List optionList = []; +} + +class AudYoFloOneSelectionOptionEntry { + // This is the list of devices for a technology + AudYoFloOneSelectionOptionCache options = + AudYoFloOneSelectionOptionCache(); + + // This is the list of options + //AudYoFloOneSelectionOptionCache options = + // AudYoFloOneSelectionOptionCache(); +} diff --git a/flutter/ayf_pack/lib/models/ayf_debug_model.dart b/flutter/ayf_pack/lib/models/ayf_debug_model.dart new file mode 100644 index 00000000..f5ca9755 --- /dev/null +++ b/flutter/ayf_pack/lib/models/ayf_debug_model.dart @@ -0,0 +1,100 @@ +import 'package:flutter/foundation.dart'; +import 'dart:math'; +import 'package:stack_trace/stack_trace.dart'; +import 'package:intl/intl.dart'; +import 'package:collection/collection.dart'; +import '../ayf_pack_local.dart'; + +class AudYoFloDebugModel with ChangeNotifier { + late AudYoFloBackendCache be; + + // Parameter for the "debug" widget + int num_lines_out_max = 128; + List out_debug = []; + bool dbgOut = true; + bool funcOut = false; + + // Parameter for the "component" widget + // Selection of component in generic property viewer + JvxComponentIdentification idSelectCp = JvxComponentIdentification(); + int ssUpdateCpId = -1; + + int idSelectProc = -1; + + void initialize(AudYoFloBackendCache beArg) { + be = beArg; + } + + void triggerUpdate() { + notifyListeners(); + } + + void addLineOutConvert(String txt) { + String token = ''; + for (int ii = 0; ii < txt.length; ii++) { + String oneChar = txt.substring(ii, ii + 1); + if (oneChar == '\n') { + if (token.length > 0) { + addLineOut(token); + token = ''; + } + } else if (oneChar == '\t') { + token += " "; + } else { + token += oneChar; + } + } + + if (token.length > 0) { + addLineOut(token); + token = ''; + } + } + + void addLineOut(String txt) { + if (out_debug.length == num_lines_out_max) { + out_debug.removeAt(0); + } + DateFormat dateFormat = DateFormat("yyyy-MM-dd HH:mm:ss"); + String dateTime = dateFormat.format(DateTime.now()); + var funcname = ''; + if (funcOut) { + Trace str = Trace.from(StackTrace.current); + var ff = str.frames; + var fr = ff.elementAt(1); + funcname = '-- ' + fr.toString(); + } + out_debug.add('${dateTime} ${funcname} -- $txt'); + triggerUpdate(); + } + + void newNumberLines(int num) { + num_lines_out_max = num; + num_lines_out_max = max(1, num_lines_out_max); + while (out_debug.length > num_lines_out_max) { + out_debug.removeAt(0); + } + triggerUpdate(); + } + + void clearAll() { + out_debug.clear(); + triggerUpdate(); + } + + void verifyComponentSelection(List lst) { + JvxComponentIdentification? cpSel = + lst.firstWhereOrNull((element) => element == idSelectCp); + if (cpSel == null) { + // If current selection is no longer available, select the first in list + idSelectCp = lst[0]; + // selectNewComponent(lst[0]); <- This should not be called as the functon is called in build function + } + } + + void selectNewComponent(JvxComponentIdentification cpId) { + idSelectCp = cpId; + ssUpdateCpId++; + triggerUpdate(); + } +} diff --git a/flutter/ayf_pack/lib/models/ayf_ui_model.dart b/flutter/ayf_pack/lib/models/ayf_ui_model.dart new file mode 100644 index 00000000..9b46ec10 --- /dev/null +++ b/flutter/ayf_pack/lib/models/ayf_ui_model.dart @@ -0,0 +1,201 @@ +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; +import '../ayf_pack_local.dart'; + +typedef AyfBottomBarAllocator = BottomAppBar? Function(); + +class AudYoFloUiHttpConnect { + String ipAddr = '127.0.0.1'; + int ipPort = 8000; + bool autoConnect = false; +} + +abstract class AudYoFloUiElementsTabBar { + List theAllocatedTabs = []; + List theAllocatedTabViews = []; + + List get allocatedTabs => theAllocatedTabs; + List get allocatedTabViews => theAllocatedTabViews; + + void createTabsAndViews(Map entriesCfgWidgets); + + void clearTabAndViews() { + theAllocatedTabs.clear(); + theAllocatedTabViews.clear(); + } +} + +class AudYoFloOneUiSection { + bool show; + String imageName; + String textShow; + AudYoFloOneUiSection(this.show, this.imageName, this.textShow); +} + +abstract class AudYoFloUiModel + with ChangeNotifier + implements AudYoFloUiElementsTabBar { + Map widgetSpecificConfigurations = {}; + + AudYoFloOneUiSection debug = AudYoFloOneUiSection( + true, 'packages/ayf_pack/images/ayf_icons/ayf-dbg.png', 'Debug'); + + AudYoFloOneUiSection components = AudYoFloOneUiSection( + true, 'packages/ayf_pack/images/ayf_icons/ayf-comp.png', 'Components'); + + // ======================================================================= + + AudYoFloPropertyValue fastRefreshPerMsecs = AudYoFloPropertyValue( + 50, "/settings/ui/refreshPerMsecFast", "Fast UI Refresh period [msecs]."); + + AudYoFloPropertyValue slowRefreshPerMsecs = AudYoFloPropertyValue(1000, + "/settings/ui/refreshPerMsecSlow", "Slow UI Refresh period [msecs]."); + + AudYoFloSettingsConfiguration settingsConfigure = + AudYoFloSettingsConfiguration(); + + bool addUpper = false; + double heightUpper = 80; + + bool addBottom = false; + double heightBottom = 80; + + bool addUpperNavBar = true; + double heightUpperNavBar = 40; + + bool addBottomNavBar = true; + double heightBottomNavBar = 80; + + // ======================================================================== + + // For the main tab layout + double navButtonSizeMain = 50; + double navButtonAreaWidthMain = 120; + double navButtonAreaHeightMain = 100; + double navButtonAreaHeightSpaceMain = 20; + double navButtonAreaWidthSpaceMain = 20; + + // For the settings dialog + double navButtonSizeSettings = 50; + double navButtonAreaWidthSettings = 120; + double navButtonAreaHeightSettings = 100; + double navButtonAreaHeightSpacSettings = 20; + double navButtonAreaWidthSpaceSettings = 20; + + double maxWidthSettings = 640; + double maxHeightSettings = 480; + + double mixerWidth = 75; + double mixerHeight = 400; + + // Show combo box to hold technology and devices of primary audio device + bool showAudioDevTech = true; + + String textApp = 'myAppName'; + String imageNameApp = ''; + double imageWScaleApp = 5; + double imageOAScaleApp = 0.7; + + bool currentColorSaveDialogTriggered = false; + + AyfBottomBarAllocator? allocateBottomAppBar; + + // Reference to platform specific object + AudYoFloPlatformSpecific? platformSpec; + + // Provide startup route: "/" means that the host is immediately started, + // "/connect" is a widget to accept an ip address with a port to connect + String initRoute = '/'; + // String initRoute = '/connect'; + + AudYoFloUiHttpConnect httpConnect = AudYoFloUiHttpConnect(); + + // We initialize the platform specific part of the application. + AudYoFloUiModel() {} + + // Pre-configure some default values + void configureSystemParameters(AudYoFloCompileFlags flags) { + // + // Initialize the selection bitfield + // + } + + int reportSetProperty(AudYoFloPropertyContentLocal prop) { + bool notifyThis = false; + int errCode = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + + if (prop.descriptor == fastRefreshPerMsecs.descriptor) { + //binauralRenderer.spreadAngle = binauralRenderer.propSpread.value; + notifyThis = true; + } + + if (notifyThis) { + notifyListeners(); + } + return errCode; + } + + // Interface function: obtain the list of properties right before showing them in the settings dialog + void filterListProperties( + String purpose, AudYoFloMultiComponentsPropertyOnChangeEntry entries) {} + + void notify() { + notifyListeners(); + } + + @override + void createTabsAndViews(Map entriesCfgWidgets) { + // Tabs are added to a list along multiple layers of the class hierarchie + // Since we dont know if we are the first here, we can not clear the list + // when entering the call. It is the task of the caller to run clear before + + // ======================================================================= + if (debug.show) { + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Column(children: [ + Image.asset(debug.imageName, + filterQuality: FilterQuality.medium, + //'images/fernlive_icons/fernlive-mixer.png', + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(debug.textShow) + ]))); + + theAllocatedTabViews.add(const AudYoFloDebugWidget()); + } + // ======================================================================= + if (components.show) { + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Column(children: [ + Image.asset(components.imageName, + filterQuality: FilterQuality.medium, + //'images/fernlive_icons/fernlive-mixer.png', + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(components.textShow) + ]))); + + theAllocatedTabViews.add(const AudYoFloComponentWidget()); + } + } + + // ======================================================================== + List removeIfPresent( + List lst, + String propIsPresent, + List removeThese) { + AudYoFloPropertyContainer? ct = + lst.firstWhereOrNull((element) => element.descriptor == propIsPresent); + if (ct != null) { + if (ct is AudYoFloPropertyContentBackend) { + if (ct.isValid) { + for (var remToken in removeThese) + lst.removeWhere((element) => element.descriptor == remToken); + } + } + } + return lst; + } +} diff --git a/flutter/ayf_pack/lib/notifiers/ayf_notifiers.dart b/flutter/ayf_pack/lib/notifiers/ayf_notifiers.dart new file mode 100644 index 00000000..2ce8b9d8 --- /dev/null +++ b/flutter/ayf_pack/lib/notifiers/ayf_notifiers.dart @@ -0,0 +1,28 @@ +//import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +// ======================================================= +// ======================================================= +// ======================================================= + +// Report periodic timer events to the widgets in the tree. We use +// the PeriodicCallbacks reference to transform a callback into +// a PeriodicNotifier in the fernline_main_widget + +abstract class AudYoFloPeriodicCallbacks { + int reportFast(); + int reportSlow(); +} + +// Report this element to the listening widgets +class AudYoFloPeriodicNotifier with ChangeNotifier { + int cntFast = 0; + int cntSlow = 0; + bool fast = false; + + AudYoFloPeriodicNotifier({bool fast = true}); + + void notify() { + notifyListeners(); + } +} diff --git a/flutter/ayf_pack/lib/periodic/ayf_periodic_notifier.dart b/flutter/ayf_pack/lib/periodic/ayf_periodic_notifier.dart new file mode 100644 index 00000000..cd969e9c --- /dev/null +++ b/flutter/ayf_pack/lib/periodic/ayf_periodic_notifier.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; +import 'dart:async'; +import '../ayf_pack_local.dart'; + +class AudYoFloHostTimer { + AudYoFloPeriodicCallbacks? callbacks; + + int timeoutFastMsecs = 50; + int timeoutSlowMsecs = 1000; + late Timer timerPeriodicFast; + late Timer timerPeriodicSlow; + + AudYoFloHostTimer( + {this.timeoutFastMsecs = 50, + this.timeoutSlowMsecs = 1000, + this.callbacks}); + + void fastCallback(Timer _) { + // Callback for fast periodic update + int timeoutNew = -1; + int? timeoutVar = callbacks?.reportFast(); + if (timeoutVar != null) { + if (timeoutVar != timeoutFastMsecs) { + timerPeriodicFast.cancel(); + timeoutFastMsecs = timeoutVar; + timerPeriodicFast = Timer.periodic( + Duration(milliseconds: timeoutFastMsecs), fastCallback); + } + } + } + + void slowCallback(Timer _) { + // Callback for slow periodic update + int timeoutNew = -1; + int? timeoutVar = callbacks?.reportSlow(); + if (timeoutVar != null) { + if (timeoutVar != timeoutSlowMsecs) { + timerPeriodicSlow.cancel(); + timeoutSlowMsecs = timeoutVar; + timerPeriodicSlow = Timer.periodic( + Duration(milliseconds: timeoutSlowMsecs), slowCallback); + } + } + } + + bool initTimer() { + timerPeriodicFast = + Timer.periodic(Duration(milliseconds: timeoutFastMsecs), fastCallback); + + timerPeriodicSlow = + Timer.periodic(Duration(milliseconds: timeoutSlowMsecs), slowCallback); + + return true; + } + + @override + bool termTimer() { + timerPeriodicFast.cancel(); + timerPeriodicSlow.cancel(); + return true; + } +} diff --git a/flutter/ayf_pack/lib/platform/ayf_platform_specific.dart b/flutter/ayf_pack/lib/platform/ayf_platform_specific.dart new file mode 100644 index 00000000..b092e561 --- /dev/null +++ b/flutter/ayf_pack/lib/platform/ayf_platform_specific.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; +import '../../ayf_pack_local.dart'; + +abstract class AudYoFloPlatformSpecific { + List cmdArgs = []; + + AyfHost? referenceHost(); + + void prepareClose(BuildContext prepareClose); + + Widget? allocateFileDropWidget( + JvxComponentIdentification identT, String textShowDrag, double sizeIcon); + + void configureSubSystem(Map args); + + Future initializeSubSystem(AudYoFloBackendCache? notifierBeCache, + AudYoFloUiModel? uiModel, AudYoFloDebugModel? notifierDbgModel); + + String get lastError; +} diff --git a/flutter/ayf_pack/lib/platform/web/ayf_platform_specific.dart b/flutter/ayf_pack/lib/platform/web/ayf_platform_specific.dart new file mode 100644 index 00000000..5467d38b --- /dev/null +++ b/flutter/ayf_pack/lib/platform/web/ayf_platform_specific.dart @@ -0,0 +1,72 @@ +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import '../../ayf_pack_local.dart'; + +class AudYoFloPlatformSpecificHtml extends AudYoFloPlatformSpecific { + AyfHost? theBeAdapter; + @override + AyfHost? referenceHost() { + return theBeAdapter; + } + + @override + void prepareClose(BuildContext context) { + /* + * This installs a handler for the "close" event. + */ + + AudYoFloBackendCache theBeCache = + Provider.of(context, listen: false); + + // Skip this part: we can not really catch the close button on "web" + } + + // Allocate and return file drop widget - there is none at this time.. + @override + Widget? allocateFileDropWidget(JvxComponentIdentification identT, + String textShowDrag, double sizeIcon) => + null; + + @override + Future initializeSubSystem(AudYoFloBackendCache? notifierBeCache, + AudYoFloUiModel? uiModel, AudYoFloDebugModel? notifierDbgModel) async { + // Do nothing atz the moment + + bool retVal = true; + if (notifierBeCache != null) { + Map args = {}; + args['cmdArgs'] = cmdArgs; + retVal = await theBeAdapter! + .initialize(notifierBeCache, notifierDbgModel, args); + if (retVal == false) { + // theBeAdapter!. + } + } + + return true; + } + + @override + void configureSubSystem(Map args) { + var elm = args.entries.firstWhereOrNull((element) => element.key == 'host'); + if (elm != null) { + if (elm.value is AyfHost) { + theBeAdapter = elm.value as AyfHost; + } + } + assert(theBeAdapter != null); + } + + @override + String get lastError { + if (theBeAdapter != null) { + return theBeAdapter!.lastError; + } + return ''; + } +} + +AudYoFloPlatformSpecific allocatePlatformSpecific() { + return AudYoFloPlatformSpecificHtml(); +} diff --git a/flutter/ayf_pack/lib/properties/ayf_properties_backend.dart b/flutter/ayf_pack/lib/properties/ayf_properties_backend.dart new file mode 100644 index 00000000..a3a7b6f1 --- /dev/null +++ b/flutter/ayf_pack/lib/properties/ayf_properties_backend.dart @@ -0,0 +1,338 @@ +import '../../ayf_pack_local.dart'; + +enum jvxPropertyProgressStates { + JVX_PROPERTY_PROGRESS_NONE, + JVX_PROPERTY_PROGRESS_WAIT +} + +// ################################################################## +// ################################################################## +class AudYoFloPropertyDescriptorBackend extends AudYoFloPropertyContainer { + // Last result from operation + int last_error = jvxErrorType.JVX_NO_ERROR; + + // Update of this cache location + + // Show which parts are valid + AudYoFloCBitField32 valid_parts = AudYoFloCBitField32(); + + // ===================================================================== + + // Gobal idx in the context of this component + int globalIdx = -1; + + // Property category + jvxPropertyCategoryTypeEnum category = + jvxPropertyCategoryTypeEnum.JVX_PROPERTY_CATEGORY_UNKNOWN; + + // ===================================================================== + // State in which the componnent is valid + AudYoFloCBitField32 allowedStateMask = AudYoFloCBitField32(); + + // Not used currently + AudYoFloCBitField32 allowedThreadingMask = AudYoFloCBitField32(); + + // Default setting to enable that properties are invalidated on state switch + jvxPropertyInvalidateTypeEnum reqInvalidateOnStateSwitch = + jvxPropertyInvalidateTypeEnum.JVX_PROPERTY_INVALIDATE_INACTIVE; + jvxPropertyInvalidateTypeEnum reqInvalidateOnTest = + jvxPropertyInvalidateTypeEnum.JVX_PROPERTY_INVALIDATE_INACTIVE; + + bool installable = false; + + // Access type of this property + jvxPropertyAccessTypeEnum accessType = + jvxPropertyAccessTypeEnum.JVX_PROPERTY_ACCESS_NONE; + + // Decoder hint type + jvxPropertyDecoderHintTypeEnum decoderHintType = + jvxPropertyDecoderHintTypeEnum.JVX_PROPERTY_DECODER_NONE; + + jvxPropertyContextEnum ctxt = + jvxPropertyContextEnum.JVX_PROPERTY_CONTEXT_UNKNOWN; + + // ===================================================================== + + // Validity as returned from the API + bool isValid = false; + + // ============================================ + // Name as returnd from API + String name = ''; + + // ============================================================== + + // Origin (if available) + String origin = ''; + + AudYoFloCBitField64 accessFlags = AudYoFloCBitField(); + + AudYoFloCBitField32 configFlags = AudYoFloCBitField32(); + + // ============================================================== + AudYoFloPropertyDescriptorBackend(JvxComponentIdentification assCpIdentArg, + {String descriptor = '', + String description = '', + jvxDataFormatEnum jvxFormat = jvxDataFormatEnum.JVX_DATAFORMAT_NONE, + int num = 1}) + : super(jvxFormat, num, descriptor, description) { + super.assCpIdent = assCpIdentArg; + descrType = jvxPropertyDescriptorTypeEnum.JVX_PROPERTY_BE_DESCRIPTOR; + cache_status.bitSet( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_DESCRIPTOR_INVALID.index); + cache_status.bitSet( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_CONTENT_INVALID.index); + } + + // ============================================================== + + void validateDescriptor() { + cache_status.bitClear( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_DESCRIPTOR_INVALID.index); + cache_status.bitClear( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_DESCRIPTOR_ERROR.index); + cache_status.bitSet( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_DESCRIPTOR_VALID.index); + ssUpdateId++; + } + + void validateContent() { + cache_status.bitClear( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_CONTENT_INVALID.index); + cache_status.bitClear( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_CONTENT_ERROR.index); + cache_status.bitSet( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_CONTENT_VALID.index); + ssUpdateId++; + } + + // ============================================================== + + void invalidateDescriptor(bool error) { + cache_status.bitClear( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_DESCRIPTOR_VALID.index); + if (error) { + cache_status.bitSet( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_DESCRIPTOR_ERROR.index); + } else { + cache_status.bitSet(jvxPropertyCacheStatusFlagShifts + .JVX_PROPERTY_DESCRIPTOR_INVALID.index); + } + ssUpdateId++; + } + + void invalidateContent(bool error) { + // Remove all flags + cache_status.bitClear( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_CONTENT_VALID.index); + cache_status.bitClear( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_CONTENT_ERROR.index); + cache_status.bitClear( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_CONTENT_INVALID.index); + + // Set the appropriate flag + if (error) { + cache_status.bitSet( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_CONTENT_ERROR.index); + } else { + cache_status.bitSet( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_CONTENT_INVALID.index); + } + ssUpdateId++; + } + + // ============================================================== + // ============================================================== + + @override + void dispose() { + cache_status.bitFClear(); + } +} + +// ===================================================================== +// ===================================================================== + +class AudYoFloPropertyContentBackend extends AudYoFloPropertyDescriptorBackend { + AudYoFloPropertyContentBackend(JvxComponentIdentification assCpIdent, + {String descriptor = '', + jvxDataFormatEnum format = jvxDataFormatEnum.JVX_DATAFORMAT_NONE, + jvxDataTypeSpecEnum dataType = + jvxDataTypeSpecEnum.JVX_DATA_TYPE_SPEC_DOUBLE, + int num = 1}) + : super(assCpIdent, descriptor: descriptor, jvxFormat: format, num: num); + + String toString() { + return 'Not Supported'; + } + + String toHelperString() { + return ''; + } + + // Show if an error occurred on the retrieval of the content + String errDescription = ''; + + jvxDataTypeSpecEnum dataType = jvxDataTypeSpecEnum.JVX_DATA_TYPE_SPEC_DOUBLE; + + jvxPropertyProgressStates setInProgress = + jvxPropertyProgressStates.JVX_PROPERTY_PROGRESS_NONE; + + jvxPropertyProgressStates getInProgress = + jvxPropertyProgressStates.JVX_PROPERTY_PROGRESS_NONE; + + // Show that content is no longer valid. If it is invalid, this may be due to + // a state change. In that case, we need to remove it + @override + bool invalidateOnStateSwitch(jvxStateSwitchEnum ss) { + bool removeFromCache = false; + + switch (reqInvalidateOnStateSwitch) { + case jvxPropertyInvalidateTypeEnum + .JVX_PROPERTY_INVALIDATE_DESCRIPTOR_CONTENT: + invalidateDescriptor(false); + invalidateContent(false); /* invalidate also content */ + break; + + case jvxPropertyInvalidateTypeEnum.JVX_PROPERTY_INVALIDATE_CONTENT: + invalidateContent(false); + break; + // Set the status back to initial + //cache_status.bitFClear(); + default: + int stateFlagNew = 0; + switch (ss) { + case jvxStateSwitchEnum.JVX_STATE_SWITCH_SELECT: + stateFlagNew = jvxState.JVX_STATE_SELECTED; + break; + case jvxStateSwitchEnum.JVX_STATE_SWITCH_ACTIVATE: + stateFlagNew = jvxState.JVX_STATE_ACTIVE; + break; + case jvxStateSwitchEnum.JVX_STATE_SWITCH_PREPARE: + stateFlagNew = jvxState.JVX_STATE_PREPARED; + break; + case jvxStateSwitchEnum.JVX_STATE_SWITCH_START: + stateFlagNew = jvxState.JVX_STATE_PROCESSING; + break; + case jvxStateSwitchEnum.JVX_STATE_SWITCH_STOP: + stateFlagNew = jvxState.JVX_STATE_PREPARED; + break; + case jvxStateSwitchEnum.JVX_STATE_SWITCH_POSTPROCESS: + stateFlagNew = jvxState.JVX_STATE_ACTIVE; + break; + case jvxStateSwitchEnum.JVX_STATE_SWITCH_DEACTIVATE: + stateFlagNew = jvxState.JVX_STATE_SELECTED; + break; + default: + } + if ((allowedStateMask.fld & stateFlagNew) == 0) { + removeFromCache = true; + } + } + return removeFromCache; + } + + /* + int descriptorFromJsonMap(AudYoFloBackendAdapter ref, Map jsonMap) { + int res = jvxErrorType.JVX_NO_ERROR; + + return res; + } + */ +} + +// ================================================================== +// ================================================================== + +abstract class AudYoFloPropertyMultiContentBackend { + T2? fld; + int fldSz = 0; + int numDigits = 4; + AudYoFloPropertyContentBackend get parpropc; +} + +// ================================================================== +// ================================================================== + +// Single string class +class AudYoFloPropertySingleStringBackend + extends AudYoFloPropertyContentBackend { + // Single value string + String value = ''; + + @override + String toString() { + return value; + } + + AudYoFloPropertySingleStringBackend(JvxComponentIdentification assCpIdent) + : super(assCpIdent, format: jvxDataFormatEnum.JVX_DATAFORMAT_STRING); +} + +// ================================================================ +// ================================================================ + +// Hold string list property +class AudYoFloPropertyMultiStringBackend + extends AudYoFloPropertyContentBackend { + // Actual array of entries + List entries = []; + + @override + String toHelperString() { + String out = ''; + for (var elmS in entries) { + if (out.isEmpty) { + out = elmS; + } else { + out = out + ', ' + elmS; + } + } + out = '[' + out + ']'; + return out; + } + + @override + String toString() { + String out = ''; + for (var elmS in entries) { + if (out.isEmpty) { + out = elmS; + } else { + out = out + ', ' + elmS; + } + } + out = '[' + out + ']'; + return out; + } + + // Destructor + @override + void dispose() { + entries.clear(); + super.dispose(); + } + + AudYoFloPropertyMultiStringBackend(JvxComponentIdentification cpId, + {jvxDataFormatEnum format = jvxDataFormatEnum.JVX_DATAFORMAT_STRING_LIST}) + : super(cpId, format: format); +} + +// ================================================================ +// ================================================================ +abstract class AudYoFloPropertyValueInRangeBackend { + double minVal = 0; + double maxVal = 1; + AudYoFloPropertyMultiContentBackend get parpropmc; +} + +// ================================================================ +// ================================================================ + +abstract class AudYoFloPropertySelectionListBackend { + int compileNum32BitBitfield = 0; + AudYoFloBitField selection = allocateAudYoFloBitfield(); + AudYoFloBitField selectable = allocateAudYoFloBitfield(); + AudYoFloBitField exclusive = allocateAudYoFloBitfield(); + AudYoFloPropertyMultiStringBackend get parpropms; +} diff --git a/flutter/ayf_pack/lib/properties/ayf_properties_from_json.dart b/flutter/ayf_pack/lib/properties/ayf_properties_from_json.dart new file mode 100644 index 00000000..b447e43b --- /dev/null +++ b/flutter/ayf_pack/lib/properties/ayf_properties_from_json.dart @@ -0,0 +1,728 @@ +import 'dart:typed_data'; +import 'dart:convert'; +import 'package:fixnum/fixnum.dart' as fn; +import 'package:ayf_pack/ayf_pack.dart'; + +typedef int AudYoFloPropertyContentFieldConverter( + AudYoFloPropertyContentBackend prop, Map newValues); + +class AudYoFloPropertyContentFromJson { + // =========================================================== + // Create a property descriptor (and content) from json map + // This function is the processor of a call to get_decsription + // =========================================================== + static AudYoFloPropertyContentBackend createPropertyFromJsonMap( + JvxComponentIdentification cpId, + AudYoFloBackendAdapterIf ref, + String descriptor, + Map jsonMap, + AudYoFloCompileFlags flags, int numDigits) { + // =================================================== + // Start to read format and size to allocate variable + bool error = false; + AudYoFloPropertyContentBackend? retVal; + jvxDataFormatEnum format = jvxDataFormatEnum.JVX_DATAFORMAT_NONE; + String? typeStr = extractStringFromJson(jsonMap, "format"); + if (typeStr != null) { + format = jvxDataFormatEEnum.fromInt(ref.translateEnumString( + typeStr, jvxDataFormatEEnum.formatName, + flags)); + } else { + error = true; + } + + int num = getIntEntryValueMap(jsonMap, "number_elements"); + if (num < 0) { + error = true; + } + + // If there is an error, return an "error" descriptor + if (error) { + AudYoFloPropertyContentBackend retVal = + AudYoFloPropertyContentBackend(cpId, descriptor: descriptor); + retVal.last_error = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + retVal.cache_status.bitClear(jvxPropertyCacheStatusFlagShifts + .JVX_PROPERTY_DESCRIPTOR_INVALID.index); + retVal.cache_status.bitSet( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_DESCRIPTOR_ERROR.index); + + return retVal; + } + + // Allocate the variable, this resets error code and status to 0 + retVal = allocatePropertyContent(cpId, format, num, flags, numDigits); + + int errCode = updatePropertyDescriptionFromJson( + cpId, ref, retVal, descriptor, jsonMap, flags, false, numDigits); + + return retVal; + } + + static int updatePropertyDescriptionFromJson( + JvxComponentIdentification cpId, + AudYoFloBackendAdapterIf ref, + AudYoFloPropertyContentBackend prop, + String descriptor, + Map jsonMap, + AudYoFloCompileFlags flags, + bool checkFormatNum, + int numDigits) { + String? typeStr; + // Now fill in all properties + + // Reset the flag from invalid to anything else + prop.cache_status.bitClear( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_DESCRIPTOR_INVALID.index); + prop.last_error = jvxErrorType.JVX_NO_ERROR; + + if (checkFormatNum) { + jvxDataFormatEnum format = jvxDataFormatEnum.JVX_DATAFORMAT_NONE; + String? typeStr = extractStringFromJson(jsonMap, "format"); + if (typeStr != null) { + format = jvxDataFormatEEnum.fromInt( + ref.translateEnumString(typeStr, jvxDataFormatEEnum.formatName, flags)); + } else { + prop.last_error = jvxErrorType.JVX_ERROR_PARSE_ERROR; + } + + int num = getIntEntryValueMap(jsonMap, "number_elements"); + if (num < 0) { + return jvxErrorType.JVX_ERROR_PARSE_ERROR; + } + + if ((format != prop.jvxFormat) || (num != prop.num)) { + prop.dispose(); + + // Create new property + prop = allocatePropertyContent(cpId, format, num, flags, numDigits); + } + } + + // Simply use the int value + prop.globalIdx = getIntEntryValueMap(jsonMap, "global_idx"); + if (prop.globalIdx < 0) { + prop.last_error = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + prop.cache_status.bitZSet( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_DESCRIPTOR_ERROR.index); + } + + // Convert to string and map to enum + typeStr = extractStringFromJson(jsonMap, "category"); + if (typeStr != null) { + prop.category = jvxPropertyCategoryTypeEEnum.fromInt( + ref.translateEnumString( + typeStr, jvxPropertyCategoryTypeEEnum.formatName, flags)); + } else { + prop.last_error = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + prop.cache_status.bitZSet( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_DESCRIPTOR_ERROR.index); + } + + // Convert to string and map to enum + typeStr = extractStringFromJson(jsonMap, "decoder_type"); + if (typeStr != null) { + prop.decoderHintType = jvxPropertyDecoderHintTypeEEnum.fromInt( + ref.translateEnumString( + typeStr, jvxPropertyDecoderHintTypeEEnum.formatName, flags)); + } else { + prop.last_error = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + prop.cache_status.bitZSet( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_DESCRIPTOR_ERROR.index); + } + + typeStr = extractStringFromJson(jsonMap, "access_type"); + if (typeStr != null) { + prop.accessType = jvxPropertyAccessTypeEEnum.fromInt(ref + .translateEnumString(typeStr, jvxPropertyAccessTypeEEnum.formatName, flags)); + } else { + prop.last_error = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + prop.cache_status.bitZSet( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_DESCRIPTOR_ERROR.index); + } + + if (prop.cache_status.bitTest( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_DESCRIPTOR_ERROR.index)) { + prop.dispose(); + return prop.last_error; + } else { + prop.valid_parts.fld |= + (1 << jvxPropertyDescriptorEnum.JVX_PROPERTY_DESCRIPTOR_CORE) | + (1 << jvxPropertyDescriptorEnum.JVX_PROPERTY_DESCRIPTOR_MIN); + } + + // ====================================================================== + // ====================================================================== + + // We need to parse into states + bool controlOk = true; + + // Preset to be valid in all states if no additional info is available + prop.allowedStateMask.fld = fn.Int32(jvxState.JVX_STATE_SELECTED | + jvxState.JVX_STATE_ACTIVE | + jvxState.JVX_STATE_PREPARED | + jvxState.JVX_STATE_PROCESSING); + typeStr = extractStringFromJson(jsonMap, "allowed_smask"); + if (typeStr != null) { + var lst = extractTextToStrList(typeStr); + prop.allowedStateMask.reset(); + for (var elm in lst) { + prop.allowedStateMask.fld |= jvxStateEInt.fromStringSingle(elm); + } + } else { + controlOk = false; + } + + // Set the default to all threads if not specified + prop.allowedThreadingMask.fld = fn.Int32(-1); + typeStr = extractStringFromJson(jsonMap, "thread_mask"); + if (typeStr != null) { + prop.allowedThreadingMask.fromHexString(typeStr); + } else { + controlOk = false; + } + + // Property valid + prop.isValid = false; + typeStr = extractStringFromJson(jsonMap, "valid"); + if (typeStr != null) { + prop.isValid = AudYoFloPropertyContentFromString.boolFromString(typeStr); + } else { + controlOk = false; + } + + prop.installable = false; + typeStr = extractStringFromJson(jsonMap, "instable"); + if (typeStr != null) { + prop.installable = + AudYoFloPropertyContentFromString.boolFromString(typeStr); + } else { + controlOk = false; + } + + prop.reqInvalidateOnStateSwitch = + jvxPropertyInvalidateTypeEnum.JVX_PROPERTY_INVALIDATE_INACTIVE; + prop.reqInvalidateOnTest = + jvxPropertyInvalidateTypeEnum.JVX_PROPERTY_INVALIDATE_INACTIVE; + + typeStr = extractStringFromJson(jsonMap, "inval_sswitch"); + if (typeStr != null) { + if (typeStr == '*') { + prop.reqInvalidateOnStateSwitch = + jvxPropertyInvalidateTypeEnum.JVX_PROPERTY_INVALIDATE_CONTENT; + } else { + prop.reqInvalidateOnStateSwitch = + jvxPropertyInvalidateTypeEnum.JVX_PROPERTY_INVALIDATE_INACTIVE; + // AudYoFloPropertyContentFromString.boolFromString(typeStr); + } + } else { + controlOk = false; + } + + typeStr = extractStringFromJson(jsonMap, "inval_test"); + if (typeStr != null) { + if (typeStr == '*') { + prop.reqInvalidateOnTest = + jvxPropertyInvalidateTypeEnum.JVX_PROPERTY_INVALIDATE_CONTENT; + } else { + prop.reqInvalidateOnTest = + jvxPropertyInvalidateTypeEnum.JVX_PROPERTY_INVALIDATE_INACTIVE; + // AudYoFloPropertyContentFromString.boolFromString(typeStr); + } + } else { + controlOk = false; + } + + if (controlOk) { + prop.valid_parts.fld |= + (1 << jvxPropertyDescriptorEnum.JVX_PROPERTY_DESCRIPTOR_CONTROL); + } + + // ====================================================================== + // ====================================================================== + + bool fullOk = true; + // All strings + typeStr = extractStringFromJson(jsonMap, "descriptor"); + if (typeStr != null) { + prop.descriptor = typeStr; + } else { + fullOk = false; + } + + typeStr = extractStringFromJson(jsonMap, "description"); + if (typeStr != null) { + prop.description = typeStr; + } else { + fullOk = false; + } + + typeStr = extractStringFromJson(jsonMap, "name"); + if (typeStr != null) { + prop.name = typeStr; + } else { + fullOk = false; + } + if (fullOk) { + prop.valid_parts.fld |= + (1 << jvxPropertyDescriptorEnum.JVX_PROPERTY_DESCRIPTOR_FULL); + } + // ====================================================================== + // ====================================================================== + + bool fullpOk = true; + typeStr = extractStringFromJson(jsonMap, "acc_flags"); + if (typeStr != null) { + int err = prop.accessFlags.fromHexString(typeStr); + if (err != jvxErrorType.JVX_NO_ERROR) { + fullpOk = false; + } + } else { + fullpOk = false; + } + + typeStr = extractStringFromJson(jsonMap, "cfg_flags"); + if (typeStr != null) { + resultParseInt32 ret32 = resultParseInt32(); + int err = prop.configFlags.fromHexString(typeStr); + if (err != jvxErrorType.JVX_NO_ERROR) { + fullpOk = false; + } + } else { + fullpOk = false; + } + if (fullpOk) { + prop.valid_parts.fld |= + (1 << jvxPropertyDescriptorEnum.JVX_PROPERTY_DESCRIPTOR_FULL_PLUS); + } + // Origin + typeStr = extractStringFromJson(jsonMap, "origin"); + if (typeStr != null) { + prop.origin = typeStr; + } + + if (prop.descriptor != descriptor) { + prop.last_error = jvxErrorType.JVX_ERROR_INVALID_SETTING; + prop.dispose(); + } + + if (prop.last_error == jvxErrorType.JVX_NO_ERROR) { + prop.cache_status.bitSet( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_DESCRIPTOR_VALID.index); + } else { + prop.cache_status.bitSet( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_DESCRIPTOR_ERROR.index); + } + return prop.last_error; + } + + static String removeBraces( + String inStr, String braceLeft, String braceRight) { + String retVal = inStr; + int idxStart = inStr.indexOf(braceLeft); + String rev = inStr.split('').reversed.join(); + int idxStop = rev.indexOf(braceRight); + if ((idxStart >= 0) && (idxStop >= 0)) { + retVal = inStr.substring(idxStart + 1, inStr.length - 1 - idxStop); + } + return retVal; + } + + static String removeBegin(String inStr, String tokenStart) { + String retVal = inStr; + if (tokenStart.length <= inStr.length) { + String cp = inStr.substring(0, tokenStart.length); + if (cp == tokenStart) { + retVal = inStr.substring(tokenStart.length); + } + } + return retVal; + } + + // =========================================================== + // Function to update the content of a property. It expects a + // valid container for the property and fills in whatever comes in. + // =========================================================== + static int updatePropertyContentFromJsonMap( + AudYoFloPropertyContentBackend prop, Map newValues, AudYoFloBackendTranslator trans) { + int errCode = jvxErrorType.JVX_NO_ERROR; + + if (prop.cache_status.bitTest( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_DESCRIPTOR_VALID.index)) { + // Set the status from invalid to something else + prop.cache_status.bitClear( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_CONTENT_INVALID.index); + + switch (prop.jvxFormat) { + // STRINGLIST + case jvxDataFormatEnum.JVX_DATAFORMAT_STRING_LIST: + if (prop is AudYoFloPropertyMultiStringBackend) { + prop.entries.clear(); + List? lst_fld = getListValueMap(newValues, 'property_fld'); + if (lst_fld != null) { + prop.entries = lst_fld.cast(); + errCode = jvxErrorType.JVX_NO_ERROR; + } else { + errCode = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + } + } else { + errCode = jvxErrorType.JVX_ERROR_INVALID_FORMAT; + } + break; + + case jvxDataFormatEnum.JVX_DATAFORMAT_STRING: + if (prop is AudYoFloPropertySingleStringBackend) { + String? txt = getStringEntryValueMap(newValues, 'property'); + if (txt != null) { + prop.value = txt; + } else { + errCode = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + } + } else { + errCode = jvxErrorType.JVX_ERROR_INVALID_FORMAT; + } + break; + + case jvxDataFormatEnum.JVX_DATAFORMAT_SELECTION_LIST: + if (prop is AudYoFloPropertySelectionListBackend) { + AudYoFloPropertySelectionListBackend propSel = + prop as AudYoFloPropertySelectionListBackend; + propSel.parpropms.entries.clear(); + List? lst_fld = getListValueMap(newValues, 'property_fld'); + if (lst_fld != null) { + // Update string list - if it was transferred + propSel.parpropms.entries = lst_fld.cast(); + } + String? txt = getStringEntryValueMap(newValues, 'property'); + if (txt != null) { + List lstFld = txt.split('::'); + if (lstFld.length == 4) { + String selected = lstFld[0]; + String selectable = lstFld[1]; + String exclusive = lstFld[2]; + String numElms = + lstFld[3]; // Number elements entries - could also be 0! + + selected = removeBraces(selected, '[', ']'); + var lstFlds = selected.split(','); + + updatePropertyContentSelListFromJsonMap( + propSel, lstFlds, selectable, exclusive, numElms); + } else { + assert(false); + } + } + } + break; + + case jvxDataFormatEnum.JVX_DATAFORMAT_VALUE_IN_RANGE: + if (prop is AudYoFloPropertyValueInRangeBackend) { + AudYoFloPropertyValueInRangeBackend propVal = + prop as AudYoFloPropertyValueInRangeBackend; + + String? txt = getStringEntryValueMap(newValues, 'property'); + if (txt != null) { + List lstFld = txt.split('::'); + if (lstFld.length == 3) { + String value = lstFld[0]; + String minV = lstFld[1]; + String maxV = lstFld[2]; + + value = removeBraces(value, '[', ']'); + var lstFlds = value.split(','); + + errCode = updatePropertyContentValueInRageFromJsonMap( + propVal, lstFlds, minV, maxV); + } else { + assert(false); + } + } + } + break; + //64 BIT FIELDS + case jvxDataFormatEnum.JVX_DATAFORMAT_64BIT_LE: + case jvxDataFormatEnum.JVX_DATAFORMAT_U64BIT_LE: + case jvxDataFormatEnum.JVX_DATAFORMAT_SIZE: + case jvxDataFormatEnum.JVX_DATAFORMAT_32BIT_LE: + case jvxDataFormatEnum.JVX_DATAFORMAT_U32BIT_LE: + case jvxDataFormatEnum.JVX_DATAFORMAT_16BIT_LE: + case jvxDataFormatEnum.JVX_DATAFORMAT_U16BIT_LE: + case jvxDataFormatEnum.JVX_DATAFORMAT_8BIT: + case jvxDataFormatEnum.JVX_DATAFORMAT_U8BIT: + case jvxDataFormatEnum.JVX_DATAFORMAT_DATA: + errCode = updatePropertyContentFromJsonMap_fields(prop, newValues, trans); + break; + + default: + errCode = jvxErrorType.JVX_ERROR_WRONG_STATE; + break; + } + } else { + // This if the descriptor is invalid + errCode = jvxErrorType.JVX_ERROR_INVALID_SETTING; + } + + prop.last_error = errCode; + if (errCode == jvxErrorType.JVX_NO_ERROR) { + prop.ssUpdateId++; + prop.cache_status.bitSet( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_CONTENT_VALID.index); + } else { + prop.cache_status.bitSet( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_CONTENT_ERROR.index); + prop.ssUpdateId++; + } + return errCode; + } + + // ========================================================================== + // ========================================================================== + + static int updatePropertyContentSelListFromJsonMap( + AudYoFloPropertySelectionListBackend propSel, + List selectedLst, + String selectable, + String exclusive, + String numElms) { + int errCode = jvxErrorType.JVX_NO_ERROR; + if (propSel.selection is AudYoFloBitFieldBackend) { + AudYoFloBitFieldBackend sel = + propSel.selection as AudYoFloBitFieldBackend; + if (sel.bitfield != null) { + errCode = AudYoFloPropertyContentFromString.updateFieldBitField( + sel.bitfield!, + sel.numEntries, + selectedLst, + sel.compileNum32BitBitfield); + } + } + if (errCode != jvxErrorType.JVX_NO_ERROR) return errCode; + + if (propSel.exclusive is AudYoFloBitFieldBackend) { + List selaLst = [selectable]; + AudYoFloBitFieldBackend sela = + propSel.selectable as AudYoFloBitFieldBackend; + if (sela.bitfield != null) { + errCode = AudYoFloPropertyContentFromString.updateFieldBitField( + sela.bitfield!, + sela.numEntries, + selaLst, + sela.compileNum32BitBitfield); + } + } + if (errCode != jvxErrorType.JVX_NO_ERROR) return errCode; + + if (propSel.exclusive is AudYoFloBitFieldBackend) { + List exclusiveLst = [exclusive]; + AudYoFloBitFieldBackend excl = + propSel.exclusive as AudYoFloBitFieldBackend; + if (excl.bitfield != null) { + errCode = AudYoFloPropertyContentFromString.updateFieldBitField( + excl.bitfield!, + excl.numEntries, + exclusiveLst, + excl.compileNum32BitBitfield); + } + } + if (errCode != jvxErrorType.JVX_NO_ERROR) return errCode; + + int? ll = int.tryParse(numElms); + if (ll != null) { + if (propSel.parpropms.entries.length != ll) { + // What to do here? + assert(false); + } + } + return errCode; + } + + static int updatePropertyContentValueInRageFromJsonMap( + AudYoFloPropertyValueInRangeBackend propVal, + List valueLst, + String minV, + String maxV) { + int errCode = jvxErrorType.JVX_NO_ERROR; + propVal.minVal = double.parse(minV); + propVal.maxVal = double.parse(maxV); + var valFld = propVal.parpropmc; + if (valFld is AudYoFloPropertyMultiContentBackend) { + var valFldFlt = + valFld as AudYoFloPropertyMultiContentBackend; + + // We might reuse this function here + // AudYoFloPropertyContentFromJson.convertTextToField( + // prop.fld, prop.fldSz, newValues, prop.decoderHintType); + if (valueLst.length != valFldFlt.fldSz) { + errCode = jvxErrorType.JVX_ERROR_POSTPONE; + return errCode; + } + + for (var idx = 0; idx < valueLst.length; idx++) { + var errCodeLoc = + AudYoFloPropertyContentFromString.convertElementFldListDouble( + valFldFlt.fld!, idx, valueLst[idx]); + if (errCodeLoc != jvxErrorType.JVX_NO_ERROR) errCode = errCodeLoc; + } + } + else if (valFld is AudYoFloPropertyMultiContentBackend) { + var valFldFlt = + valFld as AudYoFloPropertyMultiContentBackend; + + // We might reuse this function here + // AudYoFloPropertyContentFromJson.convertTextToField( + // prop.fld, prop.fldSz, newValues, prop.decoderHintType); + if (valueLst.length != valFldFlt.fldSz) { + errCode = jvxErrorType.JVX_ERROR_POSTPONE; + return errCode; + } + + for (var idx = 0; idx < valueLst.length; idx++) { + var errCodeLoc = + AudYoFloPropertyContentFromString.convertElementFldListFloat( + valFldFlt.fld!, idx, valueLst[idx]); + if (errCodeLoc != jvxErrorType.JVX_NO_ERROR) errCode = errCodeLoc; + } + } + return errCode; + } + + static List extractTextToStrList(String txt) { + var lst = txt.split('['); + if (lst.length == 2) lst = lst[1].split(']'); + String txtRemain = lst[0]; + lst = txtRemain.split(','); + return lst; + } + + static int convertTextToField(T1? fldData, int fldSz, + Map newValues, jvxPropertyDecoderHintTypeEnum decHtTp, AudYoFloBackendTranslator trans) { + int errCode = jvxErrorType.JVX_NO_ERROR; + // Get the content + String? base64Str = extractStringFromJson(newValues, 'property_fld'); + if (base64Str != null) { + // Coming as a base64 string + var lst64 = base64Decode(base64Str); + if(fldData is Float32List) + { + Float32List tLst = lst64.buffer.asFloat32List(); + for(var i = 0; i < fldData.length; i++) + { + if(i < tLst.length) { + fldData[i] = tLst[i]; + } + else + { + // report error! + } + } + } + else if(fldData is Float64List) + { + Float64List tLst = lst64.buffer.asFloat64List(); + for(var i = 0; i < fldData.length; i++) + { + if(i < tLst.length) { + fldData[i] = tLst[i]; + } + else + { + // report error! + } + } + } + errCode = jvxErrorType.JVX_NO_ERROR; + } else { + String? txt = getStringEntryValueMap(newValues, 'property'); + if (txt != null) { + int numElms = getIntEntryValueMap(newValues, 'number_elements'); + int offs = getIntEntryValueMap(newValues, 'offset'); + + if (numElms != fldSz) { + errCode = jvxErrorType.JVX_ERROR_POSTPONE; + return errCode; + } + // Parse result + // Remove leading and trailing [] + var lst = txt.split('['); + if (lst.length == 2) lst = lst[1].split(']'); + String txtRemain = lst[0]; + lst = txtRemain.split(','); + + for (int idx = 0; idx < numElms; idx++) { + String token = '0'; + if (idx < lst.length) { + token = lst[idx]; + } + int idxFld = offs + idx; + if (idxFld < fldSz) { + if (fldData != null) { + errCode = jvxErrorType.JVX_ERROR_INVALID_ARGUMENT; + if (T1 == Int64List) { + errCode = AudYoFloPropertyContentFromString + .convertElementFldListInt64( + fldData as Int64List, idx, token); + } else { + if (T1 == Uint64List) { + errCode = AudYoFloPropertyContentFromString + .convertElementFldListUint64( + fldData as Uint64List, idx, token); + } else { + if (T1 == Int32List) { + errCode = AudYoFloPropertyContentFromString + .convertElementFldListInt32( + fldData as Int32List, idx, token, decHtTp); + } else { + if (T1 == Uint32List) { + errCode = AudYoFloPropertyContentFromString + .convertElementFldListUint32( + fldData as Uint32List, idx, token); + } else { + if (T1 == Int16List) { + errCode = AudYoFloPropertyContentFromString + .convertElementFldListInt16( + fldData as Int16List, idx, token, decHtTp, trans); + } else { + if (T1 == Uint16List) { + errCode = AudYoFloPropertyContentFromString + .convertElementFldListUint16( + fldData as Uint16List, idx, token, decHtTp, trans); + } else { + if (T1 == Int8List) { + errCode = AudYoFloPropertyContentFromString + .convertElementFldListInt8( + fldData as Int8List, idx, token); + } else { + if (T1 == Uint8List) { + errCode = AudYoFloPropertyContentFromString + .convertElementFldListUint8( + fldData as Uint8List, idx, token); + } else { + if (T1 == Float64List) { + errCode = AudYoFloPropertyContentFromString + .convertElementFldListDouble( + fldData as Float64List, idx, token); + } else { + if (T1 == Float32List) { + errCode = AudYoFloPropertyContentFromString + .convertElementFldListFloat( + fldData as Float32List, idx, token); + } + } + } + } + } + } + } + } + } + } + } + if (errCode != jvxErrorType.JVX_NO_ERROR) return errCode; + } else { + errCode = jvxErrorType.JVX_ERROR_ID_OUT_OF_BOUNDS; + } + } + } + } + return errCode; + } +} diff --git a/flutter/ayf_pack/lib/properties/ayf_properties_from_string.dart b/flutter/ayf_pack/lib/properties/ayf_properties_from_string.dart new file mode 100644 index 00000000..62212f3d --- /dev/null +++ b/flutter/ayf_pack/lib/properties/ayf_properties_from_string.dart @@ -0,0 +1,196 @@ +import 'dart:typed_data'; +import 'package:fixnum/fixnum.dart'; +import '../ayf_pack_local.dart'; + +class AudYoFloPropertyContentFromString { + static int convertElementFldListInt64(Int64List fld, int idx, String token) { + int? valI = int.tryParse(token); + if (valI != null) { + fld[idx] = valI.toSigned(64); + return jvxErrorType.JVX_NO_ERROR; + } + return jvxErrorType.JVX_ERROR_PARSE_ERROR; + } + + static int convertElementFldListUint64( + Uint64List fld, int idx, String token) { + int? valI = int.tryParse(token); + if (valI == null) { + + if(token == "unselected") + { + valI = -1; + } + } + if (valI != null) { + fld[idx] = valI.toUnsigned(64); + return jvxErrorType.JVX_NO_ERROR; + } + return jvxErrorType.JVX_ERROR_PARSE_ERROR; + } + + static int convertElementFldListInt32(Int32List fld, int idx, String token, + jvxPropertyDecoderHintTypeEnum decHtTp) { + int? valI = int.tryParse(token); + if (valI != null) { + fld[idx] = valI.toSigned(32); + return jvxErrorType.JVX_NO_ERROR; + } else { + if (decHtTp == + jvxPropertyDecoderHintTypeEnum + .JVX_PROPERTY_DECODER_VALUE_OR_DONTCARE) { + if (token == 'dontcare') { + fld[idx] = -1; + return jvxErrorType.JVX_NO_ERROR; + } + } + } + return jvxErrorType.JVX_ERROR_PARSE_ERROR; + } + + static int convertElementFldListUint32( + Uint32List fld, int idx, String token) { + int? valI = int.tryParse(token); + if (valI != null) { + fld[idx] = valI.toUnsigned(32); + return jvxErrorType.JVX_NO_ERROR; + } + return jvxErrorType.JVX_ERROR_PARSE_ERROR; + } + + static int convertElementFldListInt16(Int16List fld, int idx, String token, + jvxPropertyDecoderHintTypeEnum decHtTp, AudYoFloBackendTranslator trans) { + int? valI = int.tryParse(token); + valI ??= decodeTextDecoderTypes(token, decHtTp, trans); + + if (valI != null) { + fld[idx] = valI.toSigned(16); + return jvxErrorType.JVX_NO_ERROR; + } + return jvxErrorType.JVX_ERROR_PARSE_ERROR; + } + + static int convertElementFldListUint16( + Uint16List fld, int idx, String token, jvxPropertyDecoderHintTypeEnum decTp, AudYoFloBackendTranslator trans) { + int? valI = int.tryParse(token); + valI ??= decodeTextDecoderTypes(token, decTp, trans); + + if (valI != null) { + fld[idx] = valI.toUnsigned(16); + return jvxErrorType.JVX_NO_ERROR; + } + return jvxErrorType.JVX_ERROR_PARSE_ERROR; + } + + static int convertElementFldListInt8(Int8List fld, int idx, String token) { + int? valI = int.tryParse(token); + if (valI != null) { + fld[idx] = valI.toSigned(8); + return jvxErrorType.JVX_NO_ERROR; + } + return jvxErrorType.JVX_ERROR_PARSE_ERROR; + } + + static int convertElementFldListUint8(Uint8List fld, int idx, String token) { + int? valI = int.tryParse(token); + if (valI != null) { + fld[idx] = valI.toUnsigned(8); + return jvxErrorType.JVX_NO_ERROR; + } + return jvxErrorType.JVX_ERROR_PARSE_ERROR; + } + + static int convertElementFldListDouble( + Float64List fld, int idx, String token) { + double? valD = double.tryParse(token); + if (valD != null) { + fld[idx] = valD; + return jvxErrorType.JVX_NO_ERROR; + } + return jvxErrorType.JVX_ERROR_PARSE_ERROR; + } + + static int convertElementFldListFloat( + Float32List fld, int idx, String token) { + double? valD = double.tryParse(token); + if (valD != null) { + fld[idx] = valD; + return jvxErrorType.JVX_NO_ERROR; + } + return jvxErrorType.JVX_ERROR_PARSE_ERROR; + } + + static int updateFieldBitField(Uint32List buf, int numEntries, + List bitfldTextLst, int compileNum32BitBitfield) { + int errCode = jvxErrorType.JVX_NO_ERROR; + assert(numEntries == bitfldTextLst.length); + int offs = 0; + for (var token in bitfldTextLst) { + // int idxEntry = posi >> 5; + // int inWordIdx = posi - (idxEntry << 5); + // int inFldIdx = compileNum32BitBitfield - 1 - idxEntry; + // inFldIdx += offset * compileNum32BitBitfield; + + var bitfldText = token; + String prefix = bitfldText.substring(0, 2); + + if (prefix == '0x') { + String content = bitfldText.substring(2); + int ll = content.length; + int numEntriesW32 = ll >> 3; // /8 + + // Reset the bitfield partials + for (int idx = 0; idx < compileNum32BitBitfield; idx++) { + var idxInBuf = compileNum32BitBitfield - idx - 1 + offs; + buf[idxInBuf] = 0; + } + + for (int idx = 0; idx < numEntriesW32; idx++) { + String partStr = content.substring(ll - 8 * (idx + 1), ll - 8 * idx); + Int64 val = Int64.parseHex( + partStr); // better tryParseHex but it does not exist + //if (val != null) { + var idxInBuf = compileNum32BitBitfield - idx - 1 + offs; + buf[idxInBuf] = val.toUnsigned(32).toInt(); + //} else { + // errCode = jvxErrorType.JVX_ERROR_PARSE_ERROR; + //} + } + } else { + errCode = jvxErrorType.JVX_ERROR_PARSE_ERROR; + } + offs += compileNum32BitBitfield; + } + return errCode; + } + + static bool boolFromString(String str) { + if ((str == '*') || (str == 'yes')) { + return true; + } + return false; + } + + static int? decodeTextDecoderTypes(String token, jvxPropertyDecoderHintTypeEnum decTp, AudYoFloBackendTranslator trans) +{ + int? valI; + switch (decTp) { + case jvxPropertyDecoderHintTypeEnum.JVX_PROPERTY_DECODER_SIMPLE_ONOFF: + if (token == "yes") { + valI = 1; + } else { + valI = 0; + } + break; + case jvxPropertyDecoderHintTypeEnum.JVX_PROPERTY_DECODER_FORMAT_IDX: + valI = trans.translateEnumString(token, 'jvxDataFormat', trans.compileFlags() ); + break; + default: + dbgPrint( + 'Failed to decode text string <$token> for property of type <${decTp.toString()}'); + break; + } + return valI; + } + +} diff --git a/flutter/ayf_pack/lib/properties/ayf_properties_local.dart b/flutter/ayf_pack/lib/properties/ayf_properties_local.dart new file mode 100644 index 00000000..0e7a418b --- /dev/null +++ b/flutter/ayf_pack/lib/properties/ayf_properties_local.dart @@ -0,0 +1,132 @@ +import '../ayf_pack_local.dart'; + +enum jvxPropertyCacheStatusFlagShifts { + JVX_PROPERTY_DESCRIPTOR_INVALID, + JVX_PROPERTY_DESCRIPTOR_VALID, + JVX_PROPERTY_DESCRIPTOR_ERROR, + + JVX_PROPERTY_CONTENT_INVALID, + JVX_PROPERTY_CONTENT_VALID, + JVX_PROPERTY_CONTENT_ERROR, + + JVX_PROPERTY_CONTENT_SET +} + +enum jvxPropertyDescriptorTypeEnum { + JVX_PROPERTY_MIN_DESCRIPTOR, + JVX_PROPERTY_BE_DESCRIPTOR +} + +class AudYoFloPropertyContainer { + int ssUpdateId = 0; + + // Cache status to hold indicators for every state + AudYoFloCBitField32 cache_status = AudYoFloCBitField32(); + + jvxPropertyDescriptorTypeEnum descrType = + jvxPropertyDescriptorTypeEnum.JVX_PROPERTY_MIN_DESCRIPTOR; + + // Associated component, required for set_property + JvxComponentIdentification assCpIdent = JvxComponentIdentification(); + + // Format + jvxDataFormatEnum jvxFormat = jvxDataFormatEnum.JVX_DATAFORMAT_NONE; + +// Number element + int num = 0; + +// Description of the property + String description = ''; + + // Descriptor to address + String descriptor; + + AudYoFloPropertyContainer( + this.jvxFormat, this.num, this.descriptor, this.description); + + // Some interface functions + void dispose() {} + + bool invalidateOnStateSwitch(jvxStateSwitchEnum ss) { + return false; + } + + bool checkValidDescriptor() { + return cache_status.bitTest( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_DESCRIPTOR_VALID.index); + } + + bool checkValidContent() { + return cache_status.bitTest( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_CONTENT_VALID.index); + } + + bool checkInvalidContent() { + return cache_status.bitTest( + jvxPropertyCacheStatusFlagShifts.JVX_PROPERTY_CONTENT_INVALID.index); + } +} + +class AudYoFloPropertyContentLocal extends AudYoFloPropertyContainer { + AudYoFloPropertyContentLocal( + jvxDataFormatEnum format, int num, String descriptor, String description) + : super(format, num, descriptor, description); + + @override + String toString() { + return 'not-implemented'; + } +} + +class AudYoFloPropertyStringLocal extends AudYoFloPropertyContentLocal { + String value; + + AudYoFloPropertyStringLocal(this.value, String descriptor, String description) + : super(jvxDataFormatEnum.JVX_DATAFORMAT_STRING, 1, descriptor, + description); + + @override + String toString() { + return value; + } +} + +class AudYoFloPropertyValue extends AudYoFloPropertyContentLocal { + double value; + + AudYoFloPropertyValue(this.value, String descriptor, String description) + : super(jvxDataFormatEnum.JVX_DATAFORMAT_STRING, 1, descriptor, + description); + + @override + String toString() { + return value.toString(); + } +} + +class AudYoFloPropertySelectionListLocal extends AudYoFloPropertyContentLocal { + List entries; + AudYoFloBitField selection = allocateAudYoFloBitfield(); + jvxPropertyDecoderHintTypeEnum decTp; + AudYoFloPropertySelectionListLocal( + this.entries, String descriptor, String description, this.decTp) + : super(jvxDataFormatEnum.JVX_DATAFORMAT_SELECTION_LIST, 1, descriptor, + description); + + @override + String toString() { + String out = ''; + for (var elmS in entries) { + if (out.isEmpty) { + out = elmS; + } else { + out = '$out, $elmS'; + } + } + out = '[$out]'; + return out; + } +} + +// ============================================================================ + diff --git a/flutter/ayf_pack/lib/properties/ayf_properties_to_string.dart b/flutter/ayf_pack/lib/properties/ayf_properties_to_string.dart new file mode 100644 index 00000000..2414b01b --- /dev/null +++ b/flutter/ayf_pack/lib/properties/ayf_properties_to_string.dart @@ -0,0 +1,214 @@ +import 'dart:math'; +import 'dart:typed_data'; +import 'package:fixnum/fixnum.dart'; +import '../ayf_pack_local.dart'; + +class AudYoFloPropertyContentToString { + // Currently not supported by backend! + static String convertPropMultiStringToString( + AudYoFloPropertyMultiStringBackend prop) { + String retVal = '['; + for (var elm in prop.entries) { + if (elm != prop.entries.first) { + retVal += ','; + } + retVal += elm; + } + retVal += ']'; + return retVal; + } + + static String convertPropSelectionListToString( + AudYoFloPropertySelectionListBackend prop, bool contentOnly) { + String retVal = ''; + bool multiCont = false; + if (prop.selection is AudYoFloBitFieldBackend) { + AudYoFloBitFieldBackend sel = prop.selection as AudYoFloBitFieldBackend; + multiCont = (sel.numEntries > 1); + if (multiCont) { + retVal += '['; + } + String oneEntry = ''; + for (var idx = 0; idx < sel.numEntries; idx++) { + if(idx > 0) { + retVal += ","; + } + for (var idxi = 0; idxi < sel.compileNum32BitBitfield; idxi++) { + + // For multi selections, consider the offset!! + int idxO = idx * sel.compileNum32BitBitfield + idxi; + if ((sel.bitfield![idxO] != 0) || + (idxi == sel.compileNum32BitBitfield - 1)) { + // Make sure you see 8 digits per token!! + String tok = sel.bitfield![idxO].toRadixString(16); + for (int ii = tok.length; ii < 8; ii++) { + oneEntry += '0'; + } + oneEntry += tok; + } + } + + oneEntry = '0x' + oneEntry; + retVal += oneEntry; + oneEntry = ''; + } + if (multiCont) { + retVal += ']'; + } + } + + if (!contentOnly) { + AudYoFloBitFieldBackend sela = prop.selectable as AudYoFloBitFieldBackend; + String oneEntry = ''; + for (var idxi = 0; idxi < sela.compileNum32BitBitfield; idxi++) { + if ((sela.bitfield![idxi] != 0) || + (idxi == sela.compileNum32BitBitfield - 1)) { + // Make sure you see 8 digits per token!! + String tok = sela.bitfield![idxi].toRadixString(16); + for (int ii = tok.length; ii < 8; ii++) { + oneEntry += '0'; + } + oneEntry += tok; + } + } + oneEntry = '0x' + oneEntry; + retVal += '::' + oneEntry; + + AudYoFloBitFieldBackend excl = prop.selectable as AudYoFloBitFieldBackend; + oneEntry = ''; + for (var idxi = 0; idxi < excl.compileNum32BitBitfield; idxi++) { + if ((excl.bitfield![idxi] != 0) || + (idxi == excl.compileNum32BitBitfield - 1)) { + // Make sure you see 8 digits per token!! + String tok = excl.bitfield![idxi].toRadixString(16); + for (int ii = tok.length; ii < 8; ii++) { + oneEntry += '0'; + } + oneEntry += tok; + } + } + oneEntry = '0x' + oneEntry; + retVal += '::' + oneEntry; + + oneEntry = ''; + for (var elm in prop.parpropms.entries) { + if (elm != prop.parpropms.entries.first) { + oneEntry += ':'; + } + retVal += '::' + oneEntry; + } + } + return retVal; + } + + static String? convertPropValueInRangeToString( + AudYoFloPropertyValueInRangeBackend prop, bool contentOnly, + {int precision = 4}) { + String? retVal = + convertPropMultiContentLst(prop.parpropmc.fld, precision: precision); + if (retVal != null) { + if (!contentOnly) { + retVal += '::' + prop.minVal.toString(); + retVal += '::' + prop.maxVal.toString(); + } + } + return retVal; + } + + static String? convertPropMultiContentLst(dynamic lst, {int offset = 0, int num = -1, int precision = 4}) { + String retVal = ''; + bool multiCont = false; + if (lst is List) { + multiCont = (lst.length > 1); + } + if (multiCont) { + retVal = '['; + } + + int numCopy = lst.length - offset; + if(num >= 0) + { + numCopy = min(numCopy, num); + } + + if (lst is Float64List) { + for (var i=0;i(cpId, num, form); + } + return AudYoFloPropertyMultiContentWeb(cpId, num, form); + + // Uint64List and Int64List not supported in "web" - use int64 instead + + case jvxDataFormatEnum.JVX_DATAFORMAT_16BIT_LE: + return AudYoFloPropertyMultiContentWeb(cpId, num, form); + case jvxDataFormatEnum.JVX_DATAFORMAT_8BIT: + return AudYoFloPropertyMultiContentWeb(cpId, num, form); + case jvxDataFormatEnum.JVX_DATAFORMAT_32BIT_LE: + return AudYoFloPropertyMultiContentWeb(cpId, num, form); + case jvxDataFormatEnum.JVX_DATAFORMAT_64BIT_LE: + return AudYoFloPropertyMultiContentWeb(cpId, num, form); + case jvxDataFormatEnum.JVX_DATAFORMAT_U16BIT_LE: + return AudYoFloPropertyMultiContentWeb(cpId, num, form); + case jvxDataFormatEnum.JVX_DATAFORMAT_U8BIT: + return AudYoFloPropertyMultiContentWeb(cpId, num, form); + case jvxDataFormatEnum.JVX_DATAFORMAT_U32BIT_LE: + return AudYoFloPropertyMultiContentWeb(cpId, num, form); + case jvxDataFormatEnum.JVX_DATAFORMAT_U64BIT_LE: + return AudYoFloPropertyMultiContentWeb(cpId, num, form); + case jvxDataFormatEnum.JVX_DATAFORMAT_SIZE: + return AudYoFloPropertyMultiContentWeb(cpId, num, form); + case jvxDataFormatEnum.JVX_DATAFORMAT_STRING_LIST: + return AudYoFloPropertyMultiStringBackend(cpId); + case jvxDataFormatEnum.JVX_DATAFORMAT_STRING: + return AudYoFloPropertySingleStringBackend(cpId); + case jvxDataFormatEnum.JVX_DATAFORMAT_SELECTION_LIST: + return AudYoFloPropertySelectionListWeb( + cpId, flags.compileNum32BitBitfield, num); + case jvxDataFormatEnum.JVX_DATAFORMAT_VALUE_IN_RANGE: + if (flags.compileDataTypeSpec == + jvxDataTypeSpecEnum.JVX_DATA_TYPE_SPEC_DOUBLE) { + return AudYoFloPropertyValueInRangeWeb(cpId, num); + } + return AudYoFloPropertyValueInRangeWeb(cpId, num); + + //return AudYoFloPropertyVal(cpId, flags.compileNum32BitBitfield); + + default: + break; + } + + return AudYoFloPropertyContentBackend(cpId); +} + +// ==================================================================== +// ==================================================================== + +// Class to hold a vector/array property, template to use one code for all types +// but remain type stable +class AudYoFloPropertyMultiContentWeb + extends AudYoFloPropertyContentBackend + with AudYoFloPropertyMultiContentBackend { + AudYoFloPropertyContentBackend get parpropc => this; + AudYoFloPropertyMultiContentWeb( + JvxComponentIdentification cpId, int sz, jvxDataFormatEnum formatArg) + : super(cpId, format: formatArg, num: sz) { + // In the rebuild function the fldFrmt is set according to the "real" format, not the + // "jvxrt" format. + rebuild(sz); + } + + @override + String toHelperString() { + String txt = fldSz.toString() + ' entries of type ' + jvxFormat.toString(); + return txt; + } + + @override + String toString() { + String retVal = ''; + if (fld is Float32List) { + Float32List ptr = fld as Float32List; + for (int idx = 0; idx < fldSz; idx++) { + if (retVal.isNotEmpty) { + retVal += ', '; + } + retVal += ptr.elementAt(idx).toStringAsFixed(numDigits); + } + } + if (fld is Float64List) { + Float64List ptr = fld as Float64List; + for (int idx = 0; idx < fldSz; idx++) { + if (retVal.isNotEmpty) { + retVal += ', '; + } + retVal += ptr.elementAt(idx).toStringAsFixed(numDigits); + } + } + // ================================================================ + if (fld is Int8List) { + Int8List ptr = fld as Int8List; + for (int idx = 0; idx < fldSz; idx++) { + if (retVal.isNotEmpty) { + retVal += ', '; + } + retVal += ptr.elementAt(idx).toString(); + } + } + if (fld is Int16List) { + Int16List ptr = fld as Int16List; + for (int idx = 0; idx < fldSz; idx++) { + if (retVal.isNotEmpty) { + retVal += ', '; + } + retVal += ptr.elementAt(idx).toString(); + } + } + if (fld is Int32List) { + Int32List ptr = fld as Int32List; + for (int idx = 0; idx < fldSz; idx++) { + if (retVal.isNotEmpty) { + retVal += ', '; + } + retVal += ptr.elementAt(idx).toString(); + } + } + if (fld is Int64List) { + Int64List ptr = fld as Int64List; + for (int idx = 0; idx < fldSz; idx++) { + if (retVal.isNotEmpty) { + retVal += ', '; + } + retVal += ptr.elementAt(idx).toString(); + } + } + // ---------------------------------------- + if (fld is Uint8List) { + Uint8List ptr = fld as Uint8List; + for (int idx = 0; idx < fldSz; idx++) { + if (retVal.isNotEmpty) { + retVal += ', '; + } + retVal += ptr.elementAt(idx).toString(); + } + } + if (fld is Uint16List) { + Uint16List ptr = fld as Uint16List; + + for (int idx = 0; idx < fldSz; idx++) { + if (retVal.isNotEmpty) { + retVal += ', '; + } + retVal += ptr.elementAt(idx).toString(); + } + } + if (fld is Uint32List) { + Uint32List ptr = fld as Uint32List; + for (int idx = 0; idx < fldSz; idx++) { + if (retVal.isNotEmpty) { + retVal += ', '; + } + retVal += ptr.elementAt(idx).toString(); + } + } else if (fld is Uint64List) { + Uint64List ptr = fld as Uint64List; + for (int idx = 0; idx < fldSz; idx++) { + if (retVal.isNotEmpty) { + retVal += ', '; + } + retVal += ptr.elementAt(idx).toString(); + } + } + retVal = '[' + retVal + ']'; + return retVal; + } + + void rebuild(int newSize) { + if (fldSz != newSize) { + if (fldSz != 0) { + dispose(); + } + } + + if (newSize != 0) { + recreateCore(newSize); + } + } + + // Destructor. MUST BE CALLED MANUALLY ANYWAY, flutter does not + // have destructors due to comaptibility with javascript, + // https://github.com/dart-lang/sdk/issues/3691 + @override + void dispose() { + if (fld != null) { + fld!.clear(); + } + fldSz = 0; + super.num = 0; + super.dispose(); + } + + void recreateCore(int newSize) { + fldSz = newSize; + super.num = newSize; + + if (T2 == Float32List) { + fld = Float32List(fldSz) as T2; + } else if (T2 == Float64List) { + fld = Float64List(fldSz) as T2; + } else if (T2 == Int8List) { + fld = Int8List(fldSz) as T2; + } else if (T2 == Int16List) { + fld = Int16List(fldSz) as T2; + } else if (T2 == Int32List) { + fld = Int32List(fldSz) as T2; + } else if (T2 == Int64List) { + fld = Int64List(fldSz) as T2; + } else if (T2 == Uint8List) { + fld = Uint8List(fldSz) as T2; + } else if (T2 == Uint16List) { + fld = Uint16List(fldSz) as T2; + } else if (T2 == Uint32List) { + fld = Uint32List(fldSz) as T2; + } else if (T2 == Uint64List) { + fld = Uint64List(fldSz) as T2; + } else { + assert(false); + } + } +} + +// ================================================================ +// ================================================================ + +// Class to hold a selection list which is string list + selection entries +class AudYoFloPropertySelectionListWeb + extends AudYoFloPropertyMultiStringBackend + with AudYoFloPropertySelectionListBackend { + @override + String toHelperString() { + if (selection is AudYoFloBitFieldSimple) { + AudYoFloBitFieldSimple sel = selection as AudYoFloBitFieldSimple; + return sel.numEntries.toString() + + ' entries among options ' + + super.toHelperString(); + } else { + return ' this; + + @override + String toString() { + int idx = 0; + String out = ''; + + // Loop over the number of options. Typically, there is one but there might be more! + for (; idx < super.num; idx++) { + // in the current option.. + int posi = 0; + String outOne = ''; + for (var elmS in entries) { + // Always use offset "0" since we shift the pointer manually (line + 15) + if (selection.bitTest(posi, offset: idx)) { + if (outOne.isEmpty) { + outOne = elmS; + } else { + outOne = outOne + ', ' + elmS; + } + } + posi++; + } + if (out.isEmpty) { + out = outOne; + } else { + out = out + '; ' + outOne; + } + } + out = '[' + out + ']'; + return out; + } + + // =============================================================== + AudYoFloPropertySelectionListWeb( + JvxComponentIdentification cpId, int compileNum32BitBitfieldArg, int num) + : super(cpId, format: jvxDataFormatEnum.JVX_DATAFORMAT_SELECTION_LIST) { + compileNum32BitBitfield = compileNum32BitBitfieldArg; + resize(num, false, false); + // selection.initialize(compileNum32BitBitfield); + selectable.initialize(compileNum32BitBitfield, numEntriesArg: 1); + exclusive.initialize(compileNum32BitBitfield, numEntriesArg: 1); + } + + void resize(int numArg, bool copyOld, bool forceNew) { + // In this case the selection list deviates from the string list: + // There might be a 0-length selection field + super.num = numArg; + if (selection is AudYoFloBitFieldBackend) { + AudYoFloBitFieldSimple sel = selection as AudYoFloBitFieldSimple; + if (super.num > 0) { + if (forceNew || (num != sel.numEntries)) { + AudYoFloBitField newSelection = AudYoFloBitFieldSimple(); + assert(numArg > 0); + newSelection.initialize(compileNum32BitBitfield, + numEntriesArg: numArg); + int idx = 0; + if (copyOld) { + int numCpy = min(numArg, sel.numEntries); + for (; idx < numCpy; idx++) { + newSelection.copy(selection, offset: idx); + } + } + selection.dispose(); + selection = newSelection; + } + } + } else { + assert(false); + } + } +} + +// ======================================================================== + +class AudYoFloPropertyValueInRangeWeb + extends AudYoFloPropertyMultiContentWeb + with AudYoFloPropertyValueInRangeBackend { + AudYoFloPropertyValueInRangeWeb(JvxComponentIdentification cpId, int sz) + : super(cpId, sz, jvxDataFormatEnum.JVX_DATAFORMAT_VALUE_IN_RANGE); + + @override + AudYoFloPropertyMultiContentBackend get parpropmc => this; +} diff --git a/flutter/ayf_pack/lib/properties/web/ayf_properties_from_json_simple.dart b/flutter/ayf_pack/lib/properties/web/ayf_properties_from_json_simple.dart new file mode 100644 index 00000000..339df083 --- /dev/null +++ b/flutter/ayf_pack/lib/properties/web/ayf_properties_from_json_simple.dart @@ -0,0 +1,120 @@ +import 'dart:typed_data'; +import 'package:ayf_pack/ayf_pack_web.dart'; +import 'package:ayf_pack/properties/web/ayf_properties.dart'; +import 'package:fixnum/fixnum.dart' as fn; +import '../../ayf_pack_local.dart'; + +class AudYoFloPropertyContentFromJsonSimple { + static int updatePropertyContentFromJsonMap( + AudYoFloPropertyContentBackend prop, Map newValues, AudYoFloBackendTranslator trans) { + int errCode = jvxErrorType.JVX_NO_ERROR; + switch (prop.jvxFormat) { + case jvxDataFormatEnum.JVX_DATAFORMAT_64BIT_LE: + if (prop is AudYoFloPropertyMultiContentWeb) { + errCode = + AudYoFloPropertyContentFromJson.convertTextToField( + prop.fld, prop.fldSz, newValues, prop.decoderHintType, trans); + } else { + errCode = jvxErrorType.JVX_ERROR_INVALID_FORMAT; + } + break; + case jvxDataFormatEnum.JVX_DATAFORMAT_U64BIT_LE: + case jvxDataFormatEnum.JVX_DATAFORMAT_SIZE: + if (prop is AudYoFloPropertyMultiContentWeb) { + errCode = + AudYoFloPropertyContentFromJson.convertTextToField( + prop.fld, prop.fldSz, newValues, prop.decoderHintType, trans); + } else { + errCode = jvxErrorType.JVX_ERROR_INVALID_FORMAT; + } + break; + + //32 BIT FIELDS + case jvxDataFormatEnum.JVX_DATAFORMAT_32BIT_LE: + if (prop is AudYoFloPropertyMultiContentWeb) { + errCode = + AudYoFloPropertyContentFromJson.convertTextToField( + prop.fld, prop.fldSz, newValues, prop.decoderHintType, trans); + } else { + errCode = jvxErrorType.JVX_ERROR_INVALID_FORMAT; + } + break; + case jvxDataFormatEnum.JVX_DATAFORMAT_U32BIT_LE: + if (prop is AudYoFloPropertyMultiContentWeb) { + errCode = + AudYoFloPropertyContentFromJson.convertTextToField( + prop.fld, prop.fldSz, newValues, prop.decoderHintType, trans); + } else { + errCode = jvxErrorType.JVX_ERROR_INVALID_FORMAT; + } + break; + + // 16BIT FIELDS + case jvxDataFormatEnum.JVX_DATAFORMAT_16BIT_LE: + if (prop is AudYoFloPropertyMultiContentWeb) { + errCode = + AudYoFloPropertyContentFromJson.convertTextToField( + prop.fld, prop.fldSz, newValues, prop.decoderHintType, trans); + } else { + errCode = jvxErrorType.JVX_ERROR_INVALID_FORMAT; + } + break; + case jvxDataFormatEnum.JVX_DATAFORMAT_U16BIT_LE: + if (prop is AudYoFloPropertyMultiContentWeb) { + errCode = + AudYoFloPropertyContentFromJson.convertTextToField( + prop.fld, prop.fldSz, newValues, prop.decoderHintType, trans); + } else { + errCode = jvxErrorType.JVX_ERROR_INVALID_FORMAT; + } + break; + + // 8BIT FIELDS + case jvxDataFormatEnum.JVX_DATAFORMAT_8BIT: + if (prop is AudYoFloPropertyMultiContentWeb) { + errCode = + AudYoFloPropertyContentFromJson.convertTextToField( + prop.fld, prop.fldSz, newValues, prop.decoderHintType, trans); + } else { + errCode = jvxErrorType.JVX_ERROR_INVALID_FORMAT; + } + break; + case jvxDataFormatEnum.JVX_DATAFORMAT_U8BIT: + if (prop is AudYoFloPropertyMultiContentWeb) { + errCode = + AudYoFloPropertyContentFromJson.convertTextToField( + prop.fld, prop.fldSz, newValues, prop.decoderHintType, trans); + } else { + errCode = jvxErrorType.JVX_ERROR_INVALID_FORMAT; + } + break; + + // DOUBLE/FLOAT FIELDS + case jvxDataFormatEnum.JVX_DATAFORMAT_DATA: + if (prop is AudYoFloPropertyMultiContentWeb) { + errCode = + AudYoFloPropertyContentFromJson.convertTextToField( + prop.fld, prop.fldSz, newValues, prop.decoderHintType, trans); + } else if (prop is AudYoFloPropertyMultiContentWeb) { + errCode = + AudYoFloPropertyContentFromJson.convertTextToField( + prop.fld, prop.fldSz, newValues, prop.decoderHintType, trans); + } else { + errCode = jvxErrorType.JVX_ERROR_INVALID_FORMAT; + } + break; + default: + assert(false); + break; + } + return errCode; + } +} + +// This is a global function to drive different host types to different calls: web access here, but +// there is another call in the native code!! +int updatePropertyContentFromJsonMap_fields( + AudYoFloPropertyContentBackend prop, Map newValues, AudYoFloBackendTranslator trans) { + return AudYoFloPropertyContentFromJsonSimple.updatePropertyContentFromJsonMap( + prop, newValues, trans); +} diff --git a/flutter/ayf_pack/lib/route-widgets/ayf_route_connect.dart b/flutter/ayf_pack/lib/route-widgets/ayf_route_connect.dart new file mode 100644 index 00000000..dbbbcd2c --- /dev/null +++ b/flutter/ayf_pack/lib/route-widgets/ayf_route_connect.dart @@ -0,0 +1,385 @@ +import 'package:flutter/material.dart'; +import '../ayf_pack_local.dart'; +import 'package:provider/provider.dart'; +import 'dart:async'; + +class AudYoFloConnectWidget extends StatefulWidget { + const AudYoFloConnectWidget({super.key}); + + @override + _AudYoFloConnectWidgetState createState() => _AudYoFloConnectWidgetState(); +} + +class _AudYoFloConnectWidgetState extends State { + Timer? runningTimer; + + bool firstTimeConfigDone = false; + String ipAddrSpecified = "127.0.0.1"; + int ipPortSpecified = 8000; + String latestError = ""; + int timeOutMSecs = 500; + bool autoConnect = false; + AudYoFloUiModel? uiModel; + AudYoFloBackendCache? beCache; + AudYoFloDebugModel? dbgModel; + int tickStart = 0; + bool runLoopAutoConnect = false; + + @override + void initState() { + super.initState(); + runningTimer = + Timer.periodic(Duration(milliseconds: timeOutMSecs), (timer) { + if (beCache != null) { + if (runLoopAutoConnect) { + print('Granularity expired, ${timer.tick}'); + if (autoConnect) { + print('Triggering auto-connect!'); + tryStartConnect(); + } + } + //} + } + }); + } + + @override + void dispose() { + super.dispose(); + runningTimer!.cancel(); + } + + @override + Widget build(BuildContext context) { + beCache = Provider.of(context, listen: false); + dbgModel = Provider.of(context, listen: false); + uiModel = Provider.of(context, listen: false); + + // Initial read of arguments from command line + + var myAppBar = AudYoFloConnectAppBar( + context, + uiModel!.textApp, + uiModel!.imageNameApp, + uiModel!.imageWScaleApp, + uiModel!.imageOAScaleApp, + uiModel!.heightUpperNavBar, + beCache); + + if ((uiModel != null) && (!firstTimeConfigDone)) { + ipAddrSpecified = uiModel!.httpConnect.ipAddr; + ipPortSpecified = uiModel!.httpConnect.ipPort; + autoConnect = uiModel!.httpConnect.autoConnect; + + int cnt = 0; + + for (; cnt < uiModel!.platformSpec!.cmdArgs.length; cnt++) { + if (uiModel!.platformSpec!.cmdArgs[cnt] == "--httpAutoConnect") { + autoConnect = true; + continue; + } + if (uiModel!.platformSpec!.cmdArgs[cnt] == "--httpIpAddr") { + cnt++; + if (cnt < uiModel!.platformSpec!.cmdArgs.length) { + ipAddrSpecified = uiModel!.platformSpec!.cmdArgs[cnt]; + } + continue; + } + if (uiModel!.platformSpec!.cmdArgs[cnt] == "--httpIpPort") { + cnt++; + if (cnt < uiModel!.platformSpec!.cmdArgs.length) { + ipPortSpecified = int.parse(uiModel!.platformSpec!.cmdArgs[cnt]); + } + continue; + } + + firstTimeConfigDone = true; + } + } + + runLoopAutoConnect = autoConnect && !beCache!.httpConnect.isConnected; + + if (beCache!.backendAdapterIf != null) { + latestError = beCache!.backendAdapterIf!.lastError; + } + + if (!beCache!.httpConnect.isConnected) { + tickStart = runningTimer!.tick; + } + + String showText = 'Specify address and port of the backend'; + if (autoConnect) { + showText += ' [auto]'; + } else { + showText += ' [no-auto]'; + } + Widget? centeredTextFragment = null; + if (latestError.isEmpty) { + centeredTextFragment = Center( + child: Text(showText, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.white, + fontSize: 16))); + } else { + centeredTextFragment = Center( + child: Column( + children: [ + Text(showText, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.white, + fontSize: 16)), + Text(latestError, + style: TextStyle( + fontWeight: FontWeight.bold, + fontStyle: FontStyle.italic, + color: Colors.yellow, + fontSize: 12)), + ], + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + ), + ); + } + return LayoutBuilder( + builder: (context, constraints) => Scaffold( + appBar: myAppBar, + //automaticallyImplyLeading: false, + //title: Center(child: const Text('Connect to Html Backend')), + //), + body: Center( + child: ConstrainedBox( + constraints: constraints / 2, + child: DecoratedBox( + decoration: BoxDecoration( + color: Colors.blueGrey, + border: Border.all(), + borderRadius: BorderRadius.circular(5), + boxShadow: [ + BoxShadow( + color: Colors.white, + offset: Offset(-2, -2), + blurRadius: 3), + BoxShadow( + color: Colors.black, + offset: Offset(10, 10), + blurRadius: 8) + ]), + child: Container( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + flex: 1, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Center( + child: Container( + // color: Colors.blueGrey, + child: centeredTextFragment), + ), + )), + Expanded( + flex: 2, + child: SizedBox( + width: constraints.maxWidth / 2, + child: Container( + color: Colors.white, + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceEvenly, + children: [ + const Expanded( + flex: 1, + child: Center( + child: Text('Address', + style: TextStyle( + color: Colors.black, + fontSize: 16)))), + Expanded( + flex: 2, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Focus( + child: AudYoFloActiveTextField( + showTextOnBuild: ipAddrSpecified, + onEditingComplete: (String text) { + ipAddrSpecified = text; + }, + reportOnFocusLost: true, + haveCommandHistory: false), + ), + ), + ), + const Expanded( + flex: 1, + child: Center( + child: Text('Port', + style: TextStyle( + color: Colors.black, + fontSize: 16)))), + Expanded( + flex: 1, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Focus( + child: AudYoFloActiveTextField( + showTextOnBuild: + ipPortSpecified + .toString(), + onEditingComplete: + (String text) { + ipPortSpecified = + int.parse(text); + setState(() {}); + }, + reportOnFocusLost: true, + haveCommandHistory: false), + ))), + ], + ), + ), + ), + ), + Expanded( + flex: 1, + child: Container( + color: Colors.white, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blueGrey, + padding: + const EdgeInsets.symmetric( + horizontal: 8, + vertical: 8), + textStyle: const TextStyle( + fontSize: 16, + fontWeight: + FontWeight.normal)), + + // Within the SecondScreen widget + onPressed: () async { + await tryStartConnect(); + }, + child: Text('Connect')), + ]), + )), + ]), + ), + )) + // const Text('Go back!'), + + ), + ), + ); + } + + Future tryStartConnect() async { + if ((uiModel != null) && (uiModel!.platformSpec != null)) { + runLoopAutoConnect = false; + // Attach the connect parameters to the command line + var cmdArgsLoc = uiModel!.platformSpec!.cmdArgs; + + // Make fixed length list to growable list + cmdArgsLoc = cmdArgsLoc.toList(); + cmdArgsLoc.add('--connect-addr'); + cmdArgsLoc.add(ipAddrSpecified); + cmdArgsLoc.add('--connect-port'); + cmdArgsLoc.add(ipPortSpecified.toString()); + uiModel!.platformSpec!.cmdArgs = cmdArgsLoc; + + // Run the platform specific initialization + beCache!.httpConnect.isConnected = await uiModel!.platformSpec! + .initializeSubSystem(beCache, uiModel, dbgModel); + } else { + print('Error: platform specific references not available!'); + assert(false); + } + // Navigate back to the first screen by popping the current route + // off the stack. + setState(() { + latestError = uiModel!.platformSpec!.lastError; + runLoopAutoConnect = false; + if (beCache!.httpConnect.isConnected) { + Navigator.pop(context); + } else { + runLoopAutoConnect = autoConnect; + } + }); + } +} + +class AudYoFloConnectAppBar extends StatefulWidget with PreferredSizeWidget { + final String titletxt; + final String logoPath; + final double widthScale; + final double height; + final double overallScale; + final String routeNavigatorSettingsTag; + + @override + final Size preferredSize; + + const AudYoFloConnectAppBar( + BuildContext context, + this.titletxt, + this.logoPath, + this.widthScale, + this.overallScale, + this.height, + theBeCache, + {Key? key, + this.routeNavigatorSettingsTag = ''}) + : preferredSize = const Size.fromHeight(50.0), + super(key: key); + + @override + _MyConnectAppBarState createState() => _MyConnectAppBarState(); +} + +class _MyConnectAppBarState extends State { + Color col = Colors.black; + @override + Widget build(BuildContext context) { + return AppBar( + automaticallyImplyLeading: false, + backgroundColor: Colors.white, + title: Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ + Flexible( + flex: 1, + child: + Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ + GestureDetector( + onTap: () { + debugPrint('Help triggered'); + }, + child: const Padding( + padding: EdgeInsets.all(8.0), + child: Icon(Icons.help, color: Colors.black))) + ]), + ), + Flexible( + flex: 4, + child: + Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ + Text( + widget.titletxt, + style: const TextStyle(color: Colors.black), + ), + Image.asset( + widget.logoPath, + filterQuality: FilterQuality.medium, + fit: BoxFit.fill, + width: widget.widthScale * widget.height * widget.overallScale, + height: widget.height * widget.overallScale, + ), + ]), + ) + ]), + toolbarHeight: widget.height, + ); + } +} diff --git a/flutter/ayf_pack/lib/routes/ayf_routes.dart b/flutter/ayf_pack/lib/routes/ayf_routes.dart new file mode 100644 index 00000000..7ba6a1e4 --- /dev/null +++ b/flutter/ayf_pack/lib/routes/ayf_routes.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; +import '../ayf_pack_local.dart'; + +class AudYoFloRouteGenerator { + static const String hostPage = '/'; + static const String settingsPage = '/settings'; + static const String otherPage = '/other'; + static const String connectPage = '/connect'; + + AudYoFloRouteGenerator._() {} + static Route generateRoute(RouteSettings settings) { + switch (settings.name) { + case hostPage: + return MaterialPageRoute( + builder: (_) => AudYoFloMainAppWidget(), + ); + case settingsPage: + return MaterialPageRoute( + builder: (_) => AudYoFloMainAppWidget(), + ); + case connectPage: + return MaterialPageRoute( + builder: (_) => const AudYoFloConnectWidget(), + ); + default: + throw FormatException("Route not found"); + } + } +} diff --git a/flutter/ayf_pack/lib/sequencer/ayf_sequencer_status_from_json.dart b/flutter/ayf_pack/lib/sequencer/ayf_sequencer_status_from_json.dart new file mode 100644 index 00000000..e65ea388 --- /dev/null +++ b/flutter/ayf_pack/lib/sequencer/ayf_sequencer_status_from_json.dart @@ -0,0 +1,29 @@ +import 'package:fixnum/fixnum.dart' as fn; +import 'package:ayf_pack/ayf_pack.dart'; + +class AudYoFloSequencerStatusFromJson { + static int updateSequencerStatus( + Map jsonMap, AudYoFloBackendCacheBectrlIf theBeCache) { + int retVal = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + String? errCodeExprPtr = getStringEntryValueMap(jsonMap, 'return_code'); + if (errCodeExprPtr != null) { + // Convert error code + String errCodeExpr = errCodeExprPtr; + retVal = jvxErrorTypeEInt.fromStringSingle(errCodeExpr); + } + + if (retVal == jvxErrorType.JVX_NO_ERROR) { + var readyForStartPtr = extractStringFromJson(jsonMap, "ready_for_start"); + var reasonIfNotStr = extractStringFromJson(jsonMap, "reason_if_not"); + if ((readyForStartPtr != null) && (reasonIfNotStr != null)) { + String readyForStartExpr = readyForStartPtr; + int readyForStart = + jvxErrorTypeEInt.fromStringSingle(readyForStartExpr); + theBeCache.updateProcessStatusCompleteNotify( + (readyForStart == jvxErrorType.JVX_NO_ERROR), reasonIfNotStr); + } + } + + return retVal; + } +} diff --git a/flutter/ayf_pack/lib/settings/ayf_settings_property_list.dart b/flutter/ayf_pack/lib/settings/ayf_settings_property_list.dart new file mode 100644 index 00000000..c3ec4623 --- /dev/null +++ b/flutter/ayf_pack/lib/settings/ayf_settings_property_list.dart @@ -0,0 +1,65 @@ +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; +import '../ayf_pack_local.dart'; +import './ayf_settings_property_list_content.dart'; + +// This class defines the tab widget in the settings dialog. It uses the +// AudYoFloMultiComponentsPropertyOnChange base class to obtain the +// property references in the debug cache. It is configured in the derived class to +// combine multiple components and properties in one list. This list is then +// exposed in a sliver list. +// Note that widget as well as the state class are always re-allocated when the +// window is re-opended - since we have a window on top of the UI +class AudYoFloSettingsPropertyList extends StatefulWidget { + final AudYoFloSettingsConfigurationSection secProps; + bool dbgMessages; + AudYoFloSettingsPropertyList(this.secProps, {this.dbgMessages = false}) {} + @override + State createState() { + return _AudYoFloSettingsPropertyListState(); + } +} + +// State part of the widget. The list of components and properties is typically +// set before first calling the "localBuild" function. +class _AudYoFloSettingsPropertyListState + extends State + with AudYoFloMultiComponentsPropertyOnChange { + _AudYoFloSettingsPropertyListState(); + + @override + Widget build(BuildContext context) { + // If the states are newly allocated the configuration must be done again + if (configured == false) { + resetMultiPropertySettings(); + for (var elm in widget.secProps.theSettings.entries) { + // Any call to setComponentConsider will set the base class to state + // configured = true + setComponentConsider(elm.key); + } + } + + // Call the selector & future build process in the parent class + return localBuild(context); + } + + @override + Widget innerWidget(BuildContext context) { + // Here we end up in the core build function + return AudYoFloSettingsPropertyListContent( + latestResult, widget.dbgMessages); + } + + // Callback to provide the property list for each component + @override + AudYoFloOneComponentSettings? requestedPropertiesList( + JvxComponentIdentification cpId) { + List retLst = []; + var elm = widget.secProps.theSettings.entries + .firstWhereOrNull((element) => element.key == cpId); + if (elm != null) { + return elm.value; + } + return null; + } +} diff --git a/flutter/ayf_pack/lib/settings/ayf_settings_property_list_content.dart b/flutter/ayf_pack/lib/settings/ayf_settings_property_list_content.dart new file mode 100644 index 00000000..98cee0b6 --- /dev/null +++ b/flutter/ayf_pack/lib/settings/ayf_settings_property_list_content.dart @@ -0,0 +1,309 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import '../ayf_pack_local.dart'; + +// Class to finally receive the properties to be show in this +// property-edit section +class AudYoFloSettingsPropertyListContent extends StatefulWidget { + final List props; + final bool dbgMessages; + const AudYoFloSettingsPropertyListContent(this.props, this.dbgMessages); + + @override + State createState() { + return _AudYoFloSettingsPropertyListContentState(); + } +} + +class AudYoFloPropertyContentSettingsEntry { + String tag = ''; + String ttip = ''; +} + +class AudYoFloPropertyContentSettingsEntryBackend + extends AudYoFloPropertyContentSettingsEntry { + AudYoFloPropertyContainer? bprop; +} + +class AudYoFloPropertyContentSettingsEntryLocal + extends AudYoFloPropertyContentSettingsEntry { + AudYoFloPropertyContentLocal? lprop; +} + +// ============================================================================ + +class _AudYoFloSettingsPropertyListContentState + extends State { + final ScrollController _controllerL = ScrollController(); + List props = []; + + @override + Widget build(BuildContext context) { + props = widget.props; + + AudYoFloUiModel theUiModel = + Provider.of(context, listen: false); + // From a "multi-component" lst we transform into a linear + // property list + List lstProps = []; + + int cnt = 0; + + for (AudYoFloMultiComponentsPropertyOnChangeEntry elm in props) { + theUiModel.filterListProperties('Settings', elm); + + if (elm is AudYoFloMultiComponentsPropertyOnChangeEntryBackend) { + if (elm.bprops != null) { + AudYoFloPropertyContentSettingsEntry newElm = + AudYoFloPropertyContentSettingsEntry(); + if (elm.settings.compTag.isNotEmpty) { + newElm.tag = elm.settings.compTag; + } else { + newElm.tag = elm.cpId.toString(); + } + lstProps.add(newElm); + + List propL = elm.bprops!; + + // Should be of this tye due to previous functions + AudYoFloOneComponentSettings beProps = elm.settings; + List tagl = beProps.propTags; + List tooltipl = beProps.propTooltips; + cnt = 0; + for (var elmp in propL) { + AudYoFloPropertyContentSettingsEntryBackend newElmBe = + AudYoFloPropertyContentSettingsEntryBackend(); + newElmBe.bprop = elmp; + if (cnt < tagl.length) { + newElmBe.tag = tagl[cnt]; + } else { + newElmBe.tag = elmp.description; + } + + if (cnt < tooltipl.length) { + newElmBe.ttip = tooltipl[cnt]; + } else { + newElmBe.ttip = elmp.description; + } + // newElmBe.propertyInBackend = elm.ext.propertiesFromBackend; + lstProps.add(newElmBe); + cnt++; + } + } + } else { + if (elm is AudYoFloMultiComponentsPropertyOnChangeEntryLocal) { + if (elm.lprops != null) { + AudYoFloPropertyContentSettingsEntry newElm = + AudYoFloPropertyContentSettingsEntry(); + newElm.tag = elm.settings.compTag; + lstProps.add(newElm); + + AudYoFloOneComponentSettings loProps = elm.settings; + List tagl = loProps.propTags; + List tooltipl = loProps.propTooltips; + List propL = elm.lprops!; + + cnt = 0; + for (var elmp in propL) { + AudYoFloPropertyContentSettingsEntryLocal newElmLo = + AudYoFloPropertyContentSettingsEntryLocal(); + newElmLo.lprop = elmp; + if (cnt < tagl.length) { + newElmLo.tag = tagl[cnt]; + } else { + newElmLo.tag = elmp.description; + } + + if (cnt < tooltipl.length) { + newElmLo.ttip = tooltipl[cnt]; + } else { + newElmLo.ttip = elmp.description; + } + // newElmBe.propertyInBackend = elm.ext.propertiesFromBackend; + lstProps.add(newElmLo); + cnt++; + } + } + } + } + } + + //return Container(color: Colors.blue); + return CustomScrollView(controller: _controllerL, slivers: [ + /*AudYoFloDeviceSelectionSliverAppBar( + 'Inactive Audio Devices'), + */ + SliverList( + delegate: SliverChildBuilderDelegate((BuildContext context, int index) { + // One element for each of the entries + return AudYoFloOnePropertySettingSliver(lstProps[index]); + }, childCount: lstProps.length), + ) + ]); + } +} + +class AudYoFloOnePropertySettingSliver extends StatelessWidget { + final AudYoFloPropertyContentSettingsEntry propEntry; + AudYoFloOnePropertySettingSliver(this.propEntry); + + @override + Widget build(BuildContext context) { + // Produce Device Name + + String tag = propEntry.tag; + String toolTip = propEntry.ttip; + Widget? mainWidget; + if (propEntry is AudYoFloPropertyContentSettingsEntryBackend) { + AudYoFloPropertyContentSettingsEntryBackend propEntryBe = + propEntry as AudYoFloPropertyContentSettingsEntryBackend; + + mainWidget = AudYoFloOnePropertySettingSliverCore(propBe: propEntryBe); + } else { + if (propEntry is AudYoFloPropertyContentSettingsEntryLocal) { + AudYoFloPropertyContentSettingsEntryLocal propEntryLo = + propEntry as AudYoFloPropertyContentSettingsEntryLocal; + mainWidget = AudYoFloOnePropertySettingSliverCore(propLo: propEntryLo); + } else { + mainWidget = Card( + child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + //SizedBox(height: 30, width: 60, child: Icon(Icons.settings)), + Expanded( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Center( + child: Container( + color: Colors.lightBlueAccent[50], + child: Text( + tag, + style: TextStyle(fontStyle: FontStyle.italic), + ))))) + ])); + + //Text('==============================='); + } + } + + return mainWidget; + } +} + +class AudYoFloOnePropertySettingSliverCore extends StatelessWidget { + final AudYoFloPropertyContentSettingsEntryBackend? propBe; + final AudYoFloPropertyContentSettingsEntryLocal? propLo; + + AudYoFloOnePropertySettingSliverCore({this.propBe, this.propLo}); + + @override + Widget build(BuildContext context) { + AudYoFloBackendCache theBeCache = + Provider.of(context, listen: false); + + String text = 'not-set'; + String ttip = 'not-set'; + String tag = 'not-set'; + Widget? mainSubWidget; + + if (propBe != null) { + var propCore = propBe!.bprop; + ttip = propBe!.ttip; + tag = propBe!.tag; + if (propCore != null) { + if (propCore is AudYoFloPropertyContentBackend) { + text = propCore.descriptor; + + if (propCore.isValid) { + if (propCore is AudYoFloPropertySelectionListBackend) { + mainSubWidget = AudYoFloPropListComboBoxWidget( + selLstBe: propCore as AudYoFloPropertySelectionListBackend); + } else { + mainSubWidget = AudYoFloSimpleTextField(propBe: propCore); + } + } else { + mainSubWidget = Text('-- Not available -- ', + style: TextStyle(fontStyle: FontStyle.italic)); + } + } + } + } else if (propLo != null) { + var propCore = propLo!.lprop; + ttip = propLo!.ttip; + tag = propLo!.tag; + if (propCore != null) { + text = propCore.descriptor; + + if (propCore is AudYoFloPropertySelectionListLocal) { + mainSubWidget = AudYoFloPropListComboBoxWidget(selLstLo: propCore); + } else { + mainSubWidget = AudYoFloSimpleTextField(propLo: propCore); + } + } + } + if (mainSubWidget == null) { + mainSubWidget = Text(text); + } + + return Tooltip( + message: ttip, + child: Card( + child: LayoutBuilder( + builder: (context, constraints) { + return SizedBox( + width: constraints.maxWidth, + height: 40, + child: Row( + //mainAxisAlignment: MainAxisAlignment.start, children: [ + children: [ + SizedBox( + height: 30, width: 60, child: Icon(Icons.settings)), + Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + flex: 1, + child: Text(tag), + ), + Flexible(flex: 1, child: mainSubWidget!), + ]), + ) + ]), + ); + }, + ), + )); + } +} + +/* +class AudYoFloOneLocalPropertySettingSliver extends StatelessWidget { + final AudYoFloPropertyContentSettingsEntryLocal propLo; + + AudYoFloOneLocalPropertySettingSliver(this.propLo); + + @override + Widget build(BuildContext context) { + AudYoFloBackendCache theBeCache = + Provider.of(context, listen: false); + + return Tooltip( + message: propLo.ttip, + child: Card( + child: Row( + //mainAxisAlignment: MainAxisAlignment.start, children: [ + children: [ + SizedBox(height: 30, width: 60, child: Icon(Icons.settings)), + Expanded( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text(propLo.tag))), + Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + color: Colors.red, + child: Text(propLo.lprop!.descriptor))), + ]), + )); + } +} +*/ \ No newline at end of file diff --git a/flutter/ayf_pack/lib/simple-widgets/ayf_progress_property.dart b/flutter/ayf_pack/lib/simple-widgets/ayf_progress_property.dart new file mode 100644 index 00000000..676128b5 --- /dev/null +++ b/flutter/ayf_pack/lib/simple-widgets/ayf_progress_property.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; +import '../../ayf_pack_local.dart'; +import 'package:provider/provider.dart'; + +class AudYoFloProgressIndicator extends StatefulWidget { + final bool updateRealtime; + final JvxComponentIdentification cpId; + final double progressValue; + final String propNameProgress; + + const AudYoFloProgressIndicator(this.cpId, this.propNameProgress, + this.updateRealtime, this.progressValue); + @override + _AudYoFloProgressIndicatorState createState() => + _AudYoFloProgressIndicatorState(); +} + +class _AudYoFloProgressIndicatorState extends State { + double progressValue = 0; + int oldHashCode = -1; + @override + Widget build(BuildContext context) { + return Selector( + selector: (context, perNotify) => !perNotify.fast, + shouldRebuild: (previous, next) { + if (widget.updateRealtime) { + return next ? true : false; + } + return false; + }, + builder: (context, notifier, child) { + // Check if we really have allocated a new widget + if (widget.hashCode != oldHashCode) { + progressValue = widget.progressValue; + oldHashCode = widget.hashCode; + } + + // double progressValueLocal = widget.progressValue; + var theBeCache = + Provider.of(context, listen: false); + List lst = [widget.propNameProgress]; + + List? props = + theBeCache.referenceValidPropertiesComponents(widget.cpId, lst); + if (props == null) { + // No need to invoke a listener - we wait for the operation to complete + theBeCache.triggerUpdatePropertiesComponent(widget.cpId, lst, + report: AyfPropertyReportLevel.AYF_FRONTEND_REPORT_NO_REPORT); + } else { + // Update progressValue + if (props.isNotEmpty) { + AudYoFloPropertyContainer prop = props.first; + if (prop is AudYoFloPropertyMultiContentBackend) { + AudYoFloPropertyMultiContentBackend propN = + prop as AudYoFloPropertyMultiContentBackend; + progressValue = propN.fld[0]; + theBeCache.invalidatePropertiesComponent( + widget.cpId, lst, true); + } + } + } + + // Convert percent to value between 0 and 1 + double progressValueShow = progressValue / 100; + return LinearProgressIndicator(value: progressValueShow); + }); + } +} diff --git a/flutter/ayf_pack/lib/sub-widgets/audio-devices/ayf_audio_device_active_inactive_sliver.dart b/flutter/ayf_pack/lib/sub-widgets/audio-devices/ayf_audio_device_active_inactive_sliver.dart new file mode 100644 index 00000000..ecf5ea62 --- /dev/null +++ b/flutter/ayf_pack/lib/sub-widgets/audio-devices/ayf_audio_device_active_inactive_sliver.dart @@ -0,0 +1,173 @@ +import 'package:provider/provider.dart'; +import 'package:flutter/material.dart'; +import '../../ayf_pack_local.dart'; + +import 'package:collection/collection.dart'; + +class AudYoFloAudioDeviceRenderSection { + String imageNameSoundIo = ''; + String imageNameSoundI = ''; + String imageNameSoundO = ''; + + AudYoFloAudioDeviceRenderSection( + {required this.imageNameSoundIo, + required this.imageNameSoundI, + required this.imageNameSoundO}); +} + +class AudYoFloOneDeviceSliver extends StatelessWidget { + final AudYoFloOneSelectionOptionWithId option; + final bool fromActiveList; + final AudYoFloSingleSelectionComponent? reportTarget; + + AudYoFloOneDeviceSliver(this.option, this.fromActiveList, this.reportTarget); + + Widget iconForDeviceOption( + AudYoFloOneDeviceSelectionOption dev, AudYoFloUiModel uiModel) { + String imageNameSoundIo = ''; + String imageNameSoundI = ''; + String imageNameSoundO = ''; + + MapEntry? elm = uiModel + .widgetSpecificConfigurations.entries + .firstWhereOrNull((element) => element.key == 'soundIo'); + if (elm != null) { + if (elm.value is AudYoFloAudioDeviceRenderSection) { + AudYoFloAudioDeviceRenderSection elmSoundIo = + elm.value as AudYoFloAudioDeviceRenderSection; + imageNameSoundIo = elmSoundIo.imageNameSoundIo; + imageNameSoundI = elmSoundIo.imageNameSoundI; + imageNameSoundO = elmSoundIo.imageNameSoundO; + } + } + + if ((dev.caps.bitTest(jvxDeviceCapabilityTypeShift + .JVX_DEVICE_CAPABILITY_INPUT_SHIFT.index) && + dev.caps.bitTest(jvxDeviceCapabilityTypeShift + .JVX_DEVICE_CAPABILITY_OUTPUT_SHIFT.index)) || + dev.caps.bitTest(jvxDeviceCapabilityTypeShift + .JVX_DEVICE_CAPABILITY_DUPLEX_SHIFT.index)) { + if (imageNameSoundIo.isEmpty) { + return const Icon( + Icons.report_problem, + color: Colors.green, + size: 30.0, + ); + } + return Image.asset(imageNameSoundIo); + } else { + if (dev.caps.bitTest(jvxDeviceCapabilityTypeShift + .JVX_DEVICE_CAPABILITY_INPUT_SHIFT.index)) { + if (imageNameSoundI.isEmpty) { + return const Icon( + Icons.report_problem, + color: Colors.green, + size: 30.0, + ); + } + return Image.asset(imageNameSoundI); + } + } + if (imageNameSoundO.isEmpty) { + return const Icon( + Icons.report_problem, + color: Colors.green, + size: 30.0, + ); + } + return Image.asset(imageNameSoundO); + } + + @override + Widget build(BuildContext context) { + // Produce Device Name + + AudYoFloUiModel theUiCache = + Provider.of(context, listen: false); + + String showDeviceName = option.option.description; + int idx = showDeviceName.indexOf(RegExp(r'<*>')); + if (idx != -1) { + showDeviceName = showDeviceName.substring(1, showDeviceName.length - 1); + } + deviceSelectionDragData dData = + deviceSelectionDragData(fromActiveList, option.idx); + + // Show device capabailities + Widget icon = iconForDeviceOption(option.option, theUiCache); + Widget? statusWidget; + + // Highlight the master device + String masterText = ''; + if ((option.option.devIdent.slotsubid == 0) && + (option.option.devIdent.slotid == 0)) { + masterText = "M"; + } + + String selectable = ''; + if (!option.option.selectable) { + selectable = 'x'; + } + + if (option.option.state + .bitTest(jvxStateShiftEnum.JVX_STATE_SELECTED.index)) { + statusWidget = Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + color: Colors.red, child: Icon(Icons.warning, size: 20))); + } else if (option.option.state + .bitTest(jvxStateShiftEnum.JVX_STATE_ACTIVE.index)) { + statusWidget = Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + color: Colors.green, child: Icon(Icons.stop, size: 20))); + } else if (option.option.state + .bitTest(jvxStateShiftEnum.JVX_STATE_PREPARED.index)) { + statusWidget = Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + color: Colors.yellow, child: Icon(Icons.pause, size: 20))); + } else if (option.option.state + .bitTest(jvxStateShiftEnum.JVX_STATE_PROCESSING.index)) { + statusWidget = Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + color: Colors.blue, child: Icon(Icons.play_arrow, size: 20))); + } else { + statusWidget = Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + color: Colors.grey, + child: Icon(Icons.check_box_outline_blank, size: 20))); + } + + return GestureDetector( + onTap: () { + // here, we run a callback to the next higher layer in the widget hierarchy to + // update the widget which is in parallel to the device selection construct + + if (reportTarget != null) { + reportTarget!.reportSelectionComponent(option.option.devIdent); + } + }, + child: Card( + child: Row( + //mainAxisAlignment: MainAxisAlignment.start, children: [ + children: [ + Draggable( + feedback: SizedBox(height: 30, width: 60, child: icon), + data: dData, + child: SizedBox(height: 30, width: 60, child: icon)), + Expanded( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text(showDeviceName))), + Padding( + padding: const EdgeInsets.all(8.0), + child: Container(color: Colors.red, child: Text(masterText))), + statusWidget, + Padding(padding: const EdgeInsets.all(8.0), child: Text(selectable)) + ])), + ); + } +} diff --git a/flutter/ayf_pack/lib/sub-widgets/audio-devices/ayf_audio_device_channel_selection_widget.dart b/flutter/ayf_pack/lib/sub-widgets/audio-devices/ayf_audio_device_channel_selection_widget.dart new file mode 100644 index 00000000..37b1af30 --- /dev/null +++ b/flutter/ayf_pack/lib/sub-widgets/audio-devices/ayf_audio_device_channel_selection_widget.dart @@ -0,0 +1,300 @@ +import 'package:provider/provider.dart'; +import 'package:flutter/material.dart'; +import 'package:collection/collection.dart'; +import '../../ayf_pack_local.dart'; + +class AudYoFloChannelSelectionSliverAppBar extends StatelessWidget { + final String text; + final AudYoFloPropertySelectionListBackend? props; + AudYoFloChannelSelectionSliverAppBar(this.text, this.props); + + @override + Widget build(BuildContext context) { + AudYoFloBackendCache theBeCache = + Provider.of(context, listen: false); + return SliverAppBar( + title: Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ + Flexible( + child: Center( + child: Text( + text, + style: TextStyle(color: Colors.white, fontSize: 16), + )), + flex: 1, + ), + Flexible( + child: ElevatedButton( + child: Padding( + padding: const EdgeInsets.all(8.0), child: Icon(Icons.add)), + onPressed: () { + if (props != null) { + List lst = []; + for (int posi = 0; + posi < props!.parpropms.entries.length; + posi++) { + props!.selection.bitSet(posi); + } + lst.add(props!.parpropms.descriptor); + theBeCache.triggerSetProperties( + props!.parpropms.assCpIdent, lst); + } + }, + ), + flex: 1), + Flexible( + child: ElevatedButton( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Icon(Icons.remove)), + onPressed: () { + if (props != null) { + List lst = []; + props!.selection.bitFClear(); + lst.add(props!.parpropms.descriptor); + theBeCache.triggerSetProperties( + props!.parpropms.assCpIdent, lst); + } + }, + ), + flex: 1), + ]), + backgroundColor: Colors.blueGrey, + pinned: false, + stretch: false, + onStretchTrigger: () async { + print('Hello'); + }, + floating: true, + snap: true, + //expandedHeight: 200 + ); + } +} + +abstract class AudYoFloOneChannelSliverReport { + void reportTapChannel(int idChannel, bool isInput); +} + +class AudYoFloOneChannelSliver extends StatelessWidget { + final String channelName; + final idChannel; + final bool isSelected; + final bool isInput; + final AudYoFloOneChannelSliverReport report; + + AudYoFloOneChannelSliver(this.channelName, this.idChannel, this.isSelected, + this.isInput, this.report); + + @override + Widget build(BuildContext context) { + Color col = Colors.grey; + + AudYoFloChannelTypeClass clsOnReturn = AudYoFloChannelTypeClass(); + if (isSelected) col = Colors.yellow; + + decodeInputOutputChannel(channelName, clsOnReturn, idChannel); + String showChannelName = clsOnReturn.name; + return GestureDetector( + onTap: () { + report.reportTapChannel(idChannel, isInput); + }, + child: Card( + color: col, + child: Row( + //mainAxisAlignment: MainAxisAlignment.start, children: [ + children: [ + Flexible( + flex: 10, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + color: col, child: Text(showChannelName)))), + ])), + ); + } +} + +class AudYoFloAudioDeviceInputOutputChannelWidget extends StatefulWidget { + AudYoFloAudioDeviceInputOutputChannelWidget(this.cpId); + final JvxComponentIdentification cpId; + @override + State createState() { + return _AudYoFloAudioDeviceInputOutputChannelWidgetStates(); + } +} + +class _AudYoFloAudioDeviceInputOutputChannelWidgetStates + extends State + with AudYoFloOneChannelSliverReport { + ScrollController _controllerL = ScrollController(); + ScrollController _controllerR = ScrollController(); + AudYoFloPropertySelectionListBackend? inChannels; + AudYoFloPropertySelectionListBackend? outChannels; + late AudYoFloBackendCache theBeCache; + JvxComponentIdentification cpId = JvxComponentIdentification(); + + void reportTapChannel(int idChannel, bool isInput) { + List lst = []; + if (isInput) { + if (inChannels != null) { + if (inChannels!.selection.bitTest(idChannel)) { + inChannels!.selection.bitClear(idChannel); + } else { + inChannels!.selection.bitSet(idChannel); + } + lst.add(inChannels!.parpropms.descriptor); + theBeCache.triggerSetProperties(outChannels!.parpropms.assCpIdent, lst); + } + } else { + if (outChannels != null) { + if (outChannels!.selection.bitTest(idChannel)) { + outChannels!.selection.bitClear(idChannel); + } else { + outChannels!.selection.bitSet(idChannel); + } + lst.add(outChannels!.parpropms.descriptor); + theBeCache.triggerSetProperties(outChannels!.parpropms.assCpIdent, lst); + } + } + } + + // =============================================================== + @override + Widget build(BuildContext context) { + theBeCache = Provider.of(context, listen: false); + + // Take cpId from + if (widget.cpId.cpTp != JvxComponentTypeEnum.JVX_COMPONENT_UNKNOWN) { + cpId = widget.cpId; + } + return Selector( + selector: (context, beCache) { + // if property is not there, return a -1 + int retVal = -1; + + // We are waiting for an update of the properties + retVal = beCache.updateIdComponentInCache(cpId); + + // Return the current id + return retVal; + }, + + // Return true if the id is a different one than it was before + shouldRebuild: (previous, next) => (next != previous) ? true : false, + + // The builder + builder: (context, notifier, child) { + Future triggerChannelLists( + JvxComponentIdentification cpTp) async { + int errCode = jvxErrorType.JVX_NO_ERROR; + List? props; + List lst = [ + '/system/sel_input_channels', + '/system/sel_output_channels', + ]; + props = theBeCache.referenceValidPropertiesComponents(cpTp, lst); + if (props == null) { + errCode = + await theBeCache.triggerUpdatePropertiesComponent(cpTp, lst); + if (errCode == jvxErrorType.JVX_NO_ERROR) { + props = + theBeCache.referenceValidPropertiesComponents(cpTp, lst); + } else { + cpId.reset(); + } + } + + inChannels = null; + outChannels = null; + if (props != null) { + AudYoFloPropertyContainer? iP = props.firstWhereOrNull( + (element) => + element.descriptor == '/system/sel_input_channels'); + if (iP != null) { + if (iP is AudYoFloPropertySelectionListBackend) { + inChannels = iP as AudYoFloPropertySelectionListBackend; + } + } + + AudYoFloPropertyContainer? oP = props.firstWhereOrNull( + (element) => + element.descriptor == '/system/sel_output_channels'); + if (oP != null) { + if (oP is AudYoFloPropertySelectionListBackend) { + outChannels = oP as AudYoFloPropertySelectionListBackend; + } + } + } + + return errCode; + } + + return FutureBuilder( + future: triggerChannelLists(cpId), + builder: (BuildContext context, AsyncSnapshot snapshot) { + //if (snapshot.connectionState == ConnectionState.done) { + // The future has been completed. Check the errorcode + int errCode = jvxErrorType.JVX_ERROR_INTERNAL; + int? errCodePtr = snapshot.data; + if (errCodePtr != null) { + errCode = errCodePtr; + } + + //if (errCode == jvxErrorType.JVX_NO_ERROR) { + // Looks good, future operated as desired + return Row(children: [ + Flexible( + flex: 1, + child: CustomScrollView( + controller: _controllerL, + slivers: [ + AudYoFloChannelSelectionSliverAppBar( + 'Input Channels', inChannels), + SliverList( + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + return AudYoFloOneChannelSliver( + inChannels!.parpropms.entries[index], + index, + inChannels!.selection.bitTest(index), + true, + this); + }, + childCount: (inChannels != null) + ? inChannels!.parpropms.entries.length + : 0), + ) + ], + ), + ), + Flexible( + flex: 1, + child: CustomScrollView( + controller: _controllerR, + slivers: [ + AudYoFloChannelSelectionSliverAppBar( + 'Output Channels', outChannels), + SliverList( + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + // Prepare the drag data + + return AudYoFloOneChannelSliver( + outChannels!.parpropms.entries[index], + index, + outChannels!.selection.bitTest(index), + false, + this); + }, + childCount: (outChannels != null) + ? outChannels!.parpropms.entries.length + : 0), + ) + ], + ), + ), + ]); + }); + }); + } +} diff --git a/flutter/ayf_pack/lib/sub-widgets/ayf_active_textfield.dart b/flutter/ayf_pack/lib/sub-widgets/ayf_active_textfield.dart new file mode 100644 index 00000000..7946d9f2 --- /dev/null +++ b/flutter/ayf_pack/lib/sub-widgets/ayf_active_textfield.dart @@ -0,0 +1,127 @@ +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +typedef AudYoFloActiveTextFieldOnCompleteCallback = void Function(String txt); + +class AudYoFloActiveTextField extends StatefulWidget { + final String showTextOnBuild; + final AudYoFloActiveTextFieldOnCompleteCallback? onEditingComplete; + final bool reportOnFocusLost; + final bool haveCommandHistory; + AudYoFloActiveTextField( + {this.onEditingComplete, + this.showTextOnBuild = "", + this.reportOnFocusLost = false, + this.haveCommandHistory = true}); + @override + State createState() { + return _AudYoFloActiveTextFieldStates(); + } +} + +class _AudYoFloActiveTextFieldStates extends State { + String showTextOnBuild = ''; + final TextEditingController _controllerIn = TextEditingController(); + List previousCommands = []; + int hist_cnt = -1; + int widgetHash = -1; + + @override + Widget build(BuildContext context) { + // Check if a new widget was in use. In this case, we need to reset the text field + // We need to set the value, not just the text! + // https://stackoverflow.com/questions/51127241/how-do-you-change-the-value-inside-of-a-textfield-flutter + + if (widgetHash != widget.hashCode) { + showTextOnBuild = widget.showTextOnBuild; + widgetHash = widget.hashCode; + } + + _controllerIn.value = TextEditingValue( + text: showTextOnBuild, + selection: TextSelection.fromPosition( + TextPosition(offset: showTextOnBuild.length), + )); + + return Focus( + child: TextFormField( + controller: _controllerIn, + // focusNode: myNode, + keyboardType: TextInputType.text, + maxLines: 1, + onEditingComplete: () { + String text = _controllerIn.text; + _controllerIn.clear(); + if (widget.haveCommandHistory) { + String? elm = + previousCommands.firstWhereOrNull((element) => element == text); + if (elm == null) { + previousCommands.add(text); + } else { + previousCommands.remove(text); + previousCommands.add(text); + } + hist_cnt = -1; + } + //myNode.unfocus(); + widget.onEditingComplete?.call(text); + }, + onTap: () { + //myNode.requestFocus(); + }, + decoration: const InputDecoration(border: OutlineInputBorder()), + ), + onFocusChange: (value) { + if (!value) { + if (widget.reportOnFocusLost) { + String text = _controllerIn.text; + widget.onEditingComplete?.call(text); + } + } + }, + onKeyEvent: (node, ev) { + if (ev is KeyUpEvent) { + if (ev.logicalKey == LogicalKeyboardKey.arrowUp) { + setState(() { + if (hist_cnt == -1) { + if (previousCommands.isNotEmpty) { + hist_cnt = previousCommands.length - 1; + } + } + + if (hist_cnt >= 0) { + showTextOnBuild = previousCommands.elementAt(hist_cnt); + } + if (hist_cnt > 0) { + hist_cnt--; + } + print('Arrow Up: $hist_cnt'); + }); + } + if (ev.logicalKey == LogicalKeyboardKey.arrowDown) { + setState(() { + if (hist_cnt < previousCommands.length - 1) { + hist_cnt++; + } else { + hist_cnt = -1; + } + if (hist_cnt >= 0) { + showTextOnBuild = previousCommands.elementAt(hist_cnt); + } else { + showTextOnBuild = ''; + } + print('Arrow Down: $hist_cnt'); + }); + } + } + return KeyEventResult.ignored; + }, + ); + } + + @override + void initState() { + super.initState(); + } +} diff --git a/flutter/ayf_pack/lib/sub-widgets/ayf_component_combobox.dart b/flutter/ayf_pack/lib/sub-widgets/ayf_component_combobox.dart new file mode 100644 index 00000000..26623cea --- /dev/null +++ b/flutter/ayf_pack/lib/sub-widgets/ayf_component_combobox.dart @@ -0,0 +1,170 @@ +import 'package:flutter/material.dart'; + +import 'package:collection/collection.dart'; +import 'package:provider/provider.dart'; +import '../ayf_pack_local.dart'; + +// ====================================================================== + +// Define a callback to select one of the items. If the function returns a +// null the hint text is shown instead +typedef AudYoFloComponentSelectionOptionCallback + = AudYoFloOneComponentSelectionOption? Function( + List); + +// ====================================================================== + +// Widget to display a combo box with all components + +class AudYoFloComponentSelectionOption extends StatelessWidget { + // List of selections + final List pContent; + + // No selection hint + final String noSelectionHint; + + // Callback to select one item right before build + final AudYoFloComponentSelectionOptionCallback? cbSetSelectItem; + + // Constructor: important parameters passed as arguments + AudYoFloComponentSelectionOption( + {required this.pContent, + required this.noSelectionHint, + this.cbSetSelectItem}); + + // Main build function + @override + Widget build(BuildContext context) { + // The currently selected entry to be specified right at build function + AudYoFloOneComponentSelectionOption? selInstance; + if (cbSetSelectItem != null) { + selInstance = cbSetSelectItem!(pContent); + } + + // Return the dropdown menu + return DropdownButton( + isExpanded: true, + value: selInstance, + icon: const Icon(Icons.arrow_downward), + iconSize: 24, + elevation: 16, + hint: Text(noSelectionHint), + style: const TextStyle(color: Colors.deepPurple), + underline: Container( + height: 2, + color: Colors.deepPurpleAccent, + ), + onChanged: (AudYoFloOneComponentSelectionOption? newValue) { + //setState(() {} <- I think we do not need this + }, + items: pContent + .map>( + (AudYoFloOneComponentSelectionOption value) { + Widget? elementWidget; + if (value.sids.length > 0) { + String slots = intValueList2String(value.sids); + elementWidget = Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded(child: Text(value.description)), + Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + color: Colors.red, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text(slots, + style: TextStyle(color: Colors.white))))) + ]); + } + if (elementWidget != null) { + return DropdownMenuItem( + value: value, + child: elementWidget, + ); + } else { + return DropdownMenuItem( + value: value, + child: Text(value.description), + ); + } + }).toList()); + } +} + +// =========================================================================== +class AudYoFloComponentSelectionComboBoxWidget extends StatefulWidget { + final JvxComponentIdentification cpTp; + AudYoFloComponentSelectionComboBoxWidget(this.cpTp); + + @override + State createState() { + return _AudYoFloComponentSelectionComboBox(); + } +} + +class _AudYoFloComponentSelectionComboBox + extends State { + List? pContent; + + // AudYoFloStringWithId dropdownValuePrimDev = AudYoFloStringWithId('None', -1); + @override + Widget build(BuildContext context) { + return Selector( + selector: (context, beModel) { + int retVal = -1; + // We are waiting for an update of the device list of the technology list + MapEntry? + elm = beModel.deviceOptionMap.selectionOptionCacheMap.entries + .firstWhereOrNull((element) => + (element.key.cpTp == widget.cpTp.cpTp) && + (element.key.slotid == widget.cpTp.slotid)); + if (elm != null) { + retVal = elm.value.options.ssUpdateId; + } + return retVal; + }, + //shouldRebuild: (previous, next) => next ? false : true, + shouldRebuild: (previous, next) => (next != previous) ? true : false, + builder: (context, notifier, child) { + AudYoFloBackendCache theBeCache = + Provider.of(context, listen: false); + + Future triggerSelectionOptionList( + JvxComponentIdentification tp) async { + int errCode = jvxErrorType.JVX_NO_ERROR; + pContent = await checkAndUpdateComponentOptions(theBeCache, tp); + return errCode; + } + + return FutureBuilder( + future: triggerSelectionOptionList(widget.cpTp), + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + int errCode = jvxErrorType.JVX_ERROR_INTERNAL; + int? errCodePtr = snapshot.data; + if (errCodePtr != null) { + errCode = errCodePtr; + } + + if (errCode == jvxErrorType.JVX_NO_ERROR) { + /* + if (pContent != null) { + for (var elm in pContent!) { + print('Found Device List: ${elm.descriptor}'); + } + } + */ + } + } + + List nnList = []; + if (pContent != null) { + nnList = pContent!; + } + return AudYoFloComponentSelectionOption( + pContent: nnList, noSelectionHint: 'No item selected'); + }); + }); + } +} diff --git a/flutter/ayf_pack/lib/sub-widgets/ayf_custom_list_widget.dart b/flutter/ayf_pack/lib/sub-widgets/ayf_custom_list_widget.dart new file mode 100644 index 00000000..7537c897 --- /dev/null +++ b/flutter/ayf_pack/lib/sub-widgets/ayf_custom_list_widget.dart @@ -0,0 +1,91 @@ +import 'package:flutter/material.dart'; + +// +// https://docs.flutter.dev/development/ui/advanced/slivers +// +class AudYoFloCustomListWidget extends StatefulWidget { + @override + State createState() { + return _AudYoFloCustomListWidgetStates(); + } +} + +class _AudYoFloCustomListWidgetStates extends State { + ScrollController _myControllerL = ScrollController(); + ScrollController _myControllerR = ScrollController(); + @override + Widget build(BuildContext context) { + return Row(children: [ + Flexible( + flex: 1, + child: ListView.builder( + controller: _myControllerL, + itemCount: 10, + itemBuilder: (BuildContext context, int index) { + return Card( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: SizedBox( + height: 200, + width: 200, + child: Stack(children: [ + Image.asset( + 'images/fernlive_icons/fernlive-hdrive.png'), + Text("--") + ])), + ), + Text(index.toString()), + Text('--') + ])); + }, + ), + ), + Flexible( + flex: 1, + child: CustomScrollView( + controller: _myControllerR, + slivers: [ + SliverAppBar( + title: Center(child: Text('Audio Devices')), + backgroundColor: Colors.blueGrey, + pinned: true, + stretch: true, + onStretchTrigger: () async { + print('Hello'); + }, + floating: true, + snap: true, + expandedHeight: 200), + SliverList( + delegate: + SliverChildBuilderDelegate((BuildContext context, int index) { + return Card( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: SizedBox( + height: 200, + width: 200, + child: Stack(children: [ + Image.asset( + 'images/fernlive_icons/fernlive-hdrive.png'), + Text("--") + ])), + ), + Text(index.toString()), + Text('--') + ])); + }, childCount: 10), + ) + ], + ), + ), + Text('xy') + ]); + } +} diff --git a/flutter/ayf_pack/lib/sub-widgets/devices/ayf_device_selection_widget.dart b/flutter/ayf_pack/lib/sub-widgets/devices/ayf_device_selection_widget.dart new file mode 100644 index 00000000..d0b88c1e --- /dev/null +++ b/flutter/ayf_pack/lib/sub-widgets/devices/ayf_device_selection_widget.dart @@ -0,0 +1,93 @@ +import 'package:provider/provider.dart'; +import 'package:flutter/material.dart'; +import 'package:collection/collection.dart'; +import '../../ayf_pack_local.dart'; + +//import '../ayf_devices_active_inactive_widget.dart'; +//import '../audio-devices/ayf_audio_device_channel_selection_widget.dart'; + +class AudYoFloDeviceSelectionWidget extends StatefulWidget { + final JvxComponentIdentification identT; + final String tagDevices; + final Widget? Function(JvxComponentIdentification selCpId) + callbackDeviceWidget; + final Widget? Function( + AudYoFloOneSelectionOptionWithId option, + bool fromActiveList, + AudYoFloSingleSelectionComponent?) callbackActiveInactiveDeviceSliver; + + AudYoFloDeviceSelectionWidget(this.identT, this.tagDevices, + this.callbackDeviceWidget, this.callbackActiveInactiveDeviceSliver); + @override + State createState() { + return _AudYoFloDeviceSelectionWidgetStates(); + } +} + +class _AudYoFloDeviceSelectionWidgetStates + extends State + with + AudYoFloSingleSelectionComponent, + AudYoFloOneTechnologyDevicesOnChange { + List? pContent; + JvxComponentIdentification selCpId = JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_UNKNOWN); + + // =========================================================================== + // Implements the callback in case a device was selected in a sub widget + // At the moment, this callback is called if a device was chosen in the list + // of active devices. + @override + void reportSelectionComponent(JvxComponentIdentification cpId) { + setState(() { + selCpId = cpId; + }); + } + + // =========================================================================== + // =========================================================================== + @override + void updateDevices(List devs) { + // Pass in the current devices + pContent = devs; + } + + @override + Widget build(BuildContext context) { + cpId = widget.identT; + return localBuild(context); + } + + @override + Widget innerWidget(BuildContext context) { + // ======================================================================== + // Here, we allocate the widget that show the component specific widget + Widget? devWidget = widget.callbackDeviceWidget(selCpId); + // ======================================================================== + + if ((selCpId.cpTp == JvxComponentTypeEnum.JVX_COMPONENT_UNKNOWN) || + (devWidget == null)) { + return //Container(color: Colors.blue, child: Text('Success')); + AudYoFloDeviceActiveInactiveWidget(pContent, widget.identT, + widget.tagDevices, widget.callbackActiveInactiveDeviceSliver, + reportWidget: this); + } else { + return Column( + children: [ + Flexible( + flex: 1, + child: AudYoFloDeviceActiveInactiveWidget(pContent, widget.identT, + widget.tagDevices, widget.callbackActiveInactiveDeviceSliver, + reportWidget: this)), + Flexible( + flex: 1, + //child: AudYoFloInputOutputChannelWidget(selCpId)), + child: devWidget) + ], + ); + } + } +} + +// ============================================================================== + diff --git a/flutter/ayf_pack/lib/sub-widgets/devices/ayf_devices_active_inactive_widget.dart b/flutter/ayf_pack/lib/sub-widgets/devices/ayf_devices_active_inactive_widget.dart new file mode 100644 index 00000000..eaab0b80 --- /dev/null +++ b/flutter/ayf_pack/lib/sub-widgets/devices/ayf_devices_active_inactive_widget.dart @@ -0,0 +1,181 @@ +import 'package:provider/provider.dart'; +import 'package:flutter/material.dart'; +import '../../ayf_pack_local.dart'; + +import 'package:collection/collection.dart'; + +class AudYoFloDeviceSelectionSliverAppBar extends StatelessWidget { + final String text; + AudYoFloDeviceSelectionSliverAppBar(this.text); + + @override + Widget build(BuildContext context) { + return SliverAppBar( + title: Center( + child: Text( + text, + style: TextStyle(color: Colors.white, fontSize: 16), + )), + backgroundColor: Colors.blueGrey, + pinned: false, + stretch: false, + onStretchTrigger: () async { + print('Hello'); + }, + floating: true, + snap: true, + //expandedHeight: 200 + ); + } +} + +class deviceSelectionDragData { + deviceSelectionDragData(this.isActive, this.id); + bool isActive = false; + int id = -1; +} + +class AudYoFloDeviceActiveInactiveWidget extends StatefulWidget { + final List? pContent; + final JvxComponentIdentification cpId; + final AudYoFloSingleSelectionComponent? reportWidget; + final String tagDevices; + final Widget? Function( + AudYoFloOneSelectionOptionWithId option, + bool fromActiveList, + AudYoFloSingleSelectionComponent?) callbackActiveInactiveDeviceSliver; + AudYoFloDeviceActiveInactiveWidget(this.pContent, this.cpId, this.tagDevices, + this.callbackActiveInactiveDeviceSliver, + {this.reportWidget}); + + @override + State createState() { + return _AudYoFloDeviceActiveInactiveWidgetStates(); + } +} + +class AudYoFloOneSelectionOptionWithId { + AudYoFloOneDeviceSelectionOption option; + int idx; + AudYoFloOneSelectionOptionWithId(this.option, this.idx); +} + +class _AudYoFloDeviceActiveInactiveWidgetStates + extends State { + ScrollController _controllerL = ScrollController(); + ScrollController _controllerR = ScrollController(); + List inactiveDevices = []; + List activeDevices = []; + + void createActiveInactiveLists() { + inactiveDevices.clear(); + activeDevices.clear(); + if (widget.pContent != null) { + for (var elm in widget.pContent!) { + if (elm.devIdent.slotsubid >= 0) { + AudYoFloOneSelectionOptionWithId newContent = + AudYoFloOneSelectionOptionWithId(elm, activeDevices.length); + activeDevices.add(newContent); + } else { + AudYoFloOneSelectionOptionWithId newContent = + AudYoFloOneSelectionOptionWithId(elm, inactiveDevices.length); + inactiveDevices.add(newContent); + } + } + } + } + + @override + Widget build(BuildContext context) { + createActiveInactiveLists(); + //return Container(color: Colors.blue, child: Text('Success')); + AudYoFloBackendCache theBeCache = + Provider.of(context, listen: false); + + return Row(children: [ + Flexible( + flex: 1, + child: DragTarget( + builder: (context, candidateData, rejectedData) => CustomScrollView( + controller: _controllerL, + slivers: [ + AudYoFloDeviceSelectionSliverAppBar( + ('Inactive ${widget.tagDevices}')), + SliverList( + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + Widget? sliv = + widget.callbackActiveInactiveDeviceSliver( + inactiveDevices[index], false, null); + return sliv; + }, childCount: inactiveDevices.length), + ) + ], + ), + onWillAccept: (data) { + if (data != null) { + if (data.isActive) { + return true; + } + return false; + } + return false; + }, + onAccept: (data) { + // data.id is the index in the + // We then get the index in device option list (active AND inactive) + // in + // The "target" slot is DONTCARE which means "next available" + AudYoFloOneSelectionOptionWithId selId = activeDevices[data.id]; + JvxComponentIdentification cpDeactivate = selId.option.devIdent; + theBeCache.triggerDeactivateComponent(cpDeactivate); + }), + ), + Flexible( + flex: 1, + child: DragTarget( + builder: (context, candidateData, rejectedData) => CustomScrollView( + controller: _controllerR, + slivers: [ + AudYoFloDeviceSelectionSliverAppBar( + 'Active ${widget.tagDevices}'), + SliverList( + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + // Prepare the drag data + + Widget? sliv = + widget.callbackActiveInactiveDeviceSliver( + activeDevices[index], + true, + widget.reportWidget); + return sliv; + }, childCount: activeDevices.length), + ) + ], + ), + onWillAccept: (data) { + if (data != null) { + if (!data.isActive) { + return true; + } + return false; + } + return false; + }, + onAccept: (data) { + // data.id is the index in the + // We then get the index in device option list (active AND inactive) + // in + // The "target" slot is DONTCARE which means "next available" + AudYoFloOneSelectionOptionWithId selId = inactiveDevices[data.id]; + JvxComponentIdentification cpActivate = selId.option.devIdent; + cpActivate.slotsubid = jvxSize.JVX_SIZE_DONTCARE; + theBeCache.triggerActivateComponent( + cpActivate, selId.option.optionIdx, + selectBeforeActivate: true); // Immediately activate! + }), + ), + ]); + } +} diff --git a/flutter/ayf_pack/lib/sub-widgets/file-input-devices/ayf_file_input_active_devices.dart b/flutter/ayf_pack/lib/sub-widgets/file-input-devices/ayf_file_input_active_devices.dart new file mode 100644 index 00000000..cfe09db7 --- /dev/null +++ b/flutter/ayf_pack/lib/sub-widgets/file-input-devices/ayf_file_input_active_devices.dart @@ -0,0 +1,82 @@ +import 'package:flutter/material.dart'; +import '../../ayf_pack_local.dart'; + +class AudYoFloFileInputActiveDevicesWidget extends StatefulWidget { + const AudYoFloFileInputActiveDevicesWidget(this.identT, {Key? key}) + : super(key: key); + + final JvxComponentIdentification identT; + + @override + _AudYoFloFileInputActiveDevicesWidgetState createState() => + _AudYoFloFileInputActiveDevicesWidgetState(); +} + +class _AudYoFloFileInputActiveDevicesWidgetState + extends State + with AudYoFloOneTechnologyDevicesOnChange { + List? devLst; + ScrollController _controllerL = ScrollController(); + + @override + void updateDevices(List? devs) { + devLst = devs; + } + + @override + Widget build(BuildContext context) { + cpId = widget.identT; + return localBuild(context); + } + + @override + Widget innerWidget(BuildContext context) { + List activeDevices = []; + if (devLst != null) { + for (var elm in devLst!) { + if (elm.devIdent.slotsubid >= 0) { + AudYoFloOneSelectionOptionWithId newContent = + AudYoFloOneSelectionOptionWithId(elm, activeDevices.length); + activeDevices.add(newContent); + } + } + } + + return LayoutBuilder(builder: (_, constraints) { + return CustomScrollView(slivers: [ + AudYoFloDeviceSelectionSliverAppBar(('Audio File Input Devices')), + SliverToBoxAdapter( + child: SizedBox( + height: 150, + child: Scrollbar( + // 1) https://www.youtube.com/watch?v=DbkIQSvwnZc + // 2) https://stackoverflow.com/questions/69853729/flutter-the-scrollbars-scrollcontroller-has-no-scrollposition-attached + scrollbarOrientation: ScrollbarOrientation.bottom, + // thumbVisibility: true, + controller: _controllerL, + child: CustomScrollView( + scrollDirection: Axis.horizontal, + controller: _controllerL, + slivers: [ + SliverList( + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + Widget? sliv = Container( + color: Colors.blueGrey, + width: 300, //constraints.maxWidth / 4, + child: AudYoFloOneFileDeviceSliver( + activeDevices[index], + devLst![index].devIdent, + null)); + return sliv; + }, childCount: activeDevices.length), + ) + ]))), + + //), + //), + ) + ]); + }); + } +} diff --git a/flutter/ayf_pack/lib/sub-widgets/file-input-devices/ayf_file_input_active_devices_sliver.dart b/flutter/ayf_pack/lib/sub-widgets/file-input-devices/ayf_file_input_active_devices_sliver.dart new file mode 100644 index 00000000..34418d09 --- /dev/null +++ b/flutter/ayf_pack/lib/sub-widgets/file-input-devices/ayf_file_input_active_devices_sliver.dart @@ -0,0 +1,297 @@ +import 'package:flutter/material.dart'; +import '../../ayf_pack_local.dart'; +import 'package:provider/provider.dart'; +import 'package:collection/collection.dart'; + +class AudYoFloOneFileDeviceSliver extends StatelessWidget + with AudYoFloOneComponentPropertyOnChange, AudYoFloPropertyOnChangeGroups { + final AudYoFloOneSelectionOptionWithId option; + final JvxComponentIdentification cpIdConf; + final AudYoFloSingleSelectionComponent? reportTarget; + AudYoFloBackendCache? theBeCache; + + AudYoFloPropertyOnChangeOneGroup oneGroupThisWidget = + AudYoFloPropertyOnChangeOneGroup([ + '/restart', + '/fwd10', + '/bwd10', + '/close', + '/togpause', + '/length_sec', + '/progress_percent', + '/file_status', + '/system/src_name' + ]); + + AudYoFloOneFileDeviceSliver(this.option, this.cpIdConf, this.reportTarget) { + super.cpId = cpIdConf; + theGroups['thisWidget'] = oneGroupThisWidget; + + createList(); + } + + @override + List requestedPropertiesList() { + return propsAllGroups; + } + + @override + void reconstructProperties(List props) { + reconstructProperyReferences(props); + } + + @override + Widget build(BuildContext context) { + theBeCache = Provider.of(context, listen: false); + // Produce Device Name + + /* + AudYoFloUiModel theUiCache = + Provider.of(context, listen: false); + */ + return localBuild(context); + } + + @override + Widget innerWidget(BuildContext context) { + bool updateRealtime = false; + // String showDeviceName = option.option.description; + + // Show device capabailities + Widget icon = Icon(Icons.audio_file_outlined, size: 20); + Widget? statusWidget; + + if (option.option.state + .bitTest(jvxStateShiftEnum.JVX_STATE_SELECTED.index)) { + statusWidget = Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + color: Colors.red, child: Icon(Icons.warning, size: 20))); + } else if (option.option.state + .bitTest(jvxStateShiftEnum.JVX_STATE_ACTIVE.index)) { + statusWidget = Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + color: Colors.green, child: Icon(Icons.stop, size: 20))); + } else if (option.option.state + .bitTest(jvxStateShiftEnum.JVX_STATE_PREPARED.index)) { + statusWidget = Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + color: Colors.yellow, child: Icon(Icons.pause, size: 20))); + } else if (option.option.state + .bitTest(jvxStateShiftEnum.JVX_STATE_PROCESSING.index)) { + updateRealtime = true; + statusWidget = Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + color: Colors.blue, child: Icon(Icons.play_arrow, size: 20))); + } else { + statusWidget = Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + color: Colors.grey, + child: Icon(Icons.check_box_outline_blank, size: 20))); + } + + // Extract filename + String name = ''; + AudYoFloPropertyOnChangeOneGroup? groupThisWidget; + MapEntry? groupEntryThis = + theGroups.entries + .firstWhereOrNull((element) => element.key == 'thisWidget'); + if (groupEntryThis != null) { + groupThisWidget = groupEntryThis.value; + } + if (groupThisWidget != null) { + var propStr = groupThisWidget.extractPropString('/system/src_name'); + if (propStr != null) { + name = propStr.value; + } + } + + // Extract icon sizes + double iconSize = 30; + double boxHeight = 40; + double boxWidth = 50; + + AudYoFloPropertyMultiContentBackend? propClose; + if (groupThisWidget != null) { + propClose = groupThisWidget.extractPropMultiContent('/close'); + } + + AudYoFloPropertyMultiContentBackend? propRestart; + if (groupThisWidget != null) { + propRestart = groupThisWidget.extractPropMultiContent('/restart'); + } + + AudYoFloPropertyMultiContentBackend? propFwd; + if (groupThisWidget != null) { + propFwd = groupThisWidget.extractPropMultiContent('/fwd10'); + } + + AudYoFloPropertyMultiContentBackend? propBwd; + if (groupThisWidget != null) { + propBwd = groupThisWidget.extractPropMultiContent('/bwd10'); + } + + AudYoFloPropertyMultiContentBackend? propTogPause; + if (groupThisWidget != null) { + propTogPause = groupThisWidget.extractPropMultiContent('/togpause'); + } + + double progressValue = 0; + AudYoFloPropertyMultiContentBackend? propProgress; + if (groupThisWidget != null) { + propProgress = + groupThisWidget.extractPropMultiContent('/progress_percent'); + if (propProgress != null) { + progressValue = propProgress.fld[0]; + } + } + + AudYoFloPropertySelectionListBackend? propFileStatus; + if (groupThisWidget != null) { + propFileStatus = groupThisWidget.extractPropSelectionList('/file_status'); + } + + IconData idat = Icons.stop; + if (propFileStatus != null) { + if (propFileStatus.selection.bitTest(1)) { + // If playing.. + idat = Icons.pause; + } else if (propFileStatus.selection.bitTest(2)) { + idat = Icons.play_arrow; + } else if (propFileStatus.selection.bitTest(3)) { + idat = Icons.warning; + } + } + + return GestureDetector( + onTap: () { + if (reportTarget != null) { + reportTarget!.reportSelectionComponent(option.option.devIdent); + } + }, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + color: Colors.grey, + child: Card( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Row(children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text(name /*showDeviceName*/))), + SizedBox( + height: boxHeight, + width: boxWidth, + child: FittedBox( + child: FloatingActionButton( + onPressed: () { + if (propClose != null) { + propClose.fld[0] = 1; + if (theBeCache != null) { + List propContents = ['/close']; + theBeCache!.triggerSetProperties( + cpIdConf, propContents); + } + } + }, + backgroundColor: Colors.black, + child: Icon(Icons.delete, size: iconSize)))), + ]), + Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, + //mainAxisAlignment: MainAxisAlignment.start, children: [ + + children: [ + SizedBox( + height: boxHeight, + width: boxWidth, + child: FittedBox( + child: FloatingActionButton( + onPressed: () { + if (propRestart != null) { + propRestart.fld[0] = 1; + if (theBeCache != null) { + theBeCache!.triggerSetProperties( + cpIdConf, + [propRestart.parpropc.descriptor]); + } + } + }, + backgroundColor: Colors.black, + child: Icon(Icons.keyboard_return, + size: iconSize)))), + SizedBox( + height: boxHeight, + width: boxWidth, + child: FittedBox( + child: FloatingActionButton( + onPressed: () { + if (propBwd != null) { + propBwd.fld[0] = 1; + if (theBeCache != null) { + theBeCache!.triggerSetProperties( + cpIdConf, + [propBwd.parpropc.descriptor]); + } + } + }, + backgroundColor: Colors.black, + child: Icon(Icons.fast_rewind, + size: iconSize)))), + SizedBox( + height: boxHeight, + width: boxWidth, + child: FittedBox( + child: FloatingActionButton( + onPressed: () { + if (propFwd != null) { + propFwd.fld[0] = 1; + if (theBeCache != null) { + theBeCache!.triggerSetProperties( + cpIdConf, + [propFwd.parpropc.descriptor]); + } + } + }, + backgroundColor: Colors.black, + child: Icon(Icons.fast_forward, + size: iconSize)))), + SizedBox( + height: boxHeight, + width: boxWidth, + child: FittedBox( + child: FloatingActionButton( + onPressed: () { + if (propTogPause != null) { + propTogPause.fld[0] = 1; + if (theBeCache != null) { + theBeCache!.triggerSetProperties( + cpIdConf, + [propTogPause.parpropc.descriptor]); + } + } + }, + backgroundColor: Colors.black, + child: Icon(idat, size: iconSize)))), + statusWidget + ]), + Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: AudYoFloProgressIndicator(cpIdConf, + '/progress_percent', updateRealtime, progressValue), + )) + ]), + ])), + ), + ), + ); + } +} diff --git a/flutter/ayf_pack/lib/sub-widgets/headtracker-devices/ayf_active_inactive_headtracker_device_sliver.dart b/flutter/ayf_pack/lib/sub-widgets/headtracker-devices/ayf_active_inactive_headtracker_device_sliver.dart new file mode 100644 index 00000000..2ccbc5f4 --- /dev/null +++ b/flutter/ayf_pack/lib/sub-widgets/headtracker-devices/ayf_active_inactive_headtracker_device_sliver.dart @@ -0,0 +1,117 @@ +import 'package:provider/provider.dart'; +import 'package:flutter/material.dart'; +import '../../ayf_pack_local.dart'; + +import 'package:collection/collection.dart'; + +class AudYoFloHeadtrackerDeviceRenderSection { + String imageNameHeadtracker; + + AudYoFloHeadtrackerDeviceRenderSection({required this.imageNameHeadtracker}); +} + +class AudYoFloOneHeadtrackerDeviceSliver extends StatelessWidget { + final AudYoFloOneSelectionOptionWithId option; + final bool fromActiveList; + final AudYoFloSingleSelectionComponent? reportTarget; + + AudYoFloOneHeadtrackerDeviceSliver( + this.option, this.fromActiveList, this.reportTarget); + + Widget iconForDeviceOption( + AudYoFloOneDeviceSelectionOption dev, AudYoFloUiModel uiModel) { + String imageNameHeadtracker = ''; + + MapEntry? elm = uiModel + .widgetSpecificConfigurations.entries + .firstWhereOrNull((element) => element.key == 'headtracker'); + if (elm != null) { + if (elm.value is AudYoFloHeadtrackerDeviceRenderSection) { + AudYoFloHeadtrackerDeviceRenderSection elmSoundIo = + elm.value as AudYoFloHeadtrackerDeviceRenderSection; + imageNameHeadtracker = elmSoundIo.imageNameHeadtracker; + } + } + + return Image.asset(imageNameHeadtracker); + } + + @override + Widget build(BuildContext context) { + // Produce Device Name + + AudYoFloUiModel theUiCache = + Provider.of(context, listen: false); + + String showDeviceName = option.option.description; + + deviceSelectionDragData dData = + deviceSelectionDragData(fromActiveList, option.idx); + + // Show device capabailities + Widget icon = iconForDeviceOption(option.option, theUiCache); + Widget? statusWidget; + + String selectable = ''; + if (!option.option.selectable) { + selectable = 'x'; + } + + if (option.option.state + .bitTest(jvxStateShiftEnum.JVX_STATE_SELECTED.index)) { + statusWidget = Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + color: Colors.red, child: Icon(Icons.warning, size: 20))); + } else if (option.option.state + .bitTest(jvxStateShiftEnum.JVX_STATE_ACTIVE.index)) { + statusWidget = Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + color: Colors.green, child: Icon(Icons.stop, size: 20))); + } else if (option.option.state + .bitTest(jvxStateShiftEnum.JVX_STATE_PREPARED.index)) { + statusWidget = Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + color: Colors.yellow, child: Icon(Icons.pause, size: 20))); + } else if (option.option.state + .bitTest(jvxStateShiftEnum.JVX_STATE_PROCESSING.index)) { + statusWidget = Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + color: Colors.blue, child: Icon(Icons.play_arrow, size: 20))); + } else { + statusWidget = Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + color: Colors.grey, + child: Icon(Icons.check_box_outline_blank, size: 20))); + } + + return GestureDetector( + onTap: () { + // here, we run a callback to the next higher layer in the widget hierarchy to + // update the widget which is in parallel to the device selection construct + if (reportTarget != null) { + reportTarget!.reportSelectionComponent(option.option.devIdent); + } + }, + child: Card( + child: Row( + //mainAxisAlignment: MainAxisAlignment.start, children: [ + children: [ + Draggable( + feedback: SizedBox(height: 30, width: 60, child: icon), + data: dData, + child: SizedBox(height: 30, width: 60, child: icon)), + Expanded( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text(showDeviceName))), + statusWidget, + Padding(padding: const EdgeInsets.all(8.0), child: Text(selectable)) + ])), + ); + } +} diff --git a/flutter/ayf_pack/lib/systemstate/ayf_connected_processes.dart b/flutter/ayf_pack/lib/systemstate/ayf_connected_processes.dart new file mode 100644 index 00000000..4710b600 --- /dev/null +++ b/flutter/ayf_pack/lib/systemstate/ayf_connected_processes.dart @@ -0,0 +1,148 @@ +import 'package:collection/collection.dart'; +import '../ayf_pack_local.dart'; + +class AudYoFloOneConnectedProcess { + int uId = -1; + int catId = -1; + String nameProcess = ''; + AudYoFloOneComponentInProcess? involved; + AudYoFloOneConnectedProcess(this.uId); + + String debugPrintOneIterator( + AudYoFloOneComponentInProcess involved, String tab) { + String ret = ''; + var name = involved.nameComponent; + var mod = involved.nameModule; + var cname = involved.nameConnector; + var cpIdName = involved.cpId.txt; + ret = '$tab -> $name -> $mod -> $cpIdName -> $cname\n'; + tab = tab + '\t'; + for (int ii = 0; ii < involved.next.length; ii++) { + ret += '$tab -> Branch: $ii\n'; + ret += debugPrintOneIterator(involved.next.elementAt(ii), tab); + } + return ret; + } + + String debugPrintProcess() { + String ret = 'Process connected: $nameProcess\n'; + if (involved != null) { + ret += debugPrintOneIterator(involved!, ''); + } + return ret; + } +} + +abstract class AudYoFloOneComponentInProcess { + JvxComponentIdentification cpId = JvxComponentIdentification(); + List next = []; + String nameComponent = ''; + String nameModule = ''; + String nameConnector = ''; + + void attach(AudYoFloOneComponentInProcess cpIProc) { + next.add(cpIProc); + } + + void fill(dynamic implHandle); + + // Return if the passed cpIdentitification is affected by this object + // and any following. + bool findComponentIdentification(JvxComponentIdentification cpIdArg) { + bool overallResult = false; + if (cpId == cpIdArg) { + overallResult = true; + } else { + for (var elm in next) { + overallResult = elm.findComponentIdentification(cpIdArg); + if (overallResult) { + break; + } + } + } + return overallResult; + } + + void findComponentsMatch(List lst, + Map allCps) { + var entry = + allCps.entries.firstWhereOrNull((element) => (cpId == element.key)); + if (entry != null) { + bool addElement = true; + for (var elm in lst) { + if (elm == cpId) { + addElement = false; + break; + } + } + if (addElement) { + lst.add(cpId); + } + } + for (var elm in next) { + elm.findComponentsMatch(lst, allCps); + } + } +} + +class AudYoFloConnectedProcesses { + Map reportedProcesses = new Map(); + AudYoFloConnectedProcesses(); + /* + late fernLiveNative natLib; + late Pointer opaqueHost; +*/ + AudYoFloBackendAdapterIf? be; + + void fullClear() { + reportedProcesses.clear(); + } + + void initialize(AudYoFloBackendAdapterIf? beArg) { + be = beArg; + } + + int addProcess(int uId) { + int retVal = jvxErrorType.JVX_NO_ERROR; + MapEntry? entry = reportedProcesses.entries + .firstWhereOrNull((element) => element.key == uId); + if (entry != null) { + retVal = retVal = jvxErrorType.JVX_ERROR_ALREADY_IN_USE; + } else { + // There is no handle for the process yet. Hence, allocate a new object + AudYoFloOneConnectedProcess? theProcPtr = be?.allocateProcess(uId); + if (theProcPtr != null) { + AudYoFloOneConnectedProcess theProc = theProcPtr; + reportedProcesses[uId] = theProc; + } + } + return retVal; + } + + int removeProcess(int uId) { + int retVal = jvxErrorType.JVX_NO_ERROR; + MapEntry? entry = reportedProcesses.entries + .firstWhereOrNull((element) => element.key == uId); + if (entry == null) { + retVal = retVal = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + } else { + reportedProcesses.remove(uId); + } + + return retVal; + } + + String debugPrintProcess(int uId) { + String ret = ''; + MapEntry entry = + reportedProcesses.entries.firstWhere((element) { + AudYoFloOneConnectedProcess proc = element.value; + return ((uId == proc.uId) ? true : false); + }); + if (entry != null) { + AudYoFloOneConnectedProcess proc = entry.value; + ret = proc.debugPrintProcess(); + } + return ret; + } +} diff --git a/flutter/ayf_pack/lib/systemstate/ayf_selected_components.dart b/flutter/ayf_pack/lib/systemstate/ayf_selected_components.dart new file mode 100644 index 00000000..c99833a8 --- /dev/null +++ b/flutter/ayf_pack/lib/systemstate/ayf_selected_components.dart @@ -0,0 +1,130 @@ +import 'package:collection/collection.dart'; +import '../ayf_pack_local.dart'; + +class AudYoFloPropertyCache { + List? availProperties; + Map cachedProperties = {}; + int ssPropertyCache = 0; + AudYoFloPropertyContainer? findCachedProperty(String nm) { + AudYoFloPropertyContainer? theProp; + MapEntry? entryP = cachedProperties + .entries + .firstWhereOrNull((element) => element.key == nm); + if (entryP != null) { + theProp = entryP.value; + } + return theProp; + } + + int invalidateCachedProperty(String nm) { + int errCode = jvxErrorType.JVX_NO_ERROR; + MapEntry? entryP = cachedProperties + .entries + .firstWhereOrNull((element) => element.key == nm); + if (entryP != null) { + entryP.value.ssUpdateId++; + entryP.value.cache_status.bitZSet(jvxPropertyCacheStatusFlagShifts + .JVX_PROPERTY_DESCRIPTOR_INVALID.index); + } else { + errCode = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + } + return errCode; + } +} + +/* +class AudYoFloOneDevice { + String descriptor = 'not-found'; + String description = 'not-found'; + AudYoFloCBitField32 caps = AudYoFloCBitField32(); + jvxDeviceDataFlowTypeEnum flow = + jvxDeviceDataFlowTypeEnum.JVX_DEVICE_DATAFLOW_UNKNOWN; + bool selectable = false; + int subslotid = -1; +} +*/ + +// Store one component. The ssThisComponent field holds a counter that is increased +// whenever the component has changed - e.g. if switching the state +class AudYoFloOneSelectedComponent { + String description = 'Unknown'; + AudYoFloDirectPropertiesIf? directPropRef; + int ssThisComponent = 0; + //jvxAccessProperties_flutter? propRef; + AudYoFloPropertyCache propertyCache = AudYoFloPropertyCache(); + //List? devLst; +} + +// Class to keep track of all components selected in the backend +class AudYoFloSelectedComponents { + // Store the map, one entry for each component + Map + selectedComponents = + Map(); + /* + late fernLiveNative natLib; + late Pointer opaqueHost; +*/ + AudYoFloBackendAdapterIf? be; + + // ================================================================= + AudYoFloSelectedComponents(); + + void initialize(AudYoFloBackendAdapterIf? beArg) { + be = beArg; + } + + // Add a newly created component + int addComponent(JvxComponentIdentification cpId, String description, + {AudYoFloDirectPropertiesIf? directPropRef}) { + int retVal = jvxErrorType.JVX_NO_ERROR; + MapEntry? entry = selectedComponents.entries + .firstWhereOrNull((element) => element.key == cpId); + if (entry != null) { + retVal = retVal = jvxErrorType.JVX_ERROR_ALREADY_IN_USE; + } else { + //jvxAccessProperties_flutter? theComp = be?.requestPropertyReference(cpId); + // if (theComp != null) { + var compObj = AudYoFloOneSelectedComponent(); + //compObj.propRef = theComp; + compObj.description = description; + compObj.directPropRef = directPropRef; + selectedComponents[cpId] = compObj; + //} else { + // print('No property reference for component <${cpId.txt}> on select'); + //} + } + + return retVal; + } + + // Update a component. This may be due to a state switch + int updateComponent(JvxComponentIdentification cpId) { + int retVal = jvxErrorType.JVX_NO_ERROR; + MapEntry? entry = selectedComponents.entries + .firstWhereOrNull((element) => element.key == cpId); + if (entry == null) { + retVal = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + } else { + AudYoFloOneSelectedComponent comp = entry.value; + comp.ssThisComponent++; + } + return retVal; + } + + // Remove a component from list of selected components + int removeComponent(JvxComponentIdentification cpId) { + int retVal = jvxErrorType.JVX_NO_ERROR; + MapEntry? entry = selectedComponents.entries + .firstWhereOrNull((element) => element.key == cpId); + if (entry == null) { + retVal = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + } else { + AudYoFloOneSelectedComponent entry_type = entry.value; + // jvxAccessProperties_flutter? propRefPtr = entry_type.propRef; + + selectedComponents.removeWhere((key, value) => key == cpId); + } + return retVal; + } +} diff --git a/flutter/ayf_pack/lib/types/ayf_bitfield.dart b/flutter/ayf_pack/lib/types/ayf_bitfield.dart new file mode 100644 index 00000000..e41ceb70 --- /dev/null +++ b/flutter/ayf_pack/lib/types/ayf_bitfield.dart @@ -0,0 +1,28 @@ +import 'dart:typed_data'; + +abstract class AudYoFloCBitFieldBase { + void bitZSet(int posi); + void bitSet(int posi); + bool bitTest(int posi); + void bitClear(int posi); + void bitFClear(); +} + +abstract class AudYoFloBitField { + void initialize(int num32BitBitfieldArg, {int numEntriesArg = 1}); + bool bitTest(int posi, {int offset = 0}); + int firstSet(int posiMax, {int offset = 0}); + void bitSet(int posi, {int offset = 0}); + void bitZSet(int posi, {int offset = 0}); + void bitClear(int posi, {int offset = 0}); + void bitFClear({int offset = 0}); + void copy(AudYoFloBitField copyFrom, {int offset = 0}); + void dispose(); +} + +abstract class AudYoFloBitFieldBackend extends AudYoFloBitField { + int compileNum32BitBitfield = 0; + int num32BitAlltogether = 0; + Uint32List? bitfield; + int numEntries = 0; +} diff --git a/flutter/ayf_pack/lib/types/ayf_bitfield_common.dart b/flutter/ayf_pack/lib/types/ayf_bitfield_common.dart new file mode 100644 index 00000000..7d4d368b --- /dev/null +++ b/flutter/ayf_pack/lib/types/ayf_bitfield_common.dart @@ -0,0 +1,169 @@ +import 'package:fixnum/fixnum.dart' as fn; +import '../../ayf_pack_local.dart'; + +abstract class AudYoFloCBitFieldT + extends AudYoFloCBitFieldBase { + T fld; + int szChar; + AudYoFloCBitFieldT(this.fld, this.szChar); + + // ================================================== + // Abstract helpers to be implemented in subclass + T shiftl(T old, int num); + + T ori(T old, int val); + T or(T old, T val); + + T and(T old, T val); + T reset(); + T one(); + T neg(T val); + // ================================================== + + // Some re-defined base class functions + void bitFClear() { + fld = reset(); + } + + @override + void bitClear(int posi) { + T andwith = one(); + andwith = shiftl(andwith, posi); + andwith = neg(andwith); + fld = and(fld, andwith); + } + + @override + void bitZSet(int posi) { + fld = one(); + fld = shiftl(fld, posi); + } + + @override + void bitSet(int posi) { + T orwith = one(); + orwith = shiftl(orwith, posi); + fld = or(fld, orwith); + } + + @override + bool bitTest(int posi) { + T comp = shiftl(one(), posi); + T val = and(fld, comp); + return val != 0; + } + + // Sophisticated functions + int fromHexString(String inStr) { + fld = reset(); + List lstStr = inStr.split('0x'); + if (lstStr.length == 2) { + if (lstStr[0].isEmpty) { + String str1 = lstStr[1]; + + int sz = str1.length; + if (sz > szChar) { + return jvxErrorType.JVX_ERROR_INVALID_ARGUMENT; + } + + while (sz > 0) { + String token = str1; + if (sz > 8) { + token = str1.substring(str1.length - 8, str1.length); + str1 = str1.substring(0, str1.length - 8); + } else { + token = str1; + str1 = ''; + } + fld = shiftl(fld, 32); + int val = int.parse(token, radix: 16); + fld = ori(fld, val); + sz = str1.length; + } + return jvxErrorType.JVX_NO_ERROR; + } + } + return jvxErrorType.JVX_ERROR_PARSE_ERROR; + } +} + +class AudYoFloCBitField64 extends AudYoFloCBitFieldT { + // Some + @override + fn.Int64 reset() => fn.Int64(0); + + @override + fn.Int64 one() => fn.Int64(1); + + @override + fn.Int64 shiftl(fn.Int64 val, int posi) { + return val << posi; + } + + @override + fn.Int64 ori(fn.Int64 val, int vali) { + val |= fn.Int64(vali); + return val; + } + + @override + fn.Int64 and(fn.Int64 val, fn.Int64 vali) { + val &= vali; + return val; + } + + @override + fn.Int64 or(fn.Int64 val, fn.Int64 vali) { + val |= vali; + return val; + } + + @override + fn.Int64 neg(fn.Int64 val) { + val = ~val; + return val; + } + + AudYoFloCBitField64() : super(fn.Int64(0), 16); +} + +class AudYoFloCBitField32 extends AudYoFloCBitFieldT { + @override + fn.Int32 reset() => fn.Int32(0); + + @override + fn.Int32 one() => fn.Int32(1); + + @override + fn.Int32 shiftl(fn.Int32 val, int posi) { + return val << posi; + } + + @override + fn.Int32 ori(fn.Int32 val, int vali) { + val |= fn.Int64(vali); + return val; + } + + @override + fn.Int32 and(fn.Int32 val, fn.Int32 vali) { + val &= vali; + return val; + } + + @override + fn.Int32 or(fn.Int32 val, fn.Int32 vali) { + val |= vali; + return val; + } + + @override + fn.Int32 neg(fn.Int32 val) { + val = ~val; + return val; + } + + AudYoFloCBitField32() : super(fn.Int32(0), 8); +} + +typedef AudYoFloCBitField = AudYoFloCBitField64; diff --git a/flutter/ayf_pack/lib/types/ayf_cache_types.dart b/flutter/ayf_pack/lib/types/ayf_cache_types.dart new file mode 100644 index 00000000..e56730f0 --- /dev/null +++ b/flutter/ayf_pack/lib/types/ayf_cache_types.dart @@ -0,0 +1,76 @@ +import '../ayf_pack_local.dart'; + +// ================================================ +// ================================================ +// Define some cache subsections +// ================================================ +// ================================================ + +class AudYoFloStateCache { + bool valid = false; + int ssUpdateId = 0; +} + +class AudYoFloComponentCache extends AudYoFloStateCache { + // Here, we store all selected components + AudYoFloSelectedComponents theSelectedComponents = + AudYoFloSelectedComponents(); + void fullClear() { + theSelectedComponents.selectedComponents.clear(); + } +} + +// Sequencer cache +class AudYoFloSequencerStateCache extends AudYoFloStateCache { + jvxSequencerStatusEnum seqStatus = + jvxSequencerStatusEnum.JVX_SEQUENCER_STATUS_NONE; + bool processesReady = false; + String reasonIfNot = ''; + + void fullClear() { + seqStatus = jvxSequencerStatusEnum.JVX_SEQUENCER_STATUS_NONE; + processesReady = false; + reasonIfNot = ''; + } +} + +/* +class AudYoFloComponentStateCache extends AudioFlowStateCache { + Map + componentCacheMap = {}; +} +*/ + +class AudYoFloDeviceSelectionMapCache { + // Node and device: Option for type + // Technology: Oe entry for type + slotid + Map> + selectionOptionCacheMap = {}; + void fullClear() { + selectionOptionCacheMap.clear(); + } +} + +class AudYoFloComponentSelectionMapCache { + // Node and device: Option for type + // Technology: Oe entry for type + slotid + Map> + selectionOptionCacheMap = {}; + + void fullClear() { + selectionOptionCacheMap.clear(); + } +} + +// System cache +class AudYoFloProcessesCache { + // Here, we store all processes + AudYoFloConnectedProcesses theRegisteredProcesses = + AudYoFloConnectedProcesses(); + + void fullClear() { + theRegisteredProcesses.fullClear(); + } +} diff --git a/flutter/ayf_pack/lib/types/ayf_components.dart b/flutter/ayf_pack/lib/types/ayf_components.dart new file mode 100644 index 00000000..389a27d0 --- /dev/null +++ b/flutter/ayf_pack/lib/types/ayf_components.dart @@ -0,0 +1,38 @@ +import '../ayf_pack_local.dart'; + +class JvxComponentIdentification { + JvxComponentTypeEnum cpTp; // = jvxComponentTypeEnum.JVX_COMPONENT_UNKNOWN; + int slotid; // = -1; + int slotsubid; // = -1; + int uid; // = -1; + JvxComponentIdentification( + {this.cpTp = JvxComponentTypeEnum.JVX_COMPONENT_UNKNOWN, + this.slotid = -1, + this.slotsubid = -1, + this.uid = -1}); + + void reset() { + cpTp = JvxComponentTypeEnum.JVX_COMPONENT_UNKNOWN; + slotid = -1; + slotsubid = -1; + uid = -1; + } + + String get txt { + String ret = cpTp.txt; + ret += '<$slotid,$slotsubid>'; + return ret; + } + + bool operator ==(Object arg) { + if (arg is JvxComponentIdentification) { + JvxComponentIdentification argc = arg; + if ((argc.cpTp == cpTp) && + (argc.slotid == slotid) && + (argc.slotsubid == slotsubid)) { + return true; + } + } + return false; + } +} diff --git a/flutter/ayf_pack/lib/types/ayf_model_types.dart b/flutter/ayf_pack/lib/types/ayf_model_types.dart new file mode 100644 index 00000000..30eae59e --- /dev/null +++ b/flutter/ayf_pack/lib/types/ayf_model_types.dart @@ -0,0 +1,116 @@ +import 'package:collection/collection.dart'; +import '../ayf_pack_local.dart'; + +// ========================================================================== + +abstract class AudYoFloOneComponentSettings { + JvxComponentIdentification cpId; + String compTag; + List propTags; + List propTooltips; + AudYoFloOneComponentSettings( + this.cpId, this.compTag, this.propTags, this.propTooltips); + String get specialization; +} + +class AudYoFloOneComponentSettingsLocal extends AudYoFloOneComponentSettings { + List directProps = []; + AudYoFloOneComponentSettingsLocal(String compTag, this.directProps, + {List propTags = const [], List propTooltips = const []}) + : super(JvxComponentIdentification(), compTag, propTags, propTooltips); + + @override + String get specialization { + return 'AudYoFloOneComponentSettingsLocal'; + } +} + +class AudYoFloOneComponentSettingsBackend extends AudYoFloOneComponentSettings { + List propsDescriptors; + AudYoFloOneComponentSettingsBackend( + JvxComponentIdentification cpId, String compTag, this.propsDescriptors, + {List propTags = const [], List propTooltips = const []}) + : super(cpId, compTag, propTags, propTooltips); + @override + String get specialization { + return 'AudYoFloOneComponentSettingsBackend'; + } +} + +// ======================================================================== + +enum AudYoFloSettingsConfigurationPropIfCondition { + AYF_PROPERTY_IF_EXISTS, + AYF_PROPERTY_IF_NOT_EXISTS, +} + +enum AudYoFloSettingsConfigurationPropThenAction { + AYF_PROPERTY_THEN_ENABLE, + AYF_PROPERTY_THEN_DISABLE, +} + +class AudYoFloSettingsConfigurationDepend { + String propAddress = ''; + AudYoFloSettingsConfigurationPropThenAction thenAction = + AudYoFloSettingsConfigurationPropThenAction.AYF_PROPERTY_THEN_DISABLE; +} + +class AudYoFloSettingsConfigurationDependency { + String propertyCheckIf = ''; + + AudYoFloSettingsConfigurationPropIfCondition ifCondition = + AudYoFloSettingsConfigurationPropIfCondition.AYF_PROPERTY_IF_EXISTS; + List propertyCheckThen = []; +} + +// ========================================================================= + +class AudYoFloSettingsConfigurationSection { + String nameEntry = ''; + String nameImage = ''; + // List lstDependencies = []; + Map theSettings = + {}; + + int addEntry(AudYoFloOneComponentSettings addMe) { + int errCode = jvxErrorType.JVX_NO_ERROR; + MapEntry? elm = + theSettings.entries.firstWhereOrNull((element) => false); + if (elm == null) { + theSettings[addMe.cpId] = addMe; + } + return errCode; + } + + /* + int addEntryList(JvxComponentIdentification cpId, String prop) { + int errCode = jvxErrorType.JVX_NO_ERROR; + MapEntry? elm = + theSettings.entries.firstWhereOrNull((element) => false); + if (elm != null) { + elm.value.propsSetting.add(prop); + } + return errCode; + } + */ + + int remEntry(JvxComponentIdentification cpId) { + int errCode = jvxErrorType.JVX_NO_ERROR; + MapEntry? elm = + theSettings.entries.firstWhereOrNull((element) => false); + if (elm != null) { + theSettings.remove(cpId); + } + return errCode; + } + + /* + void addDependency(AudYoFloSettingsConfigurationDependency newDep) { + lstDependencies.add(newDep); + } + */ +} + +class AudYoFloSettingsConfiguration { + Map allSections = {}; +} diff --git a/flutter/ayf_pack/lib/types/ayf_sequencer_types.dart b/flutter/ayf_pack/lib/types/ayf_sequencer_types.dart new file mode 100644 index 00000000..3c0b10d6 --- /dev/null +++ b/flutter/ayf_pack/lib/types/ayf_sequencer_types.dart @@ -0,0 +1,140 @@ +import 'package:flutter/foundation.dart'; + +// ======================================================================= +enum jvxSequencerStatusEnum { + JVX_SEQUENCER_STATUS_NONE, + JVX_SEQUENCER_STATUS_STARTUP, + JVX_SEQUENCER_STATUS_IN_OPERATION, + JVX_SEQUENCER_STATUS_WAITING, + JVX_SEQUENCER_STATUS_WANTS_TO_STOP_PROCESS, + JVX_SEQUENCER_STATUS_WANTS_TO_STOP_SEQUENCE, + JVX_SEQUENCER_STATUS_SHUTDOWN_IN_PROGRESS, + JVX_SEQUENCER_STATUS_ERROR, + JVX_SEQUENCER_STATUS_SHUTDOWN_COMPLETE +} + +extension jvxSequencerStatusEEnum on jvxSequencerStatusEnum { + String get txt => describeEnum(this); + static jvxSequencerStatusEnum fromInt(int val) { + jvxSequencerStatusEnum ss = + jvxSequencerStatusEnum.JVX_SEQUENCER_STATUS_NONE; + if (val < jvxSequencerStatusEnum.values.length) { + ss = jvxSequencerStatusEnum.values[val]; + } + return ss; + } +} + +// ===================================================================== + +enum jvxSequencerQueueTypeEnum { + JVX_SEQUENCER_QUEUE_TYPE_NONE, + JVX_SEQUENCER_QUEUE_TYPE_RUN, + JVX_SEQUENCER_QUEUE_TYPE_LEAVE, + JVX_SEQUENCER_QUEUE_TYPE_LIMIT +} + +extension jvxSequencerQueueTypeEEnum on jvxSequencerQueueTypeEnum { + String get txt => describeEnum(this); + static jvxSequencerQueueTypeEnum fromInt(int val) { + jvxSequencerQueueTypeEnum ss = + jvxSequencerQueueTypeEnum.JVX_SEQUENCER_QUEUE_TYPE_NONE; + if (val < jvxSequencerQueueTypeEnum.values.length) { + ss = jvxSequencerQueueTypeEnum.values[val]; + } + return ss; + } +} + +enum jvxSequencerElementTypeEnum { + JVX_SEQUENCER_TYPE_COMMAND_NONE, + JVX_SEQUENCER_TYPE_COMMAND_COMPONENT_ACTIVATE, + JVX_SEQUENCER_TYPE_COMMAND_COMPONENT_PREPARE, + JVX_SEQUENCER_TYPE_COMMAND_COMPONENT_START, + JVX_SEQUENCER_TYPE_COMMAND_COMPONENT_STOP, + JVX_SEQUENCER_TYPE_COMMAND_COMPONENT_POSTPROCESS, + JVX_SEQUENCER_TYPE_COMMAND_COMPONENT_DEACTIVATE, + JVX_SEQUENCER_TYPE_CONDITION_WAIT, + JVX_SEQUENCER_TYPE_CONDITION_JUMP, + JVX_SEQUENCER_TYPE_JUMP, + JVX_SEQUENCER_TYPE_COMMAND_SPECIFIC, + JVX_SEQUENCER_TYPE_WAIT_FOREVER, + JVX_SEQUENCER_TYPE_WAIT_CONDITION_RELATIVE_JUMP, + JVX_SEQUENCER_TYPE_BREAK, + JVX_SEQUENCER_TYPE_REQUEST_UPDATE_VIEWER, + JVX_SEQUENCER_TYPE_REQUEST_START_IN_PROCESSING_LOOP, + JVX_SEQUENCER_TYPE_REQUEST_STOP_IN_PROCESSING_LOOP, + JVX_SEQUENCER_TYPE_CALLBACK, + JVX_SEQUENCER_TYPE_COMMAND_PROCESS_PREPARE, + JVX_SEQUENCER_TYPE_COMMAND_PROCESS_START, + JVX_SEQUENCER_TYPE_COMMAND_PROCESS_STOP, + JVX_SEQUENCER_TYPE_COMMAND_PROCESS_POSTPROCESS, + JVX_SEQUENCER_TYPE_COMMAND_OUTPUT_TEXT, + JVX_SEQUENCER_TYPE_COMMAND_CALL_SEQUENCE, + JVX_SEQUENCER_TYPE_COMMAND_SWITCH_STATE, + JVX_SEQUENCER_TYPE_COMMAND_RESET_STATE, + JVX_SEQUENCER_TYPE_COMMAND_PROCESS_TEST, + JVX_SEQUENCER_TYPE_COMMAND_INTERCEPT, + JVX_SEQUENCER_TYPE_COMMAND_NOP, + JVX_SEQUENCER_TYPE_COMMAND_SET_PROPERTY, + JVX_SEQUENCER_TYPE_COMMAND_INVOKE_ERROR, + JVX_SEQUENCER_TYPE_COMMAND_INVOKE_FATAL_ERROR, + JVX_SEQUENCER_TYPE_UNCONDITION_WAIT, + JVX_SEQUENCER_TYPE_WAIT_AND_RUN_TASKS +} + +extension jvxSequencerElementTypeEEnum on jvxSequencerElementTypeEnum { + String get txt => describeEnum(this); + static jvxSequencerElementTypeEnum fromInt(int val) { + jvxSequencerElementTypeEnum ss = + jvxSequencerElementTypeEnum.JVX_SEQUENCER_TYPE_COMMAND_NONE; + if (val < jvxSequencerElementTypeEnum.values.length) { + ss = jvxSequencerElementTypeEnum.values[val]; + } + return ss; + } +} + +// ============================================================ + +abstract class jvxSequencerEventBitfield { + static const int JVX_SEQUENCER_EVENT_NONE = 0; + static const int JVX_SEQUENCER_EVENT_PROCESS_STARTUP_COMPLETE = 0x1; + static const int JVX_SEQUENCER_EVENT_SEQUENCE_STARTUP_COMPLETE = 0x2; + static const int JVX_SEQUENCER_EVENT_SUCCESSFUL_COMPLETION_STEP = 0x4; + static const int JVX_SEQUENCER_EVENT_INCOMPLETE_COMPLETION_STEP_ERROR = 0x8; + static const int JVX_SEQUENCER_EVENT_SUCCESSFUL_COMPLETION_SEQUENCE = 0x10; + static const int JVX_SEQUENCER_EVENT_INCOMPLETE_COMPLETION_SEQUENCE_ERROR = + 0x20; + static const int JVX_SEQUENCER_EVENT_INCOMPLETE_COMPLETION_SEQUENCE_ABORT = + 0x40; + static const int JVX_SEQUENCER_EVENT_SUCCESSFUL_COMPLETION_PROCESS = 0x80; + static const int JVX_SEQUENCER_EVENT_INCOMPLETE_COMPLETION_PROCESS_ERROR = + 0x100; + static const int JVX_SEQUENCER_EVENT_INCOMPLETE_COMPLETION_PROCESS_ABORT = + 0x200; + static const int JVX_SEQUENCER_EVENT_PROCESS_TERMINATED = 0x400; + static const int JVX_SEQUENCER_EVENT_INFO_TEXT = 0x800; + static const int JVX_SEQUENCER_EVENT_DEBUG_MESSAGE = 0x1000; + static const int JVX_SEQUENCER_EVENT_REPORT_OPERATION_STATE = 0x2000; + static const int JVX_SEQUENCER_EVENT_REPORT_ENTERED_STEP_BREAK = 0x4000; + static const int JVX_SEQUENCER_EVENT_REPORT_SKIPPED_STEP = 0x8000; +} + +class jvxSequencerEventType { + int eventMask = jvxSequencerEventBitfield.JVX_SEQUENCER_EVENT_NONE; + String description = ''; + int sequenceId = -1; + int stepId = -1; + int fId = -1; + int seq_state_id = -1; + jvxSequencerStatusEnum statSeq = + jvxSequencerStatusEnum.JVX_SEQUENCER_STATUS_NONE; + + jvxSequencerQueueTypeEnum qtp = + jvxSequencerQueueTypeEnum.JVX_SEQUENCER_QUEUE_TYPE_NONE; + jvxSequencerElementTypeEnum setp = + jvxSequencerElementTypeEnum.JVX_SEQUENCER_TYPE_COMMAND_NONE; + + bool indicateFirstError = false; +} diff --git a/flutter/ayf_pack/lib/types/ayf_types.dart b/flutter/ayf_pack/lib/types/ayf_types.dart new file mode 100644 index 00000000..855a01de --- /dev/null +++ b/flutter/ayf_pack/lib/types/ayf_types.dart @@ -0,0 +1,61 @@ +import '../ayf_pack_local.dart'; + +enum AudYoFloCommandQueueElement { AYF_COMMAND_QUEUE_NONE } + +class AudYoFloOneComponentOptions { + String name = ''; + int stat = jvxState.JVX_STATE_NONE; + AudYoFloOneComponentOptions({required this.name, required this.stat}); +} + +class AudYoFloOneComponentSelectionOptions { + List compAvail = []; + + // The list of available components may depend on a "parent". + // In this case, the list of available components must be updated if the state changes. + JvxComponentIdentification cpIdDependsOn; + int ssUpdateId = 0; + + AudYoFloOneComponentSelectionOptions({required this.cpIdDependsOn}); +} + +class AudYoFloStringWithId { + String name = 'Unknown'; + int id = -1; + AudYoFloStringWithId(this.name, this.id); +} + +class AudYoFloException implements Exception { + int errCode = jvxErrorType.JVX_NO_ERROR; + + AudYoFloException(this.errCode, String reason) { + Exception(reason); + } +} + +// =========================================================== +// This is a little bit complicated: a call by reference does not work for +// simple datatypes and a return value in a template function can not be assigned +// (or I did not find a way how to assign it). Therefore we need to pass +// the value in a struct. This will be a call by reference here and there +class AudYoFloCallByReference { + T variable; + AudYoFloCallByReference(this.variable); + set value(T valArg) { + variable = valArg; + } +} + +// This class defines a callback that reports the selection (=clicked) of a +// specific component. Currently, it is used to select a device within the list of active +// devices +abstract class AudYoFloSingleSelectionComponent { + // End up in this callback in case a selection was chosen (=clicked) + void reportSelectionComponent(JvxComponentIdentification cpId); +} + +class AudYoFloCompileFlags { + int compileNum32BitBitfield = 0; + jvxDataTypeSpecEnum compileDataTypeSpec = + jvxDataTypeSpecEnum.JVX_DATA_TYPE_SPEC_DOUBLE; +} diff --git a/flutter/ayf_pack/lib/types/web/ayf_bitfield_simple.dart b/flutter/ayf_pack/lib/types/web/ayf_bitfield_simple.dart new file mode 100644 index 00000000..a8437974 --- /dev/null +++ b/flutter/ayf_pack/lib/types/web/ayf_bitfield_simple.dart @@ -0,0 +1,107 @@ +import 'dart:math'; +import 'dart:typed_data'; +import 'package:fixnum/fixnum.dart' as fn; +import '../../ayf_pack_local.dart'; + +class AudYoFloBitFieldSimple extends AudYoFloBitFieldBackend { + int num32BitBitfield = 0; + AudYoFloBitFieldSimple(); + + @override + void initialize(int num32BitBitfieldArg, {int numEntriesArg = 1}) { + num32BitBitfield = num32BitBitfieldArg; + num32BitAlltogether = numEntriesArg * num32BitBitfield; + numEntries = numEntriesArg; + bitfield = Uint32List(num32BitAlltogether); + } + + @override + bool bitTest(int posi, {int offset = 0}) { + int idxEntry = posi >> 5; + int inWordIdx = posi - (idxEntry << 5); + int inFldIdx = num32BitBitfield - 1 - idxEntry; + inFldIdx += offset * num32BitBitfield; + if ((bitfield![inFldIdx] & (1 << inWordIdx)) != 0) { + return true; + } + return false; + } + + @override + int firstSet(int posiMax, {int offset = 0}) { + int firstSetPosi = -1; + posiMax = min(posiMax, num32BitBitfield); + for (int posi = 0; posi < posiMax; posi++) { + int idxEntry = posi >> 5; + int inWordIdx = posi - (idxEntry << 5); + int inFldIdx = num32BitBitfield - 1 - idxEntry; + inFldIdx += offset * num32BitBitfield; + if ((bitfield![inFldIdx] & (1 << inWordIdx)) != 0) { + firstSetPosi = posi; + break; + } + } + return firstSetPosi; + } + + @override + void bitSet(int posi, {int offset = 0}) { + int idxEntry = posi >> 5; + int inWordIdx = posi - (idxEntry << 5); + int inFldIdx = num32BitBitfield - 1 - idxEntry; + inFldIdx += offset * num32BitBitfield; + bitfield![inFldIdx] |= (1 << inWordIdx); + } + + @override + void bitZSet(int posi, {int offset = 0}) { + int idxEntry = posi >> 5; + int inWordIdx = posi - (idxEntry << 5); + int inFldIdx = num32BitBitfield - 1 - idxEntry; + inFldIdx += offset * num32BitBitfield; + bitfield![inFldIdx] = fn.Int32((1 << inWordIdx)) as int; + } + + @override + void bitClear(int posi, {int offset = 0}) { + int idxEntry = posi >> 5; + int inWordIdx = posi - (idxEntry << 5); + int inFldIdx = num32BitBitfield - 1 - idxEntry; + inFldIdx += offset * num32BitBitfield; + bitfield![inFldIdx] &= ~(1 << inWordIdx); + } + + @override + void bitFClear({int offset = 0}) { + for (int idx = 0; idx < num32BitBitfield; idx++) { + int inFldIdx = num32BitBitfield - 1 - idx; + inFldIdx += offset * num32BitBitfield; + bitfield![inFldIdx] = fn.Int32(0) as int; + } + } + + @override + void copy(AudYoFloBitField copyFrom, {int offset = 0}) { + assert(offset < numEntries); + if (copyFrom is AudYoFloBitFieldSimple) { + assert(offset < copyFrom.numEntries); + for (int idx = 0; idx < num32BitBitfield; idx++) { + int inFldIdx = num32BitBitfield - 1 - idx; + inFldIdx += offset * num32BitBitfield; + bitfield![inFldIdx] = copyFrom.bitfield![inFldIdx]; + } + } + } + + @override + void dispose() { + num32BitBitfield = 0; + numEntries = 0; + num32BitAlltogether = 0; + bitfield?.clear(); + } +} + +AudYoFloBitField allocateAudYoFloBitfield() { + return AudYoFloBitFieldSimple(); +} diff --git a/flutter/ayf_pack/pubspec.yaml b/flutter/ayf_pack/pubspec.yaml new file mode 100644 index 00000000..a3359b7c --- /dev/null +++ b/flutter/ayf_pack/pubspec.yaml @@ -0,0 +1,113 @@ +name: ayf_pack +description: A new Flutter plugin project. +version: 0.0.1 +homepage: + +environment: + sdk: ">=2.18.0 <3.0.0" + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + flutter_web_plugins: + sdk: flutter + plugin_platform_interface: ^2.0.2 + + another_xlider: ^1.1.0 + provider: ^6.0.3 + collection: ^1.16.0 + fixnum: ^1.0.0 + + intl: ^0.17.0 + + clipboard: ^0.1.3 + + flutter_dotenv: ^5.0.2 + + ayf_pack_native: + path: ../ayf_pack_native + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + # This section identifies this Flutter project as a plugin project. + # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) + # which should be registered in the plugin registry. This is required for + # using method channels. + # The Android 'package' specifies package in which the registered class is. + # This is required for using method channels on Android. + # The 'ffiPlugin' specifies that native code should be built and bundled. + # This is required for using `dart:ffi`. + # All these are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + platforms: + windows: + pluginClass: AyfPackPluginCApi + web: + pluginClass: AyfPackWeb + fileName: ayf_pack_web.dart + + assets: + - images/ayf_icons/ayf-mixer.png + - images/ayf_icons/ayf-stream.png + - images/ayf_icons/ayf-audio-settings.png + - images/ayf_icons/ayf-headtracker-settings.png + - images/ayf_icons/ayf-sip.png + - images/ayf_icons/ayf-webrtc.png + - images/ayf_icons/ayf-hdrive.png + - images/ayf_icons/ayf-remote.png + - images/ayf_icons/ayf-brender.png + - images/ayf_icons/ayf-plot.png + - images/ayf_icons/ayf-dbg.png + - images/ayf_icons/ayf-comp.png + - images/ayf_icons/ayf-micsrc.png + - images/ayf_icons/ayf-soundio.png + - images/ayf_icons/ayf-soundo.png + - images/ayf_icons/ayf-soundi.png + - images/ayf_icons/ayf-file-input.png + - images/ayf_icons/ayf-headtracker.png + + # To add assets to your plugin package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your plugin package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages + +#analyzer: +# strong-mode: true +# language: +# enableSuperMixins: true \ No newline at end of file diff --git a/flutter/ayf_pack/test/ayf_pack_method_channel_test.dart b/flutter/ayf_pack/test/ayf_pack_method_channel_test.dart new file mode 100644 index 00000000..66d63714 --- /dev/null +++ b/flutter/ayf_pack/test/ayf_pack_method_channel_test.dart @@ -0,0 +1,24 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:ayf_pack/ayf_pack_method_channel.dart'; + +void main() { + MethodChannelAyfPack platform = MethodChannelAyfPack(); + const MethodChannel channel = MethodChannel('ayf_pack'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + return '42'; + }); + }); + + tearDown(() { + channel.setMockMethodCallHandler(null); + }); + + test('getPlatformVersion', () async { + expect(await platform.getPlatformVersion(), '42'); + }); +} diff --git a/flutter/ayf_pack/test/ayf_pack_test.dart b/flutter/ayf_pack/test/ayf_pack_test.dart new file mode 100644 index 00000000..46894d5e --- /dev/null +++ b/flutter/ayf_pack/test/ayf_pack_test.dart @@ -0,0 +1,28 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:ayf_pack/ayf_pack_local.dart'; +import 'package:ayf_pack/ayf_pack_platform_interface.dart'; +import 'package:ayf_pack/ayf_pack_method_channel.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +class MockAyfPackPlatform + with MockPlatformInterfaceMixin + implements AyfPackPlatform { + @override + Future getPlatformVersion() => Future.value('42'); +} + +void main() { + final AyfPackPlatform initialPlatform = AyfPackPlatform.instance; + + test('$MethodChannelAyfPack is the default instance', () { + expect(initialPlatform, isInstanceOf()); + }); + + test('getPlatformVersion', () async { + AyfPack ayfPackPlugin = AyfPack(); + MockAyfPackPlatform fakePlatform = MockAyfPackPlatform(); + AyfPackPlatform.instance = fakePlatform; + + expect(await ayfPackPlugin.getPlatformVersion(), '42'); + }); +} diff --git a/flutter/ayf_pack/windows/.gitignore b/flutter/ayf_pack/windows/.gitignore new file mode 100644 index 00000000..b3eb2be1 --- /dev/null +++ b/flutter/ayf_pack/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/flutter/ayf_pack/windows/CMakeLists.txt b/flutter/ayf_pack/windows/CMakeLists.txt new file mode 100644 index 00000000..2e17698b --- /dev/null +++ b/flutter/ayf_pack/windows/CMakeLists.txt @@ -0,0 +1,53 @@ +# The Flutter tooling requires that developers have a version of Visual Studio +# installed that includes CMake 3.14 or later. You should not increase this +# version, as doing so will cause the plugin to fail to compile for some +# customers of the plugin. +cmake_minimum_required(VERSION 3.14) + +# Project-level configuration. +set(PROJECT_NAME "ayf_pack") +project(${PROJECT_NAME} LANGUAGES CXX) + +# This value is used when generating builds using this plugin, so it must +# not be changed +set(PLUGIN_NAME "ayf_pack_plugin") + +# Any new source files that you add to the plugin should be added here. +list(APPEND PLUGIN_SOURCES + "ayf_pack_plugin.cpp" + "ayf_pack_plugin.h" +) + +# Define the plugin library target. Its name must not be changed (see comment +# on PLUGIN_NAME above). +add_library(${PLUGIN_NAME} SHARED + "include/ayf_pack/ayf_pack_plugin_c_api.h" + "ayf_pack_plugin_c_api.cpp" + ${PLUGIN_SOURCES} +) + +# Apply a standard set of build settings that are configured in the +# application-level CMakeLists.txt. This can be removed for plugins that want +# full control over build settings. +apply_standard_settings(${PLUGIN_NAME}) + +# Symbols are hidden by default to reduce the chance of accidental conflicts +# between plugins. This should not be removed; any symbols that should be +# exported should be explicitly exported with the FLUTTER_PLUGIN_EXPORT macro. +set_target_properties(${PLUGIN_NAME} PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) + +# Source include directories and library dependencies. Add any plugin-specific +# dependencies here. +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. +# This list could contain prebuilt libraries, or libraries created by an +# external build triggered from this build file. +set(ayf_pack_bundled_libraries + "" + PARENT_SCOPE +) diff --git a/flutter/ayf_pack/windows/ayf_pack_plugin.cpp b/flutter/ayf_pack/windows/ayf_pack_plugin.cpp new file mode 100644 index 00000000..41e99dec --- /dev/null +++ b/flutter/ayf_pack/windows/ayf_pack_plugin.cpp @@ -0,0 +1,59 @@ +#include "ayf_pack_plugin.h" + +// This must be included before many other Windows headers. +#include + +// For getPlatformVersion; remove unless needed for your plugin implementation. +#include + +#include +#include +#include + +#include +#include + +namespace ayf_pack { + +// static +void AyfPackPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarWindows *registrar) { + auto channel = + std::make_unique>( + registrar->messenger(), "ayf_pack", + &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)); +} + +AyfPackPlugin::AyfPackPlugin() {} + +AyfPackPlugin::~AyfPackPlugin() {} + +void AyfPackPlugin::HandleMethodCall( + const flutter::MethodCall &method_call, + std::unique_ptr> result) { + if (method_call.method_name().compare("getPlatformVersion") == 0) { + std::ostringstream version_stream; + version_stream << "Windows "; + if (IsWindows10OrGreater()) { + version_stream << "10+"; + } else if (IsWindows8OrGreater()) { + version_stream << "8"; + } else if (IsWindows7OrGreater()) { + version_stream << "7"; + } + result->Success(flutter::EncodableValue(version_stream.str())); + } else { + result->NotImplemented(); + } +} + +} // namespace ayf_pack diff --git a/flutter/ayf_pack/windows/ayf_pack_plugin.h b/flutter/ayf_pack/windows/ayf_pack_plugin.h new file mode 100644 index 00000000..b787b49a --- /dev/null +++ b/flutter/ayf_pack/windows/ayf_pack_plugin.h @@ -0,0 +1,32 @@ +#ifndef FLUTTER_PLUGIN_AYF_PACK_PLUGIN_H_ +#define FLUTTER_PLUGIN_AYF_PACK_PLUGIN_H_ + +#include +#include + +#include + +namespace ayf_pack { + +class AyfPackPlugin : public flutter::Plugin { + public: + static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar); + + AyfPackPlugin(); + + virtual ~AyfPackPlugin(); + + // Disallow copy and assign. + AyfPackPlugin(const AyfPackPlugin&) = delete; + AyfPackPlugin& operator=(const AyfPackPlugin&) = delete; + + 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); +}; + +} // namespace ayf_pack + +#endif // FLUTTER_PLUGIN_AYF_PACK_PLUGIN_H_ diff --git a/flutter/ayf_pack/windows/ayf_pack_plugin_c_api.cpp b/flutter/ayf_pack/windows/ayf_pack_plugin_c_api.cpp new file mode 100644 index 00000000..e5ac9b68 --- /dev/null +++ b/flutter/ayf_pack/windows/ayf_pack_plugin_c_api.cpp @@ -0,0 +1,12 @@ +#include "include/ayf_pack/ayf_pack_plugin_c_api.h" + +#include + +#include "ayf_pack_plugin.h" + +void AyfPackPluginCApiRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + ayf_pack::AyfPackPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarManager::GetInstance() + ->GetRegistrar(registrar)); +} diff --git a/flutter/ayf_pack/windows/include/ayf_pack/ayf_pack_plugin_c_api.h b/flutter/ayf_pack/windows/include/ayf_pack/ayf_pack_plugin_c_api.h new file mode 100644 index 00000000..8852c284 --- /dev/null +++ b/flutter/ayf_pack/windows/include/ayf_pack/ayf_pack_plugin_c_api.h @@ -0,0 +1,23 @@ +#ifndef FLUTTER_PLUGIN_AYF_PACK_PLUGIN_C_API_H_ +#define FLUTTER_PLUGIN_AYF_PACK_PLUGIN_C_API_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 AyfPackPluginCApiRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // FLUTTER_PLUGIN_AYF_PACK_PLUGIN_C_API_H_ diff --git a/flutter/ayf_pack_native/.gitignore b/flutter/ayf_pack_native/.gitignore new file mode 100644 index 00000000..24476c5d --- /dev/null +++ b/flutter/ayf_pack_native/.gitignore @@ -0,0 +1,44 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/flutter/ayf_pack_native/.jvxprj.flutter b/flutter/ayf_pack_native/.jvxprj.flutter new file mode 100644 index 00000000..e69de29b diff --git a/flutter/ayf_pack_native/README.md b/flutter/ayf_pack_native/README.md new file mode 100644 index 00000000..bb803c70 --- /dev/null +++ b/flutter/ayf_pack_native/README.md @@ -0,0 +1,16 @@ +# ayf_pack_native + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/flutter/ayf_pack_native/lib/ayf_pack_native.dart b/flutter/ayf_pack_native/lib/ayf_pack_native.dart new file mode 100644 index 00000000..8f4e2ddc --- /dev/null +++ b/flutter/ayf_pack_native/lib/ayf_pack_native.dart @@ -0,0 +1,8 @@ +library ayf_pack_native; + +export 'properties/ayf_properties_from_json_native.dart'; +export 'properties/ayf_properties.dart'; +export 'types/ayf_bitfield.dart'; +export 'helpers/ayf_helper_lib.dart'; +export 'platform/ayf_platform_specific.dart'; +export 'sub-widgets/file-input-devices/ayf_file_input_core_widget.dart'; diff --git a/flutter/ayf_pack_native/lib/helpers/ayf_helper_lib.dart b/flutter/ayf_pack_native/lib/helpers/ayf_helper_lib.dart new file mode 100644 index 00000000..adffb539 --- /dev/null +++ b/flutter/ayf_pack_native/lib/helpers/ayf_helper_lib.dart @@ -0,0 +1,76 @@ +import 'dart:ffi'; +import 'dart:math'; + +import 'package:ffi/ffi.dart'; +import 'dart:typed_data'; +import 'package:collection/collection.dart'; + +//import 'package:ayf_pack/ayf_pack.dart'; + +import 'package:ayf_pack/ayf_pack.dart'; + +void printFormattedTextMessage(int id2, Pointer load_fld) { + Pointer cstr = load_fld.cast(); + String txt = cstr.toDartString(); + String typeMess = ""; + switch (id2) { + case jvxReportPriority.JVX_REPORT_PRIORITY_DEBUG: + typeMess = "DEBUG: "; + break; + case jvxReportPriority.JVX_REPORT_PRIORITY_ERROR: + typeMess = "ERROR: "; + break; + case jvxReportPriority.JVX_REPORT_PRIORITY_INFO: + typeMess = "INFO: "; + break; + case jvxReportPriority.JVX_REPORT_PRIORITY_SUCCESS: + typeMess = "SUCCESS: "; + break; + case jvxReportPriority.JVX_REPORT_PRIORITY_VERBOSE: + typeMess = "VERBOSE: "; + break; + case jvxReportPriority.JVX_REPORT_PRIORITY_WARNING: + typeMess = "WARNING: "; + break; + } + print("${typeMess}${txt}"); +} + +// This function checks if a cahced device list is in memory, otherwise it refreshes the +// list and returns it afterwards +Future?> checkAndUpdateDeviceOptions( + AudYoFloBackendCache be, JvxComponentIdentification tp) async { + List? lst; + + // Two attempts + for (int idx = 0; idx < 2; idx++) { + // This call checks that the technology has been registered. + // If you are not allowed to call this function the call throws + // an exeption with the error reason + try { + lst = be.referenceDeviceListInCache(tp); + } on AudYoFloException catch (e) { + // This exception may occur if the technology has been unselected in the + // backend but the info about this has not yet been forwarded to the frontend. + // In this case the list of devices is returned as empty. + String txt = jvxErrorTypeEInt.toStringSingle(e.errCode); + String cp = tp.txt; + print( + 'Error reported when trying to get reference to the devices list for technology $cp: <$txt>'); + + // Return an empty list + return lst; + } + + // the next call may be used without check safely + if (lst == null) { + int errCode = await be.triggerUpdateDeviceList(tp); + if (errCode != jvxErrorType.JVX_NO_ERROR) { + String errTxt = jvxErrorTypeEInt.toStringSingle(errCode); + print('Could not find any devices, error reason: <$errTxt>.'); + } + } + } + + return lst; +} diff --git a/flutter/ayf_pack_native/lib/platform/ayf_platform_specific.dart b/flutter/ayf_pack_native/lib/platform/ayf_platform_specific.dart new file mode 100644 index 00000000..e1a255b9 --- /dev/null +++ b/flutter/ayf_pack_native/lib/platform/ayf_platform_specific.dart @@ -0,0 +1,248 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:path/path.dart' as path; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'dart:io' show Platform; +import 'package:flutter_window_close/flutter_window_close.dart'; +import 'package:ayf_pack/ayf_pack.dart'; +import 'package:collection/collection.dart'; + +class AudYoFloPlatformSpecificHtmlNat extends AudYoFloPlatformSpecific { + AyfHost? theBeAdapter; + @override + AyfHost? referenceHost() { + return theBeAdapter; + } + + @override + void prepareClose(BuildContext context) { + /* + * This installs a handler for the "close" event. + */ + + AudYoFloBackendCache theBeCache = + Provider.of(context, listen: false); + + FlutterWindowClose.setWindowShouldCloseHandler(() async { + bool wantClose = false; + await showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('Do you really want to quit?'), + actions: [ + ElevatedButton( + onPressed: () { + Navigator.of(context).pop(false); + wantClose = true; + }, + child: const Text('Yes')), + ElevatedButton( + onPressed: () => Navigator.of(context).pop(false), + child: const Text('No')), + ]); + }); + if (wantClose) { + await theBeCache.triggerClose(); + } + return wantClose; + //return wantToQuit; + }); + } + + // Allocate and return file drop widget + @override + Widget? allocateFileDropWidget(JvxComponentIdentification identT, + String textShowDrag, double sizeIcon) => + AudYoFloFileInputCoreWidgetNative(identT, textShowDrag, sizeIcon); + + @override + void configureSubSystem(Map cfg) { + var elm = cfg.entries.firstWhereOrNull((element) => element.key == 'host'); + if (elm != null) { + if (elm.value is AyfHost) { + theBeAdapter = elm.value; + } + } + + assert(theBeAdapter != null); + } + + @override + Future initializeSubSystem(AudYoFloBackendCache? notifierBeCache, + AudYoFloUiModel? uiModel, AudYoFloDebugModel? notifierDbgModel) async { + return true; + } + + @override + String get lastError { + if (theBeAdapter != null) { + return theBeAdapter!.lastError; + } + return ''; + } +} + +class AudYoFloPlatformSpecificNative extends AudYoFloPlatformSpecificHtmlNat { + AyfCorePackIf? theCorePack; + bool nativeHostConnect = false; + + @override + void configureSubSystem(Map cfg) { + super.configureSubSystem(cfg); + + String descr = theBeAdapter!.descriptionHost(); + MapEntry? elm; + + if (descr == "AyfNativeAudioHost") { + nativeHostConnect = true; + + elm = + cfg.entries.firstWhereOrNull((element) => element.key == 'corePack'); + if (elm != null) { + if (elm.value is AyfCorePackIf) { + theCorePack = elm.value; + } + } + + // We need the core pack here!! + assert(theCorePack != null); + } + elm = cfg.entries.firstWhereOrNull((element) => element.key == 'cmdArgs'); + if (elm != null) { + if (elm.value is List) { + cmdArgs = elm.value; + } + } + + // cmdArgs miht also be empty + } + + @override + Future initializeSubSystem(AudYoFloBackendCache? notifierBeCache, + AudYoFloUiModel? uiModel, AudYoFloDebugModel? notifierDbgModel) async { + bool retVal = true; + if (nativeHostConnect) { + if ((theCorePack != null) && + (notifierBeCache != null) && + (notifierDbgModel != null) && + (theBeAdapter != null)) { + // Get the cross link references to the corepack dll + final entryPoints = await theCorePack!.getEntryPoints(); + + // This will delay the initialization to happen AFTER every state has been created + //Future.delayed(Duration(milliseconds: 1), () { + String runExec = Platform.resolvedExecutable; + String sepString = Platform.pathSeparator; + List tokens = runExec.split(sepString); + String appNameExe = tokens.last; + tokens = appNameExe.split('.'); + String appNameToken = tokens[0].toLowerCase(); + String hostConfigPath = ''; + String hostConfigSymbol = ''; + bool loadEnvSuccess = true; + + int hostConfigAddr = 0; + + String envFileName = "." + appNameToken + ".env"; + + int executableInBinFolder = 0; + String executableInBinFolderStr = "no"; + + // Open the dynamic library + var libraryPath = path.join("library-not-found"); + // Prepare the dynamic libraries + // Read in the + try { + await dotenv.load(fileName: envFileName); + } on Exception { + loadEnvSuccess = false; + } + + if (loadEnvSuccess) { + libraryPath = dotenv.get('AYF_BACKEND_FFI_SHARED_LIBRARY', + fallback: libraryPath); + } + + if (entryPoints is Map) { + Map entryPointsMap = entryPoints; + + // Extract hostConfig module path + hostConfigPath = entryPointsMap.entries.firstWhereOrNull((element) { + if (element.key is String) { + return (element.key == 'loadedModule'); + } + return false; + })?.value; + + // Extract hostConfig module path + hostConfigSymbol = entryPointsMap.entries.firstWhereOrNull((element) { + if (element.key is String) { + return (element.key == 'moduleEntrySymbol'); + } + return false; + })?.value; + + hostConfigAddr = entryPointsMap.entries.firstWhereOrNull((element) { + if (element.key is String) { + return (element.key == 'moduleEntryAddress'); + } + return false; + })?.value; + } + + if (cmdArgs != null) { + List cmdArgsAdd = List.from(cmdArgs!); + + // Check for host config dll + if (hostConfigPath.isNotEmpty) { + cmdArgsAdd.add('--natconfm'); + cmdArgsAdd.add(hostConfigPath); + } + + if (hostConfigSymbol.isNotEmpty) { + cmdArgsAdd.add('--natconfs'); + cmdArgsAdd.add(hostConfigSymbol); + } + + if (loadEnvSuccess) { + executableInBinFolderStr = dotenv.get( + 'AYF_BACKEND_EXECUTABLE_IN_BINFOLDER', + fallback: executableInBinFolderStr); + if (executableInBinFolderStr == "yes") { + executableInBinFolder = 1; + } + } + if (theBeAdapter != null) { + Map args = {}; + args['cmdArgs'] = cmdArgsAdd; + args['libPath'] = libraryPath; + args['exeInBin'] = executableInBinFolder; + + retVal = await theBeAdapter! + .initialize(notifierBeCache, notifierDbgModel, args); + + uiModel!.configureSystemParameters(theBeAdapter!.compFlags); + } + } + } + } else { + if (notifierBeCache != null) { + Map args = {}; + args['cmdArgs'] = cmdArgs; + retVal = await theBeAdapter! + .initialize(notifierBeCache, notifierDbgModel, args); + if (retVal == false) { + // theBeAdapter!. + } + } + // HTML connect + } + + return retVal; + } +} + +AudYoFloPlatformSpecific allocatePlatformSpecific() { + return AudYoFloPlatformSpecificNative(); +} diff --git a/flutter/ayf_pack_native/lib/properties/ayf_properties.dart b/flutter/ayf_pack_native/lib/properties/ayf_properties.dart new file mode 100644 index 00000000..612fcfc8 --- /dev/null +++ b/flutter/ayf_pack_native/lib/properties/ayf_properties.dart @@ -0,0 +1,425 @@ +import 'package:ffi/ffi.dart'; +import 'dart:ffi'; +import 'dart:typed_data'; +import 'package:fixnum/fixnum.dart' as fn; +import 'dart:math'; + +import '../ayf_pack_native.dart'; +import 'package:ayf_pack/ayf_pack.dart'; + +AudYoFloPropertyContentBackend allocatePropertyContent( + JvxComponentIdentification cpId, + jvxDataFormatEnum form, + int num, + AudYoFloCompileFlags flags, + int numDigits) { + switch (form) { + case jvxDataFormatEnum.JVX_DATAFORMAT_DATA: + if (flags.compileDataTypeSpec == + jvxDataTypeSpecEnum.JVX_DATA_TYPE_SPEC_DOUBLE) { + return AudYoFloPropertyMultiContentNative( + cpId, num, form, numDigits); + } + return AudYoFloPropertyMultiContentNative( + cpId, num, form, numDigits); + + case jvxDataFormatEnum.JVX_DATAFORMAT_16BIT_LE: + return AudYoFloPropertyMultiContentNative( + cpId, num, form, numDigits); + case jvxDataFormatEnum.JVX_DATAFORMAT_8BIT: + return AudYoFloPropertyMultiContentNative( + cpId, num, form, numDigits); + case jvxDataFormatEnum.JVX_DATAFORMAT_32BIT_LE: + return AudYoFloPropertyMultiContentNative( + cpId, num, form, numDigits); + case jvxDataFormatEnum.JVX_DATAFORMAT_64BIT_LE: + return AudYoFloPropertyMultiContentNative( + cpId, num, form, numDigits); + case jvxDataFormatEnum.JVX_DATAFORMAT_U16BIT_LE: + return AudYoFloPropertyMultiContentNative( + cpId, num, form, numDigits); + case jvxDataFormatEnum.JVX_DATAFORMAT_U8BIT: + return AudYoFloPropertyMultiContentNative( + cpId, num, form, numDigits); + case jvxDataFormatEnum.JVX_DATAFORMAT_U32BIT_LE: + return AudYoFloPropertyMultiContentNative( + cpId, num, form, numDigits); + case jvxDataFormatEnum.JVX_DATAFORMAT_U64BIT_LE: + return AudYoFloPropertyMultiContentNative( + cpId, num, form, numDigits); + case jvxDataFormatEnum.JVX_DATAFORMAT_SIZE: + return AudYoFloPropertyMultiContentNative( + cpId, num, form, numDigits); + case jvxDataFormatEnum.JVX_DATAFORMAT_STRING_LIST: + return AudYoFloPropertyMultiStringBackend(cpId); + case jvxDataFormatEnum.JVX_DATAFORMAT_STRING: + return AudYoFloPropertySingleStringBackend(cpId); + case jvxDataFormatEnum.JVX_DATAFORMAT_SELECTION_LIST: + return AudYoFloPropertySelectionListNative( + cpId, flags.compileNum32BitBitfield, num); + case jvxDataFormatEnum.JVX_DATAFORMAT_VALUE_IN_RANGE: + if (flags.compileDataTypeSpec == + jvxDataTypeSpecEnum.JVX_DATA_TYPE_SPEC_DOUBLE) { + return AudYoFloPropertyValueInRangeNative( + cpId, num, numDigits); + } + return AudYoFloPropertyValueInRangeNative(cpId, num, numDigits); + + //return AudYoFloPropertyVal(cpId, flags.compileNum32BitBitfield); + + default: + break; + } + + return AudYoFloPropertyContentBackend(cpId); +} + +// ==================================================================== +// ==================================================================== + +// Class to hold a vector/array property, template to use one code for all types +// but remain type stable +class AudYoFloPropertyMultiContentNative + extends AudYoFloPropertyContentBackend + with AudYoFloPropertyMultiContentBackend { + jvxDataFormatEnum fldFrmt = jvxDataFormatEnum.JVX_DATAFORMAT_NONE; + Pointer fldPtr = nullptr; + int szNativeElement = 0; + + AudYoFloPropertyContentBackend get parpropc => this; + AudYoFloPropertyMultiContentNative( + JvxComponentIdentification cpId, int sz, jvxDataFormatEnum formatArg, int numDigShow ) + : super(cpId, format: formatArg, num: sz) { + + numDigits = numDigShow; + // Set the default format + if (formatArg != jvxDataFormatEnum.JVX_DATAFORMAT_NONE) { + fldFrmt = formatArg; + } + + // In the rebuild function the fldFrmt is set according to the "real" format, not the + // "jvxrt" format. + rebuild(sz); + } + + @override + String toHelperString() { + String txt = fldSz.toString() + ' entries of type ' + jvxFormat.toString(); + return txt; + } + + @override + String toString() { + String retVal = ''; + if (fld is Float32List) { + Float32List ptr = fld as Float32List; + for (int idx = 0; idx < fldSz; idx++) { + if (retVal.isNotEmpty) { + retVal += ', '; + } + retVal += ptr.elementAt(idx).toStringAsFixed(numDigits); + } + } + if (fld is Float64List) { + Float64List ptr = fld as Float64List; + for (int idx = 0; idx < fldSz; idx++) { + if (retVal.isNotEmpty) { + retVal += ', '; + } + retVal += ptr.elementAt(idx).toStringAsFixed(numDigits); + } + } + // ================================================================ + if (fld is Int8List) { + Int8List ptr = fld as Int8List; + for (int idx = 0; idx < fldSz; idx++) { + if (retVal.isNotEmpty) { + retVal += ', '; + } + retVal += ptr.elementAt(idx).toString(); + } + } + if (fld is Int16List) { + Int16List ptr = fld as Int16List; + for (int idx = 0; idx < fldSz; idx++) { + if (retVal.isNotEmpty) { + retVal += ', '; + } + retVal += ptr.elementAt(idx).toString(); + } + } + if (fld is Int32List) { + Int32List ptr = fld as Int32List; + for (int idx = 0; idx < fldSz; idx++) { + if (retVal.isNotEmpty) { + retVal += ', '; + } + retVal += ptr.elementAt(idx).toString(); + } + } + if (fld is Int64List) { + Int64List ptr = fld as Int64List; + for (int idx = 0; idx < fldSz; idx++) { + if (retVal.isNotEmpty) { + retVal += ', '; + } + retVal += ptr.elementAt(idx).toString(); + } + } + // ---------------------------------------- + if (fld is Uint8List) { + Uint8List ptr = fld as Uint8List; + for (int idx = 0; idx < fldSz; idx++) { + if (retVal.isNotEmpty) { + retVal += ', '; + } + retVal += ptr.elementAt(idx).toString(); + } + } + if (fld is Uint16List) { + Uint16List ptr = fld as Uint16List; + + for (int idx = 0; idx < fldSz; idx++) { + if (retVal.isNotEmpty) { + retVal += ', '; + } + retVal += ptr.elementAt(idx).toString(); + } + } + if (fld is Uint32List) { + Uint32List ptr = fld as Uint32List; + for (int idx = 0; idx < fldSz; idx++) { + if (retVal.isNotEmpty) { + retVal += ', '; + } + retVal += ptr.elementAt(idx).toString(); + } + } else if (fld is Uint64List) { + Uint64List ptr = fld as Uint64List; + for (int idx = 0; idx < fldSz; idx++) { + if (retVal.isNotEmpty) { + retVal += ', '; + } + retVal += ptr.elementAt(idx).toString(); + } + } + retVal = '[' + retVal + ']'; + return retVal; + } + + void rebuild(int newSize) { + if (fldSz != newSize) { + if (fldSz != 0) { + dispose(); + } + } + + if (newSize != 0) { + recreateCore(newSize); + } + } + + // Destructor. MUST BE CALLED MANUALLY ANYWAY, flutter does not + // have destructors due to comaptibility with javascript, + // https://github.com/dart-lang/sdk/issues/3691 + @override + void dispose() { + if (fldPtr != nullptr) { + calloc.free(fldPtr); + } + fldSz = 0; + super.num = 0; + fldPtr = nullptr; + super.dispose(); + } + + void recreateCore(int newSize) { + fldSz = newSize; + super.num = newSize; + if (T1 == Double) { + Pointer fldPtrLoc = calloc(fldSz).cast(); + fldPtr = fldPtrLoc as Pointer; + fld = fldPtrLoc.asTypedList(fldSz) as T2; + fldFrmt = jvxDataFormatEnum.JVX_DATAFORMAT_DOUBLE; + szNativeElement = sizeOf(); + } else if (T1 == Float) { + Pointer fldPtrLoc = calloc(fldSz).cast(); + fldPtr = fldPtrLoc as Pointer; + fld = fldPtrLoc.asTypedList(fldSz) as T2; + fldFrmt = jvxDataFormatEnum.JVX_DATAFORMAT_FLOAT; + szNativeElement = sizeOf(); + } else if (T1 == Int64) { + Pointer fldPtrLoc = calloc(fldSz).cast(); + fldPtr = fldPtrLoc as Pointer; + fld = fldPtrLoc.asTypedList(fldSz) as T2; + fldFrmt = jvxDataFormatEnum.JVX_DATAFORMAT_64BIT_LE; + szNativeElement = sizeOf(); + } else if (T1 == Int32) { + Pointer fldPtrLoc = calloc(fldSz).cast(); + fldPtr = fldPtrLoc as Pointer; + fld = fldPtrLoc.asTypedList(fldSz) as T2; + fldFrmt = jvxDataFormatEnum.JVX_DATAFORMAT_32BIT_LE; + szNativeElement = sizeOf(); + } else if (T1 == Int16) { + Pointer fldPtrLoc = calloc(fldSz).cast(); + fldPtr = fldPtrLoc as Pointer; + fld = fldPtrLoc.asTypedList(fldSz) as T2; + fldFrmt = jvxDataFormatEnum.JVX_DATAFORMAT_16BIT_LE; + szNativeElement = sizeOf(); + } else if (T1 == Int8) { + Pointer fldPtrLoc = calloc(fldSz).cast(); + fldPtr = fldPtrLoc as Pointer; + fld = fldPtrLoc.asTypedList(fldSz) as T2; + fldFrmt = jvxDataFormatEnum.JVX_DATAFORMAT_8BIT; + szNativeElement = sizeOf(); + } else if (T1 == Uint64) { + Pointer fldPtrLoc = calloc(fldSz).cast(); + fldPtr = fldPtrLoc as Pointer; + fld = fldPtrLoc.asTypedList(fldSz) as T2; + fldFrmt = jvxDataFormatEnum.JVX_DATAFORMAT_U64BIT_LE; + szNativeElement = sizeOf(); + } else if (T1 == Uint32) { + Pointer fldPtrLoc = calloc(fldSz).cast(); + fldPtr = fldPtrLoc as Pointer; + fld = fldPtrLoc.asTypedList(fldSz) as T2; + fldFrmt = jvxDataFormatEnum.JVX_DATAFORMAT_U32BIT_LE; + szNativeElement = sizeOf(); + } else if (T1 == Uint16) { + Pointer fldPtrLoc = calloc(fldSz).cast(); + fldPtr = fldPtrLoc as Pointer; + fld = fldPtrLoc.asTypedList(fldSz) as T2; + fldFrmt = jvxDataFormatEnum.JVX_DATAFORMAT_U16BIT_LE; + szNativeElement = sizeOf(); + } else if (T1 == Uint8) { + Pointer fldPtrLoc = calloc(fldSz).cast(); + fldPtr = fldPtrLoc as Pointer; + fld = fldPtrLoc.asTypedList(fldSz) as T2; + fldFrmt = jvxDataFormatEnum.JVX_DATAFORMAT_U8BIT; + szNativeElement = sizeOf(); + } else { + assert(false); + } + } +} + +// ================================================================ +// ================================================================ + +// Class to hold a selection list which is string list + selection entries +class AudYoFloPropertySelectionListNative + extends AudYoFloPropertyMultiStringBackend + with AudYoFloPropertySelectionListBackend { + @override + String toHelperString() { + if (selection is AudYoFloBitFieldNative) { + AudYoFloBitFieldNative sel = selection as AudYoFloBitFieldNative; + return sel.numEntries.toString() + + ' entries among options ' + + super.toHelperString(); + } else { + return ' this; + + // Show selection of ALL options. Typically, there is only one but there + // may be more than just one. The selected options are spaced by ",", + // the options are spaced by ";" + @override + String toString() { + int idx = 0; + String out = ''; + if (selection is AudYoFloBitFieldNative) { + AudYoFloBitFieldNative sel = selection as AudYoFloBitFieldNative; + + int ptr = sel.raw.address; + for (; idx < super.num; idx++) { + Pointer ptrFld = Pointer.fromAddress(ptr); + Uint32List bitfield = ptrFld.asTypedList(sel.compileNum32BitBitfield); + AudYoFloBitFieldNative selectionIdx = AudYoFloBitFieldNative(); + selectionIdx.bitfield = bitfield; + selectionIdx.compileNum32BitBitfield = sel.compileNum32BitBitfield; + + int posi = 0; + String outOne = ''; + for (var elmS in entries) { + // Always use offset "0" since we shift the pointer manually (line + 15) + if (selectionIdx.bitTest(posi, offset: 0)) { + if (outOne.isEmpty) { + outOne = elmS; + } else { + outOne = outOne + ', ' + elmS; + } + } + posi++; + } + if (out.isEmpty) { + out = outOne; + } else { + out = out + '; ' + outOne; + } + ptr = ptr + sel.compileNum32BitBitfield * sizeOf(); + } + out = '[' + out + ']'; + } else { + out = '[]'; + } + return out; + } + + // =============================================================== + AudYoFloPropertySelectionListNative( + JvxComponentIdentification cpId, int compileNum32BitBitfieldArg, int num) + : super(cpId, format: jvxDataFormatEnum.JVX_DATAFORMAT_SELECTION_LIST) { + compileNum32BitBitfield = compileNum32BitBitfieldArg; + resize(num, false, false); + // selection.initialize(compileNum32BitBitfield); + selectable.initialize(compileNum32BitBitfield, numEntriesArg: 1); + exclusive.initialize(compileNum32BitBitfield, numEntriesArg: 1); + } + + void resize(int numArg, bool copyOld, bool forceNew) { + // In this case the selection list deviates from the string list: + // There might be a 0-length selection field + super.num = numArg; + if (selection is AudYoFloBitFieldNative) { + AudYoFloBitFieldNative sel = selection as AudYoFloBitFieldNative; + if (super.num > 0) { + if (forceNew || (num != sel.numEntries)) { + AudYoFloBitField newSelection = AudYoFloBitFieldNative(); + assert(numArg > 0); + newSelection.initialize(compileNum32BitBitfield, + numEntriesArg: numArg); + int idx = 0; + if (copyOld) { + int numCpy = min(numArg, sel.numEntries); + for (; idx < numCpy; idx++) { + newSelection.copy(selection, offset: idx); + } + } + selection.dispose(); + selection = newSelection; + } + } + } else { + assert(false); + } + } +} + +// ======================================================================== + +class AudYoFloPropertyValueInRangeNative + extends AudYoFloPropertyMultiContentNative + with AudYoFloPropertyValueInRangeBackend { + AudYoFloPropertyValueInRangeNative(JvxComponentIdentification cpId, int sz, int numDigShow) + : super(cpId, sz, jvxDataFormatEnum.JVX_DATAFORMAT_VALUE_IN_RANGE, numDigShow); + + @override + AudYoFloPropertyMultiContentBackend get parpropmc => this; +} +/* + * Simplified properties for local variables + */ diff --git a/flutter/ayf_pack_native/lib/properties/ayf_properties_from_json_native.dart b/flutter/ayf_pack_native/lib/properties/ayf_properties_from_json_native.dart new file mode 100644 index 00000000..55402435 --- /dev/null +++ b/flutter/ayf_pack_native/lib/properties/ayf_properties_from_json_native.dart @@ -0,0 +1,130 @@ +import 'dart:typed_data'; +import 'dart:ffi'; +import 'package:ayf_pack/ayf_pack.dart'; + +class AudYoFloPropertyContentFromJsonNative { + static int updatePropertyContentFromJsonMap_field( + AudYoFloPropertyContentBackend prop, Map newValues, AudYoFloBackendTranslator trans) { + int errCode = jvxErrorType.JVX_NO_ERROR; + + switch (prop.jvxFormat) { + case jvxDataFormatEnum.JVX_DATAFORMAT_64BIT_LE: + if (prop is AudYoFloPropertyMultiContentNative) { + errCode = + AudYoFloPropertyContentFromJson.convertTextToField( + prop.fld, prop.fldSz, newValues, prop.decoderHintType, trans); + } else { + errCode = jvxErrorType.JVX_ERROR_INVALID_FORMAT; + } + break; + case jvxDataFormatEnum.JVX_DATAFORMAT_U64BIT_LE: + case jvxDataFormatEnum.JVX_DATAFORMAT_SIZE: + if (prop is AudYoFloPropertyMultiContentNative) { + errCode = + AudYoFloPropertyContentFromJson.convertTextToField( + prop.fld, prop.fldSz, newValues, prop.decoderHintType, trans); + } else { + errCode = jvxErrorType.JVX_ERROR_INVALID_FORMAT; + } + break; + + //32 BIT FIELDS + case jvxDataFormatEnum.JVX_DATAFORMAT_32BIT_LE: + if (prop is AudYoFloPropertyMultiContentNative) { + errCode = + AudYoFloPropertyContentFromJson.convertTextToField( + prop.fld, prop.fldSz, newValues, prop.decoderHintType, trans); + } else { + errCode = jvxErrorType.JVX_ERROR_INVALID_FORMAT; + } + break; + case jvxDataFormatEnum.JVX_DATAFORMAT_U32BIT_LE: + if (prop is AudYoFloPropertyMultiContentNative) { + errCode = + AudYoFloPropertyContentFromJson.convertTextToField( + prop.fld, prop.fldSz, newValues, prop.decoderHintType, trans); + } else { + errCode = jvxErrorType.JVX_ERROR_INVALID_FORMAT; + } + break; + + // 16BIT FIELDS + case jvxDataFormatEnum.JVX_DATAFORMAT_16BIT_LE: + if (prop is AudYoFloPropertyMultiContentNative) { + errCode = + AudYoFloPropertyContentFromJson.convertTextToField( + prop.fld, prop.fldSz, newValues, prop.decoderHintType, trans); + } else { + errCode = jvxErrorType.JVX_ERROR_INVALID_FORMAT; + } + break; + case jvxDataFormatEnum.JVX_DATAFORMAT_U16BIT_LE: + if (prop is AudYoFloPropertyMultiContentNative) { + errCode = + AudYoFloPropertyContentFromJson.convertTextToField( + prop.fld, prop.fldSz, newValues, prop.decoderHintType, trans); + } + else { + errCode = jvxErrorType.JVX_ERROR_INVALID_FORMAT; + } + break; + + // 8BIT FIELDS + case jvxDataFormatEnum.JVX_DATAFORMAT_8BIT: + if (prop is AudYoFloPropertyMultiContentNative) { + errCode = + AudYoFloPropertyContentFromJson.convertTextToField( + prop.fld, prop.fldSz, newValues, prop.decoderHintType, trans); + } else { + errCode = jvxErrorType.JVX_ERROR_INVALID_FORMAT; + } + break; + case jvxDataFormatEnum.JVX_DATAFORMAT_U8BIT: + if (prop is AudYoFloPropertyMultiContentNative) { + errCode = + AudYoFloPropertyContentFromJson.convertTextToField( + prop.fld, prop.fldSz, newValues, prop.decoderHintType, trans); + } else { + errCode = jvxErrorType.JVX_ERROR_INVALID_FORMAT; + } + break; + + // DOUBLE/FLOAT FIELDS + case jvxDataFormatEnum.JVX_DATAFORMAT_DATA: + if (prop is AudYoFloPropertyMultiContentNative) { + errCode = + AudYoFloPropertyContentFromJson.convertTextToField( + prop.fld, prop.fldSz, newValues, prop.decoderHintType, trans); + } else if (prop + is AudYoFloPropertyMultiContentNative) { + errCode = + AudYoFloPropertyContentFromJson.convertTextToField( + prop.fld, prop.fldSz, newValues, prop.decoderHintType, trans); + } else { + errCode = jvxErrorType.JVX_ERROR_INVALID_FORMAT; + } + break; + default: + assert(false); + break; + } + + if(errCode != jvxErrorType.JVX_NO_ERROR) + { + var str1 = prop.descriptor; + var str2 = extractStringFromJson(newValues, 'property'); + str2 ??= 'unknown'; + var str3 = jvxErrorTypeEInt.toStringSingle(errCode); + dbgPrint('Error converting property <$str1> from <$str2>, reason: $str3'); + } + return errCode; + } +} + +// This is a global function to drive different host types to different calls: native access here, but +// there is another call in the web code!! +int updatePropertyContentFromJsonMap_fields( + AudYoFloPropertyContentBackend prop, Map newValues, AudYoFloBackendTranslator trans) { + return AudYoFloPropertyContentFromJsonNative + .updatePropertyContentFromJsonMap_field(prop, newValues, trans); +} diff --git a/flutter/ayf_pack_native/lib/sub-widgets/file-input-devices/ayf_file_input_core_widget.dart b/flutter/ayf_pack_native/lib/sub-widgets/file-input-devices/ayf_file_input_core_widget.dart new file mode 100644 index 00000000..93b0c00d --- /dev/null +++ b/flutter/ayf_pack_native/lib/sub-widgets/file-input-devices/ayf_file_input_core_widget.dart @@ -0,0 +1,159 @@ +import 'package:flutter/material.dart'; +import 'package:ayf_pack/ayf_pack.dart'; +import 'package:provider/provider.dart'; +import 'package:cross_file/cross_file.dart'; +import 'package:desktop_drop/desktop_drop.dart'; + +import 'package:collection/collection.dart'; + +// ======================================================================= +// ======================================================================= +class AudYoFloFileInputCoreWidgetNative extends StatefulWidget { + const AudYoFloFileInputCoreWidgetNative( + this.identT, this.textShowDrag, this.sizeIcon, + {Key? key}) + : super(key: key); + final JvxComponentIdentification identT; + final String textShowDrag; + final double sizeIcon; + + @override + _AudYoFloFileInputCoreWidgetNativeState createState() => + _AudYoFloFileInputCoreWidgetNativeState(); +} + +class _AudYoFloFileInputCoreWidgetNativeState + extends State + with + AudYoFloMultiComponentsPropertyOnChange, + AudYoFloPropertyOnChangeGroups { + final List _list = []; + bool _dragging = false; + AudYoFloBackendCache? theBeCache; + late String propName; + final String groupName = 'fileInput'; + + AudYoFloPropertyOnChangeOneGroup oneGroupTechnology = + AudYoFloPropertyOnChangeOneGroup(['/open_filename']); + + AudYoFloOneComponentSettingsBackend mySettings = + AudYoFloOneComponentSettingsBackend( + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_UNKNOWN, + slotid: -1, + slotsubid: -1), + 'tagFileInput', + []); + + _AudYoFloFileInputCoreWidgetNativeState() { + // Extract the property name + var elm = oneGroupTechnology.propsInGroup.first; + propName = elm.propertyDescr; + + theGroups[groupName] = oneGroupTechnology; + + createList(); + + // Store the local property component + mySettings.propsDescriptors = propsAllGroups; + } + + @override + AudYoFloOneComponentSettings? requestedPropertiesList( + JvxComponentIdentification cpId) { + if (cpId == mySettings.cpId) { + return mySettings; + } + return null; + } + + @override + Widget innerWidget(BuildContext context) { + // Reconstruct the properties from the latestResults struct + var fElm = latestResult.firstWhereOrNull((str) { + if (str is AudYoFloMultiComponentsPropertyOnChangeEntryBackend) { + return (str.cpId == mySettings.cpId); + } + return false; + }); + if (fElm != null) { + if (fElm is AudYoFloMultiComponentsPropertyOnChangeEntryBackend) { + if (fElm.bprops != null) { + reconstructProperyReferences(fElm.bprops!); + } + } + } + + AudYoFloPropertyOnChangeOneGroup? groupLocalWidget; + MapEntry? groupEntryLower = + theGroups.entries + .firstWhereOrNull((element) => element.key == groupName); + if (groupEntryLower != null) { + groupLocalWidget = groupEntryLower.value; + } + + AudYoFloPropertySingleStringBackend? propsLocal; + if (groupLocalWidget != null) { + propsLocal = groupLocalWidget.extractPropString(propName); + } + + return DropTarget( + onDragDone: (detail) { + for (var elmF in detail.files) { + String fName = elmF.path; + List props = [propName]; + if (propsLocal != null) { + propsLocal.value = fName; + if (theBeCache != null) { + theBeCache!.triggerSetProperties(widget.identT, props); + } + } else { + print('Warning: Failed to set property '); + } + } + }, + onDragEntered: (detail) { + setState(() { + _dragging = true; + }); + }, + onDragExited: (detail) { + setState(() { + _dragging = false; + }); + }, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + color: _dragging ? Colors.blue.withOpacity(0.4) : Colors.black26, + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Tooltip( + message: widget.textShowDrag, + child: + Icon(Icons.audio_file_outlined, size: widget.sizeIcon), + ), + ], + ), + )), + ), + ); + } + + @override + Widget build(BuildContext context) { + theBeCache = Provider.of(context, listen: false); + + if (configured == false) { + // Read out the component of focus from the widget + mySettings.cpId = widget.identT; + resetMultiPropertySettings(); + setComponentConsider(mySettings.cpId); + } + return localBuild(context); + } +} + +// ======================================================================== \ No newline at end of file diff --git a/flutter/ayf_pack_native/lib/types/ayf_bitfield.dart b/flutter/ayf_pack_native/lib/types/ayf_bitfield.dart new file mode 100644 index 00000000..9704edcd --- /dev/null +++ b/flutter/ayf_pack_native/lib/types/ayf_bitfield.dart @@ -0,0 +1,120 @@ +import 'dart:math'; +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; +import 'package:fixnum/fixnum.dart' as fn; +import 'package:ayf_pack/ayf_pack.dart'; + +// =========================================================================== +class AudYoFloBitFieldNative extends AudYoFloBitFieldBackend { + Pointer raw = nullptr; + AudYoFloBitFieldNative(); + + @override + void initialize(int num32BitBitfieldArg, {int numEntriesArg = 1}) { + compileNum32BitBitfield = num32BitBitfieldArg; + numEntries = numEntriesArg; + num32BitAlltogether = numEntriesArg * compileNum32BitBitfield; + raw = calloc(num32BitAlltogether); + bitfield = raw.asTypedList(num32BitAlltogether); + } + + @override + bool bitTest(int posi, {int offset = 0}) { + int idxEntry = posi >> 5; + int inWordIdx = posi - (idxEntry << 5); + int inFldIdx = compileNum32BitBitfield - 1 - idxEntry; + inFldIdx += offset * compileNum32BitBitfield; + if ((bitfield![inFldIdx] & (1 << inWordIdx)) != 0) { + return true; + } + return false; + } + + @override + int firstSet(int posiMax, {int offset = 0}) { + int firstSetPosi = -1; + posiMax = min(posiMax, compileNum32BitBitfield); + for (int posi = 0; posi < posiMax; posi++) { + int idxEntry = posi >> 5; + int inWordIdx = posi - (idxEntry << 5); + int inFldIdx = compileNum32BitBitfield - 1 - idxEntry; + inFldIdx += offset * compileNum32BitBitfield; + if ((bitfield![inFldIdx] & (1 << inWordIdx)) != 0) { + firstSetPosi = posi; + break; + } + } + return firstSetPosi; + } + + @override + void bitSet(int posi, {int offset = 0}) { + int idxEntry = posi >> 5; + int inWordIdx = posi - (idxEntry << 5); + int inFldIdx = compileNum32BitBitfield - 1 - idxEntry; + inFldIdx += offset * compileNum32BitBitfield; + bitfield![inFldIdx] |= (1 << inWordIdx); + } + + @override + void bitZSet(int posi, {int offset = 0}) { + int idxEntry = posi >> 5; + int inWordIdx = posi - (idxEntry << 5); + int inFldIdx = compileNum32BitBitfield - 1 - idxEntry; + inFldIdx += offset * compileNum32BitBitfield; + if (inFldIdx > 0) { + bitfield![inFldIdx] = (1 << inWordIdx); + } + } + + @override + void bitClear(int posi, {int offset = 0}) { + int idxEntry = posi >> 5; + int inWordIdx = posi - (idxEntry << 5); + int inFldIdx = compileNum32BitBitfield - 1 - idxEntry; + inFldIdx += offset * compileNum32BitBitfield; + if (inFldIdx > 0) { + bitfield![inFldIdx] &= ~(1 << inWordIdx); + } + } + + @override + void bitFClear({int offset = 0}) { + for (int idx = 0; idx < compileNum32BitBitfield; idx++) { + int inFldIdx = compileNum32BitBitfield - 1 - idx; + inFldIdx += offset * compileNum32BitBitfield; + if (inFldIdx > 0) { + bitfield![inFldIdx] = 0; + } + } + } + + @override + void copy(AudYoFloBitField copyFrom, {int offset = 0}) { + assert(offset < numEntries); + if (copyFrom is AudYoFloBitFieldNative) { + assert(offset < copyFrom.numEntries); + for (int idx = 0; idx < compileNum32BitBitfield; idx++) { + int inFldIdx = compileNum32BitBitfield - 1 - idx; + inFldIdx += offset * compileNum32BitBitfield; + bitfield![inFldIdx] = copyFrom.bitfield![inFldIdx]; + } + } + } + + @override + void dispose() { + if (raw != nullptr) { + calloc.free(raw); + } + raw = nullptr; + compileNum32BitBitfield = 0; + numEntries = 0; + num32BitAlltogether = 0; + bitfield = null; + } +} + +AudYoFloBitField allocateAudYoFloBitfield() { + return AudYoFloBitFieldNative(); +} diff --git a/flutter/ayf_pack_native/pubspec.yaml b/flutter/ayf_pack_native/pubspec.yaml new file mode 100644 index 00000000..aa6daf37 --- /dev/null +++ b/flutter/ayf_pack_native/pubspec.yaml @@ -0,0 +1,97 @@ +name: ayf_pack_native +description: A new Flutter project. + +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: '>=2.18.0 <3.0.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + ayf_pack: + path: '../ayf_pack' + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + + ffi: ^1.1.2 + flutter_window_close: ^0.2.2 + desktop_drop: ^0.4.2 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/flutter/ayf_pack_native/test/widget_test.dart b/flutter/ayf_pack_native/test/widget_test.dart new file mode 100644 index 00000000..f70efbe8 --- /dev/null +++ b/flutter/ayf_pack_native/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:ayf_pack_native/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/flutter/ayf_pack_native/windows/.gitignore b/flutter/ayf_pack_native/windows/.gitignore new file mode 100644 index 00000000..d492d0d9 --- /dev/null +++ b/flutter/ayf_pack_native/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/flutter/ayf_pack_native/windows/CMakeLists.txt b/flutter/ayf_pack_native/windows/CMakeLists.txt new file mode 100644 index 00000000..f9a27244 --- /dev/null +++ b/flutter/ayf_pack_native/windows/CMakeLists.txt @@ -0,0 +1,101 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(ayf_pack_native LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "ayf_pack_native") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Define build configuration option. +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() +# Define settings for the Profile build mode. +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. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +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() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +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/flutter/ayf_pack_native/windows/flutter/CMakeLists.txt b/flutter/ayf_pack_native/windows/flutter/CMakeLists.txt new file mode 100644 index 00000000..930d2071 --- /dev/null +++ b/flutter/ayf_pack_native/windows/flutter/CMakeLists.txt @@ -0,0 +1,104 @@ +# This file controls Flutter-level build steps. It should not be edited. +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/flutter/ayf_pack_native/windows/runner/CMakeLists.txt b/flutter/ayf_pack_native/windows/runner/CMakeLists.txt new file mode 100644 index 00000000..17411a8a --- /dev/null +++ b/flutter/ayf_pack_native/windows/runner/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +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 the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/flutter/ayf_pack_native/windows/runner/Runner.rc b/flutter/ayf_pack_native/windows/runner/Runner.rc new file mode 100644 index 00000000..46c560cd --- /dev/null +++ b/flutter/ayf_pack_native/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 +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#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", "ayf_pack_native" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "ayf_pack_native" "\0" + VALUE "LegalCopyright", "Copyright (C) 2023 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "ayf_pack_native.exe" "\0" + VALUE "ProductName", "ayf_pack_native" "\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/flutter/ayf_pack_native/windows/runner/flutter_window.cpp b/flutter/ayf_pack_native/windows/runner/flutter_window.cpp new file mode 100644 index 00000000..b43b9095 --- /dev/null +++ b/flutter/ayf_pack_native/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/flutter/ayf_pack_native/windows/runner/flutter_window.h b/flutter/ayf_pack_native/windows/runner/flutter_window.h new file mode 100644 index 00000000..6da0652f --- /dev/null +++ b/flutter/ayf_pack_native/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/flutter/ayf_pack_native/windows/runner/main.cpp b/flutter/ayf_pack_native/windows/runner/main.cpp new file mode 100644 index 00000000..eb039490 --- /dev/null +++ b/flutter/ayf_pack_native/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"ayf_pack_native", 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/flutter/ayf_pack_native/windows/runner/resource.h b/flutter/ayf_pack_native/windows/runner/resource.h new file mode 100644 index 00000000..66a65d1e --- /dev/null +++ b/flutter/ayf_pack_native/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/flutter/ayf_pack_native/windows/runner/resources/app_icon.ico b/flutter/ayf_pack_native/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/flutter/ayf_pack_native/windows/runner/runner.exe.manifest b/flutter/ayf_pack_native/windows/runner/runner.exe.manifest new file mode 100644 index 00000000..a42ea768 --- /dev/null +++ b/flutter/ayf_pack_native/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/flutter/ayf_pack_native/windows/runner/utils.cpp b/flutter/ayf_pack_native/windows/runner/utils.cpp new file mode 100644 index 00000000..f5bf9fa0 --- /dev/null +++ b/flutter/ayf_pack_native/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); + std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return 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/flutter/ayf_pack_native/windows/runner/utils.h b/flutter/ayf_pack_native/windows/runner/utils.h new file mode 100644 index 00000000..3879d547 --- /dev/null +++ b/flutter/ayf_pack_native/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/flutter/ayf_pack_native/windows/runner/win32_window.cpp b/flutter/ayf_pack_native/windows/runner/win32_window.cpp new file mode 100644 index 00000000..c10f08dc --- /dev/null +++ b/flutter/ayf_pack_native/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/flutter/ayf_pack_native/windows/runner/win32_window.h b/flutter/ayf_pack_native/windows/runner/win32_window.h new file mode 100644 index 00000000..17ba4311 --- /dev/null +++ b/flutter/ayf_pack_native/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/flutter/ayf_webhost/.gitignore b/flutter/ayf_webhost/.gitignore new file mode 100644 index 00000000..24476c5d --- /dev/null +++ b/flutter/ayf_webhost/.gitignore @@ -0,0 +1,44 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/flutter/ayf_webhost/.jvxprj.flutter b/flutter/ayf_webhost/.jvxprj.flutter new file mode 100644 index 00000000..e69de29b diff --git a/flutter/ayf_webhost/.pass.default b/flutter/ayf_webhost/.pass.default new file mode 100644 index 00000000..e69de29b diff --git a/flutter/ayf_webhost/CMakeLists.txt b/flutter/ayf_webhost/CMakeLists.txt new file mode 100644 index 00000000..8a7424ea --- /dev/null +++ b/flutter/ayf_webhost/CMakeLists.txt @@ -0,0 +1,14 @@ +## Why only in the SDK build? Since the path to the dlls is set in the .env files which are only built in the SDK build! + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pubspec.yaml.in ${CMAKE_CURRENT_SOURCE_DIR}/pubspec.yaml @ONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/launch.json.in ${CMAKE_CURRENT_SOURCE_DIR}/.vscode/launch.json @ONLY) + +if(JVX_RELEASE_SDK) + + # Configure for all tests + set(AYFBINRENDER_HOSTCONF_DLL "${CMAKE_INSTALL_PREFIX}/${INSTALL_PATH_BIN}/ayfbinrender-native-config_import.dll") + set(AYFBINRENDER_FFI_DLL "${CMAKE_INSTALL_PREFIX}/${INSTALL_PATH_BIN}/jvx-flutter-native-shared_import.dll") + set(AYFBINRENDER_FFI_PRELOAD_DLLS "${CMAKE_INSTALL_PREFIX}/${INSTALL_PATH_BIN}/zlib.dll,${CMAKE_INSTALL_PREFIX}/${INSTALL_PATH_BIN}/libfftw3-3.dll") + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/example/.ayf_webhost_example.env.in ${CMAKE_CURRENT_SOURCE_DIR}/example/.ayf_webhost_example.env @ONLY) + +endif() \ No newline at end of file diff --git a/flutter/ayf_webhost/LICENSE b/flutter/ayf_webhost/LICENSE new file mode 100644 index 00000000..ba75c69f --- /dev/null +++ b/flutter/ayf_webhost/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/flutter/ayf_webhost/README.md b/flutter/ayf_webhost/README.md new file mode 100644 index 00000000..8b55e735 --- /dev/null +++ b/flutter/ayf_webhost/README.md @@ -0,0 +1,39 @@ + + +TODO: Put a short description of the package here that helps potential users +know whether this package might be useful for them. + +## Features + +TODO: List what your package can do. Maybe include images, gifs, or videos. + +## Getting started + +TODO: List prerequisites and provide or point to information on how to +start using the package. + +## Usage + +TODO: Include short and useful examples for package users. Add longer examples +to `/example` folder. + +```dart +const like = 'sample'; +``` + +## Additional information + +TODO: Tell users more about the package: where to find more information, how to +contribute to the package, how to file issues, what response they can expect +from the package authors, and more. diff --git a/flutter/ayf_webhost/doc/listErrorCodesWebHost.xlsx b/flutter/ayf_webhost/doc/listErrorCodesWebHost.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..b059329d4438e62f91d176efb494ab4e73bcfd9f GIT binary patch literal 10934 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 literal 0 HcmV?d00001 diff --git a/flutter/ayf_webhost/example/.ayf_webhost_example.env.in b/flutter/ayf_webhost/example/.ayf_webhost_example.env.in new file mode 100644 index 00000000..1af23a65 --- /dev/null +++ b/flutter/ayf_webhost/example/.ayf_webhost_example.env.in @@ -0,0 +1,4 @@ +AYF_BACKEND_FFI_SHARED_LIBRARY = '@AYFBINRENDER_FFI_DLL@' +AYF_BACKEND_PROJECT_HOSTCONFIG_LIBRARY = '@AYFBINRENDER_HOSTCONF_DLL@' +AYF_BACKEND_PRELOAD_LIBRARIES = '@AYFBINRENDER_FFI_PRELOAD_DLLS@'; +AYF_BACKEND_EXECUTABLE_IN_BINFOLDER = "yes"; diff --git a/flutter/ayf_webhost/example/.gitignore b/flutter/ayf_webhost/example/.gitignore new file mode 100644 index 00000000..a8e938c0 --- /dev/null +++ b/flutter/ayf_webhost/example/.gitignore @@ -0,0 +1,47 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/flutter/ayf_webhost/example/README.md b/flutter/ayf_webhost/example/README.md new file mode 100644 index 00000000..c09873d7 --- /dev/null +++ b/flutter/ayf_webhost/example/README.md @@ -0,0 +1,16 @@ +# ayf_pack_example + +Demonstrates how to use the ayf_pack plugin. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/flutter/ayf_webhost/example/images/ayfbinrender_icons/ayfbinrender-logo.png b/flutter/ayf_webhost/example/images/ayfbinrender_icons/ayfbinrender-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..21c7a29e05dafc2fcdfad00fb02e2529fc5bb48e GIT binary patch literal 205019 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

G0^F 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 arguments) { + /* + if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) { + setWindowTitle('App title'); + setWindowMinSize(const Size(700, 500)); + setWindowMaxSize(Size.infinite); + } + */ + + // What is this good for in native windows?? It seems that native splash image + // does not work on platform windows! + // + // https://www.youtube.com/watch?v=dB0dOnc2k10 + // WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); + // FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); + // + + // Initialize the platform specific part here. We surely will connect via http. + // But then, we have two options: + // 1) Native code - this will add some desktop specific features + // 2) Web browser - this will run in the web browser and not have the desktop specific features + AudYoFloPlatformSpecific platformSpec = allocatePlatformSpecific(); + Map configArgs = {}; + configArgs['host'] = ayfhost; + configArgs['cmdArgs'] = arguments; + platformSpec.configureSubSystem(configArgs); + + // *************************************************** + runApp(MultiProvider( + providers: [ + ChangeNotifierProvider( + create: (_) => AudYoFloPeriodicNotifier()), + ChangeNotifierProvider( + create: (_) => AudYoFloBackendCacheSpecific()), + ChangeNotifierProvider( + create: (_) => AudYoFloUiModelSpecificWithWidget()), + ChangeNotifierProvider( + create: (_) => AudYoFloDebugModel()), + /*ChangeNotifierProvider( + create: (_) => FernliveCacheModel()),*/ + ], + + // Here, we enter the main widget. We pass the backend adapter (here: ffi bridge) + // and the core pack which holds the Ayf components precompiled plugin + child: AudYoFloMainWidget( + platformSpecific: platformSpec, + ))); +} diff --git a/flutter/ayf_webhost/example/lib/models/ayf_be_specific.dart b/flutter/ayf_webhost/example/lib/models/ayf_be_specific.dart new file mode 100644 index 00000000..8ce6c354 --- /dev/null +++ b/flutter/ayf_webhost/example/lib/models/ayf_be_specific.dart @@ -0,0 +1,5 @@ +import 'package:ayf_pack/ayf_pack.dart'; + +class AudYoFloBackendCacheSpecific extends AudYoFloBackendCache { + AudYoFloBackendCacheSpecific() : super() {} +} diff --git a/flutter/ayf_webhost/example/lib/models/ayf_ui_specific.dart b/flutter/ayf_webhost/example/lib/models/ayf_ui_specific.dart new file mode 100644 index 00000000..36725e70 --- /dev/null +++ b/flutter/ayf_webhost/example/lib/models/ayf_ui_specific.dart @@ -0,0 +1,248 @@ +import 'package:flutter/material.dart'; +import 'package:ayf_pack/ayf_pack.dart'; +import 'package:ayf_pack_native/ayf_pack_native.dart' + if (dart.library.html) 'package:ayf_pack/ayf_pack_web.dart'; + +// import '../ayfpacks/ayf_pack.dart'; + +import 'package:ayfbinrender/ayf_binrender_widget.dart'; +import 'package:ayf_pack/main-widgets/ayf_audio_settings_widget.dart'; + +class AudYoFloUiModelSpecific extends AudYoFloUiModel + with AudYoFloUiElementsTabBar { + // ========================================================================== + // We define the ui modeling parameters for the binaural rendering widget here. + // Note that this instance also defines a local property shown in the settings + // ========================================================================== + AudYoFloBinauralRendererSection binauralRenderer = + AudYoFloBinauralRendererSection(true, + 'packages/ayf_pack/images/ayf_icons/ayf-brender.png', 'Bin. Renderer', + imageNameIcon: 'packages/ayf_pack/images/ayf_icons/ayf-micsrc.png', + height: 70, + width: 70); + + // ========================================================================== + // We define some rendering parameters for the audio io widget. + // We use package specific icons here. + // ========================================================================== + AudYoFloAudioDeviceRenderSection soundIo = AudYoFloAudioDeviceRenderSection( + imageNameSoundIo: 'packages/ayf_pack/images/ayf_icons/ayf-soundio.png', + imageNameSoundI: 'packages/ayf_pack/images/ayf_icons/ayf-soundi.png', + imageNameSoundO: 'packages/ayf_pack/images/ayf_icons/ayf-soundo.png'); + + AudYoFloAudioDeviceFileInputSection fileinput = + AudYoFloAudioDeviceFileInputSection(sizeIcon: 55); + + AudYoFloHeadtrackerDeviceRenderSection headtracker = + AudYoFloHeadtrackerDeviceRenderSection( + imageNameHeadtracker: + 'packages/ayf_pack/images/ayf_icons/ayf-headtracker.png'); + + // Add a local property of type "string" + AudYoFloPropertyStringLocal propTestme = AudYoFloPropertyStringLocal( + 'Hallo Test', "/settings/testme", "Description"); + + AudYoFloUiModelSpecific() { + textApp = 'ayfbinrender'; + imageNameApp = 'images/ayfbinrender_icons/ayfbinrender-logo.png'; + + // Define some specific configuration entities + widgetSpecificConfigurations['binauralRenderer'] = binauralRenderer; + widgetSpecificConfigurations['soundIo'] = soundIo; + widgetSpecificConfigurations['headtracker'] = headtracker; + widgetSpecificConfigurations['fileinput'] = fileinput; + + showAudioDevTech = false; + + // ===================================================================== + // Define the settings dialog entries + // ===================================================================== + } + + @override + void configureSystemParameters(AudYoFloCompileFlags flags) { + super.configureSystemParameters(flags); + AudYoFloSettingsConfigurationSection oneSection = + AudYoFloSettingsConfigurationSection(); + + oneSection = AudYoFloSettingsConfigurationSection(); + oneSection.nameEntry = 'Application'; + + // ======================================================================= + // Initialize the local property at this position + // ======================================================================= + binauralRenderer.propIconSize.selection + .initialize(flags.compileNum32BitBitfield); + binauralRenderer.propIconSize.selection.bitZSet(0); + AudYoFloOneComponentSettingsLocal addThisLoUi = + AudYoFloOneComponentSettingsLocal('Ui Properties', [ + binauralRenderer.propIconSize, + binauralRenderer.propSpread, + fastRefreshPerMsecs, + slowRefreshPerMsecs + ], propTags: [ + 'Binaural Render Icon Size', + 'Spread Azimuth Angle', + 'Fast callback period', + 'Slow callback period', + ], propTooltips: [ + 'Specify the size of the binaural render icon.', + 'Setup the maximum spread angle in case of icon spread button', + ' Specify the period for the fast timer', + ' Specify the period for the slow timer', + ]); + oneSection.addEntry(addThisLoUi); + + AudYoFloOneComponentSettingsBackend addThisBeHost = + AudYoFloOneComponentSettingsBackend( + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_HOST, + slotid: 0, + slotsubid: 0), + 'Host Configuration', + [ + '/jvxLibHost/autostart' + + ///ext_interfaces/application_property_autostart' + ], + propTags: [ + 'Activate Autostart' + ], + propTooltips: [ + 'Activate Autostart feature to take effect when starting app the next time. When activated, audio processing will start immediately.' + ]); + + oneSection.addEntry(addThisBeHost); + + AudYoFloOneComponentSettingsBackend addThisBeAsio = + AudYoFloOneComponentSettingsBackend( + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_DEVICE, + slotid: 0, + slotsubid: 0), + 'ASIO Audio Device', + [ + '/JVX_GENW/system/sel_framesize', + '/JVX_GENW/system/sel_rate', + '/system/rate', + '/system/sel_framesize' + ], + propTags: [ + 'Select ASIO Buffersize', + 'Select ASIO Samplerate', + 'Samplerate', + 'Buffersize' + ], + propTooltips: [ + 'Tooltip: Select ASIO Buffersize', + 'Tooltip: Select ASIO Samplerate', + 'Tooltip: Samplerate', + 'Tooltip: Buffersize' + ]); + + oneSection.addEntry(addThisBeAsio); + + settingsConfigure.allSections[oneSection.nameEntry] = oneSection; + + // Here we may add an additional combobox in the component settings + // showAudioDevTech = true; + + // Create another section + // This is here only for demonstration and is not active at the moment! + if (false) { + oneSection = AudYoFloSettingsConfigurationSection(); + oneSection.nameEntry = 'Sip'; + + AudYoFloOneComponentSettingsBackend addThisBeSip = + AudYoFloOneComponentSettingsBackend( + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_DEVICE, + slotid: 3, + slotsubid: 0), + 'Sip Device', + [ + '/xyz', + '/xyz', + '/xyz' + ], + propTags: [ + '/xyz', + '/xyz', + '/xyz' + ], + propTooltips: [ + 'Tooltip: /xyz', + 'Tooltip: /xyz', + 'Tooltip: /xyz' + ]); + oneSection.addEntry(addThisBeSip); + + settingsConfigure.allSections[oneSection.nameEntry] = oneSection; + } + } + + @override + int reportSetProperty(AudYoFloPropertyContentLocal prop) { + bool notifyThis = false; + if (prop.descriptor == "/settings/testme") { + return jvxErrorType.JVX_NO_ERROR; + } + + if (prop.descriptor == binauralRenderer.propIconSize.descriptor) { + if (binauralRenderer.propIconSize.selection.bitTest(0)) { + binauralRenderer.height = 40; + binauralRenderer.width = 40; + } else if (binauralRenderer.propIconSize.selection.bitTest(1)) { + binauralRenderer.height = 70; + binauralRenderer.width = 70; + } else if (binauralRenderer.propIconSize.selection.bitTest(2)) { + binauralRenderer.height = 100; + binauralRenderer.width = 100; + } + notifyThis = true; + } + + if (prop.descriptor == binauralRenderer.propSpread.descriptor) { + binauralRenderer.spreadAngle = binauralRenderer.propSpread.value; + notifyThis = true; + } + + if (notifyThis) { + notifyListeners(); + } + + return super.reportSetProperty(prop); + } + + @override + void filterListProperties( + String token, AudYoFloMultiComponentsPropertyOnChangeEntry entries) { + if (entries.settings.cpId == + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_DEVICE, + slotid: 0, + slotsubid: 0)) { + if (entries is AudYoFloMultiComponentsPropertyOnChangeEntryBackend) { + AudYoFloMultiComponentsPropertyOnChangeEntryBackend entriesBe = entries; + List? lst = entriesBe.bprops; + if (lst != null) { + if (token == 'Settings') { + lst = filterListProperties_settings(lst); + } + entriesBe.bprops = lst; + } + } + // var modLst = entries.; + } + super.filterListProperties('Settings', entries); + } + + List filterListProperties_settings( + List lst) { + lst = removeIfPresent( + lst, '/JVX_GENW/system/sel_framesize', ['/system/framesize']); + lst = removeIfPresent(lst, '/JVX_GENW/system/sel_rate', ['/system/rate']); + + return lst; + } +} diff --git a/flutter/ayf_webhost/example/lib/models/ayf_ui_specific_widgets.dart b/flutter/ayf_webhost/example/lib/models/ayf_ui_specific_widgets.dart new file mode 100644 index 00000000..c2bac5a9 --- /dev/null +++ b/flutter/ayf_webhost/example/lib/models/ayf_ui_specific_widgets.dart @@ -0,0 +1,315 @@ +import 'package:flutter/material.dart'; +import 'package:ayf_pack/ayf_pack.dart'; +import 'ayf_ui_specific.dart'; + +import 'package:ayfbinrender/ayf_binrender_widget.dart'; +import 'package:ayf_pack/main-widgets/ayf_audio_settings_widget.dart'; + +import 'package:flutter/foundation.dart' show kIsWeb; + +// ============================================================================ +// Callback to allocate an audio device sliver in the list of active/inactive +// devices lists +Widget? allocateAudioDeviceSliver(AudYoFloOneSelectionOptionWithId option, + bool fromActiveList, AudYoFloSingleSelectionComponent? reportW) => + AudYoFloOneDeviceSliver(option, fromActiveList, reportW); + +// ============================================================================ +// Callback to allocate a headtracker device sliver in the list of active/ +// inactibe devices list +Widget? allocateHeadtrackerDeviceSliver(AudYoFloOneSelectionOptionWithId option, + bool fromActiveList, AudYoFloSingleSelectionComponent? reportW) => + AudYoFloOneHeadtrackerDeviceSliver(option, fromActiveList, reportW); + +BottomAppBar? allocatorBottomAppBar() { + /* + if (kIsWeb) { + */ + return AudYoFloPlayConnectBottomAppBar(); + /* + } else { + return AudYoFloBottomAppBar(); + } + */ +} + +class AudYoFloPlayConnectBottomAppBar extends BottomAppBar { + AudYoFloPlayConnectBottomAppBar() + : super( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [AudYoFloPlaypannel(), AudYoFloPlaypannel()], + )); +} + +// ============================================================================ +// ============================================================================ +// This class extends the common UiModel part and adds application specific +// configuration entries. +// ============================================================================ +class AudYoFloUiModelSpecificWithWidget extends AudYoFloUiModelSpecific { + // ========================================================================== + // Define the core widget composition here: The function configures all application + // specific widgets here. Then, if all specific widgets were added, the base class + // call may be used to attach the application unspecific widgets - which are typically + // only the debug output and the component property viewer widget. + // ========================================================================== + + AudYoFloUiModelSpecificWithWidget() : super() { + allocateBottomAppBar = allocatorBottomAppBar; + initRoute = "/connect"; + } + + @override + void createTabsAndViews(Map entriesCfgWidgets) { + // Tabs are added to a list along multiple layers of the class hierarchie + // Since we dont know if we are the first here, we can not clear the list + // when entering the call. It is the task of the caller to run clear before + + // ====================================================================== + // Add the Binaural rendering widget + // ====================================================================== + + JvxComponentIdentification cpBin = widgetConfigCpId( + entriesCfgWidgets, + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_NODE, + slotid: 0, + slotsubid: 0), + 'binrender'); + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Tooltip( + message: cpBin.txt, + child: Column(children: [ + Image.asset(binauralRenderer.imageName, + filterQuality: FilterQuality.medium, + width: navButtonSizeMain, + height: navButtonSizeMain), + GestureDetector( + onSecondaryLongPress: () { + // print('Hello'); to be filled later + }, + child: Text(binauralRenderer.textShow), + ) // <- maybe, make this switchable by right click? + ]), + ))); + theAllocatedTabViews.add(AudYoFloBinRenderWidget(cpBin)); + // ====================================================================== + // Add the primary audio device widget + // ====================================================================== + String imageNameAudioIoPri = + 'packages/ayf_pack/images/ayf_icons/ayf-audio-settings.png'; + String textAudioIoPri = 'ASIO I/O'; + JvxComponentIdentification cpTpPriTech = widgetConfigCpId( + entriesCfgWidgets, + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_TECHNOLOGY, + slotid: 0, + slotsubid: 0), + 'priaudiot'); + + JvxComponentIdentification cpTpPriDev = widgetConfigCpId( + entriesCfgWidgets, + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_DEVICE, + slotid: cpTpPriTech.slotid, + slotsubid: 0), + 'priaudiod'); + + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Tooltip( + message: '${cpTpPriTech.txt}--${cpTpPriDev.txt}', + child: Column(children: [ + Image.asset(imageNameAudioIoPri, + filterQuality: FilterQuality.medium, + //'images/fernlive_icons/fernlive-mixer.png', + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(textAudioIoPri) + ])))); + theAllocatedTabViews.add(AudYoFloAudioSettingsWidget( + cpTpPriTech, + 'Audio Technologies', + cpTpPriDev, + 'Audio Devices', + 'soundIo', + (JvxComponentIdentification selCpId) => + AudYoFloAudioDeviceInputOutputChannelWidget(selCpId), + allocateAudioDeviceSliver)); + + // ====================================================================== + // Add the secondary audio device widget + // ====================================================================== + String imageNameAudioIoSec = + 'packages/ayf_pack/images/ayf_icons/ayf-audio-settings.png'; + String textAudioIoSec = 'WASAPI I/O'; + JvxComponentIdentification cpTpSecTech = widgetConfigCpId( + entriesCfgWidgets, + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_TECHNOLOGY, + slotid: 3, + slotsubid: 0), + 'secaudiot'); + + JvxComponentIdentification cpTpSecDev = widgetConfigCpId( + entriesCfgWidgets, + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_DEVICE, + slotid: cpTpSecTech.slotid, + slotsubid: 0), + 'secaudiod'); + + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Tooltip( + message: '${cpTpSecTech.txt}--${cpTpSecDev.txt}', + child: Column(children: [ + Image.asset(imageNameAudioIoSec, + filterQuality: FilterQuality.medium, + //'images/fernlive_icons/fernlive-mixer.png', + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(textAudioIoSec) + ])))); + theAllocatedTabViews.add(AudYoFloAudioSettingsWidget( + cpTpSecTech, + 'Audio Technologies', + cpTpSecDev, + 'Audio Devices', + 'soundIo', + (JvxComponentIdentification selCpId) => + AudYoFloAudioDeviceInputOutputChannelWidget(selCpId), + allocateAudioDeviceSliver)); + + // ====================================================================== + // Add the headtracker device widget [RS-232] + // ====================================================================== + String imageNameHeadTracker = + 'packages/ayf_pack/images/ayf_icons/ayf-headtracker-settings.png'; + String textAudioIoHt = 'H-Tracker Rs232'; + JvxComponentIdentification cpTpHtTech = widgetConfigCpId( + entriesCfgWidgets, + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_CUSTOM_TECHNOLOGY, + slotid: 0, + slotsubid: 0), + 'headtrackrs232t'); + JvxComponentIdentification cpTpHtDev = widgetConfigCpId( + entriesCfgWidgets, + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_CUSTOM_DEVICE, + slotid: cpTpHtTech.slotid, + slotsubid: 0), + 'headtrackrs232d'); + ; + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Tooltip( + message: '${cpTpHtTech.txt}--${cpTpHtTech.txt}', + child: Column(children: [ + Image.asset(imageNameHeadTracker, + filterQuality: FilterQuality.medium, + //'images/fernlive_icons/fernlive-mixer.png', + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(textAudioIoHt) + ])))); + theAllocatedTabViews.add(AudYoFloAudioSettingsWidget( + cpTpHtTech, + 'Head Tracker Technologies', + cpTpHtDev, + 'Rs232 Head Tracker Devices', + 'headtracker', + (JvxComponentIdentification selCpId) => null, + allocateHeadtrackerDeviceSliver)); + + // ====================================================================== + // Add the headtracker device widget [ART-UDP] + // ====================================================================== + textAudioIoHt = 'ART Tracker'; + cpTpHtTech = widgetConfigCpId( + entriesCfgWidgets, + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_CUSTOM_TECHNOLOGY, + slotid: 1, + slotsubid: 0), + 'headtrackartt'); + cpTpHtDev = widgetConfigCpId( + entriesCfgWidgets, + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_CUSTOM_DEVICE, + slotid: cpTpHtTech.slotid, + slotsubid: 0), + 'headtrackartd'); + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Tooltip( + message: '${cpTpHtTech.txt}--${cpTpHtTech.txt}', + child: Column(children: [ + Image.asset(imageNameHeadTracker, + filterQuality: FilterQuality.medium, + //'images/fernlive_icons/fernlive-mixer.png', + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(textAudioIoHt) + ])))); + theAllocatedTabViews.add(AudYoFloAudioSettingsWidget( + cpTpHtTech, + 'Head Tracker Technologies', + cpTpHtDev, + 'ART Head Tracker Devices', + 'headtracker', + (JvxComponentIdentification selCpId) => null, + allocateHeadtrackerDeviceSliver)); + + // ====================================================================== + // Add the file input widget + // ====================================================================== + String imageNameFileIO = + 'packages/ayf_pack/images/ayf_icons/ayf-file-input.png'; + String textFileIo = 'File Input'; + /* + JvxComponentIdentification cpTpFIoTech = JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_TECHNOLOGY, + slotid: 1, + slotsubid: 0); + JvxComponentIdentification cpTpFIoDev = JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_DEVICE, + slotid: 1, + slotsubid: 0); + */ + theAllocatedTabs.add(RotatedBox( + quarterTurns: -1, + child: Column(children: [ + Image.asset(imageNameFileIO, + filterQuality: FilterQuality.medium, + //'images/fernlive_icons/fernlive-mixer.png', + width: navButtonSizeMain, + height: navButtonSizeMain), + Text(textFileIo) + ]))); + theAllocatedTabViews.add(AudYoFloFileInputControlWidget( + JvxComponentIdentification( + cpTp: JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_TECHNOLOGY, + slotid: 1, + slotsubid: 0), + "Drag Input File Here", + )); + + /*AudYoFloAudioSettingsWidget( + cpTpFIoTech, + 'File I/O Technologies', + cpTpFIoDev, + 'File I/O Devices', + 'headtracker', + (JvxComponentIdentification selCpId) => null, + allocateHeadtrackerDeviceSliver));*/ + + // Here, the base class function is called. This call allows the base class + // to attach the "standard widgets" debug and components. If you do not call these functions + // the debug widgets will not be viisible. + super.createTabsAndViews(entriesCfgWidgets); + } +} diff --git a/flutter/ayf_webhost/example/pubspec.yaml b/flutter/ayf_webhost/example/pubspec.yaml new file mode 100644 index 00000000..d4b16af1 --- /dev/null +++ b/flutter/ayf_webhost/example/pubspec.yaml @@ -0,0 +1,102 @@ +name: ayf_pack_example +description: Demonstrates how to use the ayf_pack plugin. + +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +environment: + sdk: ">=2.18.0 <3.0.0" + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + ayf_pack: + path: ../../ayf_pack + + ayfbinrender: + path: ../../ayfbinrender/ayfbinrender + + ayf_webhost: + path: '..' + # path: '../../ayf_ffihost' + + #ayf_webhost: + # path: '../../ayf_webhost' + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + + # It seems that this here does not work on platform windows + flutter_native_splash: ^2.2.18 + +# Run: +# flutter clean +# flutter pub get +# flutter pub run flutter_native_splash:create +flutter_native_splash: + color: "#ff0000" + image: images/ayf_splash/splash.png + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + assets: + - images/ayfbinrender_icons/ayfbinrender-logo.png + - .ayf_webhost_example.env + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/flutter/ayf_webhost/example/test/widget_test.dart b/flutter/ayf_webhost/example/test/widget_test.dart new file mode 100644 index 00000000..4e017de0 --- /dev/null +++ b/flutter/ayf_webhost/example/test/widget_test.dart @@ -0,0 +1,33 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:ayf_pack_example/main.dart'; + +void main() { + print('No Test implemented at this point!'); + /* + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); + */ +} diff --git a/flutter/ayf_webhost/example/web/favicon.png b/flutter/ayf_webhost/example/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/flutter/ayf_webhost/example/web/icons/Icon-192.png b/flutter/ayf_webhost/example/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/flutter/ayf_webhost/example/web/icons/Icon-512.png b/flutter/ayf_webhost/example/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 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 literal 0 HcmV?d00001 diff --git a/flutter/ayf_webhost/example/web/icons/Icon-maskable-192.png b/flutter/ayf_webhost/example/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9b4d76e525556d5d89141648c724331630325d GIT binary patch literal 5594 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 X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! literal 0 HcmV?d00001 diff --git a/flutter/ayf_webhost/example/web/icons/Icon-maskable-512.png b/flutter/ayf_webhost/example/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..d69c56691fbdb0b7efa65097c7cc1edac12a6d3e GIT binary patch literal 20998 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 MPgg&ebxsLQ025IeI{*Lx literal 0 HcmV?d00001 diff --git a/flutter/ayf_webhost/example/web/index.html b/flutter/ayf_webhost/example/web/index.html new file mode 100644 index 00000000..41b3bc33 --- /dev/null +++ b/flutter/ayf_webhost/example/web/index.html @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + example + + + + + + + + + + diff --git a/flutter/ayf_webhost/example/web/manifest.json b/flutter/ayf_webhost/example/web/manifest.json new file mode 100644 index 00000000..096edf8f --- /dev/null +++ b/flutter/ayf_webhost/example/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "example", + "short_name": "example", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/flutter/ayf_webhost/example/windows/.gitignore b/flutter/ayf_webhost/example/windows/.gitignore new file mode 100644 index 00000000..d492d0d9 --- /dev/null +++ b/flutter/ayf_webhost/example/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/flutter/ayf_webhost/example/windows/CMakeLists.txt b/flutter/ayf_webhost/example/windows/CMakeLists.txt new file mode 100644 index 00000000..a6535773 --- /dev/null +++ b/flutter/ayf_webhost/example/windows/CMakeLists.txt @@ -0,0 +1,101 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(ayf_webhost_example LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "ayf_webhost_example") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Define build configuration option. +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() +# Define settings for the Profile build mode. +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. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +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() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +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/flutter/ayf_webhost/example/windows/flutter/CMakeLists.txt b/flutter/ayf_webhost/example/windows/flutter/CMakeLists.txt new file mode 100644 index 00000000..930d2071 --- /dev/null +++ b/flutter/ayf_webhost/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,104 @@ +# This file controls Flutter-level build steps. It should not be edited. +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/flutter/ayf_webhost/example/windows/runner/CMakeLists.txt b/flutter/ayf_webhost/example/windows/runner/CMakeLists.txt new file mode 100644 index 00000000..b9e550fb --- /dev/null +++ b/flutter/ayf_webhost/example/windows/runner/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +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 the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/flutter/ayf_webhost/example/windows/runner/Runner.rc b/flutter/ayf_webhost/example/windows/runner/Runner.rc new file mode 100644 index 00000000..aecaa2b5 --- /dev/null +++ b/flutter/ayf_webhost/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 +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#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", "example" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2023 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "example.exe" "\0" + VALUE "ProductName", "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/flutter/ayf_webhost/example/windows/runner/flutter_window.cpp b/flutter/ayf_webhost/example/windows/runner/flutter_window.cpp new file mode 100644 index 00000000..b43b9095 --- /dev/null +++ b/flutter/ayf_webhost/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/flutter/ayf_webhost/example/windows/runner/flutter_window.h b/flutter/ayf_webhost/example/windows/runner/flutter_window.h new file mode 100644 index 00000000..6da0652f --- /dev/null +++ b/flutter/ayf_webhost/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/flutter/ayf_webhost/example/windows/runner/main.cpp b/flutter/ayf_webhost/example/windows/runner/main.cpp new file mode 100644 index 00000000..ea92aa2c --- /dev/null +++ b/flutter/ayf_webhost/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"ayf_pack_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/flutter/ayf_webhost/example/windows/runner/resource.h b/flutter/ayf_webhost/example/windows/runner/resource.h new file mode 100644 index 00000000..66a65d1e --- /dev/null +++ b/flutter/ayf_webhost/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/flutter/ayf_webhost/example/windows/runner/resources/app_icon.ico b/flutter/ayf_webhost/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/flutter/ayf_webhost/example/windows/runner/runner.exe.manifest b/flutter/ayf_webhost/example/windows/runner/runner.exe.manifest new file mode 100644 index 00000000..a42ea768 --- /dev/null +++ b/flutter/ayf_webhost/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/flutter/ayf_webhost/example/windows/runner/utils.cpp b/flutter/ayf_webhost/example/windows/runner/utils.cpp new file mode 100644 index 00000000..f5bf9fa0 --- /dev/null +++ b/flutter/ayf_webhost/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); + std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return 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/flutter/ayf_webhost/example/windows/runner/utils.h b/flutter/ayf_webhost/example/windows/runner/utils.h new file mode 100644 index 00000000..3879d547 --- /dev/null +++ b/flutter/ayf_webhost/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/flutter/ayf_webhost/example/windows/runner/win32_window.cpp b/flutter/ayf_webhost/example/windows/runner/win32_window.cpp new file mode 100644 index 00000000..c10f08dc --- /dev/null +++ b/flutter/ayf_webhost/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/flutter/ayf_webhost/example/windows/runner/win32_window.h b/flutter/ayf_webhost/example/windows/runner/win32_window.h new file mode 100644 index 00000000..17ba4311 --- /dev/null +++ b/flutter/ayf_webhost/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/flutter/ayf_webhost/launch.json.in b/flutter/ayf_webhost/launch.json.in new file mode 100644 index 00000000..421ce19e --- /dev/null +++ b/flutter/ayf_webhost/launch.json.in @@ -0,0 +1,35 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "ayfbinrender-web (debug-noargs)", + "request": "launch", + "type": "dart", + "program": "example/lib/main.dart", + "deviceId": "chrome", + + "args": [], + "flutterMode": "debug", + }, + { + "name": "ayfbinrender-win (debug-args)", + "request": "launch", + "type": "dart", + "program": "example/lib/main.dart", + "deviceId": "windows", + + "args": [ + // Make sure that the sofa folder is in the runtime or the sdk folder - it often is the SDK output folder!! + "--dart-entrypoint-args", "--httpAutoConnect", + "--dart-entrypoint-args", "--httpIpAddr", + "--dart-entrypoint-args", "127.0.0.1", + "--dart-entrypoint-args", "--httpIpPort", + "--dart-entrypoint-args", "8000", + ], + "flutterMode": "debug", + } + ] +} \ No newline at end of file diff --git a/flutter/ayf_webhost/lib/ayf_web_backend_constants.dart b/flutter/ayf_webhost/lib/ayf_web_backend_constants.dart new file mode 100644 index 00000000..0e21b85e --- /dev/null +++ b/flutter/ayf_webhost/lib/ayf_web_backend_constants.dart @@ -0,0 +1,11 @@ +enum jvxWebHostOperationResponseType { + JVX_RESPONSE_INIT_CONNECT, + JVX_MESSAGE_WEBSOCKET +} + +enum jvxWebHostConnectionState { + JVX_CONECTION_NONE, + JVX_CONECTION_FIRST_CONTACT_DONE, + JVX_CONECTION_WEBSOCKET_ON, + JVX_CONECTION_FULL_STATE_TRANSMITTED_OK +} diff --git a/flutter/ayf_webhost/lib/ayf_web_backend_helpers.dart b/flutter/ayf_webhost/lib/ayf_web_backend_helpers.dart new file mode 100644 index 00000000..48637134 --- /dev/null +++ b/flutter/ayf_webhost/lib/ayf_web_backend_helpers.dart @@ -0,0 +1,439 @@ +import 'dart:convert'; + +import 'package:ayf_pack/ayf_pack.dart'; +import 'backend/ayf_web_backend_anomalies.dart'; + +import 'package:http/http.dart' as http; +import 'dart:io'; +import 'dart:async'; + +class AudYoFloUrlRequest { + Map jsonMap = {}; + String lastError = ''; +} + +// =========================================================== + +class AudYoFloWebBackendHelpers { + AudYoFloWebBackendAnomalies? reportHandler; + void initializeHelper(AudYoFloWebBackendAnomalies reportRef) { + reportHandler = reportRef; + } + + Future webRequestHttpGet( + Uri url, AudYoFloUrlRequest resultOnReturn) async { + int retCode = jvxErrorType.JVX_NO_ERROR; + resultOnReturn.lastError = ''; + try { + var response = await http.get(url); + if (response.statusCode == 200) { + String responseBody = response.body; + if (responseBody.isNotEmpty) { + resultOnReturn.jsonMap = jsonDecode(responseBody); + } else { + resultOnReturn.jsonMap = {}; + } + } + } on TimeoutException catch (e) { + resultOnReturn.lastError = 'Timeout Error: $e'; + retCode = jvxErrorType.JVX_ERROR_TIMEOUT; + } on SocketException catch (e) { + resultOnReturn.lastError = 'Socket Error: $e'; + retCode = jvxErrorType.JVX_ERROR_PROTOCOL_ERROR; + } on Error catch (e) { + resultOnReturn.lastError = 'General Error: $e'; + retCode = jvxErrorType.JVX_ERROR_CALL_SUB_COMPONENT_FAILED; + } on http.ClientException catch (e) { + resultOnReturn.lastError = 'Client Exception Error: $e'; + retCode = jvxErrorType.JVX_ERROR_CALL_SUB_COMPONENT_FAILED; + } + + if (retCode == jvxErrorType.JVX_NO_ERROR) { + String? errCodeExprPtr = + getStringEntryValueMap(resultOnReturn.jsonMap, 'return_code'); + if (errCodeExprPtr != null) { + // Convert error code + String errCodeExpr = errCodeExprPtr; + retCode = jvxErrorTypeEInt.fromStringSingle(errCodeExpr); + } + + if (retCode != jvxErrorType.JVX_NO_ERROR) { + resultOnReturn.lastError = 'Json response returned ' + + jvxErrorTypeEInt.toStringSingle(retCode); + } + } + return retCode; + } + + Future webRequestHttpPost( + Uri url, String param, AudYoFloUrlRequest resultOnReturn) async { + int retCode = jvxErrorType.JVX_NO_ERROR; + resultOnReturn.lastError = ''; + try { + var response = await http.post(url, body: param); + if (response.statusCode == 200) { + String responseBody = response.body; + resultOnReturn.jsonMap = jsonDecode(responseBody); + } + } on TimeoutException catch (e) { + resultOnReturn.lastError = 'Timeout Error: $e'; + retCode = jvxErrorType.JVX_ERROR_TIMEOUT; + } on SocketException catch (e) { + resultOnReturn.lastError = 'Socket Error: $e'; + retCode = jvxErrorType.JVX_ERROR_PROTOCOL_ERROR; + } on Error catch (e) { + resultOnReturn.lastError = 'General Error: $e'; + retCode = jvxErrorType.JVX_ERROR_CALL_SUB_COMPONENT_FAILED; + } on http.ClientException catch (e) { + resultOnReturn.lastError = 'Client Exception Error: $e'; + retCode = jvxErrorType.JVX_ERROR_CALL_SUB_COMPONENT_FAILED; + } + + if (retCode == jvxErrorType.JVX_NO_ERROR) { + String? errCodeExprPtr = + getStringEntryValueMap(resultOnReturn.jsonMap, 'return_code'); + if (errCodeExprPtr != null) { + // Convert error code + String errCodeExpr = errCodeExprPtr; + retCode = jvxErrorTypeEInt.fromStringSingle(errCodeExpr); + } + + if (retCode != jvxErrorType.JVX_NO_ERROR) { + resultOnReturn.lastError = 'Json response returned ' + + jvxErrorTypeEInt.toStringSingle(retCode); + } + } + return retCode; + } + + JvxComponentTypeEnum translateStringComponentType(String inStr) { + switch (inStr) { + case "signal processing technology": + return JvxComponentTypeEnum.JVX_COMPONENT_SIGNAL_PROCESSING_TECHNOLOGY; + case "signal processing device": + return JvxComponentTypeEnum.JVX_COMPONENT_SIGNAL_PROCESSING_DEVICE; + case "signal processing node": + return JvxComponentTypeEnum.JVX_COMPONENT_SIGNAL_PROCESSING_NODE; + case "audio technology": + return JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_TECHNOLOGY; + case "audio device": + return JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_DEVICE; + case "audio node": + return JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_NODE; + case "video technology": + return JvxComponentTypeEnum.JVX_COMPONENT_VIDEO_TECHNOLOGY; + case "video device": + return JvxComponentTypeEnum.JVX_COMPONENT_VIDEO_DEVICE; + case "video node": + return JvxComponentTypeEnum.JVX_COMPONENT_VIDEO_NODE; + case "application controller technology": + return JvxComponentTypeEnum + .JVX_COMPONENT_APPLICATION_CONTROLLER_TECHNOLOGY; + case "application controller device": + return JvxComponentTypeEnum.JVX_COMPONENT_APPLICATION_CONTROLLER_DEVICE; + case "application controller node": + return JvxComponentTypeEnum.JVX_COMPONENT_APPLICATION_CONTROLLER_NODE; + case "custom technology": + return JvxComponentTypeEnum.JVX_COMPONENT_CUSTOM_TECHNOLOGY; + case "custom device": + return JvxComponentTypeEnum.JVX_COMPONENT_CUSTOM_DEVICE; + case "custom node": + return JvxComponentTypeEnum.JVX_COMPONENT_CUSTOM_NODE; + case "extern technology": + return JvxComponentTypeEnum.JVX_COMPONENT_EXTERNAL_TECHNOLOGY; + case "extern device": + return JvxComponentTypeEnum.JVX_COMPONENT_EXTERNAL_DEVICE; + case "extern node": + return JvxComponentTypeEnum.JVX_COMPONENT_EXTERNAL_NODE; + case "package": + return JvxComponentTypeEnum.JVX_COMPONENT_PACKAGE; + case "min host": + return JvxComponentTypeEnum.JVX_COMPONENT_MIN_HOST; + case "factory host": + return JvxComponentTypeEnum.JVX_COMPONENT_FACTORY_HOST; + case "host": + return JvxComponentTypeEnum.JVX_COMPONENT_HOST; + + case "packet filter rule": + return JvxComponentTypeEnum.JVX_COMPONENT_PACKETFILTER_RULE; + case "log remote handler": + return JvxComponentTypeEnum.JVX_COMPONENT_LOGREMOTEHANDLER; + case "audio codec": + return JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_CODEC; + case "audio encoder": + return JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_ENCODER; + case "audio decoder": + return JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_DECODER; + case "audio file reader": + return JvxComponentTypeEnum.JVX_COMPONENT_RT_AUDIO_FILE_READER; + case "audio file writer": + return JvxComponentTypeEnum.JVX_COMPONENT_RT_AUDIO_FILE_WRITER; + case "resampler": + return JvxComponentTypeEnum.JVX_COMPONENT_RESAMPLER; + case "data converter": + return JvxComponentTypeEnum.JVX_COMPONENT_DATACONVERTER; + case "processing group": + return JvxComponentTypeEnum.JVX_COMPONENT_PROCESSING_GROUP; + case "processing process": + return JvxComponentTypeEnum.JVX_COMPONENT_PROCESSING_PROCESS; + case "off host": + return JvxComponentTypeEnum.JVX_COMPONENT_OFF_HOST; + case "interceptor": + return JvxComponentTypeEnum.JVX_COMPONENT_INTERCEPTOR; + case "automation": + return JvxComponentTypeEnum.JVX_COMPONENT_SYSTEM_AUTOMATION; + + case "encryption/decryption": + return JvxComponentTypeEnum.JVX_COMPONENT_CRYPT; + case "web server": + return JvxComponentTypeEnum.JVX_COMPONENT_WEBSERVER; + case "remote call": + return JvxComponentTypeEnum.JVX_COMPONENT_REMOTE_CALL; + case "event loop": + return JvxComponentTypeEnum.JVX_COMPONENT_EVENTLOOP; + case "data logger": + return JvxComponentTypeEnum.JVX_COMPONENT_DATALOGGER; + case "data log reader": + return JvxComponentTypeEnum.JVX_COMPONENT_DATALOGREADER; + case "connection": + return JvxComponentTypeEnum.JVX_COMPONENT_CONNECTION; + case "config processor": + return JvxComponentTypeEnum.JVX_COMPONENT_CONFIG_PROCESSOR; + case "external call": + return JvxComponentTypeEnum.JVX_COMPONENT_EXTERNAL_CALL; + case "thread controller": + return JvxComponentTypeEnum.JVX_COMPONENT_THREADCONTROLLER; + case "threads": + return JvxComponentTypeEnum.JVX_COMPONENT_THREADS; + case "shared mem": + return JvxComponentTypeEnum.JVX_COMPONENT_SHARED_MEMORY; + case "text2speech": + return JvxComponentTypeEnum.JVX_COMPONENT_TEXT2SPEECH; + case "textlog": + return JvxComponentTypeEnum.JVX_COMPONENT_SYSTEM_TEXT_LOG; + case "localtextlog": + return JvxComponentTypeEnum.JVX_COMPONENT_LOCAL_TEXT_LOG; + default: + if (reportHandler != null) { + reportHandler!.reportStatusErrorProtocol(1, 0, inStr); + } + break; + /* + case : + return JvxComponentTypeEnum.; + {"event loop", "JVX_COMPONENT_EVENTLOOP" }, + case : + return JvxComponentTypeEnum.; + {"data logger", "JVX_COMPONENT_DATALOGGER" }, + case : + return JvxComponentTypeEnum.; + {"data log reader", "JVX_COMPONENT_DATALOGREADER" }, + case : + return JvxComponentTypeEnum.; + {"connection", "JVX_COMPONENT_CONNECTION" }, + case : + return JvxComponentTypeEnum.; + {"config processor", "JVX_COMPONENT_CONFIG_PROCESSOR" }, + case : + return JvxComponentTypeEnum.; + {"external call", "JVX_COMPONENT_EXTERNAL_CALL" }, + case : + return JvxComponentTypeEnum.; + {"thread controller", "JVX_COMPONENT_THREADCONTROLLER" }, + case : + return JvxComponentTypeEnum.; + {"threads", "JVX_COMPONENT_THREADS"}, + case : + return JvxComponentTypeEnum.; + {"shared mem", "JVX_COMPONENT_SHARED_MEMORY"}, + { "text2speech", "JVX_COMPONENT_TEXT2SPEECH" }, + { "textlog", "JVX_COMPONENT_SYSTEM_TEXT_LOG" }, + default: + assert(false); + } +} + + { + { "localtextlog", "JVX_COMPONENT_LOCAL_TEXT_LOG" }, + {"encryption/decryption", "JVX_COMPONENT_CRYPT" }, + {"web server", "JVX_COMPONENT_WEBSERVER" }, + {"remote call", "JVX_COMPONENT_REMOTE_CALL" }, + {"packet filter rule", "JVX_COMPONENT_PACKETFILTER_RULE" }, + {"log remote handler", "JVX_COMPONENT_LOGREMOTEHANDLER" } + */ + } + return JvxComponentTypeEnum.JVX_COMPONENT_UNKNOWN; + } + + jvxComponentTypeClassEnum translateStringComponentTypeClass(String inStr) { + switch (inStr) { + case "technologies": + return jvxComponentTypeClassEnum.JVX_COMPONENT_TYPE_TECHNOLOGY; + case "nodes": + return jvxComponentTypeClassEnum.JVX_COMPONENT_TYPE_NODE; + case "hosts": + return jvxComponentTypeClassEnum.JVX_COMPONENT_TYPE_HOST; + case "codecs": + return jvxComponentTypeClassEnum.JVX_COMPONENT_TYPE_CODEC; + case "tools": + return jvxComponentTypeClassEnum.JVX_COMPONENT_TYPE_TOOL; + case "simple": + return jvxComponentTypeClassEnum.JVX_COMPONENT_TYPE_SIMPLE; + case "device": + return jvxComponentTypeClassEnum.JVX_COMPONENT_TYPE_DEVICE; + case "process": + return jvxComponentTypeClassEnum.JVX_COMPONENT_TYPE_PROCESS; + } + return jvxComponentTypeClassEnum.JVX_COMPONENT_TYPE_NONE; + } + + JvxComponentIdentification translateStringComponentIdentification( + String cpIdStr, int uid) { + JvxComponentIdentification cpId = JvxComponentIdentification(); + var lst = cpIdStr.split('<'); + cpId.cpTp = translateStringComponentType(lst[0]); + if (cpId.cpTp != JvxComponentTypeEnum.JVX_COMPONENT_UNKNOWN) { + cpId.slotid = 0; + cpId.slotsubid = 0; + } + if (lst.length > 1) { + lst = lst[1].split('>'); + lst = lst[0].split(','); + cpId.slotid = int.parse(lst[0]); + if (lst.length > 1) { + cpId.slotsubid = int.parse(lst[1]); + } + } + + cpId.uid = uid; + return cpId; + } + + int translateStringState(state) { + return jvxStateEInt.fromStringSingle(state); + } + + String translateCompType(JvxComponentTypeEnum cpTp) { + String retVal = ''; + switch (cpTp) { + /* + case JvxComponentTypeEnum.XXX: + retVal = 'XXX'; + break; + */ + case JvxComponentTypeEnum.JVX_COMPONENT_SIGNAL_PROCESSING_TECHNOLOGY: + retVal = 'signal_processing_technology'; + break; + case JvxComponentTypeEnum.JVX_COMPONENT_SIGNAL_PROCESSING_DEVICE: + retVal = 'signal_processing_device'; + break; + case JvxComponentTypeEnum.JVX_COMPONENT_SIGNAL_PROCESSING_NODE: + retVal = 'signal_processing_node'; + break; + + case JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_TECHNOLOGY: + retVal = 'audio_technology'; + break; + case JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_DEVICE: + retVal = 'audio_device'; + break; + case JvxComponentTypeEnum.JVX_COMPONENT_AUDIO_NODE: + retVal = 'audio_node'; + break; + + case JvxComponentTypeEnum.JVX_COMPONENT_VIDEO_TECHNOLOGY: + retVal = 'video technology'; + break; + case JvxComponentTypeEnum.JVX_COMPONENT_VIDEO_DEVICE: + retVal = 'video_device'; + break; + case JvxComponentTypeEnum.JVX_COMPONENT_VIDEO_NODE: + retVal = 'video_node'; + break; + + case JvxComponentTypeEnum.JVX_COMPONENT_APPLICATION_CONTROLLER_TECHNOLOGY: + retVal = 'application_controller_technology'; + break; + case JvxComponentTypeEnum.JVX_COMPONENT_APPLICATION_CONTROLLER_DEVICE: + retVal = 'application_controller_device'; + break; + case JvxComponentTypeEnum.JVX_COMPONENT_APPLICATION_CONTROLLER_NODE: + retVal = 'application_controller_node'; + break; + + case JvxComponentTypeEnum.JVX_COMPONENT_CUSTOM_TECHNOLOGY: + retVal = 'custom_technology'; + break; + case JvxComponentTypeEnum.JVX_COMPONENT_CUSTOM_DEVICE: + retVal = 'custom_device'; + break; + case JvxComponentTypeEnum.JVX_COMPONENT_CUSTOM_NODE: + retVal = 'custom_node'; + break; + + case JvxComponentTypeEnum.JVX_COMPONENT_EXTERNAL_TECHNOLOGY: + retVal = 'extern_technology'; + break; + case JvxComponentTypeEnum.JVX_COMPONENT_EXTERNAL_DEVICE: + retVal = 'extern_device'; + break; + case JvxComponentTypeEnum.JVX_COMPONENT_EXTERNAL_NODE: + retVal = 'extern_node'; + break; + + case JvxComponentTypeEnum.JVX_COMPONENT_PACKAGE: + retVal = 'component_package'; + break; + + case JvxComponentTypeEnum.JVX_COMPONENT_MIN_HOST: + retVal = 'min_host'; + break; + case JvxComponentTypeEnum.JVX_COMPONENT_FACTORY_HOST: + retVal = 'factory_host'; + break; + case JvxComponentTypeEnum.JVX_COMPONENT_HOST: + retVal = 'host'; + break; + + /* + case JvxComponentTypeEnum.JVX_COMPONENT_EVENTLOOP: + retVal = 'event_loop'; + break; + + case JvxComponentTypeEnum.JVX_COMPONENT_DATALOGGER: + retVal = 'datalogger'; + break; + case JvxComponentTypeEnum.JVX_COMPONENT_DATALOGREADER: + retVal = 'datalogreader'; + break; + + case JvxComponentTypeEnum.JVX_COMPONENT_CONNECTION: + retVal = 'connection'; + break; + JVX_COMPONENT_CONFIG_PROCESSOR, + JVX_COMPONENT_EXTERNAL_CALL, + JVX_COMPONENT_THREADCONTROLLER, + JVX_COMPONENT_THREADS, + JVX_COMPONENT_SHARED_MEMORY, + JVX_COMPONENT_TEXT2SPEECH, + JVX_COMPONENT_SYSTEM_TEXT_LOG, + JVX_COMPONENT_LOCAL_TEXT_LOG, + JVX_COMPONENT_CRYPT, + JVX_COMPONENT_WEBSERVER, + JVX_COMPONENT_REMOTE_CALL, + JVX_COMPONENT_PACKETFILTER_RULE, + JVX_COMPONENT_LOGREMOTEHANDLER, + */ + default: + assert(false); + } + return retVal; + } + + // @override is not correct but this mixin implements interface + int translateEnumString( + String selection, String formatName, AudYoFloCompileFlags flags) { + return AudYoFloStringTranslator.translateEnumString( + selection, formatName, flags); + } +} diff --git a/flutter/ayf_webhost/lib/ayf_web_backend_host.dart b/flutter/ayf_webhost/lib/ayf_web_backend_host.dart new file mode 100644 index 00000000..2d331ff8 --- /dev/null +++ b/flutter/ayf_webhost/lib/ayf_web_backend_host.dart @@ -0,0 +1,6 @@ +library ayf_webhost; + +import 'package:ayf_pack/ayf_pack.dart'; +import 'backend/ayf_web_backend_adapter.dart'; + +AyfHost get ayfhost => AyfHtmlAudioHost.instance; diff --git a/flutter/ayf_webhost/lib/ayf_web_backend_typedefs.dart b/flutter/ayf_webhost/lib/ayf_web_backend_typedefs.dart new file mode 100644 index 00000000..da4fb055 --- /dev/null +++ b/flutter/ayf_webhost/lib/ayf_web_backend_typedefs.dart @@ -0,0 +1,3 @@ +class ExtractedFromJson { + String ctxt = ''; +} diff --git a/flutter/ayf_webhost/lib/backend/ayf_web_backend_adapter.dart b/flutter/ayf_webhost/lib/backend/ayf_web_backend_adapter.dart new file mode 100644 index 00000000..e90f3318 --- /dev/null +++ b/flutter/ayf_webhost/lib/backend/ayf_web_backend_adapter.dart @@ -0,0 +1,1199 @@ +//import 'dart:html'; +import 'package:fixnum/fixnum.dart' as fn; +import 'package:flutter/cupertino.dart'; +import 'dart:convert'; +import 'package:collection/collection.dart'; +import 'package:ayf_pack/ayf_pack.dart'; +import 'package:http/http.dart' as http; +import '../ayf_web_backend_constants.dart'; +import '../ayf_web_backend_typedefs.dart'; +import 'ayf_web_backend_message_processor.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; + +import '../ayf_web_backend_helpers.dart'; +import './ayf_web_backend_anomalies.dart'; + +import 'dart:async'; +import 'dart:io'; + +// =========================================================================== +// =========================================================================== +// =========================================================================== +class AyfHtmlAudioHost extends AyfHost { + // Allocate the audio host with instance Native Audio Host + static AyfHost _instance = AyfHtmlAudioHost(); + static AyfHost get instance => _instance; + + List cmdArgs = []; + @override + String descriptionHost() { + return 'AyfHtmlAudioHost'; + } + + @override + Future initialize(AudYoFloBackendCache beCache, + AudYoFloDebugModel? dbgModel, Map args) { + String addrTo = '127.0.0.1'; + int portId = 80; + var elm = + args.entries.firstWhereOrNull((element) => element.key == 'cmdArgs'); + if (elm != null) { + if (elm.value is List) { + cmdArgs = elm.value as List; + } + for (var idx = 0; idx < cmdArgs.length; idx++) { + if (cmdArgs[idx] == '--connect-addr') { + idx++; + if (idx < cmdArgs.length) { + addrTo = cmdArgs[idx]; + } + continue; + } + if (cmdArgs[idx] == '--connect-port') { + idx++; + if (idx < cmdArgs.length) { + int? portIdScan = int.tryParse(cmdArgs[idx]); + if (portIdScan != null) { + portId = portIdScan; + } + } + continue; + } + } + } + return _adapter.initialize(beCache, dbgModel, addrTo, portId); + } + // _bridge.initialize( + // cmdArgsLst, beCache, dbgModel, libraryPath, executableInBinFolder); + + @override + void terminate() => _adapter.terminate(); + void periodicTriggerSlow() => _adapter.periodic_trigger_slow(); + + // Hold the global instance + AudYoFloBackendAdapterWeb _adapter = AudYoFloBackendAdapterWeb(); + // AudioFlowBackendBridge_ get beBridge => _instance; + AudYoFloCompileFlags get compFlags => _adapter.compileFlags(); + + @override + String get lastError => _adapter.latestError; +} + +typedef Future shutdownFunction(); + +class AudYoFloBackendAdapterWeb extends AudYoFloBackendAdapterIf + with + AudYoFloWebStateProcessor, + AudYoFloWebBackendAnomalies, + AudYoFloWebBackendHelpers { + // ======================================================================== + // Variables + // ======================================================================== + + // ============================================================== + int cfg_gran_state_report = 5; + + shutdownFunction? shutdown; + + // Retain a list of deferred actions to be run on certain conditions, e.g., + // to trigger shutdown of the app. + List queuedOperations = []; + + ayfBackendAccessEnum accBackend = ayfBackendAccessEnum.JVX_ACCESS_BACKEND_FFI; + //ayfBackendAccessEnum accBackend = + // ayfBackendAccessEnum.JVX_ACCESS_BACKEND_JSON; + String latestError = ''; + + Completer? completeWebSocketTransaction; + + WebSocketChannel? channel; + + int msgId = 1; + + String contextPending = ''; + + String httpTarget = ''; + String wsTarget = ''; + + AudYoFloDebugModel? dbgModel; + bool dbgOut = true; + AudYoFloBackendCache? theBeCache; + // ============================================================== + // ======================================================================== + // ======================================================================== + + // ======================================================================== + // Coupling to host functions + // ======================================================================== + void periodic_trigger_slow() {} + + void terminate() {} + + Future initialize(AudYoFloBackendCache beCache, + AudYoFloDebugModel? dbgModelArg, String addrTo, int portId) async { + bool retVal = false; + latestError = ''; + + /* + String val = "{\"req\": \"cmdreq-rep-seq-evt\", \"type\": \"cmdreqtp-seq\"" + ", \"origin\": " + "{" + "\"component_identification\": \"unknown\", " + "\"uid\": -1" + "}, " + "\"specific\": " + "{" + "\"seqev-descr\": \"Startup of sequencer thread successfully completed\", " + "\"seqev-qtp\": \"queue_type_none\", " + "\"seqev-etp\": \"none\", " + "\"seqev-stpid\": -1, " + "\"seqev-seqid\": -1, " + "\"seqev-stpfid\": -1, " + "\"seqev-ferror\": \"yes\", " + "\"seqev-state\": 0" + "}" + "}"; + Map mm = jsonDecode(val); + */ + + dbgModel = dbgModelArg; + theBeCache = beCache; + + // Here, we start the http connection + theBeCache!.initializeWithBackendAdapterReference(this); + + // Assign backend cache in base class - + // initializeWithBackendCacheReference(beCache); + + // Set parent, report and helper reference + initializeWebProcessor(this, this, this); + + // Set + initializeHelper(this); + + // Link the debug model + dbgModel?.initialize(beCache); + + // Start HTML connection and web socket + // First command: curl 127.0.0.1:8000 + httpTarget = addrTo + ':' + portId.toString(); + String address = '/jvx/host/system/version'; + var url = Uri.http(httpTarget, address); + + AudYoFloUrlRequest urlRequest = AudYoFloUrlRequest(); + int errCode = await webRequestHttpGet(url, urlRequest); + latestError = urlRequest.lastError; + if (errCode == jvxErrorType.JVX_NO_ERROR) { + retVal = await processResponseState( + jvxWebHostOperationResponseType.JVX_RESPONSE_INIT_CONNECT, + urlRequest.jsonMap, + null); + } else { + retVal = false; + } + + if (retVal == true) { + if (connectState == + jvxWebHostConnectionState.JVX_CONECTION_FIRST_CONTACT_DONE) { + // Start the Websocket connection + // Web socket connect + wsTarget = 'ws://' + addrTo + ':' + portId.toString(); + address = '/jvx/host/ws/'; + try { + channel = WebSocketChannel.connect(Uri.parse(wsTarget + address)); + } catch (exc) { + print('$exc'); + } + + channel!.stream.listen(onMessage, + onDone: onConnectionDone, onError: onConnectionError); + + /* + * Create your own Future to map callbacks into await statements: + * https://api.dart.dev/stable/2.10.5/dart-async/Completer-class.html + * */ + contextPending = 'AYF_WS_HOST_CONNECT:' + msgId.toString(); + msgId++; + + String handshake = 'AYF_WS_HOST_CONNECT'; + if (contextPending.isNotEmpty) { + handshake += '#' + contextPending; + } + + completeWebSocketTransaction = Completer(); + channel!.sink.add(handshake); + } + await completeWebSocketTransaction!.future; + } + return retVal; + } + + @override + void reportStatusDisconnect(int idReason, int subId, String description) { + super.reportStatusDisconnect(idReason, subId, description); + latestError = 'Websocket was diconnected.'; + if (theBeCache!.requestSystemDisconnect != null) { + theBeCache!.requestSystemDisconnect!.reportEvent(latestError); + } + } + + void onMessage(dynamic body) async { + String retBody = body; + ExtractedFromJson extracted = ExtractedFromJson(); + // print('onMessage: $retBody'); + + var jsonMap = jsonDecode(retBody); + await processResponseState( + jvxWebHostOperationResponseType.JVX_MESSAGE_WEBSOCKET, + jsonMap, + extracted); + + if (extracted.ctxt.isNotEmpty) { + if (extracted.ctxt == contextPending) { + if (!completeWebSocketTransaction!.isCompleted) { + completeWebSocketTransaction!.complete(null); + } + } + } + } + + void onConnectionDone() { + print( + 'Ws connection reports a disconnect. Informing app to switch to disconnect status.'); + report!.reportStatusDisconnect(0, 0, 'Websocket disconnected'); + } + + void onConnectionError(error) { + print('Ws connection reports a connection error, reason: <$error>'); + report!.reportStatusDisconnect(1, 0, error); + } + + // ======================================================================== + // ======================================================================== + // ======================================================================== + + AudYoFloBackendAdapterWeb(); + + @override + AudYoFloCompileFlags compileFlags() { + return flags; + } + + // ========================================================= + @override + Future triggerStartSequencer() async { + // curl -d "" -X POST 127.0.0.1:8000/jvx/host/sequencer/start + + int retVal = jvxErrorType.JVX_NO_ERROR; + String cmd = '/jvx/host/sequencer/start'; + var url = Uri.http(httpTarget, cmd); + + AudYoFloUrlRequest urlRequest = AudYoFloUrlRequest(); + retVal = await webRequestHttpPost(url, '', urlRequest); + latestError = urlRequest.lastError; + if (retVal == jvxErrorType.JVX_NO_ERROR) { + retVal = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + String? errCodeExprPtr = + getStringEntryValueMap(urlRequest.jsonMap, 'return_code'); + if (errCodeExprPtr != null) { + // Convert error code + String errCodeExpr = errCodeExprPtr; + retVal = jvxErrorTypeEInt.fromStringSingle(errCodeExpr); + } + } + if (retVal != jvxErrorType.JVX_NO_ERROR) { + report!.reportStatusErrorProtocol( + 2, 0, jvxErrorTypeEInt.toStringSingle(retVal)); + } + return retVal; + } + + @override + Future triggerStopSequencer() async { + int retVal = jvxErrorType.JVX_NO_ERROR; + String cmd = '/jvx/host/sequencer/stop'; + var url = Uri.http(httpTarget, cmd); + + AudYoFloUrlRequest urlRequest = AudYoFloUrlRequest(); + retVal = await webRequestHttpPost(url, '', urlRequest); + latestError = urlRequest.lastError; + if (retVal == jvxErrorType.JVX_NO_ERROR) { + retVal = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + String? errCodeExprPtr = + getStringEntryValueMap(urlRequest.jsonMap, 'return_code'); + if (errCodeExprPtr != null) { + // Convert error code + String errCodeExpr = errCodeExprPtr; + retVal = jvxErrorTypeEInt.fromStringSingle(errCodeExpr); + } + } + if (retVal != jvxErrorType.JVX_NO_ERROR) { + report!.reportStatusErrorProtocol( + 3, 0, jvxErrorTypeEInt.toStringSingle(retVal)); + } + return retVal; + } + + @override + Future triggerToggleSequencer() async { + return await Future.delayed(Duration.zero, () async { + int errCode = jvxErrorType.JVX_NO_ERROR; + + // To be implemented! + assert(false); + return errCode; + }); + } + + @override + Future updateStatusSequencer() async { + jvxSequencerStatusEnum stat = + jvxSequencerStatusEnum.JVX_SEQUENCER_STATUS_NONE; + int retVal = jvxErrorType.JVX_NO_ERROR; + String cmd = '/jvx/host/sequencer/status'; + var url = Uri.http(httpTarget, cmd); + + AudYoFloUrlRequest urlRequest = AudYoFloUrlRequest(); + retVal = await webRequestHttpGet(url, urlRequest); + latestError = urlRequest.lastError; + if (retVal == jvxErrorType.JVX_NO_ERROR) { + retVal = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + String? errCodeExprPtr = + getStringEntryValueMap(urlRequest.jsonMap, 'return_code'); + if (errCodeExprPtr != null) { + // Convert error code + String errCodeExpr = errCodeExprPtr; + retVal = jvxErrorTypeEInt.fromStringSingle(errCodeExpr); + } + } + if (retVal != jvxErrorType.JVX_NO_ERROR) { + report!.reportStatusErrorProtocol( + 4, 0, jvxErrorTypeEInt.toStringSingle(retVal)); + } + return stat; + } + + // ============================================================ + + @override + Future transferTextCommand(String textArg, bool jsonReturn) async { + String retStr = ''; + int retVal = await Future.delayed(Duration.zero, () async { + int errCode = jvxErrorType.JVX_NO_ERROR; + + // To be implemented! + assert(false); + return errCode; + }); + + // if(retVal) // + return retStr; + } + + /* + * This function is called by the sequencer controller to acknowledge shutdown. + * We do not need this in web connection but it is used in native host + */ ///========================================================================= + @override + jvxSequencerStatusEnum acknowledgeSequencerStop() { + // This resets the sequencer status in the frontend. In native code, the acknowledge + // must be invoked here, in http, the acknowledge is done fully in the backend. + return jvxSequencerStatusEnum.JVX_SEQUENCER_STATUS_NONE; + } + + @override + AudYoFloCommandQueueElement postSequencerStop() { + // This function is called after the sequencer was terminated. We may + // add functional elements in this queue in the future. Currently, + // there is no function for this. + AudYoFloCommandQueueElement ret = + AudYoFloCommandQueueElement.AYF_COMMAND_QUEUE_NONE; + return ret; + } + + @override + AudYoFloOneConnectedProcess allocateProcess(int uId) { + var elm = pendingProcesses.entries + .firstWhereOrNull((element) => element.key == uId); + if (elm == null) { + assert(false); + } + + AudYoFloOneConnectedProcess retVal = elm!.value; + pendingProcesses.remove(uId); + return retVal; + } + + /* + @override + int processSeqEvent( + jvxSequencerEventType ev, jvxSequencerStatusEnum seqStat) { + return theBeCache!.reportSequencerEvent(ev, seqStat); + } +*/ + void addProcess(int uId) { + return theBeCache!.addProcess(uId); + } + + void remProcess(int uId) { + return theBeCache!.remProcess(uId); + } + + @override + String debugPrintProcess(int uId) { + return theBeCache!.debugPrintProcess(uId); + } + + Future triggerUpdateProcessStatus() async { + // -> processSystemReady + // -> reasonIfNot + // from (IjvxDataConnections*)->ready_for_start(astr); + // curl 127.0.0.1:8000/jvx/host/connections/ready + int retVal = jvxErrorType.JVX_NO_ERROR; + String cmd = '/jvx/host/connections/ready'; + var url = Uri.http(httpTarget, cmd); + + AudYoFloUrlRequest urlRequest = AudYoFloUrlRequest(); + retVal = await webRequestHttpGet(url, urlRequest); + latestError = urlRequest.lastError; + if (retVal == jvxErrorType.JVX_NO_ERROR) { + retVal = AudYoFloSequencerStatusFromJson.updateSequencerStatus( + urlRequest.jsonMap, theBeCache!); + } + + if (retVal != jvxErrorType.JVX_NO_ERROR) { + report!.reportStatusErrorProtocol( + 5, 0, jvxErrorTypeEInt.toStringSingle(retVal)); + } + return retVal; + } + + // ================================================================= + + // Translate component type enum to component class + @override + jvxComponentTypeClassEnum lookupComponentClass(JvxComponentTypeEnum tp) { + jvxComponentTypeClassEnum cpClass = + jvxComponentTypeClassEnum.JVX_COMPONENT_TYPE_NONE; + var elm = lookupClassTypes.entries + .firstWhereOrNull((element) => element.key == tp); + if (elm != null) { + cpClass = elm.value; + } else { + assert(false); + } + return cpClass; + } + + // Here is a good explanation of the async/await functionality: + // https://stackoverflow.com/questions/53249859/async-await-not-waiting-before-future-dart + @override + Future triggerUpdateAvailComponents(JvxComponentIdentification cpId, + List updateMe) async { + //List updateMe = []; + updateMe.clear(); + + // This is the core function call to update the status of a component + Future updateComponents = Future(() async { + // Convert the component identification into a string + int errCode = jvxErrorType.JVX_NO_ERROR; + String address = 'unknown'; + + // Generate a text command + String cmd = 'show(' + address + ');'; + int stat = jvxState.JVX_STATE_NONE; + + // Request command on backend (immediate emit) + String retText = await transferTextCommand(cmd, true); + + // Process the returned result + Map valueMap = json.decode(retText); + + // Extract the relevant information + MapEntry? entry_ecode = valueMap.entries + .firstWhereOrNull((element) => element.key == 'return_code'); + if (entry_ecode != null) { + errCode = jvxErrorTypeEInt.fromStringSingle(entry_ecode.value); + } + + // Next, return the load part for this command + MapEntry? entry_load = valueMap.entries + .firstWhereOrNull((element) => element.key == 'all_components'); + if (entry_load != null) { + for (Map singleSection in entry_load.value) { + String name = 'Unknown'; + String state = 'none'; + + MapEntry? singleEntryNm = singleSection.entries + .firstWhereOrNull((element) => element.key == 'description'); + if (singleEntryNm != null) { + name = singleEntryNm.value; + } + MapEntry? singleEntryState = singleSection.entries + .firstWhereOrNull((element) => element.key == 'state'); + if (singleEntryState != null) { + state = singleEntryState.value; + stat = jvxStateEInt.fromStringSingle(state); + } + AudYoFloOneComponentOptions newCom = + AudYoFloOneComponentOptions(name: name, stat: stat); + updateMe.add(newCom); + } + } + // theBeCache.updateAvailableComponentList(cpId, availComps); + + return errCode; + }); + return updateComponents; + } + + // =========================================================================== + // ACTIVATE COMPONENT ACTIVATE COMPONENT ACTIVATE COMPONENT ACTIVATE COMPONENT + // =========================================================================== + + @override + Future triggerActivateComponent(JvxComponentIdentification cpId, int id, + bool selectBeforeActivate) async { + int retVal = jvxErrorType.JVX_NO_ERROR; + String address = translateCompType(cpId.cpTp); + String cmd = '/jvx/host/components/' + address; + + // If selectBeforeActivate is true, the call to activate requires an + // id argument. Otherwise we live with no id argujent but function will fail if + // component is not yet SELECTED. + cmd += "/activate"; + Map queryParams = {}; + queryParams['slotid'] = cpId.slotid.toString(); + queryParams['slotsubid'] = cpId.slotsubid.toString(); + if (selectBeforeActivate) { + queryParams['id'] = id.toString(); + } + var url = Uri.http(httpTarget, cmd, queryParams); + AudYoFloUrlRequest urlRequest = AudYoFloUrlRequest(); + retVal = await webRequestHttpPost(url, '', urlRequest); + latestError = urlRequest.lastError; + if (retVal == jvxErrorType.JVX_NO_ERROR) { + retVal = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + String? errCodeExprPtr = + getStringEntryValueMap(urlRequest.jsonMap, 'return_code'); + if (errCodeExprPtr != null) { + // Convert error code + String errCodeExpr = errCodeExprPtr; + retVal = jvxErrorTypeEInt.fromStringSingle(errCodeExpr); + } + } + + if (retVal != jvxErrorType.JVX_NO_ERROR) { + report!.reportStatusErrorProtocol( + 6, 0, jvxErrorTypeEInt.toStringSingle(retVal)); + } + + return retVal; + } + + // If a new component was selected, we request the options of components at first and + // then, select the component which was indeed selected. + Future requestOptionsComponent( + JvxComponentIdentification cpId) async { + String? retVal; + int errCode = jvxErrorType.JVX_NO_ERROR; + String address = translateCompType(cpId.cpTp); + String cmd = '/jvx/host/components/' + address; + cmd += "/options"; + Map queryParams = {}; + queryParams['slotid'] = cpId.slotid.toString(); + queryParams['slotsubid'] = cpId.slotsubid.toString(); + var url = Uri.http(httpTarget, cmd, queryParams); + AudYoFloUrlRequest urlRequest = AudYoFloUrlRequest(); + errCode = await webRequestHttpGet(url, urlRequest); + latestError = urlRequest.lastError; + if (errCode == jvxErrorType.JVX_NO_ERROR) { + + var lstOptions = getListValueMap(urlRequest.jsonMap, 'options'); + if (lstOptions != null) { + for (var oneOption in lstOptions) { + bool foundHere = false; + var subslotlst = getListValueMap(oneOption, 'subslots'); + if (subslotlst != null) { + for (var oneslotsubid in subslotlst) { + if (oneslotsubid == cpId.slotsubid) { + retVal = extractStringFromJson(oneOption, 'description'); + foundHere = true; + break; + } + } + } else { + var slotlst = getListValueMap(oneOption, 'slots'); + if (slotlst != null) { + for (var oneslotid in slotlst) { + if (oneslotid == cpId.slotid) { + retVal = extractStringFromJson(oneOption, 'description'); + foundHere = true; + break; + } + } + } + } + if (foundHere) { + break; + } + } + } + } + + if (errCode != jvxErrorType.JVX_NO_ERROR) { + report!.reportStatusErrorProtocol( + 7, 0, jvxErrorTypeEInt.toStringSingle(errCode)); + } + return retVal; + } + // curl 127.0.0.1:8000/jvx/host/components/audio_device/options + + // =================================================================== + // This function is in use if a process is reported later than in the first contact! + // It will be called if a new process is reported and allocate a handle + // for the new process. Then, it triggers the backend to obtain the path - hence, the + // involved components in the chain. + Future requestInfoProcess(AudYoFloOneConnectedProcessWeb newProc) async { + int retVal = jvxErrorType.JVX_NO_ERROR; + + // curl 127.0.0.1:8000?uid=12 + String cmd = '/jvx/host/connections/path'; + Map queryParams = {}; + queryParams['uid'] = newProc.uId.toString(); + var url = Uri.http(httpTarget, cmd, queryParams); + AudYoFloUrlRequest urlRequest = AudYoFloUrlRequest(); + retVal = await webRequestHttpGet(url, urlRequest); + latestError = urlRequest.lastError; + + if (retVal == jvxErrorType.JVX_NO_ERROR) { + var secConnections = + getMapValueList(urlRequest.jsonMap, 'connection_processes'); + if (secConnections != null) { + for (var elm in secConnections) { + Map mp = elm; + int uid = getIntEntryValueMap(mp, 'uid'); + AudYoFloOneConnectedProcessWeb newProcess = + AudYoFloOneConnectedProcessWeb(uid, this, report!); + createPendingProcessFromJson(newProcess, elm); + } + } + } + + if (retVal != jvxErrorType.JVX_NO_ERROR) { + report!.reportStatusErrorProtocol( + 8, 0, jvxErrorTypeEInt.toStringSingle(retVal)); + } + return retVal; + } + // =========================================================================== + // DEACTIVATE COMPONENT DEACTIVATE COMPONENT DEACTIVATE COMPONENT DEACTIVATE COMPONENT + // =========================================================================== + + @override + Future triggerDeactivateComponent( + JvxComponentIdentification cpId, bool unselect) async { + int retVal = jvxErrorType.JVX_NO_ERROR; + String address = translateCompType(cpId.cpTp); + String cmd = '/jvx/host/components/' + address; + if (unselect) { + cmd += "/unselect"; + } else { + cmd += "/deactivate"; + } + Map queryParams = {}; + queryParams['slotid'] = cpId.slotid.toString(); + queryParams['slotsubid'] = cpId.slotsubid.toString(); + var url = Uri.http(httpTarget, cmd, queryParams); + AudYoFloUrlRequest urlRequest = AudYoFloUrlRequest(); + retVal = await webRequestHttpPost(url, '', urlRequest); + latestError = urlRequest.lastError; + + if (retVal != jvxErrorType.JVX_NO_ERROR) { + report!.reportStatusErrorProtocol( + 9, 0, jvxErrorTypeEInt.toStringSingle(retVal)); + } + return retVal; + } + + @override + Future triggerGetPropertyContentComponent( + JvxComponentIdentification cpId, List propDescrLst, + {AyfPropertyReportLevel reportArg = + AyfPropertyReportLevel.AYF_FRONTEND_REPORT_SINGLE_PROPERTY}) async { + int retVal = jvxErrorType.JVX_NO_ERROR; + // The update property list in cache is typically executed with a delay + // since here is the point where we have to request each single property. + // We are only allowed to notify the listeners if the result is not within + // the build function. + String address = translateCompType(cpId.cpTp); + String cmd = '/jvx/host/components/' + address + '/multi'; + Map queryParams = {}; + queryParams['slotid'] = cpId.slotid.toString(); + queryParams['slotsubid'] = cpId.slotsubid.toString(); + String multParam = '\['; + int cnt = 0; + for (; cnt < propDescrLst.length; cnt++) { + if (cnt > 0) { + multParam += ','; + } + multParam += propDescrLst[cnt]; + } + multParam += '\]'; + queryParams['multi'] = multParam; + var url = Uri.http(httpTarget, cmd, queryParams); + AudYoFloUrlRequest urlRequest = AudYoFloUrlRequest(); + retVal = await webRequestHttpGet(url, urlRequest); + latestError = urlRequest.lastError; + if (retVal == jvxErrorType.JVX_NO_ERROR) { + Map? subSec = getMapValueMap(urlRequest.jsonMap, 'properties'); + if (subSec != null) { + for (var elmS in propDescrLst) { + Map? subSubSec = getMapValueMap(subSec, elmS); + if (subSubSec == null) { + // Property was not returned in message - would this really happen? + } else { + AudYoFloPropertyContainer? propUpdate = + theBeCache?.referencePropertyInCache(cpId, elmS); + if (propUpdate != null) { + if (propUpdate is AudYoFloPropertyContentBackend) { + int resL = AudYoFloPropertyContentFromJson + .updatePropertyContentFromJsonMap(propUpdate, subSubSec, theBeCache!.backendAdapterIf!); + } + } else { + // Whatever the reason is to end up here.. + } + } + } + } + } + theBeCache!.updatePropertyCacheCompleteNotify(cpId, report: reportArg); + if (retVal != jvxErrorType.JVX_NO_ERROR) { + report!.reportStatusErrorProtocol( + 10, 0, jvxErrorTypeEInt.toStringSingle(retVal)); + } + return retVal; + } + + @override + Future triggerGetPropertyDescriptionComponent( + JvxComponentIdentification cpId, List propDescrLst, + {AyfPropertyReportLevel reportArg = + AyfPropertyReportLevel.AYF_FRONTEND_REPORT_NO_REPORT, int numDigits = 2}) async { + int retVal = jvxErrorType.JVX_NO_ERROR; + // The update property list in cache is typically executed with a delay + // since here is the point where we have to request each single property. + // We are only allowed to notify the listeners if the result is not within + // the build function. + String address = translateCompType(cpId.cpTp); + String cmd = '/jvx/host/components/' + address + '/multi'; + Map queryParams = {}; + queryParams['purpose'] = 'fullp|x'; + queryParams['slotid'] = cpId.slotid.toString(); + queryParams['slotsubid'] = cpId.slotsubid.toString(); + String multParam = '\['; + int cnt = 0; + for (; cnt < propDescrLst.length; cnt++) { + if (cnt > 0) { + multParam += ','; + } + multParam += propDescrLst[cnt]; + } + multParam += '\]'; + queryParams['multi'] = multParam; + var url = Uri.http(httpTarget, cmd, queryParams); + AudYoFloUrlRequest urlRequest = AudYoFloUrlRequest(); + retVal = await webRequestHttpGet(url, urlRequest); + latestError = urlRequest.lastError; + if (retVal == jvxErrorType.JVX_NO_ERROR) { + Map? subSec = getMapValueMap(urlRequest.jsonMap, 'properties'); + if (subSec != null) { + for (var elmS in propDescrLst) { + Map? subSubSec = getMapValueMap(subSec, elmS); + if (subSubSec == null) { + // This case happens if the property does not exist! + // We will not add the property to the list. + + // + } else { + // ================================================== + // Find out if property is already in the cache + // ================================================== + AudYoFloPropertyContainer? propFromCache = + theBeCache!.referencePropertyInCache(cpId, elmS); + if (propFromCache != null) { + if (propFromCache is AudYoFloPropertyContentBackend) { + // ================================================== + // An element already exists: update the property content + // ================================================== + int resL = AudYoFloPropertyContentFromJson + .updatePropertyDescriptionFromJson(cpId, this, + propFromCache, elmS, subSubSec, flags, true, numDigits); + assert(resL == jvxErrorType.JVX_NO_ERROR); + } else { + assert(false); + } + } else { + // ================================================== + // Here, we have seen a new property! + // ================================================== + + AudYoFloPropertyContentBackend updateProp = + AudYoFloPropertyContentFromJson.createPropertyFromJsonMap( + cpId, this, elmS, subSubSec, flags, numDigits); + + // ================================================== + // Here, we add the property to the cache! + // ================================================== + int resL = + theBeCache!.updateEntrySinglePropertyCache(cpId, updateProp); + if (resL != jvxErrorType.JVX_NO_ERROR) { + assert(false); + } + } + } + } + + // ================================================== + // Here, we report that property update was completed + // ================================================== + + if ((retVal == jvxErrorType.JVX_NO_ERROR) && + ((report == + AyfPropertyReportLevel + .AYF_FRONTEND_REPORT_SINGLE_PROPERTY) || + (report == + AyfPropertyReportLevel + .AYF_FRONTEND_REPORT_COMPONENT_PROPERTY))) { + theBeCache! + .updatePropertyCacheCompleteNotify(cpId, report: reportArg); + } + } + } + if (retVal != jvxErrorType.JVX_NO_ERROR) { + report!.reportStatusErrorProtocol( + 11, 0, jvxErrorTypeEInt.toStringSingle(retVal)); + } + return retVal; + } + + @override + Future triggerSetProperties( + JvxComponentIdentification cpId, List propContentsArg, + {bool invalidateProperty = true, + int offset = 0, + int num = -1, + bool preventCallMultipleTimes = false, + AyfPropertyReportLevel reportArg = + AyfPropertyReportLevel.AYF_BACKEND_REPORT_COMPONENT_PROPERTY}) async { + // List of properties + int retVal = jvxErrorType.JVX_NO_ERROR; + List propContents = []; + AyfBackendReportPropertySetEnum repEnum = AyfBackendReportPropertySetEnum + .AYF_BACKEND_REPORT_SET_PROPERTY_INACTIVE; + + // We may reduce the number of "sets" to be issued + if (preventCallMultipleTimes) { + //print( + // 'Entering with true'); + + for (var propDescr in propContentsArg) { + AudYoFloPropertyContainer? propFromCache = + theBeCache!.referencePropertyInCache(cpId, propDescr); + if (propFromCache != null) { + if (propFromCache is AudYoFloPropertyContentBackend) { + if (propFromCache.setInProgress == + jvxPropertyProgressStates.JVX_PROPERTY_PROGRESS_NONE) { + propFromCache.setInProgress = + jvxPropertyProgressStates.JVX_PROPERTY_PROGRESS_WAIT; + propContents.add(propDescr); + } else { + //print( + // 'Set for property <$propDescr> is skipped due to previous pending operation'); + } + } else { + assert(false); + } + } + } + if (propContents.isEmpty) { + //print( + // 'Leaving with true'); + return jvxErrorType.JVX_NO_ERROR; + } else { + //print( + // 'Running with true'); + } + } else { + propContents = propContentsArg; + } + // ============================================================== + + if ((reportArg == + AyfPropertyReportLevel.AYF_BACKEND_REPORT_COMPONENT_PROPERTY) || + (reportArg == + AyfPropertyReportLevel + .AYF_BACKEND_REPORT_COMPONENT_PROPERTY_COLLECT)) { + repEnum = AyfBackendReportPropertySetEnum + .AYF_BACKEND_REPORT_SET_PROPERTY_ACTIVE; + } + + String address = translateCompType(cpId.cpTp); + String cmd = '/jvx/host/components/' + address + '/multi'; + Map queryParams = {}; + queryParams['slotid'] = cpId.slotid.toString(); + queryParams['slotsubid'] = cpId.slotsubid.toString(); + if (reportArg == + AyfPropertyReportLevel.AYF_BACKEND_REPORT_COMPONENT_PROPERTY_COLLECT) { + queryParams['collect'] = 'yes'; + } + if (repEnum == + AyfBackendReportPropertySetEnum + .AYF_BACKEND_REPORT_SET_PROPERTY_ACTIVE) { + queryParams['report'] = 'yes'; + } + String multParam = '\['; + int cnt = 0; + for (; cnt < propContents.length; cnt++) { + String onePropReq = propContents[cnt] + ','; + AudYoFloPropertyContainer? propFromCache = + theBeCache!.referencePropertyInCache(cpId, propContents[cnt]); + if (propFromCache != null) { + var valTxt = ''; + var valTxtPtr = + AudYoFloPropertyContentToString.convertPropertyContentToString( + propFromCache, true, offset: offset, num: num, + precision: 4, ); + if (valTxtPtr != null) { + valTxt = valTxtPtr; + } + //propertiesToString(propFromCache, offset, num); + onePropReq += valTxt; + if (offset > 0) { + onePropReq += ',' + offset.toString(); + } + onePropReq = '(' + onePropReq + ')'; + } else { + assert(false); + } + if (cnt > 0) { + multParam += ','; + } + multParam += onePropReq; + } + multParam += '\]'; + var url = Uri.http(httpTarget, cmd, queryParams); + + AudYoFloUrlRequest urlRequest = AudYoFloUrlRequest(); + retVal = await webRequestHttpPost(url, multParam, urlRequest); + latestError = urlRequest.lastError; + if (retVal == jvxErrorType.JVX_NO_ERROR) { + var returnedProps = getMapValueList(urlRequest.jsonMap, 'properties'); + if (returnedProps != null) { + for (var elmP in returnedProps) { + String? descriptor = extractStringFromJson(elmP, 'descriptor'); + String? errCodeExprPtr = + getStringEntryValueMap(urlRequest.jsonMap, 'return_code'); + if ((descriptor != null) && (errCodeExprPtr != null)) { + bool sucSet = false; + // Convert error code + String errCodeExpr = errCodeExprPtr; + int retValLoc = jvxErrorTypeEInt.fromStringSingle(errCodeExpr); + if (retValLoc == jvxErrorType.JVX_NO_ERROR) { + for (var elmIP in propContents) { + if (descriptor! == elmIP) { + sucSet = true; + break; + } + } + } + if (sucSet == false) { + print( + 'Setting property <$descriptor!> failed, reason: <$errCodeExpr>.'); + } + } else { + report!.reportStatusErrorProtocol( + 12, 1, jvxErrorTypeEInt.toStringSingle(retVal)); + } + } + } + + if (preventCallMultipleTimes) { + for (var propDescr in propContentsArg) { + AudYoFloPropertyContainer? propFromCache = + theBeCache!.referencePropertyInCache(cpId, propDescr); + if(propFromCache != null) { + if (propFromCache is AudYoFloPropertyContentBackend) { + if (propFromCache!.setInProgress == + jvxPropertyProgressStates.JVX_PROPERTY_PROGRESS_WAIT) { + propFromCache.setInProgress = + jvxPropertyProgressStates.JVX_PROPERTY_PROGRESS_NONE; + } + } + } + } + } + // If we use reportPropertyOnSet this will not be required + if (invalidateProperty) { + theBeCache!.invalidatePropertiesComponent( + cpId, propContents, true); + } + if (reportArg != + AyfPropertyReportLevel.AYF_BACKEND_REPORT_COMPONENT_PROPERTY) { + // If we use reportPropertyOnSet this will not be required + theBeCache!.updatePropertyCacheCompleteNotify(cpId, report: reportArg); + // theBeCacheNative.updatePropertiesInCacheComplete(report: report); + } + } + if (retVal != jvxErrorType.JVX_NO_ERROR) { + report!.reportStatusErrorProtocol( + 12, 0, jvxErrorTypeEInt.toStringSingle(retVal)); + } + return retVal; + } + + // Trigger backend to update the list of properties in cache + @override + Future triggerUpdatePropertyListComponent( + JvxComponentIdentification cpId, + {AyfPropertyReportLevel reportArg = AyfPropertyReportLevel + .AYF_FRONTEND_REPORT_COMPONENT_PROPERTY}) async { + int retVal = jvxErrorType.JVX_NO_ERROR; + // The update property list in cache is typically executed with a delay + // since here is the point where we have to request each single property. + // We are only allowed to notify the listeners if the result is not within + // the build function. + String address = translateCompType(cpId.cpTp); + String cmd = '/jvx/host/components/' + address; + Map queryParams = {}; + queryParams['purpose'] = 'list'; + queryParams['slotid'] = cpId.slotid.toString(); + queryParams['slotsubid'] = cpId.slotsubid.toString(); + var url = Uri.http(httpTarget, cmd, queryParams); + + AudYoFloUrlRequest urlRequest = AudYoFloUrlRequest(); + retVal = await webRequestHttpGet(url, urlRequest); + latestError = urlRequest.lastError; + if (retVal == jvxErrorType.JVX_NO_ERROR) { + retVal = jvxErrorType.JVX_ERROR_ELEMENT_NOT_FOUND; + String? errCodeExprPtr = + getStringEntryValueMap(urlRequest.jsonMap, 'return_code'); + if (errCodeExprPtr != null) { + // Convert error code + String errCodeExpr = errCodeExprPtr; + retVal = jvxErrorTypeEInt.fromStringSingle(errCodeExpr); + } + if (retVal == jvxErrorType.JVX_NO_ERROR) { + var subSec = getMapValueList(urlRequest.jsonMap, 'properties'); + List lstProps = []; + if (subSec != null) { + for (var elmS in subSec) { + if (elmS is String) { + lstProps.add(elmS); + } + } + theBeCache!.updatePropertyCacheListCompleteNotify(cpId, lstProps, + report: reportArg); + } + } + } + + if (retVal != jvxErrorType.JVX_NO_ERROR) { + report!.reportStatusErrorProtocol( + 13, 0, jvxErrorTypeEInt.toStringSingle(retVal)); + } + return retVal; + } + + @override + Future triggerUpdateDeviceList(JvxComponentIdentification cpId) async { + // The update property list in cache is typically executed with a delay + // since here is the point where we have to request each single property. + // We are only allowed to notify the listeners if the result is not within + // the build function. + // /jvx/host/components/audio_technology/devices + int retVal = jvxErrorType.JVX_NO_ERROR; + // The update property list in cache is typically executed with a delay + // since here is the point where we have to request each single property. + // We are only allowed to notify the listeners if the result is not within + // the build function. + String address = translateCompType(cpId.cpTp); + String cmd = '/jvx/host/components/' + address; + cmd += "/devices"; + Map queryParams = {}; + queryParams['slotid'] = cpId.slotid.toString(); + queryParams['slotsubid'] = cpId.slotsubid.toString(); + var url = Uri.http(httpTarget, cmd, queryParams); + + AudYoFloUrlRequest urlRequest = AudYoFloUrlRequest(); + retVal = await webRequestHttpGet(url, urlRequest); + latestError = urlRequest.lastError; + if (retVal == jvxErrorType.JVX_NO_ERROR) { + retVal = AudYoFloDevicelistFromJson.updateDeviceList( + urlRequest.jsonMap, cpId, theBeCache!, theBeCache!.backendAdapterIf!); + } + + if (retVal != jvxErrorType.JVX_NO_ERROR) { + report!.reportStatusErrorProtocol( + 14, 0, jvxErrorTypeEInt.toStringSingle(retVal)); + } + return retVal; + } + + @override + Future triggerUpdateComponentList( + JvxComponentIdentification cpId, bool isDevice) async { + // The update property list in cache is typically executed with a delay + // since here is the point where we have to request each single property. + // We are only allowed to notify the listeners if the result is not within + // the build function. + return await Future.delayed(Duration.zero, () async { + int errCode = jvxErrorType.JVX_NO_ERROR; + + // To be implemented! + assert(false); + return errCode; + }); + } + + // ===================================================================== + // Api call to save current setup in config file + // ===================================================================== + + @override + Future triggerSaveConfig() async { + // file(config, write) + // curl -d "" -X POST 127.0.0.1:8000/jvx/host/system/file + int retVal = jvxErrorType.JVX_NO_ERROR; + // The update property list in cache is typically executed with a delay + // since here is the point where we have to request each single property. + // We are only allowed to notify the listeners if the result is not within + // the build function. + String cmd = '/jvx/host/system/file'; + var url = Uri.http(httpTarget, cmd); + AudYoFloUrlRequest urlRequest = AudYoFloUrlRequest(); + retVal = await webRequestHttpPost(url, '', urlRequest); + latestError = urlRequest.lastError; + if (retVal != jvxErrorType.JVX_NO_ERROR) { + report!.reportStatusErrorProtocol( + 15, 0, jvxErrorTypeEInt.toStringSingle(retVal)); + } + return retVal; + } + + @override + void reportPropertyListSetBackend( + JvxComponentIdentification cpId, String propLst) { + theBeCache!.reportPropertyListSetBackend(cpId, propLst); + } + + @override + Future triggerClose() async { + if (shutdown != null) { + await shutdown!(); + } + } + + @override + String get lastError => latestError; +} diff --git a/flutter/ayf_webhost/lib/backend/ayf_web_backend_anomalies.dart b/flutter/ayf_webhost/lib/backend/ayf_web_backend_anomalies.dart new file mode 100644 index 00000000..f12b6b4b --- /dev/null +++ b/flutter/ayf_webhost/lib/backend/ayf_web_backend_anomalies.dart @@ -0,0 +1,24 @@ +import 'package:collection/collection.dart'; +import 'package:ayf_pack/ayf_pack.dart'; +import 'package:http/http.dart' as http; +import '../ayf_web_backend_constants.dart'; +import '../ayf_web_backend_typedefs.dart'; +import 'ayf_web_backend_message_processor.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; + +import '../ayf_web_backend_helpers.dart'; + +import 'dart:async'; +import 'dart:io'; + +class AudYoFloWebBackendAnomalies { + void reportStatusDisconnect(int idReason, int subId, String description) { + print( + 'Status disconnect reported: Id=$idReason - SubId=$subId - reason hint: $description'); + } + + void reportStatusErrorProtocol(int idReason, int subId, String description) { + print( + 'Communication reported a protocol status error: Id=$idReason - SubId=$subId - reason hint: $description'); + } +} diff --git a/flutter/ayf_webhost/lib/backend/ayf_web_backend_message_processor.dart b/flutter/ayf_webhost/lib/backend/ayf_web_backend_message_processor.dart new file mode 100644 index 00000000..6e4723c0 --- /dev/null +++ b/flutter/ayf_webhost/lib/backend/ayf_web_backend_message_processor.dart @@ -0,0 +1,648 @@ +import 'package:ayf_pack/ayf_pack.dart'; +import 'dart:convert' as convert; +import '../ayf_web_backend_helpers.dart'; +import '../ayf_web_backend_constants.dart'; +import '../ayf_web_backend_typedefs.dart'; +import './ayf_web_backend_adapter.dart'; +import './ayf_web_backend_anomalies.dart'; + +class AudYoFloOneConnectedProcessWeb extends AudYoFloOneConnectedProcess { + AudYoFloWebBackendHelpers helper; + AudYoFloWebBackendAnomalies report; + AudYoFloOneConnectedProcessWeb(int uId, this.helper, this.report) + : super(uId); + void fill(dynamic arg) { + Map elm = arg as Map; + String? descrStr = extractStringFromJson(elm, 'descriptor'); + // String? isfromruleStr = extractStringFromJson(elm, 'is_from_rule'); + // String? statusStr = extractStringFromJson(elm, 'status'); + // String? lasttestStr = extractStringFromJson(elm, 'last_test'); + //String? readyStr = extractStringFromJson(elm, 'ready'); + var processDetails = getMapValueMap(elm, 'process_path'); + catId = getIntEntryValueMap(elm, 'category_id'); + if (descrStr != null) { + nameProcess = descrStr!; + } + if (processDetails != null) { + involved = AudYoFloOneComponentInProcessWeb(helper, report); + involved!.fill(processDetails); + } + } +} + +class AudYoFloOneComponentInProcessWeb extends AudYoFloOneComponentInProcess { + AudYoFloWebBackendHelpers helper; + AudYoFloWebBackendAnomalies report; + AudYoFloOneComponentInProcessWeb(this.helper, this.report); + + @override + void fill(dynamic arg) { + Map jsonMap = arg as Map; + + String? cpIdStr = + extractStringFromJson(jsonMap, 'component_identification'); + int uid = getIntEntryValueMap(jsonMap, 'uid'); + String? modNameStr = + extractStringFromJson(jsonMap, 'component_identification'); + String? cpNameStr = extractStringFromJson(jsonMap, 'description'); + String? connNmStr = extractStringFromJson(jsonMap, 'context'); + var nextMap = getMapValueList(jsonMap, 'next'); + + if ((cpIdStr != null) && + (modNameStr != null) && + (cpNameStr != null) && + (connNmStr != null)) { + cpId = helper.translateStringComponentIdentification(cpIdStr, uid); + nameComponent = cpNameStr; + nameModule = modNameStr; + nameConnector = connNmStr; + if (nextMap != null) { + for (var nComp in nextMap) { + AudYoFloOneComponentInProcessWeb newInvolved = + AudYoFloOneComponentInProcessWeb(helper, report); + newInvolved.fill(nComp); + attach(newInvolved); + } + } + } + } +} + +class AudYoFloWebStateProcessor { + AudYoFloWebBackendHelpers? helper; + AudYoFloWebBackendAnomalies? report; + + AudYoFloCompileFlags flags = AudYoFloCompileFlags(); + + String dataTypeToken = "jvxData"; + + jvxWebHostConnectionState connectState = + jvxWebHostConnectionState.JVX_CONECTION_NONE; + String backendGitTag = 'unknown'; + String backendDatetag = 'unknown'; + AudYoFloBackendAdapterWeb? parentRef; + // AudYoFloBackendCacheBectrlIf? _theBeCacheReference; + + Map lookupClassTypes = {}; + + // If a new process pops up, we need to store all information in a list + // Then, later, the cache will allocate a new process handle and then reuse the + // information + Map pendingProcesses = {}; + + void initializeWebProcessor( + AudYoFloBackendAdapterWeb beArg, + AudYoFloWebBackendHelpers helperArg, + AudYoFloWebBackendAnomalies reportArg) { + parentRef = beArg; + dataTypeToken = "jvxData"; + connectState = jvxWebHostConnectionState.JVX_CONECTION_NONE; + backendGitTag = 'unknown'; + backendDatetag = 'unknown'; + helper = helperArg; + report = reportArg; + } + + void terminateWebProcessor() { + lookupClassTypes.clear(); + pendingProcesses.clear(); + } + + Future processResponseState(jvxWebHostOperationResponseType tp, + Map jsonResponse, ExtractedFromJson? extrRet) async { + bool retVal = false; + switch (connectState) { + // This is the processing of the initial connection interaction + case jvxWebHostConnectionState.JVX_CONECTION_NONE: + int errCode = jvxErrorType.JVX_NO_ERROR; + String? typeStr = extractStringFromJson(jsonResponse, "return_code"); + if (typeStr != null) { + errCode = jvxErrorTypeEInt.fromStringSingle(typeStr); + if (errCode == jvxErrorType.JVX_NO_ERROR) { + var subMap = getMapValueMap(jsonResponse, "version"); + if (subMap != null) { + var me_tag = extractStringFromJson(subMap, 'me_tag'); + var git_tag = extractStringFromJson(subMap, 'git_tag'); + var date_tag = extractStringFromJson(subMap, 'date_tag'); + var num32BitField = extractStringFromJson(subMap, 'num32BitFld'); + var dtTp = extractStringFromJson(subMap, 'dtTp'); + if (me_tag != null) { + if (me_tag == 'jvx-rt-750619') { + if ((num32BitField != null) && (dtTp != null)) { + flags.compileNum32BitBitfield = int.parse(num32BitField); + flags.compileDataTypeSpec = + jvxDataTypeSpecEEnum.fromString(dtTp); + dataTypeToken += '(' + dtTp + ')'; + + if (git_tag != null) { + backendGitTag = git_tag; + } + if (date_tag != null) { + backendDatetag = date_tag; + } + connectState = jvxWebHostConnectionState + .JVX_CONECTION_FIRST_CONTACT_DONE; + retVal = true; + } + } + } + } + } + } + break; + case jvxWebHostConnectionState.JVX_CONECTION_FIRST_CONTACT_DONE: + + // ============================================================== + // Extract response with return_code and message context + // ============================================================== + + int errCode = jvxErrorType.JVX_NO_ERROR; + String? typeStr = extractStringFromJson(jsonResponse, "return_code"); + if (typeStr != null) { + errCode = jvxErrorTypeEInt.fromStringSingle(typeStr); + } + if (extrRet != null) { + typeStr = extractStringFromJson(jsonResponse, "call_context"); + if (typeStr != null) { + extrRet.ctxt = typeStr; + } + } + connectState = jvxWebHostConnectionState.JVX_CONECTION_WEBSOCKET_ON; + + // ============================================================== + // Extract system status information + // ============================================================== + + var secSystem = getMapValueMap(jsonResponse, 'system'); + if (secSystem != null) { + // =================================================================== + // Scan the involved components + // =================================================================== + + var secComponents = getListValueMap(secSystem, 'components'); + if (secComponents != null) { + // Build up component type class list at first + for (var elm in secComponents) { + var cpTpStr = extractStringFromJson(elm, 'component_type'); + var cpTpClassStr = + extractStringFromJson(elm, 'component_type_class'); + if ((cpTpStr != null) && (cpTpClassStr != null)) { + JvxComponentTypeEnum cpTp = + helper!.translateStringComponentType(cpTpStr); + jvxComponentTypeClassEnum cpCls = + helper!.translateStringComponentTypeClass(cpTpClassStr); + lookupClassTypes[cpTp] = cpCls; + } + } + + // Find the active components + for (var elm in secComponents) { + var cpTpStr = extractStringFromJson(elm, 'component_type'); + if (cpTpStr != null) { + JvxComponentTypeEnum cpTp = + helper!.translateStringComponentType(cpTpStr); + var secActive = getListValueMap(elm, 'active_components'); + int slotId = 0; + if (secActive != null) { + for (var elmA in secActive) { + reportOneComponent(elmA); + + var cpCls = parentRef!.lookupComponentClass(cpTp); + if (cpCls == + jvxComponentTypeClassEnum + .JVX_COMPONENT_TYPE_TECHNOLOGY) { + var secDevices = getListValueMap(elmA, 'devices'); + if (secDevices != null) { + for (var elmD in secDevices) { + reportOneComponent(elmD); + } + } + } + } + } + } + } + } + + // ============================================================= + // ============================================================= + var secSequencer = getListValueMap(secSystem, 'sequencer'); + if (secSequencer != null) { + for (var elm in secSequencer) { + Map mp = elm; + /* + int uid = getIntEntryValueMap(mp, 'uid'); + AudYoFloOneConnectedProcessWeb newProcess = + AudYoFloOneConnectedProcessWeb(uid); + createPendingProcessFromJson(newProcess, elm); + */ + } + } + + // ============================================================= + // ============================================================= + var secConnections = + getListValueMap(secSystem, 'connection_processes'); + if (secConnections != null) { + for (var elm in secConnections) { + Map mp = elm; + int uid = getIntEntryValueMap(mp, 'uid'); + AudYoFloOneConnectedProcessWeb newProcess = + AudYoFloOneConnectedProcessWeb(uid, helper!, report!); + createPendingProcessFromJson(newProcess, elm); + } + } + } + break; + case jvxWebHostConnectionState.JVX_CONECTION_WEBSOCKET_ON: + if (tp == jvxWebHostOperationResponseType.JVX_MESSAGE_WEBSOCKET) { + var reqStr = extractStringFromJson(jsonResponse, "req"); + if (reqStr != null) { + // This message is a command request message!! + jvxReportCommandRequestEnum reqTp = + jvxReportCommandRequestEEnum.fromInt(parentRef! + .translateEnumString(reqStr, "jvxReportCommandRequest", flags)); + var typeStr = extractStringFromJson(jsonResponse, "type"); + var mapOrigin = getMapValueMap(jsonResponse, "origin"); + var mapSpecific = getMapValueMap(jsonResponse, "specific"); + if ((typeStr != null) && (mapOrigin != null)) { + var cpIdStr = + extractStringFromJson(mapOrigin, "component_identification"); + var uidCp = getIntEntryValueMap(mapOrigin, "uid"); + + if (cpIdStr != null) { + String? identStr; + String ident = 'unknown'; + int uidRep = -1; + jvxStateSwitchEnum ssTp = + jvxStateSwitchEnum.JVX_STATE_SWITCH_NONE; + JvxComponentIdentification cpId = helper! + .translateStringComponentIdentification(cpIdStr, uidCp); + + jvxReportCommandDataTypeEnum reqDtTp = + jvxReportCommandDataTypeEEnum.fromInt(parentRef! + .translateEnumString( + typeStr, "jvxReportCommandDataType", flags)); + String specTxt = ""; + if (mapSpecific != null) { + switch (reqDtTp) { + case jvxReportCommandDataTypeEnum + .JVX_REPORT_COMMAND_TYPE_IDENT: + identStr = extractStringFromJson(mapSpecific, "ident"); + if (identStr != null) { + ident = identStr; + } + specTxt = "ident: $ident"; + break; + case jvxReportCommandDataTypeEnum + .JVX_REPORT_COMMAND_TYPE_UID: + uidRep = getIntEntryValueMap(mapSpecific, "uid"); + specTxt = "uId: $uidRep"; + break; + case jvxReportCommandDataTypeEnum + .JVX_REPORT_COMMAND_TYPE_SS: + String? ssStr = + extractStringFromJson(mapSpecific, "sswitch"); + if (ssStr != null) { + ssTp = jvxStateSwitchEEnum.fromInt(parentRef! + .translateEnumString(ssStr, "jvxStateSwitch", flags)); + } + specTxt = ssTp.toString(); + specTxt = "sswitch: $specTxt"; + break; + default: + break; + } + } + String bcText = 'no-broadcast-info'; + if (parentRef!.dbgOut) { + String txt = + 'Incoming ASYNC command requested <${reqTp.txt}> from component <${cpId.txt}> - $bcText - $specTxt'; + + parentRef!.dbgModel?.addLineOut(txt); + switch (reqTp) { + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_UNSPECIFIC: + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_UPDATE_AVAILABLE_COMPONENT_LIST: + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_UPDATE_STATUS_COMPONENT_LIST: + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_UPDATE_STATUS_COMPONENT: + if (parentRef!.dbgOut) { + parentRef!.dbgModel?.addLineOut( + 'Component <${cpId.txt}> requests to react to new status.'); + } + parentRef!.theBeCache!.reportUpdateStatusComponent(cpId); + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_SYSTEM_STATUS_CHANGED: + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_TRIGGER_SEQUENCER_IMMEDIATE: + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_UPDATE_ALL_PROPERTIES: + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_RESCHEDULE_MAIN: + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_BORN_SUBDEVICE: + if (parentRef!.dbgOut) { + parentRef!.dbgModel?.addLineOut( + 'Component was born: $ident component <${cpId.txt}>'); + } + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_DIED_SUBDEVICE: + if (parentRef!.dbgOut) { + parentRef!.dbgModel?.addLineOut( + 'Component about to die: $ident component <${cpId.txt}>'); + } + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_BORN_COMPONENT: + if (parentRef!.dbgOut) { + parentRef!.dbgModel?.addLineOut( + 'Component was born: $ident component <${cpId.txt}>'); + } + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_DIED_COMPONENT: + if (parentRef!.dbgOut) { + parentRef!.dbgModel?.addLineOut( + 'Component was born: $ident component <${cpId.txt}>'); + } + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_PROCESS_CONNECTED: + if (parentRef!.dbgOut) { + parentRef!.dbgModel + ?.addLineOut('Process connected with uid $uidRep'); + } + AudYoFloOneConnectedProcessWeb newProcess = + AudYoFloOneConnectedProcessWeb( + uidRep, helper!, report!); + + int errCodeLocal = + await parentRef!.requestInfoProcess(newProcess); + if (errCodeLocal == jvxErrorType.JVX_NO_ERROR) {} + // Here, we require more info!! + /* + newProcess.descrStr = + extractStringFromJson(elm, 'descriptor'); + newProcess.uid = uidRep; + newProcess.isfromruleStr = + extractStringFromJson(elm, 'is_from_rule'); + newProcess.statusStr = + extractStringFromJson(elm, 'status'); + newProcess.lasttestStr = + extractStringFromJson(elm, 'last_test'); + newProcess.readyStr = extractStringFromJson(elm, 'ready'); + newProcess.pathEntry = + getMapValueMap(elm, 'process_path'); + */ + if (newProcess.uId >= 0) { + pendingProcesses[newProcess.uId!] = newProcess; + parentRef!.theBeCache!.addProcess(uidRep); + parentRef!.triggerUpdateProcessStatus(); + + txt = parentRef!.debugPrintProcess(uidRep); + parentRef!.dbgModel?.addLineOutConvert(txt); + } + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_PROCESS_TO_BE_DISCONNECTED: + if (parentRef!.dbgOut) { + parentRef!.dbgModel?.addLineOut( + 'Process to be disconnected with uid $uidRep'); + } + parentRef!.theBeCache!.remProcess(uidRep); + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_TEST_CHAIN: + if (parentRef!.dbgOut) { + parentRef!.dbgModel + ?.addLineOut('Tested chain with uid $uidRep'); + } + parentRef!.theBeCache!.triggerUpdateProcessStatus(); + break; + + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_TEST_CHAIN_RUN: + break; + + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_COMPONENT_STATESWITCH: + if (parentRef!.dbgOut) { + parentRef!.dbgModel?.addLineOut( + 'Component State Switch -> ${ssTp.txt} component <${cpId.txt}>'); + } + switch (ssTp) { + case jvxStateSwitchEnum.JVX_STATE_SWITCH_SELECT: + String cpName = cpId.txt; + String? cpNameStr = + await parentRef!.requestOptionsComponent(cpId); + if (cpNameStr != null) { + cpName = cpNameStr; + } + parentRef!.theBeCache! + .reportSelectedComponent(cpId, cpName); + break; + case jvxStateSwitchEnum.JVX_STATE_SWITCH_UNSELECT: + parentRef!.theBeCache! + .reportUnselectedComponent(cpId); + break; + default: + + // Indicate that the state changed + parentRef!.theBeCache! + .reportStateSwitchComponent(cpId, ssTp); + break; + } + + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_CONFIGURATION_COMPLETE: + // runAutoStart(); + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_SEQUENCER_EVENT: + if (mapSpecific != null) { + jvxSequencerEventType ev = jvxSequencerEventType(); + jvxSequencerStatusEnum seqStat = + jvxSequencerStatusEnum.JVX_SEQUENCER_STATUS_NONE; + String? evDescrPtr = + extractStringFromJson(mapSpecific, 'seqev-descr'); + String? seqQTypePtr = + extractStringFromJson(mapSpecific, 'seqev-qtp'); + String? seqETypePtr = + extractStringFromJson(mapSpecific, 'seqev-etp'); + int stpId = + getIntEntryValueMap(mapSpecific, 'seqev-stpid'); + int seqId = + getIntEntryValueMap(mapSpecific, 'seqev-seqid'); + int stpFId = + getIntEntryValueMap(mapSpecific, 'seqev-stpfid'); + String? fErrorPtr = + extractStringFromJson(mapSpecific, 'seqev-ferror'); + int seqEvStateId = + getIntEntryValueMap(mapSpecific, 'seqev-state_id'); + String? evMaskPtr = + extractStringFromJson(mapSpecific, 'seqev-mask'); + String? seqStatePtr = + extractStringFromJson(mapSpecific, 'seq-state'); + if ((evDescrPtr != null) && + (seqQTypePtr != null) && + (seqETypePtr != null) && + (fErrorPtr != null) && + (seqStatePtr != null) && + (evMaskPtr != null)) { + seqStat = jvxSequencerStatusEEnum.fromInt(parentRef! + .translateEnumString( + seqStatePtr, 'jvxSequencerStatus', flags)); + + ev.description = evDescrPtr; + ev.fId = stpFId; + ev.qtp = jvxSequencerQueueTypeEEnum.fromInt(parentRef! + .translateEnumString( + seqQTypePtr, 'jvxSequencerQueueType', flags)); + ev.indicateFirstError = (fErrorPtr == "yes"); + ev.eventMask = int.parse(evMaskPtr); + ev.seq_state_id = seqEvStateId; + ev.sequenceId = seqId; + ev.stepId = stpId; + ev.setp = jvxSequencerElementTypeEEnum.fromInt( + parentRef!.translateEnumString( + seqETypePtr, 'jvxSequencerElementType', flags)); + } + + parentRef!.theBeCache! + .reportSequencerEvent(ev, seqStat); + } + /* + var evPtr = + natLib!.ffi_req_command_decode_sequencer_event(load_fld); + int status = natLib!.ffi_sequencer_status(opaque_host); + jvxSequencerStatusEnum seqStat = + jvxSequencerStatusEEnum.fromInt(status); + jvxSequencerEventType ev = jvxSequencerEventTypeNative + .decodeSequencerEventType(evPtr); + + one_sequencer_event evRef = evPtr.ref; + ffi.Pointer dPtr = + evRef.description.cast(); + + natLib!.ffi_host_delete(dPtr.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_CHAR_ARRAY); + natLib!.ffi_host_delete(evPtr.cast(), + ffiDeleteDatatype.JVX_DELETE_DATATYPE_ONE_SEQ_EVENT); + + jvxFernliveCommandQueueElement? whattodo = + theBeAdapter.processSeqEvent(ev, seqStat); + if (whattodo == + jvxFernliveCommandQueueElement.FERNLIVE_SHUTDOWN) { + finalizeShutdown(); + } + + // If the + //theBeAdapter.triggerUpdateProcessStatus(); + */ + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_SEQUENCER_CALLBACK: + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_PROCESS_DISCONNECT_COMPLETE: + parentRef!.theBeCache!.triggerUpdateProcessStatus(); + break; + + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_UPDATE_PROPERTY: + parentRef!.reportPropertyListSetBackend(cpId, ident); + break; + case jvxReportCommandRequestEnum + .JVX_REPORT_COMMAND_REQUEST_REPORT_TEST_SUCCESS: + break; + } + } + } + } + } + } + break; + default: + break; + } + return retVal; + } + + void createPendingProcessFromJson( + AudYoFloOneConnectedProcessWeb newProcess, Map elm) { + newProcess.fill(elm); + + if (newProcess.uId >= 0) { + // Store all data related to the process + pendingProcesses[newProcess.uId] = newProcess; + parentRef!.theBeCache!.addProcess(newProcess.uId); + parentRef!.theBeCache!.triggerUpdateProcessStatus(); + } + } + + void reportOneComponent(Map elm) { + var cpIdStr = extractStringFromJson(elm, 'component_identification'); + var descrStr = extractStringFromJson(elm, 'description'); + var stateStr = extractStringFromJson(elm, 'state'); + var uidStr = extractStringFromJson(elm, 'uid'); + if ((cpIdStr != null) && + (descrStr != null) && + (stateStr != null) && + (uidStr != null)) { + int uid = int.parse(uidStr); + + // ==================================================== + // Report every single component to backend + // ==================================================== + String txt = ''; + JvxComponentIdentification cpId = + helper!.translateStringComponentIdentification(cpIdStr, uid); + int stat = helper!.translateStringState(stateStr); + if (stat >= jvxState.JVX_STATE_SELECTED) { + txt = 'Initial connect: Reporting select component ' + cpId.txt; + parentRef!.dbgModel?.addLineOut(txt); + parentRef!.theBeCache!.reportSelectedComponent(cpId, descrStr); + } + if (stat >= jvxState.JVX_STATE_ACTIVE) { + txt = 'Initial connect: Reporting state switch ' + + jvxStateSwitchEnum.JVX_STATE_SWITCH_ACTIVATE.toString() + + ', component ' + + cpId.txt; + parentRef!.dbgModel?.addLineOut(txt); + parentRef!.theBeCache!.reportStateSwitchComponent( + cpId, jvxStateSwitchEnum.JVX_STATE_SWITCH_ACTIVATE); + } + if (stat >= jvxState.JVX_STATE_PREPARED) { + txt = 'Initial connect: Reporting state switch ' + + jvxStateSwitchEnum.JVX_STATE_SWITCH_PREPARE.toString() + + ', component ' + + cpId.txt; + parentRef!.dbgModel?.addLineOut(txt); + parentRef!.theBeCache!.reportStateSwitchComponent( + cpId, jvxStateSwitchEnum.JVX_STATE_SWITCH_PREPARE); + } + if (stat >= jvxState.JVX_STATE_PROCESSING) { + txt = 'Initial connect: Reporting state switch ' + + jvxStateSwitchEnum.JVX_STATE_SWITCH_START.toString() + + ', component ' + + cpId.txt; + parentRef!.dbgModel?.addLineOut(txt); + parentRef!.theBeCache!.reportStateSwitchComponent( + cpId, jvxStateSwitchEnum.JVX_STATE_SWITCH_START); + } + } + } +} diff --git a/flutter/ayf_webhost/pubspec.yaml.in b/flutter/ayf_webhost/pubspec.yaml.in new file mode 100644 index 00000000..0aba4d15 --- /dev/null +++ b/flutter/ayf_webhost/pubspec.yaml.in @@ -0,0 +1,59 @@ +name: ayf_webhost +description: A new Flutter package project. +version: 0.0.1 +homepage: + +environment: + sdk: ">=2.17.1 <3.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + + ayf_pack: + path: '../ayf_pack' + + http: ^0.13.6 + + web_socket_channel: ^2.1.0 + +dev_dependencies: + flutter_test: + sdk: flutter + + flutter_lints: ^2.0.0 + +# The following section is specific to Flutter packages. +flutter: + + # To add assets to your package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/flutter/ayf_webhost/test/ayf_ffihost_test.dart b/flutter/ayf_webhost/test/ayf_ffihost_test.dart new file mode 100644 index 00000000..0c231bd7 --- /dev/null +++ b/flutter/ayf_webhost/test/ayf_ffihost_test.dart @@ -0,0 +1,14 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:ayf_webhost/ayf_web_backend_host.dart'; + +void main() { + test('adds one to input values', () { + /* + final calculator = Calculator(); + expect(calculator.addOne(2), 3); + expect(calculator.addOne(-7), -6); + expect(calculator.addOne(0), 1); + */ + }); +} diff --git a/flutter/ayf_webhost/web/favicon.png b/flutter/ayf_webhost/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/flutter/ayf_webhost/web/icons/Icon-192.png b/flutter/ayf_webhost/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/flutter/ayf_webhost/web/icons/Icon-512.png b/flutter/ayf_webhost/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 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 literal 0 HcmV?d00001 diff --git a/flutter/ayf_webhost/web/icons/Icon-maskable-192.png b/flutter/ayf_webhost/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9b4d76e525556d5d89141648c724331630325d GIT binary patch literal 5594 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 X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! literal 0 HcmV?d00001 diff --git a/flutter/ayf_webhost/web/icons/Icon-maskable-512.png b/flutter/ayf_webhost/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..d69c56691fbdb0b7efa65097c7cc1edac12a6d3e GIT binary patch literal 20998 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 MPgg&ebxsLQ025IeI{*Lx literal 0 HcmV?d00001 diff --git a/flutter/ayf_webhost/web/index.html b/flutter/ayf_webhost/web/index.html new file mode 100644 index 00000000..913074ce --- /dev/null +++ b/flutter/ayf_webhost/web/index.html @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + ayf_webhost + + + + + + + + + + diff --git a/flutter/ayf_webhost/web/manifest.json b/flutter/ayf_webhost/web/manifest.json new file mode 100644 index 00000000..9a3fe344 --- /dev/null +++ b/flutter/ayf_webhost/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "ayf_webhost", + "short_name": "ayf_webhost", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +}

xgl 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 literal 0 HcmV?d00001 diff --git a/flutter/ayf_pack/doc/wikipedia/properties-flutter-multiplatform.vsdx b/flutter/ayf_pack/doc/wikipedia/properties-flutter-multiplatform.vsdx new file mode 100644 index 0000000000000000000000000000000000000000..0b4eae947720bb5058135791298e494b2f2c8845 GIT binary patch literal 19387 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 literal 0 HcmV?d00001 diff --git a/flutter/ayf_pack/example/.ayf_pack_example.env.in b/flutter/ayf_pack/example/.ayf_pack_example.env.in new file mode 100644 index 00000000..4824a438 --- /dev/null +++ b/flutter/ayf_pack/example/.ayf_pack_example.env.in @@ -0,0 +1,3 @@ +AYF_BACKEND_FFI_SHARED_LIBRARY = '@AYFBINRENDER_FFI_DLL@' +AYF_BACKEND_PRELOAD_LIBRARIES = '@AYFBINRENDER_FFI_PRELOAD_DLLS@'; +AYF_BACKEND_EXECUTABLE_IN_BINFOLDER = "yes"; diff --git a/flutter/ayf_pack/example/.gitignore b/flutter/ayf_pack/example/.gitignore new file mode 100644 index 00000000..a8e938c0 --- /dev/null +++ b/flutter/ayf_pack/example/.gitignore @@ -0,0 +1,47 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/flutter/ayf_pack/example/README.md b/flutter/ayf_pack/example/README.md new file mode 100644 index 00000000..c09873d7 --- /dev/null +++ b/flutter/ayf_pack/example/README.md @@ -0,0 +1,16 @@ +# ayf_pack_example + +Demonstrates how to use the ayf_pack plugin. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/flutter/ayf_pack/example/analysis_options.yaml b/flutter/ayf_pack/example/analysis_options.yaml new file mode 100644 index 00000000..61b6c4de --- /dev/null +++ b/flutter/ayf_pack/example/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/flutter/ayf_pack/example/images/ayfbinrender_icons/ayfbinrender-logo.png b/flutter/ayf_pack/example/images/ayfbinrender_icons/ayfbinrender-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..21c7a29e05dafc2fcdfad00fb02e2529fc5bb48e GIT binary patch literal 205019 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