From 08a7f6d10d697b3981a823d714a74d2236f4949d Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 6 Nov 2020 18:04:04 +0800 Subject: [PATCH 001/422] initialize flutter --- flutter_hbb/.gitignore | 41 ++ flutter_hbb/.metadata | 10 + flutter_hbb/README.md | 16 + flutter_hbb/android/.gitignore | 11 + flutter_hbb/android/app/build.gradle | 63 +++ .../android/app/src/debug/AndroidManifest.xml | 7 + .../android/app/src/main/AndroidManifest.xml | 47 ++ .../com/carriez/flutter_hbb/MainActivity.kt | 6 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values/styles.xml | 18 + .../app/src/profile/AndroidManifest.xml | 7 + flutter_hbb/android/build.gradle | 31 ++ flutter_hbb/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 6 + flutter_hbb/android/settings.gradle | 11 + flutter_hbb/ios/.gitignore | 32 ++ .../ios/Flutter/AppFrameworkInfo.plist | 26 + flutter_hbb/ios/Flutter/Debug.xcconfig | 1 + flutter_hbb/ios/Flutter/Release.xcconfig | 1 + .../ios/Runner.xcodeproj/project.pbxproj | 495 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 91 ++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + flutter_hbb/ios/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 122 +++++ .../Icon-App-1024x1024@1x.png | Bin 0 -> 10932 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 564 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 1588 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 1025 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 1716 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 1920 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 1895 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 3831 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 1888 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 3294 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 3612 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/README.md | 5 + .../Runner/Base.lproj/LaunchScreen.storyboard | 37 ++ .../ios/Runner/Base.lproj/Main.storyboard | 26 + flutter_hbb/ios/Runner/Info.plist | 45 ++ .../ios/Runner/Runner-Bridging-Header.h | 1 + flutter_hbb/lib/main.dart | 117 +++++ flutter_hbb/pubspec.lock | 153 ++++++ flutter_hbb/pubspec.yaml | 76 +++ flutter_hbb/test/widget_test.dart | 30 ++ 62 files changed, 1629 insertions(+) create mode 100644 flutter_hbb/.gitignore create mode 100644 flutter_hbb/.metadata create mode 100644 flutter_hbb/README.md create mode 100644 flutter_hbb/android/.gitignore create mode 100644 flutter_hbb/android/app/build.gradle create mode 100644 flutter_hbb/android/app/src/debug/AndroidManifest.xml create mode 100644 flutter_hbb/android/app/src/main/AndroidManifest.xml create mode 100644 flutter_hbb/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt create mode 100644 flutter_hbb/android/app/src/main/res/drawable/launch_background.xml create mode 100644 flutter_hbb/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 flutter_hbb/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 flutter_hbb/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 flutter_hbb/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 flutter_hbb/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 flutter_hbb/android/app/src/main/res/values/styles.xml create mode 100644 flutter_hbb/android/app/src/profile/AndroidManifest.xml create mode 100644 flutter_hbb/android/build.gradle create mode 100644 flutter_hbb/android/gradle.properties create mode 100644 flutter_hbb/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 flutter_hbb/android/settings.gradle create mode 100644 flutter_hbb/ios/.gitignore create mode 100644 flutter_hbb/ios/Flutter/AppFrameworkInfo.plist create mode 100644 flutter_hbb/ios/Flutter/Debug.xcconfig create mode 100644 flutter_hbb/ios/Flutter/Release.xcconfig create mode 100644 flutter_hbb/ios/Runner.xcodeproj/project.pbxproj create mode 100644 flutter_hbb/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 flutter_hbb/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 flutter_hbb/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 flutter_hbb/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 flutter_hbb/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 flutter_hbb/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 flutter_hbb/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 flutter_hbb/ios/Runner/AppDelegate.swift create mode 100644 flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 flutter_hbb/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 flutter_hbb/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 flutter_hbb/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 flutter_hbb/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 flutter_hbb/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 flutter_hbb/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 flutter_hbb/ios/Runner/Base.lproj/Main.storyboard create mode 100644 flutter_hbb/ios/Runner/Info.plist create mode 100644 flutter_hbb/ios/Runner/Runner-Bridging-Header.h create mode 100644 flutter_hbb/lib/main.dart create mode 100644 flutter_hbb/pubspec.lock create mode 100644 flutter_hbb/pubspec.yaml create mode 100644 flutter_hbb/test/widget_test.dart diff --git a/flutter_hbb/.gitignore b/flutter_hbb/.gitignore new file mode 100644 index 000000000..9d532b18a --- /dev/null +++ b/flutter_hbb/.gitignore @@ -0,0 +1,41 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json diff --git a/flutter_hbb/.metadata b/flutter_hbb/.metadata new file mode 100644 index 000000000..107fcb7b5 --- /dev/null +++ b/flutter_hbb/.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: 8874f21e79d7ec66d0457c7ab338348e31b17f1d + channel: stable + +project_type: app diff --git a/flutter_hbb/README.md b/flutter_hbb/README.md new file mode 100644 index 000000000..4c570f839 --- /dev/null +++ b/flutter_hbb/README.md @@ -0,0 +1,16 @@ +# flutter_hbb + +Your Remote Desktop Software + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) + +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/flutter_hbb/android/.gitignore b/flutter_hbb/android/.gitignore new file mode 100644 index 000000000..0a741cb43 --- /dev/null +++ b/flutter_hbb/android/.gitignore @@ -0,0 +1,11 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties diff --git a/flutter_hbb/android/app/build.gradle b/flutter_hbb/android/app/build.gradle new file mode 100644 index 000000000..76b52c785 --- /dev/null +++ b/flutter_hbb/android/app/build.gradle @@ -0,0 +1,63 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 29 + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.carriez.flutter_hbb" + minSdkVersion 16 + targetSdkVersion 29 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} diff --git a/flutter_hbb/android/app/src/debug/AndroidManifest.xml b/flutter_hbb/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000..64d68a588 --- /dev/null +++ b/flutter_hbb/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/flutter_hbb/android/app/src/main/AndroidManifest.xml b/flutter_hbb/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..deb519449 --- /dev/null +++ b/flutter_hbb/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + diff --git a/flutter_hbb/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt b/flutter_hbb/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt new file mode 100644 index 000000000..0f2e8d208 --- /dev/null +++ b/flutter_hbb/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt @@ -0,0 +1,6 @@ +package com.carriez.flutter_hbb + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/flutter_hbb/android/app/src/main/res/drawable/launch_background.xml b/flutter_hbb/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 000000000..304732f88 --- /dev/null +++ b/flutter_hbb/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/flutter_hbb/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/flutter_hbb/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/flutter_hbb/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/flutter_hbb/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/flutter_hbb/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/flutter_hbb/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/flutter_hbb/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/flutter_hbb/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/flutter_hbb/android/app/src/main/res/values/styles.xml b/flutter_hbb/android/app/src/main/res/values/styles.xml new file mode 100644 index 000000000..1f83a33fd --- /dev/null +++ b/flutter_hbb/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/flutter_hbb/android/app/src/profile/AndroidManifest.xml b/flutter_hbb/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 000000000..64d68a588 --- /dev/null +++ b/flutter_hbb/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/flutter_hbb/android/build.gradle b/flutter_hbb/android/build.gradle new file mode 100644 index 000000000..3100ad2d5 --- /dev/null +++ b/flutter_hbb/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.3.50' + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.5.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/flutter_hbb/android/gradle.properties b/flutter_hbb/android/gradle.properties new file mode 100644 index 000000000..94adc3a3f --- /dev/null +++ b/flutter_hbb/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/flutter_hbb/android/gradle/wrapper/gradle-wrapper.properties b/flutter_hbb/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..296b146b7 --- /dev/null +++ b/flutter_hbb/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/flutter_hbb/android/settings.gradle b/flutter_hbb/android/settings.gradle new file mode 100644 index 000000000..44e62bcf0 --- /dev/null +++ b/flutter_hbb/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/flutter_hbb/ios/.gitignore b/flutter_hbb/ios/.gitignore new file mode 100644 index 000000000..e96ef602b --- /dev/null +++ b/flutter_hbb/ios/.gitignore @@ -0,0 +1,32 @@ +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/flutter_hbb/ios/Flutter/AppFrameworkInfo.plist b/flutter_hbb/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 000000000..f2872cf47 --- /dev/null +++ b/flutter_hbb/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 9.0 + + diff --git a/flutter_hbb/ios/Flutter/Debug.xcconfig b/flutter_hbb/ios/Flutter/Debug.xcconfig new file mode 100644 index 000000000..592ceee85 --- /dev/null +++ b/flutter_hbb/ios/Flutter/Debug.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/flutter_hbb/ios/Flutter/Release.xcconfig b/flutter_hbb/ios/Flutter/Release.xcconfig new file mode 100644 index 000000000..592ceee85 --- /dev/null +++ b/flutter_hbb/ios/Flutter/Release.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj b/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000..3ed44dd0c --- /dev/null +++ b/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,495 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1020; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.carriez.flutterHbb; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.carriez.flutterHbb; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.carriez.flutterHbb; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/flutter_hbb/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/flutter_hbb/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..1d526a16e --- /dev/null +++ b/flutter_hbb/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/flutter_hbb/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/flutter_hbb/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/flutter_hbb/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/flutter_hbb/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/flutter_hbb/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000..f9b0d7c5e --- /dev/null +++ b/flutter_hbb/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/flutter_hbb/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/flutter_hbb/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000..a28140cfd --- /dev/null +++ b/flutter_hbb/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter_hbb/ios/Runner.xcworkspace/contents.xcworkspacedata b/flutter_hbb/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..1d526a16e --- /dev/null +++ b/flutter_hbb/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/flutter_hbb/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/flutter_hbb/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/flutter_hbb/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/flutter_hbb/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/flutter_hbb/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000..f9b0d7c5e --- /dev/null +++ b/flutter_hbb/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/flutter_hbb/ios/Runner/AppDelegate.swift b/flutter_hbb/ios/Runner/AppDelegate.swift new file mode 100644 index 000000000..70693e4a8 --- /dev/null +++ b/flutter_hbb/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..d36b1fab2 --- /dev/null +++ b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..dc9ada4725e9b0ddb1deab583e5b5102493aa332 GIT binary patch literal 10932 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 literal 0 HcmV?d00001 diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f091b6b0bca859a3f474b03065bef75ba58a9e4c GIT binary patch literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ literal 0 HcmV?d00001 diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d0ef06e7edb86cdfe0d15b4b0d98334a86163658 GIT binary patch literal 1716 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 literal 0 HcmV?d00001 diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c8f9ed8f5cee1c98386d13b17e89f719e83555b2 GIT binary patch literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 literal 0 HcmV?d00001 diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..75b2d164a5a98e212cca15ea7bf2ab5de5108680 GIT binary patch literal 3831 zcmVjJBgitF5mAp-i>4+KS_oR{|13AP->1TD4=w)g|)JHOx|a2Wk1Va z!k)vP$UcQ#mdj%wNQoaJ!w>jv_6&JPyutpQps?s5dmDQ>`%?Bvj>o<%kYG!YW6H-z zu`g$@mp`;qDR!51QaS}|ZToSuAGcJ7$2HF0z`ln4t!#Yg46>;vGG9N9{V@9z#}6v* zfP?}r6b{*-C*)(S>NECI_E~{QYzN5SXRmVnP<=gzP+_Sp(Aza_hKlZ{C1D&l*(7IKXxQC1Z9#6wx}YrGcn~g%;icdw>T0Rf^w0{ z$_wn1J+C0@!jCV<%Go5LA45e{5gY9PvZp8uM$=1}XDI+9m7!A95L>q>>oe0$nC->i zeexUIvq%Uk<-$>DiDb?!In)lAmtuMWxvWlk`2>4lNuhSsjAf2*2tjT`y;@d}($o)S zn(+W&hJ1p0xy@oxP%AM15->wPLp{H!k)BdBD$toBpJh+crWdsNV)qsHaqLg2_s|Ih z`8E9z{E3sA!}5aKu?T!#enD(wLw?IT?k-yWVHZ8Akz4k5(TZJN^zZgm&zM28sfTD2BYJ|Fde3Xzh;;S` z=GXTnY4Xc)8nYoz6&vF;P7{xRF-{|2Xs5>a5)@BrnQ}I(_x7Cgpx#5&Td^4Q9_FnQ zX5so*;#8-J8#c$OlA&JyPp$LKUhC~-e~Ij!L%uSMu!-VZG7Hx-L{m2DVR2i=GR(_% zCVD!4N`I)&Q5S`?P&fQZ=4#Dgt_v2-DzkT}K(9gF0L(owe-Id$Rc2qZVLqI_M_DyO z9@LC#U28_LU{;wGZ&))}0R2P4MhajKCd^K#D+JJ&JIXZ_p#@+7J9A&P<0kdRujtQ_ zOy>3=C$kgi6$0pW06KaLz!21oOryKM3ZUOWqppndxfH}QpgjEJ`j7Tzn5bk6K&@RA?vl##y z$?V~1E(!wB5rH`>3nc&@)|#<1dN2cMzzm=PGhQ|Yppne(C-Vlt450IXc`J4R0W@I7 zd1e5uW6juvO%ni(WX7BsKx3MLngO7rHO;^R5I~0^nE^9^E_eYLgiR9&KnJ)pBbfno zSVnW$0R+&6jOOsZ82}nJ126+c|%svPo;TeUku<2G7%?$oft zyaO;tVo}(W)VsTUhq^XmFi#2z%-W9a{7mXn{uzivYQ_d6b7VJG{77naW(vHt-uhnY zVN#d!JTqVh(7r-lhtXVU6o})aZbDt_;&wJVGl2FKYFBFpU-#9U)z#(A%=IVnqytR$SY-sO( z($oNE09{D^@OuYPz&w~?9>Fl5`g9u&ecFGhqX=^#fmR=we0CJw+5xna*@oHnkahk+ z9aWeE3v|An+O5%?4fA&$Fgu~H_YmqR!yIU!bFCk4!#pAj%(lI(A5n)n@Id#M)O9Yx zJU9oKy{sRAIV3=5>(s8n{8ryJ!;ho}%pn6hZKTKbqk=&m=f*UnK$zW3YQP*)pw$O* zIfLA^!-bmBl6%d_n$#tP8Zd_(XdA*z*WH|E_yILwjtI~;jK#v-6jMl^?<%Y%`gvpwv&cFb$||^v4D&V=aNy?NGo620jL3VZnA%s zH~I|qPzB~e(;p;b^gJr7Ure#7?8%F0m4vzzPy^^(q4q1OdthF}Fi*RmVZN1OwTsAP zn9CZP`FazX3^kG(KodIZ=Kty8DLTy--UKfa1$6XugS zk%6v$Kmxt6U!YMx0JQ)0qX*{CXwZZk$vEROidEc7=J-1;peNat!vS<3P-FT5po>iE z!l3R+<`#x|+_hw!HjQGV=8!q|76y8L7N8gP3$%0kfush|u0uU^?dKBaeRSBUpOZ0c z62;D&Mdn2}N}xHRFTRI?zRv=>=AjHgH}`2k4WK=#AHB)UFrR-J87GgX*x5fL^W2#d z=(%K8-oZfMO=i{aWRDg=FX}UubM4eotRDcn;OR#{3q=*?3mE3_oJ-~prjhxh%PgQT zyn)Qozaq0@o&|LEgS{Ind4Swsr;b`u185hZPOBLL<`d2%^Yp1?oL)=jnLi;Zo0ZDliTtQ^b5SmfIMe{T==zZkbvn$KTQGlbG8w}s@M3TZnde;1Am46P3juKb zl9GU&3F=q`>j!`?SyH#r@O59%@aMX^rx}Nxe<>NqpUp5=lX1ojGDIR*-D^SDuvCKF z?3$xG(gVUsBERef_YjPFl^rU9EtD{pt z0CXwpN7BN3!8>hajGaTVk-wl=9rxmfWtIhC{mheHgStLi^+Nz12a?4r(fz)?3A%at zMlvQmL<2-R)-@G1wJ0^zQK%mR=r4d{Y3fHp){nWXUL#|CqXl(+v+qDh>FkF9`eWrW zfr^D%LNfOcTNvtx0JXR35J0~Jpi2#P3Q&80w+nqNfc}&G0A~*)lGHKv=^FE+b(37|)zL;KLF>oiGfb(?&1 zV3XRu!Sw>@quKiab%g6jun#oZ%!>V#A%+lNc?q>6+VvyAn=kf_6z^(TZUa4Eelh{{ zqFX-#dY(EV@7l$NE&kv9u9BR8&Ojd#ZGJ6l8_BW}^r?DIS_rU2(XaGOK z225E@kH5Opf+CgD^{y29jD4gHbGf{1MD6ggQ&%>UG4WyPh5q_tb`{@_34B?xfSO*| zZv8!)q;^o-bz`MuxXk*G^}(6)ACb@=Lfs`Hxoh>`Y0NE8QRQ!*p|SH@{r8=%RKd4p z+#Ty^-0kb=-H-O`nAA3_6>2z(D=~Tbs(n8LHxD0`R0_ATFqp-SdY3(bZ3;VUM?J=O zKCNsxsgt@|&nKMC=*+ZqmLHhX1KHbAJs{nGVMs6~TiF%Q)P@>!koa$%oS zjXa=!5>P`vC-a}ln!uH1ooeI&v?=?v7?1n~P(wZ~0>xWxd_Aw;+}9#eULM7M8&E?Y zC-ZLhi3RoM92SXUb-5i-Lmt5_rfjE{6y^+24`y$1lywLyHO!)Boa7438K4#iLe?rh z2O~YGSgFUBH?og*6=r9rme=peP~ah`(8Zt7V)j5!V0KPFf_mebo3z95U8(up$-+EA^9dTRLq>Yl)YMBuch9%=e5B`Vnb>o zt03=kq;k2TgGe4|lGne&zJa~h(UGutjP_zr?a7~#b)@15XNA>Dj(m=gg2Q5V4-$)D|Q9}R#002ovPDHLkV1o7DH3k3x literal 0 HcmV?d00001 diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..c4df70d39da7941ef3f6dcb7f06a192d8dcb308d GIT binary patch literal 1888 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 iW8Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/flutter_hbb/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/flutter_hbb/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/flutter_hbb/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 000000000..89c2725b7 --- /dev/null +++ b/flutter_hbb/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/flutter_hbb/ios/Runner/Base.lproj/LaunchScreen.storyboard b/flutter_hbb/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000..f2e259c7c --- /dev/null +++ b/flutter_hbb/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter_hbb/ios/Runner/Base.lproj/Main.storyboard b/flutter_hbb/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 000000000..f3c28516f --- /dev/null +++ b/flutter_hbb/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter_hbb/ios/Runner/Info.plist b/flutter_hbb/ios/Runner/Info.plist new file mode 100644 index 000000000..647671745 --- /dev/null +++ b/flutter_hbb/ios/Runner/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + RustDesk + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/flutter_hbb/ios/Runner/Runner-Bridging-Header.h b/flutter_hbb/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 000000000..308a2a560 --- /dev/null +++ b/flutter_hbb/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/flutter_hbb/lib/main.dart b/flutter_hbb/lib/main.dart new file mode 100644 index 000000000..11655b668 --- /dev/null +++ b/flutter_hbb/lib/main.dart @@ -0,0 +1,117 @@ +import 'package:flutter/material.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + // This is the theme of your application. + // + // Try running your application with "flutter run". You'll see the + // application has a blue toolbar. Then, without quitting the app, try + // changing the primarySwatch below to Colors.green and then invoke + // "hot reload" (press "r" in the console where you ran "flutter run", + // or simply save your changes to "hot reload" in a Flutter IDE). + // Notice that the counter didn't reset back to zero; the application + // is not restarted. + primarySwatch: Colors.blue, + // This makes the visual density adapt to the platform that you run + // the app on. For desktop platforms, the controls will be smaller and + // closer together (more dense) than on mobile platforms. + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: MyHomePage(title: 'Flutter Demo Home Page'), + ); + } +} + +class MyHomePage extends StatefulWidget { + MyHomePage({Key key, this.title}) : super(key: key); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + int _counter = 0; + + void _incrementCounter() { + setState(() { + // This call to setState tells the Flutter framework that something has + // changed in this State, which causes it to rerun the build method below + // so that the display can reflect the updated values. If we changed + // _counter without calling setState(), then the build method would not be + // called again, and so nothing would appear to happen. + _counter++; + }); + } + + @override + Widget build(BuildContext context) { + // This method is rerun every time setState is called, for instance as done + // by the _incrementCounter method above. + // + // The Flutter framework has been optimized to make rerunning build methods + // fast, so that you can just rebuild anything that needs updating rather + // than having to individually change instances of widgets. + return Scaffold( + appBar: AppBar( + // Here we take the value from the MyHomePage object that was created by + // the App.build method, and use it to set our appbar title. + title: Text(widget.title), + ), + body: Center( + // Center is a layout widget. It takes a single child and positions it + // in the middle of the parent. + child: Column( + // Column is also a layout widget. It takes a list of children and + // arranges them vertically. By default, it sizes itself to fit its + // children horizontally, and tries to be as tall as its parent. + // + // Invoke "debug painting" (press "p" in the console, choose the + // "Toggle Debug Paint" action from the Flutter Inspector in Android + // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) + // to see the wireframe for each widget. + // + // Column has various properties to control how it sizes itself and + // how it positions its children. Here we use mainAxisAlignment to + // center the children vertically; the main axis here is the vertical + // axis because Columns are vertical (the cross axis would be + // horizontal). + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'You have pushed the button this many times:', + ), + Text( + '$_counter', + style: Theme.of(context).textTheme.headline4, + ), + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: _incrementCounter, + tooltip: 'Increment', + child: Icon(Icons.add), + ), // This trailing comma makes auto-formatting nicer for build methods. + ); + } +} diff --git a/flutter_hbb/pubspec.lock b/flutter_hbb/pubspec.lock new file mode 100644 index 000000000..f628010e7 --- /dev/null +++ b/flutter_hbb/pubspec.lock @@ -0,0 +1,153 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.5.0-nullsafety.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.1" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.3" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.1" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.15.0-nullsafety.3" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.10-nullsafety.1" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0-nullsafety.3" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0-nullsafety.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0-nullsafety.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0-nullsafety.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.19-nullsafety.2" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0-nullsafety.3" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.3" +sdks: + dart: ">=2.10.0-110 <2.11.0" diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml new file mode 100644 index 000000000..634d383f2 --- /dev/null +++ b/flutter_hbb/pubspec.yaml @@ -0,0 +1,76 @@ +name: flutter_hbb +description: Your Remote Desktop Software + +# The following line prevents the package from being accidentally published to +# pub.dev using `pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# 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 used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +version: 1.0.0+1 + +environment: + sdk: ">=2.7.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware. + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/flutter_hbb/test/widget_test.dart b/flutter_hbb/test/widget_test.dart new file mode 100644 index 000000000..8a8415e94 --- /dev/null +++ b/flutter_hbb/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 that Flutter provides. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:flutter_hbb/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(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); + }); +} From f0ee5e602db4a70582a45ddc9df246f5913522a7 Mon Sep 17 00:00:00 2001 From: open-trade Date: Sun, 8 Nov 2020 22:38:42 +0800 Subject: [PATCH 002/422] add so --- .../android/app/src/main/jniLibs/arm64-v8a/librustdesk.so | 1 + flutter_hbb/android/app/src/main/jniLibs/x86/librustdesk.so | 1 + 2 files changed, 2 insertions(+) create mode 120000 flutter_hbb/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so create mode 120000 flutter_hbb/android/app/src/main/jniLibs/x86/librustdesk.so diff --git a/flutter_hbb/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so b/flutter_hbb/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so new file mode 120000 index 000000000..0684cadc9 --- /dev/null +++ b/flutter_hbb/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so @@ -0,0 +1 @@ +../../../../../../../target/aarch64-linux-android/release/librustdesk.so \ No newline at end of file diff --git a/flutter_hbb/android/app/src/main/jniLibs/x86/librustdesk.so b/flutter_hbb/android/app/src/main/jniLibs/x86/librustdesk.so new file mode 120000 index 000000000..a4e9274f6 --- /dev/null +++ b/flutter_hbb/android/app/src/main/jniLibs/x86/librustdesk.so @@ -0,0 +1 @@ +../../../../../../../target/x86_64-linux-android/release/librustdesk.so \ No newline at end of file From 1f136f2ca02007c2ca46ce60e7b1833e4298db7e Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 9 Nov 2020 01:57:17 +0800 Subject: [PATCH 003/422] test flutter if practical --- .../app/src/main/jniLibs/{x86 => x86_64}/librustdesk.so | 0 flutter_hbb/android/gradle.properties | 1 + flutter_hbb/lib/main.dart | 7 +++++++ flutter_hbb/pubspec.lock | 7 +++++++ flutter_hbb/pubspec.yaml | 1 + 5 files changed, 16 insertions(+) rename flutter_hbb/android/app/src/main/jniLibs/{x86 => x86_64}/librustdesk.so (100%) diff --git a/flutter_hbb/android/app/src/main/jniLibs/x86/librustdesk.so b/flutter_hbb/android/app/src/main/jniLibs/x86_64/librustdesk.so similarity index 100% rename from flutter_hbb/android/app/src/main/jniLibs/x86/librustdesk.so rename to flutter_hbb/android/app/src/main/jniLibs/x86_64/librustdesk.so diff --git a/flutter_hbb/android/gradle.properties b/flutter_hbb/android/gradle.properties index 94adc3a3f..a6738207f 100644 --- a/flutter_hbb/android/gradle.properties +++ b/flutter_hbb/android/gradle.properties @@ -1,3 +1,4 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true +android.enableR8=true diff --git a/flutter_hbb/lib/main.dart b/flutter_hbb/lib/main.dart index 11655b668..f5b49ab62 100644 --- a/flutter_hbb/lib/main.dart +++ b/flutter_hbb/lib/main.dart @@ -1,6 +1,13 @@ import 'package:flutter/material.dart'; +import 'dart:io' show Platform; +import 'dart:ffi'; void main() { + final dylib = Platform.isAndroid ? DynamicLibrary.open('librustdesk.so') : DynamicLibrary.process(); + final initialize = dylib.lookupFunction('initialize'); + initialize(); + // final connect = dylib.lookupFunction), void Function(Pointer)>('connect'); + // connect(Utf8.toUtf8('test')); runApp(MyApp()); } diff --git a/flutter_hbb/pubspec.lock b/flutter_hbb/pubspec.lock index f628010e7..6efd799b4 100644 --- a/flutter_hbb/pubspec.lock +++ b/flutter_hbb/pubspec.lock @@ -57,6 +57,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0-nullsafety.1" + ffi: + dependency: "direct main" + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" flutter: dependency: "direct main" description: flutter diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index 634d383f2..c9ceec9f6 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -28,6 +28,7 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.0 + ffi: ^0.1.3 dev_dependencies: flutter_test: From f043940152b4657b44d56c2c098d2a0ade38aee1 Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 9 Nov 2020 18:25:55 +0800 Subject: [PATCH 004/422] set app_dir via ffi --- .../android/app/src/main/AndroidManifest.xml | 3 +- flutter_hbb/lib/main.dart | 16 +++- flutter_hbb/pubspec.lock | 85 +++++++++++++++++++ flutter_hbb/pubspec.yaml | 1 + 4 files changed, 100 insertions(+), 5 deletions(-) diff --git a/flutter_hbb/android/app/src/main/AndroidManifest.xml b/flutter_hbb/android/app/src/main/AndroidManifest.xml index deb519449..c107abd8c 100644 --- a/flutter_hbb/android/app/src/main/AndroidManifest.xml +++ b/flutter_hbb/android/app/src/main/AndroidManifest.xml @@ -43,5 +43,6 @@ - + + diff --git a/flutter_hbb/lib/main.dart b/flutter_hbb/lib/main.dart index f5b49ab62..a3f287ca9 100644 --- a/flutter_hbb/lib/main.dart +++ b/flutter_hbb/lib/main.dart @@ -1,20 +1,28 @@ +import 'package:ffi/ffi.dart'; import 'package:flutter/material.dart'; -import 'dart:io' show Platform; +import 'package:path_provider/path_provider.dart'; +import 'dart:io'; import 'dart:ffi'; +import 'dart:async'; void main() { - final dylib = Platform.isAndroid ? DynamicLibrary.open('librustdesk.so') : DynamicLibrary.process(); - final initialize = dylib.lookupFunction('initialize'); - initialize(); // final connect = dylib.lookupFunction), void Function(Pointer)>('connect'); // connect(Utf8.toUtf8('test')); runApp(MyApp()); } +Future initialzeFFI() async { + final dylib = Platform.isAndroid ? DynamicLibrary.open('librustdesk.so') : DynamicLibrary.process(); + String dir = (await getApplicationDocumentsDirectory()).path; + final initialize = dylib.lookupFunction), void Function(Pointer)>('initialize'); + initialize(Utf8.toUtf8(dir)); +} + class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { + initialzeFFI(); return MaterialApp( title: 'Flutter Demo', theme: ThemeData( diff --git a/flutter_hbb/pubspec.lock b/flutter_hbb/pubspec.lock index 6efd799b4..7cb6b1fb0 100644 --- a/flutter_hbb/pubspec.lock +++ b/flutter_hbb/pubspec.lock @@ -64,6 +64,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.1.3" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "5.2.1" flutter: dependency: "direct main" description: flutter @@ -74,6 +81,13 @@ packages: description: flutter source: sdk version: "0.0.0" + intl: + dependency: transitive + description: + name: intl + url: "https://pub.dartlang.org" + source: hosted + version: "0.16.1" matcher: dependency: transitive description: @@ -95,6 +109,62 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0-nullsafety.1" + path_provider: + dependency: "direct main" + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "1.6.24" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.1+2" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4+6" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4+3" + platform: + dependency: transitive + description: + name: platform + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.1" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + process: + dependency: transitive + description: + name: process + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.13" sky_engine: dependency: transitive description: flutter @@ -156,5 +226,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0-nullsafety.3" + win32: + dependency: transitive + description: + name: win32 + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.3" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.2" sdks: dart: ">=2.10.0-110 <2.11.0" + flutter: ">=1.12.13+hotfix.5 <2.0.0" diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index c9ceec9f6..921f3fe6d 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -29,6 +29,7 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.0 ffi: ^0.1.3 + path_provider: ^1.6.24 dev_dependencies: flutter_test: From 5972f25aaf470b75e12a4b347be016ff00780d06 Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 12 Nov 2020 12:09:46 +0800 Subject: [PATCH 005/422] APP_DIR --- flutter_hbb/android/app/src/main/AndroidManifest.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flutter_hbb/android/app/src/main/AndroidManifest.xml b/flutter_hbb/android/app/src/main/AndroidManifest.xml index c107abd8c..4ef1e59fa 100644 --- a/flutter_hbb/android/app/src/main/AndroidManifest.xml +++ b/flutter_hbb/android/app/src/main/AndroidManifest.xml @@ -45,4 +45,6 @@ android:value="2" /> + + From 489ccdb53f72257890bf856de2b58de6e8ffdb69 Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 13 Nov 2020 17:35:32 +0800 Subject: [PATCH 006/422] finally got android crash reason, it is because my c function connect override system connect --- flutter_hbb/lib/main.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flutter_hbb/lib/main.dart b/flutter_hbb/lib/main.dart index a3f287ca9..708c2926f 100644 --- a/flutter_hbb/lib/main.dart +++ b/flutter_hbb/lib/main.dart @@ -6,8 +6,8 @@ import 'dart:ffi'; import 'dart:async'; void main() { - // final connect = dylib.lookupFunction), void Function(Pointer)>('connect'); - // connect(Utf8.toUtf8('test')); + // final connect_remote = dylib.lookupFunction), void Function(Pointer)>('connect_remote'); + // connect_remote(Utf8.toUtf8('test')); runApp(MyApp()); } From e4681f7d9a38248f99fca0a679b9aab91dcc7efd Mon Sep 17 00:00:00 2001 From: open-trade Date: Sun, 15 Nov 2020 20:04:05 +0800 Subject: [PATCH 007/422] add provider --- flutter_hbb/lib/common.dart | 48 ++++++++++++ flutter_hbb/lib/home_page.dart | 122 +++++++++++++++++++++++++++++ flutter_hbb/lib/main.dart | 139 ++++----------------------------- flutter_hbb/pubspec.lock | 18 ++++- flutter_hbb/pubspec.yaml | 1 + 5 files changed, 203 insertions(+), 125 deletions(-) create mode 100644 flutter_hbb/lib/common.dart create mode 100644 flutter_hbb/lib/home_page.dart diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart new file mode 100644 index 000000000..d424e7e85 --- /dev/null +++ b/flutter_hbb/lib/common.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; +import 'package:ffi/ffi.dart'; +import 'package:path_provider/path_provider.dart'; +import 'dart:io'; +import 'dart:ffi'; +import 'dart:async'; + +class HexColor extends Color { + HexColor(final String hexColor) : super(_getColorFromHex(hexColor)); + + static int _getColorFromHex(String hexColor) { + hexColor = hexColor.toUpperCase().replaceAll('#', ''); + if (hexColor.length == 6) { + hexColor = 'FF' + hexColor; + } + return int.parse(hexColor, radix: 16); + } +} + +// https://juejin.im/post/6844903864852807694 +class FfiModel with ChangeNotifier { + var _connectRemote; + + FfiModel() { + initialzeFFI(); + } + + void addRemote() { + notifyListeners(); + } + + void connect(String id) { + _connectRemote(Utf8.toUtf8(id)); + } + + Future initialzeFFI() async { + final dylib = Platform.isAndroid + ? DynamicLibrary.open('librustdesk.so') + : DynamicLibrary.process(); + final initialize = dylib.lookupFunction), + void Function(Pointer)>('initialize'); + _connectRemote = dylib.lookupFunction), + void Function(Pointer)>('connect_remote'); + final dir = (await getApplicationDocumentsDirectory()).path; + initialize(Utf8.toUtf8(dir)); + notifyListeners(); + } +} diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart new file mode 100644 index 000000000..c96f307f0 --- /dev/null +++ b/flutter_hbb/lib/home_page.dart @@ -0,0 +1,122 @@ +import 'package:flutter/material.dart'; +import 'common.dart'; + +class HomePage extends StatefulWidget { + HomePage({Key key, this.title}) : super(key: key); + + final String title; + + @override + _HomePageState createState() => _HomePageState(); +} + +class _HomePageState extends State { + int _counter = 0; + + void _incrementCounter() { + setState(() { + _counter++; + }); + } + + @override + Widget build(BuildContext context) { + // This method is rerun every time setState is called + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'You have pushed the button this many times:', + ), + Text( + '$_counter', + style: Theme.of(context).textTheme.headline4, + ), + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: _incrementCounter, + tooltip: 'Increment', + child: Icon(Icons.add), + ), + ); + } + + Widget getSearchBarUI() { + return Padding( + padding: const EdgeInsets.only(top: 8.0, left: 18), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: MediaQuery.of(context).size.width * 0.75, + height: 64, + child: Padding( + padding: const EdgeInsets.only(top: 8, bottom: 8), + child: Container( + decoration: BoxDecoration( + color: HexColor('#F8FAFB'), + borderRadius: const BorderRadius.only( + bottomRight: Radius.circular(13.0), + bottomLeft: Radius.circular(13.0), + topLeft: Radius.circular(13.0), + topRight: Radius.circular(13.0), + ), + ), + child: Row( + children: [ + Expanded( + child: Container( + padding: const EdgeInsets.only(left: 16, right: 16), + child: TextFormField( + style: TextStyle( + fontFamily: 'WorkSans', + fontWeight: FontWeight.bold, + fontSize: 16, + color: Color(0xFF00B6F0), + ), + keyboardType: TextInputType.text, + decoration: InputDecoration( + labelText: 'Search for course', + border: InputBorder.none, + helperStyle: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + color: HexColor('#B9BABC'), + ), + labelStyle: TextStyle( + fontWeight: FontWeight.w600, + fontSize: 16, + letterSpacing: 0.2, + color: HexColor('#B9BABC'), + ), + ), + onEditingComplete: () {}, + ), + ), + ), + SizedBox( + width: 60, + height: 60, + child: Icon(Icons.search, color: HexColor('#B9BABC')), + ) + ], + ), + ), + ), + ), + const Expanded( + child: SizedBox(), + ) + ], + ), + ); + } +} diff --git a/flutter_hbb/lib/main.dart b/flutter_hbb/lib/main.dart index 708c2926f..959de8fbe 100644 --- a/flutter_hbb/lib/main.dart +++ b/flutter_hbb/lib/main.dart @@ -1,132 +1,25 @@ -import 'package:ffi/ffi.dart'; import 'package:flutter/material.dart'; -import 'package:path_provider/path_provider.dart'; -import 'dart:io'; -import 'dart:ffi'; -import 'dart:async'; +import 'package:provider/provider.dart'; +import 'common.dart'; +import 'home_page.dart'; void main() { - // final connect_remote = dylib.lookupFunction), void Function(Pointer)>('connect_remote'); - // connect_remote(Utf8.toUtf8('test')); - runApp(MyApp()); + runApp(_App()); } -Future initialzeFFI() async { - final dylib = Platform.isAndroid ? DynamicLibrary.open('librustdesk.so') : DynamicLibrary.process(); - String dir = (await getApplicationDocumentsDirectory()).path; - final initialize = dylib.lookupFunction), void Function(Pointer)>('initialize'); - initialize(Utf8.toUtf8(dir)); -} - -class MyApp extends StatelessWidget { - // This widget is the root of your application. +class _App extends StatelessWidget { @override Widget build(BuildContext context) { - initialzeFFI(); - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - // This is the theme of your application. - // - // Try running your application with "flutter run". You'll see the - // application has a blue toolbar. Then, without quitting the app, try - // changing the primarySwatch below to Colors.green and then invoke - // "hot reload" (press "r" in the console where you ran "flutter run", - // or simply save your changes to "hot reload" in a Flutter IDE). - // Notice that the counter didn't reset back to zero; the application - // is not restarted. - primarySwatch: Colors.blue, - // This makes the visual density adapt to the platform that you run - // the app on. For desktop platforms, the controls will be smaller and - // closer together (more dense) than on mobile platforms. - visualDensity: VisualDensity.adaptivePlatformDensity, - ), - home: MyHomePage(title: 'Flutter Demo Home Page'), - ); - } -} - -class MyHomePage extends StatefulWidget { - MyHomePage({Key key, this.title}) : super(key: key); - - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - final String title; - - @override - _MyHomePageState createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; - }); - } - - @override - Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. - return Scaffold( - appBar: AppBar( - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), - ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Invoke "debug painting" (press "p" in the console, choose the - // "Toggle Debug Paint" action from the Flutter Inspector in Android - // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) - // to see the wireframe for each widget. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'You have pushed the button this many times:', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.headline4, - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. - ); + var ffi = FfiModel(); + return ChangeNotifierProvider.value( + value: ffi, + child: MaterialApp( + title: 'RustDesk', + theme: ThemeData( + primarySwatch: Colors.blue, + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: HomePage(title: 'RustDesk'), + )); } } diff --git a/flutter_hbb/pubspec.lock b/flutter_hbb/pubspec.lock index 7cb6b1fb0..758ed271c 100644 --- a/flutter_hbb/pubspec.lock +++ b/flutter_hbb/pubspec.lock @@ -102,6 +102,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.0-nullsafety.3" + nested: + dependency: transitive + description: + name: nested + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4" path: dependency: transitive description: @@ -165,6 +172,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.13" + provider: + dependency: "direct main" + description: + name: provider + url: "https://pub.dartlang.org" + source: hosted + version: "4.3.2+2" sky_engine: dependency: transitive description: flutter @@ -232,7 +246,7 @@ packages: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "1.7.3" + version: "1.7.4" xdg_directories: dependency: transitive description: @@ -242,4 +256,4 @@ packages: version: "0.1.2" sdks: dart: ">=2.10.0-110 <2.11.0" - flutter: ">=1.12.13+hotfix.5 <2.0.0" + flutter: ">=1.16.0 <2.0.0" diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index 921f3fe6d..ad8c52b05 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -30,6 +30,7 @@ dependencies: cupertino_icons: ^1.0.0 ffi: ^0.1.3 path_provider: ^1.6.24 + provider: ^4.3.2+2 dev_dependencies: flutter_test: From da7a71cd8101e2b6f2741bf99d2df26064390685 Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 16 Nov 2020 01:13:26 +0800 Subject: [PATCH 008/422] connect ui --- flutter_hbb/lib/common.dart | 13 ++- flutter_hbb/lib/home_page.dart | 179 +++++++++++++++--------------- flutter_hbb/lib/main.dart | 4 +- flutter_hbb/test/widget_test.dart | 2 +- 4 files changed, 100 insertions(+), 98 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index d424e7e85..a14bd9c1a 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -17,9 +17,16 @@ class HexColor extends Color { } } +class MyTheme { + static const Color grayBg = Color(0xFFEEEEEE); + static const Color white = Color(0xFFFFFFFF); +} + +typedef F1 = void Function(Pointer); + // https://juejin.im/post/6844903864852807694 class FfiModel with ChangeNotifier { - var _connectRemote; + F1 _connectRemote; FfiModel() { initialzeFFI(); @@ -39,8 +46,8 @@ class FfiModel with ChangeNotifier { : DynamicLibrary.process(); final initialize = dylib.lookupFunction), void Function(Pointer)>('initialize'); - _connectRemote = dylib.lookupFunction), - void Function(Pointer)>('connect_remote'); + _connectRemote = dylib + .lookupFunction), F1>('connect_remote'); final dir = (await getApplicationDocumentsDirectory()).path; initialize(Utf8.toUtf8(dir)); notifyListeners(); diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index c96f307f0..7c076bdad 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'common.dart'; class HomePage extends StatefulWidget { @@ -11,112 +12,106 @@ class HomePage extends StatefulWidget { } class _HomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - _counter++; - }); - } + final idController = TextEditingController(); + FfiModel ffi; @override Widget build(BuildContext context) { + ffi = Provider.of(context); + // This method is rerun every time setState is called return Scaffold( - appBar: AppBar( - title: Text(widget.title), - ), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'You have pushed the button this many times:', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.headline4, - ), - ], + appBar: AppBar( + title: Text(widget.title), ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: Icon(Icons.add), - ), - ); + body: Container( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + getSearchBarUI(), + Expanded(child: Container()) + ]), + color: MyTheme.grayBg, + padding: const EdgeInsets.fromLTRB(16.0, 0.0, 16.0, 0.0), + )); + } + + void onConnect() { + ffi.connect(idController.text); } Widget getSearchBarUI() { return Padding( - padding: const EdgeInsets.only(top: 8.0, left: 18), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: MediaQuery.of(context).size.width * 0.75, - height: 64, - child: Padding( - padding: const EdgeInsets.only(top: 8, bottom: 8), - child: Container( - decoration: BoxDecoration( - color: HexColor('#F8FAFB'), - borderRadius: const BorderRadius.only( - bottomRight: Radius.circular(13.0), - bottomLeft: Radius.circular(13.0), - topLeft: Radius.circular(13.0), - topRight: Radius.circular(13.0), - ), - ), - child: Row( - children: [ - Expanded( - child: Container( - padding: const EdgeInsets.only(left: 16, right: 16), - child: TextFormField( - style: TextStyle( - fontFamily: 'WorkSans', - fontWeight: FontWeight.bold, - fontSize: 16, - color: Color(0xFF00B6F0), - ), - keyboardType: TextInputType.text, - decoration: InputDecoration( - labelText: 'Search for course', - border: InputBorder.none, - helperStyle: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - color: HexColor('#B9BABC'), - ), - labelStyle: TextStyle( - fontWeight: FontWeight.w600, - fontSize: 16, - letterSpacing: 0.2, - color: HexColor('#B9BABC'), - ), - ), - onEditingComplete: () {}, - ), - ), - ), - SizedBox( - width: 60, - height: 60, - child: Icon(Icons.search, color: HexColor('#B9BABC')), - ) - ], - ), + padding: const EdgeInsets.only(top: 8.0), + child: Container( + height: 84, + child: Padding( + padding: const EdgeInsets.only(top: 8, bottom: 8), + child: Container( + decoration: BoxDecoration( + color: MyTheme.white, + borderRadius: const BorderRadius.only( + bottomRight: Radius.circular(13.0), + bottomLeft: Radius.circular(13.0), + topLeft: Radius.circular(13.0), + topRight: Radius.circular(13.0), ), ), + child: Row( + children: [ + Expanded( + child: Container( + padding: const EdgeInsets.only(left: 16, right: 16), + child: TextFormField( + style: TextStyle( + fontFamily: 'WorkSans', + fontWeight: FontWeight.bold, + fontSize: 30, + color: Color(0xFF00B6F0), + ), + keyboardType: TextInputType.text, + decoration: InputDecoration( + labelText: 'Remote ID', + border: InputBorder.none, + helperStyle: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + color: HexColor('#B9BABC'), + ), + labelStyle: TextStyle( + fontWeight: FontWeight.w600, + fontSize: 16, + letterSpacing: 0.2, + color: HexColor('#B9BABC'), + ), + ), + autofocus: false, + controller: idController, + ), + ), + ), + SizedBox( + width: 60, + height: 60, + child: IconButton( + icon: Icon(Icons.arrow_forward, + color: HexColor('#B9BABC'), size: 45), + onPressed: onConnect, + ), + ) + ], + ), ), - const Expanded( - child: SizedBox(), - ) - ], + ), ), ); } + + @override + void dispose() { + idController.dispose(); + super.dispose(); + } } diff --git a/flutter_hbb/lib/main.dart b/flutter_hbb/lib/main.dart index 959de8fbe..de0f03e07 100644 --- a/flutter_hbb/lib/main.dart +++ b/flutter_hbb/lib/main.dart @@ -4,10 +4,10 @@ import 'common.dart'; import 'home_page.dart'; void main() { - runApp(_App()); + runApp(App()); } -class _App extends StatelessWidget { +class App extends StatelessWidget { @override Widget build(BuildContext context) { var ffi = FfiModel(); diff --git a/flutter_hbb/test/widget_test.dart b/flutter_hbb/test/widget_test.dart index 8a8415e94..3e7534740 100644 --- a/flutter_hbb/test/widget_test.dart +++ b/flutter_hbb/test/widget_test.dart @@ -13,7 +13,7 @@ import 'package:flutter_hbb/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(MyApp()); + await tester.pumpWidget(App()); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget); From b02aec4255ece7bdd26158231cc4a09b87be7660 Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 16 Nov 2020 02:12:37 +0800 Subject: [PATCH 009/422] prepare json --- flutter_hbb/lib/common.dart | 17 +++++++++++++++++ flutter_hbb/lib/home_page.dart | 2 ++ 2 files changed, 19 insertions(+) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index a14bd9c1a..36eff63ac 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -23,10 +23,13 @@ class MyTheme { } typedef F1 = void Function(Pointer); +typedef F2 = Pointer Function(); // https://juejin.im/post/6844903864852807694 class FfiModel with ChangeNotifier { F1 _connectRemote; + F2 _getPeers; + F1 _freeCString; FfiModel() { initialzeFFI(); @@ -40,6 +43,17 @@ class FfiModel with ChangeNotifier { _connectRemote(Utf8.toUtf8(id)); } + String getId() { + return ""; + } + + void peers() { + var p = _getPeers(); + var x = Utf8.fromUtf8(p); + // https://github.com/brickpop/flutter-rust-ffi + _freeCString(p); + } + Future initialzeFFI() async { final dylib = Platform.isAndroid ? DynamicLibrary.open('librustdesk.so') @@ -48,6 +62,9 @@ class FfiModel with ChangeNotifier { void Function(Pointer)>('initialize'); _connectRemote = dylib .lookupFunction), F1>('connect_remote'); + _getPeers = dylib.lookupFunction('get_peers'); + _freeCString = dylib + .lookupFunction), F1>('rust_cstr_free'); final dir = (await getApplicationDocumentsDirectory()).path; initialize(Utf8.toUtf8(dir)); notifyListeners(); diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index 7c076bdad..d117cb5f3 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -43,6 +43,7 @@ class _HomePageState extends State { } Widget getSearchBarUI() { + var id = ffi.getId(); return Padding( padding: const EdgeInsets.only(top: 8.0), child: Container( @@ -65,6 +66,7 @@ class _HomePageState extends State { child: Container( padding: const EdgeInsets.only(left: 16, right: 16), child: TextFormField( + initialValue: id, style: TextStyle( fontFamily: 'WorkSans', fontWeight: FontWeight.bold, From 818a04db16f0a66602cbc760227bfd53de65b743 Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 16 Nov 2020 11:36:53 +0800 Subject: [PATCH 010/422] prototype json deser --- flutter_hbb/lib/common.dart | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 36eff63ac..08dde00c6 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -4,6 +4,7 @@ import 'package:path_provider/path_provider.dart'; import 'dart:io'; import 'dart:ffi'; import 'dart:async'; +import 'dart:convert'; class HexColor extends Color { HexColor(final String hexColor) : super(_getColorFromHex(hexColor)); @@ -49,9 +50,18 @@ class FfiModel with ChangeNotifier { void peers() { var p = _getPeers(); - var x = Utf8.fromUtf8(p); - // https://github.com/brickpop/flutter-rust-ffi - _freeCString(p); + try { + List peers = json.decode(Utf8.fromUtf8(p)); + // https://github.com/brickpop/flutter-rust-ffi + _freeCString(p); + peers = peers + .map((s) => s as List) + .map((s) => + [s[0] as String, Peer.fromJson(s[1] as Map)]) + .toList(); + } catch (e) { + print(e); + } } Future initialzeFFI() async { @@ -70,3 +80,19 @@ class FfiModel with ChangeNotifier { notifyListeners(); } } + +class Peer { + final String name; + final String email; + + Peer(this.name, this.email); + + Peer.fromJson(Map json) + : name = json['name'], + email = json['email']; + + Map toJson() => { + 'name': name, + 'email': email, + }; +} From 02719f45f64eb518ce63340a88fd39163e9cd0cf Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 16 Nov 2020 20:20:02 +0800 Subject: [PATCH 011/422] refactor --- flutter_hbb/lib/common.dart | 68 +++++++++++++++++++--------------- flutter_hbb/lib/home_page.dart | 3 +- 2 files changed, 39 insertions(+), 32 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 08dde00c6..7f041f427 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -24,13 +24,14 @@ class MyTheme { } typedef F1 = void Function(Pointer); -typedef F2 = Pointer Function(); +typedef F2 = Pointer Function(Pointer); +typedef F3 = void Function(Pointer, Pointer); // https://juejin.im/post/6844903864852807694 class FfiModel with ChangeNotifier { - F1 _connectRemote; - F2 _getPeers; F1 _freeCString; + F2 _getByName; + F3 _setByName; FfiModel() { initialzeFFI(); @@ -41,58 +42,65 @@ class FfiModel with ChangeNotifier { } void connect(String id) { - _connectRemote(Utf8.toUtf8(id)); + setByName("connect", id); + _setByName(Utf8.toUtf8("connect"), Utf8.toUtf8(id)); + } + + void setByName(String name, String value) { + _setByName(Utf8.toUtf8(name), Utf8.toUtf8(value)); + } + + String getByName(String name) { + var p = _getByName(Utf8.toUtf8(name)); + var res = Utf8.fromUtf8(p); + // https://github.com/brickpop/flutter-rust-ffi + _freeCString(p); + return res; } String getId() { - return ""; + return getByName("remote_id"); } - void peers() { - var p = _getPeers(); + List peers() { try { - List peers = json.decode(Utf8.fromUtf8(p)); - // https://github.com/brickpop/flutter-rust-ffi - _freeCString(p); - peers = peers + List peers = json.decode(getByName("peers")); + return peers .map((s) => s as List) .map((s) => - [s[0] as String, Peer.fromJson(s[1] as Map)]) + Peer.fromJson(s[0] as String, s[1] as Map)) .toList(); } catch (e) { print(e); } + return []; } Future initialzeFFI() async { final dylib = Platform.isAndroid ? DynamicLibrary.open('librustdesk.so') : DynamicLibrary.process(); - final initialize = dylib.lookupFunction), - void Function(Pointer)>('initialize'); - _connectRemote = dylib - .lookupFunction), F1>('connect_remote'); - _getPeers = dylib.lookupFunction('get_peers'); + _getByName = dylib.lookupFunction('get_by_name'); + _setByName = + dylib.lookupFunction, Pointer), F3>( + 'set_by_name'); _freeCString = dylib .lookupFunction), F1>('rust_cstr_free'); final dir = (await getApplicationDocumentsDirectory()).path; - initialize(Utf8.toUtf8(dir)); + setByName("init", dir); notifyListeners(); } } class Peer { - final String name; - final String email; + final String id; + final String username; + final String hostname; + final String platform; - Peer(this.name, this.email); - - Peer.fromJson(Map json) - : name = json['name'], - email = json['email']; - - Map toJson() => { - 'name': name, - 'email': email, - }; + Peer.fromJson(String id, Map json) + : id = id, + username = json['username'], + hostname = json['hostname'], + platform = json['platform']; } diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index d117cb5f3..bf02c4436 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -18,6 +18,7 @@ class _HomePageState extends State { @override Widget build(BuildContext context) { ffi = Provider.of(context); + idController.text = ffi.getId(); // This method is rerun every time setState is called return Scaffold( @@ -43,7 +44,6 @@ class _HomePageState extends State { } Widget getSearchBarUI() { - var id = ffi.getId(); return Padding( padding: const EdgeInsets.only(top: 8.0), child: Container( @@ -66,7 +66,6 @@ class _HomePageState extends State { child: Container( padding: const EdgeInsets.only(left: 16, right: 16), child: TextFormField( - initialValue: id, style: TextStyle( fontFamily: 'WorkSans', fontWeight: FontWeight.bold, From 521c8ad18e68461396b53a90597fb010522c8946 Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 16 Nov 2020 21:21:27 +0800 Subject: [PATCH 012/422] add loading --- flutter_hbb/lib/common.dart | 10 ++++++++++ flutter_hbb/lib/home_page.dart | 2 +- flutter_hbb/lib/main.dart | 3 ++- flutter_hbb/pubspec.lock | 14 ++++++++++++++ flutter_hbb/pubspec.yaml | 1 + 5 files changed, 28 insertions(+), 2 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 7f041f427..817c1539b 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -5,6 +5,7 @@ import 'dart:io'; import 'dart:ffi'; import 'dart:async'; import 'dart:convert'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; class HexColor extends Color { HexColor(final String hexColor) : super(_getColorFromHex(hexColor)); @@ -104,3 +105,12 @@ class Peer { hostname = json['hostname'], platform = json['platform']; } + +// https://github.com/huangjianke/flutter_easyloading +void showLoading(String text) { + EasyLoading.show(status: text); +} + +void dismissLoading() { + EasyLoading.dismiss(); +} diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index bf02c4436..ea349b5c4 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -72,7 +72,7 @@ class _HomePageState extends State { fontSize: 30, color: Color(0xFF00B6F0), ), - keyboardType: TextInputType.text, + keyboardType: TextInputType.number, decoration: InputDecoration( labelText: 'Remote ID', border: InputBorder.none, diff --git a/flutter_hbb/lib/main.dart b/flutter_hbb/lib/main.dart index de0f03e07..f2993d410 100644 --- a/flutter_hbb/lib/main.dart +++ b/flutter_hbb/lib/main.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'common.dart'; import 'home_page.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; void main() { runApp(App()); @@ -19,7 +20,7 @@ class App extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: HomePage(title: 'RustDesk'), + home: FlutterEasyLoading(child: HomePage(title: 'RustDesk')), )); } } diff --git a/flutter_hbb/pubspec.lock b/flutter_hbb/pubspec.lock index 758ed271c..e4652d505 100644 --- a/flutter_hbb/pubspec.lock +++ b/flutter_hbb/pubspec.lock @@ -76,6 +76,20 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_easyloading: + dependency: "direct main" + description: + name: flutter_easyloading + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" + flutter_spinkit: + dependency: transitive + description: + name: flutter_spinkit + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.2+1" flutter_test: dependency: "direct dev" description: flutter diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index ad8c52b05..c75c075f3 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -31,6 +31,7 @@ dependencies: ffi: ^0.1.3 path_provider: ^1.6.24 provider: ^4.3.2+2 + flutter_easyloading: ^2.1.3 dev_dependencies: flutter_test: From eb1720c0cdb7a78a57dad7d8e756a4e10b179b42 Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 16 Nov 2020 22:00:09 +0800 Subject: [PATCH 013/422] initialize enterPasswordDialog --- flutter_hbb/lib/common.dart | 49 ++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 817c1539b..209655daf 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'package:ffi/ffi.dart'; import 'package:path_provider/path_provider.dart'; import 'dart:io'; @@ -22,10 +23,11 @@ class HexColor extends Color { class MyTheme { static const Color grayBg = Color(0xFFEEEEEE); static const Color white = Color(0xFFFFFFFF); + static const Color accent = Color(0xFF0071FF); } typedef F1 = void Function(Pointer); -typedef F2 = Pointer Function(Pointer); +typedef F2 = Pointer Function(Pointer, Pointer); typedef F3 = void Function(Pointer, Pointer); // https://juejin.im/post/6844903864852807694 @@ -51,8 +53,8 @@ class FfiModel with ChangeNotifier { _setByName(Utf8.toUtf8(name), Utf8.toUtf8(value)); } - String getByName(String name) { - var p = _getByName(Utf8.toUtf8(name)); + String getByName(String name, {String arg = ""}) { + var p = _getByName(Utf8.toUtf8(name), Utf8.toUtf8(arg)); var res = Utf8.fromUtf8(p); // https://github.com/brickpop/flutter-rust-ffi _freeCString(p); @@ -114,3 +116,44 @@ void showLoading(String text) { void dismissLoading() { EasyLoading.dismiss(); } + +void enterPasswordDialog(String id, BuildContext context) { + var ffi = Provider.of(context); + var remember = ffi.getByName("remember", arg: id) == "true"; + AlertDialog( + title: Text('Please enter your password'), + contentPadding: EdgeInsets.zero, + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + obscureText: true, + decoration: const InputDecoration( + labelText: 'Password', + ), + ), + ListTile( + title: Text( + 'Remember the password', + ), + leading: Checkbox( + value: remember, + onChanged: (_) {}, + ), + ), + ], + ), + actions: [ + FlatButton( + textColor: MyTheme.accent, + onPressed: () => Navigator.pop(context), + child: Text('Cancel'), + ), + FlatButton( + textColor: MyTheme.accent, + onPressed: () => Navigator.pop(context), + child: Text('OK'), + ), + ], + ); +} From b195b649a3b30beb40608e096e341b56ca3a8408 Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 16 Nov 2020 22:12:32 +0800 Subject: [PATCH 014/422] wrongPasswordDialog --- flutter_hbb/lib/common.dart | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 209655daf..21d3e1a40 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -117,10 +117,14 @@ void dismissLoading() { EasyLoading.dismiss(); } -void enterPasswordDialog(String id, BuildContext context) { +void showSuccess(String text) { + EasyLoading.showSuccess(text); +} + +AlertDialog enterPasswordDialog(String id, BuildContext context) { var ffi = Provider.of(context); var remember = ffi.getByName("remember", arg: id) == "true"; - AlertDialog( + return AlertDialog( title: Text('Please enter your password'), contentPadding: EdgeInsets.zero, content: Column( @@ -157,3 +161,26 @@ void enterPasswordDialog(String id, BuildContext context) { ], ); } + +AlertDialog wrongPasswordDialog(String id, BuildContext context) { + return AlertDialog( + title: Text('Please enter your password'), + contentPadding: EdgeInsets.zero, + content: Text('Do you want to enter again?'), + actions: [ + FlatButton( + textColor: MyTheme.accent, + onPressed: () => Navigator.pop(context), + child: Text('Cancel'), + ), + FlatButton( + textColor: MyTheme.accent, + onPressed: () { + Navigator.pop(context); + enterPasswordDialog(id, context); + }, + child: Text('Retry'), + ), + ], + ); +} From eccd08310dde752bf4e5dcfb2856d8db81c73e28 Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 16 Nov 2020 22:14:29 +0800 Subject: [PATCH 015/422] showDialog --- flutter_hbb/lib/common.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 21d3e1a40..46896f19f 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -121,10 +121,10 @@ void showSuccess(String text) { EasyLoading.showSuccess(text); } -AlertDialog enterPasswordDialog(String id, BuildContext context) { +void enterPasswordDialog(String id, BuildContext context) { var ffi = Provider.of(context); var remember = ffi.getByName("remember", arg: id) == "true"; - return AlertDialog( + var dialog = AlertDialog( title: Text('Please enter your password'), contentPadding: EdgeInsets.zero, content: Column( @@ -160,10 +160,11 @@ AlertDialog enterPasswordDialog(String id, BuildContext context) { ), ], ); + showDialog(context: context, builder: (context) => dialog); } -AlertDialog wrongPasswordDialog(String id, BuildContext context) { - return AlertDialog( +void wrongPasswordDialog(String id, BuildContext context) { + var dialog = AlertDialog( title: Text('Please enter your password'), contentPadding: EdgeInsets.zero, content: Text('Do you want to enter again?'), @@ -183,4 +184,5 @@ AlertDialog wrongPasswordDialog(String id, BuildContext context) { ), ], ); + showDialog(context: context, builder: (context) => dialog); } From bb9702c5f661692b7017bfbeed8b0d8595dba4ec Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 16 Nov 2020 22:14:50 +0800 Subject: [PATCH 016/422] comment --- flutter_hbb/lib/common.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 46896f19f..e191d9127 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -121,6 +121,7 @@ void showSuccess(String text) { EasyLoading.showSuccess(text); } +// https://material.io/develop/flutter/components/dialogs void enterPasswordDialog(String id, BuildContext context) { var ffi = Provider.of(context); var remember = ffi.getByName("remember", arg: id) == "true"; From bf81f362f07c3eea61a1110438a4207abe83d2f6 Mon Sep 17 00:00:00 2001 From: open-trade Date: Tue, 17 Nov 2020 00:47:27 +0800 Subject: [PATCH 017/422] event draft --- flutter_hbb/lib/common.dart | 67 +++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index e191d9127..3f413119f 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -40,27 +40,6 @@ class FfiModel with ChangeNotifier { initialzeFFI(); } - void addRemote() { - notifyListeners(); - } - - void connect(String id) { - setByName("connect", id); - _setByName(Utf8.toUtf8("connect"), Utf8.toUtf8(id)); - } - - void setByName(String name, String value) { - _setByName(Utf8.toUtf8(name), Utf8.toUtf8(value)); - } - - String getByName(String name, {String arg = ""}) { - var p = _getByName(Utf8.toUtf8(name), Utf8.toUtf8(arg)); - var res = Utf8.fromUtf8(p); - // https://github.com/brickpop/flutter-rust-ffi - _freeCString(p); - return res; - } - String getId() { return getByName("remote_id"); } @@ -79,6 +58,52 @@ class FfiModel with ChangeNotifier { return []; } + void addRemote() { + notifyListeners(); + } + + void connect(String id) { + setByName("connect", id); + } + + Map popEvent() { + var s = getByName("event"); + if (s == "") return null; + try { + Map event = json.decode(s); + return event; + } catch (e) { + print(e); + } + return null; + } + + void login(String password, bool remember) { + setByName( + "login", + json.encode({ + "password": password, + "remember": remember ? "true" : "false", + })); + } + + void close() { + setByName("close", ""); + } + + void setByName(String name, String value) { + _setByName(Utf8.toUtf8(name), Utf8.toUtf8(value)); + } + + String getByName(String name, {String arg = ""}) { + var p = _getByName(Utf8.toUtf8(name), Utf8.toUtf8(arg)); + assert(p != null); + var res = Utf8.fromUtf8(p); + // https://github.com/brickpop/flutter-rust-ffi + _freeCString(p); + return res; + } + Future initialzeFFI() async { final dylib = Platform.isAndroid ? DynamicLibrary.open('librustdesk.so') From ee64faf082750460a55ac96649ebb3eec86b8ea2 Mon Sep 17 00:00:00 2001 From: open-trade Date: Tue, 17 Nov 2020 01:22:14 +0800 Subject: [PATCH 018/422] more on events, and initialize remote page --- flutter_hbb/lib/home_page.dart | 22 +++++++++++++++------- flutter_hbb/lib/remote_page.dart | 25 +++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 7 deletions(-) create mode 100644 flutter_hbb/lib/remote_page.dart diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index ea349b5c4..8488e1476 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'common.dart'; +import 'remote_page.dart'; class HomePage extends StatefulWidget { HomePage({Key key, this.title}) : super(key: key); @@ -12,13 +13,13 @@ class HomePage extends StatefulWidget { } class _HomePageState extends State { - final idController = TextEditingController(); - FfiModel ffi; + final _idController = TextEditingController(); + FfiModel _ffi; @override Widget build(BuildContext context) { - ffi = Provider.of(context); - idController.text = ffi.getId(); + _ffi = Provider.of(context); + _idController.text = _ffi.getId(); // This method is rerun every time setState is called return Scaffold( @@ -40,7 +41,14 @@ class _HomePageState extends State { } void onConnect() { - ffi.connect(idController.text); + var id = _idController.text.trim(); + if (id == "") return; + Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => RemotePage(id: id), + ), + ); } Widget getSearchBarUI() { @@ -89,7 +97,7 @@ class _HomePageState extends State { ), ), autofocus: false, - controller: idController, + controller: _idController, ), ), ), @@ -112,7 +120,7 @@ class _HomePageState extends State { @override void dispose() { - idController.dispose(); + _idController.dispose(); super.dispose(); } } diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart new file mode 100644 index 000000000..3748fddac --- /dev/null +++ b/flutter_hbb/lib/remote_page.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'common.dart'; +import 'package:flutter/services.dart'; + +class RemotePage extends StatefulWidget { + RemotePage({Key key, this.id}) : super(key: key); + + final String id; + + @override + _RemotePageState createState() => _RemotePageState(); +} + +class _RemotePageState extends State { + FfiModel _ffi; + + @override + Widget build(BuildContext context) { + _ffi = Provider.of(context); + _ffi.connect(widget.id); + // https://stackoverflow.com/questions/46640116/make-flutter-application-fullscreen + SystemChrome.setEnabledSystemUIOverlays([]); + } +} From 190dc6006c8d8f7f2ea1d0b14d35d733c1e761ed Mon Sep 17 00:00:00 2001 From: open-trade Date: Tue, 17 Nov 2020 11:12:55 +0800 Subject: [PATCH 019/422] refactor --- flutter_hbb/lib/common.dart | 44 ++++++++++++++++---------------- flutter_hbb/lib/home_page.dart | 6 ++--- flutter_hbb/lib/remote_page.dart | 32 ++++++++++++++++++++--- 3 files changed, 52 insertions(+), 30 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 3f413119f..9e612168c 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; import 'package:ffi/ffi.dart'; import 'package:path_provider/path_provider.dart'; import 'dart:io'; @@ -32,19 +31,26 @@ typedef F3 = void Function(Pointer, Pointer); // https://juejin.im/post/6844903864852807694 class FfiModel with ChangeNotifier { - F1 _freeCString; - F2 _getByName; - F3 _setByName; - FfiModel() { - initialzeFFI(); + init(); } - String getId() { + Future init() async { + await FFI.init(); + notifyListeners(); + } +} + +class FFI { + static F1 _freeCString; + static F2 _getByName; + static F3 _setByName; + + static String getId() { return getByName("remote_id"); } - List peers() { + static List peers() { try { List peers = json.decode(getByName("peers")); return peers @@ -58,15 +64,11 @@ class FfiModel with ChangeNotifier { return []; } - void addRemote() { - notifyListeners(); - } - - void connect(String id) { + static void connect(String id) { setByName("connect", id); } - Map popEvent() { + static Map popEvent() { var s = getByName("event"); if (s == "") return null; try { @@ -78,7 +80,7 @@ class FfiModel with ChangeNotifier { return null; } - void login(String password, bool remember) { + static void login(String password, bool remember) { setByName( "login", json.encode({ @@ -87,15 +89,15 @@ class FfiModel with ChangeNotifier { })); } - void close() { + static void close() { setByName("close", ""); } - void setByName(String name, String value) { + static void setByName(String name, String value) { _setByName(Utf8.toUtf8(name), Utf8.toUtf8(value)); } - String getByName(String name, {String arg = ""}) { + static String getByName(String name, {String arg = ""}) { var p = _getByName(Utf8.toUtf8(name), Utf8.toUtf8(arg)); assert(p != null); var res = Utf8.fromUtf8(p); @@ -104,7 +106,7 @@ class FfiModel with ChangeNotifier { return res; } - Future initialzeFFI() async { + static Future init() async { final dylib = Platform.isAndroid ? DynamicLibrary.open('librustdesk.so') : DynamicLibrary.process(); @@ -116,7 +118,6 @@ class FfiModel with ChangeNotifier { .lookupFunction), F1>('rust_cstr_free'); final dir = (await getApplicationDocumentsDirectory()).path; setByName("init", dir); - notifyListeners(); } } @@ -148,8 +149,7 @@ void showSuccess(String text) { // https://material.io/develop/flutter/components/dialogs void enterPasswordDialog(String id, BuildContext context) { - var ffi = Provider.of(context); - var remember = ffi.getByName("remember", arg: id) == "true"; + var remember = FFI.getByName("remember", arg: id) == "true"; var dialog = AlertDialog( title: Text('Please enter your password'), contentPadding: EdgeInsets.zero, diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index 8488e1476..1a4a9e36d 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -14,13 +14,11 @@ class HomePage extends StatefulWidget { class _HomePageState extends State { final _idController = TextEditingController(); - FfiModel _ffi; @override Widget build(BuildContext context) { - _ffi = Provider.of(context); - _idController.text = _ffi.getId(); - + Provider.of(context); + _idController.text = FFI.getId(); // This method is rerun every time setState is called return Scaffold( appBar: AppBar( diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 3748fddac..d110cd21f 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; import 'common.dart'; import 'package:flutter/services.dart'; +import 'dart:ui' as ui; class RemotePage extends StatefulWidget { RemotePage({Key key, this.id}) : super(key: key); @@ -13,13 +13,37 @@ class RemotePage extends StatefulWidget { } class _RemotePageState extends State { - FfiModel _ffi; + @override + void initState() { + super.initState(); + FFI.connect(widget.id); + } @override Widget build(BuildContext context) { - _ffi = Provider.of(context); - _ffi.connect(widget.id); // https://stackoverflow.com/questions/46640116/make-flutter-application-fullscreen SystemChrome.setEnabledSystemUIOverlays([]); + return CustomPaint( + painter: new ImageEditor(image: null), + ); + } +} + +class ImageEditor extends CustomPainter { + ImageEditor({ + this.image, + }); + + ui.Image image; + + @override + void paint(Canvas canvas, Size size) { + if (image = null) return; + canvas.drawImage(image, new Offset(0.0, 0.0), new Paint()); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) { + return false; } } From cc0139a85c6fa8a340952b1e8d2867434bb386a3 Mon Sep 17 00:00:00 2001 From: open-trade Date: Tue, 17 Nov 2020 12:08:31 +0800 Subject: [PATCH 020/422] seems timer not work well, will try https://github.com/hanxu317317/flutter_plan_demo/blob/master/lib/src/enter.dart --- flutter_hbb/lib/main.dart | 3 +-- flutter_hbb/lib/remote_page.dart | 32 ++++++++++++++++++++++++++++---- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/flutter_hbb/lib/main.dart b/flutter_hbb/lib/main.dart index f2993d410..de0f03e07 100644 --- a/flutter_hbb/lib/main.dart +++ b/flutter_hbb/lib/main.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'common.dart'; import 'home_page.dart'; -import 'package:flutter_easyloading/flutter_easyloading.dart'; void main() { runApp(App()); @@ -20,7 +19,7 @@ class App extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: FlutterEasyLoading(child: HomePage(title: 'RustDesk')), + home: HomePage(title: 'RustDesk'), )); } } diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index d110cd21f..e3004690d 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; import 'common.dart'; import 'package:flutter/services.dart'; import 'dart:ui' as ui; +import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'dart:async'; class RemotePage extends StatefulWidget { RemotePage({Key key, this.id}) : super(key: key); @@ -12,20 +14,42 @@ class RemotePage extends StatefulWidget { _RemotePageState createState() => _RemotePageState(); } +// https://github.com/hanxu317317/flutter_plan_demo/blob/master/lib/src/enter.dart class _RemotePageState extends State { + Timer _interval; + @override void initState() { super.initState(); FFI.connect(widget.id); + WidgetsBinding.instance.addPostFrameCallback((_) { + showLoading("Connecting..."); + _interval = + Timer.periodic(Duration(milliseconds: 30), (timer) => interval()); + }); + } + + @override + void dispose() { + super.dispose(); + _interval.cancel(); + dismissLoading(); + FFI.close(); + } + + void interval() { + print(1); } @override Widget build(BuildContext context) { + // Size size = MediaQueryData.fromWindow(ui.window).size; // https://stackoverflow.com/questions/46640116/make-flutter-application-fullscreen SystemChrome.setEnabledSystemUIOverlays([]); - return CustomPaint( + return FlutterEasyLoading( + child: CustomPaint( painter: new ImageEditor(image: null), - ); + )); } } @@ -38,12 +62,12 @@ class ImageEditor extends CustomPainter { @override void paint(Canvas canvas, Size size) { - if (image = null) return; + if (image == null) return; canvas.drawImage(image, new Offset(0.0, 0.0), new Paint()); } @override bool shouldRepaint(CustomPainter oldDelegate) { - return false; + return oldDelegate != this; } } From ced0457a2eb7834f2d5285e122a65b166e1773fb Mon Sep 17 00:00:00 2001 From: open-trade Date: Tue, 17 Nov 2020 12:41:23 +0800 Subject: [PATCH 021/422] timer works fine, just it does not print same content every time --- flutter_hbb/lib/remote_page.dart | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index e3004690d..aea147a3a 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -32,24 +32,34 @@ class _RemotePageState extends State { @override void dispose() { super.dispose(); + FFI.close(); _interval.cancel(); dismissLoading(); - FFI.close(); } void interval() { - print(1); + print(DateTime.now()); } @override Widget build(BuildContext context) { // Size size = MediaQueryData.fromWindow(ui.window).size; + // MediaQuery.of(context).size.height; // https://stackoverflow.com/questions/46640116/make-flutter-application-fullscreen SystemChrome.setEnabledSystemUIOverlays([]); return FlutterEasyLoading( - child: CustomPaint( - painter: new ImageEditor(image: null), - )); + child: GestureDetector( + child: CustomPaint( + painter: new ImageEditor(image: null), + ), + onPanStart: (DragDownDetails) { + print("onPanStart $DragDownDetails"); + // hero.moveTo(DragDownDetails.globalPosition.dx, DragDownDetails.globalPosition.dy); + }, + onPanUpdate: (DragDownDetails) { + print("onPanUpdate $DragDownDetails"); + // hero.moveTo(DragDownDetails.globalPosition.dx, DragDownDetails.globalPosition.dy); + })); } } From 0ee311b5e2b56ad2c82e3f16102a0d6d47da3e18 Mon Sep 17 00:00:00 2001 From: open-trade Date: Tue, 17 Nov 2020 16:53:02 +0800 Subject: [PATCH 022/422] handle peer_info and display_switch --- flutter_hbb/lib/home_page.dart | 7 ++++++- flutter_hbb/lib/remote_page.dart | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index 1a4a9e36d..ffc346305 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -15,10 +15,15 @@ class HomePage extends StatefulWidget { class _HomePageState extends State { final _idController = TextEditingController(); + @override + void initState() { + super.initState(); + _idController.text = FFI.getId(); + } + @override Widget build(BuildContext context) { Provider.of(context); - _idController.text = FFI.getId(); // This method is rerun every time setState is called return Scaffold( appBar: AppBar( diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index aea147a3a..f6a1e2985 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -39,6 +39,21 @@ class _RemotePageState extends State { void interval() { print(DateTime.now()); + var evt = FFI.popEvent(); + if (evt == null) return; + var name = evt["name"]; + if (name == "msgbox") { + handleMsgbox(evt); + } + } + + void handleMsgbox(evt) { + var type = evt["type"]; + var title = evt["title"]; + var text = evt["text"]; + if (type == "error") { + } else if (type == "re-input-password") { + } else if (type == "input-password") {} } @override From 676278920f1a90be1ef15e8cfc1cf594d4e428d7 Mon Sep 17 00:00:00 2001 From: open-trade Date: Tue, 17 Nov 2020 18:10:49 +0800 Subject: [PATCH 023/422] rust ffi for rgba --- flutter_hbb/lib/common.dart | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 9e612168c..dd6be65ba 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -7,6 +7,12 @@ import 'dart:async'; import 'dart:convert'; import 'package:flutter_easyloading/flutter_easyloading.dart'; +class RgbaFrame extends Struct { + @Uint32() + int len; + Pointer data; +} + class HexColor extends Color { HexColor(final String hexColor) : super(_getColorFromHex(hexColor)); @@ -28,6 +34,8 @@ class MyTheme { typedef F1 = void Function(Pointer); typedef F2 = Pointer Function(Pointer, Pointer); typedef F3 = void Function(Pointer, Pointer); +typedef F4 = void Function(Pointer); +typedef F5 = Pointer Function(); // https://juejin.im/post/6844903864852807694 class FfiModel with ChangeNotifier { @@ -45,6 +53,8 @@ class FFI { static F1 _freeCString; static F2 _getByName; static F3 _setByName; + static F4 _freeRgba; + static F5 _getRgba; static String getId() { return getByName("remote_id"); @@ -116,6 +126,9 @@ class FFI { 'set_by_name'); _freeCString = dylib .lookupFunction), F1>('rust_cstr_free'); + _freeRgba = dylib + .lookupFunction), F4>('free_rgba'); + _getRgba = dylib.lookupFunction('get_rgba'); final dir = (await getApplicationDocumentsDirectory()).path; setByName("init", dir); } From 1a3a2cf6c733ae45899b496aa209b633125549dc Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 18 Nov 2020 00:28:55 +0800 Subject: [PATCH 024/422] rgba -> image --- flutter_hbb/lib/common.dart | 42 ++++++++++++++++++++++---------- flutter_hbb/lib/home_page.dart | 2 +- flutter_hbb/lib/remote_page.dart | 42 ++++++++++++++++++++++---------- 3 files changed, 59 insertions(+), 27 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index dd6be65ba..ea69756ff 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -6,6 +6,7 @@ import 'dart:ffi'; import 'dart:async'; import 'dart:convert'; import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'dart:typed_data'; class RgbaFrame extends Struct { @Uint32() @@ -55,14 +56,15 @@ class FFI { static F3 _setByName; static F4 _freeRgba; static F5 _getRgba; + static Pointer _lastRgbaFrame; static String getId() { - return getByName("remote_id"); + return getByName('remote_id'); } static List peers() { try { - List peers = json.decode(getByName("peers")); + List peers = json.decode(getByName('peers')); return peers .map((s) => s as List) .map((s) => @@ -75,12 +77,25 @@ class FFI { } static void connect(String id) { - setByName("connect", id); + setByName('connect', id); + } + + static void _clearRgbaFrame() { + if (_lastRgbaFrame != null && _lastRgbaFrame != nullptr) + _freeRgba(_lastRgbaFrame); + } + + static Uint8List getRgba() { + _clearRgbaFrame(); + _lastRgbaFrame = _getRgba(); + if (_lastRgbaFrame == null || _lastRgbaFrame == nullptr) return null; + final ref = _lastRgbaFrame.ref; + return Uint8List.sublistView(ref.data.asTypedList(ref.len)); } static Map popEvent() { - var s = getByName("event"); - if (s == "") return null; + var s = getByName('event'); + if (s == '') return null; try { Map event = json.decode(s); return event; @@ -92,24 +107,25 @@ class FFI { static void login(String password, bool remember) { setByName( - "login", + 'login', json.encode({ - "password": password, - "remember": remember ? "true" : "false", + 'password': password, + 'remember': remember ? 'true' : 'false', })); } static void close() { - setByName("close", ""); + _clearRgbaFrame(); + setByName('close', ''); } static void setByName(String name, String value) { _setByName(Utf8.toUtf8(name), Utf8.toUtf8(value)); } - static String getByName(String name, {String arg = ""}) { + static String getByName(String name, {String arg = ''}) { var p = _getByName(Utf8.toUtf8(name), Utf8.toUtf8(arg)); - assert(p != null); + assert(p != nullptr && p != null); var res = Utf8.fromUtf8(p); // https://github.com/brickpop/flutter-rust-ffi _freeCString(p); @@ -130,7 +146,7 @@ class FFI { .lookupFunction), F4>('free_rgba'); _getRgba = dylib.lookupFunction('get_rgba'); final dir = (await getApplicationDocumentsDirectory()).path; - setByName("init", dir); + setByName('init', dir); } } @@ -162,7 +178,7 @@ void showSuccess(String text) { // https://material.io/develop/flutter/components/dialogs void enterPasswordDialog(String id, BuildContext context) { - var remember = FFI.getByName("remember", arg: id) == "true"; + var remember = FFI.getByName('remember', arg: id) == 'true'; var dialog = AlertDialog( title: Text('Please enter your password'), contentPadding: EdgeInsets.zero, diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index ffc346305..d6a36b5a4 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -45,7 +45,7 @@ class _HomePageState extends State { void onConnect() { var id = _idController.text.trim(); - if (id == "") return; + if (id == '') return; Navigator.push( context, MaterialPageRoute( diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index f6a1e2985..853bc0f2a 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -17,13 +17,18 @@ class RemotePage extends StatefulWidget { // https://github.com/hanxu317317/flutter_plan_demo/blob/master/lib/src/enter.dart class _RemotePageState extends State { Timer _interval; + int x = 0; + int y = 0; + int width = 0; + int height = 0; + ui.Image image; @override void initState() { super.initState(); FFI.connect(widget.id); WidgetsBinding.instance.addPostFrameCallback((_) { - showLoading("Connecting..."); + showLoading('Connecting...'); _interval = Timer.periodic(Duration(milliseconds: 30), (timer) => interval()); }); @@ -38,22 +43,33 @@ class _RemotePageState extends State { } void interval() { - print(DateTime.now()); var evt = FFI.popEvent(); if (evt == null) return; - var name = evt["name"]; - if (name == "msgbox") { + var name = evt['name']; + if (name == 'msgbox') { handleMsgbox(evt); } + var rgba = FFI.getRgba(); + if (rgba != null) { + ui.decodeImageFromPixels(rgba, width, height, ui.PixelFormat.rgba8888, + (_image) { + setState(() { + image = _image; + }); + }); + } } void handleMsgbox(evt) { - var type = evt["type"]; - var title = evt["title"]; - var text = evt["text"]; - if (type == "error") { - } else if (type == "re-input-password") { - } else if (type == "input-password") {} + var type = evt['type']; + var title = evt['title']; + var text = evt['text']; + if (type == 'error') { + } else if (type == 're-input-password') { + wrongPasswordDialog(widget.id, context); + } else if (type == 'input-password') { + enterPasswordDialog(widget.id, context); + } } @override @@ -65,14 +81,14 @@ class _RemotePageState extends State { return FlutterEasyLoading( child: GestureDetector( child: CustomPaint( - painter: new ImageEditor(image: null), + painter: new ImageEditor(image: image), ), onPanStart: (DragDownDetails) { - print("onPanStart $DragDownDetails"); + print('onPanStart $DragDownDetails'); // hero.moveTo(DragDownDetails.globalPosition.dx, DragDownDetails.globalPosition.dy); }, onPanUpdate: (DragDownDetails) { - print("onPanUpdate $DragDownDetails"); + print('onPanUpdate $DragDownDetails'); // hero.moveTo(DragDownDetails.globalPosition.dx, DragDownDetails.globalPosition.dy); })); } From a110db32e98b1c5ddbe6fc7a7c073346987aa9d6 Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 18 Nov 2020 01:55:26 +0800 Subject: [PATCH 025/422] msgbox start to work --- flutter_hbb/lib/common.dart | 26 ++++++++++++++++++++++++-- flutter_hbb/lib/remote_page.dart | 7 ++++--- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index ea69756ff..50e82fe45 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -93,11 +93,11 @@ class FFI { return Uint8List.sublistView(ref.data.asTypedList(ref.len)); } - static Map popEvent() { + static Map popEvent() { var s = getByName('event'); if (s == '') return null; try { - Map event = json.decode(s); + Map event = json.decode(s); return event; } catch (e) { print(e); @@ -178,6 +178,7 @@ void showSuccess(String text) { // https://material.io/develop/flutter/components/dialogs void enterPasswordDialog(String id, BuildContext context) { + dismissLoading(); var remember = FFI.getByName('remember', arg: id) == 'true'; var dialog = AlertDialog( title: Text('Please enter your password'), @@ -219,6 +220,7 @@ void enterPasswordDialog(String id, BuildContext context) { } void wrongPasswordDialog(String id, BuildContext context) { + dismissLoading(); var dialog = AlertDialog( title: Text('Please enter your password'), contentPadding: EdgeInsets.zero, @@ -241,3 +243,23 @@ void wrongPasswordDialog(String id, BuildContext context) { ); showDialog(context: context, builder: (context) => dialog); } + +void msgbox(String type, String title, String text, BuildContext context) { + dismissLoading(); + var dialog = AlertDialog( + title: Text(title), + contentPadding: EdgeInsets.zero, + content: Text(text), + actions: [ + FlatButton( + textColor: MyTheme.accent, + onPressed: () { + Navigator.pop(context); + Navigator.pop(context); + }, + child: Text('OK'), + ), + ], + ); + showDialog(context: context, builder: (context) => dialog); +} diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 853bc0f2a..78a1c0839 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -60,15 +60,16 @@ class _RemotePageState extends State { } } - void handleMsgbox(evt) { + void handleMsgbox(Map evt) { var type = evt['type']; var title = evt['title']; var text = evt['text']; - if (type == 'error') { - } else if (type == 're-input-password') { + if (type == 're-input-password') { wrongPasswordDialog(widget.id, context); } else if (type == 'input-password') { enterPasswordDialog(widget.id, context); + } else { + msgbox(type, title, text, context); } } From 52139dc84bee85879fa4afb5313fee1634c34198 Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 18 Nov 2020 02:36:47 +0800 Subject: [PATCH 026/422] more on enter password --- flutter_hbb/lib/common.dart | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 50e82fe45..24f391794 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -179,6 +179,7 @@ void showSuccess(String text) { // https://material.io/develop/flutter/components/dialogs void enterPasswordDialog(String id, BuildContext context) { dismissLoading(); + final controller = TextEditingController(); var remember = FFI.getByName('remember', arg: id) == 'true'; var dialog = AlertDialog( title: Text('Please enter your password'), @@ -187,7 +188,9 @@ void enterPasswordDialog(String id, BuildContext context) { mainAxisSize: MainAxisSize.min, children: [ TextField( + autofocus: true, obscureText: true, + controller: controller, decoration: const InputDecoration( labelText: 'Password', ), @@ -198,7 +201,9 @@ void enterPasswordDialog(String id, BuildContext context) { ), leading: Checkbox( value: remember, - onChanged: (_) {}, + onChanged: (v) { + remember = v; + }, ), ), ], @@ -206,12 +211,21 @@ void enterPasswordDialog(String id, BuildContext context) { actions: [ FlatButton( textColor: MyTheme.accent, - onPressed: () => Navigator.pop(context), + onPressed: () { + Navigator.pop(context); + Navigator.pop(context); + }, child: Text('Cancel'), ), FlatButton( textColor: MyTheme.accent, - onPressed: () => Navigator.pop(context), + onPressed: () { + var text = controller.text.trim(); + if (text == '') return; + FFI.login(text, remember); + showLoading('Logging in...'); + Navigator.pop(context); + }, child: Text('OK'), ), ], @@ -222,7 +236,7 @@ void enterPasswordDialog(String id, BuildContext context) { void wrongPasswordDialog(String id, BuildContext context) { dismissLoading(); var dialog = AlertDialog( - title: Text('Please enter your password'), + title: Text('Wrong Password'), contentPadding: EdgeInsets.zero, content: Text('Do you want to enter again?'), actions: [ From 0ba74ef7ecc95049b3cebaf0ec3ac8bfa1c08f2e Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 18 Nov 2020 12:49:43 +0800 Subject: [PATCH 027/422] fix on login and config --- flutter_hbb/lib/common.dart | 52 +++++++++++++++++++++++++++------- flutter_hbb/lib/home_page.dart | 7 +---- 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 24f391794..faa62fe64 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -165,6 +165,7 @@ class Peer { // https://github.com/huangjianke/flutter_easyloading void showLoading(String text) { + dismissLoading(); EasyLoading.show(status: text); } @@ -173,17 +174,24 @@ void dismissLoading() { } void showSuccess(String text) { + dismissLoading(); EasyLoading.showSuccess(text); } +bool _hasDialog = false; + // https://material.io/develop/flutter/components/dialogs -void enterPasswordDialog(String id, BuildContext context) { +Future enterPasswordDialog(String id, BuildContext context) async { dismissLoading(); + if (_hasDialog) { + Navigator.pop(context); + } + _hasDialog = true; final controller = TextEditingController(); var remember = FFI.getByName('remember', arg: id) == 'true'; var dialog = AlertDialog( title: Text('Please enter your password'), - contentPadding: EdgeInsets.zero, + contentPadding: const EdgeInsets.all(20.0), content: Column( mainAxisSize: MainAxisSize.min, children: [ @@ -230,19 +238,30 @@ void enterPasswordDialog(String id, BuildContext context) { ), ], ); - showDialog(context: context, builder: (context) => dialog); + await showDialog( + context: context, + barrierDismissible: false, + builder: (context) => dialog); + _hasDialog = false; } -void wrongPasswordDialog(String id, BuildContext context) { +Future wrongPasswordDialog(String id, BuildContext context) async { dismissLoading(); + if (_hasDialog) { + Navigator.pop(context); + } + _hasDialog = true; var dialog = AlertDialog( title: Text('Wrong Password'), - contentPadding: EdgeInsets.zero, + contentPadding: const EdgeInsets.all(20.0), content: Text('Do you want to enter again?'), actions: [ FlatButton( textColor: MyTheme.accent, - onPressed: () => Navigator.pop(context), + onPressed: () { + Navigator.pop(context); + Navigator.pop(context); + }, child: Text('Cancel'), ), FlatButton( @@ -255,14 +274,23 @@ void wrongPasswordDialog(String id, BuildContext context) { ), ], ); - showDialog(context: context, builder: (context) => dialog); + await showDialog( + context: context, + barrierDismissible: false, + builder: (context) => dialog); + _hasDialog = false; } -void msgbox(String type, String title, String text, BuildContext context) { +Future msgbox( + String type, String title, String text, BuildContext context) async { dismissLoading(); + if (_hasDialog) { + Navigator.pop(context); + } + _hasDialog = true; var dialog = AlertDialog( title: Text(title), - contentPadding: EdgeInsets.zero, + contentPadding: const EdgeInsets.all(20.0), content: Text(text), actions: [ FlatButton( @@ -275,5 +303,9 @@ void msgbox(String type, String title, String text, BuildContext context) { ), ], ); - showDialog(context: context, builder: (context) => dialog); + await showDialog( + context: context, + barrierDismissible: false, + builder: (context) => dialog); + _hasDialog = false; } diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index d6a36b5a4..a04198e15 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -15,15 +15,10 @@ class HomePage extends StatefulWidget { class _HomePageState extends State { final _idController = TextEditingController(); - @override - void initState() { - super.initState(); - _idController.text = FFI.getId(); - } - @override Widget build(BuildContext context) { Provider.of(context); + if (_idController.text.isEmpty) _idController.text = FFI.getId(); // This method is rerun every time setState is called return Scaffold( appBar: AppBar( From df58f3230eb7b609330f80cb5f2042d7a8863c64 Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 18 Nov 2020 16:11:19 +0800 Subject: [PATCH 028/422] image shown now --- flutter_hbb/lib/common.dart | 94 +++++++++++++++++--------------- flutter_hbb/lib/remote_page.dart | 86 ++++++++++++++++++++++++----- 2 files changed, 120 insertions(+), 60 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index faa62fe64..ba1e65282 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -189,55 +189,59 @@ Future enterPasswordDialog(String id, BuildContext context) async { _hasDialog = true; final controller = TextEditingController(); var remember = FFI.getByName('remember', arg: id) == 'true'; - var dialog = AlertDialog( - title: Text('Please enter your password'), - contentPadding: const EdgeInsets.all(20.0), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextField( - autofocus: true, - obscureText: true, - controller: controller, - decoration: const InputDecoration( - labelText: 'Password', + var dialog = StatefulBuilder(builder: (context, setState) { + return AlertDialog( + title: Text('Please enter your password'), + contentPadding: const EdgeInsets.all(20.0), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + autofocus: true, + obscureText: true, + controller: controller, + decoration: const InputDecoration( + labelText: 'Password', + ), ), + ListTile( + title: Text( + 'Remember the password', + ), + leading: Checkbox( + value: remember, + onChanged: (v) { + setState(() { + remember = v; + }); + }, + ), + ), + ], + ), + actions: [ + FlatButton( + textColor: MyTheme.accent, + onPressed: () { + Navigator.pop(context); + Navigator.pop(context); + }, + child: Text('Cancel'), ), - ListTile( - title: Text( - 'Remember the password', - ), - leading: Checkbox( - value: remember, - onChanged: (v) { - remember = v; - }, - ), + FlatButton( + textColor: MyTheme.accent, + onPressed: () { + var text = controller.text.trim(); + if (text == '') return; + FFI.login(text, remember); + showLoading('Logging in...'); + Navigator.pop(context); + }, + child: Text('OK'), ), ], - ), - actions: [ - FlatButton( - textColor: MyTheme.accent, - onPressed: () { - Navigator.pop(context); - Navigator.pop(context); - }, - child: Text('Cancel'), - ), - FlatButton( - textColor: MyTheme.accent, - onPressed: () { - var text = controller.text.trim(); - if (text == '') return; - FFI.login(text, remember); - showLoading('Logging in...'); - Navigator.pop(context); - }, - child: Text('OK'), - ), - ], - ); + ); + }); await showDialog( context: context, barrierDismissible: false, diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 78a1c0839..50813fdb5 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -3,6 +3,7 @@ import 'common.dart'; import 'package:flutter/services.dart'; import 'dart:ui' as ui; import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'dart:convert'; import 'dart:async'; class RemotePage extends StatefulWidget { @@ -17,11 +18,9 @@ class RemotePage extends StatefulWidget { // https://github.com/hanxu317317/flutter_plan_demo/blob/master/lib/src/enter.dart class _RemotePageState extends State { Timer _interval; - int x = 0; - int y = 0; - int width = 0; - int height = 0; - ui.Image image; + ui.Image _image; + PeerInfo _pi = PeerInfo(); + Display _display = Display(); @override void initState() { @@ -44,22 +43,61 @@ class _RemotePageState extends State { void interval() { var evt = FFI.popEvent(); - if (evt == null) return; - var name = evt['name']; - if (name == 'msgbox') { - handleMsgbox(evt); + if (evt != null) { + var name = evt['name']; + if (name == 'msgbox') { + handleMsgbox(evt); + } else if (name == 'peer_info') { + handlePeerInfo(evt); + } else if (name == 'switch_display') { + handleSwitchDisplay(evt); + } } var rgba = FFI.getRgba(); if (rgba != null) { - ui.decodeImageFromPixels(rgba, width, height, ui.PixelFormat.rgba8888, - (_image) { + ui.decodeImageFromPixels( + rgba, _display.width, _display.height, ui.PixelFormat.bgra8888, + (__image) { setState(() { - image = _image; + _image = __image; }); }); } } + void handleSwitchDisplay(Map evt) { + _pi.currentDisplay = int.parse(evt['display']); + _display.x = int.parse(evt['x']); + _display.y = int.parse(evt['y']); + _display.width = int.parse(evt['width']); + _display.height = int.parse(evt['height']); + setState(() {}); + } + + void handlePeerInfo(Map evt) { + dismissLoading(); + _pi.username = evt['username']; + _pi.hostname = evt['hostname']; + _pi.platform = evt['platform']; + _pi.sasEnabled = evt['sas_enabled'] == "true"; + _pi.currentDisplay = int.parse(evt['current_display']); + List displays = json.decode(evt['displays']); + _pi.displays = List(); + for (int i = 0; i < displays.length; ++i) { + Map d0 = displays[i]; + var d = Display(); + d.x = d0['x']; + d.y = d0['y']; + d.width = d0['width']; + d.height = d0['height']; + _pi.displays.add(d); + } + if (_pi.currentDisplay < _pi.displays.length) { + _display = _pi.displays[_pi.currentDisplay]; + } + setState(() {}); + } + void handleMsgbox(Map evt) { var type = evt['type']; var title = evt['title']; @@ -81,9 +119,11 @@ class _RemotePageState extends State { SystemChrome.setEnabledSystemUIOverlays([]); return FlutterEasyLoading( child: GestureDetector( - child: CustomPaint( - painter: new ImageEditor(image: image), - ), + child: Container( + child: CustomPaint( + painter: new ImageEditor(image: _image), + ), + color: MyTheme.grayBg), onPanStart: (DragDownDetails) { print('onPanStart $DragDownDetails'); // hero.moveTo(DragDownDetails.globalPosition.dx, DragDownDetails.globalPosition.dy); @@ -113,3 +153,19 @@ class ImageEditor extends CustomPainter { return oldDelegate != this; } } + +class Display { + int x = 0; + int y = 0; + int width = 0; + int height = 0; +} + +class PeerInfo { + String username; + String hostname; + String platform; + bool sasEnabled; + int currentDisplay; + List displays; +} From 33186e28d1e33d053a4e5dd6f363d352d2e12685 Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 18 Nov 2020 17:36:11 +0800 Subject: [PATCH 029/422] use InteractiveViewer instead of GestureDetector --- flutter_hbb/lib/remote_page.dart | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 50813fdb5..dc198afa2 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -118,20 +118,18 @@ class _RemotePageState extends State { // https://stackoverflow.com/questions/46640116/make-flutter-application-fullscreen SystemChrome.setEnabledSystemUIOverlays([]); return FlutterEasyLoading( - child: GestureDetector( - child: Container( - child: CustomPaint( - painter: new ImageEditor(image: _image), - ), - color: MyTheme.grayBg), - onPanStart: (DragDownDetails) { - print('onPanStart $DragDownDetails'); - // hero.moveTo(DragDownDetails.globalPosition.dx, DragDownDetails.globalPosition.dy); - }, - onPanUpdate: (DragDownDetails) { - print('onPanUpdate $DragDownDetails'); - // hero.moveTo(DragDownDetails.globalPosition.dx, DragDownDetails.globalPosition.dy); - })); + child: InteractiveViewer( + constrained: false, + panEnabled: true, + onInteractionUpdate: (details) { + print("$details"); + }, + child: Container( + child: CustomPaint( + painter: new ImageEditor(image: _image), + ), + color: MyTheme.grayBg), + )); } } @@ -145,7 +143,7 @@ class ImageEditor extends CustomPainter { @override void paint(Canvas canvas, Size size) { if (image == null) return; - canvas.drawImage(image, new Offset(0.0, 0.0), new Paint()); + canvas.drawImage(image, new Offset(0, 0), new Paint()); } @override From c469329d190ec7928e6850fad8649e6a841f8e75 Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 18 Nov 2020 18:12:43 +0800 Subject: [PATCH 030/422] fix crash, current rgba ffi design still ugly and dangerous --- flutter_hbb/lib/common.dart | 4 +--- flutter_hbb/lib/remote_page.dart | 24 ++++++++++++++++-------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index ba1e65282..4605c8472 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -80,13 +80,12 @@ class FFI { setByName('connect', id); } - static void _clearRgbaFrame() { + static void clearRgbaFrame() { if (_lastRgbaFrame != null && _lastRgbaFrame != nullptr) _freeRgba(_lastRgbaFrame); } static Uint8List getRgba() { - _clearRgbaFrame(); _lastRgbaFrame = _getRgba(); if (_lastRgbaFrame == null || _lastRgbaFrame == nullptr) return null; final ref = _lastRgbaFrame.ref; @@ -115,7 +114,6 @@ class FFI { } static void close() { - _clearRgbaFrame(); setByName('close', ''); } diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index dc198afa2..34d8050cd 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -21,6 +21,7 @@ class _RemotePageState extends State { ui.Image _image; PeerInfo _pi = PeerInfo(); Display _display = Display(); + bool _decoding = false; @override void initState() { @@ -39,6 +40,7 @@ class _RemotePageState extends State { FFI.close(); _interval.cancel(); dismissLoading(); + _decoding = null; } void interval() { @@ -53,15 +55,21 @@ class _RemotePageState extends State { handleSwitchDisplay(evt); } } - var rgba = FFI.getRgba(); - if (rgba != null) { - ui.decodeImageFromPixels( - rgba, _display.width, _display.height, ui.PixelFormat.bgra8888, - (__image) { - setState(() { - _image = __image; + if (!_decoding) { + var rgba = FFI.getRgba(); + if (rgba != null) { + _decoding = true; + ui.decodeImageFromPixels( + rgba, _display.width, _display.height, ui.PixelFormat.bgra8888, + (__image) { + FFI.clearRgbaFrame(); + if (_decoding == null) return; + _decoding = false; + setState(() { + _image = __image; + }); }); - }); + } } } From 63930918f62de10ec73ff722c0b4150f631f764d Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 18 Nov 2020 18:20:13 +0800 Subject: [PATCH 031/422] exit fullscreen --- flutter_hbb/lib/remote_page.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 34d8050cd..951a6474c 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -28,6 +28,8 @@ class _RemotePageState extends State { super.initState(); FFI.connect(widget.id); WidgetsBinding.instance.addPostFrameCallback((_) { + // https://stackoverflow.com/questions/46640116/make-flutter-application-fullscreen + SystemChrome.setEnabledSystemUIOverlays([]); showLoading('Connecting...'); _interval = Timer.periodic(Duration(milliseconds: 30), (timer) => interval()); @@ -41,6 +43,7 @@ class _RemotePageState extends State { _interval.cancel(); dismissLoading(); _decoding = null; + SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values); } void interval() { @@ -123,8 +126,6 @@ class _RemotePageState extends State { Widget build(BuildContext context) { // Size size = MediaQueryData.fromWindow(ui.window).size; // MediaQuery.of(context).size.height; - // https://stackoverflow.com/questions/46640116/make-flutter-application-fullscreen - SystemChrome.setEnabledSystemUIOverlays([]); return FlutterEasyLoading( child: InteractiveViewer( constrained: false, From 0d4c85b3df00958928706c0bb8c859b056d3686f Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 18 Nov 2020 23:15:59 +0800 Subject: [PATCH 032/422] ImageModel --- flutter_hbb/lib/common.dart | 15 +++++++++++++++ flutter_hbb/lib/main.dart | 21 +++++++++++---------- flutter_hbb/lib/remote_page.dart | 28 ++++++++++++++++------------ 3 files changed, 42 insertions(+), 22 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 4605c8472..9099a53ed 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -7,6 +7,7 @@ import 'dart:async'; import 'dart:convert'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'dart:typed_data'; +import 'dart:ui' as ui; class RgbaFrame extends Struct { @Uint32() @@ -50,6 +51,17 @@ class FfiModel with ChangeNotifier { } } +class ImageModel with ChangeNotifier { + ui.Image _image; + + ui.Image get image => _image; + + void update(ui.Image image) { + _image = image; + notifyListeners(); + } +} + class FFI { static F1 _freeCString; static F2 _getByName; @@ -57,6 +69,8 @@ class FFI { static F4 _freeRgba; static F5 _getRgba; static Pointer _lastRgbaFrame; + static final imageModel = ImageModel(); + static final ffiModel = FfiModel(); static String getId() { return getByName('remote_id'); @@ -115,6 +129,7 @@ class FFI { static void close() { setByName('close', ''); + FFI.imageModel.update(null); } static void setByName(String name, String value) { diff --git a/flutter_hbb/lib/main.dart b/flutter_hbb/lib/main.dart index de0f03e07..e0589f04a 100644 --- a/flutter_hbb/lib/main.dart +++ b/flutter_hbb/lib/main.dart @@ -10,16 +10,17 @@ void main() { class App extends StatelessWidget { @override Widget build(BuildContext context) { - var ffi = FfiModel(); return ChangeNotifierProvider.value( - value: ffi, - child: MaterialApp( - title: 'RustDesk', - theme: ThemeData( - primarySwatch: Colors.blue, - visualDensity: VisualDensity.adaptivePlatformDensity, - ), - home: HomePage(title: 'RustDesk'), - )); + value: FFI.ffiModel, + child: ChangeNotifierProvider.value( + value: FFI.imageModel, + child: MaterialApp( + title: 'RustDesk', + theme: ThemeData( + primarySwatch: Colors.blue, + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: HomePage(title: 'RustDesk'), + ))); } } diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 951a6474c..009fd0085 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'common.dart'; import 'package:flutter/services.dart'; import 'dart:ui' as ui; @@ -18,7 +19,6 @@ class RemotePage extends StatefulWidget { // https://github.com/hanxu317317/flutter_plan_demo/blob/master/lib/src/enter.dart class _RemotePageState extends State { Timer _interval; - ui.Image _image; PeerInfo _pi = PeerInfo(); Display _display = Display(); bool _decoding = false; @@ -64,13 +64,11 @@ class _RemotePageState extends State { _decoding = true; ui.decodeImageFromPixels( rgba, _display.width, _display.height, ui.PixelFormat.bgra8888, - (__image) { + (image) { FFI.clearRgbaFrame(); if (_decoding == null) return; _decoding = false; - setState(() { - _image = __image; - }); + FFI.imageModel.update(image); }); } } @@ -133,17 +131,23 @@ class _RemotePageState extends State { onInteractionUpdate: (details) { print("$details"); }, - child: Container( - child: CustomPaint( - painter: new ImageEditor(image: _image), - ), - color: MyTheme.grayBg), + child: Container(child: ImagePaint(), color: MyTheme.grayBg), )); } } -class ImageEditor extends CustomPainter { - ImageEditor({ +class ImagePaint extends StatelessWidget { + @override + Widget build(BuildContext context) { + final m = Provider.of(context); + return CustomPaint( + painter: new ImagePainter(image: m.image), + ); + } +} + +class ImagePainter extends CustomPainter { + ImagePainter({ this.image, }); From 13eee4200882170d8423893c550cb76b9fae00bf Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 18 Nov 2020 23:49:48 +0800 Subject: [PATCH 033/422] prepare cursor model --- flutter_hbb/lib/common.dart | 50 ++++++++++++++++++++++++++++++++ flutter_hbb/lib/main.dart | 18 +++++++----- flutter_hbb/lib/remote_page.dart | 6 ++++ 3 files changed, 66 insertions(+), 8 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 9099a53ed..a9cf38f5a 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -62,6 +62,54 @@ class ImageModel with ChangeNotifier { } } +class CursorModel with ChangeNotifier { + ui.Image _image; + final _images = Map(); + double _x = 0; + double _y = 0; + double _hotx = 0; + double _hoty = 0; + + ui.Image get image => _image; + double get x => _x; + double get y => _y; + + void updateCursorData(Map evt) { + var id = int.parse(evt['id']); + _hotx = double.parse(evt['hotx']); + _hoty = double.parse(evt['hoty']); + var width = int.parse(evt['width']); + var height = int.parse(evt['height']); + List colors = json.decode(evt['colors']); + final rgba = Uint8List.fromList(colors.map((s) => s as int).toList()); + ui.decodeImageFromPixels(rgba, width, height, ui.PixelFormat.rgba8888, + (image) { + _image = image; + _images[id] = image; + notifyListeners(); + }); + } + + void updateCursorId(Map evt) { + final tmp = _images[int.parse(evt['id'])]; + if (tmp != null) { + _image = tmp; + notifyListeners(); + } + } + + void updateCursorPosition(Map evt) { + _x = double.parse(evt['x']); + _y = double.parse(evt['y']); + notifyListeners(); + } + + void clear() { + _image = null; + _images.clear(); + } +} + class FFI { static F1 _freeCString; static F2 _getByName; @@ -71,6 +119,7 @@ class FFI { static Pointer _lastRgbaFrame; static final imageModel = ImageModel(); static final ffiModel = FfiModel(); + static final cursorModel = CursorModel(); static String getId() { return getByName('remote_id'); @@ -130,6 +179,7 @@ class FFI { static void close() { setByName('close', ''); FFI.imageModel.update(null); + FFI.cursorModel.clear(); } static void setByName(String name, String value) { diff --git a/flutter_hbb/lib/main.dart b/flutter_hbb/lib/main.dart index e0589f04a..9e32c5bf5 100644 --- a/flutter_hbb/lib/main.dart +++ b/flutter_hbb/lib/main.dart @@ -14,13 +14,15 @@ class App extends StatelessWidget { value: FFI.ffiModel, child: ChangeNotifierProvider.value( value: FFI.imageModel, - child: MaterialApp( - title: 'RustDesk', - theme: ThemeData( - primarySwatch: Colors.blue, - visualDensity: VisualDensity.adaptivePlatformDensity, - ), - home: HomePage(title: 'RustDesk'), - ))); + child: ChangeNotifierProvider.value( + value: FFI.cursorModel, + child: MaterialApp( + title: 'RustDesk', + theme: ThemeData( + primarySwatch: Colors.blue, + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: HomePage(title: 'RustDesk'), + )))); } } diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 009fd0085..d1a16a921 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -56,6 +56,12 @@ class _RemotePageState extends State { handlePeerInfo(evt); } else if (name == 'switch_display') { handleSwitchDisplay(evt); + } else if (name == 'cursor_data') { + FFI.cursorModel.updateCursorData(evt); + } else if (name == 'cursor_id') { + FFI.cursorModel.updateCursorId(evt); + } else if (name == 'cursor_position') { + FFI.cursorModel.updateCursorPosition(evt); } } if (!_decoding) { From b594c8836e72815fa5c526048d7c06a38022156e Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 19 Nov 2020 00:32:46 +0800 Subject: [PATCH 034/422] refactor --- flutter_hbb/lib/common.dart | 207 +------------------ flutter_hbb/lib/home_page.dart | 1 + flutter_hbb/lib/main.dart | 2 +- flutter_hbb/lib/model.dart | 327 +++++++++++++++++++++++++++++++ flutter_hbb/lib/remote_page.dart | 102 +--------- 5 files changed, 333 insertions(+), 306 deletions(-) create mode 100644 flutter_hbb/lib/model.dart diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index a9cf38f5a..9f25f8fb1 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -1,19 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:ffi/ffi.dart'; -import 'package:path_provider/path_provider.dart'; -import 'dart:io'; -import 'dart:ffi'; import 'dart:async'; -import 'dart:convert'; import 'package:flutter_easyloading/flutter_easyloading.dart'; -import 'dart:typed_data'; -import 'dart:ui' as ui; - -class RgbaFrame extends Struct { - @Uint32() - int len; - Pointer data; -} +import 'model.dart'; class HexColor extends Color { HexColor(final String hexColor) : super(_getColorFromHex(hexColor)); @@ -33,199 +21,6 @@ class MyTheme { static const Color accent = Color(0xFF0071FF); } -typedef F1 = void Function(Pointer); -typedef F2 = Pointer Function(Pointer, Pointer); -typedef F3 = void Function(Pointer, Pointer); -typedef F4 = void Function(Pointer); -typedef F5 = Pointer Function(); - -// https://juejin.im/post/6844903864852807694 -class FfiModel with ChangeNotifier { - FfiModel() { - init(); - } - - Future init() async { - await FFI.init(); - notifyListeners(); - } -} - -class ImageModel with ChangeNotifier { - ui.Image _image; - - ui.Image get image => _image; - - void update(ui.Image image) { - _image = image; - notifyListeners(); - } -} - -class CursorModel with ChangeNotifier { - ui.Image _image; - final _images = Map(); - double _x = 0; - double _y = 0; - double _hotx = 0; - double _hoty = 0; - - ui.Image get image => _image; - double get x => _x; - double get y => _y; - - void updateCursorData(Map evt) { - var id = int.parse(evt['id']); - _hotx = double.parse(evt['hotx']); - _hoty = double.parse(evt['hoty']); - var width = int.parse(evt['width']); - var height = int.parse(evt['height']); - List colors = json.decode(evt['colors']); - final rgba = Uint8List.fromList(colors.map((s) => s as int).toList()); - ui.decodeImageFromPixels(rgba, width, height, ui.PixelFormat.rgba8888, - (image) { - _image = image; - _images[id] = image; - notifyListeners(); - }); - } - - void updateCursorId(Map evt) { - final tmp = _images[int.parse(evt['id'])]; - if (tmp != null) { - _image = tmp; - notifyListeners(); - } - } - - void updateCursorPosition(Map evt) { - _x = double.parse(evt['x']); - _y = double.parse(evt['y']); - notifyListeners(); - } - - void clear() { - _image = null; - _images.clear(); - } -} - -class FFI { - static F1 _freeCString; - static F2 _getByName; - static F3 _setByName; - static F4 _freeRgba; - static F5 _getRgba; - static Pointer _lastRgbaFrame; - static final imageModel = ImageModel(); - static final ffiModel = FfiModel(); - static final cursorModel = CursorModel(); - - static String getId() { - return getByName('remote_id'); - } - - static List peers() { - try { - List peers = json.decode(getByName('peers')); - return peers - .map((s) => s as List) - .map((s) => - Peer.fromJson(s[0] as String, s[1] as Map)) - .toList(); - } catch (e) { - print(e); - } - return []; - } - - static void connect(String id) { - setByName('connect', id); - } - - static void clearRgbaFrame() { - if (_lastRgbaFrame != null && _lastRgbaFrame != nullptr) - _freeRgba(_lastRgbaFrame); - } - - static Uint8List getRgba() { - _lastRgbaFrame = _getRgba(); - if (_lastRgbaFrame == null || _lastRgbaFrame == nullptr) return null; - final ref = _lastRgbaFrame.ref; - return Uint8List.sublistView(ref.data.asTypedList(ref.len)); - } - - static Map popEvent() { - var s = getByName('event'); - if (s == '') return null; - try { - Map event = json.decode(s); - return event; - } catch (e) { - print(e); - } - return null; - } - - static void login(String password, bool remember) { - setByName( - 'login', - json.encode({ - 'password': password, - 'remember': remember ? 'true' : 'false', - })); - } - - static void close() { - setByName('close', ''); - FFI.imageModel.update(null); - FFI.cursorModel.clear(); - } - - static void setByName(String name, String value) { - _setByName(Utf8.toUtf8(name), Utf8.toUtf8(value)); - } - - static String getByName(String name, {String arg = ''}) { - var p = _getByName(Utf8.toUtf8(name), Utf8.toUtf8(arg)); - assert(p != nullptr && p != null); - var res = Utf8.fromUtf8(p); - // https://github.com/brickpop/flutter-rust-ffi - _freeCString(p); - return res; - } - - static Future init() async { - final dylib = Platform.isAndroid - ? DynamicLibrary.open('librustdesk.so') - : DynamicLibrary.process(); - _getByName = dylib.lookupFunction('get_by_name'); - _setByName = - dylib.lookupFunction, Pointer), F3>( - 'set_by_name'); - _freeCString = dylib - .lookupFunction), F1>('rust_cstr_free'); - _freeRgba = dylib - .lookupFunction), F4>('free_rgba'); - _getRgba = dylib.lookupFunction('get_rgba'); - final dir = (await getApplicationDocumentsDirectory()).path; - setByName('init', dir); - } -} - -class Peer { - final String id; - final String username; - final String hostname; - final String platform; - - Peer.fromJson(String id, Map json) - : id = id, - username = json['username'], - hostname = json['hostname'], - platform = json['platform']; -} - // https://github.com/huangjianke/flutter_easyloading void showLoading(String text) { dismissLoading(); diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index a04198e15..e22903611 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'common.dart'; +import 'model.dart'; import 'remote_page.dart'; class HomePage extends StatefulWidget { diff --git a/flutter_hbb/lib/main.dart b/flutter_hbb/lib/main.dart index 9e32c5bf5..99767935a 100644 --- a/flutter_hbb/lib/main.dart +++ b/flutter_hbb/lib/main.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'common.dart'; +import 'model.dart'; import 'home_page.dart'; void main() { diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart new file mode 100644 index 000000000..8a4132e00 --- /dev/null +++ b/flutter_hbb/lib/model.dart @@ -0,0 +1,327 @@ +import 'package:ffi/ffi.dart'; +import 'package:path_provider/path_provider.dart'; +import 'dart:io'; +import 'dart:ffi'; +import 'dart:convert'; +import 'dart:typed_data'; +import 'dart:ui' as ui; +import 'package:flutter/material.dart'; +import 'dart:async'; +import 'common.dart'; + +class RgbaFrame extends Struct { + @Uint32() + int len; + Pointer data; +} + +typedef F1 = void Function(Pointer); +typedef F2 = Pointer Function(Pointer, Pointer); +typedef F3 = void Function(Pointer, Pointer); +typedef F4 = void Function(Pointer); +typedef F5 = Pointer Function(); + +// https://juejin.im/post/6844903864852807694 +class FfiModel with ChangeNotifier { + PeerInfo _pi = PeerInfo(); + Display _display = Display(); + bool _decoding = false; + + FfiModel() { + init(); + } + + Future init() async { + await FFI.init(); + notifyListeners(); + } + + void clear() { + _decoding = false; + } + + void update(String id, BuildContext context) { + var evt = FFI.popEvent(); + if (evt != null) { + var name = evt['name']; + if (name == 'msgbox') { + handleMsgbox(evt, id, context); + } else if (name == 'peer_info') { + handlePeerInfo(evt); + } else if (name == 'switch_display') { + handleSwitchDisplay(evt); + } else if (name == 'cursor_data') { + FFI.cursorModel.updateCursorData(evt); + } else if (name == 'cursor_id') { + FFI.cursorModel.updateCursorId(evt); + } else if (name == 'cursor_position') { + FFI.cursorModel.updateCursorPosition(evt); + } + } + if (!_decoding) { + var rgba = FFI.getRgba(); + if (rgba != null) { + _decoding = true; + ui.decodeImageFromPixels( + rgba, _display.width, _display.height, ui.PixelFormat.bgra8888, + (image) { + FFI.clearRgbaFrame(); + _decoding = false; + try { + // my throw exception, because the listener maybe already dispose + FFI.imageModel.update(image); + } catch (e) {} + }); + } + } + } + + void handleMsgbox(Map evt, String id, BuildContext context) { + var type = evt['type']; + var title = evt['title']; + var text = evt['text']; + if (type == 're-input-password') { + wrongPasswordDialog(id, context); + } else if (type == 'input-password') { + enterPasswordDialog(id, context); + } else { + msgbox(type, title, text, context); + } + } + + void handleSwitchDisplay(Map evt) { + _pi.currentDisplay = int.parse(evt['display']); + _display.x = double.parse(evt['x']); + _display.y = double.parse(evt['y']); + _display.width = int.parse(evt['width']); + _display.height = int.parse(evt['height']); + FFI.cursorModel.updateDisplayOrigin(_display.x, _display.y); + } + + void handlePeerInfo(Map evt) { + dismissLoading(); + _pi.username = evt['username']; + _pi.hostname = evt['hostname']; + _pi.platform = evt['platform']; + _pi.sasEnabled = evt['sas_enabled'] == "true"; + _pi.currentDisplay = int.parse(evt['current_display']); + List displays = json.decode(evt['displays']); + _pi.displays = List(); + for (int i = 0; i < displays.length; ++i) { + Map d0 = displays[i]; + var d = Display(); + d.x = d0['x'].toDouble(); + d.y = d0['y'].toDouble(); + d.width = d0['width']; + d.height = d0['height']; + _pi.displays.add(d); + } + if (_pi.currentDisplay < _pi.displays.length) { + _display = _pi.displays[_pi.currentDisplay]; + FFI.cursorModel.updateDisplayOrigin(_display.x, _display.y); + } + } +} + +class ImageModel with ChangeNotifier { + ui.Image _image; + + ui.Image get image => _image; + + void update(ui.Image image) { + _image = image; + notifyListeners(); + } +} + +class CursorModel with ChangeNotifier { + ui.Image _image; + final _images = Map(); + double _x = 0; + double _y = 0; + double _hotx = 0; + double _hoty = 0; + double _displayOriginX = 0; + double _displayOriginY = 0; + + ui.Image get image => _image; + double get x => _x - _displayOriginX - _hotx; + double get y => _y - _displayOriginY - _hoty; + + void updateCursorData(Map evt) { + var id = int.parse(evt['id']); + _hotx = double.parse(evt['hotx']); + _hoty = double.parse(evt['hoty']); + var width = int.parse(evt['width']); + var height = int.parse(evt['height']); + List colors = json.decode(evt['colors']); + final rgba = Uint8List.fromList(colors.map((s) => s as int).toList()); + ui.decodeImageFromPixels(rgba, width, height, ui.PixelFormat.rgba8888, + (image) { + _image = image; + _images[id] = image; + try { + // my throw exception, because the listener maybe already dispose + notifyListeners(); + } catch (e) {} + }); + } + + void updateCursorId(Map evt) { + final tmp = _images[int.parse(evt['id'])]; + if (tmp != null) { + _image = tmp; + notifyListeners(); + } + } + + void updateCursorPosition(Map evt) { + _x = double.parse(evt['x']); + _y = double.parse(evt['y']); + notifyListeners(); + } + + void updateDisplayOrigin(double x, double y) { + _displayOriginX = x; + _displayOriginY = y; + notifyListeners(); + } + + void clear() { + _image = null; + _images.clear(); + } +} + +class FFI { + static F1 _freeCString; + static F2 _getByName; + static F3 _setByName; + static F4 _freeRgba; + static F5 _getRgba; + static Pointer _lastRgbaFrame; + static final imageModel = ImageModel(); + static final ffiModel = FfiModel(); + static final cursorModel = CursorModel(); + + static String getId() { + return getByName('remote_id'); + } + + static List peers() { + try { + List peers = json.decode(getByName('peers')); + return peers + .map((s) => s as List) + .map((s) => + Peer.fromJson(s[0] as String, s[1] as Map)) + .toList(); + } catch (e) { + print(e); + } + return []; + } + + static void connect(String id) { + setByName('connect', id); + } + + static void clearRgbaFrame() { + if (_lastRgbaFrame != null && _lastRgbaFrame != nullptr) + _freeRgba(_lastRgbaFrame); + } + + static Uint8List getRgba() { + _lastRgbaFrame = _getRgba(); + if (_lastRgbaFrame == null || _lastRgbaFrame == nullptr) return null; + final ref = _lastRgbaFrame.ref; + return Uint8List.sublistView(ref.data.asTypedList(ref.len)); + } + + static Map popEvent() { + var s = getByName('event'); + if (s == '') return null; + try { + Map event = json.decode(s); + return event; + } catch (e) { + print(e); + } + return null; + } + + static void login(String password, bool remember) { + setByName( + 'login', + json.encode({ + 'password': password, + 'remember': remember ? 'true' : 'false', + })); + } + + static void close() { + setByName('close', ''); + FFI.imageModel.update(null); + FFI.cursorModel.clear(); + FFI.ffiModel.clear(); + } + + static void setByName(String name, String value) { + _setByName(Utf8.toUtf8(name), Utf8.toUtf8(value)); + } + + static String getByName(String name, {String arg = ''}) { + var p = _getByName(Utf8.toUtf8(name), Utf8.toUtf8(arg)); + assert(p != nullptr && p != null); + var res = Utf8.fromUtf8(p); + // https://github.com/brickpop/flutter-rust-ffi + _freeCString(p); + return res; + } + + static Future init() async { + final dylib = Platform.isAndroid + ? DynamicLibrary.open('librustdesk.so') + : DynamicLibrary.process(); + _getByName = dylib.lookupFunction('get_by_name'); + _setByName = + dylib.lookupFunction, Pointer), F3>( + 'set_by_name'); + _freeCString = dylib + .lookupFunction), F1>('rust_cstr_free'); + _freeRgba = dylib + .lookupFunction), F4>('free_rgba'); + _getRgba = dylib.lookupFunction('get_rgba'); + final dir = (await getApplicationDocumentsDirectory()).path; + setByName('init', dir); + } +} + +class Peer { + final String id; + final String username; + final String hostname; + final String platform; + + Peer.fromJson(String id, Map json) + : id = id, + username = json['username'], + hostname = json['hostname'], + platform = json['platform']; +} + +class Display { + double x = 0; + double y = 0; + int width = 0; + int height = 0; +} + +class PeerInfo { + String username; + String hostname; + String platform; + bool sasEnabled; + int currentDisplay; + List displays; +} diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index d1a16a921..6c9f63702 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'common.dart'; import 'package:flutter/services.dart'; import 'dart:ui' as ui; import 'package:flutter_easyloading/flutter_easyloading.dart'; -import 'dart:convert'; import 'dart:async'; +import 'common.dart'; +import 'model.dart'; class RemotePage extends StatefulWidget { RemotePage({Key key, this.id}) : super(key: key); @@ -19,9 +19,6 @@ class RemotePage extends StatefulWidget { // https://github.com/hanxu317317/flutter_plan_demo/blob/master/lib/src/enter.dart class _RemotePageState extends State { Timer _interval; - PeerInfo _pi = PeerInfo(); - Display _display = Display(); - bool _decoding = false; @override void initState() { @@ -42,88 +39,11 @@ class _RemotePageState extends State { FFI.close(); _interval.cancel(); dismissLoading(); - _decoding = null; SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values); } void interval() { - var evt = FFI.popEvent(); - if (evt != null) { - var name = evt['name']; - if (name == 'msgbox') { - handleMsgbox(evt); - } else if (name == 'peer_info') { - handlePeerInfo(evt); - } else if (name == 'switch_display') { - handleSwitchDisplay(evt); - } else if (name == 'cursor_data') { - FFI.cursorModel.updateCursorData(evt); - } else if (name == 'cursor_id') { - FFI.cursorModel.updateCursorId(evt); - } else if (name == 'cursor_position') { - FFI.cursorModel.updateCursorPosition(evt); - } - } - if (!_decoding) { - var rgba = FFI.getRgba(); - if (rgba != null) { - _decoding = true; - ui.decodeImageFromPixels( - rgba, _display.width, _display.height, ui.PixelFormat.bgra8888, - (image) { - FFI.clearRgbaFrame(); - if (_decoding == null) return; - _decoding = false; - FFI.imageModel.update(image); - }); - } - } - } - - void handleSwitchDisplay(Map evt) { - _pi.currentDisplay = int.parse(evt['display']); - _display.x = int.parse(evt['x']); - _display.y = int.parse(evt['y']); - _display.width = int.parse(evt['width']); - _display.height = int.parse(evt['height']); - setState(() {}); - } - - void handlePeerInfo(Map evt) { - dismissLoading(); - _pi.username = evt['username']; - _pi.hostname = evt['hostname']; - _pi.platform = evt['platform']; - _pi.sasEnabled = evt['sas_enabled'] == "true"; - _pi.currentDisplay = int.parse(evt['current_display']); - List displays = json.decode(evt['displays']); - _pi.displays = List(); - for (int i = 0; i < displays.length; ++i) { - Map d0 = displays[i]; - var d = Display(); - d.x = d0['x']; - d.y = d0['y']; - d.width = d0['width']; - d.height = d0['height']; - _pi.displays.add(d); - } - if (_pi.currentDisplay < _pi.displays.length) { - _display = _pi.displays[_pi.currentDisplay]; - } - setState(() {}); - } - - void handleMsgbox(Map evt) { - var type = evt['type']; - var title = evt['title']; - var text = evt['text']; - if (type == 're-input-password') { - wrongPasswordDialog(widget.id, context); - } else if (type == 'input-password') { - enterPasswordDialog(widget.id, context); - } else { - msgbox(type, title, text, context); - } + FFI.ffiModel.update(widget.id, context); } @override @@ -170,19 +90,3 @@ class ImagePainter extends CustomPainter { return oldDelegate != this; } } - -class Display { - int x = 0; - int y = 0; - int width = 0; - int height = 0; -} - -class PeerInfo { - String username; - String hostname; - String platform; - bool sasEnabled; - int currentDisplay; - List displays; -} From 0bb1c71b877e44513e79f4c0c3c87924e1ed011e Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 19 Nov 2020 00:53:10 +0800 Subject: [PATCH 035/422] wait for image --- flutter_hbb/lib/model.dart | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 8a4132e00..8ab833b78 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -26,6 +26,7 @@ class FfiModel with ChangeNotifier { PeerInfo _pi = PeerInfo(); Display _display = Display(); bool _decoding = false; + bool _waitForImage = false; FfiModel() { init(); @@ -61,6 +62,10 @@ class FfiModel with ChangeNotifier { if (!_decoding) { var rgba = FFI.getRgba(); if (rgba != null) { + if (_waitForImage) { + _waitForImage = false; + dismissLoading(); + } _decoding = true; ui.decodeImageFromPixels( rgba, _display.width, _display.height, ui.PixelFormat.bgra8888, @@ -120,6 +125,10 @@ class FfiModel with ChangeNotifier { _display = _pi.displays[_pi.currentDisplay]; FFI.cursorModel.updateDisplayOrigin(_display.x, _display.y); } + if (displays.length > 1) { + showLoading('Waiting for image...'); + _waitForImage = true; + } } } From 0eb19dcf2a536ba587b4146a30b1d9720923466c Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 19 Nov 2020 17:22:42 +0800 Subject: [PATCH 036/422] bar --- flutter_hbb/lib/common.dart | 1 + flutter_hbb/lib/remote_page.dart | 68 +++++++++++++++++++++++++++----- 2 files changed, 60 insertions(+), 9 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 9f25f8fb1..bce191c93 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -19,6 +19,7 @@ class MyTheme { static const Color grayBg = Color(0xFFEEEEEE); static const Color white = Color(0xFFFFFFFF); static const Color accent = Color(0xFF0071FF); + static const Color accent50 = Color(0x770071FF); } // https://github.com/huangjianke/flutter_easyloading diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 6c9f63702..b844f0611 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -19,6 +19,7 @@ class RemotePage extends StatefulWidget { // https://github.com/hanxu317317/flutter_plan_demo/blob/master/lib/src/enter.dart class _RemotePageState extends State { Timer _interval; + bool _show_bar = true; @override void initState() { @@ -50,15 +51,64 @@ class _RemotePageState extends State { Widget build(BuildContext context) { // Size size = MediaQueryData.fromWindow(ui.window).size; // MediaQuery.of(context).size.height; - return FlutterEasyLoading( - child: InteractiveViewer( - constrained: false, - panEnabled: true, - onInteractionUpdate: (details) { - print("$details"); - }, - child: Container(child: ImagePaint(), color: MyTheme.grayBg), - )); + return Scaffold( + floatingActionButton: _show_bar + ? null + : FloatingActionButton( + mini: true, + child: Icon(Icons.expand_less), + backgroundColor: MyTheme.accent50, + onPressed: () { + setState(() => _show_bar = !_show_bar); + }), + bottomNavigationBar: _show_bar + ? BottomAppBar( + color: MyTheme.accent, + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row(children: [ + IconButton( + color: Colors.white, + icon: Icon(Icons.clear), + onPressed: () {}, + ), + IconButton( + color: Colors.white, + icon: Icon(Icons.keyboard), + onPressed: () {}, + ), + IconButton( + color: Colors.white, + icon: Icon(Icons.tv), + onPressed: () {}, + ), + IconButton( + color: Colors.white, + icon: Icon(Icons.settings), + onPressed: () {}, + ) + ]), + IconButton( + color: Colors.white, + icon: Icon(Icons.expand_more), + onPressed: () { + setState(() => _show_bar = !_show_bar); + }), + ], + ), + ) + : null, + body: FlutterEasyLoading( + child: InteractiveViewer( + constrained: false, + panEnabled: true, + onInteractionUpdate: (details) { + print("$details"); + }, + child: Container(child: ImagePaint(), color: MyTheme.grayBg), + ))); } } From ad5eb7830c35f4aa6f6a4220ad3022ddd4fcfebc Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 19 Nov 2020 18:22:06 +0800 Subject: [PATCH 037/422] interactiveview not work on stack --- flutter_hbb/lib/home_page.dart | 2 +- flutter_hbb/lib/model.dart | 7 +++-- flutter_hbb/lib/remote_page.dart | 47 +++++++++++++++++++++++++------- 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index e22903611..40d05fe82 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -22,6 +22,7 @@ class _HomePageState extends State { if (_idController.text.isEmpty) _idController.text = FFI.getId(); // This method is rerun every time setState is called return Scaffold( + backgroundColor: MyTheme.grayBg, appBar: AppBar( title: Text(widget.title), ), @@ -34,7 +35,6 @@ class _HomePageState extends State { getSearchBarUI(), Expanded(child: Container()) ]), - color: MyTheme.grayBg, padding: const EdgeInsets.fromLTRB(16.0, 0.0, 16.0, 0.0), )); } diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 8ab833b78..594e73617 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -42,8 +42,9 @@ class FfiModel with ChangeNotifier { } void update(String id, BuildContext context) { - var evt = FFI.popEvent(); - if (evt != null) { + for (;;) { + var evt = FFI.popEvent(); + if (evt == null) break; var name = evt['name']; if (name == 'msgbox') { handleMsgbox(evt, id, context); @@ -139,7 +140,7 @@ class ImageModel with ChangeNotifier { void update(ui.Image image) { _image = image; - notifyListeners(); + if (image != null) notifyListeners(); } } diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index b844f0611..9180bb490 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -4,6 +4,7 @@ import 'package:flutter/services.dart'; import 'dart:ui' as ui; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'dart:async'; +import 'dart:math' as math; import 'common.dart'; import 'model.dart'; @@ -52,6 +53,7 @@ class _RemotePageState extends State { // Size size = MediaQueryData.fromWindow(ui.window).size; // MediaQuery.of(context).size.height; return Scaffold( + backgroundColor: MyTheme.grayBg, floatingActionButton: _show_bar ? null : FloatingActionButton( @@ -79,6 +81,13 @@ class _RemotePageState extends State { icon: Icon(Icons.keyboard), onPressed: () {}, ), + Transform.rotate( + angle: 15 * math.pi / 180, + child: IconButton( + color: Colors.white, + icon: Icon(Icons.flash_on), + onPressed: () {}, + )), IconButton( color: Colors.white, icon: Icon(Icons.tv), @@ -101,14 +110,18 @@ class _RemotePageState extends State { ) : null, body: FlutterEasyLoading( - child: InteractiveViewer( - constrained: false, - panEnabled: true, - onInteractionUpdate: (details) { - print("$details"); - }, - child: Container(child: ImagePaint(), color: MyTheme.grayBg), - ))); + child: InteractiveViewer( + constrained: false, + panEnabled: true, + onInteractionUpdate: (details) { + print("$details"); + }, + child: Stack(children: [ + ImagePaint(), + CursorPaint(), + ]), + ), + )); } } @@ -117,7 +130,17 @@ class ImagePaint extends StatelessWidget { Widget build(BuildContext context) { final m = Provider.of(context); return CustomPaint( - painter: new ImagePainter(image: m.image), + painter: new ImagePainter(image: m.image, x: 0, y: 0), + ); + } +} + +class CursorPaint extends StatelessWidget { + @override + Widget build(BuildContext context) { + final m = Provider.of(context); + return CustomPaint( + painter: new ImagePainter(image: m.image, x: m.x, y: m.y), ); } } @@ -125,14 +148,18 @@ class ImagePaint extends StatelessWidget { class ImagePainter extends CustomPainter { ImagePainter({ this.image, + this.x, + this.y, }); ui.Image image; + double x; + double y; @override void paint(Canvas canvas, Size size) { if (image == null) return; - canvas.drawImage(image, new Offset(0, 0), new Paint()); + canvas.drawImage(image, new Offset(x, y), new Paint()); } @override From 86add59e9290887f270ba660b68bcdec52357d8c Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 19 Nov 2020 18:41:37 +0800 Subject: [PATCH 038/422] color --- flutter_hbb/lib/common.dart | 1 + flutter_hbb/lib/remote_page.dart | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index bce191c93..3d7408a5b 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -20,6 +20,7 @@ class MyTheme { static const Color white = Color(0xFFFFFFFF); static const Color accent = Color(0xFF0071FF); static const Color accent50 = Color(0x770071FF); + static const Color canvasColor = Color(0xFF212121); } // https://github.com/huangjianke/flutter_easyloading diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 9180bb490..007f31a98 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -52,8 +52,8 @@ class _RemotePageState extends State { Widget build(BuildContext context) { // Size size = MediaQueryData.fromWindow(ui.window).size; // MediaQuery.of(context).size.height; + EasyLoading.instance.loadingStyle = EasyLoadingStyle.light; return Scaffold( - backgroundColor: MyTheme.grayBg, floatingActionButton: _show_bar ? null : FloatingActionButton( @@ -110,6 +110,8 @@ class _RemotePageState extends State { ) : null, body: FlutterEasyLoading( + child: Container( + color: MyTheme.canvasColor, child: InteractiveViewer( constrained: false, panEnabled: true, @@ -121,7 +123,7 @@ class _RemotePageState extends State { CursorPaint(), ]), ), - )); + ))); } } From 0b0fb4f1455c413ecf0274a08466fe20ecea51f2 Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 19 Nov 2020 21:59:49 +0800 Subject: [PATCH 039/422] refactor alertDialog --- flutter_hbb/lib/common.dart | 138 ++++++------------------------- flutter_hbb/lib/model.dart | 19 ++--- flutter_hbb/lib/remote_page.dart | 95 ++++++++++++++++++++- flutter_hbb/pubspec.lock | 14 ++++ flutter_hbb/pubspec.yaml | 1 + 5 files changed, 137 insertions(+), 130 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 3d7408a5b..43de33e3e 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'dart:async'; import 'package:flutter_easyloading/flutter_easyloading.dart'; -import 'model.dart'; +import 'package:tuple/tuple.dart'; class HexColor extends Color { HexColor(final String hexColor) : super(_getColorFromHex(hexColor)); @@ -41,65 +41,23 @@ void showSuccess(String text) { bool _hasDialog = false; // https://material.io/develop/flutter/components/dialogs -Future enterPasswordDialog(String id, BuildContext context) async { +Future showAlertDialog( + BuildContext context, + Tuple3> Function( + void Function(void Function())) + build) async { dismissLoading(); if (_hasDialog) { Navigator.pop(context); } _hasDialog = true; - final controller = TextEditingController(); - var remember = FFI.getByName('remember', arg: id) == 'true'; var dialog = StatefulBuilder(builder: (context, setState) { + var widgets = build(setState); return AlertDialog( - title: Text('Please enter your password'), + title: widgets.item1, contentPadding: const EdgeInsets.all(20.0), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextField( - autofocus: true, - obscureText: true, - controller: controller, - decoration: const InputDecoration( - labelText: 'Password', - ), - ), - ListTile( - title: Text( - 'Remember the password', - ), - leading: Checkbox( - value: remember, - onChanged: (v) { - setState(() { - remember = v; - }); - }, - ), - ), - ], - ), - actions: [ - FlatButton( - textColor: MyTheme.accent, - onPressed: () { - Navigator.pop(context); - Navigator.pop(context); - }, - child: Text('Cancel'), - ), - FlatButton( - textColor: MyTheme.accent, - onPressed: () { - var text = controller.text.trim(); - if (text == '') return; - FFI.login(text, remember); - showLoading('Logging in...'); - Navigator.pop(context); - }, - child: Text('OK'), - ), - ], + content: widgets.item2, + actions: widgets.item3, ); }); await showDialog( @@ -109,67 +67,17 @@ Future enterPasswordDialog(String id, BuildContext context) async { _hasDialog = false; } -Future wrongPasswordDialog(String id, BuildContext context) async { - dismissLoading(); - if (_hasDialog) { - Navigator.pop(context); - } - _hasDialog = true; - var dialog = AlertDialog( - title: Text('Wrong Password'), - contentPadding: const EdgeInsets.all(20.0), - content: Text('Do you want to enter again?'), - actions: [ - FlatButton( - textColor: MyTheme.accent, - onPressed: () { - Navigator.pop(context); - Navigator.pop(context); - }, - child: Text('Cancel'), - ), - FlatButton( - textColor: MyTheme.accent, - onPressed: () { - Navigator.pop(context); - enterPasswordDialog(id, context); - }, - child: Text('Retry'), - ), - ], - ); - await showDialog( - context: context, - barrierDismissible: false, - builder: (context) => dialog); - _hasDialog = false; -} - -Future msgbox( - String type, String title, String text, BuildContext context) async { - dismissLoading(); - if (_hasDialog) { - Navigator.pop(context); - } - _hasDialog = true; - var dialog = AlertDialog( - title: Text(title), - contentPadding: const EdgeInsets.all(20.0), - content: Text(text), - actions: [ - FlatButton( - textColor: MyTheme.accent, - onPressed: () { - Navigator.pop(context); - Navigator.pop(context); - }, - child: Text('OK'), - ), - ], - ); - await showDialog( - context: context, - barrierDismissible: false, - builder: (context) => dialog); - _hasDialog = false; +void msgbox(String type, String title, String text, BuildContext context) { + showAlertDialog( + context, + (_) => Tuple3(Text(title), Text(text), [ + FlatButton( + textColor: MyTheme.accent, + onPressed: () { + Navigator.pop(context); + Navigator.pop(context); + }, + child: Text('OK'), + ), + ])); } diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 594e73617..f3ee99956 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -41,7 +41,11 @@ class FfiModel with ChangeNotifier { _decoding = false; } - void update(String id, BuildContext context) { + void update( + String id, + BuildContext context, + void Function(Map evt, String id, BuildContext context) + handleMsgbox) { for (;;) { var evt = FFI.popEvent(); if (evt == null) break; @@ -82,19 +86,6 @@ class FfiModel with ChangeNotifier { } } - void handleMsgbox(Map evt, String id, BuildContext context) { - var type = evt['type']; - var title = evt['title']; - var text = evt['text']; - if (type == 're-input-password') { - wrongPasswordDialog(id, context); - } else if (type == 'input-password') { - enterPasswordDialog(id, context); - } else { - msgbox(type, title, text, context); - } - } - void handleSwitchDisplay(Map evt) { _pi.currentDisplay = int.parse(evt['display']); _display.x = double.parse(evt['x']); diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 007f31a98..8f881474a 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -7,6 +7,7 @@ import 'dart:async'; import 'dart:math' as math; import 'common.dart'; import 'model.dart'; +import 'package:tuple/tuple.dart'; class RemotePage extends StatefulWidget { RemotePage({Key key, this.id}) : super(key: key); @@ -45,7 +46,20 @@ class _RemotePageState extends State { } void interval() { - FFI.ffiModel.update(widget.id, context); + FFI.ffiModel.update(widget.id, context, handleMsgbox); + } + + void handleMsgbox(Map evt, String id, BuildContext context) { + var type = evt['type']; + var title = evt['title']; + var text = evt['text']; + if (type == 're-input-password') { + wrongPasswordDialog(id, context); + } else if (type == 'input-password') { + enterPasswordDialog(id, context); + } else { + msgbox(type, title, text, context); + } } @override @@ -169,3 +183,82 @@ class ImagePainter extends CustomPainter { return oldDelegate != this; } } + +void enterPasswordDialog(String id, BuildContext context) { + final controller = TextEditingController(); + var remember = FFI.getByName('remember', arg: id) == 'true'; + showAlertDialog( + context, + (setState) => Tuple3( + Text('Please enter your password'), + Column(mainAxisSize: MainAxisSize.min, children: [ + TextField( + autofocus: true, + obscureText: true, + controller: controller, + decoration: const InputDecoration( + labelText: 'Password', + ), + ), + ListTile( + title: Text( + 'Remember the password', + ), + leading: Checkbox( + value: remember, + onChanged: (v) { + setState(() { + remember = v; + }); + }, + ), + ), + ]), + [ + TextField( + autofocus: true, + obscureText: true, + controller: controller, + decoration: const InputDecoration( + labelText: 'Password', + ), + ), + ListTile( + title: Text( + 'Remember the password', + ), + leading: Checkbox( + value: remember, + onChanged: (v) { + setState(() { + remember = v; + }); + }, + ), + ), + ])); +} + +void wrongPasswordDialog(String id, BuildContext context) { + showAlertDialog( + context, + (_) => + Tuple3(Text('Wrong Password'), Text('Do you want to enter again?'), [ + FlatButton( + textColor: MyTheme.accent, + onPressed: () { + Navigator.pop(context); + Navigator.pop(context); + }, + child: Text('Cancel'), + ), + FlatButton( + textColor: MyTheme.accent, + onPressed: () { + Navigator.pop(context); + enterPasswordDialog(id, context); + }, + child: Text('Retry'), + ), + ])); +} diff --git a/flutter_hbb/pubspec.lock b/flutter_hbb/pubspec.lock index e4652d505..e4f1ca350 100644 --- a/flutter_hbb/pubspec.lock +++ b/flutter_hbb/pubspec.lock @@ -193,6 +193,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.3.2+2" + quiver: + dependency: transitive + description: + name: quiver + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.5" sky_engine: dependency: transitive description: flutter @@ -240,6 +247,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.2.19-nullsafety.2" + tuple: + dependency: "direct main" + description: + name: tuple + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" typed_data: dependency: transitive description: diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index c75c075f3..9aa35bd58 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -32,6 +32,7 @@ dependencies: path_provider: ^1.6.24 provider: ^4.3.2+2 flutter_easyloading: ^2.1.3 + tuple: ^1.0.1 dev_dependencies: flutter_test: From e516dd3267cf58536f6e80af09afce84b812cb57 Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 20 Nov 2020 00:29:59 +0800 Subject: [PATCH 040/422] skip return key of dialog --- flutter_hbb/lib/common.dart | 14 ++++++++------ flutter_hbb/lib/remote_page.dart | 10 +++++----- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 43de33e3e..55c65825b 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -53,12 +53,14 @@ Future showAlertDialog( _hasDialog = true; var dialog = StatefulBuilder(builder: (context, setState) { var widgets = build(setState); - return AlertDialog( - title: widgets.item1, - contentPadding: const EdgeInsets.all(20.0), - content: widgets.item2, - actions: widgets.item3, - ); + return WillPopScope( + onWillPop: () async => false, + child: AlertDialog( + title: widgets.item1, + contentPadding: const EdgeInsets.all(20.0), + content: widgets.item2, + actions: widgets.item3, + )); }); await showDialog( context: context, diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 8f881474a..36a24b972 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -21,7 +21,7 @@ class RemotePage extends StatefulWidget { // https://github.com/hanxu317317/flutter_plan_demo/blob/master/lib/src/enter.dart class _RemotePageState extends State { Timer _interval; - bool _show_bar = true; + bool _showBar = true; @override void initState() { @@ -68,16 +68,16 @@ class _RemotePageState extends State { // MediaQuery.of(context).size.height; EasyLoading.instance.loadingStyle = EasyLoadingStyle.light; return Scaffold( - floatingActionButton: _show_bar + floatingActionButton: _showBar ? null : FloatingActionButton( mini: true, child: Icon(Icons.expand_less), backgroundColor: MyTheme.accent50, onPressed: () { - setState(() => _show_bar = !_show_bar); + setState(() => _showBar = !_showBar); }), - bottomNavigationBar: _show_bar + bottomNavigationBar: _showBar ? BottomAppBar( color: MyTheme.accent, child: Row( @@ -117,7 +117,7 @@ class _RemotePageState extends State { color: Colors.white, icon: Icon(Icons.expand_more), onPressed: () { - setState(() => _show_bar = !_show_bar); + setState(() => _showBar = !_showBar); }), ], ), From a7c4396c71bcb193745053a2defe59fb876d7282 Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 20 Nov 2020 00:34:17 +0800 Subject: [PATCH 041/422] make onWillPop can be changed --- flutter_hbb/lib/common.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 55c65825b..2fe64bad8 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -45,7 +45,8 @@ Future showAlertDialog( BuildContext context, Tuple3> Function( void Function(void Function())) - build) async { + build, + {WillPopCallback onWillPop}) async { dismissLoading(); if (_hasDialog) { Navigator.pop(context); @@ -53,8 +54,9 @@ Future showAlertDialog( _hasDialog = true; var dialog = StatefulBuilder(builder: (context, setState) { var widgets = build(setState); + if (onWillPop == null) onWillPop = () async => false; return WillPopScope( - onWillPop: () async => false, + onWillPop: onWillPop, child: AlertDialog( title: widgets.item1, contentPadding: const EdgeInsets.all(20.0), From 090f56b9f565121d27ac45c2e5801a49669e248a Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 20 Nov 2020 00:36:23 +0800 Subject: [PATCH 042/422] refactor --- flutter_hbb/lib/common.dart | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 2fe64bad8..dc4deac90 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -39,13 +39,11 @@ void showSuccess(String text) { } bool _hasDialog = false; +typedef BuildAlertDailog = Tuple3> Function( + void Function(void Function())); // https://material.io/develop/flutter/components/dialogs -Future showAlertDialog( - BuildContext context, - Tuple3> Function( - void Function(void Function())) - build, +Future showAlertDialog(BuildContext context, BuildAlertDailog build, {WillPopCallback onWillPop}) async { dismissLoading(); if (_hasDialog) { From d9cbd4230a61cf0623339390b27993defd87d6d0 Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 20 Nov 2020 02:12:48 +0800 Subject: [PATCH 043/422] toggle option --- flutter_hbb/lib/common.dart | 13 ++-- flutter_hbb/lib/model.dart | 2 +- flutter_hbb/lib/remote_page.dart | 128 +++++++++++++++++++------------ 3 files changed, 90 insertions(+), 53 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index dc4deac90..db8f0cf41 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -43,8 +43,10 @@ typedef BuildAlertDailog = Tuple3> Function( void Function(void Function())); // https://material.io/develop/flutter/components/dialogs -Future showAlertDialog(BuildContext context, BuildAlertDailog build, - {WillPopCallback onWillPop}) async { +Future showAlertDialog(BuildContext context, BuildAlertDailog build, + [WillPopCallback onWillPop, + bool barrierDismissible, + double contentPadding = 20]) async { dismissLoading(); if (_hasDialog) { Navigator.pop(context); @@ -57,16 +59,17 @@ Future showAlertDialog(BuildContext context, BuildAlertDailog build, onWillPop: onWillPop, child: AlertDialog( title: widgets.item1, - contentPadding: const EdgeInsets.all(20.0), + contentPadding: EdgeInsets.all(contentPadding), content: widgets.item2, actions: widgets.item3, )); }); - await showDialog( + var res = await showDialog( context: context, - barrierDismissible: false, + barrierDismissible: barrierDismissible, builder: (context) => dialog); _hasDialog = false; + return res; } void msgbox(String type, String title, String text, BuildContext context) { diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index f3ee99956..2f73df63e 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -271,7 +271,7 @@ class FFI { _setByName(Utf8.toUtf8(name), Utf8.toUtf8(value)); } - static String getByName(String name, {String arg = ''}) { + static String getByName(String name, [String arg = '']) { var p = _getByName(Utf8.toUtf8(name), Utf8.toUtf8(arg)); assert(p != nullptr && p != null); var res = Utf8.fromUtf8(p); diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 36a24b972..d3ccce7a6 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -105,7 +105,9 @@ class _RemotePageState extends State { IconButton( color: Colors.white, icon: Icon(Icons.tv), - onPressed: () {}, + onPressed: () { + showOptions(widget.id, context); + }, ), IconButton( color: Colors.white, @@ -130,7 +132,7 @@ class _RemotePageState extends State { constrained: false, panEnabled: true, onInteractionUpdate: (details) { - print("$details"); + print('$details'); }, child: Stack(children: [ ImagePaint(), @@ -186,57 +188,54 @@ class ImagePainter extends CustomPainter { void enterPasswordDialog(String id, BuildContext context) { final controller = TextEditingController(); - var remember = FFI.getByName('remember', arg: id) == 'true'; + var remember = FFI.getByName('remember', id) == 'true'; showAlertDialog( context, (setState) => Tuple3( - Text('Please enter your password'), - Column(mainAxisSize: MainAxisSize.min, children: [ - TextField( - autofocus: true, - obscureText: true, - controller: controller, - decoration: const InputDecoration( - labelText: 'Password', - ), + Text('Please enter your password'), + Column(mainAxisSize: MainAxisSize.min, children: [ + TextField( + autofocus: true, + obscureText: true, + controller: controller, + decoration: const InputDecoration( + labelText: 'Password', ), - ListTile( - title: Text( - 'Remember the password', - ), - leading: Checkbox( - value: remember, - onChanged: (v) { - setState(() { - remember = v; - }); - }, - ), + ), + ListTile( + title: Text( + 'Remember the password', ), - ]), - [ - TextField( - autofocus: true, - obscureText: true, - controller: controller, - decoration: const InputDecoration( - labelText: 'Password', - ), + leading: Checkbox( + value: remember, + onChanged: (v) { + setState(() => remember = v); + }, ), - ListTile( - title: Text( - 'Remember the password', - ), - leading: Checkbox( - value: remember, - onChanged: (v) { - setState(() { - remember = v; - }); - }, - ), - ), - ])); + ), + ]), + [ + FlatButton( + textColor: MyTheme.accent, + onPressed: () { + Navigator.pop(context); + Navigator.pop(context); + }, + child: Text('Cancel'), + ), + FlatButton( + textColor: MyTheme.accent, + onPressed: () { + var text = controller.text.trim(); + if (text == '') return; + FFI.login(text, remember); + showLoading('Logging in...'); + Navigator.pop(context); + }, + child: Text('OK'), + ), + ], + )); } void wrongPasswordDialog(String id, BuildContext context) { @@ -262,3 +261,38 @@ void wrongPasswordDialog(String id, BuildContext context) { ), ])); } + +void showOptions(String id, BuildContext context) { + var showRemoteCursor = + FFI.getByName('toggle_option', 'show-remote-cursor') == 'true'; + var lockAfterSessionEnd = + FFI.getByName('toggle_option', 'lock-after-session-end') == 'true'; + showAlertDialog( + context, + (setState) => Tuple3( + null, + Column(mainAxisSize: MainAxisSize.min, children: [ + CheckboxListTile( + value: showRemoteCursor, + onChanged: (v) { + setState(() { + showRemoteCursor = v; + FFI.setByName('toggle_option', 'show-remote-cursor'); + }); + }, + title: Text('Show remote cursor')), + CheckboxListTile( + value: lockAfterSessionEnd, + onChanged: (v) { + setState(() { + lockAfterSessionEnd = v; + FFI.setByName('toggle_option', 'lock-after-session-end'); + }); + }, + title: Text('Lock after session end')) + ]), + null), + () async => true, + true, + 10); +} From ae79afaf0d64948c3a5b757a7cdb8d842fc37f63 Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 20 Nov 2020 13:06:52 +0800 Subject: [PATCH 044/422] image quality --- flutter_hbb/lib/common.dart | 1 + flutter_hbb/lib/remote_page.dart | 52 +++++++++++++++++++++++++++----- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index db8f0cf41..7c3d31f77 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -21,6 +21,7 @@ class MyTheme { static const Color accent = Color(0xFF0071FF); static const Color accent50 = Color(0x770071FF); static const Color canvasColor = Color(0xFF212121); + static const Color border = Color(0xFFCCCCCC); } // https://github.com/huangjianke/flutter_easyloading diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index d3ccce7a6..2d1afadfd 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -202,16 +202,15 @@ void enterPasswordDialog(String id, BuildContext context) { labelText: 'Password', ), ), - ListTile( + CheckboxListTile( + controlAffinity: ListTileControlAffinity.leading, title: Text( 'Remember the password', ), - leading: Checkbox( - value: remember, - onChanged: (v) { - setState(() => remember = v); - }, - ), + value: remember, + onChanged: (v) { + setState(() => remember = v); + }, ), ]), [ @@ -267,11 +266,50 @@ void showOptions(String id, BuildContext context) { FFI.getByName('toggle_option', 'show-remote-cursor') == 'true'; var lockAfterSessionEnd = FFI.getByName('toggle_option', 'lock-after-session-end') == 'true'; + String quality = FFI.getByName('image_quality'); + if (quality == '') quality = 'balanced'; showAlertDialog( context, (setState) => Tuple3( null, Column(mainAxisSize: MainAxisSize.min, children: [ + RadioListTile( + controlAffinity: ListTileControlAffinity.trailing, + title: const Text('Good image quality'), + value: 'best', + groupValue: quality, + onChanged: (String value) { + setState(() { + quality = value; + FFI.setByName('image_quality', value); + }); + }, + ), + RadioListTile( + controlAffinity: ListTileControlAffinity.trailing, + title: const Text('Balanced'), + value: 'balanced', + groupValue: quality, + onChanged: (String value) { + setState(() { + quality = value; + FFI.setByName('image_quality', value); + }); + }, + ), + RadioListTile( + controlAffinity: ListTileControlAffinity.trailing, + title: const Text('Optimize reaction time'), + value: 'low', + groupValue: quality, + onChanged: (String value) { + setState(() { + quality = value; + FFI.setByName('image_quality', value); + }); + }, + ), + Divider(color: MyTheme.border), CheckboxListTile( value: showRemoteCursor, onChanged: (v) { From 83622cffc6cc1022e3eccf65e2ff8b824bbfb032 Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 20 Nov 2020 16:37:48 +0800 Subject: [PATCH 045/422] input key ffi --- flutter_hbb/lib/common.dart | 15 ++- flutter_hbb/lib/model.dart | 2 +- flutter_hbb/lib/remote_page.dart | 186 +++++++++++++++++++------------ 3 files changed, 127 insertions(+), 76 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 7c3d31f77..9b424aac4 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -16,6 +16,7 @@ class HexColor extends Color { } class MyTheme { + MyTheme._(); static const Color grayBg = Color(0xFFEEEEEE); static const Color white = Color(0xFFFFFFFF); static const Color accent = Color(0xFF0071FF); @@ -46,7 +47,7 @@ typedef BuildAlertDailog = Tuple3> Function( // https://material.io/develop/flutter/components/dialogs Future showAlertDialog(BuildContext context, BuildAlertDailog build, [WillPopCallback onWillPop, - bool barrierDismissible, + bool barrierDismissible = false, double contentPadding = 20]) async { dismissLoading(); if (_hasDialog) { @@ -73,10 +74,20 @@ Future showAlertDialog(BuildContext context, BuildAlertDailog build, return res; } -void msgbox(String type, String title, String text, BuildContext context) { +void msgbox(String type, String title, String text, BuildContext context, + [hasCancel = false]) { showAlertDialog( context, (_) => Tuple3(Text(title), Text(text), [ + hasCancel + ? Spacer() + : FlatButton( + textColor: MyTheme.accent, + onPressed: () { + Navigator.pop(context); + }, + child: Text('Cancel'), + ), FlatButton( textColor: MyTheme.accent, onPressed: () { diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 2f73df63e..54a66b716 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -267,7 +267,7 @@ class FFI { FFI.ffiModel.clear(); } - static void setByName(String name, String value) { + static void setByName(String name, [String value = '']) { _setByName(Utf8.toUtf8(name), Utf8.toUtf8(value)); } diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 2d1afadfd..84166d2fa 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -67,79 +67,92 @@ class _RemotePageState extends State { // Size size = MediaQueryData.fromWindow(ui.window).size; // MediaQuery.of(context).size.height; EasyLoading.instance.loadingStyle = EasyLoadingStyle.light; - return Scaffold( - floatingActionButton: _showBar - ? null - : FloatingActionButton( - mini: true, - child: Icon(Icons.expand_less), - backgroundColor: MyTheme.accent50, - onPressed: () { - setState(() => _showBar = !_showBar); - }), - bottomNavigationBar: _showBar - ? BottomAppBar( - color: MyTheme.accent, - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row(children: [ - IconButton( - color: Colors.white, - icon: Icon(Icons.clear), - onPressed: () {}, - ), - IconButton( - color: Colors.white, - icon: Icon(Icons.keyboard), - onPressed: () {}, - ), - Transform.rotate( - angle: 15 * math.pi / 180, - child: IconButton( + return WillPopScope( + onWillPop: () async { + close(); + return false; + }, + child: Scaffold( + floatingActionButton: _showBar + ? null + : FloatingActionButton( + mini: true, + child: Icon(Icons.expand_less), + backgroundColor: MyTheme.accent50, + onPressed: () { + setState(() => _showBar = !_showBar); + }), + bottomNavigationBar: _showBar + ? BottomAppBar( + color: MyTheme.accent, + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row(children: [ + IconButton( color: Colors.white, - icon: Icon(Icons.flash_on), + icon: Icon(Icons.clear), + onPressed: () { + close(); + }, + ), + IconButton( + color: Colors.white, + icon: Icon(Icons.keyboard), onPressed: () {}, - )), - IconButton( - color: Colors.white, - icon: Icon(Icons.tv), - onPressed: () { - showOptions(widget.id, context); - }, - ), - IconButton( - color: Colors.white, - icon: Icon(Icons.settings), - onPressed: () {}, - ) - ]), - IconButton( - color: Colors.white, - icon: Icon(Icons.expand_more), - onPressed: () { - setState(() => _showBar = !_showBar); - }), - ], - ), - ) - : null, - body: FlutterEasyLoading( - child: Container( - color: MyTheme.canvasColor, - child: InteractiveViewer( - constrained: false, - panEnabled: true, - onInteractionUpdate: (details) { - print('$details'); - }, - child: Stack(children: [ - ImagePaint(), - CursorPaint(), - ]), - ), - ))); + ), + Transform.rotate( + angle: 15 * math.pi / 180, + child: IconButton( + color: Colors.white, + icon: Icon(Icons.flash_on), + onPressed: () { + showActions(context); + }, + )), + IconButton( + color: Colors.white, + icon: Icon(Icons.tv), + onPressed: () { + showOptions(context); + }, + ), + IconButton( + color: Colors.white, + icon: Icon(Icons.settings), + onPressed: () {}, + ) + ]), + IconButton( + color: Colors.white, + icon: Icon(Icons.expand_more), + onPressed: () { + setState(() => _showBar = !_showBar); + }), + ], + ), + ) + : null, + body: FlutterEasyLoading( + child: Container( + color: MyTheme.canvasColor, + child: InteractiveViewer( + constrained: false, + panEnabled: true, + onInteractionUpdate: (details) { + print('$details'); + }, + child: Stack(children: [ + ImagePaint(), + CursorPaint(), + ]), + ), + )))); + } + + void close() { + msgbox('', 'Close', 'Are you sure to close the connection?', context); } } @@ -261,7 +274,7 @@ void wrongPasswordDialog(String id, BuildContext context) { ])); } -void showOptions(String id, BuildContext context) { +void showOptions(BuildContext context) { var showRemoteCursor = FFI.getByName('toggle_option', 'show-remote-cursor') == 'true'; var lockAfterSessionEnd = @@ -332,5 +345,32 @@ void showOptions(String id, BuildContext context) { null), () async => true, true, - 10); + 0); +} + +void showActions(BuildContext context) { + showAlertDialog( + context, + (setState) => Tuple3( + null, + Column(mainAxisSize: MainAxisSize.min, children: [ + ListTile( + onTap: () { + Navigator.pop(context); + FFI.setByName('ctrl_alt_del'); + }, + title: Text('Insert Ctrl + Alt + Del'), + ), + ListTile( + onTap: () { + Navigator.pop(context); + FFI.setByName('lock_screen'); + }, + title: Text('Insert Lock'), + ), + ]), + null), + () async => true, + true, + 0); } From 5d11700bddc4983b1931233c932f0d8904d67d45 Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 20 Nov 2020 17:20:42 +0800 Subject: [PATCH 046/422] invoke keyboard, problem is the keyboard type has problem --- flutter_hbb/lib/remote_page.dart | 15 +++++++++++++-- flutter_hbb/pubspec.lock | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 84166d2fa..e284b591d 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -5,9 +5,9 @@ import 'dart:ui' as ui; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'dart:async'; import 'dart:math' as math; +import 'package:tuple/tuple.dart'; import 'common.dart'; import 'model.dart'; -import 'package:tuple/tuple.dart'; class RemotePage extends StatefulWidget { RemotePage({Key key, this.id}) : super(key: key); @@ -22,6 +22,7 @@ class RemotePage extends StatefulWidget { class _RemotePageState extends State { Timer _interval; bool _showBar = true; + double _bottom = 0; @override void initState() { @@ -46,6 +47,15 @@ class _RemotePageState extends State { } void interval() { + var v = MediaQuery.of(context).viewInsets.bottom; + if (v != _bottom) { + setState(() { + _bottom = v; + if (v < 80) { + SystemChrome.setEnabledSystemUIOverlays([]); + } + }); + } FFI.ffiModel.update(widget.id, context, handleMsgbox); } @@ -100,7 +110,8 @@ class _RemotePageState extends State { IconButton( color: Colors.white, icon: Icon(Icons.keyboard), - onPressed: () {}, + onPressed: () => SystemChannels.textInput + .invokeMethod('TextInput.show'), ), Transform.rotate( angle: 15 * math.pi / 180, diff --git a/flutter_hbb/pubspec.lock b/flutter_hbb/pubspec.lock index e4f1ca350..3b1d42184 100644 --- a/flutter_hbb/pubspec.lock +++ b/flutter_hbb/pubspec.lock @@ -82,7 +82,7 @@ packages: name: flutter_easyloading url: "https://pub.dartlang.org" source: hosted - version: "2.1.3" + version: "2.2.0" flutter_spinkit: dependency: transitive description: From adea49ee8848d117358dfd40fee628b704c4907e Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 20 Nov 2020 17:51:49 +0800 Subject: [PATCH 047/422] prepare keyboard --- flutter_hbb/lib/home_page.dart | 2 +- flutter_hbb/lib/remote_page.dart | 171 ++++++++++++++++--------------- 2 files changed, 92 insertions(+), 81 deletions(-) diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index 40d05fe82..6fe58838b 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -79,7 +79,7 @@ class _HomePageState extends State { fontSize: 30, color: Color(0xFF00B6F0), ), - keyboardType: TextInputType.number, + // keyboardType: TextInputType.number, decoration: InputDecoration( labelText: 'Remote ID', border: InputBorder.none, diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index e284b591d..94fc7faa4 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -23,6 +23,7 @@ class _RemotePageState extends State { Timer _interval; bool _showBar = true; double _bottom = 0; + final FocusNode _focusNode = FocusNode(); @override void initState() { @@ -39,6 +40,7 @@ class _RemotePageState extends State { @override void dispose() { + _focusNode.dispose(); super.dispose(); FFI.close(); _interval.cancel(); @@ -72,94 +74,103 @@ class _RemotePageState extends State { } } + void _handleKeyEvent(RawKeyEvent event) { + print('$event'); + } + @override Widget build(BuildContext context) { // Size size = MediaQueryData.fromWindow(ui.window).size; // MediaQuery.of(context).size.height; EasyLoading.instance.loadingStyle = EasyLoadingStyle.light; - return WillPopScope( - onWillPop: () async { - close(); - return false; - }, - child: Scaffold( - floatingActionButton: _showBar - ? null - : FloatingActionButton( - mini: true, - child: Icon(Icons.expand_less), - backgroundColor: MyTheme.accent50, - onPressed: () { - setState(() => _showBar = !_showBar); - }), - bottomNavigationBar: _showBar - ? BottomAppBar( - color: MyTheme.accent, - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row(children: [ - IconButton( - color: Colors.white, - icon: Icon(Icons.clear), - onPressed: () { - close(); - }, - ), - IconButton( - color: Colors.white, - icon: Icon(Icons.keyboard), - onPressed: () => SystemChannels.textInput - .invokeMethod('TextInput.show'), - ), - Transform.rotate( - angle: 15 * math.pi / 180, - child: IconButton( + return RawKeyboardListener( + focusNode: _focusNode, + onKey: _handleKeyEvent, + child: WillPopScope( + onWillPop: () async { + close(); + return false; + }, + child: Scaffold( + floatingActionButton: _showBar + ? null + : FloatingActionButton( + mini: true, + child: Icon(Icons.expand_less), + backgroundColor: MyTheme.accent50, + onPressed: () { + setState(() => _showBar = !_showBar); + }), + bottomNavigationBar: _showBar + ? BottomAppBar( + color: MyTheme.accent, + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row(children: [ + IconButton( color: Colors.white, - icon: Icon(Icons.flash_on), + icon: Icon(Icons.clear), onPressed: () { - showActions(context); + close(); }, - )), - IconButton( - color: Colors.white, - icon: Icon(Icons.tv), - onPressed: () { - showOptions(context); - }, - ), - IconButton( - color: Colors.white, - icon: Icon(Icons.settings), - onPressed: () {}, - ) - ]), - IconButton( - color: Colors.white, - icon: Icon(Icons.expand_more), - onPressed: () { - setState(() => _showBar = !_showBar); - }), - ], - ), - ) - : null, - body: FlutterEasyLoading( - child: Container( - color: MyTheme.canvasColor, - child: InteractiveViewer( - constrained: false, - panEnabled: true, - onInteractionUpdate: (details) { - print('$details'); - }, - child: Stack(children: [ - ImagePaint(), - CursorPaint(), - ]), - ), - )))); + ), + IconButton( + color: Colors.white, + icon: Icon(Icons.keyboard), + onPressed: () { + SystemChannels.textInput + .invokeMethod('TextInput.show'); + _focusNode.requestFocus(); + }), + Transform.rotate( + angle: 15 * math.pi / 180, + child: IconButton( + color: Colors.white, + icon: Icon(Icons.flash_on), + onPressed: () { + showActions(context); + }, + )), + IconButton( + color: Colors.white, + icon: Icon(Icons.tv), + onPressed: () { + showOptions(context); + }, + ), + IconButton( + color: Colors.white, + icon: Icon(Icons.settings), + onPressed: () {}, + ) + ]), + IconButton( + color: Colors.white, + icon: Icon(Icons.expand_more), + onPressed: () { + setState(() => _showBar = !_showBar); + }), + ], + ), + ) + : null, + body: FlutterEasyLoading( + child: Container( + color: MyTheme.canvasColor, + child: InteractiveViewer( + constrained: false, + panEnabled: true, + onInteractionUpdate: (details) { + print('$details'); + }, + child: Stack(children: [ + ImagePaint(), + CursorPaint(), + ]), + ), + ))))); } void close() { From 6b774b52cd5ab35cd8ba893612739015b637fa19 Mon Sep 17 00:00:00 2001 From: open-trade Date: Sat, 21 Nov 2020 02:18:04 +0800 Subject: [PATCH 048/422] GestureDetector --- flutter_hbb/lib/remote_page.dart | 51 ++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 94fc7faa4..ba465a858 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -23,6 +23,7 @@ class _RemotePageState extends State { Timer _interval; bool _showBar = true; double _bottom = 0; + bool _pan = false; final FocusNode _focusNode = FocusNode(); @override @@ -140,11 +141,15 @@ class _RemotePageState extends State { showOptions(context); }, ), - IconButton( - color: Colors.white, - icon: Icon(Icons.settings), - onPressed: () {}, - ) + Container( + color: _pan ? Colors.blue[500] : null, + child: IconButton( + color: Colors.white, + icon: Icon(Icons.pan_tool), + onPressed: () { + setState(() => _pan = !_pan); + }, + )) ]), IconButton( color: Colors.white, @@ -159,16 +164,32 @@ class _RemotePageState extends State { body: FlutterEasyLoading( child: Container( color: MyTheme.canvasColor, - child: InteractiveViewer( - constrained: false, - panEnabled: true, - onInteractionUpdate: (details) { - print('$details'); + child: GestureDetector( + onTap: () { + if (_pan) return; }, - child: Stack(children: [ - ImagePaint(), - CursorPaint(), - ]), + onDoubleTap: () { + if (_pan) return; + }, + onLongPress: () { + if (_pan) return; + }, + child: InteractiveViewer( + constrained: false, + panEnabled: _pan, + onInteractionUpdate: (details) { + // print('$details'); + }, + onInteractionStart: (s) { + print('$s'); + }, + onInteractionEnd: (x) { + print('$x'); + }, + child: Stack(children: [ + ImagePaint(), + CursorPaint(), + ])), ), ))))); } @@ -344,7 +365,7 @@ void showOptions(BuildContext context) { }); }, ), - Divider(color: MyTheme.border), + Divider(color: Colors.black), CheckboxListTile( value: showRemoteCursor, onChanged: (v) { From 18ea8d9151a4d4a555daf7c92669d902c4ef5c1d Mon Sep 17 00:00:00 2001 From: open-trade Date: Sat, 21 Nov 2020 14:40:28 +0800 Subject: [PATCH 049/422] password widget --- flutter_hbb/lib/common.dart | 40 ++++++++++++++++++++++++++++++++ flutter_hbb/lib/home_page.dart | 1 + flutter_hbb/lib/remote_page.dart | 9 +------ 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 9b424aac4..d10ced032 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -98,3 +98,43 @@ void msgbox(String type, String title, String text, BuildContext context, ), ])); } + +class PasswordWidget extends StatefulWidget { + PasswordWidget({Key key, this.controller}) : super(key: key); + + final TextEditingController controller; + + @override + _PasswordWidgetState createState() => _PasswordWidgetState(); +} + +class _PasswordWidgetState extends State { + bool _passwordVisible = false; + @override + Widget build(BuildContext context) { + return TextFormField( + autofocus: true, + keyboardType: TextInputType.text, + controller: widget.controller, + obscureText: !_passwordVisible, //This will obscure text dynamically + decoration: InputDecoration( + labelText: 'Password', + hintText: 'Enter your password', + // Here is key idea + suffixIcon: IconButton( + icon: Icon( + // Based on passwordVisible state choose the icon + _passwordVisible ? Icons.visibility : Icons.visibility_off, + color: Theme.of(context).primaryColorDark, + ), + onPressed: () { + // Update the state i.e. toogle the state of passwordVisible variable + setState(() { + _passwordVisible = !_passwordVisible; + }); + }, + ), + ), + ); + } +} diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index 6fe58838b..527ed41ee 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -82,6 +82,7 @@ class _HomePageState extends State { // keyboardType: TextInputType.number, decoration: InputDecoration( labelText: 'Remote ID', + hintText: 'Enter your remote ID', border: InputBorder.none, helperStyle: TextStyle( fontWeight: FontWeight.bold, diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index ba465a858..93342e738 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -250,14 +250,7 @@ void enterPasswordDialog(String id, BuildContext context) { (setState) => Tuple3( Text('Please enter your password'), Column(mainAxisSize: MainAxisSize.min, children: [ - TextField( - autofocus: true, - obscureText: true, - controller: controller, - decoration: const InputDecoration( - labelText: 'Password', - ), - ), + PasswordWidget(controller: controller), CheckboxListTile( controlAffinity: ListTileControlAffinity.leading, title: Text( From 49760b4ad5c4ead995a30bc6a120915ef3b64880 Mon Sep 17 00:00:00 2001 From: open-trade Date: Sun, 22 Nov 2020 13:23:56 +0800 Subject: [PATCH 050/422] hide bar if keyboard --- flutter_hbb/lib/remote_page.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 93342e738..ee984a328 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -102,8 +102,9 @@ class _RemotePageState extends State { onPressed: () { setState(() => _showBar = !_showBar); }), - bottomNavigationBar: _showBar + bottomNavigationBar: _showBar && _bottom < 100 ? BottomAppBar( + elevation: 10, color: MyTheme.accent, child: Row( mainAxisSize: MainAxisSize.max, From 25b2f52462f63c0d53e62ea8e989160b4e075cfc Mon Sep 17 00:00:00 2001 From: open-trade Date: Sun, 22 Nov 2020 14:47:43 +0800 Subject: [PATCH 051/422] MultiTapGestureRecognizer draft --- flutter_hbb/lib/remote_page.dart | 99 ++++++++++++++++++++++++-------- 1 file changed, 74 insertions(+), 25 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index ee984a328..ab0b6b458 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/gestures.dart'; import 'package:provider/provider.dart'; import 'package:flutter/services.dart'; import 'dart:ui' as ui; @@ -102,7 +103,7 @@ class _RemotePageState extends State { onPressed: () { setState(() => _showBar = !_showBar); }), - bottomNavigationBar: _showBar && _bottom < 100 + bottomNavigationBar: _showBar ? BottomAppBar( elevation: 10, color: MyTheme.accent, @@ -122,6 +123,8 @@ class _RemotePageState extends State { color: Colors.white, icon: Icon(Icons.keyboard), onPressed: () { + SystemChrome.setEnabledSystemUIOverlays( + SystemUiOverlay.values); SystemChannels.textInput .invokeMethod('TextInput.show'); _focusNode.requestFocus(); @@ -165,33 +168,48 @@ class _RemotePageState extends State { body: FlutterEasyLoading( child: Container( color: MyTheme.canvasColor, - child: GestureDetector( - onTap: () { - if (_pan) return; - }, - onDoubleTap: () { - if (_pan) return; - }, - onLongPress: () { - if (_pan) return; - }, - child: InteractiveViewer( - constrained: false, - panEnabled: _pan, - onInteractionUpdate: (details) { - // print('$details'); + child: RawGestureDetector( + gestures: { + MultiTouchGestureRecognizer: + GestureRecognizerFactoryWithHandlers< + MultiTouchGestureRecognizer>( + () => MultiTouchGestureRecognizer(), + (MultiTouchGestureRecognizer instance) { + instance.onMultiTap = ( + touchCount, + addOrRemove, + ) => + print('$touchCount, $addOrRemove'); + }, + ), + }, + child: GestureDetector( + onTap: () { + if (_pan) return; }, - onInteractionStart: (s) { - print('$s'); + onDoubleTap: () { + if (_pan) return; }, - onInteractionEnd: (x) { - print('$x'); + onLongPress: () { + if (_pan) return; }, - child: Stack(children: [ - ImagePaint(), - CursorPaint(), - ])), - ), + child: InteractiveViewer( + constrained: false, + panEnabled: _pan, + onInteractionUpdate: (details) { + // print('$details'); + }, + onInteractionStart: (s) { + print('$s'); + }, + onInteractionEnd: (x) { + print('$x'); + }, + child: Stack(children: [ + ImagePaint(), + CursorPaint(), + ])), + )), ))))); } @@ -411,3 +429,34 @@ void showActions(BuildContext context) { true, 0); } + +class MultiTouchGestureRecognizer extends MultiTapGestureRecognizer { + MultiTouchGestureRecognizerCallback onMultiTap; + var numberOfTouches = 0; + + MultiTouchGestureRecognizer() { + super.onTapDown = (pointer, details) => addTouch(pointer, details); + super.onTapUp = (pointer, details) => removeTouch(pointer, details); + super.onTapCancel = (pointer) => cancelTouch(pointer); + super.onTap = (pointer) => captureDefaultTap(pointer); + } + + void addTouch(int pointer, TapDownDetails details) { + numberOfTouches++; + onMultiTap(numberOfTouches, true); + } + + void removeTouch(int pointer, TapUpDetails details) { + numberOfTouches--; + onMultiTap(numberOfTouches, false); + } + + void cancelTouch(int pointer) { + numberOfTouches = 0; + } + + void captureDefaultTap(int pointer) {} +} + +typedef MultiTouchGestureRecognizerCallback = void Function( + int touchCount, bool addOrRemove); From f6f7e1ead153df38e60ac94e94521d7c75307c1c Mon Sep 17 00:00:00 2001 From: open-trade Date: Sun, 22 Nov 2020 18:29:04 +0800 Subject: [PATCH 052/422] use hidden textfield to capture input --- flutter_hbb/lib/model.dart | 6 +- flutter_hbb/lib/remote_page.dart | 244 ++++++++++++++++--------------- 2 files changed, 132 insertions(+), 118 deletions(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 54a66b716..46dd6d0dc 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -138,8 +138,8 @@ class ImageModel with ChangeNotifier { class CursorModel with ChangeNotifier { ui.Image _image; final _images = Map(); - double _x = 0; - double _y = 0; + double _x = -10000; + double _y = -10000; double _hotx = 0; double _hoty = 0; double _displayOriginX = 0; @@ -189,6 +189,8 @@ class CursorModel with ChangeNotifier { } void clear() { + _x = -10000; + _x = -10000; _image = null; _images.clear(); } diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index ab0b6b458..558cdd5e1 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -85,132 +85,144 @@ class _RemotePageState extends State { // Size size = MediaQueryData.fromWindow(ui.window).size; // MediaQuery.of(context).size.height; EasyLoading.instance.loadingStyle = EasyLoadingStyle.light; - return RawKeyboardListener( - focusNode: _focusNode, - onKey: _handleKeyEvent, - child: WillPopScope( - onWillPop: () async { - close(); - return false; - }, - child: Scaffold( - floatingActionButton: _showBar - ? null - : FloatingActionButton( - mini: true, - child: Icon(Icons.expand_less), - backgroundColor: MyTheme.accent50, - onPressed: () { - setState(() => _showBar = !_showBar); - }), - bottomNavigationBar: _showBar - ? BottomAppBar( - elevation: 10, - color: MyTheme.accent, - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row(children: [ - IconButton( + return WillPopScope( + onWillPop: () async { + close(); + return false; + }, + child: Scaffold( + floatingActionButton: _showBar + ? null + : FloatingActionButton( + mini: true, + child: Icon(Icons.expand_less), + backgroundColor: MyTheme.accent50, + onPressed: () { + setState(() => _showBar = !_showBar); + }), + bottomNavigationBar: _showBar + ? BottomAppBar( + elevation: 10, + color: MyTheme.accent, + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row(children: [ + IconButton( + color: Colors.white, + icon: Icon(Icons.clear), + onPressed: () { + close(); + }, + ), + IconButton( + color: Colors.white, + icon: Icon(Icons.keyboard), + onPressed: () { + SystemChrome.setEnabledSystemUIOverlays( + SystemUiOverlay.values); + _focusNode.requestFocus(); + SystemChannels.textInput + .invokeMethod('TextInput.show'); + }), + Transform.rotate( + angle: 15 * math.pi / 180, + child: IconButton( color: Colors.white, - icon: Icon(Icons.clear), + icon: Icon(Icons.flash_on), onPressed: () { - close(); + showActions(context); }, - ), - IconButton( - color: Colors.white, - icon: Icon(Icons.keyboard), - onPressed: () { - SystemChrome.setEnabledSystemUIOverlays( - SystemUiOverlay.values); - SystemChannels.textInput - .invokeMethod('TextInput.show'); - _focusNode.requestFocus(); - }), - Transform.rotate( - angle: 15 * math.pi / 180, - child: IconButton( - color: Colors.white, - icon: Icon(Icons.flash_on), - onPressed: () { - showActions(context); - }, - )), - IconButton( + )), + IconButton( + color: Colors.white, + icon: Icon(Icons.tv), + onPressed: () { + showOptions(context); + }, + ), + Container( + color: _pan ? Colors.blue[500] : null, + child: IconButton( color: Colors.white, - icon: Icon(Icons.tv), + icon: Icon(Icons.pan_tool), onPressed: () { - showOptions(context); + setState(() => _pan = !_pan); }, - ), - Container( - color: _pan ? Colors.blue[500] : null, - child: IconButton( - color: Colors.white, - icon: Icon(Icons.pan_tool), - onPressed: () { - setState(() => _pan = !_pan); - }, - )) - ]), - IconButton( - color: Colors.white, - icon: Icon(Icons.expand_more), - onPressed: () { - setState(() => _showBar = !_showBar); - }), - ], - ), - ) - : null, - body: FlutterEasyLoading( - child: Container( - color: MyTheme.canvasColor, - child: RawGestureDetector( - gestures: { - MultiTouchGestureRecognizer: - GestureRecognizerFactoryWithHandlers< - MultiTouchGestureRecognizer>( - () => MultiTouchGestureRecognizer(), - (MultiTouchGestureRecognizer instance) { - instance.onMultiTap = ( - touchCount, - addOrRemove, - ) => - print('$touchCount, $addOrRemove'); - }, - ), + )) + ]), + IconButton( + color: Colors.white, + icon: Icon(Icons.expand_more), + onPressed: () { + setState(() => _showBar = !_showBar); + }), + ], + ), + ) + : null, + body: FlutterEasyLoading( + child: Container( + color: MyTheme.canvasColor, + child: RawGestureDetector( + gestures: { + MultiTouchGestureRecognizer: + GestureRecognizerFactoryWithHandlers< + MultiTouchGestureRecognizer>( + () => MultiTouchGestureRecognizer(), + (MultiTouchGestureRecognizer instance) { + instance.onMultiTap = ( + touchCount, + addOrRemove, + ) => + print('$touchCount, $addOrRemove'); }, - child: GestureDetector( - onTap: () { - if (_pan) return; + ), + }, + child: GestureDetector( + onTap: () { + if (_pan) return; + }, + onDoubleTap: () { + if (_pan) return; + }, + onLongPress: () { + if (_pan) return; + }, + child: InteractiveViewer( + constrained: false, + panEnabled: _pan, + onInteractionUpdate: (details) { + // print('$details'); }, - onDoubleTap: () { - if (_pan) return; + onInteractionStart: (s) { + print('$s'); }, - onLongPress: () { - if (_pan) return; + onInteractionEnd: (x) { + print('$x'); }, - child: InteractiveViewer( - constrained: false, - panEnabled: _pan, - onInteractionUpdate: (details) { - // print('$details'); - }, - onInteractionStart: (s) { - print('$s'); - }, - onInteractionEnd: (x) { - print('$x'); - }, - child: Stack(children: [ - ImagePaint(), - CursorPaint(), - ])), - )), - ))))); + child: Stack(children: [ + ImagePaint(), + CursorPaint(), + SizedBox( + width: 0, + height: 0, + child: _bottom < 100 + ? Container() + : TextFormField( + textInputAction: TextInputAction.newline, + autocorrect: false, + enableSuggestions: false, + focusNode: _focusNode, + maxLines: null, + keyboardType: TextInputType.multiline, + onChanged: (x) => print('$x'), + ), + ), + ])), + )), + )))); } void close() { From 364fb780845677ba08fd3a61755ae4006d03c9af Mon Sep 17 00:00:00 2001 From: open-trade Date: Sun, 22 Nov 2020 21:08:19 +0800 Subject: [PATCH 053/422] permissions --- flutter_hbb/lib/model.dart | 33 ++++++++++++++++++++++++-------- flutter_hbb/lib/remote_page.dart | 6 +----- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 46dd6d0dc..5db8636d0 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -1,3 +1,5 @@ +import 'dart:html'; + import 'package:ffi/ffi.dart'; import 'package:path_provider/path_provider.dart'; import 'dart:io'; @@ -23,22 +25,35 @@ typedef F5 = Pointer Function(); // https://juejin.im/post/6844903864852807694 class FfiModel with ChangeNotifier { - PeerInfo _pi = PeerInfo(); - Display _display = Display(); - bool _decoding = false; - bool _waitForImage = false; + PeerInfo _pi; + Display _display; + bool _decoding; + bool _waitForImage; + final _permissions = Map(); + + get permissions => _permissions; FfiModel() { - init(); + clear(); + () async { + await FFI.init(); + notifyListeners(); + }(); } - Future init() async { - await FFI.init(); - notifyListeners(); + void updatePermission(Map evt) { + evt.forEach((k, v) { + if (k == 'name') return; + _permissions[k] = v == 'true'; + }); } void clear() { + _pi = PeerInfo(); + _display = Display(); _decoding = false; + _waitForImage = false; + _permissions.clear(); } void update( @@ -62,6 +77,8 @@ class FfiModel with ChangeNotifier { FFI.cursorModel.updateCursorId(evt); } else if (name == 'cursor_position') { FFI.cursorModel.updateCursorPosition(evt); + } else if (name == 'permission') { + FFI.ffiModel.updatePermission(evt); } } if (!_decoding) { diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 558cdd5e1..c5bb1c2ff 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -76,10 +76,6 @@ class _RemotePageState extends State { } } - void _handleKeyEvent(RawKeyEvent event) { - print('$event'); - } - @override Widget build(BuildContext context) { // Size size = MediaQueryData.fromWindow(ui.window).size; @@ -210,7 +206,7 @@ class _RemotePageState extends State { height: 0, child: _bottom < 100 ? Container() - : TextFormField( + : TextField( textInputAction: TextInputAction.newline, autocorrect: false, enableSuggestions: false, From 06cb5b6c356e843b9a4e4757e725c935ece953ba Mon Sep 17 00:00:00 2001 From: open-trade Date: Sun, 22 Nov 2020 21:12:22 +0800 Subject: [PATCH 054/422] remove dart.html --- flutter_hbb/lib/model.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 5db8636d0..cc7f4dfdc 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -1,5 +1,3 @@ -import 'dart:html'; - import 'package:ffi/ffi.dart'; import 'package:path_provider/path_provider.dart'; import 'dart:io'; From 8e529399c37a272cc2554bae2df40cc879b277d4 Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 23 Nov 2020 01:16:17 +0800 Subject: [PATCH 055/422] InteractiveView buggy --- flutter_hbb/lib/common.dart | 14 +- flutter_hbb/lib/home_page.dart | 12 +- flutter_hbb/lib/model.dart | 2 +- flutter_hbb/lib/remote_page.dart | 329 ++++++++++++++++++------------- 4 files changed, 204 insertions(+), 153 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index d10ced032..acdf11e8b 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -3,18 +3,6 @@ import 'dart:async'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:tuple/tuple.dart'; -class HexColor extends Color { - HexColor(final String hexColor) : super(_getColorFromHex(hexColor)); - - static int _getColorFromHex(String hexColor) { - hexColor = hexColor.toUpperCase().replaceAll('#', ''); - if (hexColor.length == 6) { - hexColor = 'FF' + hexColor; - } - return int.parse(hexColor, radix: 16); - } -} - class MyTheme { MyTheme._(); static const Color grayBg = Color(0xFFEEEEEE); @@ -112,7 +100,7 @@ class _PasswordWidgetState extends State { bool _passwordVisible = false; @override Widget build(BuildContext context) { - return TextFormField( + return TextField( autofocus: true, keyboardType: TextInputType.text, controller: widget.controller, diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index 527ed41ee..1a7de8666 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -72,7 +72,9 @@ class _HomePageState extends State { Expanded( child: Container( padding: const EdgeInsets.only(left: 16, right: 16), - child: TextFormField( + child: TextField( + autocorrect: false, + enableSuggestions: false, style: TextStyle( fontFamily: 'WorkSans', fontWeight: FontWeight.bold, @@ -82,18 +84,18 @@ class _HomePageState extends State { // keyboardType: TextInputType.number, decoration: InputDecoration( labelText: 'Remote ID', - hintText: 'Enter your remote ID', + // hintText: 'Enter your remote ID', border: InputBorder.none, helperStyle: TextStyle( fontWeight: FontWeight.bold, fontSize: 16, - color: HexColor('#B9BABC'), + color: Color(0xFFB9BABC), ), labelStyle: TextStyle( fontWeight: FontWeight.w600, fontSize: 16, letterSpacing: 0.2, - color: HexColor('#B9BABC'), + color: Color(0xFFB9BABC), ), ), autofocus: false, @@ -106,7 +108,7 @@ class _HomePageState extends State { height: 60, child: IconButton( icon: Icon(Icons.arrow_forward, - color: HexColor('#B9BABC'), size: 45), + color: Color(0xFFB9BABC), size: 45), onPressed: onConnect, ), ) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index cc7f4dfdc..56e541748 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -132,7 +132,7 @@ class FfiModel with ChangeNotifier { _display = _pi.displays[_pi.currentDisplay]; FFI.cursorModel.updateDisplayOrigin(_display.x, _display.y); } - if (displays.length > 1) { + if (displays.length > 0) { showLoading('Waiting for image...'); _waitForImage = true; } diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index c5bb1c2ff..4f9e22919 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -87,138 +87,182 @@ class _RemotePageState extends State { return false; }, child: Scaffold( - floatingActionButton: _showBar - ? null - : FloatingActionButton( - mini: true, - child: Icon(Icons.expand_less), - backgroundColor: MyTheme.accent50, - onPressed: () { - setState(() => _showBar = !_showBar); - }), - bottomNavigationBar: _showBar - ? BottomAppBar( - elevation: 10, - color: MyTheme.accent, - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row(children: [ - IconButton( - color: Colors.white, - icon: Icon(Icons.clear), - onPressed: () { - close(); - }, - ), - IconButton( - color: Colors.white, - icon: Icon(Icons.keyboard), - onPressed: () { - SystemChrome.setEnabledSystemUIOverlays( - SystemUiOverlay.values); - _focusNode.requestFocus(); - SystemChannels.textInput - .invokeMethod('TextInput.show'); - }), - Transform.rotate( - angle: 15 * math.pi / 180, - child: IconButton( - color: Colors.white, - icon: Icon(Icons.flash_on), - onPressed: () { - showActions(context); - }, - )), - IconButton( - color: Colors.white, - icon: Icon(Icons.tv), - onPressed: () { - showOptions(context); - }, - ), - Container( - color: _pan ? Colors.blue[500] : null, - child: IconButton( - color: Colors.white, - icon: Icon(Icons.pan_tool), - onPressed: () { - setState(() => _pan = !_pan); - }, - )) - ]), + floatingActionButton: _showBar + ? null + : FloatingActionButton( + mini: true, + child: Icon(Icons.expand_less), + backgroundColor: MyTheme.accent50, + onPressed: () { + setState(() => _showBar = !_showBar); + }), + bottomNavigationBar: _showBar + ? BottomAppBar( + elevation: 10, + color: MyTheme.accent, + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row(children: [ + IconButton( + color: Colors.white, + icon: Icon(Icons.clear), + onPressed: () { + close(); + }, + ), IconButton( color: Colors.white, - icon: Icon(Icons.expand_more), + icon: Icon(Icons.keyboard), onPressed: () { - setState(() => _showBar = !_showBar); + SystemChrome.setEnabledSystemUIOverlays( + SystemUiOverlay.values); + _focusNode.requestFocus(); + SystemChannels.textInput + .invokeMethod('TextInput.show'); }), - ], - ), - ) - : null, - body: FlutterEasyLoading( - child: Container( - color: MyTheme.canvasColor, - child: RawGestureDetector( - gestures: { - MultiTouchGestureRecognizer: - GestureRecognizerFactoryWithHandlers< - MultiTouchGestureRecognizer>( - () => MultiTouchGestureRecognizer(), - (MultiTouchGestureRecognizer instance) { - instance.onMultiTap = ( - touchCount, - addOrRemove, - ) => - print('$touchCount, $addOrRemove'); - }, - ), + Transform.rotate( + angle: 15 * math.pi / 180, + child: IconButton( + color: Colors.white, + icon: Icon(Icons.flash_on), + onPressed: () { + showActions(context); + }, + )), + IconButton( + color: Colors.white, + icon: Icon(Icons.tv), + onPressed: () { + showOptions(context); + }, + ), + Container( + color: _pan ? Colors.blue[500] : null, + child: IconButton( + color: Colors.white, + icon: Icon(Icons.pan_tool), + onPressed: () { + setState(() => _pan = !_pan); + }, + )) + ]), + IconButton( + color: Colors.white, + icon: Icon(Icons.expand_more), + onPressed: () { + setState(() => _showBar = !_showBar); + }), + ], + ), + ) + : null, + body: RawGestureDetector( + gestures: { + MultiTouchGestureRecognizer: + GestureRecognizerFactoryWithHandlers< + MultiTouchGestureRecognizer>( + () => MultiTouchGestureRecognizer(), + (MultiTouchGestureRecognizer instance) { + instance.onMultiTap = ( + touchCount, + addOrRemove, + ) => + print('$touchCount, $addOrRemove'); }, - child: GestureDetector( - onTap: () { - if (_pan) return; - }, - onDoubleTap: () { - if (_pan) return; - }, - onLongPress: () { - if (_pan) return; - }, - child: InteractiveViewer( - constrained: false, - panEnabled: _pan, - onInteractionUpdate: (details) { - // print('$details'); - }, - onInteractionStart: (s) { - print('$s'); - }, - onInteractionEnd: (x) { - print('$x'); - }, - child: Stack(children: [ - ImagePaint(), - CursorPaint(), - SizedBox( - width: 0, - height: 0, - child: _bottom < 100 - ? Container() - : TextField( - textInputAction: TextInputAction.newline, - autocorrect: false, - enableSuggestions: false, - focusNode: _focusNode, - maxLines: null, - keyboardType: TextInputType.multiline, - onChanged: (x) => print('$x'), - ), - ), - ])), - )), - )))); + ), + TapGestureRecognizer: + GestureRecognizerFactoryWithHandlers( + () => TapGestureRecognizer(), + (TapGestureRecognizer instance) { + instance.onTap = () { + print('tap'); + }; + }, + ), + ImmediateMultiDragGestureRecognizer: + GestureRecognizerFactoryWithHandlers< + ImmediateMultiDragGestureRecognizer>( + () => ImmediateMultiDragGestureRecognizer(), + (ImmediateMultiDragGestureRecognizer instance) { + instance + ..onStart = (x) { + return CustomDrag(); + }; + }, + ), + LongPressGestureRecognizer: + GestureRecognizerFactoryWithHandlers< + LongPressGestureRecognizer>( + () => LongPressGestureRecognizer(), + (LongPressGestureRecognizer instance) { + instance.onLongPress = () { + print('long press'); + }; + }, + ), + PanGestureRecognizer: + GestureRecognizerFactoryWithHandlers( + () => PanGestureRecognizer(), + (PanGestureRecognizer instance) { + instance + ..onStart = (detail) { + print('pan start'); + } + ..onUpdate = (detail) { + print('$detail'); + }; + }, + ), + ScaleGestureRecognizer: GestureRecognizerFactoryWithHandlers< + ScaleGestureRecognizer>( + () => ScaleGestureRecognizer(), + (ScaleGestureRecognizer instance) { + instance + ..onStart = (detail) { + print('scale start'); + } + ..onUpdate = (detail) { + print('$detail'); + }; + }, + ), + DoubleTapGestureRecognizer: + GestureRecognizerFactoryWithHandlers< + DoubleTapGestureRecognizer>( + () => DoubleTapGestureRecognizer(), + (DoubleTapGestureRecognizer instance) { + instance.onDoubleTap = () { + print('double tap'); + }; + }, + ), + }, + child: FlutterEasyLoading( + child: Container( + color: MyTheme.canvasColor, + child: Stack(children: [ + ImagePaint(), + CursorPaint(), + SizedBox( + width: 0, + height: 0, + child: _bottom < 100 + ? Container() + : TextField( + textInputAction: TextInputAction.newline, + autocorrect: false, + enableSuggestions: false, + focusNode: _focusNode, + maxLines: null, + keyboardType: TextInputType.multiline, + onChanged: (x) => print('$x'), + ), + ), + ])), + )), + )); } void close() { @@ -329,7 +373,6 @@ void wrongPasswordDialog(String id, BuildContext context) { FlatButton( textColor: MyTheme.accent, onPressed: () { - Navigator.pop(context); enterPasswordDialog(id, context); }, child: Text('Retry'), @@ -443,10 +486,11 @@ class MultiTouchGestureRecognizer extends MultiTapGestureRecognizer { var numberOfTouches = 0; MultiTouchGestureRecognizer() { - super.onTapDown = (pointer, details) => addTouch(pointer, details); - super.onTapUp = (pointer, details) => removeTouch(pointer, details); - super.onTapCancel = (pointer) => cancelTouch(pointer); - super.onTap = (pointer) => captureDefaultTap(pointer); + this + ..onTapDown = addTouch + ..onTapUp = removeTouch + ..onTapCancel = cancelTouch + ..onTap = captureDefaultTap; } void addTouch(int pointer, TapDownDetails details) { @@ -460,11 +504,28 @@ class MultiTouchGestureRecognizer extends MultiTapGestureRecognizer { } void cancelTouch(int pointer) { - numberOfTouches = 0; + numberOfTouches--; + print('$numberOfTouches x'); } - void captureDefaultTap(int pointer) {} + void captureDefaultTap(int pointer) { + print('$pointer'); + } } typedef MultiTouchGestureRecognizerCallback = void Function( int touchCount, bool addOrRemove); + +typedef OnUpdate(DragUpdateDetails details); + +class CustomDrag extends Drag { + @override + void update(DragUpdateDetails details) { + print('xx $details'); + } + + @override + void end(DragEndDetails details) { + super.end(details); + } +} From 8090726649ca5db0b06b32424acdc24d3143a5a3 Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 23 Nov 2020 12:00:56 +0800 Subject: [PATCH 056/422] lock and long press pop menu --- flutter_hbb/lib/remote_page.dart | 29 +++++++++++++++-- flutter_hbb/pubspec.lock | 56 +++++++++++++++++++++++++++++++- flutter_hbb/pubspec.yaml | 1 + 3 files changed, 82 insertions(+), 4 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 4f9e22919..b8cfbc699 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -9,6 +9,7 @@ import 'dart:math' as math; import 'package:tuple/tuple.dart'; import 'common.dart'; import 'model.dart'; +import 'package:wakelock/wakelock.dart'; class RemotePage extends StatefulWidget { RemotePage({Key key, this.id}) : super(key: key); @@ -38,6 +39,7 @@ class _RemotePageState extends State { _interval = Timer.periodic(Duration(milliseconds: 30), (timer) => interval()); }); + Wakelock.enable(); } @override @@ -48,6 +50,7 @@ class _RemotePageState extends State { _interval.cancel(); dismissLoading(); SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values); + Wakelock.disable(); } void interval() { @@ -197,9 +200,29 @@ class _RemotePageState extends State { LongPressGestureRecognizer>( () => LongPressGestureRecognizer(), (LongPressGestureRecognizer instance) { - instance.onLongPress = () { - print('long press'); - }; + var x = 0.0; + var y = 0.0; + instance + ..onLongPressStart = (details) { + x = details.globalPosition.dx; + y = details.globalPosition.dy; + } + ..onLongPress = () { + print('long press'); + () async { + await showMenu( + context: context, + position: RelativeRect.fromLTRB(x, y, 0, 0), + items: [ + PopupMenuItem( + child: const Text('Doge'), value: 'Doge'), + PopupMenuItem( + child: const Text('Lion'), value: 'Lion'), + ], + elevation: 8.0, + ); + }(); + }; }, ), PanGestureRecognizer: diff --git a/flutter_hbb/pubspec.lock b/flutter_hbb/pubspec.lock index 3b1d42184..4a14ca263 100644 --- a/flutter_hbb/pubspec.lock +++ b/flutter_hbb/pubspec.lock @@ -43,6 +43,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.15.0-nullsafety.3" + csslib: + dependency: transitive + description: + name: csslib + url: "https://pub.dartlang.org" + source: hosted + version: "0.16.2" cupertino_icons: dependency: "direct main" description: @@ -95,6 +102,25 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + html: + dependency: transitive + description: + name: html + url: "https://pub.dartlang.org" + source: hosted + version: "0.14.0+4" + import_js_library: + dependency: transitive + description: + name: import_js_library + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" intl: dependency: transitive description: @@ -102,6 +128,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.16.1" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.2" matcher: dependency: transitive description: @@ -268,6 +301,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0-nullsafety.3" + wakelock: + dependency: "direct main" + description: + name: wakelock + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.1+1" + wakelock_platform_interface: + dependency: transitive + description: + name: wakelock_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0+1" + wakelock_web: + dependency: transitive + description: + name: wakelock_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0+3" win32: dependency: transitive description: @@ -284,4 +338,4 @@ packages: version: "0.1.2" sdks: dart: ">=2.10.0-110 <2.11.0" - flutter: ">=1.16.0 <2.0.0" + flutter: ">=1.20.0 <2.0.0" diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index 9aa35bd58..038f1e574 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -33,6 +33,7 @@ dependencies: provider: ^4.3.2+2 flutter_easyloading: ^2.1.3 tuple: ^1.0.1 + wakelock: ^0.2.1+1 dev_dependencies: flutter_test: From d73d3166d32f7fc5cf7db86fe27fec77f17c5634 Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 23 Nov 2020 13:03:51 +0800 Subject: [PATCH 057/422] switch scale mode if 3 fingers tap --- flutter_hbb/lib/drag.dart | 104 +++++++++++++ flutter_hbb/lib/remote_page.dart | 251 ++++++++++++------------------- 2 files changed, 204 insertions(+), 151 deletions(-) create mode 100644 flutter_hbb/lib/drag.dart diff --git a/flutter_hbb/lib/drag.dart b/flutter_hbb/lib/drag.dart new file mode 100644 index 000000000..551daa880 --- /dev/null +++ b/flutter_hbb/lib/drag.dart @@ -0,0 +1,104 @@ +import 'package:flutter/gestures.dart'; + +class MultiTouchGestureRecognizer extends MultiTapGestureRecognizer { + MultiTouchGestureRecognizerCallback onMultiTap; + var numberOfTouches = 0; + + MultiTouchGestureRecognizer() { + this + ..onTapDown = addTouch + ..onTapUp = removeTouch + ..onTapCancel = cancelTouch + ..onTap = captureDefaultTap; + } + + void addTouch(int pointer, TapDownDetails details) { + numberOfTouches++; + onMultiTap(numberOfTouches, true); + } + + void removeTouch(int pointer, TapUpDetails details) { + numberOfTouches--; + onMultiTap(numberOfTouches, false); + } + + void cancelTouch(int pointer) { + numberOfTouches = 0; + } + + void captureDefaultTap(int pointer) {} +} + +typedef MultiTouchGestureRecognizerCallback = void Function( + int touchCount, bool addOrRemove); + +typedef OnUpdate(DragUpdateDetails details); + +class CustomMultiDrag extends Drag { + CustomMultiDrag({this.events, this.offset}); + + List events; + Offset offset; + + @override + void update(DragUpdateDetails details) { + var n = events.length; + print('$n $details'); + } + + @override + void end(DragEndDetails details) { + super.end(details); + } +} + +typedef OnDisposeState(); + +// clone _ImmediatePointerState +class CustomPointerState extends MultiDragPointerState { + final OnDisposeState onDisposeState; + CustomPointerState(Offset initialPosition, PointerDeviceKind kind, + {this.onDisposeState}) + : super(initialPosition, kind); + + @override + void checkForResolutionAfterMove() { + assert(pendingDelta != null); + if (pendingDelta.distance > computeHitSlop(kind)) + resolve(GestureDisposition.accepted); + } + + @override + void accepted(GestureMultiDragStartCallback starter) { + starter(initialPosition); + } + + @override + void dispose() { + onDisposeState.call(); + super.dispose(); + } +} + +// clone ImmediateMultiDragGestureRecognizer +class CustomMultiDragGestureRecognizer + extends MultiDragGestureRecognizer { + var events = List(); + + /// Create a gesture recognizer for tracking multiple pointers at once. + CustomMultiDragGestureRecognizer({ + Object debugOwner, + PointerDeviceKind kind, + }) : super(debugOwner: debugOwner, kind: kind); + + @override + CustomPointerState createNewPointerState(PointerDownEvent event) { + events.add(event); + return CustomPointerState(event.position, event.kind, onDisposeState: () { + events.remove(event); + }); + } + + @override + String get debugDescription => 'custom_multidrag'; +} diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index b8cfbc699..c0a84b2b6 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -7,9 +7,10 @@ import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'dart:async'; import 'dart:math' as math; import 'package:tuple/tuple.dart'; -import 'common.dart'; -import 'model.dart'; import 'package:wakelock/wakelock.dart'; +import 'common.dart'; +import 'drag.dart'; +import 'model.dart'; class RemotePage extends StatefulWidget { RemotePage({Key key, this.id}) : super(key: key); @@ -26,6 +27,7 @@ class _RemotePageState extends State { bool _showBar = true; double _bottom = 0; bool _pan = false; + var _scaleMode = false; final FocusNode _focusNode = FocusNode(); @override @@ -162,106 +164,7 @@ class _RemotePageState extends State { ) : null, body: RawGestureDetector( - gestures: { - MultiTouchGestureRecognizer: - GestureRecognizerFactoryWithHandlers< - MultiTouchGestureRecognizer>( - () => MultiTouchGestureRecognizer(), - (MultiTouchGestureRecognizer instance) { - instance.onMultiTap = ( - touchCount, - addOrRemove, - ) => - print('$touchCount, $addOrRemove'); - }, - ), - TapGestureRecognizer: - GestureRecognizerFactoryWithHandlers( - () => TapGestureRecognizer(), - (TapGestureRecognizer instance) { - instance.onTap = () { - print('tap'); - }; - }, - ), - ImmediateMultiDragGestureRecognizer: - GestureRecognizerFactoryWithHandlers< - ImmediateMultiDragGestureRecognizer>( - () => ImmediateMultiDragGestureRecognizer(), - (ImmediateMultiDragGestureRecognizer instance) { - instance - ..onStart = (x) { - return CustomDrag(); - }; - }, - ), - LongPressGestureRecognizer: - GestureRecognizerFactoryWithHandlers< - LongPressGestureRecognizer>( - () => LongPressGestureRecognizer(), - (LongPressGestureRecognizer instance) { - var x = 0.0; - var y = 0.0; - instance - ..onLongPressStart = (details) { - x = details.globalPosition.dx; - y = details.globalPosition.dy; - } - ..onLongPress = () { - print('long press'); - () async { - await showMenu( - context: context, - position: RelativeRect.fromLTRB(x, y, 0, 0), - items: [ - PopupMenuItem( - child: const Text('Doge'), value: 'Doge'), - PopupMenuItem( - child: const Text('Lion'), value: 'Lion'), - ], - elevation: 8.0, - ); - }(); - }; - }, - ), - PanGestureRecognizer: - GestureRecognizerFactoryWithHandlers( - () => PanGestureRecognizer(), - (PanGestureRecognizer instance) { - instance - ..onStart = (detail) { - print('pan start'); - } - ..onUpdate = (detail) { - print('$detail'); - }; - }, - ), - ScaleGestureRecognizer: GestureRecognizerFactoryWithHandlers< - ScaleGestureRecognizer>( - () => ScaleGestureRecognizer(), - (ScaleGestureRecognizer instance) { - instance - ..onStart = (detail) { - print('scale start'); - } - ..onUpdate = (detail) { - print('$detail'); - }; - }, - ), - DoubleTapGestureRecognizer: - GestureRecognizerFactoryWithHandlers< - DoubleTapGestureRecognizer>( - () => DoubleTapGestureRecognizer(), - (DoubleTapGestureRecognizer instance) { - instance.onDoubleTap = () { - print('double tap'); - }; - }, - ), - }, + gestures: buildGuestures(), child: FlutterEasyLoading( child: Container( color: MyTheme.canvasColor, @@ -288,6 +191,101 @@ class _RemotePageState extends State { )); } + Map buildGuestures() { + var m = { + TapGestureRecognizer: + GestureRecognizerFactoryWithHandlers( + () => TapGestureRecognizer(), + (TapGestureRecognizer instance) { + instance.onTap = () { + print('tap'); + }; + }, + ), + MultiTouchGestureRecognizer: + GestureRecognizerFactoryWithHandlers( + () => MultiTouchGestureRecognizer(), + (MultiTouchGestureRecognizer instance) { + instance.onMultiTap = ( + touchCount, + addOrRemove, + ) { + if (touchCount == 3 && addOrRemove) { + setState(() => _scaleMode = !_scaleMode); + } + }; + }, + ), + CustomMultiDragGestureRecognizer: GestureRecognizerFactoryWithHandlers< + CustomMultiDragGestureRecognizer>( + () => CustomMultiDragGestureRecognizer(), + (CustomMultiDragGestureRecognizer instance) { + instance + ..onStart = (offset) { + return CustomMultiDrag(events: instance.events, offset: offset); + }; + }, + ), + LongPressGestureRecognizer: + GestureRecognizerFactoryWithHandlers( + () => LongPressGestureRecognizer(), + (LongPressGestureRecognizer instance) { + var x = 0.0; + var y = 0.0; + instance + ..onLongPressStart = (details) { + x = details.globalPosition.dx; + y = details.globalPosition.dy; + } + ..onLongPress = () { + print('long press'); + () async { + await showMenu( + context: context, + position: RelativeRect.fromLTRB(x, y, 0, 0), + items: [ + PopupMenuItem( + child: const Text('Doge'), value: 'Doge'), + PopupMenuItem( + child: const Text('Lion'), value: 'Lion'), + ], + elevation: 8.0, + ); + }(); + }; + }, + ), + ScaleGestureRecognizer: + GestureRecognizerFactoryWithHandlers( + () => ScaleGestureRecognizer(), + (ScaleGestureRecognizer instance) { + instance + ..onStart = (detail) { + print('scale start'); + } + ..onUpdate = (detail) { + print('$detail'); + }; + }, + ), + DoubleTapGestureRecognizer: + GestureRecognizerFactoryWithHandlers( + () => DoubleTapGestureRecognizer(), + (DoubleTapGestureRecognizer instance) { + instance.onDoubleTap = () { + print('double tap'); + }; + }, + ), + }; + if (_scaleMode) { + m.remove(CustomMultiDragGestureRecognizer); + } else { + m.remove(ScaleGestureRecognizer); + } + return m; + } + void close() { msgbox('', 'Close', 'Are you sure to close the connection?', context); } @@ -503,52 +501,3 @@ void showActions(BuildContext context) { true, 0); } - -class MultiTouchGestureRecognizer extends MultiTapGestureRecognizer { - MultiTouchGestureRecognizerCallback onMultiTap; - var numberOfTouches = 0; - - MultiTouchGestureRecognizer() { - this - ..onTapDown = addTouch - ..onTapUp = removeTouch - ..onTapCancel = cancelTouch - ..onTap = captureDefaultTap; - } - - void addTouch(int pointer, TapDownDetails details) { - numberOfTouches++; - onMultiTap(numberOfTouches, true); - } - - void removeTouch(int pointer, TapUpDetails details) { - numberOfTouches--; - onMultiTap(numberOfTouches, false); - } - - void cancelTouch(int pointer) { - numberOfTouches--; - print('$numberOfTouches x'); - } - - void captureDefaultTap(int pointer) { - print('$pointer'); - } -} - -typedef MultiTouchGestureRecognizerCallback = void Function( - int touchCount, bool addOrRemove); - -typedef OnUpdate(DragUpdateDetails details); - -class CustomDrag extends Drag { - @override - void update(DragUpdateDetails details) { - print('xx $details'); - } - - @override - void end(DragEndDetails details) { - super.end(details); - } -} From 479b8303d7714cd161d41fa3b110f771edd69688 Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 23 Nov 2020 17:45:38 +0800 Subject: [PATCH 058/422] new scale mode --- flutter_hbb/lib/model.dart | 1 + flutter_hbb/lib/remote_page.dart | 25 +++++++++---------------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 56e541748..374f366e8 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -44,6 +44,7 @@ class FfiModel with ChangeNotifier { if (k == 'name') return; _permissions[k] = v == 'true'; }); + print('$_permissions'); } void clear() { diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index c0a84b2b6..5d2d49df4 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -26,7 +26,6 @@ class _RemotePageState extends State { Timer _interval; bool _showBar = true; double _bottom = 0; - bool _pan = false; var _scaleMode = false; final FocusNode _focusNode = FocusNode(); @@ -143,15 +142,6 @@ class _RemotePageState extends State { showOptions(context); }, ), - Container( - color: _pan ? Colors.blue[500] : null, - child: IconButton( - color: Colors.white, - icon: Icon(Icons.pan_tool), - onPressed: () { - setState(() => _pan = !_pan); - }, - )) ]), IconButton( color: Colors.white, @@ -238,19 +228,22 @@ class _RemotePageState extends State { y = details.globalPosition.dy; } ..onLongPress = () { - print('long press'); () async { - await showMenu( + print('long press: $x $y'); + var value = await showMenu( context: context, - position: RelativeRect.fromLTRB(x, y, 0, 0), + position: + RelativeRect.fromLTRB(x + 20, y + 20, x + 20, y + 20), items: [ PopupMenuItem( - child: const Text('Doge'), value: 'Doge'), - PopupMenuItem( - child: const Text('Lion'), value: 'Lion'), + child: Text(_scaleMode ? 'Pan Mode' : 'Scale Mode'), + value: 'mode'), ], elevation: 8.0, ); + if (value == 'mode') { + setState(() => _scaleMode = !_scaleMode); + } }(); }; }, From d11625174204eeb04fcd61afeddf768347fa5131 Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 23 Nov 2020 23:18:42 +0800 Subject: [PATCH 059/422] pan/scale draft --- flutter_hbb/lib/main.dart | 18 ++++---- flutter_hbb/lib/model.dart | 60 ++++++++++++++++++++++++--- flutter_hbb/lib/remote_page.dart | 70 ++++++++++++++++++++++++++++++-- 3 files changed, 131 insertions(+), 17 deletions(-) diff --git a/flutter_hbb/lib/main.dart b/flutter_hbb/lib/main.dart index 99767935a..73666ef43 100644 --- a/flutter_hbb/lib/main.dart +++ b/flutter_hbb/lib/main.dart @@ -16,13 +16,15 @@ class App extends StatelessWidget { value: FFI.imageModel, child: ChangeNotifierProvider.value( value: FFI.cursorModel, - child: MaterialApp( - title: 'RustDesk', - theme: ThemeData( - primarySwatch: Colors.blue, - visualDensity: VisualDensity.adaptivePlatformDensity, - ), - home: HomePage(title: 'RustDesk'), - )))); + child: ChangeNotifierProvider.value( + value: FFI.canvasModel, + child: MaterialApp( + title: 'RustDesk', + theme: ThemeData( + primarySwatch: Colors.blue, + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: HomePage(title: 'RustDesk'), + ))))); } } diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 374f366e8..3a3812c41 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -6,6 +6,7 @@ import 'dart:convert'; import 'dart:typed_data'; import 'dart:ui' as ui; import 'package:flutter/material.dart'; +import 'package:tuple/tuple.dart'; import 'dart:async'; import 'common.dart'; @@ -47,6 +48,8 @@ class FfiModel with ChangeNotifier { print('$_permissions'); } + bool keyboard() => _permissions['keyboard'] == true; + void clear() { _pi = PeerInfo(); _display = Display(); @@ -151,9 +154,50 @@ class ImageModel with ChangeNotifier { } } +class CanvasModel with ChangeNotifier { + double _x; + double _y; + double _scale; + double _xPan; + double _yPan; + + CanvasModel() { + clear(); + } + + double get x => _x; + double get y => _y; + double get scale => _scale; + + void startPan() { + _xPan = 0; + _yPan = 0; + } + + void updateOffset(double dx, double dy) { + _x += dx; + _y += dy; + notifyListeners(); + } + + void updateScale(double v) { + _scale *= v; + if (_scale > 1) _scale = 1; + notifyListeners(); + } + + void clear() { + _x = 0; + _y = 0; + _scale = 1.0; + _xPan = 0; + _yPan = 0; + } +} + class CursorModel with ChangeNotifier { ui.Image _image; - final _images = Map(); + final _images = Map>(); double _x = -10000; double _y = -10000; double _hotx = 0; @@ -162,8 +206,10 @@ class CursorModel with ChangeNotifier { double _displayOriginY = 0; ui.Image get image => _image; - double get x => _x - _displayOriginX - _hotx; - double get y => _y - _displayOriginY - _hoty; + double get x => _x - _displayOriginX; + double get y => _y - _displayOriginY; + double get hotx => _hotx; + double get hoty => _hoty; void updateCursorData(Map evt) { var id = int.parse(evt['id']); @@ -176,7 +222,7 @@ class CursorModel with ChangeNotifier { ui.decodeImageFromPixels(rgba, width, height, ui.PixelFormat.rgba8888, (image) { _image = image; - _images[id] = image; + _images[id] = Tuple3(image, _hotx, _hoty); try { // my throw exception, because the listener maybe already dispose notifyListeners(); @@ -187,7 +233,9 @@ class CursorModel with ChangeNotifier { void updateCursorId(Map evt) { final tmp = _images[int.parse(evt['id'])]; if (tmp != null) { - _image = tmp; + _image = tmp.item1; + _hotx = tmp.item2; + _hoty = tmp.item3; notifyListeners(); } } @@ -222,6 +270,7 @@ class FFI { static final imageModel = ImageModel(); static final ffiModel = FfiModel(); static final cursorModel = CursorModel(); + static final canvasModel = CanvasModel(); static String getId() { return getByName('remote_id'); @@ -283,6 +332,7 @@ class FFI { FFI.imageModel.update(null); FFI.cursorModel.clear(); FFI.ffiModel.clear(); + FFI.canvasModel.clear(); } static void setByName(String name, [String value = '']) { diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 5d2d49df4..d8857ba42 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -27,6 +27,9 @@ class _RemotePageState extends State { bool _showBar = true; double _bottom = 0; var _scaleMode = false; + double _xOffset = 0; + double _yOffset = 0; + double _scale = 1; final FocusNode _focusNode = FocusNode(); @override @@ -153,8 +156,55 @@ class _RemotePageState extends State { ), ) : null, - body: RawGestureDetector( - gestures: buildGuestures(), + body: GestureDetector( + onLongPressStart: (details) { + var x = details.globalPosition.dx; + var y = details.globalPosition.dy; + print('long press'); + () async { + var value = await showMenu( + context: context, + position: + RelativeRect.fromLTRB(x + 20, y + 20, x + 20, y + 20), + items: [ + PopupMenuItem( + child: Text(_scaleMode ? 'Pan Mode' : 'Scale Mode'), + value: 'mode'), + ], + elevation: 8.0, + ); + if (value == 'mode') { + setState(() => _scaleMode = !_scaleMode); + } + }(); + }, + onDoubleTap: () { + print('double tap'); + }, + onTap: () { + print('tap'); + }, + onScaleStart: (details) { + _scale = 1; + _xOffset = details.focalPoint.dx; + _yOffset = details.focalPoint.dy; + FFI.canvasModel.startPan(); + }, + onScaleUpdate: (details) { + var scale = details.scale; + if (scale == 1) { + var x = details.focalPoint.dx; + var y = details.focalPoint.dy; + var dx = x - _xOffset; + var dy = y - _yOffset; + FFI.canvasModel.updateOffset(dx, dy); + _xOffset = x; + _yOffset = y; + } else { + FFI.canvasModel.updateScale(scale / _scale); + _scale = scale; + } + }, child: FlutterEasyLoading( child: Container( color: MyTheme.canvasColor, @@ -288,8 +338,11 @@ class ImagePaint extends StatelessWidget { @override Widget build(BuildContext context) { final m = Provider.of(context); + final c = Provider.of(context); + var s = c.scale; return CustomPaint( - painter: new ImagePainter(image: m.image, x: 0, y: 0), + painter: + new ImagePainter(image: m.image, x: c.x / s, y: c.y / s, scale: s), ); } } @@ -298,8 +351,14 @@ class CursorPaint extends StatelessWidget { @override Widget build(BuildContext context) { final m = Provider.of(context); + final c = Provider.of(context); + var s = c.scale; return CustomPaint( - painter: new ImagePainter(image: m.image, x: m.x, y: m.y), + painter: new ImagePainter( + image: m.image, + x: m.x * s - m.hotx + c.x, + y: m.y * s - m.hoty + c.y, + scale: 1), ); } } @@ -309,15 +368,18 @@ class ImagePainter extends CustomPainter { this.image, this.x, this.y, + this.scale, }); ui.Image image; double x; double y; + double scale; @override void paint(Canvas canvas, Size size) { if (image == null) return; + canvas.scale(scale, scale); canvas.drawImage(image, new Offset(x, y), new Paint()); } From f86673ebcb722baef26e351d9eec89c96dbb0ae1 Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 23 Nov 2020 23:25:50 +0800 Subject: [PATCH 060/422] remove custom guesture --- flutter_hbb/lib/drag.dart | 104 ----------------------------- flutter_hbb/lib/remote_page.dart | 109 +------------------------------ 2 files changed, 2 insertions(+), 211 deletions(-) delete mode 100644 flutter_hbb/lib/drag.dart diff --git a/flutter_hbb/lib/drag.dart b/flutter_hbb/lib/drag.dart deleted file mode 100644 index 551daa880..000000000 --- a/flutter_hbb/lib/drag.dart +++ /dev/null @@ -1,104 +0,0 @@ -import 'package:flutter/gestures.dart'; - -class MultiTouchGestureRecognizer extends MultiTapGestureRecognizer { - MultiTouchGestureRecognizerCallback onMultiTap; - var numberOfTouches = 0; - - MultiTouchGestureRecognizer() { - this - ..onTapDown = addTouch - ..onTapUp = removeTouch - ..onTapCancel = cancelTouch - ..onTap = captureDefaultTap; - } - - void addTouch(int pointer, TapDownDetails details) { - numberOfTouches++; - onMultiTap(numberOfTouches, true); - } - - void removeTouch(int pointer, TapUpDetails details) { - numberOfTouches--; - onMultiTap(numberOfTouches, false); - } - - void cancelTouch(int pointer) { - numberOfTouches = 0; - } - - void captureDefaultTap(int pointer) {} -} - -typedef MultiTouchGestureRecognizerCallback = void Function( - int touchCount, bool addOrRemove); - -typedef OnUpdate(DragUpdateDetails details); - -class CustomMultiDrag extends Drag { - CustomMultiDrag({this.events, this.offset}); - - List events; - Offset offset; - - @override - void update(DragUpdateDetails details) { - var n = events.length; - print('$n $details'); - } - - @override - void end(DragEndDetails details) { - super.end(details); - } -} - -typedef OnDisposeState(); - -// clone _ImmediatePointerState -class CustomPointerState extends MultiDragPointerState { - final OnDisposeState onDisposeState; - CustomPointerState(Offset initialPosition, PointerDeviceKind kind, - {this.onDisposeState}) - : super(initialPosition, kind); - - @override - void checkForResolutionAfterMove() { - assert(pendingDelta != null); - if (pendingDelta.distance > computeHitSlop(kind)) - resolve(GestureDisposition.accepted); - } - - @override - void accepted(GestureMultiDragStartCallback starter) { - starter(initialPosition); - } - - @override - void dispose() { - onDisposeState.call(); - super.dispose(); - } -} - -// clone ImmediateMultiDragGestureRecognizer -class CustomMultiDragGestureRecognizer - extends MultiDragGestureRecognizer { - var events = List(); - - /// Create a gesture recognizer for tracking multiple pointers at once. - CustomMultiDragGestureRecognizer({ - Object debugOwner, - PointerDeviceKind kind, - }) : super(debugOwner: debugOwner, kind: kind); - - @override - CustomPointerState createNewPointerState(PointerDownEvent event) { - events.add(event); - return CustomPointerState(event.position, event.kind, onDisposeState: () { - events.remove(event); - }); - } - - @override - String get debugDescription => 'custom_multidrag'; -} diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index d8857ba42..fdce55b5a 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/gestures.dart'; import 'package:provider/provider.dart'; import 'package:flutter/services.dart'; import 'dart:ui' as ui; @@ -9,7 +8,6 @@ import 'dart:math' as math; import 'package:tuple/tuple.dart'; import 'package:wakelock/wakelock.dart'; import 'common.dart'; -import 'drag.dart'; import 'model.dart'; class RemotePage extends StatefulWidget { @@ -26,7 +24,6 @@ class _RemotePageState extends State { Timer _interval; bool _showBar = true; double _bottom = 0; - var _scaleMode = false; double _xOffset = 0; double _yOffset = 0; double _scale = 1; @@ -167,15 +164,11 @@ class _RemotePageState extends State { position: RelativeRect.fromLTRB(x + 20, y + 20, x + 20, y + 20), items: [ - PopupMenuItem( - child: Text(_scaleMode ? 'Pan Mode' : 'Scale Mode'), - value: 'mode'), + PopupMenuItem(child: Text('Test'), value: 'mode'), ], elevation: 8.0, ); - if (value == 'mode') { - setState(() => _scaleMode = !_scaleMode); - } + if (value == 'mode') {} }(); }, onDoubleTap: () { @@ -231,104 +224,6 @@ class _RemotePageState extends State { )); } - Map buildGuestures() { - var m = { - TapGestureRecognizer: - GestureRecognizerFactoryWithHandlers( - () => TapGestureRecognizer(), - (TapGestureRecognizer instance) { - instance.onTap = () { - print('tap'); - }; - }, - ), - MultiTouchGestureRecognizer: - GestureRecognizerFactoryWithHandlers( - () => MultiTouchGestureRecognizer(), - (MultiTouchGestureRecognizer instance) { - instance.onMultiTap = ( - touchCount, - addOrRemove, - ) { - if (touchCount == 3 && addOrRemove) { - setState(() => _scaleMode = !_scaleMode); - } - }; - }, - ), - CustomMultiDragGestureRecognizer: GestureRecognizerFactoryWithHandlers< - CustomMultiDragGestureRecognizer>( - () => CustomMultiDragGestureRecognizer(), - (CustomMultiDragGestureRecognizer instance) { - instance - ..onStart = (offset) { - return CustomMultiDrag(events: instance.events, offset: offset); - }; - }, - ), - LongPressGestureRecognizer: - GestureRecognizerFactoryWithHandlers( - () => LongPressGestureRecognizer(), - (LongPressGestureRecognizer instance) { - var x = 0.0; - var y = 0.0; - instance - ..onLongPressStart = (details) { - x = details.globalPosition.dx; - y = details.globalPosition.dy; - } - ..onLongPress = () { - () async { - print('long press: $x $y'); - var value = await showMenu( - context: context, - position: - RelativeRect.fromLTRB(x + 20, y + 20, x + 20, y + 20), - items: [ - PopupMenuItem( - child: Text(_scaleMode ? 'Pan Mode' : 'Scale Mode'), - value: 'mode'), - ], - elevation: 8.0, - ); - if (value == 'mode') { - setState(() => _scaleMode = !_scaleMode); - } - }(); - }; - }, - ), - ScaleGestureRecognizer: - GestureRecognizerFactoryWithHandlers( - () => ScaleGestureRecognizer(), - (ScaleGestureRecognizer instance) { - instance - ..onStart = (detail) { - print('scale start'); - } - ..onUpdate = (detail) { - print('$detail'); - }; - }, - ), - DoubleTapGestureRecognizer: - GestureRecognizerFactoryWithHandlers( - () => DoubleTapGestureRecognizer(), - (DoubleTapGestureRecognizer instance) { - instance.onDoubleTap = () { - print('double tap'); - }; - }, - ), - }; - if (_scaleMode) { - m.remove(CustomMultiDragGestureRecognizer); - } else { - m.remove(ScaleGestureRecognizer); - } - return m; - } - void close() { msgbox('', 'Close', 'Are you sure to close the connection?', context); } From 23f1ce5da5525a66d6a1fb546fa807af26cdcf5c Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 23 Nov 2020 23:52:30 +0800 Subject: [PATCH 061/422] minor --- flutter_hbb/lib/common.dart | 2 -- flutter_hbb/lib/model.dart | 6 +++--- flutter_hbb/lib/remote_page.dart | 6 ++---- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index acdf11e8b..6a1ada3fc 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -13,7 +13,6 @@ class MyTheme { static const Color border = Color(0xFFCCCCCC); } -// https://github.com/huangjianke/flutter_easyloading void showLoading(String text) { dismissLoading(); EasyLoading.show(status: text); @@ -32,7 +31,6 @@ bool _hasDialog = false; typedef BuildAlertDailog = Tuple3> Function( void Function(void Function())); -// https://material.io/develop/flutter/components/dialogs Future showAlertDialog(BuildContext context, BuildAlertDailog build, [WillPopCallback onWillPop, bool barrierDismissible = false, diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 3a3812c41..f976769f4 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -22,7 +22,6 @@ typedef F3 = void Function(Pointer, Pointer); typedef F4 = void Function(Pointer); typedef F5 = Pointer Function(); -// https://juejin.im/post/6844903864852807694 class FfiModel with ChangeNotifier { PeerInfo _pi; Display _display; @@ -63,6 +62,7 @@ class FfiModel with ChangeNotifier { BuildContext context, void Function(Map evt, String id, BuildContext context) handleMsgbox) { + var pos; for (;;) { var evt = FFI.popEvent(); if (evt == null) break; @@ -78,11 +78,12 @@ class FfiModel with ChangeNotifier { } else if (name == 'cursor_id') { FFI.cursorModel.updateCursorId(evt); } else if (name == 'cursor_position') { - FFI.cursorModel.updateCursorPosition(evt); + pos = evt; } else if (name == 'permission') { FFI.ffiModel.updatePermission(evt); } } + if (pos != null) FFI.cursorModel.updateCursorPosition(pos); if (!_decoding) { var rgba = FFI.getRgba(); if (rgba != null) { @@ -343,7 +344,6 @@ class FFI { var p = _getByName(Utf8.toUtf8(name), Utf8.toUtf8(arg)); assert(p != nullptr && p != null); var res = Utf8.fromUtf8(p); - // https://github.com/brickpop/flutter-rust-ffi _freeCString(p); return res; } diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index fdce55b5a..ee38e3e97 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -19,7 +19,6 @@ class RemotePage extends StatefulWidget { _RemotePageState createState() => _RemotePageState(); } -// https://github.com/hanxu317317/flutter_plan_demo/blob/master/lib/src/enter.dart class _RemotePageState extends State { Timer _interval; bool _showBar = true; @@ -34,7 +33,6 @@ class _RemotePageState extends State { super.initState(); FFI.connect(widget.id); WidgetsBinding.instance.addPostFrameCallback((_) { - // https://stackoverflow.com/questions/46640116/make-flutter-application-fullscreen SystemChrome.setEnabledSystemUIOverlays([]); showLoading('Connecting...'); _interval = @@ -82,8 +80,8 @@ class _RemotePageState extends State { @override Widget build(BuildContext context) { - // Size size = MediaQueryData.fromWindow(ui.window).size; - // MediaQuery.of(context).size.height; + print('${MediaQueryData.fromWindow(ui.window).size}'); + print('${MediaQuery.of(context).size}'); EasyLoading.instance.loadingStyle = EasyLoadingStyle.light; return WillPopScope( onWillPop: () async { From 982df980bdd7b6a362bf445d8dd216f83f233196 Mon Sep 17 00:00:00 2001 From: open-trade Date: Tue, 24 Nov 2020 11:25:56 +0800 Subject: [PATCH 062/422] fix msgbox hasCancel and home id editor focus problem --- flutter_hbb/lib/common.dart | 11 +++++++---- flutter_hbb/lib/home_page.dart | 7 ++++++- flutter_hbb/lib/model.dart | 25 +++++++++++++++++++++++++ flutter_hbb/lib/remote_page.dart | 3 --- 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 6a1ada3fc..469564182 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -61,19 +61,22 @@ Future showAlertDialog(BuildContext context, BuildAlertDailog build, } void msgbox(String type, String title, String text, BuildContext context, - [hasCancel = false]) { + [bool hasCancel]) { + if (hasCancel == null) { + hasCancel = type != 'error'; + } showAlertDialog( context, (_) => Tuple3(Text(title), Text(text), [ hasCancel - ? Spacer() - : FlatButton( + ? FlatButton( textColor: MyTheme.accent, onPressed: () { Navigator.pop(context); }, child: Text('Cancel'), - ), + ) + : Spacer(), FlatButton( textColor: MyTheme.accent, onPressed: () { diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index 1a7de8666..57f3dca76 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -48,6 +48,10 @@ class _HomePageState extends State { builder: (BuildContext context) => RemotePage(id: id), ), ); + FocusScopeNode currentFocus = FocusScope.of(context); + if (!currentFocus.hasPrimaryFocus) { + currentFocus.unfocus(); + } } Widget getSearchBarUI() { @@ -98,7 +102,7 @@ class _HomePageState extends State { color: Color(0xFFB9BABC), ), ), - autofocus: false, + autofocus: _idController.text.isEmpty, controller: _idController, ), ), @@ -110,6 +114,7 @@ class _HomePageState extends State { icon: Icon(Icons.arrow_forward, color: Color(0xFFB9BABC), size: 45), onPressed: onConnect, + autofocus: _idController.text.isNotEmpty, ), ) ], diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index f976769f4..6588b2352 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -177,7 +177,24 @@ class CanvasModel with ChangeNotifier { void updateOffset(double dx, double dy) { _x += dx; + if (_x > 0) { + _x = 0; + dx = -dx; + } _y += dy; + if (_y > 0) { + _y = 0; + dy = -dy; + } + _xPan += dx; + _yPan += dy; + var px = (_xPan > 0 ? _xPan.floor() : _xPan.ceil()).toDouble(); + var py = (_yPan > 0 ? _yPan.floor() : _yPan.ceil()).toDouble(); + if (px != 0 || py != 0) { + FFI.cursorModel.update(-px, -py); + _xPan -= px; + _yPan -= py; + } notifyListeners(); } @@ -253,6 +270,14 @@ class CursorModel with ChangeNotifier { notifyListeners(); } + void update(double dx, double dy) { + _x += dx; + _y += dy; + var x = _x.toInt(); + var y = _y.toInt(); + FFI.setByName('send_mouse', json.encode({'x': '$x', 'y': '$y'})); + } + void clear() { _x = -10000; _x = -10000; diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index ee38e3e97..4a03136e2 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -169,9 +169,6 @@ class _RemotePageState extends State { if (value == 'mode') {} }(); }, - onDoubleTap: () { - print('double tap'); - }, onTap: () { print('tap'); }, From b776f1339ad8ec1c2287f76de8a8690897a3420f Mon Sep 17 00:00:00 2001 From: open-trade Date: Tue, 24 Nov 2020 12:11:55 +0800 Subject: [PATCH 063/422] initialize send mouse and max/min scale --- flutter_hbb/lib/model.dart | 41 +++++++++++++++++++++++++++++++- flutter_hbb/lib/remote_page.dart | 5 ++-- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 6588b2352..a1c2ed229 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -1,6 +1,7 @@ import 'package:ffi/ffi.dart'; import 'package:path_provider/path_provider.dart'; import 'dart:io'; +import 'dart:math'; import 'dart:ffi'; import 'dart:convert'; import 'dart:typed_data'; @@ -89,6 +90,10 @@ class FfiModel with ChangeNotifier { if (rgba != null) { if (_waitForImage) { _waitForImage = false; + final size = MediaQueryData.fromWindow(ui.window).size; + final xscale = size.width / _display.width.toDouble(); + final yscale = size.height / _display.height.toDouble(); + FFI.canvasModel.scale = max(xscale, yscale); dismissLoading(); } _decoding = true; @@ -153,6 +158,22 @@ class ImageModel with ChangeNotifier { _image = image; if (image != null) notifyListeners(); } + + double get maxScale { + if (_image == null) return 1.0; + final size = MediaQueryData.fromWindow(ui.window).size; + final xscale = size.width / _image.width; + final yscale = size.height / _image.height; + return max(1.0, max(xscale, yscale)); + } + + double get minScale { + if (_image == null) return 1.0; + final size = MediaQueryData.fromWindow(ui.window).size; + final xscale = size.width / _image.width; + final yscale = size.height / _image.height; + return min(xscale, yscale); + } } class CanvasModel with ChangeNotifier { @@ -170,6 +191,11 @@ class CanvasModel with ChangeNotifier { double get y => _y; double get scale => _scale; + set scale(v) { + _scale = v; + notifyListeners(); + } + void startPan() { _xPan = 0; _yPan = 0; @@ -200,7 +226,10 @@ class CanvasModel with ChangeNotifier { void updateScale(double v) { _scale *= v; - if (_scale > 1) _scale = 1; + final maxs = FFI.imageModel.maxScale; + final mins = FFI.imageModel.minScale; + if (_scale > maxs) _scale = maxs; + if (_scale < mins) _scale = mins; notifyListeners(); } @@ -302,6 +331,16 @@ class FFI { return getByName('remote_id'); } + static void tap() { + FFI.sendMouse('down', 'left'); + FFI.sendMouse('up', 'left'); + } + + static void sendMouse(String type, String buttons) { + FFI.setByName( + 'send_mouse', json.encode({'type': type, 'buttons': buttons})); + } + static List peers() { try { List peers = json.decode(getByName('peers')); diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 4a03136e2..d3c95d23a 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -80,7 +80,8 @@ class _RemotePageState extends State { @override Widget build(BuildContext context) { - print('${MediaQueryData.fromWindow(ui.window).size}'); + final size = MediaQueryData.fromWindow(ui.window).size; + print('$size'); print('${MediaQuery.of(context).size}'); EasyLoading.instance.loadingStyle = EasyLoadingStyle.light; return WillPopScope( @@ -170,7 +171,7 @@ class _RemotePageState extends State { }(); }, onTap: () { - print('tap'); + FFI.tap(); }, onScaleStart: (details) { _scale = 1; From 514341180de07777a7d6ba70b979fb4ed591b944 Mon Sep 17 00:00:00 2001 From: open-trade Date: Tue, 24 Nov 2020 22:03:04 +0800 Subject: [PATCH 064/422] new pan draft --- flutter_hbb/lib/home_page.dart | 3 + flutter_hbb/lib/model.dart | 132 ++++++++++++++++++++++++------- flutter_hbb/lib/remote_page.dart | 4 +- 3 files changed, 107 insertions(+), 32 deletions(-) diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index 57f3dca76..66ca89384 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -55,6 +55,9 @@ class _HomePageState extends State { } Widget getSearchBarUI() { + if (!FFI.ffiModel.initialized) { + return Container(); + } return Padding( padding: const EdgeInsets.only(top: 8.0), child: Container( diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index a1c2ed229..967defa48 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -28,14 +28,17 @@ class FfiModel with ChangeNotifier { Display _display; bool _decoding; bool _waitForImage; + bool _initialized = false; final _permissions = Map(); get permissions => _permissions; + get initialized => _initialized; FfiModel() { clear(); () async { await FFI.init(); + _initialized = true; notifyListeners(); }(); } @@ -90,10 +93,6 @@ class FfiModel with ChangeNotifier { if (rgba != null) { if (_waitForImage) { _waitForImage = false; - final size = MediaQueryData.fromWindow(ui.window).size; - final xscale = size.width / _display.width.toDouble(); - final yscale = size.height / _display.height.toDouble(); - FFI.canvasModel.scale = max(xscale, yscale); dismissLoading(); } _decoding = true; @@ -155,6 +154,12 @@ class ImageModel with ChangeNotifier { ui.Image get image => _image; void update(ui.Image image) { + if (_image == null && image != null) { + final size = MediaQueryData.fromWindow(ui.window).size; + final xscale = size.width / image.width; + final yscale = size.height / image.height; + FFI.canvasModel.scale = max(xscale, yscale); + } _image = image; if (image != null) notifyListeners(); } @@ -180,8 +185,6 @@ class CanvasModel with ChangeNotifier { double _x; double _y; double _scale; - double _xPan; - double _yPan; CanvasModel() { clear(); @@ -196,31 +199,13 @@ class CanvasModel with ChangeNotifier { notifyListeners(); } - void startPan() { - _xPan = 0; - _yPan = 0; + void panX(double dx) { + _x += dx; + notifyListeners(); } - void updateOffset(double dx, double dy) { - _x += dx; - if (_x > 0) { - _x = 0; - dx = -dx; - } + void panY(double dy) { _y += dy; - if (_y > 0) { - _y = 0; - dy = -dy; - } - _xPan += dx; - _yPan += dy; - var px = (_xPan > 0 ? _xPan.floor() : _xPan.ceil()).toDouble(); - var py = (_yPan > 0 ? _yPan.floor() : _yPan.ceil()).toDouble(); - if (px != 0 || py != 0) { - FFI.cursorModel.update(-px, -py); - _xPan -= px; - _yPan -= py; - } notifyListeners(); } @@ -237,8 +222,6 @@ class CanvasModel with ChangeNotifier { _x = 0; _y = 0; _scale = 1.0; - _xPan = 0; - _yPan = 0; } } @@ -251,6 +234,8 @@ class CursorModel with ChangeNotifier { double _hoty = 0; double _displayOriginX = 0; double _displayOriginY = 0; + double _xPan; + double _yPan; ui.Image get image => _image; double get x => _x - _displayOriginX; @@ -258,6 +243,91 @@ class CursorModel with ChangeNotifier { double get hotx => _hotx; double get hoty => _hoty; + void startPan() { + _xPan = 0; + _yPan = 0; + } + + // physical display coordinate + Rect getVisibleRect() { + final size = MediaQueryData.fromWindow(ui.window).size; + final xoffset = FFI.canvasModel.x; + final yoffset = FFI.canvasModel.y; + final scale = FFI.canvasModel.scale; + final x0 = _displayOriginX - xoffset / scale; + final y0 = _displayOriginY - yoffset / scale; + return Rect.fromLTWH(x0, y0, size.width / scale, size.height / scale); + } + + void updatePan(double dx, double dy) { + final r = getVisibleRect(); + var cx = r.center.dx; + var cy = r.center.dy; + var tryMoveCanvasX = false; + if (dx > 0) { + final maxCanvasCanMove = + _displayOriginX + FFI.imageModel.image.width - r.right; + tryMoveCanvasX = _x + dx > cx && maxCanvasCanMove > 0; + if (tryMoveCanvasX) { + dx = min(dx, maxCanvasCanMove); + } else { + final maxCursorCanMove = r.right - _x; + dx = min(dx, maxCursorCanMove); + } + } else if (dx < 0) { + final maxCanvasCanMove = _displayOriginX - r.left; + tryMoveCanvasX = _x + dx < cx && maxCanvasCanMove < 0; + if (tryMoveCanvasX) { + dx = max(dx, maxCanvasCanMove); + } else { + final maxCursorCanMove = r.left - _x; + dx = max(dx, maxCursorCanMove); + } + } + var tryMoveCanvasY = false; + if (dy > 0) { + final mayCanvasCanMove = + _displayOriginY + FFI.imageModel.image.width - r.right; + tryMoveCanvasY = _y + dy > cy && mayCanvasCanMove > 0; + if (tryMoveCanvasY) { + dy = min(dy, mayCanvasCanMove); + } else { + final mayCursorCanMove = r.right - _y; + dy = min(dy, mayCursorCanMove); + } + } else if (dy < 0) { + final mayCanvasCanMove = _displayOriginY - r.left; + tryMoveCanvasY = _y + dy < cy && mayCanvasCanMove < 0; + if (tryMoveCanvasY) { + dy = max(dy, mayCanvasCanMove); + } else { + final mayCursorCanMove = r.left - _y; + dy = max(dy, mayCursorCanMove); + } + } + + if (dx == 0 && dy == 0) return; + _x += dx; + _y += dy; + if (tryMoveCanvasX && dx != 0) { + FFI.canvasModel.panX(dx); + } + if (tryMoveCanvasY && dy != 0) { + FFI.canvasModel.panY(dy); + } + + _xPan += dx; + _yPan += dy; + var px = (_xPan > 0 ? _xPan.floor() : _xPan.ceil()).toDouble(); + var py = (_yPan > 0 ? _yPan.floor() : _yPan.ceil()).toDouble(); + if (px != 0 || py != 0) { + FFI.cursorModel.update(-px, -py); + _xPan -= px; + _yPan -= py; + } + notifyListeners(); + } + void updateCursorData(Map evt) { var id = int.parse(evt['id']); _hotx = double.parse(evt['hotx']); @@ -308,6 +378,8 @@ class CursorModel with ChangeNotifier { } void clear() { + _xPan = 0; + _yPan = 0; _x = -10000; _x = -10000; _image = null; diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index d3c95d23a..5b2dd930e 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -177,7 +177,7 @@ class _RemotePageState extends State { _scale = 1; _xOffset = details.focalPoint.dx; _yOffset = details.focalPoint.dy; - FFI.canvasModel.startPan(); + FFI.cursorModel.startPan(); }, onScaleUpdate: (details) { var scale = details.scale; @@ -186,7 +186,7 @@ class _RemotePageState extends State { var y = details.focalPoint.dy; var dx = x - _xOffset; var dy = y - _yOffset; - FFI.canvasModel.updateOffset(dx, dy); + FFI.cursorModel.updatePan(dx, dy); _xOffset = x; _yOffset = y; } else { From 4b07075355774f2bea69f43aa86c3a990783e93c Mon Sep 17 00:00:00 2001 From: open-trade Date: Tue, 24 Nov 2020 23:36:46 +0800 Subject: [PATCH 065/422] new pan works --- flutter_hbb/lib/model.dart | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 967defa48..f67e15465 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -51,7 +51,7 @@ class FfiModel with ChangeNotifier { print('$_permissions'); } - bool keyboard() => _permissions['keyboard'] == true; + bool keyboard() => _permissions['keyboard'] != false; void clear() { _pi = PeerInfo(); @@ -287,21 +287,21 @@ class CursorModel with ChangeNotifier { var tryMoveCanvasY = false; if (dy > 0) { final mayCanvasCanMove = - _displayOriginY + FFI.imageModel.image.width - r.right; + _displayOriginY + FFI.imageModel.image.height - r.bottom; tryMoveCanvasY = _y + dy > cy && mayCanvasCanMove > 0; if (tryMoveCanvasY) { dy = min(dy, mayCanvasCanMove); } else { - final mayCursorCanMove = r.right - _y; + final mayCursorCanMove = r.bottom - _y; dy = min(dy, mayCursorCanMove); } } else if (dy < 0) { - final mayCanvasCanMove = _displayOriginY - r.left; + final mayCanvasCanMove = _displayOriginY - r.top; tryMoveCanvasY = _y + dy < cy && mayCanvasCanMove < 0; if (tryMoveCanvasY) { dy = max(dy, mayCanvasCanMove); } else { - final mayCursorCanMove = r.left - _y; + final mayCursorCanMove = r.top - _y; dy = max(dy, mayCursorCanMove); } } @@ -310,10 +310,10 @@ class CursorModel with ChangeNotifier { _x += dx; _y += dy; if (tryMoveCanvasX && dx != 0) { - FFI.canvasModel.panX(dx); + FFI.canvasModel.panX(-dx); } if (tryMoveCanvasY && dy != 0) { - FFI.canvasModel.panY(dy); + FFI.canvasModel.panY(-dy); } _xPan += dx; @@ -321,7 +321,7 @@ class CursorModel with ChangeNotifier { var px = (_xPan > 0 ? _xPan.floor() : _xPan.ceil()).toDouble(); var py = (_yPan > 0 ? _yPan.floor() : _yPan.ceil()).toDouble(); if (px != 0 || py != 0) { - FFI.cursorModel.update(-px, -py); + FFI.cursorModel.update(px, py); _xPan -= px; _yPan -= py; } @@ -366,15 +366,16 @@ class CursorModel with ChangeNotifier { void updateDisplayOrigin(double x, double y) { _displayOriginX = x; _displayOriginY = y; + _x = x; + _y = y; + FFI.moveMouse(x, y); notifyListeners(); } void update(double dx, double dy) { _x += dx; _y += dy; - var x = _x.toInt(); - var y = _y.toInt(); - FFI.setByName('send_mouse', json.encode({'x': '$x', 'y': '$y'})); + FFI.moveMouse(_x, _y); } void clear() { @@ -409,10 +410,18 @@ class FFI { } static void sendMouse(String type, String buttons) { + if (!FFI.ffiModel.keyboard()) return; FFI.setByName( 'send_mouse', json.encode({'type': type, 'buttons': buttons})); } + static void moveMouse(double x, double y) { + if (!FFI.ffiModel.keyboard()) return; + var x2 = x.toInt(); + var y2 = y.toInt(); + FFI.setByName('send_mouse', json.encode({'x': '$x2', 'y': '$y2'})); + } + static List peers() { try { List peers = json.decode(getByName('peers')); From 434850ed0d1f9acd43cf22521aa4f3b264f2d85e Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 25 Nov 2020 00:13:23 +0800 Subject: [PATCH 066/422] fix on new pan --- flutter_hbb/lib/model.dart | 28 ++++------------------------ flutter_hbb/lib/remote_page.dart | 1 - 2 files changed, 4 insertions(+), 25 deletions(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index f67e15465..ebf4b1755 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -234,8 +234,6 @@ class CursorModel with ChangeNotifier { double _hoty = 0; double _displayOriginX = 0; double _displayOriginY = 0; - double _xPan; - double _yPan; ui.Image get image => _image; double get x => _x - _displayOriginX; @@ -243,11 +241,6 @@ class CursorModel with ChangeNotifier { double get hotx => _hotx; double get hoty => _hoty; - void startPan() { - _xPan = 0; - _yPan = 0; - } - // physical display coordinate Rect getVisibleRect() { final size = MediaQueryData.fromWindow(ui.window).size; @@ -260,6 +253,9 @@ class CursorModel with ChangeNotifier { } void updatePan(double dx, double dy) { + final scale = FFI.canvasModel.scale; + dx /= scale; + dy /= scale; final r = getVisibleRect(); var cx = r.center.dx; var cy = r.center.dy; @@ -316,15 +312,7 @@ class CursorModel with ChangeNotifier { FFI.canvasModel.panY(-dy); } - _xPan += dx; - _yPan += dy; - var px = (_xPan > 0 ? _xPan.floor() : _xPan.ceil()).toDouble(); - var py = (_yPan > 0 ? _yPan.floor() : _yPan.ceil()).toDouble(); - if (px != 0 || py != 0) { - FFI.cursorModel.update(px, py); - _xPan -= px; - _yPan -= py; - } + FFI.moveMouse(_x, _y); notifyListeners(); } @@ -372,15 +360,7 @@ class CursorModel with ChangeNotifier { notifyListeners(); } - void update(double dx, double dy) { - _x += dx; - _y += dy; - FFI.moveMouse(_x, _y); - } - void clear() { - _xPan = 0; - _yPan = 0; _x = -10000; _x = -10000; _image = null; diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 5b2dd930e..5ea8e2d37 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -177,7 +177,6 @@ class _RemotePageState extends State { _scale = 1; _xOffset = details.focalPoint.dx; _yOffset = details.focalPoint.dy; - FFI.cursorModel.startPan(); }, onScaleUpdate: (details) { var scale = details.scale; From 7f9826a71009d9fb7b82363d49406fab9b2c80c8 Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 25 Nov 2020 00:41:39 +0800 Subject: [PATCH 067/422] change showActions to pop --- flutter_hbb/lib/remote_page.dart | 62 +++++++++++--------------------- 1 file changed, 21 insertions(+), 41 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 5ea8e2d37..1369e4bbd 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -153,23 +153,6 @@ class _RemotePageState extends State { ) : null, body: GestureDetector( - onLongPressStart: (details) { - var x = details.globalPosition.dx; - var y = details.globalPosition.dy; - print('long press'); - () async { - var value = await showMenu( - context: context, - position: - RelativeRect.fromLTRB(x + 20, y + 20, x + 20, y + 20), - items: [ - PopupMenuItem(child: Text('Test'), value: 'mode'), - ], - elevation: 8.0, - ); - if (value == 'mode') {} - }(); - }, onTap: () { FFI.tap(); }, @@ -421,28 +404,25 @@ void showOptions(BuildContext context) { } void showActions(BuildContext context) { - showAlertDialog( - context, - (setState) => Tuple3( - null, - Column(mainAxisSize: MainAxisSize.min, children: [ - ListTile( - onTap: () { - Navigator.pop(context); - FFI.setByName('ctrl_alt_del'); - }, - title: Text('Insert Ctrl + Alt + Del'), - ), - ListTile( - onTap: () { - Navigator.pop(context); - FFI.setByName('lock_screen'); - }, - title: Text('Insert Lock'), - ), - ]), - null), - () async => true, - true, - 0); + final size = MediaQueryData.fromWindow(ui.window).size; + final x = 150.0; + final y = size.height; + () async { + var value = await showMenu( + context: context, + position: RelativeRect.fromLTRB(x, y, x, y), + items: [ + PopupMenuItem( + child: Text('Insert Ctrl + Alt + Del'), value: 'cad'), + PopupMenuItem(child: Text('Insert Lock'), value: 'lock'), + ], + elevation: 8.0, + ); + if (value == 'cad') { + FFI.setByName('ctrl_alt_del'); + } + if (value == 'lock') { + FFI.setByName('lock_screen'); + } + }(); } From ed3fd81ef7b23b5a3dcd1f715826b3bf644fd5de Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 25 Nov 2020 01:13:08 +0800 Subject: [PATCH 068/422] start mouse tool --- flutter_hbb/lib/remote_page.dart | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 1369e4bbd..0d9b2814d 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -26,6 +26,7 @@ class _RemotePageState extends State { double _xOffset = 0; double _yOffset = 0; double _scale = 1; + bool _mouseTools = false; final FocusNode _focusNode = FocusNode(); @override @@ -125,15 +126,6 @@ class _RemotePageState extends State { SystemChannels.textInput .invokeMethod('TextInput.show'); }), - Transform.rotate( - angle: 15 * math.pi / 180, - child: IconButton( - color: Colors.white, - icon: Icon(Icons.flash_on), - onPressed: () { - showActions(context); - }, - )), IconButton( color: Colors.white, icon: Icon(Icons.tv), @@ -141,6 +133,22 @@ class _RemotePageState extends State { showOptions(context); }, ), + Container( + color: _mouseTools ? Colors.blue[500] : null, + child: IconButton( + color: Colors.white, + icon: Icon(Icons.mouse), + onPressed: () { + setState(() => _mouseTools = !_mouseTools); + }, + )), + IconButton( + color: Colors.white, + icon: Icon(Icons.more_vert), + onPressed: () { + showActions(context); + }, + ), ]), IconButton( color: Colors.white, @@ -176,6 +184,7 @@ class _RemotePageState extends State { _scale = scale; } }, + onScaleEnd: (_) {}, child: FlutterEasyLoading( child: Container( color: MyTheme.canvasColor, @@ -404,8 +413,8 @@ void showOptions(BuildContext context) { } void showActions(BuildContext context) { - final size = MediaQueryData.fromWindow(ui.window).size; - final x = 150.0; + final size = MediaQuery.of(context).size; + final x = 120.0; final y = size.height; () async { var value = await showMenu( From e3b0ee9caf6ee49e892483565ecc685a9d6beed9 Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 25 Nov 2020 11:20:40 +0800 Subject: [PATCH 069/422] adjust cursor position for keyboard --- flutter_hbb/lib/model.dart | 7 +++++++ flutter_hbb/lib/remote_page.dart | 8 +++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index ebf4b1755..4949ddcf6 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -252,6 +252,13 @@ class CursorModel with ChangeNotifier { return Rect.fromLTWH(x0, y0, size.width / scale, size.height / scale); } + double adjustForKeyboard() { + var keyboardHeight = MediaQueryData.fromWindow(ui.window).viewInsets.bottom; + if (keyboardHeight < 100) return 0; + var h = _y - getVisibleRect().top; + return h > 200 ? h - 200 : 0; + } + void updatePan(double dx, double dy) { final scale = FFI.canvasModel.scale; dx /= scale; diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 0d9b2814d..137181686 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -221,10 +221,11 @@ class ImagePaint extends StatelessWidget { Widget build(BuildContext context) { final m = Provider.of(context); final c = Provider.of(context); + final adjust = FFI.cursorModel.adjustForKeyboard(); var s = c.scale; return CustomPaint( - painter: - new ImagePainter(image: m.image, x: c.x / s, y: c.y / s, scale: s), + painter: new ImagePainter( + image: m.image, x: c.x / s, y: (c.y - adjust) / s, scale: s), ); } } @@ -234,12 +235,13 @@ class CursorPaint extends StatelessWidget { Widget build(BuildContext context) { final m = Provider.of(context); final c = Provider.of(context); + final adjust = FFI.cursorModel.adjustForKeyboard(); var s = c.scale; return CustomPaint( painter: new ImagePainter( image: m.image, x: m.x * s - m.hotx + c.x, - y: m.y * s - m.hoty + c.y, + y: m.y * s - m.hoty + c.y - adjust, scale: 1), ); } From 2606a44f77445095a2e8b00c6da09231796a956d Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 25 Nov 2020 13:03:48 +0800 Subject: [PATCH 070/422] help tools --- flutter_hbb/lib/remote_page.dart | 91 +++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 2 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 137181686..77e009b31 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -4,7 +4,6 @@ import 'package:flutter/services.dart'; import 'dart:ui' as ui; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'dart:async'; -import 'dart:math' as math; import 'package:tuple/tuple.dart'; import 'package:wakelock/wakelock.dart'; import 'common.dart'; @@ -27,6 +26,13 @@ class _RemotePageState extends State { double _yOffset = 0; double _scale = 1; bool _mouseTools = false; + var _shift = false; + var _ctrl = false; + var _alt = false; + var _command = false; + var _drag = false; + var _right = false; + var _scroll = false; final FocusNode _focusNode = FocusNode(); @override @@ -139,7 +145,11 @@ class _RemotePageState extends State { color: Colors.white, icon: Icon(Icons.mouse), onPressed: () { - setState(() => _mouseTools = !_mouseTools); + setState(() { + _mouseTools = !_mouseTools; + _command = _ctrl = _shift = + _alt = _scroll = _drag = _right = false; + }); }, )), IconButton( @@ -191,6 +201,7 @@ class _RemotePageState extends State { child: Stack(children: [ ImagePaint(), CursorPaint(), + getHelpTools(), SizedBox( width: 0, height: 0, @@ -214,6 +225,82 @@ class _RemotePageState extends State { void close() { msgbox('', 'Close', 'Are you sure to close the connection?', context); } + + Widget getHelpTools() { + if (!_mouseTools) { + return SizedBox(); + } + var textStyle = TextStyle(color: Colors.white, fontSize: 11); + var wrap = (String text, void Function() onPressed, [bool active]) { + return ButtonTheme( + padding: EdgeInsets.symmetric( + vertical: 6, horizontal: 12), //adds padding inside the button + materialTapTargetSize: MaterialTapTargetSize + .shrinkWrap, //limits the touch area to the button area + minWidth: 0, //wraps child's width + height: 0, + child: FlatButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5.0), + ), + color: active == true ? MyTheme.accent50 : null, + child: Text(text, style: textStyle), + onPressed: onPressed)); + }; + return Stack(children: [ + SizedBox( + height: 80, + child: Container(child: null, color: Color(0x66000000)), + ), + Container( + padding: const EdgeInsets.only(top: 20, left: 8, right: 8), + child: Wrap( + spacing: 5, + runSpacing: 5, + children: [ + wrap('Drag', () { + setState(() { + _drag = !_drag; + if (_drag) { + _scroll = false; + _right = false; + } + }); + }, _drag), + wrap('Scroll', () { + setState(() { + _scroll = !_scroll; + if (_drag) { + _drag = false; + _right = false; + } + }); + }, _scroll), + wrap('Right', () { + setState(() { + _right = !_right; + if (_drag) { + _scroll = false; + _drag = false; + } + }); + }, _right), + wrap('Ctrl', () { + setState(() => _ctrl = !_ctrl); + }, _ctrl), + wrap('Alt', () { + setState(() => _alt = !_alt); + }, _alt), + wrap('Shift', () { + setState(() => _shift = !_shift); + }, _shift), + wrap('Command', () { + setState(() => _command = !_command); + }, _command), + ], + )) + ]); + } } class ImagePaint extends StatelessWidget { From b04f90ef6719d8beb96caaf844df5fab6755062a Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 25 Nov 2020 14:41:57 +0800 Subject: [PATCH 071/422] adjust canvas offset when scale --- flutter_hbb/lib/model.dart | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 4949ddcf6..9584c056c 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -1,4 +1,5 @@ import 'package:ffi/ffi.dart'; +import 'package:flutter/gestures.dart'; import 'package:path_provider/path_provider.dart'; import 'dart:io'; import 'dart:math'; @@ -210,11 +211,20 @@ class CanvasModel with ChangeNotifier { } void updateScale(double v) { + final offset = FFI.cursorModel.offset; + var r = FFI.cursorModel.getVisibleRect(); + final px0 = (offset.dx - r.left) * _scale; + final py0 = (offset.dy - r.top) * _scale; _scale *= v; final maxs = FFI.imageModel.maxScale; final mins = FFI.imageModel.minScale; if (_scale > maxs) _scale = maxs; if (_scale < mins) _scale = mins; + r = FFI.cursorModel.getVisibleRect(); + final px1 = (offset.dx - r.left) * _scale; + final py1 = (offset.dy - r.top) * _scale; + _x -= px1 - px0; + _y -= py1 - py0; notifyListeners(); } @@ -238,10 +248,11 @@ class CursorModel with ChangeNotifier { ui.Image get image => _image; double get x => _x - _displayOriginX; double get y => _y - _displayOriginY; + Offset get offset => Offset(_x, _y); double get hotx => _hotx; double get hoty => _hoty; - // physical display coordinate + // remote physical display coordinate Rect getVisibleRect() { final size = MediaQueryData.fromWindow(ui.window).size; final xoffset = FFI.canvasModel.x; @@ -255,8 +266,10 @@ class CursorModel with ChangeNotifier { double adjustForKeyboard() { var keyboardHeight = MediaQueryData.fromWindow(ui.window).viewInsets.bottom; if (keyboardHeight < 100) return 0; - var h = _y - getVisibleRect().top; - return h > 200 ? h - 200 : 0; + final s = FFI.canvasModel.scale; + final thresh = 120; + var h = (_y - getVisibleRect().top) * s; // local physical display height + return h > thresh ? h - thresh : 0; } void updatePan(double dx, double dy) { From 114f03f00cce9fc31c404b0d8087e3b442eb03ae Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 25 Nov 2020 16:28:46 +0800 Subject: [PATCH 072/422] keyboard works --- flutter_hbb/lib/model.dart | 44 ++++-- flutter_hbb/lib/remote_page.dart | 234 +++++++++++++++++++++++-------- 2 files changed, 206 insertions(+), 72 deletions(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 9584c056c..c65784b21 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -395,6 +395,10 @@ class FFI { static F4 _freeRgba; static F5 _getRgba; static Pointer _lastRgbaFrame; + static var shift = false; + static var ctrl = false; + static var alt = false; + static var command = false; static final imageModel = ImageModel(); static final ffiModel = FfiModel(); static final cursorModel = CursorModel(); @@ -405,21 +409,38 @@ class FFI { } static void tap() { - FFI.sendMouse('down', 'left'); - FFI.sendMouse('up', 'left'); + sendMouse('down', 'left'); + sendMouse('up', 'left'); + } + + static void resetModifiers() { + shift = ctrl = alt = command = false; + } + + static Map modify(Map evt) { + if (ctrl) evt['ctrl'] = 'true'; + if (shift) evt['shift'] = 'true'; + if (alt) evt['alt'] = 'true'; + if (command) evt['command'] = 'true'; + return evt; } static void sendMouse(String type, String buttons) { - if (!FFI.ffiModel.keyboard()) return; - FFI.setByName( - 'send_mouse', json.encode({'type': type, 'buttons': buttons})); + if (!ffiModel.keyboard()) return; + setByName( + 'send_mouse', json.encode(modify({'type': type, 'buttons': buttons}))); + } + + static void inputKey(String name) { + if (!ffiModel.keyboard()) return; + setByName('input_key', json.encode(modify({'name': name}))); } static void moveMouse(double x, double y) { - if (!FFI.ffiModel.keyboard()) return; + if (!ffiModel.keyboard()) return; var x2 = x.toInt(); var y2 = y.toInt(); - FFI.setByName('send_mouse', json.encode({'x': '$x2', 'y': '$y2'})); + setByName('send_mouse', json.encode(modify({'x': '$x2', 'y': '$y2'}))); } static List peers() { @@ -475,10 +496,11 @@ class FFI { static void close() { setByName('close', ''); - FFI.imageModel.update(null); - FFI.cursorModel.clear(); - FFI.ffiModel.clear(); - FFI.canvasModel.clear(); + imageModel.update(null); + cursorModel.clear(); + ffiModel.clear(); + canvasModel.clear(); + resetModifiers(); } static void setByName(String name, [String value = '']) { diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 77e009b31..f82cef032 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -22,17 +22,17 @@ class _RemotePageState extends State { Timer _interval; bool _showBar = true; double _bottom = 0; + String _value = ''; double _xOffset = 0; double _yOffset = 0; double _scale = 1; bool _mouseTools = false; - var _shift = false; - var _ctrl = false; - var _alt = false; - var _command = false; var _drag = false; var _right = false; var _scroll = false; + var _arrows = false; + var _more = false; + var _fn = false; final FocusNode _focusNode = FocusNode(); @override @@ -59,12 +59,19 @@ class _RemotePageState extends State { Wakelock.disable(); } + void resetTool() { + _scroll = _drag = _right = false; + FFI.resetModifiers(); + } + void interval() { var v = MediaQuery.of(context).viewInsets.bottom; if (v != _bottom) { + resetTool(); + _value = ''; setState(() { _bottom = v; - if (v < 80) { + if (v < 100) { SystemChrome.setEnabledSystemUIOverlays([]); } }); @@ -147,8 +154,7 @@ class _RemotePageState extends State { onPressed: () { setState(() { _mouseTools = !_mouseTools; - _command = _ctrl = _shift = - _alt = _scroll = _drag = _right = false; + resetTool(); }); }, )), @@ -214,7 +220,17 @@ class _RemotePageState extends State { focusNode: _focusNode, maxLines: null, keyboardType: TextInputType.multiline, - onChanged: (x) => print('$x'), + onChanged: (x) { + var char = x[x.length - 1]; + if (x.length < _value.length) { + char = 'VK_BACK'; + } else if (char == '\n') { + char = 'VK_RETURN'; + } + if (char != '' && char != null) + FFI.inputKey(char); + _value = x; + }, ), ), ])), @@ -227,11 +243,12 @@ class _RemotePageState extends State { } Widget getHelpTools() { - if (!_mouseTools) { + final keyboard = _bottom >= 100; + if (!_mouseTools && !keyboard) { return SizedBox(); } - var textStyle = TextStyle(color: Colors.white, fontSize: 11); - var wrap = (String text, void Function() onPressed, [bool active]) { + var wrap = + (String text, void Function() onPressed, [bool active, IconData icon]) { return ButtonTheme( padding: EdgeInsets.symmetric( vertical: 6, horizontal: 12), //adds padding inside the button @@ -244,62 +261,157 @@ class _RemotePageState extends State { borderRadius: BorderRadius.circular(5.0), ), color: active == true ? MyTheme.accent50 : null, - child: Text(text, style: textStyle), + child: icon != null + ? Icon(icon, color: Colors.white) + : Text(text, + style: TextStyle(color: Colors.white, fontSize: 11)), onPressed: onPressed)); }; - return Stack(children: [ - SizedBox( - height: 80, - child: Container(child: null, color: Color(0x66000000)), - ), - Container( - padding: const EdgeInsets.only(top: 20, left: 8, right: 8), - child: Wrap( - spacing: 5, - runSpacing: 5, - children: [ - wrap('Drag', () { + final mouse = [ + wrap('Drag', () { + setState(() { + _drag = !_drag; + if (_drag) { + _scroll = false; + _right = false; + } + }); + }, _drag), + wrap('Scroll', () { + setState(() { + _scroll = !_scroll; + if (_drag) { + _drag = false; + _right = false; + } + }); + }, _scroll), + wrap('Right', () { + setState(() { + _right = !_right; + if (_drag) { + _scroll = false; + _drag = false; + } + }); + }, _right) + ]; + final modifiers = [ + wrap('Ctrl', () { + setState(() => FFI.ctrl = !FFI.ctrl); + }, FFI.ctrl), + wrap('Alt', () { + setState(() => FFI.alt = !FFI.alt); + }, FFI.alt), + wrap('Shift', () { + setState(() => FFI.shift = !FFI.shift); + }, FFI.shift), + wrap('Command', () { + setState(() => FFI.command = !FFI.command); + }, FFI.command), + ]; + final keys = [ + wrap( + 'Arrows', + () => setState(() { setState(() { - _drag = !_drag; - if (_drag) { - _scroll = false; - _right = false; + _arrows = !_arrows; + if (_arrows) { + _fn = false; + _more = false; } }); - }, _drag), - wrap('Scroll', () { - setState(() { - _scroll = !_scroll; - if (_drag) { - _drag = false; - _right = false; + }), + _arrows), + wrap( + 'Fn', + () => setState( + () { + _fn = !_fn; + if (_fn) { + _arrows = false; + _more = false; } - }); - }, _scroll), - wrap('Right', () { - setState(() { - _right = !_right; - if (_drag) { - _scroll = false; - _drag = false; + }, + ), + _fn), + wrap( + 'More', + () => setState( + () { + _more = !_more; + if (_more) { + _arrows = false; + _fn = false; } - }); - }, _right), - wrap('Ctrl', () { - setState(() => _ctrl = !_ctrl); - }, _ctrl), - wrap('Alt', () { - setState(() => _alt = !_alt); - }, _alt), - wrap('Shift', () { - setState(() => _shift = !_shift); - }, _shift), - wrap('Command', () { - setState(() => _command = !_command); - }, _command), - ], - )) - ]); + }, + ), + _more), + ]; + final arrows = [ + SizedBox(width: 9999), + wrap('', () { + FFI.inputKey('VK_LEFT'); + }, false, Icons.keyboard_arrow_left), + wrap('', () { + FFI.inputKey('VK_UP'); + }, false, Icons.keyboard_arrow_up), + wrap('', () { + FFI.inputKey('VK_DOWN'); + }, false, Icons.keyboard_arrow_down), + wrap('', () { + FFI.inputKey('VK_RIGHT'); + }, false, Icons.keyboard_arrow_right), + ]; + final fn = [ + SizedBox(width: 9999), + ]; + for (var i = 1; i <= 12; ++i) { + final name = 'F' + i.toString(); + fn.add(wrap(name, () { + FFI.inputKey('VK_' + name); + })); + } + final more = [ + SizedBox(width: 9999), + wrap('Esc', () { + FFI.inputKey('VK_ESCAPE'); + }), + wrap('Tab', () { + FFI.inputKey('VK_TAB'); + }), + wrap('Home', () { + FFI.inputKey('VK_HOME'); + }), + wrap('End', () { + FFI.inputKey('VK_END'); + }), + wrap('Del', () { + FFI.inputKey('VK_DELETE'); + }), + wrap('PeUp', () { + FFI.inputKey('VK_PRIOR'); + }), + wrap('PgDown', () { + FFI.inputKey('VK_NEXT'); + }), + ]; + return Container( + color: Color(0x77000000), + padding: EdgeInsets.only( + top: keyboard ? 24 : 4, left: 8, right: 8, bottom: 8), + child: Wrap( + spacing: 4, + runSpacing: 4, + children: [SizedBox(width: 9999)] + + (keyboard + ? modifiers + + keys + + (_arrows ? arrows : []) + + (_fn ? fn : []) + + (_more ? more : []) + : mouse + modifiers), + )); } } @@ -514,7 +626,7 @@ void showActions(BuildContext context) { child: Text('Insert Ctrl + Alt + Del'), value: 'cad'), PopupMenuItem(child: Text('Insert Lock'), value: 'lock'), ], - elevation: 8.0, + elevation: 8, ); if (value == 'cad') { FFI.setByName('ctrl_alt_del'); From 87c339d884ca1a916ee969a531b2c95dca1846f1 Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 25 Nov 2020 17:02:27 +0800 Subject: [PATCH 073/422] scroll/drag/right --- flutter_hbb/lib/model.dart | 13 ++++++++++--- flutter_hbb/lib/remote_page.dart | 23 ++++++++++++++++------- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index c65784b21..e61f1f283 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -408,9 +408,16 @@ class FFI { return getByName('remote_id'); } - static void tap() { - sendMouse('down', 'left'); - sendMouse('up', 'left'); + static void tap(bool right) { + sendMouse('down', right ? 'right' : 'left'); + sendMouse('up', right ? 'right' : 'left'); + } + + static void scroll(double y) { + var y2 = y.round(); + if (y2 == 0) return; + setByName('send_mouse', + json.encode(modify({'type': 'wheel', 'y': y2.toString()}))); } static void resetModifiers() { diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index f82cef032..b98029da6 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -94,9 +94,6 @@ class _RemotePageState extends State { @override Widget build(BuildContext context) { - final size = MediaQueryData.fromWindow(ui.window).size; - print('$size'); - print('${MediaQuery.of(context).size}'); EasyLoading.instance.loadingStyle = EasyLoadingStyle.light; return WillPopScope( onWillPop: () async { @@ -178,12 +175,16 @@ class _RemotePageState extends State { : null, body: GestureDetector( onTap: () { - FFI.tap(); + if (_drag || _scroll) return; + FFI.tap(_right); }, onScaleStart: (details) { _scale = 1; _xOffset = details.focalPoint.dx; _yOffset = details.focalPoint.dy; + if (_drag) { + FFI.sendMouse('down', 'left'); + } }, onScaleUpdate: (details) { var scale = details.scale; @@ -192,15 +193,23 @@ class _RemotePageState extends State { var y = details.focalPoint.dy; var dx = x - _xOffset; var dy = y - _yOffset; - FFI.cursorModel.updatePan(dx, dy); + if (_scroll) { + FFI.scroll(-dy); + } else { + FFI.cursorModel.updatePan(dx, dy); + } _xOffset = x; _yOffset = y; - } else { + } else if (!_drag && !_scroll) { FFI.canvasModel.updateScale(scale / _scale); _scale = scale; } }, - onScaleEnd: (_) {}, + onScaleEnd: (_) { + if (_drag) { + FFI.sendMouse('up', 'left'); + } + }, child: FlutterEasyLoading( child: Container( color: MyTheme.canvasColor, From 57e271cc40473cc252ac503826d946f50de3c870 Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 25 Nov 2020 18:33:09 +0800 Subject: [PATCH 074/422] peers --- flutter_hbb/assets/linux.png | Bin 0 -> 4119 bytes flutter_hbb/assets/mac.png | Bin 0 -> 3931 bytes flutter_hbb/assets/win.png | Bin 0 -> 1797 bytes flutter_hbb/lib/common.dart | 8 ++++ flutter_hbb/lib/home_page.dart | 65 ++++++++++++++++++++++++++++++--- flutter_hbb/lib/model.dart | 5 ++- 6 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 flutter_hbb/assets/linux.png create mode 100644 flutter_hbb/assets/mac.png create mode 100644 flutter_hbb/assets/win.png diff --git a/flutter_hbb/assets/linux.png b/flutter_hbb/assets/linux.png new file mode 100644 index 0000000000000000000000000000000000000000..19813114233fd8af32501caa0f224637353158d9 GIT binary patch literal 4119 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 literal 0 HcmV?d00001 diff --git a/flutter_hbb/assets/mac.png b/flutter_hbb/assets/mac.png new file mode 100644 index 0000000000000000000000000000000000000000..e0d71d8d3896bc6d1b4c9b095703f35238ebaf97 GIT binary patch literal 3931 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 literal 0 HcmV?d00001 diff --git a/flutter_hbb/assets/win.png b/flutter_hbb/assets/win.png new file mode 100644 index 0000000000000000000000000000000000000000..155f4e75d81098bf8925bfa3fa073e3dcf8e343f GIT binary patch literal 1797 zcmeAS@N?(olHy`uVBq!ia0y~yU_8LUz{J483=~oN?UMnd&IkB}xB}__!w9VCnCA}k zl1oXDUoZm;D;qlpm!OoioRX%tuCa}si*G<^SWH}eVrp7`ad~w^Q%lFhnTwV#U$tiQ z?jt8oojHH??z7i#-+%b{>GPMb-+ur3`)|stV~2s}F(!GtySV(^GUFfv1M3t|7srr_ zTW{|i^pkNHX}idj_9{%&_JOO}L?wM?*UJCyC%G$xRVV6JHYPXArXFN@XfR2&=i{AM zpLK6mtVn*wUiy8ClIJ894792A5yvF4wS66I26y}KbX?5pd8cj=nXc=$x^UxHv00za z8tM7vCiv|Xuj@P+CjCAAT>iq7>3j*FW-`yHdB&hN-?9P7eGs@M_w7Vuc7va17=7%D z877~XIgmbY=iQse|7J7v*FR&ZbKbdci&SmR{&0p5KY;3k&&A}PE(WSnpR@CHvb90O zarh!0-ijJU~`ows9)`83O>YrWX+_ARs-t!qdr!t;1FJ8^q zBVXn6J~cE~?N{6_eeFNpFSeZUe_$LQE&Kj|%5?q@_l#}UGo&%ysaHCzx;~}hxBcPs z(koILW&=HZKbJ$5x#rK}RSf+O>v~x|jU0@pixvnQMrC);Jk6HzY!j2)>czbV#Wxwg z8*guU?8mmFQ)2Jg`oL1V15;x|=5iQB#a3-8W}nS-;Y=#)og-H7WM}b%n0xlRUOTgt zdCotvo0*K)i@M*`G^FWiKiJ~mbcWC3FmwNFmN_pPk6&f{cO&~qYqJOUkJZK=b5B2F zU=3hk~!~s+ZF#)6!LlDDQn%i|Z z|FhNe*Zb@~Z1rDX>4lO6zlhHLGv{;Z{=bhOUO(oq&2neYnV-AjWf|`5i&0!)y&2>* zLY_c)7sF>%4=~E?vf5|1|EpBtLxoFB|KhwfZ{M2MabvFKgp!7(Q-bpH*2PT^Iv3Rw zmK;9U+vq^;{B`T@O$qYavhT`^!yi?c|E%Yizjx2-nxNox^*&9>2Xm8m%AT-z&t|0f zVBhT7tId~Axtym^%=GT}^E`#umln^p-ks9${a(a>qY0gi_VOoukGweR@QnG+_m7YN zHpU$4RAV-oP|P58!jpmbiA)1q5#ND!X3JY0M^~yd|G6*yx=ukq#`_%Gj{=40?9*&c zMhBXiEAK_9FxwbRxY*WshAl%uoe{|1`kp7n%z@dG{lUV`4!Eh7K` literal 0 HcmV?d00001 diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 469564182..9c93e513c 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -127,3 +127,11 @@ class _PasswordWidgetState extends State { ); } } + +Color str2color(String str, [alpha = 0xFF]) { + var hash = 160 << 16 + 114 << 8 + 91; + for (var i = 0; i < str.length; i += 1) { + hash = str.codeUnitAt(i) + ((hash << 5) - hash); + } + return Color((hash & 0xFFFFFF) | (alpha << 24)); +} diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index 66ca89384..f9446067e 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -33,6 +33,7 @@ class _HomePageState extends State { crossAxisAlignment: CrossAxisAlignment.center, children: [ getSearchBarUI(), + getPeers(), Expanded(child: Container()) ]), padding: const EdgeInsets.fromLTRB(16.0, 0.0, 16.0, 0.0), @@ -41,13 +42,20 @@ class _HomePageState extends State { void onConnect() { var id = _idController.text.trim(); + connect(id); + } + + void connect(String id) { if (id == '') return; - Navigator.push( - context, - MaterialPageRoute( - builder: (BuildContext context) => RemotePage(id: id), - ), - ); + () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => RemotePage(id: id), + ), + ); + setState(() {}); + }(); FocusScopeNode currentFocus = FocusScope.of(context); if (!currentFocus.hasPrimaryFocus) { currentFocus.unfocus(); @@ -133,4 +141,49 @@ class _HomePageState extends State { _idController.dispose(); super.dispose(); } + + Widget getPlatformImage(String platform) { + platform = platform.toLowerCase(); + if (platform == 'osx') + platform = 'mac'; + else if (platform != 'linux') platform = 'win'; + return Image.asset('assets/$platform.png', width: 36, height: 36); + } + + Widget getPeers() { + final cards = []; + var peers = FFI.peers(); + peers.forEach((p) { + cards.add(Card( + child: GestureDetector( + onTap: () => connect('${p.id}'), + onLongPressStart: (details) { + var x = details.globalPosition.dx; + var y = details.globalPosition.dy; + () async { + var value = await showMenu( + context: context, + position: RelativeRect.fromLTRB(x, y, x, y), + items: [ + PopupMenuItem( + child: Text('Remove'), value: 'remove'), + ], + elevation: 8, + ); + if (value == 'remove') { + setState(() => FFI.setByName('remove', '${p.id}')); + } + }(); + }, + child: ListTile( + subtitle: Text('${p.username}@${p.hostname}'), + title: Text('${p.id}'), + leading: Container( + padding: const EdgeInsets.all(6), + child: getPlatformImage('${p.platform}'), + color: str2color('${p.id}${p.platform}', 0x77)), + )))); + }); + return Wrap(children: cards); + } } diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index e61f1f283..58a37de3b 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -389,6 +389,7 @@ class CursorModel with ChangeNotifier { } class FFI { + static String _dir = ''; static F1 _freeCString; static F2 _getByName; static F3 _setByName; @@ -535,8 +536,8 @@ class FFI { _freeRgba = dylib .lookupFunction), F4>('free_rgba'); _getRgba = dylib.lookupFunction('get_rgba'); - final dir = (await getApplicationDocumentsDirectory()).path; - setByName('init', dir); + _dir = (await getApplicationDocumentsDirectory()).path; + setByName('init', _dir); } } From ad9788681b17909b2f6237cd81740ab9117e573f Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 25 Nov 2020 20:18:17 +0800 Subject: [PATCH 075/422] update pubsepc --- flutter_hbb/pubspec.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index 038f1e574..2d6d6af40 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -51,9 +51,8 @@ flutter: 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 + assets: + - assets/ # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware. From d75cdb78116303d3444bdbb00342f0f2ec5c0c98 Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 25 Nov 2020 20:39:08 +0800 Subject: [PATCH 076/422] bug on peer platform for mac os --- flutter_hbb/lib/home_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index f9446067e..62157b6b0 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -144,7 +144,7 @@ class _HomePageState extends State { Widget getPlatformImage(String platform) { platform = platform.toLowerCase(); - if (platform == 'osx') + if (platform == 'mac os') platform = 'mac'; else if (platform != 'linux') platform = 'win'; return Image.asset('assets/$platform.png', width: 36, height: 36); From 7a2d687367361ba1924ac68841fd7d6e8b2f05f8 Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 25 Nov 2020 20:51:32 +0800 Subject: [PATCH 077/422] trick way to make backspace work always --- flutter_hbb/lib/remote_page.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index b98029da6..8b5fcbbea 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -68,7 +68,7 @@ class _RemotePageState extends State { var v = MediaQuery.of(context).viewInsets.bottom; if (v != _bottom) { resetTool(); - _value = ''; + _value = ' ' * 1000; setState(() { _bottom = v; if (v < 100) { @@ -222,16 +222,18 @@ class _RemotePageState extends State { height: 0, child: _bottom < 100 ? Container() - : TextField( + : TextFormField( textInputAction: TextInputAction.newline, autocorrect: false, enableSuggestions: false, focusNode: _focusNode, maxLines: null, + initialValue: + _value, // trick way to make backspace work always keyboardType: TextInputType.multiline, onChanged: (x) { var char = x[x.length - 1]; - if (x.length < _value.length) { + if (x.length <= _value.length) { char = 'VK_BACK'; } else if (char == '\n') { char = 'VK_RETURN'; From 8583b6b140183f02644b853b8243e17e19c7328e Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 25 Nov 2020 21:15:06 +0800 Subject: [PATCH 078/422] long press for drag --- flutter_hbb/lib/remote_page.dart | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 8b5fcbbea..8998d6db2 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -178,6 +178,17 @@ class _RemotePageState extends State { if (_drag || _scroll) return; FFI.tap(_right); }, + onLongPressStart: (_) { + if (_drag) { + // case: to show password on windows + FFI.sendMouse('down', 'left'); + } + }, + onLongPressEnd: (_) { + if (_drag) { + FFI.sendMouse('up', 'left'); + } + }, onScaleStart: (details) { _scale = 1; _xOffset = details.focalPoint.dx; From dfb28e9e5eb3ed208bf3ed41ca1d54c803ef5794 Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 25 Nov 2020 23:52:58 +0800 Subject: [PATCH 079/422] display switch --- flutter_hbb/lib/model.dart | 2 + flutter_hbb/lib/remote_page.dart | 151 +++++++++++++++++++------------ 2 files changed, 96 insertions(+), 57 deletions(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 58a37de3b..cff46961a 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -34,6 +34,7 @@ class FfiModel with ChangeNotifier { get permissions => _permissions; get initialized => _initialized; + get pi => _pi; FfiModel() { clear(); @@ -118,6 +119,7 @@ class FfiModel with ChangeNotifier { _display.width = int.parse(evt['width']); _display.height = int.parse(evt['height']); FFI.cursorModel.updateDisplayOrigin(_display.x, _display.y); + notifyListeners(); } void handlePeerInfo(Map evt) { diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 8998d6db2..19f92074e 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -279,6 +279,7 @@ class _RemotePageState extends State { minWidth: 0, //wraps child's width height: 0, child: FlatButton( + splashColor: Colors.black, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(5.0), ), @@ -568,67 +569,103 @@ void showOptions(BuildContext context) { FFI.getByName('toggle_option', 'lock-after-session-end') == 'true'; String quality = FFI.getByName('image_quality'); if (quality == '') quality = 'balanced'; + var displays = []; + final pi = FFI.ffiModel.pi; + if (pi.displays.length > 1) { + final cur = pi.currentDisplay; + final children = []; + for (var i = 0; i < pi.displays.length; ++i) + children.add(InkWell( + onTap: () { + if (i == cur) return; + FFI.setByName('switch_display', i.toString()); + Navigator.pop(context); + }, + child: Ink( + width: 40, + height: 40, + decoration: BoxDecoration( + border: Border.all(color: Colors.black87), + color: i == cur ? Colors.black87 : Colors.white), + child: Center( + child: Text((i + 1).toString(), + style: TextStyle( + color: i == cur ? Colors.white : Colors.black87)))))); + displays.add(Padding( + padding: const EdgeInsets.only(top: 8), + child: Wrap( + alignment: WrapAlignment.center, + spacing: 8, + children: children, + ))); + displays.add(Divider(color: MyTheme.border)); + } showAlertDialog( context, (setState) => Tuple3( null, - Column(mainAxisSize: MainAxisSize.min, children: [ - RadioListTile( - controlAffinity: ListTileControlAffinity.trailing, - title: const Text('Good image quality'), - value: 'best', - groupValue: quality, - onChanged: (String value) { - setState(() { - quality = value; - FFI.setByName('image_quality', value); - }); - }, - ), - RadioListTile( - controlAffinity: ListTileControlAffinity.trailing, - title: const Text('Balanced'), - value: 'balanced', - groupValue: quality, - onChanged: (String value) { - setState(() { - quality = value; - FFI.setByName('image_quality', value); - }); - }, - ), - RadioListTile( - controlAffinity: ListTileControlAffinity.trailing, - title: const Text('Optimize reaction time'), - value: 'low', - groupValue: quality, - onChanged: (String value) { - setState(() { - quality = value; - FFI.setByName('image_quality', value); - }); - }, - ), - Divider(color: Colors.black), - CheckboxListTile( - value: showRemoteCursor, - onChanged: (v) { - setState(() { - showRemoteCursor = v; - FFI.setByName('toggle_option', 'show-remote-cursor'); - }); - }, - title: Text('Show remote cursor')), - CheckboxListTile( - value: lockAfterSessionEnd, - onChanged: (v) { - setState(() { - lockAfterSessionEnd = v; - FFI.setByName('toggle_option', 'lock-after-session-end'); - }); - }, - title: Text('Lock after session end')) - ]), + Column( + mainAxisSize: MainAxisSize.min, + children: displays + + [ + RadioListTile( + controlAffinity: ListTileControlAffinity.trailing, + title: const Text('Good image quality'), + value: 'best', + groupValue: quality, + onChanged: (String value) { + setState(() { + quality = value; + FFI.setByName('image_quality', value); + }); + }, + ), + RadioListTile( + controlAffinity: ListTileControlAffinity.trailing, + title: const Text('Balanced'), + value: 'balanced', + groupValue: quality, + onChanged: (String value) { + setState(() { + quality = value; + FFI.setByName('image_quality', value); + }); + }, + ), + RadioListTile( + controlAffinity: ListTileControlAffinity.trailing, + title: const Text('Optimize reaction time'), + value: 'low', + groupValue: quality, + onChanged: (String value) { + setState(() { + quality = value; + FFI.setByName('image_quality', value); + }); + }, + ), + Divider(color: MyTheme.border), + CheckboxListTile( + value: showRemoteCursor, + onChanged: (v) { + setState(() { + showRemoteCursor = v; + FFI.setByName( + 'toggle_option', 'show-remote-cursor'); + }); + }, + title: Text('Show remote cursor')), + CheckboxListTile( + value: lockAfterSessionEnd, + onChanged: (v) { + setState(() { + lockAfterSessionEnd = v; + FFI.setByName( + 'toggle_option', 'lock-after-session-end'); + }); + }, + title: Text('Lock after session end')) + ]), null), () async => true, true, From 4724b0111172c80cd73e69589d9c888e6a0c0e2d Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 26 Nov 2020 00:06:57 +0800 Subject: [PATCH 080/422] scroll --- flutter_hbb/lib/home_page.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index 62157b6b0..7edb4add1 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -26,7 +26,7 @@ class _HomePageState extends State { appBar: AppBar( title: Text(widget.title), ), - body: Container( + body: SingleChildScrollView( child: Column( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, @@ -34,7 +34,6 @@ class _HomePageState extends State { children: [ getSearchBarUI(), getPeers(), - Expanded(child: Container()) ]), padding: const EdgeInsets.fromLTRB(16.0, 0.0, 16.0, 0.0), )); From ae6198813cfb59bbefd4910511a4d9a9023d759b Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 26 Nov 2020 00:33:45 +0800 Subject: [PATCH 081/422] working on ID/Relay server settings --- flutter_hbb/lib/home_page.dart | 60 ++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index 7edb4add1..cef284e8e 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:tuple/tuple.dart'; import 'common.dart'; import 'model.dart'; import 'remote_page.dart'; @@ -24,6 +25,27 @@ class _HomePageState extends State { return Scaffold( backgroundColor: MyTheme.grayBg, appBar: AppBar( + centerTitle: true, + actions: [ + IconButton( + icon: Icon(Icons.more_vert), + onPressed: () { + () async { + var value = await showMenu( + context: context, + position: RelativeRect.fromLTRB(3000, 70, 3000, 70), + items: [ + PopupMenuItem( + child: Text('ID/Relay Server'), value: 'server'), + ], + elevation: 8, + ); + if (value == 'server') { + showServer(context); + } + }(); + }) + ], title: Text(widget.title), ), body: SingleChildScrollView( @@ -186,3 +208,41 @@ class _HomePageState extends State { return Wrap(children: cards); } } + +void showServer(BuildContext context) { + showAlertDialog( + context, + (setState) => Tuple3( + Text('ID/Relay Server'), + Column(mainAxisSize: MainAxisSize.min, children: [ + TextFormField( + decoration: InputDecoration( + labelText: 'ID Server', + ), + ), + TextFormField( + decoration: InputDecoration( + labelText: 'Relay Server', + ), + ), + ]), + [ + FlatButton( + textColor: MyTheme.accent, + onPressed: () { + Navigator.pop(context); + }, + child: Text('Cancel'), + ), + FlatButton( + textColor: MyTheme.accent, + onPressed: () { + Navigator.pop(context); + }, + child: Text('OK'), + ), + ], + ), + () async => true, + true); +} From 11796de5d7566bcffa732e18c400704421e21724 Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 26 Nov 2020 00:47:52 +0800 Subject: [PATCH 082/422] try form --- flutter_hbb/lib/home_page.dart | 53 +++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index cef284e8e..8dcb57cc2 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -210,22 +210,46 @@ class _HomePageState extends State { } void showServer(BuildContext context) { + final formKey = GlobalKey(); + var id = ''; + var relay = ''; showAlertDialog( context, (setState) => Tuple3( Text('ID/Relay Server'), - Column(mainAxisSize: MainAxisSize.min, children: [ - TextFormField( - decoration: InputDecoration( - labelText: 'ID Server', - ), - ), - TextFormField( - decoration: InputDecoration( - labelText: 'Relay Server', - ), - ), - ]), + Form( + key: formKey, + child: + Column(mainAxisSize: MainAxisSize.min, children: [ + TextFormField( + decoration: InputDecoration( + labelText: 'ID Server', + ), + validator: (value) { + if (value.isEmpty) { + return 'Please enter valid server address'; + } + return null; + }, + onSaved: (String value) { + id = value; + }, + ), + TextFormField( + decoration: InputDecoration( + labelText: 'Relay Server', + ), + validator: (value) { + if (value.isEmpty) { + return 'Please enter valid server address'; + } + return null; + }, + onSaved: (String value) { + relay = value; + }, + ), + ])), [ FlatButton( textColor: MyTheme.accent, @@ -237,7 +261,10 @@ void showServer(BuildContext context) { FlatButton( textColor: MyTheme.accent, onPressed: () { - Navigator.pop(context); + if (formKey.currentState.validate()) { + formKey.currentState.save(); + Navigator.pop(context); + } }, child: Text('OK'), ), From 44131cd2a8f1b34498aec0b1ab5b35285cdb4ff8 Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 26 Nov 2020 15:40:00 +0800 Subject: [PATCH 083/422] audio ffi --- flutter_hbb/lib/model.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index cff46961a..17ddbc000 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -87,6 +87,8 @@ class FfiModel with ChangeNotifier { pos = evt; } else if (name == 'permission') { FFI.ffiModel.updatePermission(evt); + } else if (name == "audio_format") { + // } } if (pos != null) FFI.cursorModel.updateCursorPosition(pos); @@ -397,6 +399,7 @@ class FFI { static F3 _setByName; static F4 _freeRgba; static F5 _getRgba; + static F5 _getAudio; static Pointer _lastRgbaFrame; static var shift = false; static var ctrl = false; @@ -538,6 +541,7 @@ class FFI { _freeRgba = dylib .lookupFunction), F4>('free_rgba'); _getRgba = dylib.lookupFunction('get_rgba'); + _getAudio = dylib.lookupFunction('get_audio'); _dir = (await getApplicationDocumentsDirectory()).path; setByName('init', _dir); } From 608e9fc6f16f49e7c84794daff0b5700e7375b25 Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 26 Nov 2020 20:28:37 +0800 Subject: [PATCH 084/422] fix on shrink/maximize/fullscreen and flutter audio (not work well), to-do: how about fullscreen closed restore --- .../src/main/jniLibs/arm64-v8a/librustdesk.so | 1 - .../src/main/jniLibs/x86_64/librustdesk.so | 1 - flutter_hbb/lib/home_page.dart | 37 +++++---- flutter_hbb/lib/model.dart | 51 ++++++++++-- flutter_hbb/pubspec.lock | 77 +++++++++++++++++++ flutter_hbb/pubspec.yaml | 2 + 6 files changed, 146 insertions(+), 23 deletions(-) delete mode 120000 flutter_hbb/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so delete mode 120000 flutter_hbb/android/app/src/main/jniLibs/x86_64/librustdesk.so diff --git a/flutter_hbb/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so b/flutter_hbb/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so deleted file mode 120000 index 0684cadc9..000000000 --- a/flutter_hbb/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../target/aarch64-linux-android/release/librustdesk.so \ No newline at end of file diff --git a/flutter_hbb/android/app/src/main/jniLibs/x86_64/librustdesk.so b/flutter_hbb/android/app/src/main/jniLibs/x86_64/librustdesk.so deleted file mode 120000 index a4e9274f6..000000000 --- a/flutter_hbb/android/app/src/main/jniLibs/x86_64/librustdesk.so +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../target/x86_64-linux-android/release/librustdesk.so \ No newline at end of file diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index 8dcb57cc2..1de6ee6af 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -172,6 +172,9 @@ class _HomePageState extends State { } Widget getPeers() { + if (!FFI.ffiModel.initialized) { + return Container(); + } final cards = []; var peers = FFI.peers(); peers.forEach((p) { @@ -211,6 +214,8 @@ class _HomePageState extends State { void showServer(BuildContext context) { final formKey = GlobalKey(); + final id0 = FFI.getByName('custom-rendezvous-server'); + final relay0 = FFI.getByName('relay-server'); var id = ''; var relay = ''; showAlertDialog( @@ -222,31 +227,23 @@ void showServer(BuildContext context) { child: Column(mainAxisSize: MainAxisSize.min, children: [ TextFormField( + initialValue: id0, decoration: InputDecoration( labelText: 'ID Server', ), - validator: (value) { - if (value.isEmpty) { - return 'Please enter valid server address'; - } - return null; - }, + validator: validate, onSaved: (String value) { - id = value; + id = value.trim(); }, ), TextFormField( + initialValue: relay0, decoration: InputDecoration( labelText: 'Relay Server', ), - validator: (value) { - if (value.isEmpty) { - return 'Please enter valid server address'; - } - return null; - }, + validator: validate, onSaved: (String value) { - relay = value; + relay = value.trim(); }, ), ])), @@ -263,6 +260,9 @@ void showServer(BuildContext context) { onPressed: () { if (formKey.currentState.validate()) { formKey.currentState.save(); + if (id != id0) + FFI.setByName('custom-rendezvous-server', id); + if (relay != relay0) FFI.setByName('relay-server', relay); Navigator.pop(context); } }, @@ -273,3 +273,12 @@ void showServer(BuildContext context) { () async => true, true); } + +String validate(value) { + value = value.trim(); + if (value.isEmpty) { + return null; + } + final res = FFI.getByName('test_if_valid_server', value); + return res.isEmpty ? null : res; +} diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 17ddbc000..ef755bd14 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -1,6 +1,8 @@ import 'package:ffi/ffi.dart'; import 'package:flutter/gestures.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:flutter_sound/flutter_sound.dart'; +import 'package:device_info/device_info.dart'; import 'dart:io'; import 'dart:math'; import 'dart:ffi'; @@ -31,6 +33,7 @@ class FfiModel with ChangeNotifier { bool _waitForImage; bool _initialized = false; final _permissions = Map(); + final _audioPlayer = FlutterSoundPlayer(); get permissions => _permissions; get initialized => _initialized; @@ -40,6 +43,7 @@ class FfiModel with ChangeNotifier { clear(); () async { await FFI.init(); + await _audioPlayer.openAudioSession(); _initialized = true; notifyListeners(); }(); @@ -63,11 +67,17 @@ class FfiModel with ChangeNotifier { _permissions.clear(); } - void update( + Future stopAudio() async { + final st = await _audioPlayer.getPlayerState(); + if (st != PlayerState.isPlaying) return; + await _audioPlayer.stopPlayer(); + } + + Future update( String id, BuildContext context, void Function(Map evt, String id, BuildContext context) - handleMsgbox) { + handleMsgbox) async { var pos; for (;;) { var evt = FFI.popEvent(); @@ -88,7 +98,15 @@ class FfiModel with ChangeNotifier { } else if (name == 'permission') { FFI.ffiModel.updatePermission(evt); } else if (name == "audio_format") { - // + try { + var s = int.parse(evt['sample_rate']); + var c = int.parse(evt['channels']); + await stopAudio(); + await _audioPlayer.startPlayerFromStream( + codec: Codec.opusWebM, numChannels: c, sampleRate: s); + } catch (e) { + print('audio_format: $e'); + } } } if (pos != null) FFI.cursorModel.updateCursorPosition(pos); @@ -108,9 +126,22 @@ class FfiModel with ChangeNotifier { try { // my throw exception, because the listener maybe already dispose FFI.imageModel.update(image); - } catch (e) {} + } catch (e) { + print('update image: $e'); + } }); } + var frame = FFI._getAudio(); + if (frame != null && frame != nullptr) { + final ref = frame.ref; + final bytes = Uint8List.sublistView(ref.data.asTypedList(ref.len)); + try { + await _audioPlayer.feedFromStream(bytes); + } catch (e) { + print('play audio frame: $e'); + } + FFI._freeRgba(frame); + } } } @@ -355,7 +386,9 @@ class CursorModel with ChangeNotifier { try { // my throw exception, because the listener maybe already dispose notifyListeners(); - } catch (e) {} + } catch (e) { + print('notify cursor: $e'); + } }); } @@ -465,7 +498,7 @@ class FFI { Peer.fromJson(s[0] as String, s[1] as Map)) .toList(); } catch (e) { - print(e); + print('peers(): $e'); } return []; } @@ -493,7 +526,7 @@ class FFI { Map event = json.decode(s); return event; } catch (e) { - print(e); + print('popEvent(): $e'); } return null; } @@ -543,6 +576,10 @@ class FFI { _getRgba = dylib.lookupFunction('get_rgba'); _getAudio = dylib.lookupFunction('get_audio'); _dir = (await getApplicationDocumentsDirectory()).path; + DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); + AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; + print( + '${androidInfo.product} ${androidInfo.brand} ${androidInfo.device} ${androidInfo.model} ${androidInfo.brand} ${androidInfo.manufacturer}'); setByName('init', _dir); } } diff --git a/flutter_hbb/pubspec.lock b/flutter_hbb/pubspec.lock index 4a14ca263..f65d572b2 100644 --- a/flutter_hbb/pubspec.lock +++ b/flutter_hbb/pubspec.lock @@ -43,6 +43,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.15.0-nullsafety.3" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.5" csslib: dependency: transitive description: @@ -57,6 +71,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.0" + device_info: + dependency: "direct main" + description: + name: device_info + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + device_info_platform_interface: + dependency: transitive + description: + name: device_info_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" fake_async: dependency: transitive description: @@ -90,6 +118,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.0" + flutter_sound: + dependency: "direct main" + description: + name: flutter_sound + url: "https://pub.dartlang.org" + source: hosted + version: "6.4.2+1" + flutter_sound_platform_interface: + dependency: transitive + description: + name: flutter_sound_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "6.4.2+1" + flutter_sound_web: + dependency: transitive + description: + name: flutter_sound_web + url: "https://pub.dartlang.org" + source: hosted + version: "6.4.2+1" flutter_spinkit: dependency: transitive description: @@ -135,6 +184,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.6.2" + logger: + dependency: transitive + description: + name: logger + url: "https://pub.dartlang.org" + source: hosted + version: "0.7.0+2" matcher: dependency: transitive description: @@ -233,6 +289,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.5" + recase: + dependency: transitive + description: + name: recase + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" sky_engine: dependency: transitive description: flutter @@ -266,6 +329,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0-nullsafety.1" + synchronized: + dependency: transitive + description: + name: synchronized + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0+2" term_glyph: dependency: transitive description: @@ -294,6 +364,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.0-nullsafety.3" + uuid: + dependency: transitive + description: + name: uuid + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.2" vector_math: dependency: transitive description: diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index 2d6d6af40..89e034813 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -34,6 +34,8 @@ dependencies: flutter_easyloading: ^2.1.3 tuple: ^1.0.1 wakelock: ^0.2.1+1 + flutter_sound: ^6.4.2+1 + device_info: ^1.0.0 dev_dependencies: flutter_test: From b311ce869287dff522557a9dfc014ad467dbdfb1 Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 26 Nov 2020 20:38:33 +0800 Subject: [PATCH 085/422] ignore jniLibs --- flutter_hbb/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/flutter_hbb/.gitignore b/flutter_hbb/.gitignore index 9d532b18a..eea406451 100644 --- a/flutter_hbb/.gitignore +++ b/flutter_hbb/.gitignore @@ -39,3 +39,4 @@ app.*.symbols # Obfuscation related app.*.map.json +jniLibs From 43452f48d91e4939b988ff3e29a1babfee9fd0f2 Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 26 Nov 2020 21:41:25 +0800 Subject: [PATCH 086/422] close msgbox if loading --- flutter_hbb/lib/common.dart | 5 ++++- flutter_hbb/lib/model.dart | 12 +++++++----- flutter_hbb/lib/remote_page.dart | 4 ++-- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 9c93e513c..cf07bdf6f 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -13,7 +13,10 @@ class MyTheme { static const Color border = Color(0xFFCCCCCC); } -void showLoading(String text) { +void showLoading(String text, BuildContext context) { + if (_hasDialog) { + Navigator.pop(context); + } dismissLoading(); EasyLoading.show(status: text); } diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index ef755bd14..b3ef083c1 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -86,7 +86,7 @@ class FfiModel with ChangeNotifier { if (name == 'msgbox') { handleMsgbox(evt, id, context); } else if (name == 'peer_info') { - handlePeerInfo(evt); + handlePeerInfo(evt, context); } else if (name == 'switch_display') { handleSwitchDisplay(evt); } else if (name == 'cursor_data') { @@ -155,7 +155,7 @@ class FfiModel with ChangeNotifier { notifyListeners(); } - void handlePeerInfo(Map evt) { + void handlePeerInfo(Map evt, BuildContext context) { dismissLoading(); _pi.username = evt['username']; _pi.hostname = evt['hostname']; @@ -178,7 +178,7 @@ class FfiModel with ChangeNotifier { FFI.cursorModel.updateDisplayOrigin(_display.x, _display.y); } if (displays.length > 0) { - showLoading('Waiting for image...'); + showLoading('Waiting for image...', context); _waitForImage = true; } } @@ -578,8 +578,10 @@ class FFI { _dir = (await getApplicationDocumentsDirectory()).path; DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; - print( - '${androidInfo.product} ${androidInfo.brand} ${androidInfo.device} ${androidInfo.model} ${androidInfo.brand} ${androidInfo.manufacturer}'); + final name = '${androidInfo.brand}-${androidInfo.model}'; + final id = androidInfo.id; + setByName('info1', id); + setByName('info2', name); setByName('init', _dir); } } diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 19f92074e..641c5fddb 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -41,7 +41,7 @@ class _RemotePageState extends State { FFI.connect(widget.id); WidgetsBinding.instance.addPostFrameCallback((_) { SystemChrome.setEnabledSystemUIOverlays([]); - showLoading('Connecting...'); + showLoading('Connecting...', context); _interval = Timer.periodic(Duration(milliseconds: 30), (timer) => interval()); }); @@ -530,7 +530,7 @@ void enterPasswordDialog(String id, BuildContext context) { var text = controller.text.trim(); if (text == '') return; FFI.login(text, remember); - showLoading('Logging in...'); + showLoading('Logging in...', context); Navigator.pop(context); }, child: Text('OK'), From 2a9ff10eae892627b2f5bc84fee9016d7adfb776 Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 26 Nov 2020 22:48:15 +0800 Subject: [PATCH 087/422] fix on close alert in loading --- flutter_hbb/lib/common.dart | 2 +- flutter_hbb/lib/model.dart | 6 ++++-- flutter_hbb/lib/remote_page.dart | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index cf07bdf6f..0aa15cdf0 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -14,7 +14,7 @@ class MyTheme { } void showLoading(String text, BuildContext context) { - if (_hasDialog) { + if (_hasDialog && context != null) { Navigator.pop(context); } dismissLoading(); diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index b3ef083c1..cfc937e3c 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -100,10 +100,12 @@ class FfiModel with ChangeNotifier { } else if (name == "audio_format") { try { var s = int.parse(evt['sample_rate']); - var c = int.parse(evt['channels']); + // var c = int.parse(evt['channels']); + // Flutter Sound does not support Floating Point PCM data, nor records with more that one audio channel. + // On Flutter Sound, Raw PCM is only PCM INT-Linerar 16 monophony await stopAudio(); await _audioPlayer.startPlayerFromStream( - codec: Codec.opusWebM, numChannels: c, sampleRate: s); + codec: Codec.pcm16, numChannels: 1, sampleRate: s); } catch (e) { print('audio_format: $e'); } diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 641c5fddb..6b361e85b 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -530,7 +530,7 @@ void enterPasswordDialog(String id, BuildContext context) { var text = controller.text.trim(); if (text == '') return; FFI.login(text, remember); - showLoading('Logging in...', context); + showLoading('Logging in...', null); Navigator.pop(context); }, child: Text('OK'), From f8903877e261fedcb57ce1d9fb56d48f14d2cbe2 Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 27 Nov 2020 02:02:41 +0800 Subject: [PATCH 088/422] better input --- flutter_hbb/lib/remote_page.dart | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 6b361e85b..926188c7d 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -243,14 +243,19 @@ class _RemotePageState extends State { _value, // trick way to make backspace work always keyboardType: TextInputType.multiline, onChanged: (x) { - var char = x[x.length - 1]; if (x.length <= _value.length) { - char = 'VK_BACK'; - } else if (char == '\n') { - char = 'VK_RETURN'; - } - if (char != '' && char != null) + final char = 'VK_BACK'; FFI.inputKey(char); + } + for (var i = _value.length; + i < x.length; + ++i) { + var char = x[i]; + if (char == '\n') { + char = 'VK_RETURN'; + } + FFI.inputKey(char); + } _value = x; }, ), From 00358b456d72a9b500b315fd8919d325cbabf247 Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 27 Nov 2020 02:14:27 +0800 Subject: [PATCH 089/422] adjust color and padding per my phone --- flutter_hbb/lib/common.dart | 1 + flutter_hbb/lib/remote_page.dart | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 0aa15cdf0..3751a8d68 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -9,6 +9,7 @@ class MyTheme { static const Color white = Color(0xFFFFFFFF); static const Color accent = Color(0xFF0071FF); static const Color accent50 = Color(0x770071FF); + static const Color accent80 = Color(0xAA0071FF); static const Color canvasColor = Color(0xFF212121); static const Color border = Color(0xFFCCCCCC); } diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 926188c7d..b883dba5e 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -278,7 +278,7 @@ class _RemotePageState extends State { (String text, void Function() onPressed, [bool active, IconData icon]) { return ButtonTheme( padding: EdgeInsets.symmetric( - vertical: 6, horizontal: 12), //adds padding inside the button + vertical: 6, horizontal: 11), //adds padding inside the button materialTapTargetSize: MaterialTapTargetSize .shrinkWrap, //limits the touch area to the button area minWidth: 0, //wraps child's width @@ -288,7 +288,7 @@ class _RemotePageState extends State { shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(5.0), ), - color: active == true ? MyTheme.accent50 : null, + color: active == true ? MyTheme.accent80 : null, child: icon != null ? Icon(icon, color: Colors.white) : Text(text, @@ -334,7 +334,7 @@ class _RemotePageState extends State { wrap('Shift', () { setState(() => FFI.shift = !FFI.shift); }, FFI.shift), - wrap('Command', () { + wrap('Cmd', () { setState(() => FFI.command = !FFI.command); }, FFI.command), ]; @@ -364,7 +364,7 @@ class _RemotePageState extends State { ), _fn), wrap( - 'More', + '...', () => setState( () { _more = !_more; @@ -417,7 +417,7 @@ class _RemotePageState extends State { wrap('Del', () { FFI.inputKey('VK_DELETE'); }), - wrap('PeUp', () { + wrap('PgUp', () { FFI.inputKey('VK_PRIOR'); }), wrap('PgDown', () { @@ -425,7 +425,7 @@ class _RemotePageState extends State { }), ]; return Container( - color: Color(0x77000000), + color: Color(0xAA000000), padding: EdgeInsets.only( top: keyboard ? 24 : 4, left: 8, right: 8, bottom: 8), child: Wrap( From 8edabe654f4b5c7512002c59455a75e189eafe81 Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 27 Nov 2020 10:52:09 +0800 Subject: [PATCH 090/422] disable sound due to crash --- flutter_hbb/android/app/build.gradle | 2 +- flutter_hbb/lib/model.dart | 44 ++++++++++++++++------------ 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/flutter_hbb/android/app/build.gradle b/flutter_hbb/android/app/build.gradle index 76b52c785..b965b9517 100644 --- a/flutter_hbb/android/app/build.gradle +++ b/flutter_hbb/android/app/build.gradle @@ -39,7 +39,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.carriez.flutter_hbb" - minSdkVersion 16 + minSdkVersion 18 targetSdkVersion 29 versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index cfc937e3c..b97cc9aa1 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -73,11 +73,11 @@ class FfiModel with ChangeNotifier { await _audioPlayer.stopPlayer(); } - Future update( + void update( String id, BuildContext context, void Function(Map evt, String id, BuildContext context) - handleMsgbox) async { + handleMsgbox) { var pos; for (;;) { var evt = FFI.popEvent(); @@ -98,17 +98,19 @@ class FfiModel with ChangeNotifier { } else if (name == 'permission') { FFI.ffiModel.updatePermission(evt); } else if (name == "audio_format") { - try { - var s = int.parse(evt['sample_rate']); - // var c = int.parse(evt['channels']); - // Flutter Sound does not support Floating Point PCM data, nor records with more that one audio channel. - // On Flutter Sound, Raw PCM is only PCM INT-Linerar 16 monophony - await stopAudio(); - await _audioPlayer.startPlayerFromStream( - codec: Codec.pcm16, numChannels: 1, sampleRate: s); - } catch (e) { - print('audio_format: $e'); - } + () async { + try { + var s = int.parse(evt['sample_rate']); + // var c = int.parse(evt['channels']); + // Flutter Sound does not support Floating Point PCM data, nor records with more that one audio channel. + // On Flutter Sound, Raw PCM is only PCM INT-Linerar 16 monophony + await stopAudio(); + await _audioPlayer.startPlayerFromStream( + codec: Codec.pcm16, numChannels: 1, sampleRate: s); + } catch (e) { + print('audio_format: $e'); + } + }(); } } if (pos != null) FFI.cursorModel.updateCursorPosition(pos); @@ -133,17 +135,19 @@ class FfiModel with ChangeNotifier { } }); } - var frame = FFI._getAudio(); - if (frame != null && frame != nullptr) { - final ref = frame.ref; - final bytes = Uint8List.sublistView(ref.data.asTypedList(ref.len)); + } + var frame = FFI._getAudio(); + if (frame != null && frame != nullptr) { + final ref = frame.ref; + final bytes = Uint8List.sublistView(ref.data.asTypedList(ref.len)); + () async { try { - await _audioPlayer.feedFromStream(bytes); + // await _audioPlayer.feedFromStream(bytes); } catch (e) { print('play audio frame: $e'); } FFI._freeRgba(frame); - } + }(); } } @@ -248,6 +252,7 @@ class CanvasModel with ChangeNotifier { } void updateScale(double v) { + if (FFI.imageModel.image == null) return; final offset = FFI.cursorModel.offset; var r = FFI.cursorModel.getVisibleRect(); final px0 = (offset.dx - r.left) * _scale; @@ -310,6 +315,7 @@ class CursorModel with ChangeNotifier { } void updatePan(double dx, double dy) { + if (FFI.imageModel.image == null) return; final scale = FFI.canvasModel.scale; dx /= scale; dy /= scale; From 61cbe7454741312cbf043f69e6567c24b348346b Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 27 Nov 2020 11:41:52 +0800 Subject: [PATCH 091/422] remove more audio --- flutter_hbb/lib/model.dart | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index b97cc9aa1..376d15df1 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -100,6 +100,7 @@ class FfiModel with ChangeNotifier { } else if (name == "audio_format") { () async { try { + /* var s = int.parse(evt['sample_rate']); // var c = int.parse(evt['channels']); // Flutter Sound does not support Floating Point PCM data, nor records with more that one audio channel. @@ -107,6 +108,7 @@ class FfiModel with ChangeNotifier { await stopAudio(); await _audioPlayer.startPlayerFromStream( codec: Codec.pcm16, numChannels: 1, sampleRate: s); + */ } catch (e) { print('audio_format: $e'); } @@ -138,11 +140,13 @@ class FfiModel with ChangeNotifier { } var frame = FFI._getAudio(); if (frame != null && frame != nullptr) { - final ref = frame.ref; - final bytes = Uint8List.sublistView(ref.data.asTypedList(ref.len)); () async { try { - // await _audioPlayer.feedFromStream(bytes); + /* + final ref = frame.ref; + final bytes = Uint8List.sublistView(ref.data.asTypedList(ref.len)); + await _audioPlayer.feedFromStream(bytes); + */ } catch (e) { print('play audio frame: $e'); } From af4f772b8abdf7c51c728b5459ba5f875a7770a9 Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 27 Nov 2020 11:56:14 +0800 Subject: [PATCH 092/422] fix keyboard show/hide/show/hide for the first time click on keyboard button after password dialog --- flutter_hbb/lib/remote_page.dart | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index b883dba5e..0d7f2a9c4 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -34,6 +34,7 @@ class _RemotePageState extends State { var _more = false; var _fn = false; final FocusNode _focusNode = FocusNode(); + var _showEdit = true; @override void initState() { @@ -130,11 +131,20 @@ class _RemotePageState extends State { color: Colors.white, icon: Icon(Icons.keyboard), onPressed: () { - SystemChrome.setEnabledSystemUIOverlays( - SystemUiOverlay.values); - _focusNode.requestFocus(); - SystemChannels.textInput - .invokeMethod('TextInput.show'); + // destroy first, so that our _value trick can work + setState(() => _showEdit = false); + Timer(Duration(milliseconds: 30), () { + // show now, and sleep a while to requestFocus to + // make sure edit ready, so that keyboard wont show/hide/show/hide happen + setState(() => _showEdit = true); + Timer(Duration(milliseconds: 30), () { + SystemChrome.setEnabledSystemUIOverlays( + SystemUiOverlay.values); + _focusNode.requestFocus(); + SystemChannels.textInput + .invokeMethod('TextInput.show'); + }); + }); }), IconButton( color: Colors.white, @@ -231,7 +241,7 @@ class _RemotePageState extends State { SizedBox( width: 0, height: 0, - child: _bottom < 100 + child: !_showEdit ? Container() : TextFormField( textInputAction: TextInputAction.newline, From 8902672b307c66edbe77d01be4e1f6d41e78be75 Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 27 Nov 2020 12:05:23 +0800 Subject: [PATCH 093/422] better adjustForKeyboard --- flutter_hbb/lib/model.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 376d15df1..8ed3330bb 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -143,7 +143,7 @@ class FfiModel with ChangeNotifier { () async { try { /* - final ref = frame.ref; + final ref = frame.ref; final bytes = Uint8List.sublistView(ref.data.asTypedList(ref.len)); await _audioPlayer.feedFromStream(bytes); */ @@ -315,7 +315,7 @@ class CursorModel with ChangeNotifier { final s = FFI.canvasModel.scale; final thresh = 120; var h = (_y - getVisibleRect().top) * s; // local physical display height - return h > thresh ? h - thresh : 0; + return h - thresh; } void updatePan(double dx, double dy) { From 2f3a46db1cc340d17715ea8d052ff39cfa5c7309 Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 27 Nov 2020 12:45:05 +0800 Subject: [PATCH 094/422] style --- flutter_hbb/lib/remote_page.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 0d7f2a9c4..37fa62461 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -520,9 +520,11 @@ void enterPasswordDialog(String id, BuildContext context) { Column(mainAxisSize: MainAxisSize.min, children: [ PasswordWidget(controller: controller), CheckboxListTile( + contentPadding: const EdgeInsets.all(0), + dense: true, controlAffinity: ListTileControlAffinity.leading, title: Text( - 'Remember the password', + 'Remember password', ), value: remember, onChanged: (v) { From 69ede0fb03d2cc993ebbfbb9e356f9f09fd3ee2f Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 27 Nov 2020 12:46:40 +0800 Subject: [PATCH 095/422] text --- flutter_hbb/lib/remote_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 37fa62461..ca7648eb7 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -516,7 +516,7 @@ void enterPasswordDialog(String id, BuildContext context) { showAlertDialog( context, (setState) => Tuple3( - Text('Please enter your password'), + Text('Password required'), Column(mainAxisSize: MainAxisSize.min, children: [ PasswordWidget(controller: controller), CheckboxListTile( From bc75d8788154add9dea61743e4382c766001aef9 Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 27 Nov 2020 14:40:40 +0800 Subject: [PATCH 096/422] move open audio session --- flutter_hbb/lib/model.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 8ed3330bb..592f0c131 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -43,7 +43,6 @@ class FfiModel with ChangeNotifier { clear(); () async { await FFI.init(); - await _audioPlayer.openAudioSession(); _initialized = true; notifyListeners(); }(); @@ -71,6 +70,7 @@ class FfiModel with ChangeNotifier { final st = await _audioPlayer.getPlayerState(); if (st != PlayerState.isPlaying) return; await _audioPlayer.stopPlayer(); + await _audioPlayer.closeAudioSession(); } void update( @@ -106,6 +106,7 @@ class FfiModel with ChangeNotifier { // Flutter Sound does not support Floating Point PCM data, nor records with more that one audio channel. // On Flutter Sound, Raw PCM is only PCM INT-Linerar 16 monophony await stopAudio(); + await _audioPlayer.openAudioSession(); await _audioPlayer.startPlayerFromStream( codec: Codec.pcm16, numChannels: 1, sampleRate: s); */ From e881f6ca298dcb6560823ff0c4ba6bb34a31867e Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 27 Nov 2020 16:06:35 +0800 Subject: [PATCH 097/422] reconnect logic --- flutter_hbb/lib/remote_page.dart | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index ca7648eb7..8a09caf48 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -20,6 +20,7 @@ class RemotePage extends StatefulWidget { class _RemotePageState extends State { Timer _interval; + Timer _timer; bool _showBar = true; double _bottom = 0; String _value = ''; @@ -35,6 +36,7 @@ class _RemotePageState extends State { var _fn = false; final FocusNode _focusNode = FocusNode(); var _showEdit = true; + var _reconnects = 1; @override void initState() { @@ -55,6 +57,7 @@ class _RemotePageState extends State { super.dispose(); FFI.close(); _interval.cancel(); + _timer?.cancel(); dismissLoading(); SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values); Wakelock.disable(); @@ -90,6 +93,24 @@ class _RemotePageState extends State { enterPasswordDialog(id, context); } else { msgbox(type, title, text, context); + final hasRetry = type == "error" && + title == "Connection Error" && + text.toLowerCase().indexOf("offline") < 0 && + text.toLowerCase().indexOf("exist") < 0 && + text.toLowerCase().indexOf("handshake") < 0 && + text.toLowerCase().indexOf("failed") < 0 && + text.toLowerCase().indexOf("resolve") < 0 && + text.toLowerCase().indexOf("manually") < 0; + if (hasRetry) { + _timer?.cancel(); + _timer = Timer(Duration(seconds: _reconnects), () { + FFI.setByName('reconnect'); + showLoading('Connecting...', context); + }); + _reconnects *= 2; + } else { + _reconnects = 1; + } } } @@ -133,11 +154,13 @@ class _RemotePageState extends State { onPressed: () { // destroy first, so that our _value trick can work setState(() => _showEdit = false); - Timer(Duration(milliseconds: 30), () { + _timer?.cancel(); + _timer = Timer(Duration(milliseconds: 30), () { // show now, and sleep a while to requestFocus to // make sure edit ready, so that keyboard wont show/hide/show/hide happen setState(() => _showEdit = true); - Timer(Duration(milliseconds: 30), () { + _timer?.cancel(); + _timer = Timer(Duration(milliseconds: 30), () { SystemChrome.setEnabledSystemUIOverlays( SystemUiOverlay.values); _focusNode.requestFocus(); From 7662ab0a0f23e95afc11ef725fcd7fa37b524267 Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 27 Nov 2020 17:34:09 +0800 Subject: [PATCH 098/422] refresh on all --- flutter_hbb/lib/model.dart | 2 ++ flutter_hbb/lib/remote_page.dart | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 592f0c131..549582211 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -168,6 +168,7 @@ class FfiModel with ChangeNotifier { void handlePeerInfo(Map evt, BuildContext context) { dismissLoading(); + _pi.version = evt['version']; _pi.username = evt['username']; _pi.hostname = evt['hostname']; _pi.platform = evt['platform']; @@ -620,6 +621,7 @@ class Display { } class PeerInfo { + String version; String username; String hostname; String platform; diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 8a09caf48..c6fc57e2d 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -721,10 +721,16 @@ void showActions(BuildContext context) { context: context, position: RelativeRect.fromLTRB(x, y, x, y), items: [ - PopupMenuItem( - child: Text('Insert Ctrl + Alt + Del'), value: 'cad'), - PopupMenuItem(child: Text('Insert Lock'), value: 'lock'), - ], + PopupMenuItem( + child: Text('Insert Ctrl + Alt + Del'), value: 'cad'), + PopupMenuItem( + child: Text('Insert Lock'), value: 'lock'), + ] + + FFI.ffiModel.pi.version.isEmpty + ? [] + : [ + PopupMenuItem(child: Text('Refresh'), value: 'refresh'), + ], elevation: 8, ); if (value == 'cad') { @@ -733,5 +739,8 @@ void showActions(BuildContext context) { if (value == 'lock') { FFI.setByName('lock_screen'); } + if (value == 'refresh') { + FFI.setByName('refresh'); + } }(); } From 60f48c5326fe48692209d786f1a84f5a737903ce Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 27 Nov 2020 17:59:42 +0800 Subject: [PATCH 099/422] refresh --- flutter_hbb/lib/model.dart | 7 +++++++ flutter_hbb/lib/remote_page.dart | 20 ++++++++++---------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 549582211..baf51e1ee 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -163,6 +163,7 @@ class FfiModel with ChangeNotifier { _display.width = int.parse(evt['width']); _display.height = int.parse(evt['height']); FFI.cursorModel.updateDisplayOrigin(_display.x, _display.y); + FFI.canvasModel.resetOffset(); notifyListeners(); } @@ -252,6 +253,12 @@ class CanvasModel with ChangeNotifier { notifyListeners(); } + void resetOffset() { + _x = 0; + _y = 0; + notifyListeners(); + } + void panY(double dy) { _y += dy; notifyListeners(); diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index c6fc57e2d..259f33471 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -721,16 +721,16 @@ void showActions(BuildContext context) { context: context, position: RelativeRect.fromLTRB(x, y, x, y), items: [ - PopupMenuItem( - child: Text('Insert Ctrl + Alt + Del'), value: 'cad'), - PopupMenuItem( - child: Text('Insert Lock'), value: 'lock'), - ] + - FFI.ffiModel.pi.version.isEmpty - ? [] - : [ - PopupMenuItem(child: Text('Refresh'), value: 'refresh'), - ], + PopupMenuItem( + child: Text('Insert Ctrl + Alt + Del'), value: 'cad'), + PopupMenuItem(child: Text('Insert Lock'), value: 'lock'), + ] + + (FFI.ffiModel.pi.version.isEmpty + ? [] + : [ + PopupMenuItem( + child: Text('Refresh'), value: 'refresh'), + ]), elevation: 8, ); if (value == 'cad') { From 9c532c507b873018c9462950faea34b27ee1d7e7 Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 27 Nov 2020 22:50:24 +0800 Subject: [PATCH 100/422] to-do: adjust rather then reset --- flutter_hbb/lib/model.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index baf51e1ee..a6facdcf1 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -163,7 +163,6 @@ class FfiModel with ChangeNotifier { _display.width = int.parse(evt['width']); _display.height = int.parse(evt['height']); FFI.cursorModel.updateDisplayOrigin(_display.x, _display.y); - FFI.canvasModel.resetOffset(); notifyListeners(); } @@ -435,6 +434,7 @@ class CursorModel with ChangeNotifier { _x = x; _y = y; FFI.moveMouse(x, y); + FFI.canvasModel.resetOffset(); notifyListeners(); } From ca0137b228a67567027be0b404a755a1baf5ce9d Mon Sep 17 00:00:00 2001 From: open-trade Date: Sat, 28 Nov 2020 00:22:25 +0800 Subject: [PATCH 101/422] my own oboe --- flutter_hbb/android/app/build.gradle | 2 +- flutter_hbb/lib/model.dart | 43 ---------------------------- flutter_hbb/pubspec.yaml | 1 - 3 files changed, 1 insertion(+), 45 deletions(-) diff --git a/flutter_hbb/android/app/build.gradle b/flutter_hbb/android/app/build.gradle index b965b9517..efe0a7623 100644 --- a/flutter_hbb/android/app/build.gradle +++ b/flutter_hbb/android/app/build.gradle @@ -39,7 +39,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.carriez.flutter_hbb" - minSdkVersion 18 + minSdkVersion 21 targetSdkVersion 29 versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index a6facdcf1..f8e3dab49 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -1,7 +1,6 @@ import 'package:ffi/ffi.dart'; import 'package:flutter/gestures.dart'; import 'package:path_provider/path_provider.dart'; -import 'package:flutter_sound/flutter_sound.dart'; import 'package:device_info/device_info.dart'; import 'dart:io'; import 'dart:math'; @@ -33,7 +32,6 @@ class FfiModel with ChangeNotifier { bool _waitForImage; bool _initialized = false; final _permissions = Map(); - final _audioPlayer = FlutterSoundPlayer(); get permissions => _permissions; get initialized => _initialized; @@ -66,13 +64,6 @@ class FfiModel with ChangeNotifier { _permissions.clear(); } - Future stopAudio() async { - final st = await _audioPlayer.getPlayerState(); - if (st != PlayerState.isPlaying) return; - await _audioPlayer.stopPlayer(); - await _audioPlayer.closeAudioSession(); - } - void update( String id, BuildContext context, @@ -97,23 +88,6 @@ class FfiModel with ChangeNotifier { pos = evt; } else if (name == 'permission') { FFI.ffiModel.updatePermission(evt); - } else if (name == "audio_format") { - () async { - try { - /* - var s = int.parse(evt['sample_rate']); - // var c = int.parse(evt['channels']); - // Flutter Sound does not support Floating Point PCM data, nor records with more that one audio channel. - // On Flutter Sound, Raw PCM is only PCM INT-Linerar 16 monophony - await stopAudio(); - await _audioPlayer.openAudioSession(); - await _audioPlayer.startPlayerFromStream( - codec: Codec.pcm16, numChannels: 1, sampleRate: s); - */ - } catch (e) { - print('audio_format: $e'); - } - }(); } } if (pos != null) FFI.cursorModel.updateCursorPosition(pos); @@ -139,21 +113,6 @@ class FfiModel with ChangeNotifier { }); } } - var frame = FFI._getAudio(); - if (frame != null && frame != nullptr) { - () async { - try { - /* - final ref = frame.ref; - final bytes = Uint8List.sublistView(ref.data.asTypedList(ref.len)); - await _audioPlayer.feedFromStream(bytes); - */ - } catch (e) { - print('play audio frame: $e'); - } - FFI._freeRgba(frame); - }(); - } } void handleSwitchDisplay(Map evt) { @@ -453,7 +412,6 @@ class FFI { static F3 _setByName; static F4 _freeRgba; static F5 _getRgba; - static F5 _getAudio; static Pointer _lastRgbaFrame; static var shift = false; static var ctrl = false; @@ -595,7 +553,6 @@ class FFI { _freeRgba = dylib .lookupFunction), F4>('free_rgba'); _getRgba = dylib.lookupFunction('get_rgba'); - _getAudio = dylib.lookupFunction('get_audio'); _dir = (await getApplicationDocumentsDirectory()).path; DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index 89e034813..49b28d44f 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -34,7 +34,6 @@ dependencies: flutter_easyloading: ^2.1.3 tuple: ^1.0.1 wakelock: ^0.2.1+1 - flutter_sound: ^6.4.2+1 device_info: ^1.0.0 dev_dependencies: From 4d4d7673de336ca30c2618beef5814392a41ddbe Mon Sep 17 00:00:00 2001 From: open-trade Date: Sat, 28 Nov 2020 13:00:58 +0800 Subject: [PATCH 102/422] more on clipboard --- flutter_hbb/lib/remote_page.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 259f33471..562f3b5c9 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -173,6 +173,7 @@ class _RemotePageState extends State { color: Colors.white, icon: Icon(Icons.tv), onPressed: () { + setState(() => _showEdit = false); showOptions(context); }, ), @@ -192,6 +193,7 @@ class _RemotePageState extends State { color: Colors.white, icon: Icon(Icons.more_vert), onPressed: () { + setState(() => _showEdit = false); showActions(context); }, ), From 6a5454f72ad8e680f4febd16b9db323cdebaeee1 Mon Sep 17 00:00:00 2001 From: open-trade Date: Sat, 28 Nov 2020 13:22:19 +0800 Subject: [PATCH 103/422] clipboard --- flutter_hbb/lib/model.dart | 12 ++++++++++++ flutter_hbb/lib/remote_page.dart | 32 ++++++++++++++++++++------------ 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index f8e3dab49..0cfe97827 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -1,4 +1,5 @@ import 'package:ffi/ffi.dart'; +import 'package:flutter/services.dart'; import 'package:flutter/gestures.dart'; import 'package:path_provider/path_provider.dart'; import 'package:device_info/device_info.dart'; @@ -61,6 +62,10 @@ class FfiModel with ChangeNotifier { _display = Display(); _decoding = false; _waitForImage = false; + clearPermissions(); + } + + void clearPermissions() { _permissions.clear(); } @@ -86,6 +91,8 @@ class FfiModel with ChangeNotifier { FFI.cursorModel.updateCursorId(evt); } else if (name == 'cursor_position') { pos = evt; + } else if (name == 'clipboard') { + Clipboard.setData(ClipboardData(text: evt['content'])); } else if (name == 'permission') { FFI.ffiModel.updatePermission(evt); } @@ -438,6 +445,11 @@ class FFI { json.encode(modify({'type': 'wheel', 'y': y2.toString()}))); } + static void reconnect() { + setByName('reconnect'); + FFI.ffiModel.clearPermissions(); + } + static void resetModifiers() { shift = ctrl = alt = command = false; } diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 562f3b5c9..b45099095 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -104,7 +104,7 @@ class _RemotePageState extends State { if (hasRetry) { _timer?.cancel(); _timer = Timer(Duration(seconds: _reconnects), () { - FFI.setByName('reconnect'); + FFI.reconnect(); showLoading('Connecting...', context); }); _reconnects *= 2; @@ -718,31 +718,39 @@ void showActions(BuildContext context) { final size = MediaQuery.of(context).size; final x = 120.0; final y = size.height; + var more = []; + if (FFI.ffiModel.pi.version.isNotEmpty) { + more.add(PopupMenuItem(child: Text('Refresh'), value: 'refresh')); + } + if (FFI.ffiModel.permissions['keyboard'] != false && + FFI.ffiModel.permissions['clipboard'] != false) { + more.add(PopupMenuItem(child: Text('Paste'), value: 'paste')); + } () async { var value = await showMenu( context: context, position: RelativeRect.fromLTRB(x, y, x, y), - items: [ + items: [ PopupMenuItem( child: Text('Insert Ctrl + Alt + Del'), value: 'cad'), PopupMenuItem(child: Text('Insert Lock'), value: 'lock'), ] + - (FFI.ffiModel.pi.version.isEmpty - ? [] - : [ - PopupMenuItem( - child: Text('Refresh'), value: 'refresh'), - ]), + more, elevation: 8, ); if (value == 'cad') { FFI.setByName('ctrl_alt_del'); - } - if (value == 'lock') { + } else if (value == 'lock') { FFI.setByName('lock_screen'); - } - if (value == 'refresh') { + } else if (value == 'refresh') { FFI.setByName('refresh'); + } else if (value == 'paste') { + () async { + ClipboardData data = await Clipboard.getData(Clipboard.kTextPlain); + if (data.text != null) { + FFI.setByName('input_string', '${data.text}'); + } + }(); } }(); } From d57a0c85d8a479900b32d69fc43e36c1328164e9 Mon Sep 17 00:00:00 2001 From: open-trade Date: Sat, 28 Nov 2020 13:34:59 +0800 Subject: [PATCH 104/422] mute --- flutter_hbb/lib/remote_page.dart | 139 ++++++++++++++++--------------- 1 file changed, 74 insertions(+), 65 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index b45099095..a9b937ada 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -642,83 +642,92 @@ void showOptions(BuildContext context) { ))); displays.add(Divider(color: MyTheme.border)); } - showAlertDialog( - context, - (setState) => Tuple3( - null, - Column( - mainAxisSize: MainAxisSize.min, - children: displays + - [ - RadioListTile( - controlAffinity: ListTileControlAffinity.trailing, - title: const Text('Good image quality'), - value: 'best', - groupValue: quality, - onChanged: (String value) { + showAlertDialog(context, (setState) { + final more = []; + if (FFI.ffiModel.permissions['audio'] != false) { + more.add(CheckboxListTile( + value: FFI.getByName('toggle_option', 'disable-audio') == 'true', + onChanged: (v) { + setState(() { + lockAfterSessionEnd = v; + FFI.setByName('toggle_option', 'disable-audio'); + }); + }, + title: Text('Mute'))); + } + return Tuple3( + null, + Column( + mainAxisSize: MainAxisSize.min, + children: displays + + [ + RadioListTile( + controlAffinity: ListTileControlAffinity.trailing, + title: const Text('Good image quality'), + value: 'best', + groupValue: quality, + onChanged: (String value) { + setState(() { + quality = value; + FFI.setByName('image_quality', value); + }); + }, + ), + RadioListTile( + controlAffinity: ListTileControlAffinity.trailing, + title: const Text('Balanced'), + value: 'balanced', + groupValue: quality, + onChanged: (String value) { + setState(() { + quality = value; + FFI.setByName('image_quality', value); + }); + }, + ), + RadioListTile( + controlAffinity: ListTileControlAffinity.trailing, + title: const Text('Optimize reaction time'), + value: 'low', + groupValue: quality, + onChanged: (String value) { + setState(() { + quality = value; + FFI.setByName('image_quality', value); + }); + }, + ), + Divider(color: MyTheme.border), + CheckboxListTile( + value: showRemoteCursor, + onChanged: (v) { setState(() { - quality = value; - FFI.setByName('image_quality', value); + showRemoteCursor = v; + FFI.setByName('toggle_option', 'show-remote-cursor'); }); }, - ), - RadioListTile( - controlAffinity: ListTileControlAffinity.trailing, - title: const Text('Balanced'), - value: 'balanced', - groupValue: quality, - onChanged: (String value) { + title: Text('Show remote cursor')), + CheckboxListTile( + value: lockAfterSessionEnd, + onChanged: (v) { setState(() { - quality = value; - FFI.setByName('image_quality', value); + lockAfterSessionEnd = v; + FFI.setByName( + 'toggle_option', 'lock-after-session-end'); }); }, - ), - RadioListTile( - controlAffinity: ListTileControlAffinity.trailing, - title: const Text('Optimize reaction time'), - value: 'low', - groupValue: quality, - onChanged: (String value) { - setState(() { - quality = value; - FFI.setByName('image_quality', value); - }); - }, - ), - Divider(color: MyTheme.border), - CheckboxListTile( - value: showRemoteCursor, - onChanged: (v) { - setState(() { - showRemoteCursor = v; - FFI.setByName( - 'toggle_option', 'show-remote-cursor'); - }); - }, - title: Text('Show remote cursor')), - CheckboxListTile( - value: lockAfterSessionEnd, - onChanged: (v) { - setState(() { - lockAfterSessionEnd = v; - FFI.setByName( - 'toggle_option', 'lock-after-session-end'); - }); - }, - title: Text('Lock after session end')) - ]), - null), - () async => true, - true, - 0); + title: Text('Lock after session end')) + ] + + more), + null); + }, () async => true, true, 0); } void showActions(BuildContext context) { final size = MediaQuery.of(context).size; final x = 120.0; final y = size.height; - var more = []; + final more = []; if (FFI.ffiModel.pi.version.isNotEmpty) { more.add(PopupMenuItem(child: Text('Refresh'), value: 'refresh')); } From d036225bd2a2c5772e8fead63357814e5509d247 Mon Sep 17 00:00:00 2001 From: open-trade Date: Sat, 28 Nov 2020 13:39:34 +0800 Subject: [PATCH 105/422] refactor --- flutter_hbb/lib/remote_page.dart | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index a9b937ada..f0a555e9c 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -605,10 +605,6 @@ void wrongPasswordDialog(String id, BuildContext context) { } void showOptions(BuildContext context) { - var showRemoteCursor = - FFI.getByName('toggle_option', 'show-remote-cursor') == 'true'; - var lockAfterSessionEnd = - FFI.getByName('toggle_option', 'lock-after-session-end') == 'true'; String quality = FFI.getByName('image_quality'); if (quality == '') quality = 'balanced'; var displays = []; @@ -649,7 +645,6 @@ void showOptions(BuildContext context) { value: FFI.getByName('toggle_option', 'disable-audio') == 'true', onChanged: (v) { setState(() { - lockAfterSessionEnd = v; FFI.setByName('toggle_option', 'disable-audio'); }); }, @@ -699,19 +694,21 @@ void showOptions(BuildContext context) { ), Divider(color: MyTheme.border), CheckboxListTile( - value: showRemoteCursor, + value: FFI.getByName( + 'toggle_option', 'show-remote-cursor') == + 'true', onChanged: (v) { setState(() { - showRemoteCursor = v; FFI.setByName('toggle_option', 'show-remote-cursor'); }); }, title: Text('Show remote cursor')), CheckboxListTile( - value: lockAfterSessionEnd, + value: FFI.getByName( + 'toggle_option', 'lock-after-session-end') == + 'true', onChanged: (v) { setState(() { - lockAfterSessionEnd = v; FFI.setByName( 'toggle_option', 'lock-after-session-end'); }); From 1928768bae5b4d4117bc960b6858586234487d60 Mon Sep 17 00:00:00 2001 From: open-trade Date: Sat, 28 Nov 2020 15:24:44 +0800 Subject: [PATCH 106/422] bug fix --- flutter_hbb/lib/remote_page.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index f0a555e9c..73c022f88 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -639,7 +639,7 @@ void showOptions(BuildContext context) { displays.add(Divider(color: MyTheme.border)); } showAlertDialog(context, (setState) { - final more = []; + final more = []; if (FFI.ffiModel.permissions['audio'] != false) { more.add(CheckboxListTile( value: FFI.getByName('toggle_option', 'disable-audio') == 'true', @@ -724,7 +724,7 @@ void showActions(BuildContext context) { final size = MediaQuery.of(context).size; final x = 120.0; final y = size.height; - final more = []; + final more = >[]; if (FFI.ffiModel.pi.version.isNotEmpty) { more.add(PopupMenuItem(child: Text('Refresh'), value: 'refresh')); } @@ -736,7 +736,7 @@ void showActions(BuildContext context) { var value = await showMenu( context: context, position: RelativeRect.fromLTRB(x, y, x, y), - items: [ + items: [ PopupMenuItem( child: Text('Insert Ctrl + Alt + Del'), value: 'cad'), PopupMenuItem(child: Text('Insert Lock'), value: 'lock'), From b81ecdf31851728d6820681589ca6c1575dc938d Mon Sep 17 00:00:00 2001 From: open-trade Date: Sat, 28 Nov 2020 15:56:25 +0800 Subject: [PATCH 107/422] not sure if fix first image and clipboard/keyboard issue --- flutter_hbb/lib/remote_page.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 73c022f88..595a91930 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -41,6 +41,7 @@ class _RemotePageState extends State { @override void initState() { super.initState(); + _value = ' ' * 1000; FFI.connect(widget.id); WidgetsBinding.instance.addPostFrameCallback((_) { SystemChrome.setEnabledSystemUIOverlays([]); @@ -55,9 +56,9 @@ class _RemotePageState extends State { void dispose() { _focusNode.dispose(); super.dispose(); - FFI.close(); _interval.cancel(); _timer?.cancel(); + FFI.close(); dismissLoading(); SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values); Wakelock.disable(); @@ -163,9 +164,9 @@ class _RemotePageState extends State { _timer = Timer(Duration(milliseconds: 30), () { SystemChrome.setEnabledSystemUIOverlays( SystemUiOverlay.values); - _focusNode.requestFocus(); SystemChannels.textInput .invokeMethod('TextInput.show'); + _focusNode.requestFocus(); }); }); }), From fa78e7b291c9d15cf4bb2e33e5a53e17f39cf0f4 Mon Sep 17 00:00:00 2001 From: open-trade Date: Sat, 28 Nov 2020 17:42:29 +0800 Subject: [PATCH 108/422] seems fix first image crossline issue --- flutter_hbb/lib/model.dart | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 0cfe97827..3bea25c8c 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -29,7 +29,7 @@ typedef F5 = Pointer Function(); class FfiModel with ChangeNotifier { PeerInfo _pi; Display _display; - bool _decoding; + var _decoding = false; bool _waitForImage; bool _initialized = false; final _permissions = Map(); @@ -60,7 +60,6 @@ class FfiModel with ChangeNotifier { void clear() { _pi = PeerInfo(); _display = Display(); - _decoding = false; _waitForImage = false; clearPermissions(); } @@ -106,11 +105,13 @@ class FfiModel with ChangeNotifier { dismissLoading(); } _decoding = true; + final pid = FFI.id; ui.decodeImageFromPixels( rgba, _display.width, _display.height, ui.PixelFormat.bgra8888, (image) { FFI.clearRgbaFrame(); _decoding = false; + if (FFI.id != pid) return; try { // my throw exception, because the listener maybe already dispose FFI.imageModel.update(image); @@ -365,8 +366,10 @@ class CursorModel with ChangeNotifier { var height = int.parse(evt['height']); List colors = json.decode(evt['colors']); final rgba = Uint8List.fromList(colors.map((s) => s as int).toList()); + var pid = FFI.id; ui.decodeImageFromPixels(rgba, width, height, ui.PixelFormat.rgba8888, (image) { + if (FFI.id != pid) return; _image = image; _images[id] = Tuple3(image, _hotx, _hoty); try { @@ -413,6 +416,7 @@ class CursorModel with ChangeNotifier { } class FFI { + static String id = ""; static String _dir = ''; static F1 _freeCString; static F2 _getByName; @@ -496,6 +500,7 @@ class FFI { static void connect(String id) { setByName('connect', id); + FFI.id = id; } static void clearRgbaFrame() { @@ -532,6 +537,7 @@ class FFI { } static void close() { + id = ""; setByName('close', ''); imageModel.update(null); cursorModel.clear(); From 722a382ce2a79045d0721d1cd6c64d7cfbe1f730 Mon Sep 17 00:00:00 2001 From: open-trade Date: Sat, 28 Nov 2020 18:06:27 +0800 Subject: [PATCH 109/422] better input --- flutter_hbb/lib/remote_page.dart | 84 ++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 595a91930..e91ac3bfe 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -9,6 +9,8 @@ import 'package:wakelock/wakelock.dart'; import 'common.dart'; import 'model.dart'; +final initText = '\1' * 1024; + class RemotePage extends StatefulWidget { RemotePage({Key key, this.id}) : super(key: key); @@ -41,7 +43,7 @@ class _RemotePageState extends State { @override void initState() { super.initState(); - _value = ' ' * 1000; + _value = initText; FFI.connect(widget.id); WidgetsBinding.instance.addPostFrameCallback((_) { SystemChrome.setEnabledSystemUIOverlays([]); @@ -56,9 +58,9 @@ class _RemotePageState extends State { void dispose() { _focusNode.dispose(); super.dispose(); + FFI.close(); _interval.cancel(); _timer?.cancel(); - FFI.close(); dismissLoading(); SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values); Wakelock.disable(); @@ -73,7 +75,7 @@ class _RemotePageState extends State { var v = MediaQuery.of(context).viewInsets.bottom; if (v != _bottom) { resetTool(); - _value = ' ' * 1000; + _value = initText; setState(() { _bottom = v; if (v < 100) { @@ -115,6 +117,46 @@ class _RemotePageState extends State { } } + void handleInput(String newValue) { + if (_value[0] == '\1' && newValue[0] != '\1') { + // clipboard + _value = ''; + } + if (newValue.length <= _value.length) { + final char = 'VK_BACK'; + FFI.inputKey(char); + } else { + final content = newValue.substring(_value.length); + if (content.length > 1) { + FFI.setByName('input_string', content); + } else { + var char = content; + if (char == '\n') { + char = 'VK_RETURN'; + } + FFI.inputKey(char); + } + } + _value = newValue; + } + + void openKeyboard() { + // destroy first, so that our _value trick can work + setState(() => _showEdit = false); + _timer?.cancel(); + _timer = Timer(Duration(milliseconds: 30), () { + // show now, and sleep a while to requestFocus to + // make sure edit ready, so that keyboard wont show/hide/show/hide happen + setState(() => _showEdit = true); + _timer?.cancel(); + _timer = Timer(Duration(milliseconds: 30), () { + SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values); + SystemChannels.textInput.invokeMethod('TextInput.show'); + _focusNode.requestFocus(); + }); + }); + } + @override Widget build(BuildContext context) { EasyLoading.instance.loadingStyle = EasyLoadingStyle.light; @@ -152,24 +194,7 @@ class _RemotePageState extends State { IconButton( color: Colors.white, icon: Icon(Icons.keyboard), - onPressed: () { - // destroy first, so that our _value trick can work - setState(() => _showEdit = false); - _timer?.cancel(); - _timer = Timer(Duration(milliseconds: 30), () { - // show now, and sleep a while to requestFocus to - // make sure edit ready, so that keyboard wont show/hide/show/hide happen - setState(() => _showEdit = true); - _timer?.cancel(); - _timer = Timer(Duration(milliseconds: 30), () { - SystemChrome.setEnabledSystemUIOverlays( - SystemUiOverlay.values); - SystemChannels.textInput - .invokeMethod('TextInput.show'); - _focusNode.requestFocus(); - }); - }); - }), + onPressed: openKeyboard), IconButton( color: Colors.white, icon: Icon(Icons.tv), @@ -278,22 +303,7 @@ class _RemotePageState extends State { initialValue: _value, // trick way to make backspace work always keyboardType: TextInputType.multiline, - onChanged: (x) { - if (x.length <= _value.length) { - final char = 'VK_BACK'; - FFI.inputKey(char); - } - for (var i = _value.length; - i < x.length; - ++i) { - var char = x[i]; - if (char == '\n') { - char = 'VK_RETURN'; - } - FFI.inputKey(char); - } - _value = x; - }, + onChanged: handleInput, ), ), ])), From d89ad33b98464e935dd7af383efc83471d6c46e1 Mon Sep 17 00:00:00 2001 From: open-trade Date: Sun, 29 Nov 2020 00:13:55 +0800 Subject: [PATCH 110/422] has crash when popup msgbox when there is keyboard or some other condition --- flutter_hbb/assets/insecure.png | Bin 0 -> 4126 bytes flutter_hbb/assets/insecure_relay.png | Bin 0 -> 4616 bytes flutter_hbb/assets/linux.png | Bin 4119 -> 4247 bytes flutter_hbb/assets/mac.png | Bin 3931 -> 2885 bytes flutter_hbb/assets/secure.png | Bin 0 -> 2509 bytes flutter_hbb/assets/secure_relay.png | Bin 0 -> 5147 bytes flutter_hbb/assets/win.png | Bin 1797 -> 1379 bytes flutter_hbb/lib/common.dart | 7 ++-- flutter_hbb/lib/home_page.dart | 2 +- flutter_hbb/lib/model.dart | 37 ++++++++++++++++++- flutter_hbb/lib/remote_page.dart | 51 +++++++++++++++----------- 11 files changed, 70 insertions(+), 27 deletions(-) create mode 100644 flutter_hbb/assets/insecure.png create mode 100644 flutter_hbb/assets/insecure_relay.png create mode 100644 flutter_hbb/assets/secure.png create mode 100644 flutter_hbb/assets/secure_relay.png diff --git a/flutter_hbb/assets/insecure.png b/flutter_hbb/assets/insecure.png new file mode 100644 index 0000000000000000000000000000000000000000..0c954468d939085cdf323e29cac1271e5be66e5f GIT binary patch literal 4126 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 Gxc>o{396m| literal 0 HcmV?d00001 diff --git a/flutter_hbb/assets/insecure_relay.png b/flutter_hbb/assets/insecure_relay.png new file mode 100644 index 0000000000000000000000000000000000000000..878d574674ef9fd005eab6579dc10261051b1f04 GIT binary patch literal 4616 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 W8iXC&Q2XDd1JHhCfT+~44f{XEhJaZB literal 0 HcmV?d00001 diff --git a/flutter_hbb/assets/linux.png b/flutter_hbb/assets/linux.png index 19813114233fd8af32501caa0f224637353158d9..456e58675ae4cfc9db01574174e1b7afe6a60c1f 100644 GIT binary patch delta 4118 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 UuLe`dPyhe`07*qoM6N<$f|3^OhyVZp delta 3987 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 literal 0 HcmV?d00001 diff --git a/flutter_hbb/assets/win.png b/flutter_hbb/assets/win.png index 155f4e75d81098bf8925bfa3fa073e3dcf8e343f..5ce86a25755115dcee601fb7bb683b9e06712945 100644 GIT binary patch literal 1379 zcmeAS@N?(olHy`uVBq!ia0vp^CxG}V2Q!dV+~VN^q~-+pgt!9f|AQ4wR=AJ?bf0la zkY6yvpNGuc_k_en-m(5*;&}D-+EZQi%2*}SMJDcYpQdiTnyrOL90gx9(F zr#rOsH~qSr$H2hi?CIhdQgQ3;-E9Bc1|n<;(&kGo`YtaM_qv^H!1gqZ?aBZD+izw* z_RgQRWtDa3z3^EkzgXpN+8uw+JVTMANdbsXJoK2E8nlVc-8Ak%%aouEzE=Lp2P|00aXjtaYmT^*>HS;{Io?T z7MvoAr`jqTZQ06%TeoUF^>g4|)FO~*Kl$#%AJ1-mHFp2NlqI0*>vDs)>-*E>8|Syx zS1l5lu3tSty?Z8~Un6@!OUTkGJ5R)I+{9j4Z?3>u`1g$K#&asK*a{UFu?re;?{fT} zX?phB{LQD6?=}dya7vamUr*Y)Gl-|k)Ma`*tBQgb%hnaqH*O}pGr!o{yraMG$sd7G zRhtv#Wo9#8+Pq6obrMBHW}6-D?M?1ZXg(sIt@iq+ z!mjDpm?jCR8aoR-R9ltS9ChGuotx02ZMz$9x?Ic=;LLDZa;7QI;E%!V6Fnzh-h0_* zlA>&I_rb5+E8p*1-*e+_xRJbJ2JfT12l=jN8$U1~_I<2iec88IF_eLkMZf_PNiQxzomNv2)Bi zh{!_gx{QT?b|BO3}+f?^G&d_D0& z_}a{#Nvn4(uHp~%*kP0Cs1X)=^qRFf%cBX*KdW}l*8`>}J=>1Y&m69Vxxcj$e?0s6 z_MOiHnHTXFKK1Qb!Ccy&T-{n69_0RLi=thj|Jk;0T+g|h=D)P4^tjP9<$KKz90}cCvFvnJ>)gg?Rq4x=`8t31`=@85E!eTR=-L_$ z#gqpt(-yc)z8`h`T)>US9QRb-zNqjw*Ic%pX$EC}4k!J3hNbgt3_o?ec?c|x7(8A5 KT-G@yGywp&Jg<8I literal 1797 zcmeAS@N?(olHy`uVBq!ia0y~yU_8LUz{J483=~oN?UMnd&IkB}xB}__!w9VCnCA}k zl1oXDUoZm;D;qlpm!OoioRX%tuCa}si*G<^SWH}eVrp7`ad~w^Q%lFhnTwV#U$tiQ z?jt8oojHH??z7i#-+%b{>GPMb-+ur3`)|stV~2s}F(!GtySV(^GUFfv1M3t|7srr_ zTW{|i^pkNHX}idj_9{%&_JOO}L?wM?*UJCyC%G$xRVV6JHYPXArXFN@XfR2&=i{AM zpLK6mtVn*wUiy8ClIJ894792A5yvF4wS66I26y}KbX?5pd8cj=nXc=$x^UxHv00za z8tM7vCiv|Xuj@P+CjCAAT>iq7>3j*FW-`yHdB&hN-?9P7eGs@M_w7Vuc7va17=7%D z877~XIgmbY=iQse|7J7v*FR&ZbKbdci&SmR{&0p5KY;3k&&A}PE(WSnpR@CHvb90O zarh!0-ijJU~`ows9)`83O>YrWX+_ARs-t!qdr!t;1FJ8^q zBVXn6J~cE~?N{6_eeFNpFSeZUe_$LQE&Kj|%5?q@_l#}UGo&%ysaHCzx;~}hxBcPs z(koILW&=HZKbJ$5x#rK}RSf+O>v~x|jU0@pixvnQMrC);Jk6HzY!j2)>czbV#Wxwg z8*guU?8mmFQ)2Jg`oL1V15;x|=5iQB#a3-8W}nS-;Y=#)og-H7WM}b%n0xlRUOTgt zdCotvo0*K)i@M*`G^FWiKiJ~mbcWC3FmwNFmN_pPk6&f{cO&~qYqJOUkJZK=b5B2F zU=3hk~!~s+ZF#)6!LlDDQn%i|Z z|FhNe*Zb@~Z1rDX>4lO6zlhHLGv{;Z{=bhOUO(oq&2neYnV-AjWf|`5i&0!)y&2>* zLY_c)7sF>%4=~E?vf5|1|EpBtLxoFB|KhwfZ{M2MabvFKgp!7(Q-bpH*2PT^Iv3Rw zmK;9U+vq^;{B`T@O$qYavhT`^!yi?c|E%Yizjx2-nxNox^*&9>2Xm8m%AT-z&t|0f zVBhT7tId~Axtym^%=GT}^E`#umln^p-ks9${a(a>qY0gi_VOoukGweR@QnG+_m7YN zHpU$4RAV-oP|P58!jpmbiA)1q5#ND!X3JY0M^~yd|G6*yx=ukq#`_%Gj{=40?9*&c zMhBXiEAK_9FxwbRxY*WshAl%uoe{|1`kp7n%z@dG{lUV`4!Eh7K` diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 3751a8d68..c84260b1e 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -64,12 +64,13 @@ Future showAlertDialog(BuildContext context, BuildAlertDailog build, return res; } -void msgbox(String type, String title, String text, BuildContext context, - [bool hasCancel]) { +Future msgbox( + String type, String title, String text, BuildContext context, + [bool hasCancel]) async { if (hasCancel == null) { hasCancel = type != 'error'; } - showAlertDialog( + return await showAlertDialog( context, (_) => Tuple3(Text(title), Text(text), [ hasCancel diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index 1de6ee6af..bba97dc5c 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -168,7 +168,7 @@ class _HomePageState extends State { if (platform == 'mac os') platform = 'mac'; else if (platform != 'linux') platform = 'win'; - return Image.asset('assets/$platform.png', width: 36, height: 36); + return Image.asset('assets/$platform.png', width: 24, height: 24); } Widget getPeers() { diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 3bea25c8c..65ecd32b2 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -33,9 +33,13 @@ class FfiModel with ChangeNotifier { bool _waitForImage; bool _initialized = false; final _permissions = Map(); + bool _secure; + bool _direct; get permissions => _permissions; get initialized => _initialized; + get secure => _secure; + get direct => _direct; get pi => _pi; FfiModel() { @@ -61,9 +65,32 @@ class FfiModel with ChangeNotifier { _pi = PeerInfo(); _display = Display(); _waitForImage = false; + _secure = null; + _direct = null; clearPermissions(); } + void setConnectionType(bool secure, bool direct) { + _secure = secure; + _direct = direct; + } + + Image getConnectionImage() { + String icon; + if (secure == true && direct == true) { + icon = 'secure'; + } else if (secure == false && direct == true) { + icon = 'insecure'; + } else if (secure == false && direct == false) { + icon = 'insecure_relay'; + } else if (secure == true && direct == false) { + icon = 'secure_relay'; + } + return icon == null + ? null + : Image.asset('assets/$icon.png', width: 48, height: 48); + } + void clearPermissions() { _permissions.clear(); } @@ -71,7 +98,10 @@ class FfiModel with ChangeNotifier { void update( String id, BuildContext context, - void Function(Map evt, String id, BuildContext context) + void Function( + Map evt, + String id, + ) handleMsgbox) { var pos; for (;;) { @@ -79,9 +109,12 @@ class FfiModel with ChangeNotifier { if (evt == null) break; var name = evt['name']; if (name == 'msgbox') { - handleMsgbox(evt, id, context); + handleMsgbox(evt, id); } else if (name == 'peer_info') { handlePeerInfo(evt, context); + } else if (name == 'connection_ready') { + FFI.ffiModel.setConnectionType( + evt['secure'] == 'true', evt['direct'] == 'true'); } else if (name == 'switch_display') { handleSwitchDisplay(evt); } else if (name == 'cursor_data') { diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index e91ac3bfe..163db41ba 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -86,7 +86,7 @@ class _RemotePageState extends State { FFI.ffiModel.update(widget.id, context, handleMsgbox); } - void handleMsgbox(Map evt, String id, BuildContext context) { + void handleMsgbox(Map evt, String id) { var type = evt['type']; var title = evt['title']; var text = evt['text']; @@ -95,25 +95,29 @@ class _RemotePageState extends State { } else if (type == 'input-password') { enterPasswordDialog(id, context); } else { - msgbox(type, title, text, context); - final hasRetry = type == "error" && - title == "Connection Error" && - text.toLowerCase().indexOf("offline") < 0 && - text.toLowerCase().indexOf("exist") < 0 && - text.toLowerCase().indexOf("handshake") < 0 && - text.toLowerCase().indexOf("failed") < 0 && - text.toLowerCase().indexOf("resolve") < 0 && - text.toLowerCase().indexOf("manually") < 0; - if (hasRetry) { - _timer?.cancel(); - _timer = Timer(Duration(seconds: _reconnects), () { - FFI.reconnect(); - showLoading('Connecting...', context); - }); - _reconnects *= 2; - } else { - _reconnects = 1; - } + showMsgBox(type, title, text); + } + } + + Future showMsgBox(String type, String title, String text) async { + await msgbox(type, title, text, context); + final hasRetry = type == "error" && + title == "Connection Error" && + text.toLowerCase().indexOf("offline") < 0 && + text.toLowerCase().indexOf("exist") < 0 && + text.toLowerCase().indexOf("handshake") < 0 && + text.toLowerCase().indexOf("failed") < 0 && + text.toLowerCase().indexOf("resolve") < 0 && + text.toLowerCase().indexOf("manually") < 0; + if (hasRetry) { + _timer?.cancel(); + _timer = Timer(Duration(seconds: _reconnects), () { + FFI.reconnect(); + showLoading('Connecting...', context); + }); + _reconnects *= 2; + } else { + _reconnects = 1; } } @@ -330,7 +334,7 @@ class _RemotePageState extends State { minWidth: 0, //wraps child's width height: 0, child: FlatButton( - splashColor: Colors.black, + splashColor: MyTheme.accent, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(5.0), ), @@ -620,6 +624,9 @@ void showOptions(BuildContext context) { if (quality == '') quality = 'balanced'; var displays = []; final pi = FFI.ffiModel.pi; + final image = FFI.ffiModel.getConnectionImage(); + if (image != null) + displays.add(Padding(padding: const EdgeInsets.only(top: 8), child: image)); if (pi.displays.length > 1) { final cur = pi.currentDisplay; final children = []; @@ -647,6 +654,8 @@ void showOptions(BuildContext context) { spacing: 8, children: children, ))); + } + if (displays.isNotEmpty) { displays.add(Divider(color: MyTheme.border)); } showAlertDialog(context, (setState) { From 0797334e354cfb7f21b9e0a9f513d748cfd1ddaa Mon Sep 17 00:00:00 2001 From: open-trade Date: Sun, 29 Nov 2020 01:36:10 +0800 Subject: [PATCH 111/422] new msgbox to avoid crash --- flutter_hbb/lib/common.dart | 67 +++++++++++++++++++----------- flutter_hbb/lib/remote_page.dart | 6 +-- flutter_hbb/pubspec.lock | 71 +++----------------------------- flutter_hbb/pubspec.yaml | 5 ++- 4 files changed, 55 insertions(+), 94 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index c84260b1e..c1e531125 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -64,33 +64,52 @@ Future showAlertDialog(BuildContext context, BuildAlertDailog build, return res; } -Future msgbox( - String type, String title, String text, BuildContext context, - [bool hasCancel]) async { +void msgbox(String type, String title, String text, BuildContext context, + [bool hasCancel]) { + var wrap = (String text, void Function() onPressed) => ButtonTheme( + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), + materialTapTargetSize: MaterialTapTargetSize + .shrinkWrap, //limits the touch area to the button area + minWidth: 0, //wraps child's width + height: 0, + child: FlatButton( + focusColor: MyTheme.accent, + onPressed: onPressed, + child: Text(text, style: TextStyle(color: MyTheme.accent)))); + + dismissLoading(); + if (_hasDialog) { + Navigator.pop(context); + } + final buttons = [ + Expanded(child: Container()), + wrap('OK', () { + dismissLoading(); + Navigator.pop(context); + }) + ]; if (hasCancel == null) { hasCancel = type != 'error'; } - return await showAlertDialog( - context, - (_) => Tuple3(Text(title), Text(text), [ - hasCancel - ? FlatButton( - textColor: MyTheme.accent, - onPressed: () { - Navigator.pop(context); - }, - child: Text('Cancel'), - ) - : Spacer(), - FlatButton( - textColor: MyTheme.accent, - onPressed: () { - Navigator.pop(context); - Navigator.pop(context); - }, - child: Text('OK'), - ), - ])); + if (hasCancel) { + buttons.insert( + 1, + wrap('Cancel', () { + dismissLoading(); + })); + } + EasyLoading.showWidget(Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(title, style: TextStyle(fontSize: 21)), + SizedBox(height: 20), + Text(text, style: TextStyle(fontSize: 15)), + SizedBox(height: 20), + Row( + children: buttons, + ) + ], + )); } class PasswordWidget extends StatefulWidget { diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 163db41ba..f814339a4 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -99,8 +99,8 @@ class _RemotePageState extends State { } } - Future showMsgBox(String type, String title, String text) async { - await msgbox(type, title, text, context); + void showMsgBox(String type, String title, String text) { + msgbox(type, title, text, context); final hasRetry = type == "error" && title == "Connection Error" && text.toLowerCase().indexOf("offline") < 0 && @@ -179,7 +179,7 @@ class _RemotePageState extends State { onPressed: () { setState(() => _showBar = !_showBar); }), - bottomNavigationBar: _showBar + bottomNavigationBar: _showBar && FFI.ffiModel.pi.displays != null ? BottomAppBar( elevation: 10, color: MyTheme.accent, diff --git a/flutter_hbb/pubspec.lock b/flutter_hbb/pubspec.lock index f65d572b2..cc63ee266 100644 --- a/flutter_hbb/pubspec.lock +++ b/flutter_hbb/pubspec.lock @@ -43,20 +43,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.15.0-nullsafety.3" - convert: - dependency: transitive - description: - name: convert - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.1" - crypto: - dependency: transitive - description: - name: crypto - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.5" csslib: dependency: transitive description: @@ -114,31 +100,12 @@ packages: flutter_easyloading: dependency: "direct main" description: - name: flutter_easyloading - url: "https://pub.dartlang.org" - source: hosted + path: "." + ref: HEAD + resolved-ref: "2d085c411dc262043b6173c3b962d3e66ce657b1" + url: "git://github.com/open-trade/flutter_easyloading" + source: git version: "2.2.0" - flutter_sound: - dependency: "direct main" - description: - name: flutter_sound - url: "https://pub.dartlang.org" - source: hosted - version: "6.4.2+1" - flutter_sound_platform_interface: - dependency: transitive - description: - name: flutter_sound_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "6.4.2+1" - flutter_sound_web: - dependency: transitive - description: - name: flutter_sound_web - url: "https://pub.dartlang.org" - source: hosted - version: "6.4.2+1" flutter_spinkit: dependency: transitive description: @@ -184,13 +151,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.6.2" - logger: - dependency: transitive - description: - name: logger - url: "https://pub.dartlang.org" - source: hosted - version: "0.7.0+2" matcher: dependency: transitive description: @@ -289,13 +249,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.5" - recase: - dependency: transitive - description: - name: recase - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.1" sky_engine: dependency: transitive description: flutter @@ -329,13 +282,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0-nullsafety.1" - synchronized: - dependency: transitive - description: - name: synchronized - url: "https://pub.dartlang.org" - source: hosted - version: "2.2.0+2" term_glyph: dependency: transitive description: @@ -364,13 +310,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.0-nullsafety.3" - uuid: - dependency: transitive - description: - name: uuid - url: "https://pub.dartlang.org" - source: hosted - version: "2.2.2" vector_math: dependency: transitive description: diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index 49b28d44f..e24940cff 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -31,7 +31,10 @@ dependencies: ffi: ^0.1.3 path_provider: ^1.6.24 provider: ^4.3.2+2 - flutter_easyloading: ^2.1.3 + flutter_easyloading: + git: + url: git://github.com/open-trade/flutter_easyloading + #path: flutter_easyloading tuple: ^1.0.1 wakelock: ^0.2.1+1 device_info: ^1.0.0 From 6bb2434b9de8ab2509011d36b6a94f5448247de4 Mon Sep 17 00:00:00 2001 From: open-trade Date: Sun, 29 Nov 2020 02:07:26 +0800 Subject: [PATCH 112/422] mask --- flutter_hbb/lib/common.dart | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index c1e531125..fef4ed0bd 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -19,7 +19,7 @@ void showLoading(String text, BuildContext context) { Navigator.pop(context); } dismissLoading(); - EasyLoading.show(status: text); + EasyLoading.show(status: text, maskType: EasyLoadingMaskType.black); } void dismissLoading() { @@ -28,7 +28,7 @@ void dismissLoading() { void showSuccess(String text) { dismissLoading(); - EasyLoading.showSuccess(text); + EasyLoading.showSuccess(text, maskType: EasyLoadingMaskType.black); } bool _hasDialog = false; @@ -98,18 +98,20 @@ void msgbox(String type, String title, String text, BuildContext context, dismissLoading(); })); } - EasyLoading.showWidget(Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(title, style: TextStyle(fontSize: 21)), - SizedBox(height: 20), - Text(text, style: TextStyle(fontSize: 15)), - SizedBox(height: 20), - Row( - children: buttons, - ) - ], - )); + EasyLoading.showWidget( + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(title, style: TextStyle(fontSize: 21)), + SizedBox(height: 20), + Text(text, style: TextStyle(fontSize: 15)), + SizedBox(height: 20), + Row( + children: buttons, + ) + ], + ), + maskType: EasyLoadingMaskType.black); } class PasswordWidget extends StatefulWidget { From 4698ac925b74a5639911e3ce7e552059ca8ad902 Mon Sep 17 00:00:00 2001 From: open-trade Date: Sun, 29 Nov 2020 02:15:23 +0800 Subject: [PATCH 113/422] move EasyLoading up --- flutter_hbb/lib/remote_page.dart | 275 +++++++++++++++---------------- 1 file changed, 137 insertions(+), 138 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index f814339a4..69f4a9948 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -169,148 +169,147 @@ class _RemotePageState extends State { close(); return false; }, - child: Scaffold( - floatingActionButton: _showBar - ? null - : FloatingActionButton( - mini: true, - child: Icon(Icons.expand_less), - backgroundColor: MyTheme.accent50, - onPressed: () { - setState(() => _showBar = !_showBar); - }), - bottomNavigationBar: _showBar && FFI.ffiModel.pi.displays != null - ? BottomAppBar( - elevation: 10, - color: MyTheme.accent, - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row(children: [ - IconButton( - color: Colors.white, - icon: Icon(Icons.clear), - onPressed: () { - close(); - }, - ), - IconButton( - color: Colors.white, - icon: Icon(Icons.keyboard), - onPressed: openKeyboard), - IconButton( - color: Colors.white, - icon: Icon(Icons.tv), - onPressed: () { - setState(() => _showEdit = false); - showOptions(context); - }, - ), - Container( - color: _mouseTools ? Colors.blue[500] : null, - child: IconButton( + child: FlutterEasyLoading( + child: Scaffold( + backgroundColor: MyTheme.canvasColor, + floatingActionButton: _showBar + ? null + : FloatingActionButton( + mini: true, + child: Icon(Icons.expand_less), + backgroundColor: MyTheme.accent50, + onPressed: () { + setState(() => _showBar = !_showBar); + }), + bottomNavigationBar: _showBar && FFI.ffiModel.pi.displays != null + ? BottomAppBar( + elevation: 10, + color: MyTheme.accent, + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row(children: [ + IconButton( color: Colors.white, - icon: Icon(Icons.mouse), + icon: Icon(Icons.clear), onPressed: () { - setState(() { - _mouseTools = !_mouseTools; - resetTool(); - }); + close(); }, - )), - IconButton( - color: Colors.white, - icon: Icon(Icons.more_vert), - onPressed: () { - setState(() => _showEdit = false); - showActions(context); - }, - ), - ]), - IconButton( - color: Colors.white, - icon: Icon(Icons.expand_more), - onPressed: () { - setState(() => _showBar = !_showBar); - }), - ], - ), - ) - : null, - body: GestureDetector( - onTap: () { - if (_drag || _scroll) return; - FFI.tap(_right); - }, - onLongPressStart: (_) { - if (_drag) { - // case: to show password on windows - FFI.sendMouse('down', 'left'); - } - }, - onLongPressEnd: (_) { - if (_drag) { - FFI.sendMouse('up', 'left'); - } - }, - onScaleStart: (details) { - _scale = 1; - _xOffset = details.focalPoint.dx; - _yOffset = details.focalPoint.dy; - if (_drag) { - FFI.sendMouse('down', 'left'); - } - }, - onScaleUpdate: (details) { - var scale = details.scale; - if (scale == 1) { - var x = details.focalPoint.dx; - var y = details.focalPoint.dy; - var dx = x - _xOffset; - var dy = y - _yOffset; - if (_scroll) { - FFI.scroll(-dy); - } else { - FFI.cursorModel.updatePan(dx, dy); - } - _xOffset = x; - _yOffset = y; - } else if (!_drag && !_scroll) { - FFI.canvasModel.updateScale(scale / _scale); - _scale = scale; - } - }, - onScaleEnd: (_) { - if (_drag) { - FFI.sendMouse('up', 'left'); - } - }, - child: FlutterEasyLoading( - child: Container( - color: MyTheme.canvasColor, - child: Stack(children: [ - ImagePaint(), - CursorPaint(), - getHelpTools(), - SizedBox( - width: 0, - height: 0, - child: !_showEdit - ? Container() - : TextFormField( - textInputAction: TextInputAction.newline, - autocorrect: false, - enableSuggestions: false, - focusNode: _focusNode, - maxLines: null, - initialValue: - _value, // trick way to make backspace work always - keyboardType: TextInputType.multiline, - onChanged: handleInput, - ), + ), + IconButton( + color: Colors.white, + icon: Icon(Icons.keyboard), + onPressed: openKeyboard), + IconButton( + color: Colors.white, + icon: Icon(Icons.tv), + onPressed: () { + setState(() => _showEdit = false); + showOptions(context); + }, + ), + Container( + color: _mouseTools ? Colors.blue[500] : null, + child: IconButton( + color: Colors.white, + icon: Icon(Icons.mouse), + onPressed: () { + setState(() { + _mouseTools = !_mouseTools; + resetTool(); + }); + }, + )), + IconButton( + color: Colors.white, + icon: Icon(Icons.more_vert), + onPressed: () { + setState(() => _showEdit = false); + showActions(context); + }, + ), + ]), + IconButton( + color: Colors.white, + icon: Icon(Icons.expand_more), + onPressed: () { + setState(() => _showBar = !_showBar); + }), + ], ), - ])), + ) + : null, + body: GestureDetector( + onTap: () { + if (_drag || _scroll) return; + FFI.tap(_right); + }, + onLongPressStart: (_) { + if (_drag) { + // case: to show password on windows + FFI.sendMouse('down', 'left'); + } + }, + onLongPressEnd: (_) { + if (_drag) { + FFI.sendMouse('up', 'left'); + } + }, + onScaleStart: (details) { + _scale = 1; + _xOffset = details.focalPoint.dx; + _yOffset = details.focalPoint.dy; + if (_drag) { + FFI.sendMouse('down', 'left'); + } + }, + onScaleUpdate: (details) { + var scale = details.scale; + if (scale == 1) { + var x = details.focalPoint.dx; + var y = details.focalPoint.dy; + var dx = x - _xOffset; + var dy = y - _yOffset; + if (_scroll) { + FFI.scroll(-dy); + } else { + FFI.cursorModel.updatePan(dx, dy); + } + _xOffset = x; + _yOffset = y; + } else if (!_drag && !_scroll) { + FFI.canvasModel.updateScale(scale / _scale); + _scale = scale; + } + }, + onScaleEnd: (_) { + if (_drag) { + FFI.sendMouse('up', 'left'); + } + }, + child: Stack(children: [ + ImagePaint(), + CursorPaint(), + getHelpTools(), + SizedBox( + width: 0, + height: 0, + child: !_showEdit + ? Container() + : TextFormField( + textInputAction: TextInputAction.newline, + autocorrect: false, + enableSuggestions: false, + focusNode: _focusNode, + maxLines: null, + initialValue: + _value, // trick way to make backspace work always + keyboardType: TextInputType.multiline, + onChanged: handleInput, + ), + ), + ]), )), )); } From 87343945237b5bbc21733dcbdf5409926f7f71d8 Mon Sep 17 00:00:00 2001 From: open-trade Date: Sun, 29 Nov 2020 13:32:38 +0800 Subject: [PATCH 114/422] google analytics --- flutter_hbb/android/app/build.gradle | 2 + flutter_hbb/android/app/google-services.json | 40 +++++++++++ flutter_hbb/android/build.gradle | 1 + flutter_hbb/pubspec.lock | 70 ++++++++++++++++++++ flutter_hbb/pubspec.yaml | 1 + 5 files changed, 114 insertions(+) create mode 100644 flutter_hbb/android/app/google-services.json diff --git a/flutter_hbb/android/app/build.gradle b/flutter_hbb/android/app/build.gradle index efe0a7623..2f2db6973 100644 --- a/flutter_hbb/android/app/build.gradle +++ b/flutter_hbb/android/app/build.gradle @@ -61,3 +61,5 @@ flutter { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" } + +apply plugin: 'com.google.gms.google-services' diff --git a/flutter_hbb/android/app/google-services.json b/flutter_hbb/android/app/google-services.json new file mode 100644 index 000000000..3945e432a --- /dev/null +++ b/flutter_hbb/android/app/google-services.json @@ -0,0 +1,40 @@ +{ + "project_info": { + "project_number": "768133699366", + "firebase_url": "https://rustdesk.firebaseio.com", + "project_id": "rustdesk", + "storage_bucket": "rustdesk.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:768133699366:android:5fc9015370e344457993e7", + "android_client_info": { + "package_name": "com.carriez.flutter_hbb" + } + }, + "oauth_client": [ + { + "client_id": "768133699366-s9gdfsijefsd5g1nura4kmfne42lencn.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyAPOsKcXjrAR-7Z148sYr_gdB_JQZkamTM" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "768133699366-s9gdfsijefsd5g1nura4kmfne42lencn.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/flutter_hbb/android/build.gradle b/flutter_hbb/android/build.gradle index 3100ad2d5..4273368a3 100644 --- a/flutter_hbb/android/build.gradle +++ b/flutter_hbb/android/build.gradle @@ -8,6 +8,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:3.5.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath 'com.google.gms:google-services:4.3.3' } } diff --git a/flutter_hbb/pubspec.lock b/flutter_hbb/pubspec.lock index cc63ee266..c5f42abad 100644 --- a/flutter_hbb/pubspec.lock +++ b/flutter_hbb/pubspec.lock @@ -92,6 +92,55 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "5.2.1" + firebase: + dependency: transitive + description: + name: firebase + url: "https://pub.dartlang.org" + source: hosted + version: "7.3.2" + firebase_analytics: + dependency: "direct main" + description: + name: firebase_analytics + url: "https://pub.dartlang.org" + source: hosted + version: "6.2.0" + firebase_analytics_platform_interface: + dependency: transitive + description: + name: firebase_analytics_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + firebase_analytics_web: + dependency: transitive + description: + name: firebase_analytics_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.1" + firebase_core: + dependency: transitive + description: + name: firebase_core + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.2+1" + firebase_core_platform_interface: + dependency: transitive + description: + name: firebase_core_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + firebase_core_web: + dependency: transitive + description: + name: firebase_core_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.1+1" flutter: dependency: "direct main" description: flutter @@ -130,6 +179,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.14.0+4" + http: + dependency: transitive + description: + name: http + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.2" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.4" import_js_library: dependency: transitive description: @@ -214,6 +277,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.0.4+3" + pedantic: + dependency: transitive + description: + name: pedantic + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.2" platform: dependency: transitive description: diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index e24940cff..598abcf5a 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -38,6 +38,7 @@ dependencies: tuple: ^1.0.1 wakelock: ^0.2.1+1 device_info: ^1.0.0 + firebase_analytics: ^6.2.0 dev_dependencies: flutter_test: From ac4b14292de0f786523e5f3454b2927906ece8f9 Mon Sep 17 00:00:00 2001 From: open-trade Date: Sun, 29 Nov 2020 14:00:59 +0800 Subject: [PATCH 115/422] more google analytics --- flutter_hbb/lib/main.dart | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/flutter_hbb/lib/main.dart b/flutter_hbb/lib/main.dart index 73666ef43..70da191ff 100644 --- a/flutter_hbb/lib/main.dart +++ b/flutter_hbb/lib/main.dart @@ -1,15 +1,21 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:firebase_analytics/firebase_analytics.dart'; +import 'package:firebase_analytics/observer.dart'; +import 'package:firebase_core/firebase_core.dart'; import 'model.dart'; import 'home_page.dart'; -void main() { +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp(); runApp(App()); } class App extends StatelessWidget { @override Widget build(BuildContext context) { + final analytics = FirebaseAnalytics(); return ChangeNotifierProvider.value( value: FFI.ffiModel, child: ChangeNotifierProvider.value( @@ -25,6 +31,9 @@ class App extends StatelessWidget { visualDensity: VisualDensity.adaptivePlatformDensity, ), home: HomePage(title: 'RustDesk'), + navigatorObservers: [ + FirebaseAnalyticsObserver(analytics: analytics), + ], ))))); } } From 71493c8e7e3d5ad904e59c0ad0c7aa4e41f84f41 Mon Sep 17 00:00:00 2001 From: open-trade Date: Sun, 29 Nov 2020 14:19:01 +0800 Subject: [PATCH 116/422] have to put easyloading on FlutterEasyLoading, if on scaffold, mouse not work --- flutter_hbb/lib/remote_page.dart | 195 ++++++++++++++++--------------- flutter_hbb/pubspec.lock | 8 +- flutter_hbb/pubspec.yaml | 6 +- 3 files changed, 104 insertions(+), 105 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 69f4a9948..1838d40d9 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -165,82 +165,81 @@ class _RemotePageState extends State { Widget build(BuildContext context) { EasyLoading.instance.loadingStyle = EasyLoadingStyle.light; return WillPopScope( - onWillPop: () async { - close(); - return false; - }, - child: FlutterEasyLoading( - child: Scaffold( - backgroundColor: MyTheme.canvasColor, - floatingActionButton: _showBar - ? null - : FloatingActionButton( - mini: true, - child: Icon(Icons.expand_less), - backgroundColor: MyTheme.accent50, - onPressed: () { - setState(() => _showBar = !_showBar); - }), - bottomNavigationBar: _showBar && FFI.ffiModel.pi.displays != null - ? BottomAppBar( - elevation: 10, - color: MyTheme.accent, - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row(children: [ - IconButton( + onWillPop: () async { + close(); + return false; + }, + child: Scaffold( + floatingActionButton: _showBar + ? null + : FloatingActionButton( + mini: true, + child: Icon(Icons.expand_less), + backgroundColor: MyTheme.accent50, + onPressed: () { + setState(() => _showBar = !_showBar); + }), + bottomNavigationBar: _showBar && FFI.ffiModel.pi.displays != null + ? BottomAppBar( + elevation: 10, + color: MyTheme.accent, + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row(children: [ + IconButton( + color: Colors.white, + icon: Icon(Icons.clear), + onPressed: () { + close(); + }, + ), + IconButton( + color: Colors.white, + icon: Icon(Icons.keyboard), + onPressed: openKeyboard), + IconButton( + color: Colors.white, + icon: Icon(Icons.tv), + onPressed: () { + setState(() => _showEdit = false); + showOptions(context); + }, + ), + Container( + color: _mouseTools ? Colors.blue[500] : null, + child: IconButton( color: Colors.white, - icon: Icon(Icons.clear), + icon: Icon(Icons.mouse), onPressed: () { - close(); + setState(() { + _mouseTools = !_mouseTools; + resetTool(); + }); }, - ), - IconButton( - color: Colors.white, - icon: Icon(Icons.keyboard), - onPressed: openKeyboard), - IconButton( - color: Colors.white, - icon: Icon(Icons.tv), - onPressed: () { - setState(() => _showEdit = false); - showOptions(context); - }, - ), - Container( - color: _mouseTools ? Colors.blue[500] : null, - child: IconButton( - color: Colors.white, - icon: Icon(Icons.mouse), - onPressed: () { - setState(() { - _mouseTools = !_mouseTools; - resetTool(); - }); - }, - )), - IconButton( - color: Colors.white, - icon: Icon(Icons.more_vert), - onPressed: () { - setState(() => _showEdit = false); - showActions(context); - }, - ), - ]), - IconButton( - color: Colors.white, - icon: Icon(Icons.expand_more), - onPressed: () { - setState(() => _showBar = !_showBar); - }), - ], - ), - ) - : null, - body: GestureDetector( + )), + IconButton( + color: Colors.white, + icon: Icon(Icons.more_vert), + onPressed: () { + setState(() => _showEdit = false); + showActions(context); + }, + ), + ]), + IconButton( + color: Colors.white, + icon: Icon(Icons.expand_more), + onPressed: () { + setState(() => _showBar = !_showBar); + }), + ], + ), + ) + : null, + body: FlutterEasyLoading( + child: GestureDetector( onTap: () { if (_drag || _scroll) return; FFI.tap(_right); @@ -288,30 +287,32 @@ class _RemotePageState extends State { FFI.sendMouse('up', 'left'); } }, - child: Stack(children: [ - ImagePaint(), - CursorPaint(), - getHelpTools(), - SizedBox( - width: 0, - height: 0, - child: !_showEdit - ? Container() - : TextFormField( - textInputAction: TextInputAction.newline, - autocorrect: false, - enableSuggestions: false, - focusNode: _focusNode, - maxLines: null, - initialValue: - _value, // trick way to make backspace work always - keyboardType: TextInputType.multiline, - onChanged: handleInput, - ), - ), - ]), - )), - )); + child: Container( + color: MyTheme.canvasColor, + child: Stack(children: [ + ImagePaint(), + CursorPaint(), + getHelpTools(), + SizedBox( + width: 0, + height: 0, + child: !_showEdit + ? Container() + : TextFormField( + textInputAction: TextInputAction.newline, + autocorrect: false, + enableSuggestions: false, + focusNode: _focusNode, + maxLines: null, + initialValue: + _value, // trick way to make backspace work always + keyboardType: TextInputType.multiline, + onChanged: handleInput, + ), + ), + ]))), + )), + ); } void close() { diff --git a/flutter_hbb/pubspec.lock b/flutter_hbb/pubspec.lock index c5f42abad..7b0a5a1c7 100644 --- a/flutter_hbb/pubspec.lock +++ b/flutter_hbb/pubspec.lock @@ -149,11 +149,9 @@ packages: flutter_easyloading: dependency: "direct main" description: - path: "." - ref: HEAD - resolved-ref: "2d085c411dc262043b6173c3b962d3e66ce657b1" - url: "git://github.com/open-trade/flutter_easyloading" - source: git + path: flutter_easyloading + relative: true + source: path version: "2.2.0" flutter_spinkit: dependency: transitive diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index 598abcf5a..fa8384bbf 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -32,9 +32,9 @@ dependencies: path_provider: ^1.6.24 provider: ^4.3.2+2 flutter_easyloading: - git: - url: git://github.com/open-trade/flutter_easyloading - #path: flutter_easyloading + #git: + #url: git://github.com/open-trade/flutter_easyloading + path: flutter_easyloading tuple: ^1.0.1 wakelock: ^0.2.1+1 device_info: ^1.0.0 From 8e1486870950c58e9035f1fe9159e8c8b419b0a5 Mon Sep 17 00:00:00 2001 From: open-trade Date: Sun, 29 Nov 2020 14:28:07 +0800 Subject: [PATCH 117/422] bug fix --- flutter_hbb/lib/model.dart | 1 + flutter_hbb/lib/remote_page.dart | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 65ecd32b2..3c38865e1 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -193,6 +193,7 @@ class FfiModel with ChangeNotifier { showLoading('Waiting for image...', context); _waitForImage = true; } + notifyListeners(); } } diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 1838d40d9..b02723165 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -163,6 +163,7 @@ class _RemotePageState extends State { @override Widget build(BuildContext context) { + final pi = Provider.of(context).pi; EasyLoading.instance.loadingStyle = EasyLoadingStyle.light; return WillPopScope( onWillPop: () async { @@ -179,7 +180,7 @@ class _RemotePageState extends State { onPressed: () { setState(() => _showBar = !_showBar); }), - bottomNavigationBar: _showBar && FFI.ffiModel.pi.displays != null + bottomNavigationBar: _showBar && pi.displays != null ? BottomAppBar( elevation: 10, color: MyTheme.accent, From ea99455d2283ad04da9f92773e64a1dc55a01404 Mon Sep 17 00:00:00 2001 From: open-trade Date: Sun, 29 Nov 2020 14:28:47 +0800 Subject: [PATCH 118/422] fix --- flutter_hbb/pubspec.lock | 8 +++++--- flutter_hbb/pubspec.yaml | 6 +++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/flutter_hbb/pubspec.lock b/flutter_hbb/pubspec.lock index 7b0a5a1c7..c5f42abad 100644 --- a/flutter_hbb/pubspec.lock +++ b/flutter_hbb/pubspec.lock @@ -149,9 +149,11 @@ packages: flutter_easyloading: dependency: "direct main" description: - path: flutter_easyloading - relative: true - source: path + path: "." + ref: HEAD + resolved-ref: "2d085c411dc262043b6173c3b962d3e66ce657b1" + url: "git://github.com/open-trade/flutter_easyloading" + source: git version: "2.2.0" flutter_spinkit: dependency: transitive diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index fa8384bbf..598abcf5a 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -32,9 +32,9 @@ dependencies: path_provider: ^1.6.24 provider: ^4.3.2+2 flutter_easyloading: - #git: - #url: git://github.com/open-trade/flutter_easyloading - path: flutter_easyloading + git: + url: git://github.com/open-trade/flutter_easyloading + #path: flutter_easyloading tuple: ^1.0.1 wakelock: ^0.2.1+1 device_info: ^1.0.0 From 21a9815071681161451ea50f3bf4afdc455ec9af Mon Sep 17 00:00:00 2001 From: open-trade Date: Sun, 29 Nov 2020 21:33:36 +0800 Subject: [PATCH 119/422] bug fix --- flutter_hbb/lib/remote_page.dart | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index b02723165..55d801831 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -267,25 +267,26 @@ class _RemotePageState extends State { onScaleUpdate: (details) { var scale = details.scale; if (scale == 1) { - var x = details.focalPoint.dx; - var y = details.focalPoint.dy; - var dx = x - _xOffset; - var dy = y - _yOffset; - if (_scroll) { - FFI.scroll(-dy); - } else { + if (!_scroll) { + var x = details.focalPoint.dx; + var y = details.focalPoint.dy; + var dx = x - _xOffset; + var dy = y - _yOffset; FFI.cursorModel.updatePan(dx, dy); + _xOffset = x; + _yOffset = y; } - _xOffset = x; - _yOffset = y; } else if (!_drag && !_scroll) { FFI.canvasModel.updateScale(scale / _scale); _scale = scale; } }, - onScaleEnd: (_) { + onScaleEnd: (details) { if (_drag) { FFI.sendMouse('up', 'left'); + } else if (_scroll) { + FFI.scroll( + details.velocity.pixelsPerSecond.dy > 0 ? -1 : 1); } }, child: Container( @@ -359,7 +360,7 @@ class _RemotePageState extends State { wrap('Scroll', () { setState(() { _scroll = !_scroll; - if (_drag) { + if (_scroll) { _drag = false; _right = false; } @@ -368,7 +369,7 @@ class _RemotePageState extends State { wrap('Right', () { setState(() { _right = !_right; - if (_drag) { + if (_right) { _scroll = false; _drag = false; } From 14130e135cd5758f5d71137eeb55474b44342368 Mon Sep 17 00:00:00 2001 From: open-trade Date: Sun, 29 Nov 2020 22:00:13 +0800 Subject: [PATCH 120/422] icon --- .../android/app/src/main/AndroidManifest.xml | 2 +- .../main/res/mipmap-hdpi/launcher_icon.png | Bin 0 -> 2693 bytes .../main/res/mipmap-mdpi/launcher_icon.png | Bin 0 -> 1902 bytes .../main/res/mipmap-xhdpi/launcher_icon.png | Bin 0 -> 3510 bytes .../main/res/mipmap-xxhdpi/launcher_icon.png | Bin 0 -> 5300 bytes .../main/res/mipmap-xxxhdpi/launcher_icon.png | Bin 0 -> 7053 bytes .../ios/Runner.xcodeproj/project.pbxproj | 2 +- .../Icon-App-1024x1024@1x.png | Bin 10932 -> 79746 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 564 -> 993 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 1283 -> 1619 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 1588 -> 2316 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 1025 -> 1283 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 1716 -> 2218 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 1920 -> 3205 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 1283 -> 1619 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 1895 -> 2986 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 2665 -> 4330 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 2665 -> 4330 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 3831 -> 6651 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 1888 -> 2840 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 3294 -> 5545 bytes .../Icon-App-83.5x83.5@2x.png | Bin 3612 -> 6190 bytes flutter_hbb/pubspec.lock | 63 ++++++++++++++++++ flutter_hbb/pubspec.yaml | 6 ++ 24 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 flutter_hbb/android/app/src/main/res/mipmap-hdpi/launcher_icon.png create mode 100644 flutter_hbb/android/app/src/main/res/mipmap-mdpi/launcher_icon.png create mode 100644 flutter_hbb/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png create mode 100644 flutter_hbb/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png create mode 100644 flutter_hbb/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png diff --git a/flutter_hbb/android/app/src/main/AndroidManifest.xml b/flutter_hbb/android/app/src/main/AndroidManifest.xml index 4ef1e59fa..d9b85398d 100644 --- a/flutter_hbb/android/app/src/main/AndroidManifest.xml +++ b/flutter_hbb/android/app/src/main/AndroidManifest.xml @@ -8,7 +8,7 @@ + android:icon="@mipmap/launcher_icon"> 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 literal 0 HcmV?d00001 diff --git a/flutter_hbb/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png b/flutter_hbb/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..062de677b14cddf431c6fa405ee93c1c459bb088 GIT binary patch literal 5300 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 literal 0 HcmV?d00001 diff --git a/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj b/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj index 3ed44dd0c..811c03816 100644 --- a/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj +++ b/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj @@ -492,4 +492,4 @@ /* End XCConfigurationList section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; -} +} \ No newline at end of file diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png index dc9ada4725e9b0ddb1deab583e5b5102493aa332..9666e3043f986c9cfda65d54030a25ba33ea4543 100644 GIT binary patch literal 79746 zcmeFZ`9DkTu&(BqX5)*`+Mmvu2y=RVsT?BH1h2?E5y#mJlL? z!O+$_FPlH5q+rH?poO?L3Zh16PdG}cSYD0N0j-C@{%tVL_IL~%M`a+alGu5Brj>;Q+mj5du4U= z%(oKD465%&-hpU@*7KmG1MiJ$&ui4VioDs5)HyBtscE_?=Y-8O^7HwqR~zVFCWZUqfv`I%^NLUQG#*Vl zeE!jYXSt-lU3_GgCcw=(6koo=R~;T{%~i!^KVgC9Ja`+=$6&|RlocoP+g_F9D#fY( zY?a$Hz;;)#m7*2!W{7hPvnzk&G448>la@h&V(H4>?iwCzYZb*ie3iq9UFo|AU)Z!6 zU;FO#U|V75y<&GDw_m~iy5HMAJK*?-Ph=i%Z;qG=sgSzn8uio=>h|S2oVZrdy@c7L zUNQioRAq4C{PiHj!ia02NNds(MZe>?K6s;Kp&VwNd&U2Y?WewNeliv3iktl%I5Jl+ zLsUzYx%uZZlJ%LW@kQ3N&)yzyKN@*?0t)QcLhxoExn;5>s(#dxsc-;3*Y$r~{(A+2 zLIwU^)oJOjEGjdjWOWL#qW2k}O8OGtrdc6r8pEBzN3=RE zRjE%8+G-)|p9Gmdr9fu5HKHN;A>LqrKG-Aqr}`BNjRsL0?8Yi?0O{ zI{~}xh}JO%%5(nxaoIPXI*mIfu>gSmc=Bhxap%Z!R2)I-{g$!y!B+#VInl>Ox;(7O zMf14+4QR1Ol+87OM`>Gv)11|0t>6r{)&$YV%LYDYEXlPcgbD@Xm&9v5Q@{EU!ex$6 ziMx&PRonmraz5A#+83FSZ3D(Ay$<86G*(LQMy3o6ELX1@8q2UcgEkqLD@v31I|ZCXol zo=dct5)}oJ-p@Mrp=u#xL~fIV0-!MbIxtzotW@9O^>`b7VYG7~P|r%ayridW%}X%b z?rD7I=#wtNn%z-`99sU!hA`NCOm2)@o)|TsU789FG)78!a#;-P*xy}3u^kR&6z>r- z>=Cs|{#w-1?%uJ@U)i=aO~~*p3HfpbtSi96{`<{$srU|D0%VRV4@20u#~m3L*DZ2% z6pqkRk$M!WbzagRHvP>TwflC8rE_F)ai_!-g31LV&lQnoj|S)Pi;O$^`+Vfsvt`Q_ z!rD=C2H3}%0`#We6Q0xwm68i~j-zqQf5ys&Xf^K@e(eq0vDRmh4nT`ie>&;kJm^&` zJBQ0)`cjfMxgR((=ap@6oAl|_p4QhBgG%@H8Ya_<)T_(!@%SyZv!}&{_2lJAi7CWm z4RN{al2ihMF9r!d>KWjaS4G5XI%(GXP=#J}&%T{--N1?djod&>no4%}MFof6`k>ux zpnn23&KKA88A@~2UxY?@+c2uOg_U|}DNyNc$dbRm!bb8d<(5W>?|dWd!!Mbi;WA~{ zhI88Cv)f)E)DMxjJq`^Js9W=4y(L-lHm?Wy*sy=zhc`Qt|7_BaGhla{6wLqqt;tA= zb!JFb>#;Xw;On1sgl9a~S3TW1M}FD8R%t2N+i8^Hmq<77L)eFhXX6g8tiY}vtj&&P z>cDdc*`T0aUrJJOs;WbIuJY+(gYfjJFo+YT{=(8H;l!JU4f)l_54j{5=Fz59mWdG7 zH-pP2uks8TBij+0NKf%KS%iiX7m&*MDc2UT#B=6yUp`0(xbfJW-v^pmYVor&)qbyUAiXr_DXlv&84H>sd_c@7mdVRvuD2t?y?@3 zH<1xFdD2kU7Jsl-Sw+${;6L>K+z*ILQAqGZ&j1=5Sez*6S!}|&5v|Max2MzJ^i%j2 zx2w{qz3*&jf|l!oUb}@Y8PR{yOrPPKg+QZvcQiio2*tbxlTQ02ovE%?hR<-0CbZO6 zVAkrybEh^sJKof#5PYs6z@3W-FiGM9MkP?SE3a+ELh45@pcmjL!x-s_n;q<)8G=-} zOs!kRkCxI)ieQIC)0^<&4qaGpOSBIW*f>7E$&d~ip?tN?2)Brdq*!e!?DKUab@*W} z6{B*mfGrMmB&>1DBVHf@5ovjM7aHn|J_2udQrATr;Mt3lQ|8`#tPQqR^wr8Q23e{? zm$-?d%6xu3B6TIJsXnPJ45zgTXt+Olk4MU!25k<|ktVXVeu+9j7m4w;>f(>YBsx)Ri3DNoO6y}-B_`2$_ zf27Ajd0o5!QW+HXTGIOq8Tn)cK#X`<$Mt6HdrreE*NE?4zjh26+4*UUn;U2d99&c+ zNXa#QITw<%1QKK6fTS$|RyGvZ3z{IN4{uI=^f&*Q7gszJS8sLUR=olxj^VIdl*J(y zRkH&-*VvI}{Hsf}#-_xJGJM@-pAs+?#%Sja^N=vAc?f;SE}*Y$(QC_Xr({l`Qqc=@ z&ya=b1Wt+Q&#-%H)O8~=C!pW)RO4u?%{JWso`^2ILBI5Mk zksizr|Htb3+nMf}9KQK2J^@wtZc zqE}W`tMkB=Xr8BdUCz$GBPt#CtC-s{GqfsL++2QH$6J4PPFOhO^15nT{(@~zfD{;8 zJvl3WXghnLGh3Z&e#gJ#ho|BawL2Yjkl~NbT+l`w9{!n#aZIO@Scn9I+NG7cb0NMZ z99$rXDL1kF4Az5!h-|UkMfo-P4U!WRm!k(GkZ56@HRx;QTWEPJm@FQ!Fy!M2 z$A#zgll~w%!`wf^REJ6853=Et_{%Yrf@d0(5xScR7{GM{pS4R?^o*3I#}`GtMnqhF z(K34~f7a`?;nV!7GUe+$uxtUwyKt9R6@dNsdZPvo&9b#kS9*W_pU}QDKJ5#Sb73% zSaMN1B6?fv!6fyw`5b$+*|7P1>G3cn3-(JJHH`9nADy)V&HPfK{(`L?vQUmh;Yd$8 zES6%XSS}p54hr)0=;rK+z+V1L_ndKWsk6$&2OGeIwGB_ ziFiTt8uV$brMC%kGi^o7rcLvrj0tFHg~<< zXEDMWg>_u^QY_8NL6*7N>oZz!vV)bUuu|v!pU^)C4~kZ;j%^w$sD{rv)5=EezCa%X z?v|rEHdJ=&(O@c>Vtyl^=84^_b4kd`|IpGyidGrouUFe5VhvT&ZS!Ze9C=`4!~1?Q z)YD3rX<5>rrdh*YOtVi?d zz*>q#g8FnP#>)-yTE>y+B^P4LdF6vSGY9ZPZ%wK!jwU;ntI(7%TbglgguE2j-%d631o6x1W0KHp$ksAp87-H*nd==kAu zpmxO2&>3|*;~I>%z!(B%e%k*@q|w^C_sbuQTN*)hAq7>&&+x@6A5NUIB4-IR8UZaM zd}np|=ufR*{+nau4qNAWeY6e6&guER!K zd`z>ruF|pAy*g*-9(^E=&f7jsT96(E)dKjEf&Tsg~!6HJx48KMtTNz z$MNINjeQSaitSMfxO-iI49nuiVdKLAODl#0m9aGxljbZYWC9}*qmS5B5;bM&KvwfA3I=-5cQmds9dCqEx z-@YROgdXA)S>lp`JV!7J>IoNGY5sb_q;qR+P=#{ol$`=2*!OB#f)WJNYQS?FBENaq zJc;)xE|{qI2+pxv*sM}NlBrUyoI4l}JnaA%AhU9TN@*{O*BuWittTLf7I@Ke9jf#3 zLU&yrMPyE!T5L08=h*xsUaSd+w*u7S=k7k}wr-@L5zq#$kn3g{ik*xXYG8K=a*U)u z;B78D4_q#~F@Eo0CK56IG}wqhyz~O+*m2Xfg8Ajq?-CH4ob95wS@q4l?M#md0B`fV z$|bRkwQeWOU5axj3VvZ_BPs-$C$jymG8XZ2BV%B-FYX97#ua}{lD1)hnPgjBcwBb z5^)?NpWdfBEa?YAoh{axpZVExfd8-`VxkQvPgO4E%uIkVpC?AM&PsuXdjH+j_v*hq zvpxSAx>k9`Z^l2enbt|$tTS=xgY(<`A)*Dbh-(U3RfRsYExqkGMonLEbBbP0+Te?e z51r{G)#QPOzr18xf4d3-34*|SjVS8hIhbk}D$E6*ngVPa?9Vi&KXXekg8AFj!9U(! z=*oZGH(I?I+#;p9{B0(2xA@J;MS1hS}Jw zB10S|hDhGxE>})0j4e3WuxfL>HOjNgGJnQ4Rc<0O)qkbxTC5wlGZkRM<6wXu;I}6l zuh5-PYA|LN5ph&Wk2AloS7a#w=Ja8(7IfQjru9`l$P~SB1I!#yrZZLj@u=33%rv8g z*&W+9OrW2`&(Svns+%E56ObpruU73I8#e1)(3g*6&*@|l+!V?W6i$i5ujR!B8DrCB z!JDB~4Z@+7MdOi$nm$BBIw-sNpSj-u{4RdcxxOfi%V7q70cKHDhJQMsuIE84cZi`R z%dk^M?>^v<$n2K_edh!7a?Y=*2Gu$5YJC#^3L^o%ricrhD=~YdCLXU>@tbw|>$zLi zP`@oi9MC}PK7du0GUcOYwP@3PEgP7yU)o`Bt&@V&Z-fsVdp1ytOR>y&fi9b^w#9E> z%Xw44tSX>0l;yb5)Djhy=uBWD4GZS50N}Oty3-@NIQUc@l)G>6`~|D_qIo6eqQEF; zKGcJCW{Wk*tJSWPknwsj@B>Q&t>ZmbNgI*L`!`kVZ7GB($GPW$l^+vsj|JN1mY5AW zEGjdIryx@E73~vurixnG>gZ^=o8ybHR`Ts6e~@V=1wQ92nHXShHO8_RsC0}4NfKs> z9$UHn7(oOKzNWFGhYmiTV2av;y*eNM{?u9H5^Zcg^pf3rsCG;!Ra~)yF&{}EsydMI zR{65>l>tfbG*e)q#}tH*`_RCq8zbJMR&j|3?Zq0*s|cel_|M+v5fWYbB@$%=*$pKR z)@Hhv$J@a6tON(*z9;gDy?(me2aK`E2DM)uxP8z=eAHulQ13-4Kx?ZUL6dl~e=`*L zy`KxfAP@|#6nLoRM0N`OTp;(mi>q9o&b_~t+lTOGFtb!TV8i+gsDT$W+>WU%*@zUd z16NNE_yobwrZTebo!-bHW}`6=as97RIMQ z-jCedcGH-TpwMfW+a_avXKa<3jrucAFupw}%A~H;AhA{lX&vFAQ#v+C0njhZITp;t z2I}%&lAplQ6LgE92k1-{Gz7cp=5QghfVWE`gBa7!{etFudhB%$C``W&29!NVAYb1= zX1NBi)%wM?vOnoBgerNlO`+eN7>Vvvkh{b-pYdZ@sx!M2-ATAT)Pjo6F>=Fb_#NEN z!&tAFkOyA*TX9T#l|oFi{ot!_E2quJSvpaJ?(*7Z5aNkWxrj$&77bwi(St!50G4i z3)asM|AO^Yal*xUDA8TBt72;lm%p>b#HUg(<<8AigX&|QG)OkliTbpxq|3;inMVIE zeO`9-ff1QKJk-^N!c@TuRmy9ibM0v#&A!!_>T}3~AC~^Zkgo@QZ`*y=3kY@9`_~8$ zkMBH<(J#^X(-%(r6#pR$6f*thVbh77$btchsR7}?_z{NpSuA4_qLKmbgyfwo;PYPi zYmcB>jjL0op1OPTQl2Z*2P|GcUw5XG?<~aQnWc> z3)#sHhU%`}&=9j@sw0D+3#c)S6Ju&_hChET(|f?3Pys#UJ#;O zy+x7{@j;;5FkRhDQNl0?4lNEjB{N_qMzN>TmDxH7d!+_`V$$>1;{MFTkvaQ+Wm6c* ziS|1`2iZ^```#zs!#84kA4~wPlm{{=aBhy$^ProRR9?5p#d3yX&JIq+BGF3zF@`&V zm(w>sflQbMdhqIr_mx#lrGR0;@f4N_4aMv(04>4br&=>yHAWFr@{P&l2Y-{$N=1W^Ph)n18xU5dpK5Ery zp5ql_M@I@D7kH`t&4TSU#rzMmGneCmS{i)H4J>nVkf4>p)Dqd!hrQu;40j=(%K58yY*>Qe2wc6uZyh-U+;#Uxfsa3D*@nmiOn3ya#FG=^ml(fA_uPwr_T*^- z)OKk}lp)6jetX*?4lg0QN&5e&Kgq1kUi6a2bF|?^dUe-~8K&|u$#F+fFs_^T(Yo<} zra6BSGKtFyl|PN((8YR?h8SMEDYqwAurE!W^I{?GIgqL5%&FlAIo#R&u!iuT}m+gbjQ49vg7vwUxZ- zr{~ct;t`q+4j+e@Yl~gl7rT1zwjlUPXT2kxS{O=Atncziz!kTz*J=%O-5Wg{+E>@0 z&Unv7qvE_u*lzt*f)R5O->d&Bi|^#gnALsQ=d-a|15aNaui3)*BZM*N!N6UtBS z8mpB2zVG)=1%M_)aB)jQ_n-c9_rZDg$>q)wUTohpvN@9T_WcM26_KrX0&yeV+@AIW zOsoeQdLy@izR@URkFo@Q`rBFXLYNJE?}rNTGpM(<@Ihp{S^V{lG1o^D;L_U5oC#52 zuvOMK5rW8U(m&jOGJ_r{TVBWx_fJbh|7Q|K5xm$!jjCmpllaB=b)5?scsE3>#7!KkLC4#sb91q$@(_p3 zj~~~RW2Je&ceE_>kIIG~;R2J_-)wdf+}4D|b(xO=g5U>5pH9Gcu)i-QC7n(zxDM9__wEUnZHzhsrVZM^d@(aE3OGfb>6933!C#2ymrOY?` zNVXcmp+n*Bk_uo-=luYJd4T156ZsO(x!K`7VDnksZC zSGg<24i*-S3bdTmOh#GEi~;V#yVtOwoYoK9UWN%U751TbHTG}MYWY|0uAW|PzvoT-RxlA>GO4F|LUdcT)V*|Z@yq-8BoUoIsBK-~4`zxg z-_-n!xn2S}Tz78h(5(zht(FjRxQ@pr@ja547I<{){EV>N6yw}!vkD|&HunJfz5w1g zH-aN%xAX$eUNcpnH)p0SwAAL;fqjsiC;7m%++zu3)}G9_45v95DAfuRDvLO021Xc* zKa#ic0kr{a4LB~<#8nDbrtS+NB98C;Y$pq z(b1vgDIX{CS0zR8c5*GAbQ_iaXp=)B5tVU$&r>17NX1GVvz6iY7*=Pd#*!h8i zQxN^#%_~8PRNtm}cca2^t>Tk8YG6ynW(|53=2*VqSX?)*N2NF^@M)fKtv;lHV{hn? zr1{zb7!0j?h$UL#Q&t%##5>Cj8M4}I;tAAB(7m#)qE)9hd;*RXpAW#{9Q$PbL(F#l z@uKeFk=2ro@#Oj|4wv1(5euLD4F{EuJ27=S3!F8NC4mAzA&QzPc5JAG8S+VeU)S~b0BTs%>g@=6yrP#c897LmxS0L=|_512YX2s^ZE zYjr@hCg}Y;5#>>W{^ImC&8Pf<)c$22@A++>?&1gYRne*4GTwIVKXSL=M4H*eEFs14 ztwYqt&u+CJGK>zfi@v(>SU&E((Wz8Xp&=7b7e|5S_x+R#`oPUAXG9nQ$J+Rf4QP;S z$G%Rdbnm$kb(vSCcc(0&Fp2ycWeUcVe+ZXWMV_yccdpo`*$6=olx|q)6cBsXzew$B zfrl~N$GA02Z&PJ2#2e_-8dWo7WjQTQ?Rjfj(K77M?LO3MZ%_K=H4x-UI(l$A>Vq`W zr8cVGJWGP*lz>aE>3Ka}Y++zS>(*{Lr#f**&|sMb^B<%kuAcAVxpd-tq#@VK3m-jb z4K1VZ2^yY6qHXCKRo;CDzpmaX+y8<}aCnH4NLdYr3TKJh3F77z9Q>WME^8?YahA}u zHI&>d;>cL8IAN9K?UdMrvk_p}Mk}mz1WP{cY1?g;nlHCDZR5!J-C~P5_eVlF>d(HI zXJqzOM~9^YC7sA=#s*O5bqW_}$31BbDnI0lyJ;`HDV#o_S{LAG+^%sZwyJ#fNctw9 z;jV-27-BO~9AKj|ne2x)$I`W%QuCFp5%%`{jE5eMws)t3)HBZax8A(9oFfvq8twE< zNjYcLty8_-U?$(_mY1iE%ShEyi4y|~-b>e9l16_1qoB@Gg??^w%Fh5Lm#_kI!adcs5dw)@_T%V#dGd!Gy3N7T15&055NoX3^nCi^SxE)Q4Sk=!*r zL8|Tv=eS8G4CXULDESPCP)`qAIOwbB&55g)tX}-)j=N8Ot?yE5At0DQqX-LmZxFqH zu4_Nw;#Yve)YQ%v_27#l2rZ$$t~h6R+>%a(Bt!8(1J$gCjH_~+6+*ef$&-IBSifcB z6Q-Fn3WJc9p1&&tOcQ??s>3h7AH;x;kQK97`d5ppt7VB^wU9JVwTemq;9Zs8KI6)cqJk z$WJ`xcegZ^LR#-0#Q40_9WWoOpJdsw9Qw|`x!AYJ_gqHPbgvFZTmlO3ZLn%?IzbJu zm3kESuHt=lW)|=qwwxD)szm z>0Nz1nb|$3+n^8__rV+9pg}xI5QMG~*V-^iwN&8%o8khS_1|V;0<8_5uf6=&i`)~W z96K5Los$J_k^dz?IrXWC{hc&t5#(G5-hD^K55R7Ew+h*?+MSF$B)sk=o88(Q=%G>m z*2GkQX_#C(>RcawZdH&^&y%0o3fvcM^~4ZZ$2uzIUK zS^T21YpqWtX>vgF8R_>Z9`$yG)=j)&C?9ZrV74mYzGhOe`zx_7r9pDxY{t4KM2=G) zZuxHruidq*NnQOw7ky(})qM~6?-?fhoNYh0Ae8BIaEn(z4Bs$3!g#AhNkam&Xqe5h zoCqL+9(nBb75_X)<`wyNj^(Dh=X$C8!s#v`UFwse{9kXL5)_6a5V!64TDNW}Ik=PS zKrw-*;`4b)A?tw&j1zx@9sSHY?!9MP&>eLIjo$D0uGL8Ih?qxJ??Jz_+Gb#8N>9$L zEc0653Y!>byVmtR-M%;D{mq6~eFOzVz6oE6=EypO6yz+eJuJPyWBA@f5j&1bb6qvN z^FNo+Cl`7HfqTsaF0q=J8eVW|7jcg7^!_|^{nGgJ@V-zUklPRiVcS8Uz#g&ok6P)v zHywTmzGWREiN+NU^acQa=+zbH2|`rwa+dr4!$yzmn4h z^kZ4PpmU3_Z_PciHY}C4usu9b!Rdv|(CZ&124 zyy@*ItOiNtp9(DZqJP!0*sGiZ@HSko#X8hi+^SMDuC*jopT4-3F;zpyRGyuhI3+IY zVI5JaJf-3x=7KRZPCHg#n4N<0XT2TO6|EH730Ia(RemnHrERMEqIS~ija9e}rb5u; z4}MLCZtAkQeWmLQ)IYGU0BMS=UHY;dxNgp%T1?l!dKn(Sn!l!#N{$Z>?+k4(fUc|; zZG7`>_PQ-RMAS=dImB!IRZkSLc+k%1QI4cYJvy8B!ObpB=r`hQIj$fzeI5){7va=J zYVa*@23Gz4R3ommE^QrO5RtPPRX;4^909qk2H4o%2H1kDBup{FzD~wS@rj6o-p;db zf7)|ThOm|>LFh^U^0qoLCU5w{qs=x7SCI2a7&Ws)L00^G{6>hyB1p*~s?r5Ru{^wEN>xKXAbi z#O#QZ)g3m zx(xSQcELe4uQ()sLy45}Hhc;6{oraXzSgJX5vpQ<5esvPT@QmW3KLsYpfMlIT!e^9{o%^v)bAkF@d@eVp&AA zb5O1t-wy454Hb@R8l2kLzFE|b=w4|BbAhUJS@Rt~{)@Ytd>uAPA^_omD4QxsWv|2109cvz@%WYbW+{$;L=9Kt83=O-d$;u zTmGT7lRfThgbS!YAOzla=mJjTN;3h6qyv0sYV%8SbxU7e z=v@{qBm~aW$}Z?t7|X_(^VJE;x*nCy#Q+;O*O7oBa(XYRuTtNJLxxAVYb2R65V9}+9Qe!z*UTm-BBGb$>SjjoQe0r*NUaMU9$6`x zy_hDhHSE2}sQ%J~mTCMlMTgF5(y0 z{-O7WmvIFQA?2NjqYUpmq#;3|XgfL}JWZPYBJa$v64$tdZ8?qOTeV>MR~g=UHi}V~ z1_N*1^uFNKj@5ecQ|paasGcpxLOWDO0A7-2BkqwAR6YYNB0qy?m3M#)=8U+VnQr{`C-8qb=F6f%vY)fjS@5 z^u;#FWBkcAR)8&o2m8Q}w{k4G@MJz?`ED3In7kN4UutL`ZSW#jq;r=OK2Ty-!;Y z{Ojoi9Ua`7*9SC+;467Dr%oDhGl$WR8l@62^`53Q> z@B4iAgryduV+&f(HFBB4k(?osJ2kjU^Y&%F%wwpM_-os$cO$W*>ea8d=%gQ`b>SEX_}wUR3}_U;m2+zKZdP#b84R~1VC-c(_>_k$ zakG|GK3e5(UpIJIisrG%*6kB{;F8qMC8UeurFz!P%yAV&-})y!JpZ9&i4;)g=(`wq zbFF|!XOY_adth{{4f?H%hZ;Ifwvgv_K1F{g8tmyBY|;= zqXgo?9Ux`1*#8b(@C`Mqm;o!2h`c@BTM@fo*ac+_tcZ6lg!~-VDlh4a6;htXRr=&y z*+r&2RQI?LiK{nngZi8x{Gj;H__B_4{8Br&01{*#Wa1*7Q?_imjB5_&{$FyoWT=Cb z5lzS7kx&h>>h1;TYr~h38}4LLy0m|s#@g)<&jZ2fB=Ev#3zak_x;vI!U$7HC&c{6> znYAiqwLen=`5`2f&piQUZ0+2$^;mwn-2DwG7=9(do*1Ri?=V-K2b z=+T=D1#{M!0b|d=;d3L^4dHO+rZmLsRH#?f=bNVbH!m`xD%petO%_0&1-o9dysd!T zAKqQpul#{z6*C`Y-|FYICuR(JRD7Ko{i4n}hOdt<+_=Sw zo`IJ|u(T&BXI@_0PPQj3@0Ht5eK{9)w`&=d4R6RMv7FwLbDcoRUSTvofh#Ab>F`M% zH{`*DlsM|7QU?PGJnC8^GEd;?j^|%Uw53!BiJp(;$4|aNc0WxhycKZ$F}u1=TqU;1 zFY5e(C229+Qa$V%)srB@p8RuU-Cm=Uh!0SSt$JPUh%Bi?xDWr#IZS28US)m_pOnGc z1p|fmYF^X^Td8G&52n{+AyCyj=T>WYxN~nA^~pq`sebYIvX9cE7rb0|)g9%VOVb2! zEOhe;#6|&4oH$m@*JFRO1msb12;H7kD%3zVMBTN+TA2%&3|}!P3n4(L0Mi)IM{eGw zl777y#t9!-Pva}=S@M|lC{wdE66znC#JC0|A9s^Etp-97_C4c8bLv~_VH*tdNm;Dy zej%~nh-Zpx+St+Djr6VI`d4W-BI|fQgF%c zp&X-%eyHn73yu1Go}+tO+rF+@1l;Are(cQbMVQDM+UULB^ddVVH9Ki;2e7wpkx&@< z_e7sIDCiF(CIR}C;=m76JjtYZGNTInyThVjwl&F02uh|v7ZC&hDp0}ML34eAmGkUjlAPQ*;48d z-?R{}0|LLj&d!9pIIseZQ_Ph@hRr$jt=CXe^k>4n;=TLh!Uw_0woO;GiY8| zL5oWa`Yi8D{dAo*I}0S59bFgEd-&Vp_+vxHW_bCRuq$VVIT1Gp;xRSyQ}i+(^(!77 z&Mh6^PO^*(AWCSjGhN|st`ICN@|#k*6O`60u=08NR@Vt?2#d-R^(Mw+LuIXJ?aP=S z=+nES4m#H4v4$eTW)lpOgA#2o!l1P613a~X!YZqbpeiDv6hj7au#3Jq92Rz$DBa~X z;u6Y^4(LE1y9@N!uCbm(!t?t~ixw~PJ95zO`NasVF2cSyd3lj-rRRzmjIe)U%SBX% zb}9Kf^VdanX0|Rue&Ddc!~f*gGdkx8@!Q7h6Oau)%q9eJiUMu6R8F9@b}0xT65dsx z0%LpO)+5$Jk&rR6yCjWA#dGj@_$N|}hkE^Rxx6}Gj!X}e%AHb zew69H9=F5cJFTtP%-%gj#JjE1R(}8Twrp^p(hAJ!nh!HsP7!Ib9ufAvzj(3vwx?rK zyy0?{Mk^RIg^%Q7vlY4SXc?h~27c|es2{N-jVDDPL)}%roE1G%I4I7B-HGx|2}ztP z-j6tXWZdg`w3=|?GfkDNDsuy955JGomjZ&(FKEky+wF;l;zFJp8I_g7A_}~c?2jf* zHG2NHhQ%BB6kOkecIm9PQ1Q^_PS-IZ^)R8DRL?$LFTB?V-eXdxL;&1914Fx)$-^;M zz|(Dng&+95R-dI9HNX8E(Y+y=mdAhi3Zv}6T!C{-ZQ1H4`i@g$+Wt(GYE{`oE&xw% z#0!8R=JZs9)n^JE?rLW<&glD_eqxUFY{gPgyd%wDw1iqz$Gs9HzFsiE2*#@$a*2ZD z&{Kiv^G0^G-SFVLd&j%jPm%JN#RKaq8MKP9tQ&1mRrq%eT6d^D3U>J*n0%jAb!f@E zkuO%^rQ%*gZ^IpXV^gSPSi9ddu zyzkV1b9M6YF$Rfujx3G$9@ygbTEn_x=|h_x@$29CJ`G2+gCFp~J$TyXUh3xG)q1g4 zd|t&*Z)Y|LXTC);XTkqeD-|k9OR9OcSelIlLLa4`Mr+>)4IU0TxNGHr+Gh4*fujF^ zO&%|3e98+U?yY)+W~zvzR*c8MYVZO^U(iktm@v!3!$X_aB3pOVZ&F7Xc2cv%qN3?r zUX2m`G(HvD;8JHzYmQH6l!V(#lRo#cm2*b8B-A{sOVtE!IuWAFyrMamKr-UfTdA@! zRVmd+8?+NQSrBe!1x2PE#^O6t2C!KaU&r6&!M#baG0B+9NcJLy7rMKNKl@-;&GnpJe{|%pT7nf&%p^d$&)GqXl;)vo5j<u@&*XH`&sg zB44p(^*%}wr3i;Sc%BO6mF07Rt#7^s(|@c|H*iB`q;zf>w(k4=gzyUJ;xQC24_UL{ zo&sGJLRa#HerSU18sjhRq#BHJ!jo{n;Ysr-7TlY`Z5wK5q!FVP&*(~|u{4;&aVN81 z4IV3W=-@7+@x@Yl>to@QE}rG63#sLp(37Z<41w%lzrh%v<-)`94CoR~C8zE>Jf5rl zNh|l;rz^MM`E?P;D~nVwwZJy$1(yJz!4rYt^DF9^-8m-*3J3e%6NBcOlhl8C$u9|) zc(s0kBTZ`C+7bgja@vW0&55yEsTR&+mR5YUBxQcTNtT)JgObQtAFVly^MWkJ@}+E_ zb=rA=#!)!(_h0*d{ZI7Qrm~k#iwqIZP%Upg`W;d?JTh$3j*-5*ZCsh$BwnbT3*H`L zdD@zL0X~8;TW-BZ+-!rqdy@l&l+TRhaSzOY;8u|JW4pc;u?xh@w7i+;`@mxFG(3bQZ{8@v9|Rik;3>?xkFx%FVA@YEz~ad8Z#_X* z&tF7|1ir$h&Fg7!Ko__oj&ogVpexQ3WAiWTtl=u|B+`qd#~x2$LhtN{r)uN_tLyja zq;Y{I_y`TUEX=(VhvVpDd)vS6oD)mRk|)N;P=1Q9S5$a(Y&|CU4+-y6&_F_mzARYo zC}z;71%QTxC|}-xXrh-uo_50pXT(pNz=oAy^%%lHVNbbyQ3lRvMD?VW!F^wLEOSnJ zULw8P*m@0fJCSxQWSrd5(*7XYi#7x&^C1sQp%lTzUl$`s&S@2#O7Yz|s?UwkcxiTQ znZrchK6?}KLH)Td+sGFMc;C5b#E;5mJv;=T9t(JLna(%PBMOpW2Pq8CPJ-%X0UY1{ zbPKU4ig{M0V7{mXMu>Tblz0J-ZVS*(8+ zFZMHc1Ln{b&hC)y&M!t}tNUvhb@@SGvk;{9Oh7H?X<+{VHi^5^)?*P$&=E$lbH{3& z`jo8tXu)&<#hm-(%D@ZNE{_h{xykh^YUQbtnd;O*y734-Fu#a578oHQtG2^;hUjGhCq>}78 z@rTka2+xXd`!(75vIQP2ZIETk|f2tKP&M4jzfH zgwrDP9KxhorFL(J@v(y{HSMdfSRCU5ZK$|}a6#5!85K8Hjk2x)@WT3_6LhVjYN%t` z5e?_NqlER}lA8pZ?a=}rr)i{jV{&*$n1UmhGWZnxj%w2)ORSQWr#r`n=wny+iKmw@R20MO15c7ZtF) zqPg)~__Lo*fIiH#a)cmWx5ejWRw1F$(XzdnNt}7>^WBV^ZjY+vl!3R< zpB9F?T)B9RjY@lVRC5Ok`nM1hv%S+cPs?PL}>ur!Aem=yLjM!IK{R%LO}lfyQ^#de>UmX8M?e zHTP;oR%1v{SRCs|{A*~^vv_%|O+PK?^e-3wIxevsAz6gUQ6;1GoZSzK zxkPB2i#YZtRxP2pX|2`vs#tEvT>%RO%>P5vcZN0jJkf3fNRuLdU++3~N3}oc+kRbokp#X5>(C*`F;b2J8iW&l!Q} zT~)m(;SJOhjyvnaQ9+oq{Dm!+LUROB4#o{gx#M=eY7ke7N+Mf#tUO8D2eukAfLYzU z_UtV}N@%X3ucR>|Dp2!J{Ix#_>vo)c&0x^b5 z!JkROk8s)bb3cY51f{m&suiF{cjN8@G7r3I&f;c~T|#gPNO}?db{>xPZf-oZ|KlJT z$}swng2g|nCPspBgY&SLs>i)M)McRyb){1OXwW}WcWXOPl}q!?Vwk-7D~US)m(6ke zCL^xeNaj59;PQQS>HR%R>;HP^ITGi&5Bk_a2M?=i_|p#UxSHSIzS{4r5IRwbEibw8 zcK-TweE87l%}O?GRJ4H|_4!{lHk)|tt8~_$n1Hv60LE6;SUcWnI;3-&H*4h6WM7B= z9hlmf`O-2FH^&PbY+%A+uc`{xQUa6nnxv`oUx!w+hi4E5Hhj6dr^bs6eH`r}_^$K; z$=q~X^QnCNBkQ3F%xdLFkgtdF=g63G{(Dj}ITIN@mTYcG`VU;SajOz@RE>&FVPRl{ zppyjf?AW?#s+N-tknx?Pp^3!TL6G`}=Pd4%Iq-sT zR>kRvxOHYy`FtGnR@&wyrcq}w*u4!ltsjOe>HkU2)N=W?-8}Ah=h?VWaRW8wGhqzJ zb|}Dq!K9!|Mz!-ko!F!5DbtS(PhK)EugRw$##^^#whl2DTgJ%zjPR>Ngy#DgEvzL9 z>=a|C^CB_RA>e@WnFa#;faUzTW$hbX^mbQ|HOtjMq;%PBVCU8`e^=LNdD6CZ;?RAf%sH_>EQ|EYU z-<$zvClu)f1527-!vcBN&lP+j>+^tVjpqC+D^raz+d!*YW=f3)1f0xhcEu~Xhk;>K8m*qP+71IVCT#b3f~3Y)Og!h z(acyowySA%G&iyR*&&>vws-AC%^$iW-%@aAZS+qr_J;%OMG8CtDWGAL z$!)4lDV_AtLM%7W2~Srut+(6X{F=`g(L}u-{PHI(NgP;`QNp$;`q=P@KVB1tuj;aRcQ?^?+BVm&-UVASR!DK%b9UFa zvkrL02N+k1ySG9UEc?4oc^K2VU9DER}6F8+eR@0t$wed9Vkk<)eh)lwJsX7cbTX_CggphSQ$QSGy$@yyx)TrHLxQ&&71Uo%j{#b|goYDoaa?@W3A+{IE{_ z`JTUcry{bbIah#tsI;JCZ2JVm3Epqvn-?%mEe(50hoZ+Yj8G^5=RQI{RqMVgu!bFO z6^kh0g;YCc9GT}}2!I_%W!pp$V<9{00SpC~SWjBoM<;0ilQp5581}C!EhHVHcV%g8 z%i@EZ6XBFuhyL{xhk3jekOp8Tk<;M02OHsOgvM{jQv1D2@Al5~C#H)fj;tO7 zSzFj9;xwzeF{>h${re?LZFDPKh1P zl;8XNQ_7>v@2WuR8uZv7XOfXG^M7w(y@KpUs8`In@~BK0tDv+WWSoUMk_I#piJ9z2 z7K0c!{QIbaG(%^U=SyYe6>LHl#7mM7Ul>wcqbh1T?2sGgIq32)s}MOki!P{VY;b-{ zLYiDxM7-IhrEvyEdV~JQ;AEG@VSqmh@JW!v9KS((vbW>)nh~8gW#ZLT*@moUThXd| zF#c?Zb6-c7UTl8~>m8w9qW9FiIVKfjI_Xf!=5*Uf&t(K8q^rDJ;p zm{~w?y|nZ;uKD9X#Ixrl`D?l6RuHbSkSUY-T-D_LwN-bJP+_rB#|QLc_7ICH;Ptzm z!yIz4Z+|Z|)JxdTk>^QTj>I!N6s!^riyDiF$9mYiY%-ZeMxG56*je;1r?hH5*mSv# z;J!LhXC!m|LM}=Ff0erNt)CfG{!g~=)k!bdCr^P*Wms;r zYB8cXcrG?#WaQ-SfHE89ALH}_AA z;*V?&Pm7%{cbc)=tpIe2+|ZgPJ*=WmN*4W>Oq{q%`{P((2Yb`Ht_|QwVVf~5f*qS& zD0iKbgxv7eqXXDD0A!p zGb_W6T_uk9K3?Tj&xe!P4@W-~l=b=ktucp9+Gvk3^*pSDwP?+6pVgK|%)MjUu;)&N zU*i+XSVfUjK0Q4^^J%>9OMU=(S=WKSAA4cq!jVEJ1&-^9O4c-SjDHS=dZ?@~W?FYi z=QO}0P4rWE*!qGt^RT&^Jm1h2q}nqxDVdL=C>WRa@g_;8EK(C;~bHh6sza7DdF!wMB~7A(%V!3IP-)^&sAloa6n<$gE$(S{w4Ou93LG zb^H0GtXbREUAe;Nc-U{X@4oGBYQPHas^}e`V_xoa17C>o98u4DDYfq~rsuY+H1uM~ zXAs=QV&sSH>UzKXG@){N<8vIsNp$%p?gtfj!RhC)*?c7+y?B-XMqrOOLx$nTbDhAlu3bh`0Jak=N?@W7w*CVwWILF z9UXp^h9Z(SdJ93d3gd&{UW-Sy%w4N<7P*$vJN{XArPcSWoG)cFiWYX zrUR4Y7$!AX&nn}~*Sk8|Xksz@DEm}%jqad@9aNR~D;blANUXoziikA9-No&Qv~1+O zPbmH#leO=LUG5E1TkX=0Lwz1WQjBOQ&n*8rrJk z8U<=5kY}1;dO+dp$zGJ)1&(aKT@4ZW^anT>3eMz)_?jwU;s|?I$+#QY@<=E4UML|J z*+W@+CnODANFVyAdX@BE6A{1S-umMh+7Q@Em%@B;1AM~afo}kWqahp=Yn|>``1Ro;a^HIdkfReW1CD%9opC( zy>Wgl$0v(3RgycAE+a2?;VxP^E?${l`@j`8#s0_RxJNC=oX?7Xc9N z!QI?M(XFu5enR|!-*TVE_|VegLutY?88&EFIO7Ngat-X9r_Yz{a^hHdT7*yJANO@v z4J8|NSQ0SDT}_Rnz6$J(M+g^cp^{61&90M?1!eu8J+3eC0i>7g;U=;{Jv`TN?`kjg zXSpy<5%tc60Ap8x`=5U41jUF<*r)Yd7NXX7KP{hRYsC4@bmXjtD<^ zicHhr`v?cUo`r&dJ*ziKuVn>C^Kg6}v_c^4O;rI|PmiL^idiWypcJEfa4am^N0b%r z2t!y%4LPGO9l@>VgwgwddOSY)oY|Eup};;}aPu-3KGuqj4Bq^D1bQ;~TVw{8`{U#4 ztkzr)h#Q_P?g_&vt4s1+`}H3-i|_ebQBUr0X97>}X&8X?^fs`^-3}BQ>Gnc#rqv~2 zdY8cm1jNHF;D9L;>;4 z?-eOxer|htpKCmBSnuC(_91R8_fDJD4+r<4u|FrB5BWCKfRhb0t#! zAO7Fkbi#S)IU9D65i(+$yvLm%XOZc zHa6C&+x0Y~(sw!T?vxhZe9N~r{_#)H4p`9%Z)KVE9PwE+NtJX?3hBUaJL2}IV3(Fj zaOvo%oj(IXT2kTyZ`3EKSlC>hY>|{faVDwXcyD%CG(x7bK*9clR+{a>W>y?&R5n$+ zLQ{K1O`Y!ed5;MF*2xCUmg0KigTbou!8LBwVyK+*;X@rY?$1@tDTGnYD=DPOMKi3I$vs@`R0yn=Ano?5E)nh6|d)QTR;wBqi z=@U&h61-jc@pZf}Ndfl{-7Uv}E|rJ?iREBYyv>2XesorLFf!HqJ*gy!ImPTRiDWN* zfb$2o`ezq}1Ca9Alsi`kZ`ehKu~>zJY@J_LXtUW!>+1puMcS2^{vd zxzxeFTy=RS^Q0@GL}dH(fV`r)+2*UxLL)wHil>lkBUFN8wB|?d#DT+4Y^T-fxWz>` z61iurxDTNiBujD3yl12CX7E0%mpO4!?gFFyqGwOawrZJ0#0zhxVLU@QgA6iRw-u2C zNFDMOmxo(kL=IQ+N}%BEoh70itj-$#%ya!x~a^joB403j^r5DxYqWk?|RkgqXg@@iR1X0Xtz^5MP>VGjZZ(3fyM( z8MLmMMW)Zk8BFFw_WJE6i?~CqUrQGAx&CCK$ zGiAh_y-I5|&D1iYBY!_H|A)exbHKyy=UIOo*mu!2AL-iG*|@}u=$V`1myc}3bXjsp z#Qo*e#%u0dPf__Jdkc%~(_Q9Q8(J@&P-#ll&sba!4FO+@3|VSf{rgusjCHeJ2L_s+ zGxG~w?XjnkpC{8)vZ4y7Z2Z=oteFh(lFZaytcyHe_t=|5jgmT~gHuX;tb2vjC1!1C>uf%aczfZSKb za)9;@`TE-ubho)^c8=TLK3sk{djE+zJ%@LjZ2GL~6WnyKY^iunu1>{{nA!1Ce_=kQ z<$9HLTI(dvK@RlZU#okqH}`(|iG;;_(vgujACho7+k0zUYy5UiCo|+TFNbbc4=qy` zqd%{+}A3`}xvTEY37e@V(6fI7PKNYO7VB@SI7eby@gFJO0<}w>fpr zzpKOrVGPtEe@P=q>XDw7j{=YN`^uef>MVQS6_EEy&vVqB{CMV;TL>7TO^sZ>tQfvI zvY$@6diwwG<|$<{zmK_RKI(82%R^a)Jjd16cl1N~_iT^}<{yxz5c zCg@AeJt17Q;(G~Kn>m%|Yg7x3Ov9!%o)nw|WGFapHwMWvI)^CH_pSm^($HY+vim{PK&#Hhyzcqy|Yspd;y~ z5WseI^v)98KDL(}m?{fgl%TV0kJ`&KT;7}!0VU~-6P*CLRp0cHS6P{(@{<3If%1`Y zmB;Ax4L=L!_M>Jg^PN-MW8~X^B{{XcbvKmopa%pls?jkUU3DyfQEInRi4l5V-3#lY=$nk)DCGg)s%@B?kbSQZtUjVKe*xR%?yzlt!P_W9H|GO!MvXOsTtSh&kOkdchQQFG&jc7h~|M{y|xD| zV~usgTYE~M)Y%C06!{<>VIdg>%10=jLZ~I*6{X}OV0zg6tO?3pfW@Xju}29nID@5p z{(kzjP%`6Q6dccjhVm!D)3)qY{80SZ>O*WraP>L) zO$*hnxS!ma&N$?H`({$6Ix^>JzY8lt@z_=P#B;77X1zkvoq*CWj!u#jbFZO#btrB-=AjPFOs`P^e-Xk#hu|a^kNucpX6CwuuY2!_Wzte zVpsp(1D|pKZouEJPQe{JnI}`qQCIX9N>z>MEsG5}{qmK9wg1kpeqSEAS74+F%Ql1^ z)oOU@+(*iMd>5!*2aEhgK<_$2zqmaliU%y+Qki*m0w-a<>?Uv^u`%9J@tjNP#Cq6e zTM+uW9}17Yen~2e;sSR(XvLG~m49AnRDQBv{3WkE7PX&_qhL}WI(DXSbnM72+T`Lq zB-c<9i#mRGNfJh_O_#snWGNRrF_U9Ds1rDojm)-vv2O(=<)>etM*$C>;hjVc5!L`i zU5WFAppG*4%Ypzxp^&0DI0WjGasTwgWE?>QdkJBG|G=(iM|)B7T^nrsev(UPdYk&8 zoRGZ(HQq!5nQztief_bY2l$<;D}#e{br_Vd>m$l^O=jit*NoyGEM9LNz~D3iu^TyieydqGJg0X#73KOV^l~88{TiW6QKO&b$U$$w=~5ljuEmNHT|6 zV$i3l3Y7{&q|lj3-u!~}y$LSuN@o-aT}HyBOZ7TaD*px#S1!!+h42RcE~4Fw(2k3L zv1FibNuDSr7wnzQDCQ@)%NT^K{q$>+XwOSVP%MXWejc09N|MO)wOh~SvK*cp(j8Zj z%BKAr<);J-7?Cu{3kbrHFl=jZSf&4!Es`me#%M0oVV#-Ju4hxQc)RQ~_KL@5-Vd=W zT^pL9U%TNpyiU^Z3=(UoD8-su^~cFr|WoOb=3iz1h&sXSDFYeMAVDc&Xx-1y!9f<$n=poC;11xgm zeiMvarj#xlz8OksDe`0X)XRg~sHSZZ+fA3=ocUFH*~%91qTCK-GQ&%Y9nDTLxr-=^ zcEvHSZG?>6YuF~8&SD;Md_oc@J_H+fRm}h>ynTN$_xp#(jEr<>mMCZ(B@Qd+gl=*Z zYr^|9A#?0!Kz@3M_2#W>eeR)=7}AU2HgKZqLt+p@af38n2eP@9QZEI zt&jdDQf8l-QjE!PnZTrPDi`_X98oVX8u8uZaJik(^v3BkPLz~dRI4$+IdNZyB^l51 zQAt0IRi$$m>^Z?a#!HA18nRqrO*N7ZbxUosPZATxRPTH^a=;@In|0#FI_-YlQslnwy96ieD<4#Vz`m!?e@R|Y- zIKwb;xRclV!JV*7ki}#eQCZJ|F;at}3yuUYtD^yx$5w=7%|#>K7lwU`CNT0QgnyRo zAUE$5xBkeA06*@nJDL~^wj7y9EN+%b2m#uT{pT<72}6i-dbE%epQ(|~Xpfp=N5dkc z1~Ox~#{1J=t8YpTg`bqIj*VVR`OXo@PYJrw^Rx{qw&AcBpb|{O7iRAxtYtFEABuYj zXKuO{J~W$-+9O+S9Gq=^6ZSyEd1lZd#sywZAb?teDy5jo9s(G>B&Qi{~XK>Dl9rrHl5MKv@&Ln2prO!B>Y6T@X zgpnSM!!C{#@f*d~uP-J{>82WfG$1{;=`yWBWX+^`GiMT>3io>W9&PbA@D{t)Q9P&m z!o&CeO?=4<&b5Z3l_Kv8WyAMo3d;5Dlnqav6#QK&i&Owx4l%UGK%S z^sd;tGdK){zr@z~p!kYK90H+#JF9q=f&sF10jsbk}RK`8Eb#Hz_ ze4V0y>7_G7t<3-2w$Cp%8B~yEp`33|cl=THHq4{(ZN4sLV*wwewoFsV(+;;I8KGLE z0KV&9qO51?{tq$s8>hJh-yb>0YbJtH018nTlQ(hK)wZ5ASak~8d==iwYL%Q~uNHHq zdA_hy|Q}B)Vm_S^}w7#q=J_4FcvM zqtN@b(4|J-N(&O==54IQPk5Q~hN}qa`@VuuA$ITZet*wno!t}iCo_FRxt$oO@=)@C$&WRpN~t^r&hB)%vnymy1!X+c>4UDIG-Ci zyC=Kr;k}_`S2g*cGPvHV)9NOvJrH`DLL^`POg;5gqX+7a2!hENIHzo5c*8GuGSm69 z(GwAz4z)pQ@SF}?;?T~Ln8wEH9z|z}?yPhEVnV20rh4?N+ITmg{SR`<&jx03bGREC z=ZdiY9u%qt;jSuot%M=d7=t9E?s`~<$_$1O0nQ^%FDse9&b{l}ohgeCT@d|JK~(@(Z=T$2zrKQ_jJ&ZMA6xgb z!{;d?U4nB_Ybl3{8!U1@W`^e+d`DH>_N^m4$r3Obfqu7;h-ER%0np0@-TQ%@sk>j@ z)ijXxa8t7ggEF%t)knC5M&r{De%d%=`a;O2?N4k|f7g^ZbhOO`+=Y`3ILp(o|8kez zy43h|7I|pu<=^@sC5ptJ&Fngcum>ah%GOW(SDjk)b-I7_7&aY8m(xQHSWbm=aPerhvF zpE8SZVQ8IoP?yzD~>0}%-cIt`}RP!wD*fPVXkRDJ#u8{jvI6OU-xf^u_ z@otK|02dWx1qXB#^+|M|f6{})bMJfX`~Gjg8GfAebum2Up8DMedT)kTT}07>^&5-A z*Ta*?!PUWYUO*^Pjhk3}Z}|TDYdAZ5Q<)2|dKdcZE8jLslc6*gH;^ERR{`HpWm5aU z`Yzbsl;i?^o5*l!XJfoP@@s%#X8LX9$|8zLa@aIE{Rgwk%U011IJ1(ergp11DlQ)O09KPIT?4}h4BL~BlWa$)-F?}kP>dkUo!I^4161790{%b}pg?xE; zfF<$XVq6Vr*u+@U#Ffy|;g?DDk-2f-D%Wu2Oxfs1ot<(E)O_|H9BjJct})$aU-$?C z>JmkT2iCYa@zAQxI@(TJKb&lWZ8GT_nVV+I*U9eO!?@se?B9jYWzz2^aXaAj4dKP0 zG@~b!E>k+P+^ECqDa2jn(XYBS+5Yr)*EVAE&#NCe-Qs>nht7P~TwU+3!<47U-`t-b-rw*fozoR4=n zpz6Y%K8d{2iZbzVS|N}t$*zAxZ-P0K89PC{Bejb|i!7hFOPR3~YHH$xL8&0I1*O3U z2W=7g;0;Sjh9PIECvCiW8tAJU08fU)(L_$5xP3ADEs{X(WYHnQaC5$UsQ6|F;oe@s zSRnH(>Ud3V0cDQrsEqI{M(|s~rx51DuN}|orl3qvVM&nn?j!ugaJ1eH<;HhUdFM?< zeS(7)w!_ux9fm$NJ7v|@+fewT-whH?g(Iqi@=;L($7)`lU0wWhZo_l%LFl=8Bw;a) z!FUsl)GoClxxqo$!#8G-m}t8W%%{D?p5z#z&7#?Zr@jJ9J@?Z$JvPmjR28@PGC9sZuHX@mAxL^J_Ve?C(jXQ| zm3xWMOj-_i&TmKo^5u?Q(GRB^MDMXYGUKWZP_GySPCSRWxAk{upk8jG#;d3>iT~}l z6v(D~OfB%@MUVxP{3B(GVw6w>=39RdN?oz6R|kUW>_BCu*eh<9DpRhpaE7kn8v@M5qn1I6dohXW22Zf-M%X8nZNZ_^1r{4Uiu-5 zKxG+eE+lM%rI0cWP(}*U%!&AeYz+02y*hAy%BRu>ZdrSEAz;fQzRLcy0fpce=v zH3_ZRGCHy0(<7M~K7x4Nx-ktoZeo<|I4dMDhkdjd`8sRp;?#=U3RqcAh)YSPW2cD8 z`ZgYAUGLYs{=kx1?;CI^pSg6!g`OK3`GdXIl;gY=}?@O0m6jfuY7;zd6TtH zba|njc|I#fT2P&inR6~PCn594ZP+i{x!Jf43&dm0xdS%*gxB=1`KFPW<~%E)I&tku zI3D{3K4K}-KqecxSmYb?4e2yN)bK`~Lt`SUN8O|S6+4hoZ@p274FMZ|+mB)F1E*~;H5{*evflGl`0Z*ZgJl#rX~dy|A~ZG;hk#Ri3-?RS=86}a&M zY!URf8|ve$$AUqKj=*?J3a9(jz8kbwyR~B+#2gq}?Zv+f;!BEiVf@y?m5u(tIEyR~ z*)m>Iz85FZo4L?sZqehDxMF4Ew>ro2u?F#E7(NO-9(A`J6fB>AwCCw1Oahg+ih$?}hX2KkgO zcveY@ti_<5bS=y>H$FXY2V&2QkG#tqmK3k}{3?b_`$v4K#G!o&gAchsf7Aw*zz8D* zVSjnhOfGBRT)4n@#FBj`8U>6vVAPa8W!XrOp~W$VO{SX8{K5I5iQycGDVlix?K{Lz z?3K`XOx+{OcUxv?k|-y>cdg6d9>7bVY>PgOnxPBl-KHI1^qUb5u*)QU1XZ>a+k_Oe0=q;I9QOCfqTLS^ z9+ag&%>hdqx@?AGIJn>pg}xK8M)jI0uC~K$i@kdVV|E(`a}rjINYevk+19}2Vka$j zFUB|^Z4xRiHGi5znj`~<5eLHXVhF;`G=#vGUkzpdSravm+?pAgAwnM!CL#qU)aeA3 zKIW6xG_`5vmmC9o7iM@9lW@|r?I_62-N(gpuf-ZsOQ?R2KC*2oN${hcHj8yrct`5}~Y-6(uez3ODY% z*kAvR2b}nE_~t+?+4LwP3?tMb<57=!`MAHb_C80GTVL7;ysWJh1KX6qN2dE9X3EYR zc0WxtrKmEV?ORbpe~35?JE~ho7e}Ee+|!DGHbDyTeR<9-qLWiN#(&uQo#c1r^aW)n zqm|Utfufa}3aWM^Dt0Wh#1`tJyS(0*w%%!Fy4GX(w!>z_~=*qlAd2WmtRzr64tm3k-Jq93mJC) zz>tNLE&>t5VrTm$A;^rU)BFRb8wOSo;^Kbu7(0Xbj>~=8CFJrxRh^f)%4R0KSEQH6 z=D+6Q$+Rf;!(&sCeqg_Yvn@!%;nPttTKS1lib`*FxJ@-tKMhjNJ6Q*v#6e2jxgMng z+5L3KqoqY3kRVv4VZeM5J2he_-r2ZPs@b9h$s}`lb2Qmv4!dcA9u@g3cwrbj^}sYEaRNSHw)uU;SMF0B_kauZuQ4n9F=S@Wt^g0_G%3nx<8xohDU_F4#;L#-DIZ9N> zU@p=p$)(~^8{7SKK9pJ;3jg!09eWoe_;_a$hgr@&aq~lTLe<>|xq#TQZ6I=MsZ)cf z?#g6X71Go~$psC3$#C;{{F-USS-U5dW4tvOQWtT&=$l+u<5o2~MnV8hEZ66r2=0JR zjA%ZoXcA$$TeFS&eNHAaNvYbM<&QKWmL?C^CCD2>OzXj*z0FTb{4YvDPujC+%ZMaC zl}^2sESo)O6tmt7>?K1}nT@uTf1NnVngkH>b-YzG0aT@mK3 ziDV)nW0-=Ob?*6?S2=5t{QbvA7vm^DyQ``r6@^D1D}J6G@c+6eTj;V^=r$A>=7mnS z@0(rN&Ir0qowVL;W7D<%bo~uYhHk-rJ|cN^s1OIozJlw2M;NZb9N#L8+y)}d0`);a zQfi@&^v=j#xsx8bsN^^ET9&&dt=|;t__klYf;Pj*sDyY$XHyll^Y6oyuW`#_<3`3t zwNY6;2MmBE8uW@Afai0_1Y`;)K{pM<3vN;#;H}9&1i0}w!cQ1MeIdOkl!+ZMon08< z1a%!N8Y}iepPh(dAv?nyizMAZ&wyv1lasRq2vFYgb@DI3)haj~1Kvwk5-_trANAlT zN)9zn7~7mgj9-G|+{B@&+h_E_fS7<4e9f(;<}w2rgM#s6qTqO0-0EDR;@{nSHBr-= z>LsT%K;x8m*a~KfCi{PnWA|uYsOLiU{APuZ4G=nq7N)F+sP-tx6)E=A6%UTsx{kRU zP+OFd(ib#>o*v#0IhIp$8d86F?!8LUzS|2U(Ncu!WqkgP(`GedhxVC~guV(CzZ`9U zL&j}jjc-g&JobrcT%)v*d+c|-{Z+Z8f*Ai~2JK+C3E_xr^?~1{P58feDS&q(_;_cU zFqn-^!liEtDTfK5iMhe03LtIpQ3<6G>j5c2E@=ccp8d_R>3$MkQtiaA*rmmI>{}wlS>nkcjME#uqm1{d8F*t{>)ltHxy z`Zj2MKxDB#7hdJV%l}mz{(h@dBaFGFf+a&fASxSe!{1;D-yC=&p?w*R=gxZsGx4Gw zB;K+MmZ}CuW3ISu<*eQ~-Ur!0*r(({q z-N|~?m!kc)vINH*LN3%ObAom3)sK!=iLf90q>9f1eU3UHwlp87 zh=}(@tUyTe(5t06h$EXD-*nY!zu8RywId*%NiLwvFLM#kZ^5KPF6BoOl2~_5rna$% z76;n4I?dDCt>w-O?YdvQ&s{V65Aq#GBlOlpQ5v7hY3q?-+_i2kV z8UHfXF@kw`QdCxIcKvf*CCAhlW2(=H_l0A%kcC)xl{u%g(NWhoyUnNB4;Z^A;)MOJ z9c><&2h>a3JmX`$Bdft}T8t2_A*MDV&jw#QC}ku9L)qZO;DVE_e!0^P~sm+5p*YOb(Ul`warab^@Y^;?6AWg z&fg`=wFPGHFI-VJ6r|=%Trc(7m!O?-LVlz4#+(Hh-XbbRbH)o_6vr65@uG>@!4Elx z(}tPV{UW?TPhR#odEi&r`S>wp4K5&U`TPj`b-jQ&46Rx_H|B9{vy zE}j063%)NlKz@w~>$$b}II^1HI3b}d&64z};2R{Az1ZMQ=oFV|>X}9`?Cag-@VwlH zT$Zq7v&Pou2P{(zf7bV(+6Q|13_vfpoIda_$~Y!ruFg%=jv@cqk2?hh^x@yzSzl#N z&AoC!6W!aBVhlirY|2hMo5-Q#j#Uaj&dChV;DkX*KNk$=mOD}tA8+AY+%11)ge=4G zd7a^LNNsx?=KGZcyl68&!wb(X!7mjTTLe|T1%6xe#8e8019!9Xy^U>*=7sp8NA4LF z_t7u~rWYs4m-?7?Hpm#A2w(o3VwaqBncc*M1zI2H0z1vS%6%^$#aEaqvQu2%{=~y- z^zh;vua2)LtL!xuz%>3MMmN;Bm-Sq3oJ`SmWH|9)gl~cBZU3_r?cd9USS(@N&&T4& z@%kC&Z8&~ZmadA^ueKN$5illb18A*PLMcJ%4QViBnXUiyvP9#J$A5tm<$kdxthz6s z7mE9(di_`r`@_<(L-fy}(x#V;>)qv4h3R{|y6nZ25ESQtDufFek89tkmM?z!iSsLY zE(JbubypLDeqF57y(d~(*YT@g)d+-aCIHB5$QgI->KAZ73SHHIPtf>{LyC_XPz9^- zbXU9=@_bD;tV5t1ibF?N&);RogW+vkmX|CA2PMr2Zoc8jsyzKK+Z3jUNyu=U8#nQ) zH$MshRrCElJ)cUCR&4O`NhG>dYE2Ul|{`Er<=fN*8qHUMKnH5Yq{DO^eSX3a#&trACde!H-uDw8I*@_iS z4flOhN|9PiHs$y21uu06{MI`-PXl{-NPV~n0)%O3QdXMGA;(J+$hcPZ z&g~}H&N>--;9~2dhrbP|BY-N1mB!2W_mh614yqUc+WZIUPl|60K*?Kp3{lF|{B%~S1p4dw)y-#kCV6&85G4Qye_5O1yx zA1hKtb+^11puP)&By(Ex6OJqyPHr=6_7#L6Q7Q!xb|-iH4XN=I4gj(qT-Eq5!Jw)D&Jj^t$ALN*jE*KEtDe8E%brTp?TTL!HfN`!UOg`FQxh zyW`<{VG`A1;N~!f3K!xDWkasGAHxtVwf=T~1y>Q2>oC-MXPMWbVb?nm7+;GY@Peo| z=m1mVgw?eqp5np`%CirLroHAYuALzTo^H92cdqe`uP$w zl{S^xf`0Elvgp&w&|Ntzi1)UZN`?~`Pb#xm@zTc&(vI2U8%I!ynxNnSj1CDFJK&3s zo{XRDMMvdeVAl9e8W1j&NC7Vca9TRRk4=Q5&H{F{K~!<}ZIvzCW0TbUCVd}e&wv>y z7I=Kb<;LT?o5NDSVe!lIf7|}Ee{*4pFov>*`(l75&wHW*0 zFzi@#4Q*Dlcp-?q;45*4=5!}SV(KD`y3D>Tbp~U0(gC7d6C_aX+ z@YLL!mY+4&b*O>^@1#J&k@IMX#t5si2tmexYN80LPSoh=g9i{wwl~nk_y3;CKofNx zEZ4lXtk&*M_+}V@yp5*WwioJ`Kmm6l{d#>z8%o#`Y`6(mg=$W*n}#n;DANz2#?`jm zs`cZ(*XcP;!CkPAbFP#_0`xyv9+B|KT{qqKUp57U+5zT7;?{Gg;GFt8J$4-}fq!@^ zK6)kMUCBziGTZsLUe7u&lHDbiK}}I)>%uPS5~hv{J<*yR;}uW)IE0KP#9l|4T+1Kkd6 z<&763R)$RAjB zc5XgfAbV5&|M2wPfmHwB|IgRGW;RVD*H$5w72$>u86lx}_Q>8*#=V*-dt?)Z>=8mn zrL00_M`UKNZ0_s#y!HA1`mf@3U-$Vu^Kl;M91S4zCj>1IYPjuey`l$%H!*-{LWF0c z`-0(2DyeF0Hi;&Zf~a~BsI$)nQ4oK8g~12^Qt;<}3gS(i{IsRkmW(V=CC#66LnivSz(!n|eCA@a!tgtdJ$nz_Q{h#WlH6U%P`9UYl=YLP2 z3oj|Fs$xDPFm=(CFuZ;qeDVOdMDhs7N$p{hlud}Pj;t{(ZtZ88PJz)0?tdph@o_d! zQ1)g+$nDz7G;imkqMEs)R8kf&h)!g>HSIp*7)JUYf)+BlJ=Ujlvh$oi{6q(>f)#M~ zg@763T)S+7YWSyK;dTF8LT&b6w<>pjyD0^L8fP}lUD^H3{J4~`CNWR}$!ViOGdo{a zGMrPrn0mJ1`b=*Rged>IMtJ3Vbg>vZE`t%L0c>pGPrWvJB;^6*&qYg>{0D?Hfq3cF zSFB(s@btXTd??TjfT=JOk(CfK#ERjkk%)o2n}Mz-*=U3&Fh4pYE2_DdzMcu(;OeWB zA+TJ!ExU648#e`UMFSYg0{C<0eU~ejAKo|*1UEj%LX9YQb<`KTMrV+jOFr*K$D_tW z9TZ^f2+;&0vCEC>K=(cR*)?SZ)|V8tfMbvoxg-dAQn2ahfKv`^`HtrVL&~6EV9{^t z1nfGKMDUEnCs6@@vm#7AIfuA_gL#4G>GR;*j3O_`wd2TLVctV6#^w!4dV?k z;@z#N@F04P5l1ebIJjvs;X(f$y;W{e(nYY`+5Y4?Ev$m2XD0MMa?{jLfuMQHtqFsU z=V8SyMgO$&c=Ql5D*ZV|7|?qljW)@r3>axa65*{EczTYSs7<0PN58{31sBZ;E*%79 zCQ8G`MG9h4WI72R;Y)y9zp;7Nm833$)xGn-YrDN?`{tP(xce2!xGMWavWnjz5+aY7 zMqxNX)Q}Nq%L8!sn{DrFlCVSk-Sb|H!(lq4&Asw9fMkeGtXyV>RXO|yns#G&u6q*! z4&;DAnMGRl#8gsZen)kk@9KzlFPgJY`wPlQSI*l&1!=fm)N-xBJ9WG7iPvlp%q+}f zr7Da$v+};ZYQN#B8ie+_T3;$pS#ol`}(ehh0SpbP8WvA${duVefVt!5na{5xZ0Z+eeHNCM!rEZI|2>Cv9vLksONy|iVfxhXDGZSl zw33*5cy&nE`qJ-$A$Kq&1l*M2M6%Yj`{nP+fuI-%%y2oW@y@rJ6r%w%C*Xpmuh_la zz8HU2u#3Y_lAlLYmbH%qudie9r+vs05goe+asIs`tofqZkrZ7KL(6iCD?gD=SOUK1`lP3xgBQ2FfL%o($ZVspIt1@cOWUhA1U)7 zc3qNwO(M*uA6(>1-N{$=KCWhT^VO5}rs_@EkD+WJyB|e>f<~0Q&ixn%TMW#?z97kA zJr0uTJ2?-}P6`rh)I#kg25vQ2F@(!$$#sqX;m%Ni#@gv9<=KTdS7(zPNRt?&P>@2p zewY^U7^VKui@YljU9Xd+C6NLo8Nr#iDZJt83||a5u7WOuxL5Kwt}*t($$vK>D)4?E z=6~h8{D}E!*x%0#cNeJsbAcR!sn)rMMc@BB4+hU{yN-ceiI0{&+lt*q8lZ&}T$h1T zqVPnV3o>U^y};)zz{~}Pk3N(dU^>ub6m*d0GsoN&r%}BUU2AI*97t{7`;1+M=`?T~AGVCQ+3!*i0#abzKZ-YNpAy(&n0P_!E zaGde7hY+6O2S_q4c#2h5TXjPngz$x>eXSeIOix z&Ak9a6%IjKa9>9Tn)195%V%(Sqsd8x1`d}y58|cNvh z9Xm~OamGyGYxv~T8dvJg^W*^aubFss*$PO)o9kJ1Ih9PsynxO}^<<JHUib-AORv3FG1JSC*-z_i%M+x zkUR#>RiRd7GzNtY3;J^vdb${0I`*(mFHjUloaCu zB$Acz@4#4|krJd~QAjD|5cEVZIkP1!SBslaQ+P4mn7@({OdH6IoNXt_Za1H}ekOFi zKJb99TIn1L1K1z-_Ve7*j9|1%UnR)f#nITb!i^?Rk}q{jI+{aF6y#EpZ!Md}abob* zR0BUof-?ub_99q`D1vHi#ci2g1_=Xm$A-fYP#^xDmGmEo9M8i;oqRb#(4rVs!2ElI zn7aTft_Cugh{a7tPDJ%`Kfjhj7WkEC$cVBeSJBf^vpusqEX$ly@bJMZp8&b8Wghi{V z7J15CGMR=$@Wi;5K#UjL4~apZkVo=mpG zQ4VA_&-@w|7_D5TP{3uVoSB62wZ#t}!F{~kOx?Z#L`{HBOi2$#n&je}Aug(M2O2?#k-&Eb6 zSeNGh0s+g{MD)_*9#-dna{oLlQFNPKv^miDa{OaX_X^9gk?)~P5A|wtNEeaOEb?6i zA1r<`C_@WUD{l(WNYLIosnxb*R}I7~=5*t)LAypQwv3814jW5qdTHmXA^wWHSa^V@ zaq=}ES~FL?-w$-=jh7cTye_ZaCSM_?<1>I%VCi0YkVG>pp)8H zmX&E6-)oaPv%e+q^Q1c_isc zL7au*WeJa-OTsKF7w8>B2jMJ@atY9+0M>ljB~y3ioFp^hEQb1&ZVFx625pkilLD4e z-j1l#!btAPJDaGjRUKk3B#TL6V7vhG7-7sqb-DMAIBPR{dtFwLBA1@XPK!~KV1F5Z02*8%59$VvO zi!X+rEapKpx_4Gpn_mr;riOr5BP_od@fGB|XRemi)&jMK1=K(v%--kigp)IdHK2?^ z5jw6`cyh(nCII{)@v>RmnKFzmpfx|GjogCfKNbGM%D-o9X|%~hO@jT7&U2JRY=-KK z(Q_eS5+Az@P0l5eX0X%KCEN&Atr5|+U)@0sA-W)lh~$6^>u|IWHzcf?-{(hgpFgfQ z+}!^SBKZK4Aqe^3w1D@zFZ9t*j8v$JPizdwx9%QiLn&JcFOBoj)0V60avM^t4rcxfm^wpV$Bp@~Hkj=8m44*(7VVFv4o_12exY_ULP-J`)Szj*T zG_U6T!T+zb0o?K2nuIUO-jQwO9y*`&1cdZvp(+smx+FV!eauV(F8atrVO3VFU68n0 zU{j#{_Fsf9b;XqXF*f5-Y%&Oa#K%hswlw}6)q*0q-@|0?Oo1C9hzh5Zs>(hv{s%x0 zHTQ$+)tMn$mHi1KDa^ zj6VA_IE3&M3s^Fe{BtRAO_?^1(7pO|U7DHuZ1Ipv*Ev3f;D7ZnaFfGyW68?%wWY|> z%AL~+7lF5{ z^MzuQGPcI6zYVpJ5O0vj1fTtgXQi0CnkG^ZBk~ItRGh+hwfEgI-J!2VvzyXc;S+<% zhK&7XF$;`_Ofll8%Z>wzOn4_BsAu=@%p0c5m1=t+5{AXdznhEn7#r~)CYy8CQs1_w`n;(*9AWl0wt6O+=(o zgFl3aKa^^BzJiAh1D`PZJ!1rP(?QG9Z{Ozmbm{3C-XsM0a{B)44d*4jd#8vG_}?#d zzPXxb1%Hkhp)|&%dzy?Z(O{_d`#llg8oy`>K@ z|Nnx_X3Czl?_%%0-6SM>6Wa|LiZJnyrs;Ahcm{vCk?Mq^kvr&4y0BsF21=D4*Z%Ouep4*P8@nA%{vf2p%5N>o7XhhA>lrfp$7f^5F6rs6N2|e3*{N zwC_*D1e;7Vjh!Qt_COf<=R1^#Sdas3NbKGbhS-(7vZwNEY`JAn|KL@X$NmS}Cw+$u zQzd!+e~yBi)N&W2#4QQy80ZIhP~+QK!l1NA>r?~ZDZ}{5U#kDD`HLDZ@lpKqkqn+@ zOhiFO;!dOxS~w3OiY-|U0mCpq2*AES=wp>qa##=pc^7|mnHtxFI-cmDU3XKFTIV@} z1lz(dsVPP=^qrnLuhk^P>NYg>h@K)e^?w68!8jliw?b9n?rkwZmvKx8;YWxVHK@_L z8YB(@A?S`X;ZX_;q2YzB|1(~bd*W5imycb4%BBKtH{Fd)B2|oQ@NrFbxQHVsp{6|d zcw=ZnIuGkJeS~4LXPD)bLjF6?P>)r`ESRJ@rO5Gp1M9zjA3 zFuTHc9%O{^@`kU8`xGxG)=_~(IQ zZj8Tw6pYRI#$RPG>#c{&(|;w8dR_P};4DiPfx+Y`s7iab0z>!F}>jX|yq#(k?G#b4PK>@B7 zHO`S4B(hEih8}-&lq-Xrb~e|-f=h3|O=#u(C_Uc;A&JK~YsDyu{y+Ixphh&GSn%_K zNhOkTXctJh0fR)Fr!kwdtBzxR>U|Q7%dZH!U`v=Bcx8%UU}kt)ukjWBwa_jcnHg~x zGBeekbCNa9;SpRafQV5d4RbOgriLiMfNwLVX798iHP>Zuw7Gbi)AxVqN_5ir=cT_= zL%sP;ozOI4`tB9x`zUCkAil)n%%HRm#QFg!_OFheVkA10^#bwUeI2XS2rT%gDhXxs z;jF~j7!{%fWyTQ>0Q*GJIl&hp5)TADwS^IgQq^t1HpP3Po;qxeLx^~55`y{v(g0~n zT{Eaxr5FakZXYL-H=M91eLe~UMd>{);d|14#jTy|uXBk*z}l)mUYzl zlSslZXwm+r|CR$dg>{esdz_{w%D8*)>t+qsG$XK|35x1j7&srgMBH+^1kpv9CfOi3 zF5)Qs&A;iT?Et4xJTeOZHreQDjDfI~+YS?0{vj-XDrUdI*aIwIDLm3j$(#fr59}FW zK1)cN!2H9rA|n7yhRYLgvcMjWl#8hj*OdECO6EA!_OQj6L*XlLk<_OxkX?V4JGiLA z0{IUCEuX;Q6Tdo-v?}67|Cz+WHxNN!1`cXhNFN*qOhQ4sNkq=>3b|nca__SmY*t4) zD2Njn;$7yLJ$_6<2r?f5D##(CGXIgrOc4Oz{19}+te}?@oM>si&jr96WermD%3vr1 zc=i_zOI7bhncy^(f2dDFB!_JlxDRx3p!*SX>7Ygq8dh1j-1KLYEuRlGPDiN_yUhg7 z16i)xN|UF?g@;aTH|S6>hkZK)PLf;RsZNY>Tc{SXqQuY) z4QabX?^)DEw!EYHFL4r_yz)&yCgkK(qaM!+tNibkcA|{LBwcoFLM?I+;8&UcRpPD` zg;KMaAD336Aexk<=yU1hNgP;iEN0^_s9&DzC3F@xJgfTp3jtuW{KEET{`mDO7D}LH zJhM|Q=iYWe9ynZd@baJjgDR+T^1_w%7s=j6)BA!Z>4Trn{{44$EEL27eeZ?}G4S7S z)s8Q;;{LPThe5`BnEr*|@PQ-+9C!JF9wANAk?PtrN5GqBAdZ}9K<&wu)TAcP8a(-o zjP}uSm(0Fa764Tg=KjED+t&H+g!t(AWF4sNzW+=T@`njoDiZ5hTzgFAI9}CtY3|iis@zs_!~U&^ zOOXc)tT0&tO*r{3LS`aQ(*wNF1C+VD@LiuW63a`qsBq@cFUYi~oxBVSIURK{%>xhJQYF`9q{CYBq~^qGym|S<_)ZwKbuGS(H_-z4m2jI^0-YHzlI=n2INqf zd!qh0fI|ypDA*;C0@=$Hkdv6Y$CQONkEZ~t0P~jpOt?i5X@-`ec1HRd2?_(XqV zHadm-@5) zXGjqcnZ7+`xK_))+jkDZd6QgWGx+o^8{@eRvxH%@Ghy@Sbv8n(vC}yn4#7><|Dmc{ zAEMY5jICPm@b=Qhpy0wdZ%zwNc6+ z7T%ZF(HS?Z8IVx01AYhmuWB$Ajy128-M_w;qRTd5s^X+I-C36ooUZgd-c)CVFVu~H4*c%p_iW!L4>GJ_+L3< z&vff5H4&3}=?N>brmKyDC`sna3a;!bt#-;&!k+s9sh_L?-l!r(zqaU|Vc!A6%pbxD z@EKMFRFRV%i7)DySpLuWjs8U`DURL*9^mth4SC2?GXH{%E{$i{2|?PS0aXT0S~3v& zjVg$-$N|O}EI=|8ti_1-1!%)^2>K&rWYc!aH)WC$z@V}1ubNN6dd`VcDarCKv2h@m zL@33BTsqlzv)e+Gfqkaje^NNS<_+?HNX#{`^GVv%zy@&b+dIVq&BS6J>td z%bU>h=`JD}{Wi~KY-cy?OB*`oy)W)_{7+b+uWJIg6r^j1gyX?)?|%FYqRU}&6$p>@ zQF~y#w7~CeTyOKmqML5N5y0G1rW*1Ba{G2V0z5~D+z$d6ed)yhK5Yx>d`7FCK~(tt zzbde1By{8fs9FRG&DTmSdLMNa*CKxwi_0ZtE_=j;OHSiR*55rcinCv z=0}YENbtcqe(>FHb!x!j$=RSgT9-;(dJetd-S~2YJL5JE=5I-G7~d^*A(Ff3f(M;i zwvm4K+q}M&i-klJ6iTzU+bF=_mF z11=seZoF@2CEAsiU4O|#T-&bqk4`IrWoktB$9U388wB7PU;xH+vFvlT-fH>)+KSFy z3U$5*a2$>!)mhl|z1@a4U*DXP#F`&u1@pWs1F+S-v&HbO2Kc?<6dg+L5UOIoz&oe( z+vY!`9Q3kbTdt8{_jy4fY5^`#fgyVkXa+p66TJ8{ko;l?=Q(nMK9cE1_TlBCVSV2J zKtE-fybj#0)2zK$OFBOQ)1PK1G%R5c3Ndvnl0mB)v&#uI_Fe^GbGhKFCT9;x*qNHp zRf)0UU!G_t6BjN6nUXs#byo!eb3dk0OfLa+8Lo~*v=R=28VORqfc>8l5pJ6lIq-Vq zMD}Hy|8ZZAC)|16!C=>MQ^y}S!S<;x@k_WF{}1auQ|(K65mVRmSw_nPrd@<8J6r=w z@`3Prpn9eHq7abXB!9YUammhmU97gjbiQaFQkc`CHX=cPBh=O>(zJc&?fMj-4)~CP1{kr3KZs8U3 z03jb(9=~P$9u}f>t^%0_KF}RE0p4&XgN!-xzny^&v=P=sYE>?;`{ZJ5jUY8XI60H1 zkz~wtSJV^sPK`i%*T~3?%Xl|1!H0vLgh5v*eydS8GGV7l>;2WP(O3w&o1BI7o5b9q zM3VzmrYj{~+YD=Mu9-{wf_KHwT)LS_OqvtkHamW_Jj(F6WVPP8eA5fRp3!t|A}+;C z@ad->Gcd&Vj*>9=)auTG*f004SE2Igg$sW=-+vDZdAT1hsq=8>`G*4^#g_c%SKGrH z!c%Jv_aKZ1tGPii&x1UI%z`mK=S+na#kJ($txXh293o=*#dkc7K;5Hqu0XOwLq{hd zl@1ICd8-=^G+)s2XCAsi9#u18_PX{IGMWe>5!iPvp}4^vl$Uw`K%AHuB_P!4?djbH zCC$j+Dm}F=q^YpM>;p1Ulv;h)(!N{f@_RV7Aj~NM=Evz_ZV?O#zu%c-m2~{GNgWQQ4VJE-;LYR@v_B(9Np3fu_}uW-bBqtbX7r zynt?Usq!)J?fYg@UyuFpdv+D*`V8|PLOplPZcuSeo5G1|=@F^5M`e9(PYzWR(9a{| zo*_W1>Zj;2kkN54KYw=DyN$ajFo3(3G*>PB%pi-UFNV~(TpgPle9|WQN(x7q|B%Oc zF`H+#=;2M&532BA>moI%qXk|j)RtBw5^6pPwBQz4w;iFb8BsyZr!u3+G+S1GK0PQOS?gEI@Hcw zkY}gmcms}{JqXy>LVgPd3h|K1K&C1OH5T8yi~PfVAINz!k^xAu2*yLmMMkq4Vb_mPx?lSD!IU(-9QGR4(}eVB8waWUB!bFw}0Fo6S(n0 z5aYGyu(;jO-8PFZ11iC`s&S`eO=AGN&PA0wDMKc^bn{2Vqe1~bM51V*;p8okX zl|ySm(&Z9y0mjLr&o6UKy)T+&EM~7+>As5y%j&VVCcL=IN>G_+sh3_eQZyz*`5hRhzl_L8a>lBb-rrO zG70iFZ?6{P^4h0NG`&z+=OZ5OZN9m=V01m)&0aQdrH{9bx!EyY}0)b0xKYiCA;i z>pKva?M`&s@N)xyQskau8Z98dc>OP)W%l$6jc&o&2G^yr|0DV1IbJ#l?oY|C%niEc z+68(Bcso;n_!9p`@6k@w)PJY?Mfcl3Ke?@EOPE{eP`)TX1qlED~$#*lSnlxZ%a!1|DN^4@n&nu!X&!1x(H;6Fh z=(5fcU`4VYI_FOFO`DDsM5D^uj~y?+DZD(ovboVn-8iz7u?5Ena-?qg&5sbWMq8<` zRXyY*!o^YF;#y|-nm;UR%+o_0Fa^|wR}Y%dfC_8&hqJJqJMxL z*N6b3Kc{n9j{Mv`t|qLVx!xc5g0; zf*8aUI{lEcO~^0MUc1svnUMaN?gmdT2IPw0DH5V?%&>dE?%CkOwNwn}5w4CnMRNBZ z3ftw-5?O((>KC&`Ai8h(_u^xvzCcp$D&f56;-(PkBsFM$S0OlTn|klW(cTL-srOnD zx3Vh8eINhYpn5SJzEsA02@SImcQ6k-)7W1L9cZ;IRM#frz!lLICPrebJO*qY&e$Ly z9M!<56NtG8B)(lt-)g9a$T=g?M32$yKBUcajl&)SATtch@7PGow`4nh}K3mz{%9yup{{6&a;K7oo`L-J^Sf9b^~iz zzru+ItyMG4snUa@qFUgnw#ZDMHE&&xp41-);sOi>M@!Xb??j1q`dfy>Z}`;tsb2A+ z<`a;}4C#%mt~KrlVcr|oqSsYGyaSgDw^17|%;=*p;H9f|mYMoh+{>~zx6Dq1Bl)OP zNPUL|wlp@>R$iFJ6TJ7iZ7_}Rn@?VUBQbQP0;^+_a@!jj6f*d ztQZdjf3Z9o-6Wa%sOs#;K#-Q0u>2~&q3QF*D}N$j@}Iir0~OBO<~Zzf3<*Q&7>V{U zi-q(3VXGLq9P>E)Kj~)_#Ogw0$asjwZ_wmAglO-3E~u`Tv4Zd{*ik<+_fDMZv%NoT z*`l;pgS}9Mfqh6}AhkhDWebgNsNw`LIsoa05L^5Mg_E5uoXP&-MNCD4bU(aCMr1eV zUXFNeE)?J0yX`{hkmTLaG-(TF$I%%$pFsnWXk}CJ%vA(qeDK; zfn4V|b#h4S^|TmW!c^e*#4UOI+=-E9Tve7Z%%=%xj=NbVHGoZ4p|HuSX~Il|lK9-S z!PNNr9RXBU^3l%sl}!&bJ|KCx#J&|xcAO5E>)?7lPde3=)b#x@>}?c)-GgypSx2a2 zzg=^hG+KOG0C=VCJbs{s^Fyr41qx!?aif}2AvI;CuK z74y=24nm$5JJS~IC;Cn|9bOAlquJ!xJ$**l-=`J7CxnbnHdZU9$!-$hy$m8J&B!w{{7CDj&4;_wUt-P67QxZoP08Fxi|F4b zbl5NC@FGjaY}lvMweU>JeEi^po(FbU3(y!UEnkPni^d7`%$L&l)4aQ2-%z(5DXn4c z;&sP~`-M@I=K0ABw`wS0y<95Vu&;jP{?4%PdT0Jf>ng!~7>(P@S$VCMrd9DGIA>=@ zHWm7`39IlQ&n5YL{bm^;82Gn2{lcL$M7^Aw-lOu|gL7+qr)VR^OWdFt>}DFSf-4I+ z6fy>vOq3g4Beb^cU#(=JyN+B{FF207RyJWR#euK5h87CLk}Yf0AWL&M5p-4Q!vSZk zd9ZqXx$Mu8uf4^eUXSyGr+a-Q+4bThz9giQtTQ<{x-{6~$wEH~&dD9>-~wOfcSEZG z29f#qiXd_FSbN)U++IVS`za)&+QYAl9g1zNH6*NbRZf_3p$&>~hL&vnRu1KQRxlja zv?To{8_rV`hmaS1dg0%mwN{Lfz@0&&s}+PuE}W!%6OO@$wkL&8G!#i_8j|>d`j&kq zTr%L}801qk5a!GNESjJKHALc9b+p`H^qD+%FWpoH8HKYwiFeDLmY6RQ?LEL&6G5g; z1JZ>cZ61D@ko7U2OOk<=B~51GM9qd{4GpE|SMArg!V2ZfI`im9z08Xn;QHsNKR2N& z{+Yk?5R`Dik`wK_c4jxETa`ODXVo(&wKYc%MP?8W%3y z-;zxwcf>Q%JntlO;>0K|V4hkBD@ggma2~xXf)7LMj}<%vd!+7a!|$?a6)Y-xeo%#P z2Z4W0u(xx0Hv$v&a2Fl$D_@j~7P;}A^IT&hyJg4IJh_4&`DS@2s$aKZGAwP_S0`y> z`+}GMk~-%_%j0#Om7er2Zc6&Wyu(5J*HR^fXwKr_4O{F!9k65Ku|D-2ON>+#j0F!) zE9oDXn3!R*_B4!Ra%4fD^zzlKuYSDMQ}JQr`s1Y4r5sGldAFc131N@u*HNFTv*%6` znOA8uR^}bfhG+?#|H-yOz}+)jyx93MT+ZY!)mJmEvUHwFJ~T0CDsU_1Sf|rGcM>T? zbom`$#AAErKN4(Z!(9ko9SKea^(-0Q6I}E*abf)hUy9!TIH2eF3nUzONT&`)HCa&6 zJ4;+;3S+@sJP-V-m{*zCbA>;!XGdSC3RW~+0=FC{6icHB-RPvCzrQ*6ulw}+Ut~C* zd{Nmu%|n{57GhJXJS+=n?pS9AUimpjPke`0$heVIRAuyb`dQ?+v2#GSrq$)iXhUS! zz+BXYpFXpI=0u_ebr^#H>mKIW9i4hq`*(B z0vm8e${I8=*lnk=l~w%vPKp_QoIrUyqlIlB$$?>9cOBM%?m z(yK%bcqp*~AS?0qbRB1_t<(I_ic#`<#(DGL(Fbv{$vuyTq<%D)A?RXbH5$k}{Z1tr zyWy;aCy+=ZmuPsFvIJ`Du`HX^yn9SG`;6YGUq|X~gT2S$=$_L6qf(581o~k`cjIth zS-S73^b^G{-gQBIIP0T&f7r^{$lvZ=g6O33-6HO`g}hKyi22?WXxw^ZPTv)q-XC>; zJf_mAZS&GzdW-25DH~Vom<`qA1it3|mGOE*CG|T&mG`kg`;#44g)38vUwtuzM@c*+ z3rSf}c@Iar{|@Pjt7}RQ@@)9ijdZ0ZVTohuk*Pt9KJ|wpT$1kz~LH zERLURvP?Zr{Kj?`yk|cPaHP$N(gsUDg`6#+2Yc@KDq&nwtnvqS%QyyP6kwlp$;VQ{ zk6<56OTA^3!)>0k;3s_#;ub;=2CiSZhs8-L{qJ!3tUgcoK-RDjOL$NGM3USxWq$SuTsT=!vw^09rj!!aJqSFy( zG+zY#xbdG(H?j;Dy|!bmK}G|;q8ZJ=G05wBVV-fB>Fo;EpND%;e%tN%mDd(b4)1vm z&Ko7|c$I>JU20z9=^HTAq! z1=lJn;MlIE@RVJ{CbUYZLq4jQY0%jA*nk*r@2k;K=(PKl>D4TMpZea{8C7vYd5@mQ zT4+=>J@Vce9Z{~+*X)2EN*zy%NUURE30{-U!8o0|=G4nExZJ?otTPITTii1(*{6() zKUvODLHV;53h}zM_)9r|Og zT(Uj}M!52^AYG_M#*(V>_Pv1j{?D(HzDl$MA~r)YGT>2)M7zrMz;Na=x#f5r^>;=_ z69fmAlxtK(n4~l8Rj17J;|ACoFu3LB55O=2UVGsZa+0@ zpj=rx2kv1rUVcTFVBi{3kTbtsxv)hKG*Rx(&j_;(@I7Q6?<9PR|ACW`7hu+N(`6(I z5|69~?eFwfQ_4!TKPnp5Qwth-D^M9UF6QD$<0`=Gss1&60ou z^zWv2>wemrUDR<1+!QUOD?8mI9W#ZNo?Gb#&@ z^>2RFpRjfvMjAWA7AP5s?NKx+$8=!z?ZxW@%Ktt6v-aNSAR{E)P&}AF`1cUH-Zt9- z`q#UPP3THm_8Jd1!*VU@J{vJMeao5t82HW5Wp6TJMuD!Y3t@*wTba>wH}{#Ympx^c zMQ8>T3^#*uq-SFJ+paR3|0(dLh4iD}OOH08Y||u>%02_fGgxyWnq!bUve*33(-qp! zV0z*x2rdIOJhce<1QJy=f%pMt?y>$kuJ-K3ZI!vkXO-KUTS&bm|As;l?4)4de!Qhd z2wpVcLz&U*xxbnca;i6iyS^fW6e5RFFAQ^;e1YV?r^_UT(Rnaoy5gjxf<~$C^X#E} z2J?xx>Q&Uj3{UN~^iy_;Y3sN zhrjw*S^e5MN+--c=3O1lp)KugU0Xt|*L04#jZ5C0-u2KS0CW4b24B6z{~p30=w{*I`Sg2P-3y zYnG2ZmBOFVwSyL)L;Me7pGlmPK_Zco)0_8V;*I3mfQ(N>&l)L-!vau_Ej$0GjpJC8o$*UCCU08y=sPRU~txS;zD?3mHo8f-I#Dsvn9Dr ztrmdaL~}ZN@)LWEcq`XGi((s}psh97j0hS`N07s7R;AmlWCMuJS8`+7U;Py*$o*S! zq%C_W}BXQMXiodQam|`9==MTLHWW+R^8mZDD!^s&h?`!Bgb$g7M#pdeqCJW z{l)(3GZ-58Wg5&0?WB$Er^M2j@?A>#{_DToQ2ay2kVr)=9BL0lYdz+a4Q^qH(g>da7Doq`+&)cfZ z+=iU2V`x^;16G(bVjeCxKX?it4U#74vIkD_OxUJ+fz|2h(gVZV02b8r^wUv6$URWwA|g#Dd3#y9KJ(nSfa~T zBt|wte(DK7)1`VLAhm0{w`8-=izAtZZWT)n=PfJVyztXe@WBw18_1xh;UsjLL}5p} zs|mh~|LNcME=(%&?)6>bjDdY$aCpUfTlJG5TL35O|BYK7!?>kQ#d2oo9C!cviD9d4 zA~$`-S0;t6s6}JCPYk2IY}phB&YnI)w-_sv<-t3|Eqj@jIn>sEc|c0k04bcrT} ztkP$hou@{D5pFPo%C$^;LOGBo$c}qQ0rMaPz+mfk&^2Ye+$Wh5Zbt8O!Rn(Q3q7Z$ z%i27}CQ85l$8h|`_JzG@r(dx^>3Et^an9FV#i357$-uy_$O?NaQh3=RNff#`;+(t; zaR&4=DD|P{RaT`}ZI4n1Hy_!KDtQo@`iT79?*;ho+PcsU^N;aZgGM-uZ-tn!O|PVq z;3WP01dbhJxqb`?lG0mOG|zLJwn-=FNttoOLfFK zTIUG>-A&JSie)EevdLd9X?o_)3?p&eS9;LBOQ@P2N@uM2iRyHw?5?d9sNf2$tDxIq zMz?<}IvubpzTj%HdFN*zEe{c%g2n@MxzZkVNcm;K0Sc=Z(b-Sc`{$y!K^mdjtOfeJ*wPVL-sx(D#6_t#NiW{YD1~_E;xr8J#gH7Sp{a z%MDSx+b@UuYRfi0&5l0Y+H}9?HL1h&v9vO8woH0xS#+SXEVz4P9VerBtsd z)>PkBo9eaKKX=DGcWqM=txTH_NraSVG*1$|^z@N7s(37}*64K@>dY5J{0ucQ3&!hC ze@}Q^UpKmLl=CXU zw1gMiNT4s7BIFRa%(Ca;&5@2s*KADAJ+}cAkC7XIS;>;@l$<6U>LG8qNs%3JC2Xl_ z;S~Pxko2=XmgwW!?#jA-``V(|q$u^|(33r#LYIR9{Ix^muOWbx>v z@UHBt5I8a%61?lWnYvq?leCI{2`S9^8hrg+*$ove?^`x=2L>Q*13n+$u3%LPr6XSk z=Zmf9Q-llG-#ua?$Y6@7WigDY&+lIwIO;a7E^_IOiw)93v$Z3B>d?j4E9y=SzlKbe z%Q1qQH>PLdf>9QwMe1rFAGhIF@BXOWwY2{9OTSUyyg8q;wBs%717D|ml_$nA*LiSL zUlUVeKVeNA=8L5mb%~e<5|mGORq%V<64&;s=43DP_8;Zpw~iQ{J~hv))lz41gtbWS z$~;k?30e*tNx573@G(bMnxl(`DHuU8p_}yKA_ts-!j!suO<~Pda26c>j^JtTFp(A4 zBR(F99Q2h=N5oVp7OJajpl{C3bMI&}Ss`ezp6t+70vrohA7k3DH5Kf1cEPk;JN zOFaC|;MiL20!`oZwJ=5FDcFK<2ICN8?TL2w})pxRT|9*lu2^@X3tXAFou%Ir=mcp%DcZ9%#I&DtlwRoJG_xUy%jy}8E8J5fhLjjzpnWJ|cU`ZyTf z%rQYxf1N%%zogPbA`&}UTO$@Qqx+&n1>iEDnz9X#W8jzdukOR-TPJ@u=I&=ZaHR+7 zG6~&!re(8)=T0`!^7;7fYU^TYsNmr4NNMau#-#ED_Za#@lqrKAUwOVh3*lnvuNfn-tJyN+UyTki)9!}6q?!80A!_NZKkqHgp<{JJ`QE{A> zXNl}4D(Qpwkfb-iKA1^SJXU7$u+(XC**UotAG{;=dgTVan&yJo^HOeyh^zOX)`N_Q zQhhgegNj6cJuQoKH_o`SPyc6hyTSZ&)m3zSdD_?t}qM;i=|TZ-Ue2Pky@x%&UxK zIcRTG$IE~eNecQg^n@)1lh;$`vtV^BeO}DnhadxVrC7u7Z#5r18=OE=?1Lu7FElV zMe8DK2Qm^cG;@B83xRgP=wjHaw+=m5?qsRsoiPFJT!U%7hH??2(9r?GRjc02+InSw2ZexS*&%h`d{S0u)G>jm2`FWL&^fXAww)HL3+OqII%rl`( zM&9DjDA$?)?VI(&E= zyhDhB_3G$`xI#L^v0BOo9tNT-36lys`J(jY7< zt(1s#BO)Dw!iR1}N*XBv1xe{z_P)>h{k{K+yL)4vnKNh3%!KeycKV$=FT7Mxc(Y-r zTS*rMGw;-`D1<#m+cu}k9-HfKYHo>6a$zC|5B2t~jcyBVB%))0%Z$Vw9yjh!)nQ0^ zmh0#`O1s_G_l|g_2cr)aZ=evFEamroSp)ICH(6&G#N2GQMS~^7l!5HTn7c|}l_yhW3QO+o|ZysMp zvC%jl+w}*(Z)ZtAI~)tVo!h>$HC%p8XI>Svyn#HVwEKEJqC^{ICNHvoH#Gdd+FN|8 z{r6mdTdCUNtl8VG^2X;@u9fe|u^r4I9!=DHSAnDJg1H6&d4>3^w9q&nuqLv(7+?oD zODYg-rY}AJ``i#TD zUQXjhZG;oaonKEgUe)>tFK~!>htFM5HZ7+noZBQV3mz1=JsumTB&2G!mP$Hb_M*BN zqRa~NE$$TvdS*Muk^RH@;af7cl9hO|?CX;`4|`!isLRL<`ke25o|R|&j#ZN;Wh)L# z7z+IntXiMUncV?2;nhDOWrm-VmgSD!Q6=Hy^&M2oedokX+{p7}zyU``qgjWB4LiEP zQs==#!t{FPiH*c5)5%@o3bg<#3grOP3Aqj@T21HUncvnq>7!(MQ?7j6Qj_`e-A64EY!9S zD*mI$u*8A0`#o`<3E}Smm0luVLeG!&!Ff38Z?+3VuzC0WHL8R)%}*JkBvDV^CCz>! zKIK~3z^Lg+0X>P^lhc7}FW6|aHJZ(Df_znJFxb?*%wS|qNdoHQXkTE6_to;x@olWPW8ZUOfjA2t&Gzw{ z+A*w0P4g;Y$o4r|Xrdex(lF*Dg3tMd2^EGx;C|kuY5*_&6M0e1r|yT!(8iBZSUr{gfN>9ELo zN}EiaN?H1AZJMj}(fFWi;kcFBrF0kI8WhE-K!PZA=>>-1u zWjNjHZ-Hw&$b_7n_i{$V{56G}FEkY6jFein{|1eCJcn~X9G;^}79N$>$v1|)_#hb6 z*nt3+bE^o5MVrn%$zk&URy-SF!V!bAG>B~eSDxbCgHd`CdSx1d`)YNBMx%F^WRk(FQ(tzG?rEiL1>lwms`8Mf}9pAwnb(K7N2S$WGxmxeII{IP#>|WT#=!r`KiDnPJEjLN9f>J5dLXd_BH%a zaoLDU+`C?i8crREI&?hV$#MT8Q`*y6JA1UWV0r#{EcCE=Bb|I@Rl9!^lay zyhS}sN`JRioj$a`OEJXBy;pEt>6BPiNoiqJ6hJvEqwX7dmK7S6l*kSZOE||RAihyzt8yd()g9Wj8I5E)OL3mwauVbqSp(XG9fZsuD|d2OM-KSl4ku~sqZW5XQK*qPh_|I zcWt;fOpU9vT@%ieT(bs08Xk?g%Er6q4G{2~4v_q{Qn}e~P4Rt}`I*k0QRY)yo*dmB zO5>E?7iRm4vQpxEw8t`2D&`t!G09W#mIm>%*;iU_su_;Ht5BQ$5pe~xV72l2-VT)N zJjM|n_)?NZPX!zj9J1PEzPUo64M;K;3Xt~I^ud!vauwt(wzGn=OEI%Vd3^E?A5{-b zUU26YtXjB!n&2EhzQnj8TQ=ohVU2dO~A4m{%+uR_j1vG>ZvAnxX==x zzYC>*uCElFn~Hp2cwIO5)%YNjlKj7jfiBVyQ+*KQ*NA1+E~6R4-S|GVU*X6-y$`cl$9MZUP|>V< zxf6On>4~|a2lXl~>}#y99%>3ES_Q7%0YBI`PMI|E?YtDsq%K!ke@lG!%gGIEuQ|Yc z-j0CUVfAfm-5=BI|DE~O&`tB{=4bP>mfmHj7F{!B@rpTvqrs5AQ>FhVTyMzAfl~Bt zwg&O3D(+CkQSAt--nT8*x`WC@>eE#p9`!U>i`I;pzG^Emn(Vdu#nMK#*T8dAx`v#+ z#MhD__;zdLz6TeV2bXoc#y1^@{U%Xgo+ARCk3Di`xgIoxXARj1)86*CjTP#S#I?=3 zRC`b1r+fK)JB88m+q}}gLk1XIa+U>?PwCiT77-G)_aTLcIh@U{A6#lbjG!6gmYLBl zSu3}bD(7+rrM5T(u>wA-1+AYRp77^1ctkv*tUaH2a~&|QqQtltF7U`3xuf4>=+@#( z2-Xw5$zh9eKMN}qQ8x=bY3<5KGnM~bm}7;s2b4a3 z43XUaQChmDH90`7EQl{bJE(&!r^4)qQCp z9%*5EQC6<@^62~#!q5%*xu&6Ll(4hRO?^6ZiV+rnLlPz&8Xg=A`xnD;Lmt^1$68jH zUW(M&)@DD*=9;<|x&B3xuhYZ7lg%Xysw01=@#o8EN2p1I);DqBvC`b9igh|S_S7fGcUquii}hxY z9^;`FtK`zuSe?yCIy1zSL+5X=tkPhqZ^p!TqTs7t*sc$2l&ap_*AVO0uL^Eq3|6wB z%e4>H|6IWbI!3TgNy&S=q0D;gK}2zg>pT@Q?IOJ{IiZ&e$E|w$mL5reg-nH%>=xKm z`xtQEvyU0X-+Sw~mC7tL9&GtJ&|LMM{USXvm1?rFq12xZt)O<73Cb;?RqmDwv)GfS zqG>$OdqysDgznaZzvMJqqdlMU+3lU@-2lj{``+Z~Si0TSanGizYRkSbv#SNLXVdFf zI$K}4tK)YIs(qeB1d%eUO{_a3F^XrR8~bsZ7rG*6=;4tl#rcJFruiUi#g5|$+R))G zwEKY05Fn-a^5GP)c-cbs_*Pt{rd(_!n;;2_@)GX7OBG$tYM@9^jnz5&nH~2nkgI;- zyFi)frQ+6O6RJIpsN){(t)oA)6SAJD-v0D~X)Dboj@X}0PZZU(W58g)N1to%^3U8E zu6#0Y9XEc}^)QX#@>XB7g=`G03&HA-GAqEknV2T6g7Ox3`?pxT>iLx@#l zd9*K-c1rOV!63y1N7`*I@2k|R?4%%ApE^d<&~TkCf=^WPNMyBYA0Czr7wfKKMFLD-leo#Mj> z14^hB1YmxUHB5#wFVf-!*QaFbIf~LLVkLjPc(T0y55U|5iM*6^H!zNM8R1p303@s7WE#*9);GcGc_QdV5w`hl0DXJ+2*bls5o4` zsY4u~4A5`bEN_@JjgF=IGS|~s+DEShRJX2v_sU^&u&;CGi0Y!=W9(YK()nm>pd}O^ zS|D~5i8=_{UC@63@!56x%HRJaZt#@q*KHYD$QL{qGtcqOQ3$1hpO3a3RCZwT`Nh85xG`%z2t+ldC+N}9q46#*Y-X_4CU({z z(YK3df4cVaEK)N>ibgwclRJ7|O*N+PJL|K&jaI;_C@hOU7`!;R&kdglZw(zhy_+}9namd>^ znsJgtad;i1$6ok5B!HaXkKFS(WEI4|AG|oZHxR$O;h@ z#0yJB$=C-Sl>6+e!~vZDo(xT^_{}BJ%>T0Fj!XD6&M&X_LUxKWtau@@>E7YV3D*o| z#9vDKR%LDwJ?Q;PfMKkxt}v;IE7uCOW@xak51&-4P51e&HKU|{kyOtd*Q&Atn@*_F z!I2_cYu5Ii5;d0!CzN=~K0Q2=`QE3rxnuHHGcK5Uo}ZTQx#&i={1OY=+x)4BeQ5qy|~j9LoYDM>YU%Du%g0Ee`A zZ@VcupRMM9gxCClwoDBdnN~X0@%S8|*_bu7!V`B{lWO$XlkM{WW732wLC$GBpnNK0 z+n|DW7O6>w|U%N^i9~y~8tMtMwKFIkm(A4*H1gek~z$Rj|2ctba11*zDv3GQ( z=>u#OA=O78T8!r2a~LRVkpYs9mT36QAm>FNtg}Y^xqFG@ zqcBJkVBIH_Ctmk<;AM&b81B`4<5aDW2Ibz5gP=AC7<_ocZ`Hl`Y>M+Mw790E+GnqZ zyZhxRFZz_}PVOi*x$9cZJ0I z#e3)R@(PB9%Qg)P3J5(seF^b+x;m`(Ay{nHpp^CS6B-i)GTfbeKl>DGG{Ww~p094o zs?{b=?O3i32FXbgEOEpyH?BtMscI+U;=`WplB5&Io@eQ^^?5{Sy4Lhxjwk_t-{Lx9G zJQQSw&VNJxC5PCx_)u@ynb#|orwRLo9w~$AluI`oZGASSXNU#7$uXRLh;WN!DpbZbO9eW@ zs5cP?&^mq%d08Ny?11+!MQDBI@p+&d!7f_r`vxLj$FR=PjP{wvjmHDh&g+o>^N%pM z546Y(2HS+j!hO>CL{Ja<$d>D1;VAQ5rwhI<3pK72@5Gg_L?Ts5NpToUmw#q1k_?Qj z#iKZ5KGphI%RQ7aWQ0`4sQtZ{AMu=)q;QiU(zx3=b2W%7A)Y~QsD7X`QaNdazL z7nopSAI=s|C0`%fN2M}JYaHoCVRpV8t23PRDdoRFlH>(YbB8&Yn@WAIrIB-hlrfVl zCr?&p#lPC62U>C-bIjP>C*0+*zZ>eb0-bE%o4ELf(5qBDddaiWPkjxvDt_Js+x>cO zL5)X+Jl9l-1vx<_QFS|c#KMIW{en@sT%spG8d>tPE`wRcbwV=vbK-ya;?CM%DVP_2 zvhJ9+UY6}@GAyeAs%~tlh8tKQ+8gG$J?1jALPJBD9etwYzQIEfgRY6?G<@J*Z~E{> ztBIxC$+5vE!}XRm7vs>#9B|aA|Jj|Dipk1G;{KyS`)OKUP>55Q7fgc>s6} zjznNvhlsr5L+6{{R4lWAdZ55jnyd02&pH&ks5Pg=3g%U5!e5T?z=kL1GootvY^I?# zOa6FL;`F}a#$ejrZwCY!{e3NKp(K1x=h^9h$~;zwg{z}EYJI)nU1=I_oyAi_IS9|6 zAOlO}&+QUP-6|HulAttj+L^wTcG`B&-?OJ}4XxH2lM8oO#HaNJ98>OP4N2fO3vrxlDNyWjC{hcW9lc3r6MC*jfFtsoNaZ{hE6rPk`AQZ!rpM-iuQp^bBpO&l;!|4sw|hGq*wrg% z))vv0a>p;rTEU*yqMYlH-cHVa(mVeICI9zhLHw^YI%8sAv-H8b4ie-2T}t1hMrdpn z8uKOvfIA0I=t|uoS*WT!ga`Vol=ttxl9~NP78)4dk>kpN`o&Rz)!UB;8U(ntdHfCf zX%R`7as^rZ#-T;@Xv8u8JW1t4RjC_L59 zb8M{6$kM}GTic983O?Cv4@;Lgbq)Lh#2j%jaAGYX?&Vw;(HdAwKI|WWZr_l&SCXKP z?}pGC++b)>^4TU2!~>$>!}rx*FNSHW^3$L!G4o760fQ&TI?#yrP!E0ws_HA)*g(TM z2x;H@TVUP$*s#{`J`g?i`7XL(A21*G#$2njZcMwFummH0_@P`NK6yij`xWc3@gf0! zcU3}VQ=&Y<+#4R0`aSiE@#Kgv(nwuIvC(ZkJS5F^$kUCzmkDz8zX*F8YV;N6*)ZG( zE*{;gw-NiT=&Bcx9paT;Z{XMx;iWTe6D-0~U7bvD&y_G>F8}>EQcg#QaiZMU zQpo-HXbT}jNpYZ+YBKi5M6N|9Q3zFZUSWd@!#IDT8}9W=OuWpKJ`3XRfCtJaFfd>5 zCmI^;Ugp?6r2V_^+UzUot#ow<2ur15@f)ioD04iV^U%6PML*^^h+zN&Gd>d5Uh(6Xw8H)#Zt!?8Q{LdD zfKcVB>v;_s;2E1iH|Z}#Khi*{_+U5ffoO5$MkQHg4)XnTbXvNI6sRQ)k!4}6{j&aJ z`}#-m(;HX}P+O|?w77&Ko>bb&3(X0O=I6`PKSQz9M&B6A`tw=2&`tFztE7~CuKs8; z1!Um;d}?8epo(D8NA+)I`eWm-hi6~hmzAvP*Ojx)2Vhaqrx5aM_Hy;)C+B&DjXo^t z&<65_xBr~G{;t{E35Q474+%pVV8hiAmWR3!aqugSpoz!sw(lTTHO;RbS!=L;Yc3@f zxeR5cf-gj%MTpYqm4{Z;oa9W@2%#R##YUPJyb()`9>eIC{YJYhse|gw^b=fScH>G5 zLE8u0WvQ%(D+}b^!UHh0)GCtkQSE|h%s|Hfn2XoOja2!a11~ga{rtYW;mB{aFoM(3 z?h)=dj9pk$NZ~`|Fm`A1uEOx?N0{dmAdTO)i%6mhO>JoEzr~Fto_;c4J=!=s+{a&S zksFq(+YIH@Go@CJ(X@bDr7uAX!g~Y|G7vxo=F%F1P+G>Q;Da|L5pU2@*wSb0+wc5gY!X!g-JRVCGRVhlirR6%6n5~fspCJ zQ#zISp`DX2mv}Jbyd@T#@*S>uKCrFAPUR1D)CpYvp2@*a9bG|01bP%4Ocsw)kp?%U zT?dVM5%waN{$3!Q27}SX@j5HAW^$8{AFuVd6m3AzSq?t z40Tc5yv+wB1#Ak}5W_~T!s*&}MQc}=w5^0lx#C60p93vwM`)Nu znN+n83hirM2W*@N7ZyGSC*;X?jXpYq8N18M2l#oMZzAo#ujACh9~w)lUi*F@^g@jt z2rlHFj?Okz54_N1g37G^GpBC5;5hwGP|nJXFuo<6Yxv*x_op;cs_apJZq$VPo}z>7 zOQ#kZXHGmqqd-c2!1rV7rs_J#6mc#%>YU)X^{kce8Gkp+VLN7{v zK*$lq%}IJy_DjU*oJ-OZhrG_%O1JP6j^e`?xOOZ6rQ5c|Kw9t0%pgDp5a%~4F;Mva z-lar%DRZW{O>W0`g~~J?p`=haWn8$sjjr5dV}bKnYrLTh;v2U)CDi~jW;#ud4nFoQQK-5 zz;uiNbvs(g5BOlkznx9LiBVm+OuulfP~V#fk@M`3;9>OCEyvoajO#M5eXcB66q)Hl zlB8t3h|&?KS}lw4k;Z%V zqYDYRBpKXGsedkch2-;E=?O<%_wTe5trfaa1P}q5l^VGWM{o$6IZF!?`Sq+NAMo5B z{YI5w;Fu_?{WK2MSt|czJFswLLx>8VtVy^0YR|9?nuA~pRltei7*B&hI~6=4Akk2k z4ref;f$wUBSYlTlbh8lJz3wt&f{PZv2{>0?Cb)VMz6pJ}7@-LRp-Bs;3MO*jRn}|U zd2M#*6VQ~d-`f1%kWwG_HXZagM28RoobHEvBn~dx<@{}8=@*;bt(#;lNQ@MncpYfl zd$&cKcBb3HBQ%nuPDz_gZWWU(;6-b)VoSWr%5@IE#T7h?0b!c!`-{JB2 zwf=vX?e~wU4NmpHd1Z$4?-R}pjoGcYgL5+{0uH1$UenI9lNF_EkjstAW`lrDrLcrv z-Z5-J!|&N17rU>7pjRWf%EGCc*Yj0WtKU+3UO$k7T{!zrcx`KLFN}^6pfyBdvy1>A zf>fO+!o}1ruCcxTBC(rw0kmA&NSK=jo^t3A7o(DQe(B>)o;Bm*GzPD<2tN4>Q`}sE zkQt%_hy+j%-zLhu?I}Dk&_2fI90}hxxAyCH_tv0q8|Z5=Z-~@=`x_$Gv7iPxnfTNc z=iyyB^iAMh3khf#`FC%E3!X4zmUx%LCM#pU=9S@-MG-1*|L)$cSlXdb?Xp77A(AAE9iIZ-ixRo5#gNX)@NS4X{)f@i-lNSQ7{C{F!Uq z_)hr@sNrp>mE|UXhbr!QWay}8+U=sJCKjHJ!MvUDiel9IAF$7+;~dJjy5x5aG61;d z+#bZf+4#i7OGHo@_9d7r;QMnnK(wait;G)m^#L+JA@ zs`7Pvnt+ahdjzl^N6tHDeN{8)D?Wuem{{Nwe)>AJB>B=kjn2jm)7=^ znYB_j$6d==!5qEiT&{Et8YCL@=^Z5heSe>0{ldPN)&=BeE2ICcAh}FBtR1l^iFcW( zZR=hOWAR@t5y2V)PHZ(IxMpY8u=-1j=JkvxDX!zcPJI9Z-=+$icnwOw?5XDT6MeU; zuLcfU9@QQmTRDOD3Wv)~77`e-?bf&3IaR-b0bs@coxHd@oxJN1wK*tmcfb?Oct;M+ z)kTI2?2oQEUsA&%@y$gZ&GK9FxgX>`Z>8a5SSDFl7JVE(>_+|E~ z*7Y!qo!Ln90z4{3T%50=nT=*lW6tQuUk-QGUft{NYH+g7=4Kg4biMbVp$L&by=##n z9(W;1!P7}i=YBN6 zfbBicZxK%iRz0?O7t6PC7g`I63Z_l78%?8FaWSiOTt3s3ez(9bcRXh*Qx#=}5?ajV zEiJ$geaifUvy~ak*pGJXCdLr#+hmO*j(p4@~8ie(VEg-i}=;{Y1*dxFc4bUaEIhSjjJU9 zY7?_$F$vhHwuuE%NLOhGMa+9-tZAJofo8r_M&tJuf(8&5=zS(;f3|ri-Tl9 zGi8_3EPl#xJ!}&CZXMwQzVjBY@{N4mo3Be!gU2VAz0TVKd+FFWBZK4<$jhce<{FwK znB$UoYGx~SKIhHdR9&v&%p9dASxd&Z`Cuk?IfFp&?WFE@3g(>sOig&;`dE&Wbn2rQ zttpevl_S5Bm? zo{A=|ktjg)x8Fhuul^np5w%SO|4e2z(oXjPbEe08I0yo<+fG)zcQZp2&T*b2ekeHpOQIZ=}vzC-6CUApdWZ zTBP`cBvFA;aJxDiq+~633^zaCww{j}PB~wdOuf;|YbN@t@O8#8h6(fQVN^iJHyUh{n^^NV?zFUB=wQ$nQ~c6ucUN@Ej))GX^^g4VVarr@xm|gq*H%QC-In?}{)u%% z-c8PF#akJw99`*#yS@)d+qheA?1^=8bJ{NdX}s_inlLA2#aQsO|RYv3_Rcq8psvAd=+t#$W$FR9)@&CxC6 zkH)+uEZp7UGk@s^2^WxszLpy4mlyl}=q4-UiWSf#TmL6IkrHHyE|f-|8*K-mcbp=2 zD2p1G*ck_t9rZ9H3i0oesIVIpIEUFMPt2uIxNg5oRo+07d#}5-^9@uS*ihgbIXK{e zZ;`UfW^{O}g7Hfqj5JIl9fkeE{U(Ncp9}E6Vo#oRX_UAW$^^-4Y(k;SG(K3FC-EC! z6?QZ?Q$Wly+*}xtmTn?mHT)%x#RIQ0;GbDiR=urgeB^C3_4g}ZfSB+09qAr)q?)qy zpzcxES^fQ+%C!}|2-%)4=zy^8{)5T3a&C2{=7jribd`Jmz_qe3@`TY)fauR)G7tWF zdr9f68M!E~Gg(bHv|p~!=A=1WtXyW}fB8zYvLvW5+JA-X74FZ5wdo5}SAE*~PRmxu zm^gcfO?rFPNYox~w0y-k=}a zp&yQ5GZL9KPc6-ATU}hRe&Pl!gZb8}47iRjRqcO;eO1NRu}exvq+w*C@2=wkzKNT~ zF$!(w;ixdpAdk#=q;MEwh^A$NDzJKJk^li?P}j&-Bf6*|Ui&UB&co4t3-f-xGYlkP z69z>rRHrEu!ya`V2+rs5_e?}*L)^{oMnVlGa8tMe6{r4sM;Bu+hN^ z)z7AC?N@ue=-}wNXH=&i`WO$9>Zq$mgvzsECNgwuqATHPs@NZZCk{l$;iZX&+Dz08 z`ugBRT{hJzGaB?vqY4vn!3|}N_XCsvwq7QQeBZQ{{R>}%=zm)-0h<=mBm8WucjX&9 zRIe-lnCQ|ZBIN$HG)Noz`rUA4CPOMB#|tlEuR7|QzNb}wixT7a6H(C8LHXO|BGULc z{&i@YCgy>Lo83)t^)LSY&T+fEbXyWAYP8Yz+F72S0q>!r+YNs`%9P(OZf6R8gc$Go zihl*{CQOtUJaBxJ|6p1Kg>N%R7Fup&JHQ&8q*Q%b{S0+P6!_Hfxl&o)sdbgDC$yLn z9;LK%I}B-X<)6ig_5n9&>-N$|lldp;+&fSb-;KYCSYUm`?5-g_x#cv$m|&0J|3$tR zQarkRu020ZPz7wv41b5a3-&L12XLflWmXXHQO$;hpM&%S-B8xxz_&-CcM`x9059Ab z(*E-A3@b)bx`v=Iw!tp1jZ$=c6h=bOra7CAN8Y+1K*MZQ@cZ*Ki73pZz~@^xGgZ-A zHyDg$H7sYGXPf>tH*zO=?dH#ziNb5D&Q* zN+6z`+NXNFneJMTsz;i68BJ#v+)-u^;?CIbeCwr^I4+bbmd7Ks0pD&MiB^iTNlN~HiL`gs~Dpt8fM24nXwS6nI8@8DXrjyxam5t7= z3(jL`n>57!FQP7BcnRNed{5X7PE>x3AoM8z@S}T)V4Wvn*Am=a=ViU6<-2I{{_2F@ zZRQD{s+xJ8s>Spt@3lqtD1Rzt{b9a__?n=~mLm4;NdehSabkN+RYf``&GN@oi;%ha z3?=ymM($P58M)MYJXvA&^}voDzA$8svM#jqv-ntx{vanrKC zI)WR@@OJH(Y@v#sXbilv5P6caT6g6NrwWjPSgFZiKPR5wfwX>FOMWlLDX#Z*m>><_ z$9@@%ZyjUWDX3~JiDbGXRqVlTM! z1J7sN&~n$O@#&_?(c_bf-P^GglfRh-dmi>*XM-qJwYTC8F)977hgqkq(=!J zj|mBLDKc+wFhNgID(KO_J(e-E>Y{#E&*k0g_0E`>eqFZ-|o zW;&kl>Pf>jTxF5F&)PY^r~9U8fl<@#SVro<6rmb}2)zh)NE4_*>1QgRQ-ntHdLK|W zGbT(Zy$$N{FEZp+m4U0y0^#g31qF&ZxRWO|?U3+qvbvNA+nxt6;E}>M$^L~w`_~Kp zt6|9tpZj~T)On_++}Eg1P58M8SI$YgS}~g;<{EegVVye_Gi%Cwf6J~x^eoq)n5&J{ z{ZLh_Ef_)ag694U;pg}^c}r-qiHF!apF_%xp~=CEW+A7Ith*r(kgGhR-s^aN>t&_Of>`>uq(kz2@>2glP=5r0cmL_tLV$QHQ+IGXc+P(Dd8JIh;prsdi z)3{@i$jGK^_MV$_9Jxcv*k)59{^Dq~3&ytZWY_1+Fbn&v5nIh>-^^NSIW3o69KNH5 z@!OT1uksr|Tn^S@Jmn8du>6O;1h1(aya#*D+Q8#rO@r2`IQheD1)W+I+`MXvkCdifg?`h%N5{24_FwZI^0Nk$Y^_7Gkn&+5YU#YTiEcDFUo9y!+oZ$l^$L0ThI% zb^gT=uHCpOOVjVN9t=#7mk(Z;-Or0aCr0vJ@7yTV7!3U07?jtH4ejv1~6Agx?5Lg%gdTo z>Raf4!hvN+EEVS(4tYDh|23ZND{zG&m)0PGYqtG#b$fe%xib^kel@3$Y?I|Z`a$eR zLG>s(T2cCKWzgQ?v#FLFzG82T2zvC*Z{s)B`8X>Vye*G)uFqHJjLxJY+0pd-`v~@O z8zU5#LAK9E+G?JwE-*AHz3dIiLnFs7xK)Ga8K?rYlGAcgj!E({@{3Q2sX`e2Usz0CSEa=r4|cDXw8Wco{?dknW_czqE)0{J+mWUQe~SM|$$u(shr4 zT4{y^wb5knE>EGV=b2doHPinbrgC{%wH6iXV*a5my^hPpg{eI|nI?BW=$$zAe1dfK z#{>B0y?%vIZe{GndEbGv4~kHLt!{q030e{IS7%++vA=d68rOZ{X?VFqRQ)agVv;*+=JCX& zaEy!kxfb(o?=fGku#k=2DD>M&!i!z!+cp>kCIkX1Yrverciqo(&nQCeYX5L8^~$6E z`*yw!Nq-8aVnF^<&ZR$`v32|MURl1zBfk5tq+77lpME5q)yN(h680z3sz0vgqxR0! zHHdK3hp27r4FAn{M8d=M06-s)3Pho!jGm;7pNdVsMiOIX6jrT#vx3Hr9<+sD$FCYJ zXHcFMS}O1zy94uw-MB%EyMD@w_ko2tsAp{7-65&qBKHB&jmlgxAf4ItM`!YXH`=>5f{w8vTJ4yqkp6dtQsS7Yq9g6D&wJTI=m|+0(7|&G zbTzUMR3DKrYrP#oKog|*Um$gN*gKzl@k{Q>Yx#DWnoPNt70`4B;;-Ix=%Z+Fec%cC z|J@6=k-Tde~pPhw|cItKl`@{p7kUtsZ z-j=VfnSX5^@=qN9tGn5FnH;aWf(t8%BUU?-CWTJ7c?xrf5qcI&FRw(Qt@J?a9UndY z%cnUImADH@FsHNF(Wd8I3quBL3FGrln|Zy*VH_$1Cvhj%&e83lsFKP#^|ZFW;X2f_ z!3))5T78(X}K*i3=!L+C8-#o7BWyK60XH!GK8s)%jOX=>aXl8w%{W<^vHV zDgdM*{KT(sZHwdoLPYsbL>@uwnTdn1(w(SoltK8UiMrH4n zt8^4DvA^!VR&%c#>zY_zSutj1)la%1?{`V7jb8FgCy|~2-G?dsVrwmt^E^!XR^wsJ zRMKci55A(WE~xUi^c$R*93Cb%csCa*P5HODx4h{zHf5Y-=s9j?d>9NX3x}&Y6b*`_ zmk57?YJf~g8;$BMO{+@qY^i~x=R0MGs?wJ)fi9BoU8kBX1in$w`LYHNmSu(rOxvlN z*K>(t>zj;>{nqhWrRpuJo1sk$8mq&oKfe#R| zFnqZZIUYZA`7YZ=_(+D6^vC1rjKe_2A2C!Rt0%{)B@%64(jy8@bEubVHiYOwhGKA+ z@j_J48=_502xhd_YbwUMU;pfr2zf9Z0^h|s@@(}-jK+bGGac38cwoJv+Xsc(!{T=X zc-40u%k7>h4;a^`D@4%6{x+b2^C~dnQZ(wz-!vOlP7bv;BG!P)NvX5(3g(3y{)uvW zQ5rXBbnsocF-FfuZE|p3 zd93w8?|y?0l)5oQxyWh0p5Bu)3pnZ7#=q}z^%bk7P+ke>76C3&`Zdsj3)`|Kdu-V|p|G0$v3tdzYr|mTzDP}DMHK5KL%Pml9ZeTH z@R;v}apueWHqm>SPO9gPJ1kUfb1fAhR_xfJx9AryElA`&Uimgp2G+s@>NzLwP`n9^ zgtd$bL;KLniGg?kWR^{{vzpM*oXA>r)$6^V5QsTwWs*JiO`Sf}3u5rv3=2Y$y zsr>co)gYd}x`(*!{PuNQe1RSHRX+dG1lN~t1ofl5Zl&tP)69EZ_0$UN1bVXdQR`b% z=XjV&?GFHLcstqS2OlWa46{In1fH+P_U>AmOqFmbkFjuma9v}t>Am^7+{KW@)w1-G zpMLEpATF!#o5+s!*hrfrt0U;Bw5tTHe4IB0=IY{Y)fJEC>iztY6!cDdh^fj$j=FIQ z^TxB+6#d(6#eKJ^4@ssLIioiYO+H{o&TBtNz|Iyq8kiTSB!u)oBk>3nBRv zExFH>Mh=0a5E7a_}-=iuvLJNI_!@{SIzrUQ#9{!@Z)eaZePgpL$8DYZgG+ z$6r!N!la0E+YGdp4n4`Jq>WJHBpoe9b|aJ6AG=mM9F#&`aQ1bCjg^4$g(7= z4n4Taa!I?{z!T>bem?K$CWyb^IZ~iWEQl8%PIF93j3;oTeoUZbhybZSMpK$G?y6Jb zBxOnIs36u^?Lq(WE@aB8VkN{;Mg4r(bI^PaE1zi3sh-)n9&XPg#$PK;x@jVL!t#Te ztDT;_h4gUINj9RGRbK(>;4S?hz#^6^@=Oc3JnYz+-W0a`5cLW7^%E)Lf2lI%APovdfme#)L?D zq}Z+v^H-ux*4?*Clbx4~fWT&xV&^NAi=Vp)eBYg=%Q5Uys(s75SE=?ZdhNv>4);bS zkNumBC$~PoosOOg(cvd)Bk@EA5{!9G4AU7BaSC(_bQpy-q-&(_+y9x!|8lz=Va?>+ z9+?=M`CpX)TC6baTwwQJL6^DGq{4B_kSnd-i5k@@$15X@bzjh~t93tjAkmpeps;mL z7;&JH($tmYJDB&0CqymNiw0;>0*q&)yM0C*(v?p-$I)eNf+Jn}=rB?wQ*^~46 zH15JSxUeSz_&wwvQ@=1mfCT6IkQW^N@1#8M;|=e{OXP#PKMRM~LX8^%*WMcMBqbBU zZ91np`UL!JKd$(cYnoZdt~mFL5*b9z`hfl+q!D zu(7*K#*Ds`$JW85o)v^oZKmjRf)$Q<6B--jPx}-$wL;sb+#G)E>igLBIVscTbg9(+ zxaT|@_$??FOxxQ4C~4NFzJ_|F}^q^0Hv<|MO7cP>|_X>kC6nPLR(iZD}4!W z{u%Jd0n1ys5&x?dw(_m!yKx+HR*c3_)|;Z8XL*!l9j+w)`qE0&kFjT&uM9d562Jhk zILEW|4a&6=l*O4{la|}cnm#x1kzGks6X`YYH{w0M`(^~vxXXcE6WE2ovErv~7Ic;t+q- zp{#`c=s8VxhV>~H+#afLgRX2$=pW9aem_=K6oFJ#eC|im?w|hSJw`bE*F}=w$EU97 zW*v%jzhE#VYDL9{GW4}7fahNfoT=l+LHSEJ{CUggCf#qKE-6U9^VPKI|8B)oB`;5N z$T1dDs*|Bd0TTr!@mZN_{LsL6>i=^TB|B3D3IF~bX~0@+atmnGySw5Id^gyoaF>t! zKmu2&wh{;y)pwc=@NgdVF^Tdh&U^m*LP3gekib8r0$h0C@trfheFxm9z(-G6CxSi~ z>5uM}VRLk#+W<^<^R(oaxYy!`c7TV5`%im%g(ET4p~x!MMpsfu;(w=xC%cqH^%nWn zk79Dy+4Df=`R_Wc)5l&hLH|puzyjZ^`Au4}xM%-=UvvF(Ag?4|rriH)@3|V9%DUYF zBr+CIXFvf7ii(w})R7V_z{r5;pcAD9MJa*=L_i3TsPl~l5iAJO6$KfZi1dm) z14xe$B8C=PLUPXCsB^!+a3AhV@{sI(RxkUky_P$8AOjNt1)YDMohmJjSwf2dB}e*7 zTxYC8qkiDytm*7^&s9`Q-Cyx}A@=Uo{aADVo5^59)fOYLdtKU)-m=BkK;ItwK5o-( z>9vu**5^rs^i7K-J8<4M38Q2TW|MAFtEoSB#IMjh{M+1k#ZFnxr&3+nv`t6NmE$@T zVuAsv49;bSc>Va_`i*CSoGH9}{cbRICGER7zoUN9l9s;cKRsGcbk0o29Fu=BcNu^8 zJ7O=j7#Jm)dyQKD@*o}E3=if8@3FyAEG@uLXALT}q6Gen6P&PnRfAu#J8S2uNTfyK z=r>ZTHiTn<`76tg$WpM+M`e4f0*O(Wu;vUNo*#+RFR-bWQFeiTl^V(I9 zx}TSkCSqOuSZ-%Q|5fi&c!}Z{ipGB*4%?%Tn%3>bGCo#`Ju;+mOZUNvy9rX;+!lAj z%PXgP_>`MPQ+cHCbb>caFw@MB%U6JFVZrk*w?uWK+#{T+|2DYz7iQF=$fn}3JtJ@1K_n+NX#02{$V zD{k|(m9)LP`8qv<$p08Gcn#Bi)!jBS8ts;NYNR`ACZMnd_{GkT4M0uy2k(2WTn?s< z5uPI6E%d{|BegvRIre>0oLH_0J)8yOjOG1r%0miHuE!xp%4*^)z~VY)-p6#^(J=b= z`LbB>1YMk4#X%o?-n*Hji$~fpFTy@^4lnk=`9O0ABR^R&3aP3Utt*_lrQ<7M6}=L= za0Gw+2USJtQ|o}~I4GU>`q~hYAWqx#N }NT74B7qwJ6SZ~TmZ?qg_pfK~+!= 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 literal 10932 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 uuX8dkN1oFes delta 540 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 a0{1_&;gnn2jcRKE0000VP&)c diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png index f091b6b0bca859a3f474b03065bef75ba58a9e4c..237041ef4ef465bbc7c6d1421e8c2018a9dcfa73 100644 GIT binary patch delta 2305 zcmV+c3I6u942%+xBYy#eX+uL$Nkc;*aB^>EX>4Tx0C=2zkv&MmKpe$iQ?)7;2P=p; zWT;M7L`5963Pq?8YK2xEOfLO`CJjl7i=*ILaPVWX>fqw6tAnc`2!4P#IXWr2NQwVT z3N2ziIPS;0dyl(!fKV+m&1xG5G~G56v8b3zuZn?Jbf6zy@PA`WW|lE0NpZ9sU-$6w z^)AA*TA%xK^r#t&0X~st?1$8Vf-E(<&} zWF`~y#35oa*ThN_v%IMhPZ5VzO{aVz<*~|ni?dcNv-&;x3xgSbWtrRA0S2B7*_2($PgBTd zf%h}|rZmuZ3v{n}z18<|`T!)UtN0CYa0ra#D0{udyMH^Id;9lHy}utmcyf~IB5y4K z00%=!L_t(|0qt4|P*qhJ{zO4Z#3dC3%~5kuG%(jK#U<2wF=$g_i-aiR7Wzb41pAH`CWU+Mdv^ha{AM2SasPAA z`M>j@^M5b*o(A}zp~1aYpr-MFa9zg(!gU?a6ZUN17=7O#i^e`r@joA3_f`+e__>4< z`(fB(HX-!t%^9E@KRepD072@3~xf9_dFs7KvrlkikEp`5VdjcX@7%vdJ4)l9|z@W}R098oN048q) zN`FaIklhdoj0phF<^#Kb2U0SDUHsTf%#5t9yYuwAl?Sc_P8a4 zyR-zpCfUxd9JehfQoi$x9k+Goidi=i@bUyc*b7TYn@Ta#;*rXUUb~o4NlC`J;2(d zzzTZkF+J6{YY)cqX-@!gM~rH1f`9OAE=4!V-KQ=A;c-A-fxbS^aG#FlByFSx$33pz zX3mo&oCYPcF#tn^-CX1fm6X!5@%qW$iTT@ru&#{BD$Bb&!(JdQZOb**w;{p{ z)Mcv=07uUlQ>FsB^x)Aufn_7A%7gMGw*18Fc84mYTmwmDyT<%w$NM0&KH(}q# zjG3P5vO}lzE1-pR$>X^COiv$uSc%Jep__1<<{VJ6lzzLQUjZ$gkc2c5vf6sq5Mj@T za#>N4eg(8}U8BF|k_5#Obbk{r(~!AZU0rK9rJGucXh64$S2bqchszzOjVYprM+ zfdwoEEGgDWDy*BjNYvD~9i)MAbQ2bp%Hb?vSof+jf2MuCSv?)e)_<=^Wprh664F6k zminjhe)YQ<&g!;b)Cmi`%?c`fEzDV&llsPB^=%`t`-JiS4f)k%?$hNX)oWL=4CgaD zirsHqsqq0oWRSY7fZ0`2y0Lx*3=tOT;}<_M?F&_x$8a+Krc+5iuCrrWb|9DW+6nTf zp5n>Lh3h)|y#t0L=YMJ@_cQvcFFT=Z;v^tyD{I%I&h_WUdtT(qh#oGlvxRs5=#Wv3 z6&NOb{2cHxOTm{^l6wL13T5>;-~~=f+(BA~+aO;hhKt%-EaBIN(CK+;kGbIp(@9Z! zn(zFjB^omtShWvWva>u}qkpyD8A_}6XPPK^D1RsR z{{hTQhUt)JDj6&+1xBv}cFX`mJ88G|U=F@$s8Z+Vu)uH4s%uG@bNU}U%c6gc^6bkj zOlQ9ZR3euZKnJ$EGkmQ z&kxh1X8^@zFhf?VyB6WghKCbzm`W!yBYK1-V<_`$Z-0L8&E%dhRho#G2jScXBYFUT zu@KltvS;nEs_?iZ6Uhme*ni;PXH5-E%Vx8jOp=t0$ItVVa>oU$mT!qYO(v2Pw#95h z$B1PRhrVhXY%vKkk({tNyO?K7gjb;I@t^w^#r6!Ouh=FNYkTSKk#AvYW;lOIo;}=m zC_&1Md3q?iB7MK8O;{Y%ObqISVTrM5?k8S+SR5oQM6vxC$<9UQm#JFK9LHgCP&2;E zsN2W+#0fX$(3h`M_f{{I-maTyEN{h3LlH+`}3HRDcHI4^_>pC6~ buIu<0I3et#iqgn000000NkvXXu0mjf^b2Wv delta 1572 zcmV+<2HW|J60{7EBYy?^Nklc)UfJ<2uenUn3ARN>mf|M z+(eagCX~wv_9N9Br6dE4W@{R1X8yU75JVAXqB_2?1W&7xoYHg zkR5NEVWsUG^?wLzRHH9Aa#eCDK9NvdPE(De9-HaQc(6~^lpTe2pm3&x9G1K5Xawdi z4F*DRWI%>vgEBPFqaRR51|&E>B!fiay027YsK+Vrn=1PqDKdi{m<&P1nQ3_Gw<%cH zHU+slT8moWCbIKH=(W3t1j5BNfsIKQ#Mzi(J59kx zT!xVJ&SKqb4XjdxWQV7$IrZ-{l-x3`7M}qzHsPzP1S_$c=96h(q%b9ZXPvcV z;D5oPsau6Q(U`-YLD*G41z_cgz)a99av0W_U=aHAj?mj@2KZI50efIA zO8^kn_?FjaZ_hfbhSh@3^GzxJx*P{Jc7Hi*OHPZ!wvEV2a9XKq###zPPeRs75i0(W zU?doBGb9yqT58~qn?wQ4oCi3oWdc^W%JAgTVR)*l33@Ky3ip4Rqz14Wx_gwsGYGq) zMY~B;*z8j>WF8xV`^#!USYHDVZf}I{OZGzBnutHA1D(}!4{};5>;)mO27l7F zHh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG;P%vktHcXwTj~+IE(~px)3*MY z77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC(zYC$g|@+I+kY;dg_dE>scBf& zbP1Nc@Hz<3R)V`=AGkc;8CXqdi+`PV3Vc;#lxQO~AoO*}pfE+=*r3ffZ_YMC zflq>i%a-`Xw)YRuK;MUtq{SGa7vtY^gg0bKSp&F>y|A>h5qhqSvgdtV{(lh0c$xz5 z;*U?KQl%W)htLmD=yhL!@aldLHW(=H00^&qu7J0mA5v0IaX@pn_bKobsC(e>(nAjI zzjyto3Dmc!oOxjlw&JZqeL%+vsN0Zy8@1@rs~rRNAqO?umG&QV WYK#ibz}*4>0000EX>4Tx0C=2zkv&MmKpe$iQ?)7;2P=p; zWT;M7L`5963Pq?8YK2xEOfLO`CJjl7i=*ILaPVWX>fqw6tAnc`2!4P#IXWr2NQwVT z3N2ziIPS;0dyl(!fKV+m&1xG5G~G56v8b3zuZn?Jbf6zy@PA`WW|lE0NpZ9sU-$6w z^)AA*TA%xK^r#t&0X~st?1$8Vf-E(<&} zWF`~y#35oa*ThN_v%IMhPZ5VzO{aVz<*~|ni?dcNv-&;x3xgSbWtrRA0S2B7*_2($PgBTd zf%h}|rZmuZ3v{n}z18<|`T!)UtN0CYa0ra#D0{udyMH^Id;9lHy}utmcyf~IB5y4K z00TNnL_t(|0p*r|NK;W1$G_@a+X9zTt2Bv3L5&PEStUhjVEqv#=0E)r{WK~hQc5%v ziXtqGX-57bL{LGIN>E9qwX{NLA*n%SsaQ?y2RcpE>1OBdYHzvs-s)-iM_+jC-23j{ z=bdxUJ%8`0Fbz`u$IzI4X;^tO7dpdY8u=@J@+cgz+ge~bTZ9({r5JTcKp8Np^afcz ziecmOfgfh*Bav|W5>z@3p68jwq0Gan08Ug~R<6rqd+(?xl!sJW^q272K-UkTZwQDW z`zzT!gI=I2%^au$34b%b^JAt*~!d!rcfDn|B!$q_C4hEp>zl2#UuNlt z3=9K@D+!dr$Lk^hJ?*1i|Ci9vK*4VL zCJ%Ip!rJ3=@R7cuRPZewJe(Cx zz!eMs#>)914rM(JYcWJr-@z|;#c7+wR*J|uV#9EMmpGKQ6s)J2YBiOWV5r+E4rN^% zYog+^a;U{HOd7^KWWd_YeD~=IdrDzvT^sY5^)!=dLD?R%kh*y&4jmWk+Qyowf1zpV a@P7iT*Y+9Q({&yI0000h*(Yl delta 1004 zcmVU6E9hRDZsKRZ8`ICh6f+zh_Ab z(jRY2a{B+|os<)hT5u?RkVuNN!js0QT$N5qdKbV*dH@&&Vjutv8ARzpRqqL`fs%v3 z_*Hd00YYHFNIC{Q1h^F1P!f5C!xIU|E+^yzaDu?XOc`!nZ+PjJh~`vV%1!_aj2TH& zKwB1AHSp7kgnyx5V{+EJ5gM3@MPI>kpg&?m&MZY^mCPDRTDzYO*c$nehN*{r1;b!} zL~B$N2&udO8tBp-PM4v#VA!7mo$41xCTJ~SU|=SOZ3V-D{)qMzXd9GbL+F1E1Yg28 z1RU&-XuA$zU1(Mh+ag0OA2L@}lJyL@X7yGouP$&$M1OlzM%Nm7TCsNcLSx`u3}=gu zz_8*kvf$vZc1ylSt62f92V^XU?#Y zz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#YwqkoqZx^jlHgV2aMo8z}1PBGB7;Ej|r zUB(UDU}Sw|KG_{Ict^w^3nDNEn{Q#}yRSHTWQ?6}jPTd&B7rp|u4dqtoymH&RdRgj zT#2{N3mqTk(vPPJH*F&>&U5!|8yVTRi>^YB#EGCzC1t+ajDgsRm$x_ZV)O4@I&qfh zrhh)<9J$s!Kq@Bk2*~DAzd+ItgnGi{)g+zGbzBzZYX#nnI#*DSOq^;G=#=I-Jg>aXP1F zlEh^jkE`b1p$afa*ZWyv)#99=s8_FR$dIH^l3o73@CH04X-v{g?RT@=lj`|W(&I~U amdroade@bUF7!n0bR`*ZZD8H)ivk$9FFrcJy-JiTcfocD=CEHA6X=ftBXU6A;Z z>x##3oO3PokWD!y=X-L4SmTG72cdMub+K6blL3kGAj+x_*gV3b_hk+6mx)2Cnpmzf=WgK1r`MwD1woyA51iH#B7rxZD8- zo($QPUCB>V$Yz1}Gy0}9(02=TuX(-I_i_3FB&nbN#p zjizbo2-%d=hzpiET1lpcn&YHtrj5#sYYvi%C=@Og?k1pOf+$H6Abp1mgYwS3k9*&R z()eaB?|*Xs`_B8ncmDHl@6^Ws1hxNd1*!`*M4vR&5Pi~6Iii-PuIRbwbp%Wt3I}xM zhzBo+D%DI2#kj{$KZCTm1Bj1^#?9nRTW_&V)PHOrgvERbCrevXlr#elu<&mWwCLLd z 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 delta 1906 zcmV-&2aWiJ8GsLvBYy`+Nkl^fDmF(5(5$|?Cx}D zKEJa&XZP%OyE`*GvvYQ4PV&!g2|L^Q?YG}tx;sY@GzV#^0C$Z`m}blr)&(OPMHT6T@%AxHOWYGG zm>@=0?<)?0r0XK9>leRXyD^j4O4bXT!@LESVI<~>`yB+S2b_f__vvyMHkwGyyXedq3usx&X*}E{O;}+nJ#G_xm zJbQX5 zuFrVK*|x9RQeZ1J@1v9MX;>n34(i>=YE@Iur`0Vah(inE3VUFZNqf~ ztSz{1fz3Fsn_x4F>o(Yo;kpqvBe-sbwH(*Yuz!}~S^-;|MX;9Py1im8!}VY-!u4P+ z!nLEvx?+Rogo+Kw9OBXy&lGIvJ51_7Hl6Q>*MAJa!lMDGGML530`Te&0ciU({DC7` z;RKg{uC>($*mdXnVQQBjiVyqYC7mf!Z%e-Q!?Oo{ufu`G3CAC=UjiTpU<94UTFz2KEDuNS@KKkg)K8!v1L8RUP$_! z({LG$Dy&JLEVpiNVe^G(HCbA*SpsX?292)#=JVQDUMp-cOJPl`+yTHTD!AOit-zAD zptXYa!q%OVLa^mo25b7^Z>kE`3rqgr-G4i;Lw<+P(8khESW8aaBmxfY`WS#6j<92Q z-++&Y z$gbH4#2sT0cU0SdFs=*W*4hKGpuR1QGacHFy;`# zR&NJkbTNp}Ee5f08HmLzK%D(q3@lH;Tzx$?VT;&xJq-KyHV__o3dD)?KrE<6*iXV0 z8;6<{IKw@dyDwJ90H5^3h;|9?twz{UGr|>1Pzhdn6W(r0ifeZd=JI+AAz<~yL(QJk zAWW))+=`8mGj|J!70oF@d4B;WxB=jBM1bWWRBb&1!ie3BUzOdm}yC-xA%SY``k$rbfk;CHqifhU*jfGM@DkYCec zD9&E@F9*za0?~OQZiDE|Cf;;0 z`uZ%H=IeGyG^H0ruev{yIVARgwZUG)j7>F#3~dh+8DOtPByR=(VDhz2enf#EjcXCI z=NUPYr4@S)GhX!?s(0Eh(nGe!y$#$r1B$||I~`wPhUuCQEo;#Xb$EXk=6HgBBRd#c ziK|jN$e~{zV~|;*E@Ij-Z(+tI^B8RW51ptE!nMnPyZ`_I07*qoM6N<$f{pEq_5c6? diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png index 2ccbfd967d9697cd4b83225558af2911e9571c9b..f2413d68ba14fc7f79c44ff7255f7dcef8389dde 100644 GIT binary patch delta 1603 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 a0{1_&;gnn2jcRKE0000VP&)c diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png index c8f9ed8f5cee1c98386d13b17e89f719e83555b2..ecf0b29728886675a908966f071d18eb921aaf92 100644 GIT binary patch delta 2981 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 bK%4j delta 1881 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 TF3bAg4+CJVZ=qMOizfUJN(KeK delta 2657 zcmV-n3ZC`qA?Xy5BYz4oNkloFh3q0M zFesq&64WThn3$;G69TfjsAv=f2G9}pgSx99+!YO}2Uveik)GA5ewEg1Va|1@=x;Pyj3t}zC)T-c zzoz+u9TyK|)jteh(` z;D6}gLagCR0hn*76kN#wCv*`b8GyswI1s7kNaT!{fEXv;5ofK|nu3vU_1cRKLBd@TQ@Ei6F6QJ?HM!O!+{x(L<6rNAv)=55QOzlPWhqI{ zjSR5c$#aW1xy}c;$06La(Y*xlX>0w3EDGGn02k}7*%ZplRLFsw%E3MJO@BuPkU#Ja zcbp(_BL!RxGHHe;wff^+2G>u>ah(dq7O)#K0ekx={yBF*$P%aJSOMmjHHmAxvW0cp zt8@K;ETVH`1(=8Ie(Z;Hy+9V#xv>Mxz*+%0w;aeKJ2#eqVJL7nrpksp7|5bKS2)0a zId?FSYb0>4P=MLsl6(J^Q-5;@54jg=+fq2sO~Hz-hVXEO0L<$0j+BKdGX;Npqt{os zJmk~Y_Q1L83|RfW)5G^!;(u>i7{EI*HIqAp9`Cbzna*hHhI2Lm9I{{?%y{Hgdzxli z7Fd?00Ed;L$qk&zI`y$@{Dn;5j(iW`=!aXNV*G=kjb8%Qi`UtJMSq7ZT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah-w13P)xT`>~eFoJC!>{2X zL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw>F+gN+i+VD?gMXwcIhn8 zrz`>e>J^TI3E-MW>VM=sWZual)ns9B%8_$25BcpOz+KP;+J(1(UiAp*Q=dc!FQL}*R43bTaVm7wy#IrC-VD&k-4FWYMd;wg6!1z8a3SP;O>P#-$->_l zEAM2>hI~v3$Y(TlIdJE^0@|6AK_7#uc{~E1^f2gGJ^}i)rGJ>FDPRt4F~}m-WGQZK zw?8N6;NJH#XeW;Webf!0kG=!+v2#G5xX^L14Y#o*Tz9&z7NjOib92f(6aq==Ntoo74a!u6w~r!$mq|FDi7h6+6fTa4$$0Y>@Y*O}J#< z8ic&ucAQ^e_cw)#3uFTLcly|p7IMmPAXha6205mSA%EQja0eR?DV=N%;eS$2zHI*MpiObUuA|j7`m5?j`@^G6SO!-;(G1aCOu{sGli$ zGck~Eqx{gt?U$`9P3JRJO>}Ku- zRew#6AKb$jGi@lro`A;#P%m@cnyJP&(r%f}Zq^V3mIc=hnLfVXHRn=pj;2#4v8WiM zav|%So5|ksmkM-bV7H;F_%f>b72G`eFJ!d@wTC-#ALO^GRCtHl=K|Y}dX%fTm0A$N zzc4;N3AGsYM~OOND&T_#%8wZzWYfL@by`|620=mAU7(LZ-At!cdlWCvu@AK#bt~#< z9;~k^d5}}7VXmK@h?&@_rUHYJ9zD=M%4AFQ`}k8OC$9ssPq=LnkwKG P00000NkvXXu0mjfO)eU< diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png index a6d6b8609df07bf62e5100a53a01510388bd2b22..defbb2e964ba9a1d8f7c6dfd7862715693da59d9 100644 GIT binary patch literal 4330 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 TF3bAg4+CJVZ=qMOizfUJN(KeK delta 2657 zcmV-n3ZC`qA?Xy5BYz4oNkloFh3q0M zFesq&64WThn3$;G69TfjsAv=f2G9}pgSx99+!YO}2Uveik)GA5ewEg1Va|1@=x;Pyj3t}zC)T-c zzoz+u9TyK|)jteh(` z;D6}gLagCR0hn*76kN#wCv*`b8GyswI1s7kNaT!{fEXv;5ofK|nu3vU_1cRKLBd@TQ@Ei6F6QJ?HM!O!+{x(L<6rNAv)=55QOzlPWhqI{ zjSR5c$#aW1xy}c;$06La(Y*xlX>0w3EDGGn02k}7*%ZplRLFsw%E3MJO@BuPkU#Ja zcbp(_BL!RxGHHe;wff^+2G>u>ah(dq7O)#K0ekx={yBF*$P%aJSOMmjHHmAxvW0cp zt8@K;ETVH`1(=8Ie(Z;Hy+9V#xv>Mxz*+%0w;aeKJ2#eqVJL7nrpksp7|5bKS2)0a zId?FSYb0>4P=MLsl6(J^Q-5;@54jg=+fq2sO~Hz-hVXEO0L<$0j+BKdGX;Npqt{os zJmk~Y_Q1L83|RfW)5G^!;(u>i7{EI*HIqAp9`Cbzna*hHhI2Lm9I{{?%y{Hgdzxli z7Fd?00Ed;L$qk&zI`y$@{Dn;5j(iW`=!aXNV*G=kjb8%Qi`UtJMSq7ZT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah-w13P)xT`>~eFoJC!>{2X zL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw>F+gN+i+VD?gMXwcIhn8 zrz`>e>J^TI3E-MW>VM=sWZual)ns9B%8_$25BcpOz+KP;+J(1(UiAp*Q=dc!FQL}*R43bTaVm7wy#IrC-VD&k-4FWYMd;wg6!1z8a3SP;O>P#-$->_l zEAM2>hI~v3$Y(TlIdJE^0@|6AK_7#uc{~E1^f2gGJ^}i)rGJ>FDPRt4F~}m-WGQZK zw?8N6;NJH#XeW;Webf!0kG=!+v2#G5xX^L14Y#o*Tz9&z7NjOib92f(6aq==Ntoo74a!u6w~r!$mq|FDi7h6+6fTa4$$0Y>@Y*O}J#< z8ic&ucAQ^e_cw)#3uFTLcly|p7IMmPAXha6205mSA%EQja0eR?DV=N%;eS$2zHI*MpiObUuA|j7`m5?j`@^G6SO!-;(G1aCOu{sGli$ zGck~Eqx{gt?U$`9P3JRJO>}Ku- zRew#6AKb$jGi@lro`A;#P%m@cnyJP&(r%f}Zq^V3mIc=hnLfVXHRn=pj;2#4v8WiM zav|%So5|ksmkM-bV7H;F_%f>b72G`eFJ!d@wTC-#ALO^GRCtHl=K|Y}dX%fTm0A$N zzc4;N3AGsYM~OOND&T_#%8wZzWYfL@by`|620=mAU7(LZ-At!cdlWCvu@AK#bt~#< z9;~k^d5}}7VXmK@h?&@_rUHYJ9zD=M%4AFQ`}k8OC$9ssPq=LnkwKG P00000NkvXXu0mjfO)eU< diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png index 75b2d164a5a98e212cca15ea7bf2ab5de5108680..0d277b087942a0ce61d24176f940909ae80bc8ab 100644 GIT binary patch literal 6651 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 diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png index c4df70d39da7941ef3f6dcb7f06a192d8dcb308d..28d753ffa5889b5734dc426e836b962922efd79c 100644 GIT binary patch delta 2834 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 delta 1874 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 literal 3612 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 iW8=2.10.0-110 <2.11.0" flutter: ">=1.20.0 <2.0.0" diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index 598abcf5a..43df138e0 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -41,9 +41,15 @@ dependencies: firebase_analytics: ^6.2.0 dev_dependencies: + flutter_launcher_icons: "^0.8.0" flutter_test: sdk: flutter +flutter_icons: + android: "launcher_icon" + ios: true + image_path: "../128x128@2x.png" + # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec From a8a83f758f29dcf309844eafb582e4df434a4bc7 Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 30 Nov 2020 11:35:43 +0800 Subject: [PATCH 121/422] new version number and add about me --- flutter_hbb/lib/home_page.dart | 34 ++++++++++++++++++++++- flutter_hbb/pubspec.lock | 51 +++++++++++++++++++++++++++++++++- flutter_hbb/pubspec.yaml | 4 ++- 3 files changed, 86 insertions(+), 3 deletions(-) diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index bba97dc5c..1f1a6673d 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:tuple/tuple.dart'; +import 'package:package_info/package_info.dart'; +import 'package:url_launcher/url_launcher.dart'; import 'common.dart'; import 'model.dart'; import 'remote_page.dart'; @@ -37,11 +39,15 @@ class _HomePageState extends State { items: [ PopupMenuItem( child: Text('ID/Relay Server'), value: 'server'), + PopupMenuItem( + child: Text('About RustDesk'), value: 'about'), ], elevation: 8, ); if (value == 'server') { showServer(context); + } else if (value == 'about') { + showAbout(context); } }(); }) @@ -269,7 +275,33 @@ void showServer(BuildContext context) { child: Text('OK'), ), ], - ), + )); +} + +Future showAbout(BuildContext context) async { + PackageInfo packageInfo = await PackageInfo.fromPlatform(); + showAlertDialog( + context, + (setState) => Tuple3( + null, + Wrap(direction: Axis.vertical, spacing: 12, children: [ + Text('Version: ${packageInfo.version}'), + InkWell( + onTap: () async { + const url = 'https://forum.rustdesk.com/'; + if (await canLaunch(url)) { + await launch(url); + } + }, + child: Padding( + padding: EdgeInsets.symmetric(vertical: 8), + child: Text('Support', + style: TextStyle( + decoration: TextDecoration.underline, + )), + )), + ]), + null), () async => true, true); } diff --git a/flutter_hbb/pubspec.lock b/flutter_hbb/pubspec.lock index 6b67cf28e..ad0879e75 100644 --- a/flutter_hbb/pubspec.lock +++ b/flutter_hbb/pubspec.lock @@ -277,6 +277,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.0.4" + package_info: + dependency: "direct main" + description: + name: package_info + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.3+2" path: dependency: transitive description: @@ -429,6 +436,48 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.0-nullsafety.3" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + url: "https://pub.dartlang.org" + source: hosted + version: "5.7.10" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.1+4" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.1+9" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.9" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5+1" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.1+3" vector_math: dependency: transitive description: @@ -487,4 +536,4 @@ packages: version: "2.2.1" sdks: dart: ">=2.10.0-110 <2.11.0" - flutter: ">=1.20.0 <2.0.0" + flutter: ">=1.22.0 <2.0.0" diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index 43df138e0..25cfb59ec 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.0.0+1 +version: 1.1.0 environment: sdk: ">=2.7.0 <3.0.0" @@ -39,6 +39,8 @@ dependencies: wakelock: ^0.2.1+1 device_info: ^1.0.0 firebase_analytics: ^6.2.0 + package_info: ^0.4.3+2 + url_launcher: ^5.7.10 dev_dependencies: flutter_launcher_icons: "^0.8.0" From e62f411a08aa0d7a3826596c1acf5eb4acfa99e9 Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 30 Nov 2020 15:04:59 +0800 Subject: [PATCH 122/422] download --- flutter_hbb/lib/home_page.dart | 32 ++++++++++++++++++++++++++++++-- flutter_hbb/pubspec.lock | 2 +- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index 1f1a6673d..b15d101ca 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -3,6 +3,7 @@ import 'package:provider/provider.dart'; import 'package:tuple/tuple.dart'; import 'package:package_info/package_info.dart'; import 'package:url_launcher/url_launcher.dart'; +import 'dart:async'; import 'common.dart'; import 'model.dart'; import 'remote_page.dart'; @@ -18,6 +19,16 @@ class HomePage extends StatefulWidget { class _HomePageState extends State { final _idController = TextEditingController(); + var _updateUrl = ''; + + @override + void initState() { + super.initState(); + Timer(Duration(seconds: 5), () { + _updateUrl = FFI.getByName('software_update_url'); + if (_updateUrl.isNotEmpty) setState(() {}); + }); + } @override Widget build(BuildContext context) { @@ -60,10 +71,27 @@ class _HomePageState extends State { mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.center, children: [ + _updateUrl.isEmpty + ? SizedBox(height: 0) + : InkWell( + onTap: () async { + final url = _updateUrl + '.apk'; + if (await canLaunch(url)) { + await launch(url); + } + }, + child: Container( + alignment: AlignmentDirectional.center, + width: double.infinity, + color: Colors.pinkAccent, + padding: EdgeInsets.symmetric(vertical: 12), + child: Text('Download new version', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold)))), getSearchBarUI(), getPeers(), ]), - padding: const EdgeInsets.fromLTRB(16.0, 0.0, 16.0, 0.0), )); } @@ -94,7 +122,7 @@ class _HomePageState extends State { return Container(); } return Padding( - padding: const EdgeInsets.only(top: 8.0), + padding: const EdgeInsets.fromLTRB(16.0, 8.0, 16.0, 0.0), child: Container( height: 84, child: Padding( diff --git a/flutter_hbb/pubspec.lock b/flutter_hbb/pubspec.lock index ad0879e75..dd24d84ca 100644 --- a/flutter_hbb/pubspec.lock +++ b/flutter_hbb/pubspec.lock @@ -179,7 +179,7 @@ packages: description: path: "." ref: HEAD - resolved-ref: "2d085c411dc262043b6173c3b962d3e66ce657b1" + resolved-ref: "663154242c05ccf85d4dde984852095664493e46" url: "git://github.com/open-trade/flutter_easyloading" source: git version: "2.2.0" From b5607e54601ac8b1be3e8123ef042390ed9e0ddb Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 30 Nov 2020 16:05:23 +0800 Subject: [PATCH 123/422] check keyboard for lock-after-session-end --- flutter_hbb/lib/remote_page.dart | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 55d801831..dc193e12e 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -672,6 +672,17 @@ void showOptions(BuildContext context) { }, title: Text('Mute'))); } + if (FFI.ffiModel.permissions['keyboard'] != false) { + CheckboxListTile( + value: FFI.getByName('toggle_option', 'lock-after-session-end') == + 'true', + onChanged: (v) { + setState(() { + FFI.setByName('toggle_option', 'lock-after-session-end'); + }); + }, + title: Text('Lock after session end')); + } return Tuple3( null, Column( @@ -725,17 +736,6 @@ void showOptions(BuildContext context) { }); }, title: Text('Show remote cursor')), - CheckboxListTile( - value: FFI.getByName( - 'toggle_option', 'lock-after-session-end') == - 'true', - onChanged: (v) { - setState(() { - FFI.setByName( - 'toggle_option', 'lock-after-session-end'); - }); - }, - title: Text('Lock after session end')) ] + more), null); From 6856453d828bfa96d9e2d118f5067023cf067f7d Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 30 Nov 2020 16:10:03 +0800 Subject: [PATCH 124/422] fix --- flutter_hbb/lib/remote_page.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index dc193e12e..d4fbf7b7f 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -673,7 +673,7 @@ void showOptions(BuildContext context) { title: Text('Mute'))); } if (FFI.ffiModel.permissions['keyboard'] != false) { - CheckboxListTile( + more.add(CheckboxListTile( value: FFI.getByName('toggle_option', 'lock-after-session-end') == 'true', onChanged: (v) { @@ -681,7 +681,7 @@ void showOptions(BuildContext context) { FFI.setByName('toggle_option', 'lock-after-session-end'); }); }, - title: Text('Lock after session end')); + title: Text('Lock after session end'))); } return Tuple3( null, From e367a33fc86fa59671109d001c7c70c93573a0cd Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 30 Nov 2020 17:37:38 +0800 Subject: [PATCH 125/422] sign --- flutter_hbb/android/app/build.gradle | 17 ++++++++++++++++- flutter_hbb/android/key.jks | Bin 0 -> 2240 bytes 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 flutter_hbb/android/key.jks diff --git a/flutter_hbb/android/app/build.gradle b/flutter_hbb/android/app/build.gradle index 2f2db6973..c83feda57 100644 --- a/flutter_hbb/android/app/build.gradle +++ b/flutter_hbb/android/app/build.gradle @@ -1,3 +1,9 @@ +def keystoreProperties = new Properties() +def keystorePropertiesFile = rootProject.file('key.properties') +if (keystorePropertiesFile.exists()) { + keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -45,11 +51,20 @@ android { versionName flutterVersionName } + signingConfigs { + release { + keyAlias keystoreProperties['keyAlias'] + keyPassword keystoreProperties['keyPassword'] + storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null + storePassword keystoreProperties['storePassword'] + } + } + buildTypes { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug + signingConfig signingConfigs.release } } } diff --git a/flutter_hbb/android/key.jks b/flutter_hbb/android/key.jks new file mode 100644 index 0000000000000000000000000000000000000000..8e1763f466454ebf7ceb600a44c0af498a44ab47 GIT binary patch literal 2240 zcmcgt_fyk}63;gU2qgqVlY^s(0-=ST&=sX9O#~EpLP84=DG5b-iBu5~6gc6qQH?Ya zP7o-D{vZ$xf}ui>F$flRSrz~apus!<00Tj(&_z_VxPHIN)3||_vs|II-s$@M&}feN z?6HPmyc0S*NkB)*sh~rtW?;)JFrEAIyQxTf6)E{8x44O}!6G5EH>?49 z`$Dt4L8-!y&DPLWiew`gOFpOU7FD7sJ=JQOmZy1zJ7XcAgec)~kT2ybKaF|;6Ffgu zqr=#;gO_NMIcD*BxfBK1xfjzJDxmL8w+*+!ebx2tqNMx%^*)+|w7cXmf%8zhcBMmb zf@@288+L1G2tS>*)pdyze#9}DA6C+4wW(c4^0<)A3xvNh8(~{7k@_VYAJVA3UTq~*Gmwpcx>ramFFXTe(PDxl5D8Q$r*bk`dV|1|cO zsc|C1IwmJE#`Tc*NI-W-xN)VTV|_O5b6j$KQEf(hmk7VBjaT6hb+w=iY-K9Je9vpw zhW$_zj~Wxxup(AEfvW^9VsvTb`mWRWb*7Ga+g9@@(t}7wVd=-%Xp1&lmfRlezO9ixpf&erIZ4*Dt?!w` zAA1%RG)u{D3X4#iaLTH2@2yag56Cp~sClJinPA^n?3Uy=p1leN*G{_^Yffj3QB1)H zrYKD-{svv&UFfZ(eHstto?wdaz9Z6+Q({`L6cTHZ%^(+LvlAS#!av4jva~(EN}IOT zHTeZ6rI>0y;PR77ui~y>K03XlT<)Moe7m3eL2lvH(32eaK!SJ#i{7v#ObNO`A3h`K?D zmIU@?$lV%4nBKg*yVG@kZi{<8=v`G>^5+PC$u~Ll_c3DMZ+;>NNdK<3*A96uea06z zcVBUyWWolPsZ|&GZK;+Iq&U;<(0bmj&(d&`Q1Ov`OK+3AQQfKXv8gRO*VAT&h4Tu) zEcOqHO$E73%k8qiqOB??dqz6vqET|`ax;*o1v?D|eS6Tk%>Ix=`wGmX$a{i~XL0V< zArf8IPK!Mn6M{Zuj_c+>|W~pQ2)l8rfJ_x;pZx&)8Zz+3w?=)^M2A-9J=^vmfk5AZ z1o8ozHmov@wl{}+wF~R2jYm-}bX`iUMRSDZ<=aNpOXIwX-Xi0#^o;xPYx)g+cdUlT zyORy}S`EJg!Ga2WR*}mco%zg&nOkk%y03d+#}BwmVc}=V7fz>{>5FcDSNFq(GP0hE zQnv6WG6G8G;W%oc*qruel(d9j=gQ;nDb5wtwgt~8U4h{WJr)1xBJ75O8U_afK|(N8 zNFas^zWEda2Ebsbf1#y3hKmJ`jyI0q<^li?7zjgB;=sUJAU04I6xg4rbP9v~J77b> z{XKn%1V3Mj+rJp=&lvJVXiw_cQLHWm3dPec^xrldD6omO5+4TjcN&7?4E6906!Qoq zIG?n`{3^huj8Rfj!C-L8su&e}0WMW6Mpa4Uf8qak7%Cv~GZ`n(0zw72K^Q8)4yFPC zXrw`laXp*erDsept4wcoEPsd*Y0kjcp_9iZh6v25hDS{`^4*bIh#1L_>QC{xh*Uk~ zbwE^$xUs)^hl?vz@D~P)8 zYuWcY^F?A&P(dE&k8k)SK{`U1U-2UIlFJpryQG4VE#`mdt`61RB?%k>nXh)HUUbY7 z56g;Pj%Hv3%Lmb$M0hO@z8x-nd8hjzPjTj0j}R<(S?YbDQ`l77OLw<3%!pV{3Bn%?q^J*-9r&;x}#a^1)9A2F0h;xoU4S^vJAS)200&BiHh=m#MA*p01%Kb zjuFL(oa|Br0p*ACzKI&y6Jsu>Lr+Zt;T Jf!IyC{{rTQ%KiWV literal 0 HcmV?d00001 From 0388e512999690bb701427583d0fb43ab84fa76a Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 30 Nov 2020 21:14:23 +0800 Subject: [PATCH 126/422] padding --- flutter_hbb/lib/home_page.dart | 60 ++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index b15d101ca..a9e1da622 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -212,35 +212,37 @@ class _HomePageState extends State { final cards = []; var peers = FFI.peers(); peers.forEach((p) { - cards.add(Card( - child: GestureDetector( - onTap: () => connect('${p.id}'), - onLongPressStart: (details) { - var x = details.globalPosition.dx; - var y = details.globalPosition.dy; - () async { - var value = await showMenu( - context: context, - position: RelativeRect.fromLTRB(x, y, x, y), - items: [ - PopupMenuItem( - child: Text('Remove'), value: 'remove'), - ], - elevation: 8, - ); - if (value == 'remove') { - setState(() => FFI.setByName('remove', '${p.id}')); - } - }(); - }, - child: ListTile( - subtitle: Text('${p.username}@${p.hostname}'), - title: Text('${p.id}'), - leading: Container( - padding: const EdgeInsets.all(6), - child: getPlatformImage('${p.platform}'), - color: str2color('${p.id}${p.platform}', 0x77)), - )))); + cards.add(Padding( + padding: EdgeInsets.symmetric(horizontal: 12), + child: Card( + child: GestureDetector( + onTap: () => connect('${p.id}'), + onLongPressStart: (details) { + var x = details.globalPosition.dx; + var y = details.globalPosition.dy; + () async { + var value = await showMenu( + context: context, + position: RelativeRect.fromLTRB(x, y, x, y), + items: [ + PopupMenuItem( + child: Text('Remove'), value: 'remove'), + ], + elevation: 8, + ); + if (value == 'remove') { + setState(() => FFI.setByName('remove', '${p.id}')); + } + }(); + }, + child: ListTile( + subtitle: Text('${p.username}@${p.hostname}'), + title: Text('${p.id}'), + leading: Container( + padding: const EdgeInsets.all(6), + child: getPlatformImage('${p.platform}'), + color: str2color('${p.id}${p.platform}', 0x77)), + ))))); }); return Wrap(children: cards); } From 1acb64c35d2720c532d1b9a470c39c7c20fd10a7 Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 21 Dec 2020 17:26:23 +0800 Subject: [PATCH 127/422] new keyboard offset and scroll --- flutter_hbb/lib/model.dart | 6 ++++-- flutter_hbb/lib/remote_page.dart | 17 +++++++++++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 3c38865e1..9133283ce 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -319,10 +319,12 @@ class CursorModel with ChangeNotifier { } double adjustForKeyboard() { - var keyboardHeight = MediaQueryData.fromWindow(ui.window).viewInsets.bottom; + final m = MediaQueryData.fromWindow(ui.window); + var keyboardHeight = m.viewInsets.bottom; + final size = m.size; if (keyboardHeight < 100) return 0; final s = FFI.canvasModel.scale; - final thresh = 120; + final thresh = (size.height - keyboardHeight) / 2; var h = (_y - getVisibleRect().top) * s; // local physical display height return h - thresh; } diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index d4fbf7b7f..6cb968e0a 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -28,6 +28,8 @@ class _RemotePageState extends State { String _value = ''; double _xOffset = 0; double _yOffset = 0; + double _xOffset0 = 0; + double _yOffset0 = 0; double _scale = 1; bool _mouseTools = false; var _drag = false; @@ -258,8 +260,8 @@ class _RemotePageState extends State { }, onScaleStart: (details) { _scale = 1; - _xOffset = details.focalPoint.dx; - _yOffset = details.focalPoint.dy; + _xOffset = _xOffset0 = details.focalPoint.dx; + _yOffset = _yOffset0 = details.focalPoint.dy; if (_drag) { FFI.sendMouse('down', 'left'); } @@ -275,6 +277,9 @@ class _RemotePageState extends State { FFI.cursorModel.updatePan(dx, dy); _xOffset = x; _yOffset = y; + } else { + _xOffset = details.focalPoint.dx; + _yOffset = details.focalPoint.dy; } } else if (!_drag && !_scroll) { FFI.canvasModel.updateScale(scale / _scale); @@ -285,8 +290,12 @@ class _RemotePageState extends State { if (_drag) { FFI.sendMouse('up', 'left'); } else if (_scroll) { - FFI.scroll( - details.velocity.pixelsPerSecond.dy > 0 ? -1 : 1); + var dy = (_yOffset - _yOffset0) / 10; + if (dy.abs() > 0.1) { + if (dy > 0 && dy < 1) dy = 1; + if (dy < 0 && dy > -1) dy = -1; + FFI.scroll(dy); + } } }, child: Container( From 5e17a995e6343e3094d5f79f082ade86c4e0b1a3 Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 21 Dec 2020 18:28:28 +0800 Subject: [PATCH 128/422] remember cursor, canvas offset and scale --- flutter_hbb/lib/model.dart | 65 ++++++++++++++++++++++++++- flutter_hbb/pubspec.lock | 92 +++++++++++++++++++++++++++----------- flutter_hbb/pubspec.yaml | 1 + 3 files changed, 132 insertions(+), 26 deletions(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 9133283ce..e76084b2f 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -2,6 +2,7 @@ import 'package:ffi/ffi.dart'; import 'package:flutter/services.dart'; import 'package:flutter/gestures.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:device_info/device_info.dart'; import 'dart:io'; import 'dart:math'; @@ -38,6 +39,7 @@ class FfiModel with ChangeNotifier { get permissions => _permissions; get initialized => _initialized; + get display => _display; get secure => _secure; get direct => _direct; get pi => _pi; @@ -187,7 +189,7 @@ class FfiModel with ChangeNotifier { } if (_pi.currentDisplay < _pi.displays.length) { _display = _pi.displays[_pi.currentDisplay]; - FFI.cursorModel.updateDisplayOrigin(_display.x, _display.y); + initializeCursorAndCanvas(); } if (displays.length > 0) { showLoading('Waiting for image...', context); @@ -243,6 +245,13 @@ class CanvasModel with ChangeNotifier { double get y => _y; double get scale => _scale; + void update(double x, double y, double scale) { + _x = x; + _y = y; + _scale = scale; + notifyListeners(); + } + set scale(v) { _scale = v; notifyListeners(); @@ -443,6 +452,16 @@ class CursorModel with ChangeNotifier { notifyListeners(); } + void updateDisplayOriginWithCursor( + double x, double y, double xCursor, double yCursor) { + _displayOriginX = x; + _displayOriginY = y; + _x = xCursor; + _y = yCursor; + FFI.moveMouse(x, y); + notifyListeners(); + } + void clear() { _x = -10000; _x = -10000; @@ -573,6 +592,8 @@ class FFI { } static void close() { + savePreference(id, cursorModel.x, cursorModel.y, canvasModel.x, + canvasModel.y, canvasModel.scale, ffiModel.pi.currentDisplay); id = ""; setByName('close', ''); imageModel.update(null); @@ -647,3 +668,45 @@ class PeerInfo { int currentDisplay; List displays; } + +void savePreference(String id, double xCursor, double yCursor, double xCanvas, + double yCanvas, double scale, int currentDisplay) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + final p = Map(); + p['xCursor'] = xCursor; + p['yCursor'] = yCursor; + p['xCanvas'] = xCanvas; + p['yCanvas'] = yCanvas; + p['scale'] = scale; + p['currentDisplay'] = currentDisplay; + prefs.setString('peer' + id, json.encode(p)); +} + +Future> getPreference(String id) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + var p = prefs.getString('peer' + id); + if (p == null) return null; + Map m = json.decode(p); + return m; +} + +void initializeCursorAndCanvas() async { + var p = await getPreference(FFI.id); + int currentDisplay = 0; + if (p != null) { + currentDisplay = p['currentDisplay']; + } + if (p == null || currentDisplay != FFI.ffiModel.pi.currentDisplay) { + FFI.cursorModel + .updateDisplayOrigin(FFI.ffiModel.display.x, FFI.ffiModel.display.y); + return; + } + double xCursor = p['xCursor']; + double yCursor = p['yCursor']; + double xCanvas = p['xCanvas']; + double yCanvas = p['yCanvas']; + double scale = p['scale']; + FFI.cursorModel.updateDisplayOriginWithCursor( + FFI.ffiModel.display.x, FFI.ffiModel.display.y, xCursor, yCursor); + FFI.canvasModel.update(xCanvas, yCanvas, scale); +} diff --git a/flutter_hbb/pubspec.lock b/flutter_hbb/pubspec.lock index dd24d84ca..f343c67e1 100644 --- a/flutter_hbb/pubspec.lock +++ b/flutter_hbb/pubspec.lock @@ -21,42 +21,42 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0-nullsafety.1" + version: "2.5.0-nullsafety.3" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0-nullsafety.3" characters: dependency: transitive description: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.3" + version: "1.1.0-nullsafety.5" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0-nullsafety.3" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0-nullsafety.3" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0-nullsafety.3" + version: "1.15.0-nullsafety.5" convert: dependency: transitive description: @@ -84,7 +84,7 @@ packages: name: cupertino_icons url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.0.2" device_info: dependency: "direct main" description: @@ -105,7 +105,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0-nullsafety.3" ffi: dependency: "direct main" description: @@ -126,14 +126,14 @@ packages: name: firebase url: "https://pub.dartlang.org" source: hosted - version: "7.3.2" + version: "7.3.3" firebase_analytics: dependency: "direct main" description: name: firebase_analytics url: "https://pub.dartlang.org" source: hosted - version: "6.2.0" + version: "6.3.0" firebase_analytics_platform_interface: dependency: transitive description: @@ -154,7 +154,7 @@ packages: name: firebase_core url: "https://pub.dartlang.org" source: hosted - version: "0.5.2+1" + version: "0.5.3" firebase_core_platform_interface: dependency: transitive description: @@ -255,21 +255,21 @@ packages: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.2" + version: "0.6.3-nullsafety.3" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10-nullsafety.1" + version: "0.12.10-nullsafety.3" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0-nullsafety.6" nested: dependency: transitive description: @@ -290,7 +290,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.1" + version: "1.8.0-nullsafety.3" path_provider: dependency: "direct main" description: @@ -367,7 +367,7 @@ packages: name: provider url: "https://pub.dartlang.org" source: hosted - version: "4.3.2+2" + version: "4.3.2+3" quiver: dependency: transitive description: @@ -375,6 +375,48 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.5" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.12+4" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.2+4" + shared_preferences_macos: + dependency: transitive + description: + name: shared_preferences_macos + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.1+11" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.2+7" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.1+3" sky_engine: dependency: transitive description: flutter @@ -386,42 +428,42 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.2" + version: "1.8.0-nullsafety.4" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.10.0-nullsafety.1" + version: "1.10.0-nullsafety.6" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0-nullsafety.3" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0-nullsafety.3" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0-nullsafety.3" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19-nullsafety.2" + version: "0.2.19-nullsafety.6" tuple: dependency: "direct main" description: @@ -435,7 +477,7 @@ packages: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0-nullsafety.5" url_launcher: dependency: "direct main" description: @@ -484,7 +526,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.3" + version: "2.1.0-nullsafety.5" wakelock: dependency: "direct main" description: @@ -535,5 +577,5 @@ packages: source: hosted version: "2.2.1" sdks: - dart: ">=2.10.0-110 <2.11.0" + dart: ">=2.12.0-0.0 <3.0.0" flutter: ">=1.22.0 <2.0.0" diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index 25cfb59ec..77824d3b1 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -41,6 +41,7 @@ dependencies: firebase_analytics: ^6.2.0 package_info: ^0.4.3+2 url_launcher: ^5.7.10 + shared_preferences: ^0.5.12+4 dev_dependencies: flutter_launcher_icons: "^0.8.0" From 0ed605717fd7faf28d1392ead097e9815cfe038c Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 21 Dec 2020 19:05:31 +0800 Subject: [PATCH 129/422] OS password --- flutter_hbb/lib/model.dart | 12 ++++++++ flutter_hbb/lib/remote_page.dart | 51 +++++++++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index e76084b2f..2ce9398e7 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -690,6 +690,18 @@ Future> getPreference(String id) async { return m; } +Future getPassword(String id) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + var p = prefs.getString('peer' + id + '-password'); + if (p == null) return ""; + return p; +} + +void savePassword(String id, String password) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + prefs.setString('peer' + id + '-password', password); +} + void initializeCursorAndCanvas() async { var p = await getPreference(FFI.id); int currentDisplay = 0; diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 6cb968e0a..d7cfd258b 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -751,7 +751,7 @@ void showOptions(BuildContext context) { }, () async => true, true, 0); } -void showActions(BuildContext context) { +void showActions(BuildContext context) async { final size = MediaQuery.of(context).size; final x = 120.0; final y = size.height; @@ -763,6 +763,21 @@ void showActions(BuildContext context) { FFI.ffiModel.permissions['clipboard'] != false) { more.add(PopupMenuItem(child: Text('Paste'), value: 'paste')); } + var password = await getPassword(FFI.id); + more.add(PopupMenuItem( + child: Row( + children: ([ + Text('OS Password'), + FlatButton( + textColor: MyTheme.accent, + onPressed: () { + showSetOSPassword(context); + Navigator.pop(context); + }, + child: Icon(Icons.edit), + ) + ])), + value: 'enter_os_password')); () async { var value = await showMenu( context: context, @@ -788,6 +803,40 @@ void showActions(BuildContext context) { FFI.setByName('input_string', '${data.text}'); } }(); + } else if (value == 'enter_os_password') { + if (password != "") FFI.setByName('input_string', password); } }(); } + +void showSetOSPassword(BuildContext context) async { + final controller = TextEditingController(); + var password = await getPassword(FFI.id); + controller.text = password; + showAlertDialog( + context, + (setState) => Tuple3( + Text('Password required'), + Column(mainAxisSize: MainAxisSize.min, children: [ + PasswordWidget(controller: controller), + ]), + [ + FlatButton( + textColor: MyTheme.accent, + onPressed: () { + Navigator.pop(context); + }, + child: Text('Cancel'), + ), + FlatButton( + textColor: MyTheme.accent, + onPressed: () { + var text = controller.text.trim(); + savePassword(FFI.id, text); + Navigator.pop(context); + }, + child: Text('OK'), + ), + ], + )); +} From 8455c69733326940bb5e81fbf717ce924c701e33 Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 21 Dec 2020 19:08:54 +0800 Subject: [PATCH 130/422] append return after password paste --- flutter_hbb/lib/remote_page.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index d7cfd258b..73ed7eed8 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -804,7 +804,10 @@ void showActions(BuildContext context) async { } }(); } else if (value == 'enter_os_password') { - if (password != "") FFI.setByName('input_string', password); + if (password != "") { + FFI.setByName('input_string', password); + FFI.inputKey('VK_RETURN'); + } } }(); } From a238b6a10a2c635a060543980d4fc70e3ef238c2 Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 21 Dec 2020 21:52:20 +0800 Subject: [PATCH 131/422] improve enter os password --- flutter_hbb/lib/model.dart | 4 +-- flutter_hbb/lib/remote_page.dart | 22 ++++++++++++----- flutter_hbb/pubspec.lock | 42 ++++++++++++++++---------------- flutter_hbb/pubspec.yaml | 2 +- 4 files changed, 40 insertions(+), 30 deletions(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 2ce9398e7..2760654bd 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -445,8 +445,8 @@ class CursorModel with ChangeNotifier { void updateDisplayOrigin(double x, double y) { _displayOriginX = x; _displayOriginY = y; - _x = x; - _y = y; + _x = x + 1; + _y = y + 1; FFI.moveMouse(x, y); FFI.canvasModel.resetOffset(); notifyListeners(); diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 73ed7eed8..ff7b97ed7 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -751,7 +751,7 @@ void showOptions(BuildContext context) { }, () async => true, true, 0); } -void showActions(BuildContext context) async { +void showActions(BuildContext context) { final size = MediaQuery.of(context).size; final x = 120.0; final y = size.height; @@ -763,7 +763,6 @@ void showActions(BuildContext context) async { FFI.ffiModel.permissions['clipboard'] != false) { more.add(PopupMenuItem(child: Text('Paste'), value: 'paste')); } - var password = await getPassword(FFI.id); more.add(PopupMenuItem( child: Row( children: ([ @@ -804,10 +803,21 @@ void showActions(BuildContext context) async { } }(); } else if (value == 'enter_os_password') { - if (password != "") { - FFI.setByName('input_string', password); - FFI.inputKey('VK_RETURN'); - } + () async { + var password = await getPassword(FFI.id); + if (password != "") { + var x = FFI.cursorModel.x; + var y = FFI.cursorModel.y; + FFI.moveMouse(x + 3, y + 3); + await Future.delayed(Duration(milliseconds: 50)); + FFI.moveMouse(x, y); + await Future.delayed(Duration(milliseconds: 50)); + FFI.tap(true); + await Future.delayed(Duration(milliseconds: 300)); + FFI.setByName('input_string', password); + FFI.inputKey('VK_RETURN'); + } + }(); } }(); } diff --git a/flutter_hbb/pubspec.lock b/flutter_hbb/pubspec.lock index f343c67e1..caa914825 100644 --- a/flutter_hbb/pubspec.lock +++ b/flutter_hbb/pubspec.lock @@ -21,42 +21,42 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0-nullsafety.3" + version: "2.5.0-nullsafety.1" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.3" + version: "2.1.0-nullsafety.1" characters: dependency: transitive description: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.5" + version: "1.1.0-nullsafety.3" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.3" + version: "1.2.0-nullsafety.1" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.3" + version: "1.1.0-nullsafety.1" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0-nullsafety.5" + version: "1.15.0-nullsafety.3" convert: dependency: transitive description: @@ -84,7 +84,7 @@ packages: name: cupertino_icons url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" + version: "1.0.0" device_info: dependency: "direct main" description: @@ -105,7 +105,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.3" + version: "1.2.0-nullsafety.1" ffi: dependency: "direct main" description: @@ -255,21 +255,21 @@ packages: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.3-nullsafety.3" + version: "0.6.2" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10-nullsafety.3" + version: "0.12.10-nullsafety.1" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.6" + version: "1.3.0-nullsafety.3" nested: dependency: transitive description: @@ -290,7 +290,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.3" + version: "1.8.0-nullsafety.1" path_provider: dependency: "direct main" description: @@ -428,42 +428,42 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.4" + version: "1.8.0-nullsafety.2" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.10.0-nullsafety.6" + version: "1.10.0-nullsafety.1" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.3" + version: "2.1.0-nullsafety.1" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.3" + version: "1.1.0-nullsafety.1" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.3" + version: "1.2.0-nullsafety.1" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19-nullsafety.6" + version: "0.2.19-nullsafety.2" tuple: dependency: "direct main" description: @@ -477,7 +477,7 @@ packages: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.5" + version: "1.3.0-nullsafety.3" url_launcher: dependency: "direct main" description: @@ -526,7 +526,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.5" + version: "2.1.0-nullsafety.3" wakelock: dependency: "direct main" description: @@ -577,5 +577,5 @@ packages: source: hosted version: "2.2.1" sdks: - dart: ">=2.12.0-0.0 <3.0.0" + dart: ">=2.10.0-110 <2.11.0" flutter: ">=1.22.0 <2.0.0" diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index 77824d3b1..94fbdd592 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.0 +version: 1.1.1 environment: sdk: ">=2.7.0 <3.0.0" From d8bb3384d90b25adc37eaa8bc71d471729b7a203 Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 21 Dec 2020 22:12:24 +0800 Subject: [PATCH 132/422] build number --- flutter_hbb/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index 94fbdd592..d9731723e 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.1 +version: 1.1.1+2 environment: sdk: ">=2.7.0 <3.0.0" From eac83438e15962dafb7425588085d3e4d2f6fc3a Mon Sep 17 00:00:00 2001 From: open-trade Date: Tue, 22 Dec 2020 14:41:14 +0800 Subject: [PATCH 133/422] fix ( issue --- flutter_hbb/lib/common.dart | 2 +- flutter_hbb/lib/home_page.dart | 3 ++- flutter_hbb/lib/remote_page.dart | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index fef4ed0bd..9bb4c9b7d 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -129,9 +129,9 @@ class _PasswordWidgetState extends State { Widget build(BuildContext context) { return TextField( autofocus: true, - keyboardType: TextInputType.text, controller: widget.controller, obscureText: !_passwordVisible, //This will obscure text dynamically + keyboardType: TextInputType.visiblePassword, decoration: InputDecoration( labelText: 'Password', hintText: 'Enter your password', diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index a9e1da622..36757d840 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -145,13 +145,14 @@ class _HomePageState extends State { child: TextField( autocorrect: false, enableSuggestions: false, + keyboardType: TextInputType.visiblePassword, + // keyboardType: TextInputType.number, style: TextStyle( fontFamily: 'WorkSans', fontWeight: FontWeight.bold, fontSize: 30, color: Color(0xFF00B6F0), ), - // keyboardType: TextInputType.number, decoration: InputDecoration( labelText: 'Remote ID', // hintText: 'Enter your remote ID', diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index ff7b97ed7..1e85d9125 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -317,7 +317,8 @@ class _RemotePageState extends State { maxLines: null, initialValue: _value, // trick way to make backspace work always - keyboardType: TextInputType.multiline, + keyboardType: TextInputType + .visiblePassword, // must use visiblePassword here, if else when input '(', new ')' will be auto appended on android, cause our proboems onChanged: handleInput, ), ), From cb2965e8e41f842a374b087bea9192d0bcf5db96 Mon Sep 17 00:00:00 2001 From: open-trade Date: Tue, 22 Dec 2020 14:54:46 +0800 Subject: [PATCH 134/422] revert back because enter not work any more --- flutter_hbb/lib/remote_page.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 1e85d9125..ff7b97ed7 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -317,8 +317,7 @@ class _RemotePageState extends State { maxLines: null, initialValue: _value, // trick way to make backspace work always - keyboardType: TextInputType - .visiblePassword, // must use visiblePassword here, if else when input '(', new ')' will be auto appended on android, cause our proboems + keyboardType: TextInputType.multiline, onChanged: handleInput, ), ), From 6623e3c7efe1ebaa96f0ff9c5b8b8ea24b095d77 Mon Sep 17 00:00:00 2001 From: open-trade Date: Tue, 22 Dec 2020 15:26:35 +0800 Subject: [PATCH 135/422] hacking way for bracket issue --- flutter_hbb/lib/remote_page.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index ff7b97ed7..72458a36e 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -45,7 +45,6 @@ class _RemotePageState extends State { @override void initState() { super.initState(); - _value = initText; FFI.connect(widget.id); WidgetsBinding.instance.addPostFrameCallback((_) { SystemChrome.setEnabledSystemUIOverlays([]); @@ -77,7 +76,6 @@ class _RemotePageState extends State { var v = MediaQuery.of(context).viewInsets.bottom; if (v != _bottom) { resetTool(); - _value = initText; setState(() { _bottom = v; if (v < 100) { @@ -141,6 +139,11 @@ class _RemotePageState extends State { char = 'VK_RETURN'; } FFI.inputKey(char); + final brackets = '("[<{(“【《{'; + if (brackets.indexOf(char) >= 0) { + openKeyboard(); + return; + } } } _value = newValue; @@ -148,6 +151,7 @@ class _RemotePageState extends State { void openKeyboard() { // destroy first, so that our _value trick can work + _value = initText; setState(() => _showEdit = false); _timer?.cancel(); _timer = Timer(Duration(milliseconds: 30), () { From 2ce172087a03f1b0a3dfa36210d66a9981a67740 Mon Sep 17 00:00:00 2001 From: open-trade Date: Tue, 22 Dec 2020 15:35:45 +0800 Subject: [PATCH 136/422] try again --- flutter_hbb/lib/remote_page.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 72458a36e..cb18c99ef 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -141,8 +141,10 @@ class _RemotePageState extends State { FFI.inputKey(char); final brackets = '("[<{(“【《{'; if (brackets.indexOf(char) >= 0) { - openKeyboard(); - return; + _timer?.cancel(); + _timer = Timer(Duration(milliseconds: 30), () { + openKeyboard(); + }); } } } From 0dc375bda13f9fb5de7949d6aa6e482d82fac37c Mon Sep 17 00:00:00 2001 From: open-trade Date: Tue, 22 Dec 2020 15:45:55 +0800 Subject: [PATCH 137/422] try again --- flutter_hbb/lib/remote_page.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index cb18c99ef..42fbed149 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -141,6 +141,8 @@ class _RemotePageState extends State { FFI.inputKey(char); final brackets = '("[<{(“【《{'; if (brackets.indexOf(char) >= 0) { + SystemChannels.textInput.invokeMethod('TextInput.hide'); + setState(() => _showEdit = false); _timer?.cancel(); _timer = Timer(Duration(milliseconds: 30), () { openKeyboard(); From 5286eed96fd7b3758f282bf7cfe4ffefe330239c Mon Sep 17 00:00:00 2001 From: open-trade Date: Tue, 22 Dec 2020 16:00:10 +0800 Subject: [PATCH 138/422] try again --- flutter_hbb/lib/remote_page.dart | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 42fbed149..7e3856c77 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -132,6 +132,21 @@ class _RemotePageState extends State { } else { final content = newValue.substring(_value.length); if (content.length > 1) { + if (_value != '' && + content.length == 2 && + (content == '""' || + content == '()' || + content == '[]' || + content == '<>' || + content == "{}" || + content == '”“' || + content == '《》' || + content == '()' || + content == '【】')) { + FFI.inputKey(content[0]); + openKeyboard(); + return; + } FFI.setByName('input_string', content); } else { var char = content; @@ -139,15 +154,6 @@ class _RemotePageState extends State { char = 'VK_RETURN'; } FFI.inputKey(char); - final brackets = '("[<{(“【《{'; - if (brackets.indexOf(char) >= 0) { - SystemChannels.textInput.invokeMethod('TextInput.hide'); - setState(() => _showEdit = false); - _timer?.cancel(); - _timer = Timer(Duration(milliseconds: 30), () { - openKeyboard(); - }); - } } } _value = newValue; From dc8f3db2fcd9b344fd1284782a93877ba9a8f484 Mon Sep 17 00:00:00 2001 From: open-trade Date: Tue, 22 Dec 2020 16:07:48 +0800 Subject: [PATCH 139/422] try again --- flutter_hbb/lib/remote_page.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 7e3856c77..8ffd1d0ac 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -143,7 +143,8 @@ class _RemotePageState extends State { content == '《》' || content == '()' || content == '【】')) { - FFI.inputKey(content[0]); + // can not only input content[0], because when input ], [ are also auo insert, which cause ] never be input + FFI.setByName('input_string', content); openKeyboard(); return; } From 5d5097eccd2541bcace0b5672cf73c08760a6b79 Mon Sep 17 00:00:00 2001 From: open-trade Date: Tue, 22 Dec 2020 17:38:08 +0800 Subject: [PATCH 140/422] move port_forward out from ui --- flutter_hbb/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index d9731723e..b86c0c352 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.1+2 +version: 1.1.1+3 environment: sdk: ">=2.7.0 <3.0.0" From 130104a4758ab54760d0a6682e15e5c9f2cadc14 Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 24 Dec 2020 10:44:44 +0800 Subject: [PATCH 141/422] fixed on refresh(reset origin and scaled), and move arrows to more, smaller padding, to-do: scale restore not working --- flutter_hbb/lib/model.dart | 4 +- flutter_hbb/lib/remote_page.dart | 72 +++++++++++++++----------------- 2 files changed, 37 insertions(+), 39 deletions(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 2760654bd..7d4986be7 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -159,12 +159,14 @@ class FfiModel with ChangeNotifier { } void handleSwitchDisplay(Map evt) { + var old = _pi.currentDisplay; _pi.currentDisplay = int.parse(evt['display']); _display.x = double.parse(evt['x']); _display.y = double.parse(evt['y']); _display.width = int.parse(evt['width']); _display.height = int.parse(evt['height']); - FFI.cursorModel.updateDisplayOrigin(_display.x, _display.y); + if (old != _pi.currentDisplay) + FFI.cursorModel.updateDisplayOrigin(_display.x, _display.y); notifyListeners(); } diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 8ffd1d0ac..b6b64a6c8 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -35,8 +35,7 @@ class _RemotePageState extends State { var _drag = false; var _right = false; var _scroll = false; - var _arrows = false; - var _more = false; + var _more = true; var _fn = false; final FocusNode _focusNode = FocusNode(); var _showEdit = true; @@ -354,7 +353,8 @@ class _RemotePageState extends State { (String text, void Function() onPressed, [bool active, IconData icon]) { return ButtonTheme( padding: EdgeInsets.symmetric( - vertical: 6, horizontal: 11), //adds padding inside the button + vertical: icon != null ? 3 : 6, + horizontal: 6), //adds padding inside the button materialTapTargetSize: MaterialTapTargetSize .shrinkWrap, //limits the touch area to the button area minWidth: 0, //wraps child's width @@ -366,7 +366,7 @@ class _RemotePageState extends State { ), color: active == true ? MyTheme.accent80 : null, child: icon != null - ? Icon(icon, color: Colors.white) + ? Icon(icon, size: 17, color: Colors.white) : Text(text, style: TextStyle(color: Colors.white, fontSize: 11)), onPressed: onPressed)); @@ -415,25 +415,12 @@ class _RemotePageState extends State { }, FFI.command), ]; final keys = [ - wrap( - 'Arrows', - () => setState(() { - setState(() { - _arrows = !_arrows; - if (_arrows) { - _fn = false; - _more = false; - } - }); - }), - _arrows), wrap( 'Fn', () => setState( () { _fn = !_fn; if (_fn) { - _arrows = false; _more = false; } }, @@ -445,28 +432,12 @@ class _RemotePageState extends State { () { _more = !_more; if (_more) { - _arrows = false; _fn = false; } }, ), _more), ]; - final arrows = [ - SizedBox(width: 9999), - wrap('', () { - FFI.inputKey('VK_LEFT'); - }, false, Icons.keyboard_arrow_left), - wrap('', () { - FFI.inputKey('VK_UP'); - }, false, Icons.keyboard_arrow_up), - wrap('', () { - FFI.inputKey('VK_DOWN'); - }, false, Icons.keyboard_arrow_down), - wrap('', () { - FFI.inputKey('VK_RIGHT'); - }, false, Icons.keyboard_arrow_right), - ]; final fn = [ SizedBox(width: 9999), ]; @@ -499,6 +470,35 @@ class _RemotePageState extends State { wrap('PgDown', () { FFI.inputKey('VK_NEXT'); }), + SizedBox(width: 9999), + wrap('', () { + FFI.inputKey('VK_LEFT'); + }, false, Icons.keyboard_arrow_left), + wrap('', () { + FFI.inputKey('VK_UP'); + }, false, Icons.keyboard_arrow_up), + wrap('', () { + FFI.inputKey('VK_DOWN'); + }, false, Icons.keyboard_arrow_down), + wrap('', () { + FFI.inputKey('VK_RIGHT'); + }, false, Icons.keyboard_arrow_right), + wrap('Ctrl+C', () { + var old = FFI.ctrl; + FFI.ctrl = true; + FFI.inputKey( + 'VK_C', + ); + FFI.ctrl = old; + }), + wrap('Ctrl+S', () { + var old = FFI.ctrl; + FFI.ctrl = true; + FFI.inputKey( + 'VK_S', + ); + FFI.ctrl = old; + }), ]; return Container( color: Color(0xAA000000), @@ -509,11 +509,7 @@ class _RemotePageState extends State { runSpacing: 4, children: [SizedBox(width: 9999)] + (keyboard - ? modifiers + - keys + - (_arrows ? arrows : []) + - (_fn ? fn : []) + - (_more ? more : []) + ? modifiers + keys + (_fn ? fn : []) + (_more ? more : []) : mouse + modifiers), )); } From 483db1a0160ce8a142327e1ba4efa866d3dd3896 Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 29 Jan 2021 00:20:39 +0800 Subject: [PATCH 142/422] upgrade rustup --- flutter_hbb/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index b86c0c352..bb8f66dd6 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.1+3 +version: 1.1.2+4 environment: sdk: ">=2.7.0 <3.0.0" From 15db8d6772ea43456ac1a2883bf24d99447d5ba8 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 17 Mar 2021 23:30:14 +0800 Subject: [PATCH 143/422] pubspec --- flutter_hbb/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index bb8f66dd6..eb76e8996 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.2+4 +version: 1.1.2+5 environment: sdk: ">=2.7.0 <3.0.0" From 0813190574633a39dd13491dc31329203501d929 Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 31 Mar 2021 19:40:23 +0800 Subject: [PATCH 144/422] check rendivious servers if failed to connect for mobile --- flutter_hbb/pubspec.lock | 40 ++++++++++++++++++++-------------------- flutter_hbb/pubspec.yaml | 2 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/flutter_hbb/pubspec.lock b/flutter_hbb/pubspec.lock index caa914825..53b9dc141 100644 --- a/flutter_hbb/pubspec.lock +++ b/flutter_hbb/pubspec.lock @@ -21,42 +21,42 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0-nullsafety.1" + version: "2.5.0-nullsafety.3" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0-nullsafety.3" characters: dependency: transitive description: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.3" + version: "1.1.0-nullsafety.5" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0-nullsafety.3" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0-nullsafety.3" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0-nullsafety.3" + version: "1.15.0-nullsafety.5" convert: dependency: transitive description: @@ -105,7 +105,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0-nullsafety.3" ffi: dependency: "direct main" description: @@ -255,21 +255,21 @@ packages: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.2" + version: "0.6.3-nullsafety.3" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10-nullsafety.1" + version: "0.12.10-nullsafety.3" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0-nullsafety.6" nested: dependency: transitive description: @@ -290,7 +290,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.1" + version: "1.8.0-nullsafety.3" path_provider: dependency: "direct main" description: @@ -428,42 +428,42 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.2" + version: "1.8.0-nullsafety.4" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.10.0-nullsafety.1" + version: "1.10.0-nullsafety.6" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0-nullsafety.3" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0-nullsafety.3" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0-nullsafety.3" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19-nullsafety.2" + version: "0.2.19-nullsafety.6" tuple: dependency: "direct main" description: @@ -477,7 +477,7 @@ packages: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0-nullsafety.5" url_launcher: dependency: "direct main" description: @@ -526,7 +526,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.3" + version: "2.1.0-nullsafety.5" wakelock: dependency: "direct main" description: @@ -577,5 +577,5 @@ packages: source: hosted version: "2.2.1" sdks: - dart: ">=2.10.0-110 <2.11.0" + dart: ">=2.12.0-0.0 <3.0.0" flutter: ">=1.22.0 <2.0.0" diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index eb76e8996..58a3b1c9a 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.2+5 +version: 1.1.3 environment: sdk: ">=2.7.0 <3.0.0" From c959cda6c14275dd0b04915aa9abe8f5d3cf9d85 Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 1 Apr 2021 16:59:42 +0800 Subject: [PATCH 145/422] set key --- flutter_hbb/lib/home_page.dart | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index 36757d840..d3d98c04f 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -253,8 +253,10 @@ void showServer(BuildContext context) { final formKey = GlobalKey(); final id0 = FFI.getByName('custom-rendezvous-server'); final relay0 = FFI.getByName('relay-server'); + final key0 = FFI.getByName('key'); var id = ''; var relay = ''; + var key = ''; showAlertDialog( context, (setState) => Tuple3( @@ -283,6 +285,16 @@ void showServer(BuildContext context) { relay = value.trim(); }, ), + TextFormField( + initialValue: key0, + decoration: InputDecoration( + labelText: 'Key', + ), + validator: null, + onSaved: (String value) { + key = value.trim(); + }, + ), ])), [ FlatButton( @@ -300,6 +312,7 @@ void showServer(BuildContext context) { if (id != id0) FFI.setByName('custom-rendezvous-server', id); if (relay != relay0) FFI.setByName('relay-server', relay); + if (key != key0) FFI.setByName('key', key); Navigator.pop(context); } }, From 7b0abd9157edc73117518730ca62f993c46c82c6 Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 1 Apr 2021 18:18:56 +0800 Subject: [PATCH 146/422] fix --- flutter_hbb/lib/remote_page.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index b6b64a6c8..356060d2a 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -107,6 +107,7 @@ class _RemotePageState extends State { text.toLowerCase().indexOf("handshake") < 0 && text.toLowerCase().indexOf("failed") < 0 && text.toLowerCase().indexOf("resolve") < 0 && + text.toLowerCase().indexOf("mismatch") < 0 && text.toLowerCase().indexOf("manually") < 0; if (hasRetry) { _timer?.cancel(); From e7c733be1dac2818431c93026047b85cad6eb8fe Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 7 Apr 2021 11:55:00 +0800 Subject: [PATCH 147/422] working on change_id --- flutter_hbb/pubspec.lock | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/flutter_hbb/pubspec.lock b/flutter_hbb/pubspec.lock index 53b9dc141..caa914825 100644 --- a/flutter_hbb/pubspec.lock +++ b/flutter_hbb/pubspec.lock @@ -21,42 +21,42 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0-nullsafety.3" + version: "2.5.0-nullsafety.1" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.3" + version: "2.1.0-nullsafety.1" characters: dependency: transitive description: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.5" + version: "1.1.0-nullsafety.3" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.3" + version: "1.2.0-nullsafety.1" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.3" + version: "1.1.0-nullsafety.1" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0-nullsafety.5" + version: "1.15.0-nullsafety.3" convert: dependency: transitive description: @@ -105,7 +105,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.3" + version: "1.2.0-nullsafety.1" ffi: dependency: "direct main" description: @@ -255,21 +255,21 @@ packages: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.3-nullsafety.3" + version: "0.6.2" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10-nullsafety.3" + version: "0.12.10-nullsafety.1" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.6" + version: "1.3.0-nullsafety.3" nested: dependency: transitive description: @@ -290,7 +290,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.3" + version: "1.8.0-nullsafety.1" path_provider: dependency: "direct main" description: @@ -428,42 +428,42 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.4" + version: "1.8.0-nullsafety.2" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.10.0-nullsafety.6" + version: "1.10.0-nullsafety.1" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.3" + version: "2.1.0-nullsafety.1" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.3" + version: "1.1.0-nullsafety.1" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.3" + version: "1.2.0-nullsafety.1" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19-nullsafety.6" + version: "0.2.19-nullsafety.2" tuple: dependency: "direct main" description: @@ -477,7 +477,7 @@ packages: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.5" + version: "1.3.0-nullsafety.3" url_launcher: dependency: "direct main" description: @@ -526,7 +526,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.5" + version: "2.1.0-nullsafety.3" wakelock: dependency: "direct main" description: @@ -577,5 +577,5 @@ packages: source: hosted version: "2.2.1" sdks: - dart: ">=2.12.0-0.0 <3.0.0" + dart: ">=2.10.0-110 <2.11.0" flutter: ">=1.22.0 <2.0.0" From 67711a386d43ce3f64ceae46c209d71fc628cf47 Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 7 Apr 2021 21:58:30 +0800 Subject: [PATCH 148/422] forum.rustdesk.com => rustdesk.com --- flutter_hbb/lib/home_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index d3d98c04f..df37b71be 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -332,7 +332,7 @@ Future showAbout(BuildContext context) async { Text('Version: ${packageInfo.version}'), InkWell( onTap: () async { - const url = 'https://forum.rustdesk.com/'; + const url = 'https://rustdesk.com/'; if (await canLaunch(url)) { await launch(url); } From 97e5a3f826c12e43ce81d38e365a41720b9bc31a Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 8 Apr 2021 12:59:55 +0800 Subject: [PATCH 149/422] some sh --- flutter_hbb/build.sh | 2 ++ flutter_hbb/ndk_arm64.sh | 2 ++ flutter_hbb/ndk_x64.sh | 2 ++ flutter_hbb/pubspec.lock | 42 ++++++++++++++++++++-------------------- 4 files changed, 27 insertions(+), 21 deletions(-) create mode 100755 flutter_hbb/build.sh create mode 100644 flutter_hbb/ndk_arm64.sh create mode 100644 flutter_hbb/ndk_x64.sh diff --git a/flutter_hbb/build.sh b/flutter_hbb/build.sh new file mode 100755 index 000000000..d0372066d --- /dev/null +++ b/flutter_hbb/build.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +flutter build apk --target-platform android-arm64 --release diff --git a/flutter_hbb/ndk_arm64.sh b/flutter_hbb/ndk_arm64.sh new file mode 100644 index 000000000..d28009f6c --- /dev/null +++ b/flutter_hbb/ndk_arm64.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +cargo ndk --platform 21 --target aarch64-linux-android build --release diff --git a/flutter_hbb/ndk_x64.sh b/flutter_hbb/ndk_x64.sh new file mode 100644 index 000000000..6272b0390 --- /dev/null +++ b/flutter_hbb/ndk_x64.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +cargo ndk --platform 21 --target x86_64-linux-android build --release diff --git a/flutter_hbb/pubspec.lock b/flutter_hbb/pubspec.lock index caa914825..d16209cd5 100644 --- a/flutter_hbb/pubspec.lock +++ b/flutter_hbb/pubspec.lock @@ -21,42 +21,42 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0-nullsafety.1" + version: "2.5.0" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0" characters: dependency: transitive description: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.3" + version: "1.1.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0-nullsafety.3" + version: "1.15.0" convert: dependency: transitive description: @@ -105,7 +105,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" ffi: dependency: "direct main" description: @@ -255,21 +255,21 @@ packages: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.2" + version: "0.6.3" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10-nullsafety.1" + version: "0.12.10" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0" nested: dependency: transitive description: @@ -290,7 +290,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.1" + version: "1.8.0" path_provider: dependency: "direct main" description: @@ -428,42 +428,42 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.2" + version: "1.8.0" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.10.0-nullsafety.1" + version: "1.10.0" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19-nullsafety.2" + version: "0.2.19" tuple: dependency: "direct main" description: @@ -477,7 +477,7 @@ packages: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0" url_launcher: dependency: "direct main" description: @@ -526,7 +526,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.3" + version: "2.1.0" wakelock: dependency: "direct main" description: @@ -577,5 +577,5 @@ packages: source: hosted version: "2.2.1" sdks: - dart: ">=2.10.0-110 <2.11.0" - flutter: ">=1.22.0 <2.0.0" + dart: ">=2.12.0-0.0 <3.0.0" + flutter: ">=1.22.0" From 679f291078351feefac59bcc54c893e9af93ffa3 Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 9 Apr 2021 00:31:10 +0800 Subject: [PATCH 150/422] chmod --- flutter_hbb/ndk_arm64.sh | 0 flutter_hbb/ndk_x64.sh | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 flutter_hbb/ndk_arm64.sh mode change 100644 => 100755 flutter_hbb/ndk_x64.sh diff --git a/flutter_hbb/ndk_arm64.sh b/flutter_hbb/ndk_arm64.sh old mode 100644 new mode 100755 diff --git a/flutter_hbb/ndk_x64.sh b/flutter_hbb/ndk_x64.sh old mode 100644 new mode 100755 From eaf528e09e45d5b67a9144a1a23c497c15840f0a Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 9 Apr 2021 01:12:15 +0800 Subject: [PATCH 151/422] build number required, and can increase only --- flutter_hbb/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index 58a3b1c9a..445c32b79 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.3 +version: 1.1.3+6 environment: sdk: ">=2.7.0 <3.0.0" From 0c354375310001e10f4cd14966f58c317761874b Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sat, 24 Apr 2021 22:28:59 +0800 Subject: [PATCH 152/422] 1.1.3 -> 1.1.4 --- flutter_hbb/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index 445c32b79..9e2ff102f 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.3+6 +version: 1.1.4+7 environment: sdk: ">=2.7.0 <3.0.0" From 6b6ffb3f17c69782b7925c72efa5cd195e3a015a Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sun, 25 Apr 2021 00:19:35 +0800 Subject: [PATCH 153/422] more zh, for configuration and installation and upgrade, and flutter, not test yet --- flutter_hbb/lib/common.dart | 75 ++++++++++++++++++++++++++++++-- flutter_hbb/lib/home_page.dart | 19 ++++---- flutter_hbb/lib/model.dart | 2 +- flutter_hbb/lib/remote_page.dart | 52 ++++++++++++---------- 4 files changed, 111 insertions(+), 37 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 9bb4c9b7d..ccf9acc03 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -2,6 +2,73 @@ import 'package:flutter/material.dart'; import 'dart:async'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:tuple/tuple.dart'; +import 'dart:io'; + +final bool isZh = Platform.localeName == "zh_CN"; + +final langs = >{ + 'zh': { + 'Remote ID': '远程', + 'ID/Relay Server': 'ID/中继服务器', + 'About': '关于', + 'Mute': '静音', + 'ID Server': 'ID服务器', + 'Relay Server': '中继服务器', + 'Invalid IP': '无效IP', + 'Invalid format': '无效格式', + 'Cancel': '取消', + 'Close': '关闭', + 'Retry': '再试', + 'OK': '确认', + 'Password Required': '需要密码', + 'Please enter your password': '请输入密码', + 'Remember password': '记住密码', + 'Wrong Password': '密码错误', + 'Do you want to enter again?': '还想输入一次吗?', + 'Connection Error': '连接错误', + 'Error': '错误', + 'Reset by the peer': '连接被对方关闭', + 'Connecting...': '正在连接...', + 'Connection in progress. Please wait.': '连接进行中,请稍等。', + 'Please try 1 minute later': '一分钟后再试', + 'Login Error': '登录错误', + 'Successful': '成功', + 'Connected, waiting for image...': '已连接,等待画面传输...', + 'Custom Image Quality': '设置画面质量', + 'Privacy mode': '隐私模式', + 'Remove': '删除', + 'Adjust Window': '调节窗口', + 'Good image quality': '好画质', + 'Balanced': '一般画质', + 'Optimize reaction time': '优化反应时间', + 'Custom': '自定义画质', + 'Show remote cursor': '显示远程光标', + 'Disable clipboard': '禁止剪贴板', + 'Lock after session end': '断开后锁定远程电脑', + 'Insert': '插入', + 'Insert Lock': '锁定远程电脑', + 'Refresh': '刷新画面', + 'ID not exist': 'ID不存在', + 'Failed to connect to rendezvous server': '连接服务器失败', + 'Remote desktop is offline': '远程电脑不在线', + 'Key mismatch': 'Key不匹配', + 'Timeout': '连接超时', + 'Failed to connect to relay server': '无法连接到中继服务器', + 'Failed to connect via rendezvous server': '无法通过服务器建立连接', + 'Failed to make direct connection to remote desktop': '无法建立直接连接', + 'OS Password': '操作系统密码', + 'Paste': '粘贴', + 'Logging in...': '正在登录...', + 'Are you sure to close the connection?': '是否确认关闭连接?', + }, + 'en': {} +}; + +String translate(name) { + final tmp = isZh ? langs['zh'] : langs['en']; + final v = tmp[name]; + return v != null ? v : name; +} class MyTheme { MyTheme._(); @@ -83,7 +150,7 @@ void msgbox(String type, String title, String text, BuildContext context, } final buttons = [ Expanded(child: Container()), - wrap('OK', () { + wrap(translate('OK'), () { dismissLoading(); Navigator.pop(context); }) @@ -94,7 +161,7 @@ void msgbox(String type, String title, String text, BuildContext context, if (hasCancel) { buttons.insert( 1, - wrap('Cancel', () { + wrap(translate('Cancel'), () { dismissLoading(); })); } @@ -102,9 +169,9 @@ void msgbox(String type, String title, String text, BuildContext context, Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(title, style: TextStyle(fontSize: 21)), + Text(translate(title), style: TextStyle(fontSize: 21)), SizedBox(height: 20), - Text(text, style: TextStyle(fontSize: 15)), + Text(translate(text), style: TextStyle(fontSize: 15)), SizedBox(height: 20), Row( children: buttons, diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index df37b71be..6f63ad0d6 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -49,9 +49,11 @@ class _HomePageState extends State { position: RelativeRect.fromLTRB(3000, 70, 3000, 70), items: [ PopupMenuItem( - child: Text('ID/Relay Server'), value: 'server'), + child: Text(translate('ID/Relay Server')), + value: 'server'), PopupMenuItem( - child: Text('About RustDesk'), value: 'about'), + child: Text(translate('About') + ' RustDesk'), + value: 'about'), ], elevation: 8, ); @@ -154,7 +156,7 @@ class _HomePageState extends State { color: Color(0xFF00B6F0), ), decoration: InputDecoration( - labelText: 'Remote ID', + labelText: translate('Remote ID'), // hintText: 'Enter your remote ID', border: InputBorder.none, helperStyle: TextStyle( @@ -227,7 +229,8 @@ class _HomePageState extends State { position: RelativeRect.fromLTRB(x, y, x, y), items: [ PopupMenuItem( - child: Text('Remove'), value: 'remove'), + child: Text(translate('Remove')), + value: 'remove'), ], elevation: 8, ); @@ -260,7 +263,7 @@ void showServer(BuildContext context) { showAlertDialog( context, (setState) => Tuple3( - Text('ID/Relay Server'), + Text(translate('ID/Relay Server')), Form( key: formKey, child: @@ -268,7 +271,7 @@ void showServer(BuildContext context) { TextFormField( initialValue: id0, decoration: InputDecoration( - labelText: 'ID Server', + labelText: translate('ID Server'), ), validator: validate, onSaved: (String value) { @@ -278,7 +281,7 @@ void showServer(BuildContext context) { TextFormField( initialValue: relay0, decoration: InputDecoration( - labelText: 'Relay Server', + labelText: translate('Relay Server'), ), validator: validate, onSaved: (String value) { @@ -316,7 +319,7 @@ void showServer(BuildContext context) { Navigator.pop(context); } }, - child: Text('OK'), + child: Text(translate('OK')), ), ], )); diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 7d4986be7..f2e543b09 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -179,7 +179,7 @@ class FfiModel with ChangeNotifier { _pi.sasEnabled = evt['sas_enabled'] == "true"; _pi.currentDisplay = int.parse(evt['current_display']); List displays = json.decode(evt['displays']); - _pi.displays = List(); + _pi.displays = []; for (int i = 0; i < displays.length; ++i) { Map d0 = displays[i]; var d = Display(); diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 356060d2a..ad29bf11c 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -47,7 +47,7 @@ class _RemotePageState extends State { FFI.connect(widget.id); WidgetsBinding.instance.addPostFrameCallback((_) { SystemChrome.setEnabledSystemUIOverlays([]); - showLoading('Connecting...', context); + showLoading(translate('Connecting...'), context); _interval = Timer.periodic(Duration(milliseconds: 30), (timer) => interval()); }); @@ -113,7 +113,7 @@ class _RemotePageState extends State { _timer?.cancel(); _timer = Timer(Duration(seconds: _reconnects), () { FFI.reconnect(); - showLoading('Connecting...', context); + showLoading(translate('Connecting...'), context); }); _reconnects *= 2; } else { @@ -579,7 +579,7 @@ void enterPasswordDialog(String id, BuildContext context) { showAlertDialog( context, (setState) => Tuple3( - Text('Password required'), + Text(translate('Password required')), Column(mainAxisSize: MainAxisSize.min, children: [ PasswordWidget(controller: controller), CheckboxListTile( @@ -587,7 +587,7 @@ void enterPasswordDialog(String id, BuildContext context) { dense: true, controlAffinity: ListTileControlAffinity.leading, title: Text( - 'Remember password', + translate('Remember password'), ), value: remember, onChanged: (v) { @@ -602,7 +602,7 @@ void enterPasswordDialog(String id, BuildContext context) { Navigator.pop(context); Navigator.pop(context); }, - child: Text('Cancel'), + child: Text(translate('Cancel')), ), FlatButton( textColor: MyTheme.accent, @@ -610,10 +610,10 @@ void enterPasswordDialog(String id, BuildContext context) { var text = controller.text.trim(); if (text == '') return; FFI.login(text, remember); - showLoading('Logging in...', null); + showLoading(translate('Logging in...'), null); Navigator.pop(context); }, - child: Text('OK'), + child: Text(translate('OK')), ), ], )); @@ -622,22 +622,22 @@ void enterPasswordDialog(String id, BuildContext context) { void wrongPasswordDialog(String id, BuildContext context) { showAlertDialog( context, - (_) => - Tuple3(Text('Wrong Password'), Text('Do you want to enter again?'), [ + (_) => Tuple3(Text(translate('Wrong Password')), + Text(translate('Do you want to enter again?')), [ FlatButton( textColor: MyTheme.accent, onPressed: () { Navigator.pop(context); Navigator.pop(context); }, - child: Text('Cancel'), + child: Text(translate('Cancel')), ), FlatButton( textColor: MyTheme.accent, onPressed: () { enterPasswordDialog(id, context); }, - child: Text('Retry'), + child: Text(translate('Retry')), ), ])); } @@ -691,7 +691,7 @@ void showOptions(BuildContext context) { FFI.setByName('toggle_option', 'disable-audio'); }); }, - title: Text('Mute'))); + title: Text(translate('Mute')))); } if (FFI.ffiModel.permissions['keyboard'] != false) { more.add(CheckboxListTile( @@ -702,7 +702,7 @@ void showOptions(BuildContext context) { FFI.setByName('toggle_option', 'lock-after-session-end'); }); }, - title: Text('Lock after session end'))); + title: Text(translate('Lock after session end')))); } return Tuple3( null, @@ -712,7 +712,7 @@ void showOptions(BuildContext context) { [ RadioListTile( controlAffinity: ListTileControlAffinity.trailing, - title: const Text('Good image quality'), + title: Text(translate('Good image quality')), value: 'best', groupValue: quality, onChanged: (String value) { @@ -724,7 +724,7 @@ void showOptions(BuildContext context) { ), RadioListTile( controlAffinity: ListTileControlAffinity.trailing, - title: const Text('Balanced'), + title: Text(translate('Balanced')), value: 'balanced', groupValue: quality, onChanged: (String value) { @@ -736,7 +736,7 @@ void showOptions(BuildContext context) { ), RadioListTile( controlAffinity: ListTileControlAffinity.trailing, - title: const Text('Optimize reaction time'), + title: Text(translate('Optimize reaction time')), value: 'low', groupValue: quality, onChanged: (String value) { @@ -756,7 +756,7 @@ void showOptions(BuildContext context) { FFI.setByName('toggle_option', 'show-remote-cursor'); }); }, - title: Text('Show remote cursor')), + title: Text(translate('Show remote cursor'))), ] + more), null); @@ -769,11 +769,13 @@ void showActions(BuildContext context) { final y = size.height; final more = >[]; if (FFI.ffiModel.pi.version.isNotEmpty) { - more.add(PopupMenuItem(child: Text('Refresh'), value: 'refresh')); + more.add(PopupMenuItem( + child: Text(translate('Refresh')), value: 'refresh')); } if (FFI.ffiModel.permissions['keyboard'] != false && FFI.ffiModel.permissions['clipboard'] != false) { - more.add(PopupMenuItem(child: Text('Paste'), value: 'paste')); + more.add( + PopupMenuItem(child: Text(translate('Paste')), value: 'paste')); } more.add(PopupMenuItem( child: Row( @@ -795,8 +797,10 @@ void showActions(BuildContext context) { position: RelativeRect.fromLTRB(x, y, x, y), items: [ PopupMenuItem( - child: Text('Insert Ctrl + Alt + Del'), value: 'cad'), - PopupMenuItem(child: Text('Insert Lock'), value: 'lock'), + child: Text(translate('Insert') + ' Ctrl + Alt + Del'), + value: 'cad'), + PopupMenuItem( + child: Text(translate('Insert Lock')), value: 'lock'), ] + more, elevation: 8, @@ -841,7 +845,7 @@ void showSetOSPassword(BuildContext context) async { showAlertDialog( context, (setState) => Tuple3( - Text('Password required'), + Text(translate('Password required')), Column(mainAxisSize: MainAxisSize.min, children: [ PasswordWidget(controller: controller), ]), @@ -851,7 +855,7 @@ void showSetOSPassword(BuildContext context) async { onPressed: () { Navigator.pop(context); }, - child: Text('Cancel'), + child: Text(translate('Cancel')), ), FlatButton( textColor: MyTheme.accent, @@ -860,7 +864,7 @@ void showSetOSPassword(BuildContext context) async { savePassword(FFI.id, text); Navigator.pop(context); }, - child: Text('OK'), + child: Text(translate('OK')), ), ], )); From 15850fd8ff5d059ed2e019de984b78cd714a3f19 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sun, 25 Apr 2021 00:22:44 +0800 Subject: [PATCH 154/422] fix one translate --- flutter_hbb/lib/common.dart | 2 +- flutter_hbb/lib/model.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index ccf9acc03..6562af1f0 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -33,7 +33,6 @@ final langs = >{ 'Please try 1 minute later': '一分钟后再试', 'Login Error': '登录错误', 'Successful': '成功', - 'Connected, waiting for image...': '已连接,等待画面传输...', 'Custom Image Quality': '设置画面质量', 'Privacy mode': '隐私模式', 'Remove': '删除', @@ -60,6 +59,7 @@ final langs = >{ 'Paste': '粘贴', 'Logging in...': '正在登录...', 'Are you sure to close the connection?': '是否确认关闭连接?', + 'Waiting for image...': '等待画面传输...', }, 'en': {} }; diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index f2e543b09..94feb030a 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -194,7 +194,7 @@ class FfiModel with ChangeNotifier { initializeCursorAndCanvas(); } if (displays.length > 0) { - showLoading('Waiting for image...', context); + showLoading(translate('Waiting for image...'), context); _waitForImage = true; } notifyListeners(); From c34e128945edf654949150705bf7e393fce62308 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sun, 25 Apr 2021 00:23:36 +0800 Subject: [PATCH 155/422] fix --- flutter_hbb/lib/common.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 6562af1f0..10a042e68 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -8,7 +8,7 @@ final bool isZh = Platform.localeName == "zh_CN"; final langs = >{ 'zh': { - 'Remote ID': '远程', + 'Remote ID': '远程ID', 'ID/Relay Server': 'ID/中继服务器', 'About': '关于', 'Mute': '静音', From 51be791a3a3bbf0f2889f7d2d12b390cefb95047 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sun, 25 Apr 2021 12:47:20 +0800 Subject: [PATCH 156/422] more zh --- flutter_hbb/lib/common.dart | 9 ++++++--- flutter_hbb/lib/home_page.dart | 2 +- flutter_hbb/lib/remote_page.dart | 8 ++++---- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 10a042e68..455a2e7d8 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -21,6 +21,7 @@ final langs = >{ 'Retry': '再试', 'OK': '确认', 'Password Required': '需要密码', + 'Enter your password': '输入你的密码', 'Please enter your password': '请输入密码', 'Remember password': '记住密码', 'Wrong Password': '密码错误', @@ -56,6 +57,7 @@ final langs = >{ 'Failed to connect via rendezvous server': '无法通过服务器建立连接', 'Failed to make direct connection to remote desktop': '无法建立直接连接', 'OS Password': '操作系统密码', + 'Password': '密码', 'Paste': '粘贴', 'Logging in...': '正在登录...', 'Are you sure to close the connection?': '是否确认关闭连接?', @@ -142,7 +144,8 @@ void msgbox(String type, String title, String text, BuildContext context, child: FlatButton( focusColor: MyTheme.accent, onPressed: onPressed, - child: Text(text, style: TextStyle(color: MyTheme.accent)))); + child: + Text(translate(text), style: TextStyle(color: MyTheme.accent)))); dismissLoading(); if (_hasDialog) { @@ -200,8 +203,8 @@ class _PasswordWidgetState extends State { obscureText: !_passwordVisible, //This will obscure text dynamically keyboardType: TextInputType.visiblePassword, decoration: InputDecoration( - labelText: 'Password', - hintText: 'Enter your password', + labelText: translate('Password'), + hintText: translate('Enter your password'), // Here is key idea suffixIcon: IconButton( icon: Icon( diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index 6f63ad0d6..08b4ab532 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -305,7 +305,7 @@ void showServer(BuildContext context) { onPressed: () { Navigator.pop(context); }, - child: Text('Cancel'), + child: Text(translate('Cancel')), ), FlatButton( textColor: MyTheme.accent, diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index ad29bf11c..4cf055088 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -368,7 +368,7 @@ class _RemotePageState extends State { color: active == true ? MyTheme.accent80 : null, child: icon != null ? Icon(icon, size: 17, color: Colors.white) - : Text(text, + : Text(translate(text), style: TextStyle(color: Colors.white, fontSize: 11)), onPressed: onPressed)); }; @@ -579,7 +579,7 @@ void enterPasswordDialog(String id, BuildContext context) { showAlertDialog( context, (setState) => Tuple3( - Text(translate('Password required')), + Text(translate('Password Required')), Column(mainAxisSize: MainAxisSize.min, children: [ PasswordWidget(controller: controller), CheckboxListTile( @@ -780,7 +780,7 @@ void showActions(BuildContext context) { more.add(PopupMenuItem( child: Row( children: ([ - Text('OS Password'), + Text(translate('OS Password')), FlatButton( textColor: MyTheme.accent, onPressed: () { @@ -845,7 +845,7 @@ void showSetOSPassword(BuildContext context) async { showAlertDialog( context, (setState) => Tuple3( - Text(translate('Password required')), + Text(translate('Password Required')), Column(mainAxisSize: MainAxisSize.min, children: [ PasswordWidget(controller: controller), ]), From 0c17a67573c1d185ed426b50cf86a2b646fe2252 Mon Sep 17 00:00:00 2001 From: open-trade Date: Sun, 25 Apr 2021 12:49:03 +0800 Subject: [PATCH 157/422] fix on zh --- flutter_hbb/lib/common.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 455a2e7d8..cbf2bec98 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -4,7 +4,7 @@ import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:tuple/tuple.dart'; import 'dart:io'; -final bool isZh = Platform.localeName == "zh_CN"; +final bool isZh = Platform.localeName.startsWith('zh'); final langs = >{ 'zh': { From ae1c6bcdfbbf6f2477322bef5f304cbe56eaf39c Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 14 Jun 2021 21:27:46 +0800 Subject: [PATCH 158/422] flutter 1.1.6 --- flutter_hbb/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index 9e2ff102f..3e235b0d4 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.4+7 +version: 1.1.6+8 environment: sdk: ">=2.7.0 <3.0.0" From b15c88c0146c41541e8714f9d3cda78ec88b944c Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 7 Jul 2021 21:18:00 +0800 Subject: [PATCH 159/422] typo --- flutter_hbb/lib/common.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index cbf2bec98..282af15b2 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -48,7 +48,7 @@ final langs = >{ 'Insert': '插入', 'Insert Lock': '锁定远程电脑', 'Refresh': '刷新画面', - 'ID not exist': 'ID不存在', + 'ID does not exist': 'ID不存在', 'Failed to connect to rendezvous server': '连接服务器失败', 'Remote desktop is offline': '远程电脑不在线', 'Key mismatch': 'Key不匹配', From 76bd1b00ef4593696e16cfc3caf8817970692a53 Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 29 Jul 2021 02:00:11 +0800 Subject: [PATCH 160/422] working on ios --- flutter_hbb/ios_arm64.sh | 2 ++ 1 file changed, 2 insertions(+) create mode 100755 flutter_hbb/ios_arm64.sh diff --git a/flutter_hbb/ios_arm64.sh b/flutter_hbb/ios_arm64.sh new file mode 100755 index 000000000..01a09a4b4 --- /dev/null +++ b/flutter_hbb/ios_arm64.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +cargo build --release --target aarch64-apple-ios From 0cdef55e3d67e3b6accd0b31df241f56bf742112 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 2 Aug 2021 17:08:53 +0800 Subject: [PATCH 161/422] flutter project upgraded --- flutter_hbb/README.md | 16 -- flutter_hbb/android/app/build.gradle | 10 +- .../android/app/src/main/AndroidManifest.xml | 10 +- .../res/drawable-v21/launch_background.xml | 12 ++ .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 544 -> 2693 bytes .../main/res/mipmap-hdpi/launcher_icon.png | Bin 2693 -> 0 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 442 -> 1902 bytes .../main/res/mipmap-mdpi/launcher_icon.png | Bin 1902 -> 0 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 721 -> 3510 bytes .../main/res/mipmap-xhdpi/launcher_icon.png | Bin 3510 -> 0 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 1031 -> 5300 bytes .../main/res/mipmap-xxhdpi/launcher_icon.png | Bin 5300 -> 0 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 1443 -> 7053 bytes .../main/res/mipmap-xxxhdpi/launcher_icon.png | Bin 7053 -> 0 bytes .../app/src/main/res/values-night/styles.xml | 18 ++ .../app/src/main/res/values/styles.xml | 8 +- flutter_hbb/android/build.gradle | 4 +- flutter_hbb/android/flutter_hbb_android.iml | 29 +++ flutter_hbb/android/gradle.properties | 1 - .../gradle/wrapper/gradle-wrapper.properties | 2 +- flutter_hbb/android/key.properties | 1 + flutter_hbb/ios/.gitignore | 1 + .../ios/Flutter/AppFrameworkInfo.plist | 4 +- .../ios/Runner.xcodeproj/project.pbxproj | 26 +-- .../contents.xcworkspacedata | 2 +- flutter_hbb/lib/model.dart | 20 ++- flutter_hbb/pubspec.lock | 165 ++++++++---------- flutter_hbb/pubspec.yaml | 26 +-- 28 files changed, 173 insertions(+), 182 deletions(-) delete mode 100644 flutter_hbb/README.md create mode 100644 flutter_hbb/android/app/src/main/res/drawable-v21/launch_background.xml delete mode 100644 flutter_hbb/android/app/src/main/res/mipmap-hdpi/launcher_icon.png delete mode 100644 flutter_hbb/android/app/src/main/res/mipmap-mdpi/launcher_icon.png delete mode 100644 flutter_hbb/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png delete mode 100644 flutter_hbb/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png delete mode 100644 flutter_hbb/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png create mode 100644 flutter_hbb/android/app/src/main/res/values-night/styles.xml create mode 100644 flutter_hbb/android/flutter_hbb_android.iml create mode 120000 flutter_hbb/android/key.properties diff --git a/flutter_hbb/README.md b/flutter_hbb/README.md deleted file mode 100644 index 4c570f839..000000000 --- a/flutter_hbb/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# flutter_hbb - -Your Remote Desktop Software - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) - -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/flutter_hbb/android/app/build.gradle b/flutter_hbb/android/app/build.gradle index c83feda57..2eeab0b0a 100644 --- a/flutter_hbb/android/app/build.gradle +++ b/flutter_hbb/android/app/build.gradle @@ -32,21 +32,17 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 29 + compileSdkVersion 30 sourceSets { main.java.srcDirs += 'src/main/kotlin' } - lintOptions { - disable 'InvalidPackage' - } - defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.carriez.flutter_hbb" - minSdkVersion 21 - targetSdkVersion 29 + minSdkVersion 16 + targetSdkVersion 30 versionCode flutterVersionCode.toInteger() versionName flutterVersionName } diff --git a/flutter_hbb/android/app/src/main/AndroidManifest.xml b/flutter_hbb/android/app/src/main/AndroidManifest.xml index d9b85398d..567c1ebbb 100644 --- a/flutter_hbb/android/app/src/main/AndroidManifest.xml +++ b/flutter_hbb/android/app/src/main/AndroidManifest.xml @@ -1,14 +1,8 @@ - - + android:icon="@mipmap/ic_launcher"> + + + + + + + diff --git a/flutter_hbb/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/flutter_hbb/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index db77bb4b7b0906d62b1847e87f15cdcacf6a4f29..ed28303a914d6780d414837087b9860d26190e46 100644 GIT binary patch literal 2693 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 AQ2+n{ diff --git a/flutter_hbb/android/app/src/main/res/mipmap-hdpi/launcher_icon.png b/flutter_hbb/android/app/src/main/res/mipmap-hdpi/launcher_icon.png deleted file mode 100644 index ed28303a914d6780d414837087b9860d26190e46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2693 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 kHwL)NFS;iHCOmi*pfB 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 literal 721 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wSy!iOI#yLg7ec#$`gxH85~pclTsBt za}(23gHjVyDhp4h+5i=O3-AeX1=1l$e`s#|#^}+&7(N@w0CIr{$Oe+Uk^K-ZP~83C zcc@hG6rikF&NPT(23>y!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ diff --git a/flutter_hbb/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png b/flutter_hbb/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png deleted file mode 100644 index afb25941a6e6d62b55394458f9570ffc3bd39b8f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3510 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 diff --git a/flutter_hbb/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/flutter_hbb/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index d5f1c8d34e7a88e3f88bea192c3a370d44689c3c..062de677b14cddf431c6fa405ee93c1c459bb088 100644 GIT binary patch literal 5300 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 oc|zI#9*+N!6#ajcw0aM)kMdtDv0yR!M{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof diff --git a/flutter_hbb/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png b/flutter_hbb/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png deleted file mode 100644 index 062de677b14cddf431c6fa405ee93c1c459bb088..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5300 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 literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` diff --git a/flutter_hbb/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png b/flutter_hbb/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png deleted file mode 100644 index cc13fc9d5800961d018c6b6f6f165e9375348b4d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7053 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 diff --git a/flutter_hbb/android/app/src/main/res/values-night/styles.xml b/flutter_hbb/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 000000000..449a9f930 --- /dev/null +++ b/flutter_hbb/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/flutter_hbb/android/app/src/main/res/values/styles.xml b/flutter_hbb/android/app/src/main/res/values/styles.xml index 1f83a33fd..d74aa35c2 100644 --- a/flutter_hbb/android/app/src/main/res/values/styles.xml +++ b/flutter_hbb/android/app/src/main/res/values/styles.xml @@ -1,7 +1,7 @@ - - diff --git a/flutter_hbb/android/build.gradle b/flutter_hbb/android/build.gradle index 4273368a3..110174e8d 100644 --- a/flutter_hbb/android/build.gradle +++ b/flutter_hbb/android/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:4.1.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.google.gms:google-services:4.3.3' } @@ -22,8 +22,6 @@ allprojects { rootProject.buildDir = '../build' subprojects { project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { project.evaluationDependsOn(':app') } diff --git a/flutter_hbb/android/flutter_hbb_android.iml b/flutter_hbb/android/flutter_hbb_android.iml new file mode 100644 index 000000000..18999696a --- /dev/null +++ b/flutter_hbb/android/flutter_hbb_android.iml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/flutter_hbb/android/gradle.properties b/flutter_hbb/android/gradle.properties index a6738207f..94adc3a3f 100644 --- a/flutter_hbb/android/gradle.properties +++ b/flutter_hbb/android/gradle.properties @@ -1,4 +1,3 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true -android.enableR8=true diff --git a/flutter_hbb/android/gradle/wrapper/gradle-wrapper.properties b/flutter_hbb/android/gradle/wrapper/gradle-wrapper.properties index 296b146b7..bc6a58afd 100644 --- a/flutter_hbb/android/gradle/wrapper/gradle-wrapper.properties +++ b/flutter_hbb/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/flutter_hbb/android/key.properties b/flutter_hbb/android/key.properties new file mode 120000 index 000000000..6dc71cf47 --- /dev/null +++ b/flutter_hbb/android/key.properties @@ -0,0 +1 @@ +/Users/hzhou/.android/key.properties \ No newline at end of file diff --git a/flutter_hbb/ios/.gitignore b/flutter_hbb/ios/.gitignore index e96ef602b..151026b91 100644 --- a/flutter_hbb/ios/.gitignore +++ b/flutter_hbb/ios/.gitignore @@ -18,6 +18,7 @@ Flutter/App.framework Flutter/Flutter.framework Flutter/Flutter.podspec Flutter/Generated.xcconfig +Flutter/ephemeral/ Flutter/app.flx Flutter/app.zip Flutter/flutter_assets/ diff --git a/flutter_hbb/ios/Flutter/AppFrameworkInfo.plist b/flutter_hbb/ios/Flutter/AppFrameworkInfo.plist index f2872cf47..9367d483e 100644 --- a/flutter_hbb/ios/Flutter/AppFrameworkInfo.plist +++ b/flutter_hbb/ios/Flutter/AppFrameworkInfo.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) + en CFBundleExecutable App CFBundleIdentifier @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 9.0 + 8.0 diff --git a/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj b/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj index 811c03816..0724246e2 100644 --- a/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj +++ b/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj @@ -289,16 +289,8 @@ CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); PRODUCT_BUNDLE_IDENTIFIER = com.carriez.flutterHbb; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -421,16 +413,8 @@ CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); PRODUCT_BUNDLE_IDENTIFIER = com.carriez.flutterHbb; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -448,16 +432,8 @@ CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); PRODUCT_BUNDLE_IDENTIFIER = com.carriez.flutterHbb; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -492,4 +468,4 @@ /* End XCConfigurationList section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; -} \ No newline at end of file +} diff --git a/flutter_hbb/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/flutter_hbb/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 1d526a16e..919434a62 100644 --- a/flutter_hbb/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/flutter_hbb/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 94feb030a..38a577f4f 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -21,7 +21,6 @@ class RgbaFrame extends Struct { Pointer data; } -typedef F1 = void Function(Pointer); typedef F2 = Pointer Function(Pointer, Pointer); typedef F3 = void Function(Pointer, Pointer); typedef F4 = void Function(Pointer); @@ -475,7 +474,6 @@ class CursorModel with ChangeNotifier { class FFI { static String id = ""; static String _dir = ''; - static F1 _freeCString; static F2 _getByName; static F3 _setByName; static F4 _freeRgba; @@ -606,14 +604,22 @@ class FFI { } static void setByName(String name, [String value = '']) { - _setByName(Utf8.toUtf8(name), Utf8.toUtf8(value)); + var a = name.toNativeUtf8(); + var b = value.toNativeUtf8(); + _setByName(a, b); + calloc.free(a); + calloc.free(b); } static String getByName(String name, [String arg = '']) { - var p = _getByName(Utf8.toUtf8(name), Utf8.toUtf8(arg)); + var a = name.toNativeUtf8(); + var b = arg.toNativeUtf8(); + var p = _getByName(a, b); assert(p != nullptr && p != null); - var res = Utf8.fromUtf8(p); - _freeCString(p); + var res = p.toDartString(); + calloc.free(p); + calloc.free(a); + calloc.free(b); return res; } @@ -625,8 +631,6 @@ class FFI { _setByName = dylib.lookupFunction, Pointer), F3>( 'set_by_name'); - _freeCString = dylib - .lookupFunction), F1>('rust_cstr_free'); _freeRgba = dylib .lookupFunction), F4>('free_rgba'); _getRgba = dylib.lookupFunction('get_rgba'); diff --git a/flutter_hbb/pubspec.lock b/flutter_hbb/pubspec.lock index d16209cd5..7f7f91a5d 100644 --- a/flutter_hbb/pubspec.lock +++ b/flutter_hbb/pubspec.lock @@ -7,21 +7,21 @@ packages: name: archive url: "https://pub.dartlang.org" source: hosted - version: "2.0.13" + version: "3.1.2" args: dependency: transitive description: name: args url: "https://pub.dartlang.org" source: hosted - version: "1.6.0" + version: "2.2.0" async: dependency: transitive description: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0" + version: "2.6.1" boolean_selector: dependency: transitive description: @@ -57,48 +57,34 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.15.0" - convert: - dependency: transitive - description: - name: convert - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.1" crypto: dependency: transitive description: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "2.1.5" - csslib: - dependency: transitive - description: - name: csslib - url: "https://pub.dartlang.org" - source: hosted - version: "0.16.2" + version: "3.0.1" cupertino_icons: dependency: "direct main" description: name: cupertino_icons url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.0.3" device_info: dependency: "direct main" description: name: device_info url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "2.0.2" device_info_platform_interface: dependency: transitive description: name: device_info_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "2.0.1" fake_async: dependency: transitive description: @@ -112,63 +98,63 @@ packages: name: ffi url: "https://pub.dartlang.org" source: hosted - version: "0.1.3" + version: "1.1.2" file: dependency: transitive description: name: file url: "https://pub.dartlang.org" source: hosted - version: "5.2.1" + version: "6.1.2" firebase: dependency: transitive description: name: firebase url: "https://pub.dartlang.org" source: hosted - version: "7.3.3" + version: "9.0.1" firebase_analytics: dependency: "direct main" description: name: firebase_analytics url: "https://pub.dartlang.org" source: hosted - version: "6.3.0" + version: "8.2.0" firebase_analytics_platform_interface: dependency: transitive description: name: firebase_analytics_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.3" + version: "2.0.1" firebase_analytics_web: dependency: transitive description: name: firebase_analytics_web url: "https://pub.dartlang.org" source: hosted - version: "0.1.1" + version: "0.3.0+1" firebase_core: dependency: transitive description: name: firebase_core url: "https://pub.dartlang.org" source: hosted - version: "0.5.3" + version: "1.4.0" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "4.0.1" firebase_core_web: dependency: transitive description: name: firebase_core_web url: "https://pub.dartlang.org" source: hosted - version: "0.2.1+1" + version: "1.1.0" flutter: dependency: "direct main" description: flutter @@ -189,7 +175,7 @@ packages: name: flutter_launcher_icons url: "https://pub.dartlang.org" source: hosted - version: "0.8.1" + version: "0.9.1" flutter_spinkit: dependency: transitive description: @@ -207,48 +193,27 @@ packages: description: flutter source: sdk version: "0.0.0" - html: - dependency: transitive - description: - name: html - url: "https://pub.dartlang.org" - source: hosted - version: "0.14.0+4" http: dependency: transitive description: name: http url: "https://pub.dartlang.org" source: hosted - version: "0.12.2" + version: "0.13.3" http_parser: dependency: transitive description: name: http_parser url: "https://pub.dartlang.org" source: hosted - version: "3.1.4" + version: "4.0.0" image: dependency: transitive description: name: image url: "https://pub.dartlang.org" source: hosted - version: "2.1.19" - import_js_library: - dependency: transitive - description: - name: import_js_library - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.2" - intl: - dependency: transitive - description: - name: intl - url: "https://pub.dartlang.org" - source: hosted - version: "0.16.1" + version: "3.0.2" js: dependency: transitive description: @@ -276,14 +241,14 @@ packages: name: nested url: "https://pub.dartlang.org" source: hosted - version: "0.0.4" + version: "1.0.0" package_info: dependency: "direct main" description: name: package_info url: "https://pub.dartlang.org" source: hosted - version: "0.4.3+2" + version: "2.0.2" path: dependency: transitive description: @@ -297,126 +262,126 @@ packages: name: path_provider url: "https://pub.dartlang.org" source: hosted - version: "1.6.24" + version: "2.0.2" path_provider_linux: dependency: transitive description: name: path_provider_linux url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+2" + version: "2.0.2" path_provider_macos: dependency: transitive description: name: path_provider_macos url: "https://pub.dartlang.org" source: hosted - version: "0.0.4+6" + version: "2.0.2" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "2.0.1" path_provider_windows: dependency: transitive description: name: path_provider_windows url: "https://pub.dartlang.org" source: hosted - version: "0.0.4+3" + version: "2.0.3" pedantic: dependency: transitive description: name: pedantic url: "https://pub.dartlang.org" source: hosted - version: "1.9.2" + version: "1.11.1" petitparser: dependency: transitive description: name: petitparser url: "https://pub.dartlang.org" source: hosted - version: "3.1.0" + version: "4.1.0" platform: dependency: transitive description: name: platform url: "https://pub.dartlang.org" source: hosted - version: "2.2.1" + version: "3.0.0" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.3" + version: "2.0.1" process: dependency: transitive description: name: process url: "https://pub.dartlang.org" source: hosted - version: "3.0.13" + version: "4.2.3" provider: dependency: "direct main" description: name: provider url: "https://pub.dartlang.org" source: hosted - version: "4.3.2+3" + version: "5.0.0" quiver: dependency: transitive description: name: quiver url: "https://pub.dartlang.org" source: hosted - version: "2.1.5" + version: "3.0.1" shared_preferences: dependency: "direct main" description: name: shared_preferences url: "https://pub.dartlang.org" source: hosted - version: "0.5.12+4" + version: "2.0.6" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux url: "https://pub.dartlang.org" source: hosted - version: "0.0.2+4" + version: "2.0.2" shared_preferences_macos: dependency: transitive description: name: shared_preferences_macos url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+11" + version: "2.0.2" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "2.0.0" shared_preferences_web: dependency: transitive description: name: shared_preferences_web url: "https://pub.dartlang.org" source: hosted - version: "0.1.2+7" + version: "2.0.1" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+3" + version: "2.0.2" sky_engine: dependency: transitive description: flutter @@ -428,7 +393,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" stack_trace: dependency: transitive description: @@ -463,14 +428,14 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19" + version: "0.3.0" tuple: dependency: "direct main" description: name: tuple url: "https://pub.dartlang.org" source: hosted - version: "1.0.3" + version: "2.0.0" typed_data: dependency: transitive description: @@ -484,42 +449,42 @@ packages: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "5.7.10" + version: "6.0.9" url_launcher_linux: dependency: transitive description: name: url_launcher_linux url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+4" + version: "2.0.1" url_launcher_macos: dependency: transitive description: name: url_launcher_macos url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+9" + version: "2.0.1" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.0.9" + version: "2.0.4" url_launcher_web: dependency: transitive description: name: url_launcher_web url: "https://pub.dartlang.org" source: hosted - version: "0.1.5+1" + version: "2.0.2" url_launcher_windows: dependency: transitive description: name: url_launcher_windows url: "https://pub.dartlang.org" source: hosted - version: "0.0.1+3" + version: "2.0.1" vector_math: dependency: transitive description: @@ -533,49 +498,63 @@ packages: name: wakelock url: "https://pub.dartlang.org" source: hosted - version: "0.2.1+1" + version: "0.5.2" + wakelock_macos: + dependency: transitive + description: + name: wakelock_macos + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0+1" wakelock_platform_interface: dependency: transitive description: name: wakelock_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "0.1.0+1" + version: "0.2.1+1" wakelock_web: dependency: transitive description: name: wakelock_web url: "https://pub.dartlang.org" source: hosted - version: "0.1.0+3" + version: "0.2.0+1" + wakelock_windows: + dependency: transitive + description: + name: wakelock_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0" win32: dependency: transitive description: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "1.7.4" + version: "2.2.5" xdg_directories: dependency: transitive description: name: xdg_directories url: "https://pub.dartlang.org" source: hosted - version: "0.1.2" + version: "0.2.0" xml: dependency: transitive description: name: xml url: "https://pub.dartlang.org" source: hosted - version: "4.5.1" + version: "5.1.2" yaml: dependency: transitive description: name: yaml url: "https://pub.dartlang.org" source: hosted - version: "2.2.1" + version: "3.1.0" sdks: - dart: ">=2.12.0-0.0 <3.0.0" - flutter: ">=1.22.0" + dart: ">=2.13.0 <3.0.0" + flutter: ">=2.0.0" diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index 3e235b0d4..3e7a9e9f6 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -27,29 +27,29 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.0 - ffi: ^0.1.3 - path_provider: ^1.6.24 - provider: ^4.3.2+2 + cupertino_icons: ^1.0.3 + ffi: ^1.1.2 + path_provider: ^2.0.2 + provider: ^5.0.0 flutter_easyloading: git: url: git://github.com/open-trade/flutter_easyloading #path: flutter_easyloading - tuple: ^1.0.1 - wakelock: ^0.2.1+1 - device_info: ^1.0.0 - firebase_analytics: ^6.2.0 - package_info: ^0.4.3+2 - url_launcher: ^5.7.10 - shared_preferences: ^0.5.12+4 + tuple: ^2.0.0 + wakelock: ^0.5.2 + device_info: ^2.0.2 + firebase_analytics: ^8.2.0 + package_info: ^2.0.2 + url_launcher: ^6.0.9 + shared_preferences: ^2.0.6 dev_dependencies: - flutter_launcher_icons: "^0.8.0" + flutter_launcher_icons: ^0.9.1 flutter_test: sdk: flutter flutter_icons: - android: "launcher_icon" + android: "ic_launcher" ios: true image_path: "../128x128@2x.png" From 600f604611c47bab8c48cfed8cd37cdfcd68304e Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 2 Aug 2021 20:54:56 +0800 Subject: [PATCH 162/422] refactor --- flutter_hbb/lib/common.dart | 98 +++++++------------------------- flutter_hbb/lib/home_page.dart | 8 +-- flutter_hbb/lib/model.dart | 26 ++++++++- flutter_hbb/lib/remote_page.dart | 39 ++++++------- 4 files changed, 71 insertions(+), 100 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 282af15b2..a40356de2 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -2,74 +2,11 @@ import 'package:flutter/material.dart'; import 'dart:async'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:tuple/tuple.dart'; -import 'dart:io'; -final bool isZh = Platform.localeName.startsWith('zh'); +typedef F = String Function(String); -final langs = >{ - 'zh': { - 'Remote ID': '远程ID', - 'ID/Relay Server': 'ID/中继服务器', - 'About': '关于', - 'Mute': '静音', - 'ID Server': 'ID服务器', - 'Relay Server': '中继服务器', - 'Invalid IP': '无效IP', - 'Invalid format': '无效格式', - 'Cancel': '取消', - 'Close': '关闭', - 'Retry': '再试', - 'OK': '确认', - 'Password Required': '需要密码', - 'Enter your password': '输入你的密码', - 'Please enter your password': '请输入密码', - 'Remember password': '记住密码', - 'Wrong Password': '密码错误', - 'Do you want to enter again?': '还想输入一次吗?', - 'Connection Error': '连接错误', - 'Error': '错误', - 'Reset by the peer': '连接被对方关闭', - 'Connecting...': '正在连接...', - 'Connection in progress. Please wait.': '连接进行中,请稍等。', - 'Please try 1 minute later': '一分钟后再试', - 'Login Error': '登录错误', - 'Successful': '成功', - 'Custom Image Quality': '设置画面质量', - 'Privacy mode': '隐私模式', - 'Remove': '删除', - 'Adjust Window': '调节窗口', - 'Good image quality': '好画质', - 'Balanced': '一般画质', - 'Optimize reaction time': '优化反应时间', - 'Custom': '自定义画质', - 'Show remote cursor': '显示远程光标', - 'Disable clipboard': '禁止剪贴板', - 'Lock after session end': '断开后锁定远程电脑', - 'Insert': '插入', - 'Insert Lock': '锁定远程电脑', - 'Refresh': '刷新画面', - 'ID does not exist': 'ID不存在', - 'Failed to connect to rendezvous server': '连接服务器失败', - 'Remote desktop is offline': '远程电脑不在线', - 'Key mismatch': 'Key不匹配', - 'Timeout': '连接超时', - 'Failed to connect to relay server': '无法连接到中继服务器', - 'Failed to connect via rendezvous server': '无法通过服务器建立连接', - 'Failed to make direct connection to remote desktop': '无法建立直接连接', - 'OS Password': '操作系统密码', - 'Password': '密码', - 'Paste': '粘贴', - 'Logging in...': '正在登录...', - 'Are you sure to close the connection?': '是否确认关闭连接?', - 'Waiting for image...': '等待画面传输...', - }, - 'en': {} -}; - -String translate(name) { - final tmp = isZh ? langs['zh'] : langs['en']; - final v = tmp[name]; - return v != null ? v : name; +class Translator { + static F call; } class MyTheme { @@ -83,6 +20,15 @@ class MyTheme { static const Color border = Color(0xFFCCCCCC); } +final ButtonStyle flatButtonStyle = TextButton.styleFrom( + primary: MyTheme.accent, + minimumSize: Size(88, 36), + padding: EdgeInsets.symmetric(horizontal: 16.0), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(2.0)), + ), +); + void showLoading(String text, BuildContext context) { if (_hasDialog && context != null) { Navigator.pop(context); @@ -141,11 +87,11 @@ void msgbox(String type, String title, String text, BuildContext context, .shrinkWrap, //limits the touch area to the button area minWidth: 0, //wraps child's width height: 0, - child: FlatButton( - focusColor: MyTheme.accent, + child: TextButton( + style: flatButtonStyle, onPressed: onPressed, - child: - Text(translate(text), style: TextStyle(color: MyTheme.accent)))); + child: Text(Translator.call(text), + style: TextStyle(color: MyTheme.accent)))); dismissLoading(); if (_hasDialog) { @@ -153,7 +99,7 @@ void msgbox(String type, String title, String text, BuildContext context, } final buttons = [ Expanded(child: Container()), - wrap(translate('OK'), () { + wrap(Translator.call('OK'), () { dismissLoading(); Navigator.pop(context); }) @@ -164,7 +110,7 @@ void msgbox(String type, String title, String text, BuildContext context, if (hasCancel) { buttons.insert( 1, - wrap(translate('Cancel'), () { + wrap(Translator.call('Cancel'), () { dismissLoading(); })); } @@ -172,9 +118,9 @@ void msgbox(String type, String title, String text, BuildContext context, Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(translate(title), style: TextStyle(fontSize: 21)), + Text(Translator.call(title), style: TextStyle(fontSize: 21)), SizedBox(height: 20), - Text(translate(text), style: TextStyle(fontSize: 15)), + Text(Translator.call(text), style: TextStyle(fontSize: 15)), SizedBox(height: 20), Row( children: buttons, @@ -203,8 +149,8 @@ class _PasswordWidgetState extends State { obscureText: !_passwordVisible, //This will obscure text dynamically keyboardType: TextInputType.visiblePassword, decoration: InputDecoration( - labelText: translate('Password'), - hintText: translate('Enter your password'), + labelText: Translator.call('Password'), + hintText: Translator.call('Enter your password'), // Here is key idea suffixIcon: IconButton( icon: Icon( diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index 08b4ab532..9fa515fda 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -300,15 +300,15 @@ void showServer(BuildContext context) { ), ])), [ - FlatButton( - textColor: MyTheme.accent, + TextButton( + style: flatButtonStyle, onPressed: () { Navigator.pop(context); }, child: Text(translate('Cancel')), ), - FlatButton( - textColor: MyTheme.accent, + TextButton( + style: flatButtonStyle, onPressed: () { if (formKey.currentState.validate()) { formKey.currentState.save(); diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 38a577f4f..d22e110e8 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -44,6 +44,7 @@ class FfiModel with ChangeNotifier { get pi => _pi; FfiModel() { + Translator.call = translate; clear(); () async { await FFI.init(); @@ -193,7 +194,7 @@ class FfiModel with ChangeNotifier { initializeCursorAndCanvas(); } if (displays.length > 0) { - showLoading(translate('Waiting for image...'), context); + showLoading(translate('Connected, waiting for image...'), context); _waitForImage = true; } notifyListeners(); @@ -728,3 +729,26 @@ void initializeCursorAndCanvas() async { FFI.ffiModel.display.x, FFI.ffiModel.display.y, xCursor, yCursor); FFI.canvasModel.update(xCanvas, yCanvas, scale); } + +final bool isCn = Platform.localeName.endsWith('CN'); + +final langs = >{ + 'cn': { + 'Remote ID': '远程ID', + 'Paste': '粘贴', + 'Are you sure to close the connection?': '是否确认关闭连接?', + }, + 'en': {} +}; + +String translate(name) { + final tmp = isCn ? langs['cn'] : langs['en']; + final v = tmp[name]; + if (v == null) { + var a = 'translate'; + var b = '{"locale": "${Platform.localeName}", "text": "${name}"}'; + return FFI.getByName(a, b); + } else { + return v; + } +} diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 4cf055088..d5299d71c 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -360,12 +360,13 @@ class _RemotePageState extends State { .shrinkWrap, //limits the touch area to the button area minWidth: 0, //wraps child's width height: 0, - child: FlatButton( - splashColor: MyTheme.accent, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(5.0), + child: TextButton( + style: TextButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5.0), + ), + primary: active == true ? MyTheme.accent80 : null, ), - color: active == true ? MyTheme.accent80 : null, child: icon != null ? Icon(icon, size: 17, color: Colors.white) : Text(translate(text), @@ -596,16 +597,16 @@ void enterPasswordDialog(String id, BuildContext context) { ), ]), [ - FlatButton( - textColor: MyTheme.accent, + TextButton( + style: flatButtonStyle, onPressed: () { Navigator.pop(context); Navigator.pop(context); }, child: Text(translate('Cancel')), ), - FlatButton( - textColor: MyTheme.accent, + TextButton( + style: flatButtonStyle, onPressed: () { var text = controller.text.trim(); if (text == '') return; @@ -624,16 +625,16 @@ void wrongPasswordDialog(String id, BuildContext context) { context, (_) => Tuple3(Text(translate('Wrong Password')), Text(translate('Do you want to enter again?')), [ - FlatButton( - textColor: MyTheme.accent, + TextButton( + style: flatButtonStyle, onPressed: () { Navigator.pop(context); Navigator.pop(context); }, child: Text(translate('Cancel')), ), - FlatButton( - textColor: MyTheme.accent, + TextButton( + style: flatButtonStyle, onPressed: () { enterPasswordDialog(id, context); }, @@ -781,8 +782,8 @@ void showActions(BuildContext context) { child: Row( children: ([ Text(translate('OS Password')), - FlatButton( - textColor: MyTheme.accent, + TextButton( + style: flatButtonStyle, onPressed: () { showSetOSPassword(context); Navigator.pop(context); @@ -850,15 +851,15 @@ void showSetOSPassword(BuildContext context) async { PasswordWidget(controller: controller), ]), [ - FlatButton( - textColor: MyTheme.accent, + TextButton( + style: flatButtonStyle, onPressed: () { Navigator.pop(context); }, child: Text(translate('Cancel')), ), - FlatButton( - textColor: MyTheme.accent, + TextButton( + style: flatButtonStyle, onPressed: () { var text = controller.text.trim(); savePassword(FFI.id, text); From 5df90d4fa17880388c071fbf350d559b4e641a55 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 2 Aug 2021 22:21:23 +0800 Subject: [PATCH 163/422] fix on help tools and tranlating 'Failed' --- flutter_hbb/lib/common.dart | 1 - flutter_hbb/lib/model.dart | 5 ++++- flutter_hbb/lib/remote_page.dart | 38 +++++++++++++++----------------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index a40356de2..26d9826d6 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -21,7 +21,6 @@ class MyTheme { } final ButtonStyle flatButtonStyle = TextButton.styleFrom( - primary: MyTheme.accent, minimumSize: Size(88, 36), padding: EdgeInsets.symmetric(horizontal: 16.0), shape: const RoundedRectangleBorder( diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index d22e110e8..0dbd3182c 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -741,7 +741,10 @@ final langs = >{ 'en': {} }; -String translate(name) { +String translate(String name) { + if (name.startsWith('Failed') && name.contains(':')) { + return name.split(': ').map((x) => translate(x)).join(': '); + } final tmp = isCn ? langs['cn'] : langs['en']; final v = tmp[name]; if (v == null) { diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index d5299d71c..66613ad76 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -352,26 +352,24 @@ class _RemotePageState extends State { } var wrap = (String text, void Function() onPressed, [bool active, IconData icon]) { - return ButtonTheme( - padding: EdgeInsets.symmetric( - vertical: icon != null ? 3 : 6, - horizontal: 6), //adds padding inside the button - materialTapTargetSize: MaterialTapTargetSize - .shrinkWrap, //limits the touch area to the button area - minWidth: 0, //wraps child's width - height: 0, - child: TextButton( - style: TextButton.styleFrom( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(5.0), - ), - primary: active == true ? MyTheme.accent80 : null, - ), - child: icon != null - ? Icon(icon, size: 17, color: Colors.white) - : Text(translate(text), - style: TextStyle(color: Colors.white, fontSize: 11)), - onPressed: onPressed)); + return TextButton( + style: TextButton.styleFrom( + minimumSize: Size(0, 0), + padding: EdgeInsets.symmetric( + vertical: icon != null ? 3 : 6, + horizontal: 6), //adds padding inside the button + tapTargetSize: MaterialTapTargetSize + .shrinkWrap, //limits the touch area to the button area + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5.0), + ), + backgroundColor: active == true ? MyTheme.accent80 : null, + ), + child: icon != null + ? Icon(icon, size: 17, color: Colors.white) + : Text(translate(text), + style: TextStyle(color: Colors.white, fontSize: 11)), + onPressed: onPressed); }; final mouse = [ wrap('Drag', () { From 7573793d6cdd856189558d14354306f9baea0430 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 3 Aug 2021 00:35:25 +0800 Subject: [PATCH 164/422] onLongPress -> right click --- flutter_hbb/lib/remote_page.dart | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 66613ad76..ab79968f8 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -28,7 +28,6 @@ class _RemotePageState extends State { String _value = ''; double _xOffset = 0; double _yOffset = 0; - double _xOffset0 = 0; double _yOffset0 = 0; double _scale = 1; bool _mouseTools = false; @@ -258,24 +257,17 @@ class _RemotePageState extends State { : null, body: FlutterEasyLoading( child: GestureDetector( + onLongPress: () { + if (_drag || _scroll) return; + FFI.tap(true); + }, onTap: () { if (_drag || _scroll) return; FFI.tap(_right); }, - onLongPressStart: (_) { - if (_drag) { - // case: to show password on windows - FFI.sendMouse('down', 'left'); - } - }, - onLongPressEnd: (_) { - if (_drag) { - FFI.sendMouse('up', 'left'); - } - }, onScaleStart: (details) { _scale = 1; - _xOffset = _xOffset0 = details.focalPoint.dx; + _xOffset = details.focalPoint.dx; _yOffset = _yOffset0 = details.focalPoint.dy; if (_drag) { FFI.sendMouse('down', 'left'); @@ -304,6 +296,11 @@ class _RemotePageState extends State { onScaleEnd: (details) { if (_drag) { FFI.sendMouse('up', 'left'); + setState(() { + _drag = false; + _scroll = false; + _right = false; + }); } else if (_scroll) { var dy = (_yOffset - _yOffset0) / 10; if (dy.abs() > 0.1) { From f29115c024a6bdebf5438ec99f38f96eb7ef3de7 Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 4 Aug 2021 22:35:55 +0800 Subject: [PATCH 165/422] flutter run can run on iOS, though id editorbox not shown --- flutter_hbb/{build.sh => build_android.sh} | 0 flutter_hbb/ios/Flutter/Debug.xcconfig | 1 + flutter_hbb/ios/Flutter/Release.xcconfig | 1 + flutter_hbb/ios/Podfile | 45 +++++ flutter_hbb/ios/Podfile.lock | 171 ++++++++++++++++++ .../ios/Runner.xcodeproj/project.pbxproj | 106 ++++++++++- .../contents.xcworkspacedata | 3 + flutter_hbb/ios/Runner/AppDelegate.swift | 8 + .../ios/Runner/GoogleService-Info.plist | 36 ++++ .../ios/Runner/Runner-Bridging-Header.h | 2 + flutter_hbb/ios/Runner/ffi.h | 4 + 11 files changed, 372 insertions(+), 5 deletions(-) rename flutter_hbb/{build.sh => build_android.sh} (100%) create mode 100644 flutter_hbb/ios/Podfile create mode 100644 flutter_hbb/ios/Podfile.lock create mode 100644 flutter_hbb/ios/Runner/GoogleService-Info.plist create mode 100644 flutter_hbb/ios/Runner/ffi.h diff --git a/flutter_hbb/build.sh b/flutter_hbb/build_android.sh similarity index 100% rename from flutter_hbb/build.sh rename to flutter_hbb/build_android.sh diff --git a/flutter_hbb/ios/Flutter/Debug.xcconfig b/flutter_hbb/ios/Flutter/Debug.xcconfig index 592ceee85..ec97fc6f3 100644 --- a/flutter_hbb/ios/Flutter/Debug.xcconfig +++ b/flutter_hbb/ios/Flutter/Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/flutter_hbb/ios/Flutter/Release.xcconfig b/flutter_hbb/ios/Flutter/Release.xcconfig index 592ceee85..c4855bfe2 100644 --- a/flutter_hbb/ios/Flutter/Release.xcconfig +++ b/flutter_hbb/ios/Flutter/Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/flutter_hbb/ios/Podfile b/flutter_hbb/ios/Podfile new file mode 100644 index 000000000..0df0a09f8 --- /dev/null +++ b/flutter_hbb/ios/Podfile @@ -0,0 +1,45 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +platform :ios, '11.0' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end + + diff --git a/flutter_hbb/ios/Podfile.lock b/flutter_hbb/ios/Podfile.lock new file mode 100644 index 000000000..c6b65e985 --- /dev/null +++ b/flutter_hbb/ios/Podfile.lock @@ -0,0 +1,171 @@ +PODS: + - device_info (0.0.1): + - Flutter + - Firebase/Analytics (8.3.0): + - Firebase/Core + - Firebase/Core (8.3.0): + - Firebase/CoreOnly + - FirebaseAnalytics (~> 8.3.0) + - Firebase/CoreOnly (8.3.0): + - FirebaseCore (= 8.3.0) + - firebase_analytics (8.2.0): + - Firebase/Analytics (= 8.3.0) + - firebase_core + - Flutter + - firebase_core (1.4.0): + - Firebase/CoreOnly (= 8.3.0) + - Flutter + - FirebaseAnalytics (8.3.0): + - FirebaseAnalytics/AdIdSupport (= 8.3.0) + - FirebaseCore (~> 8.0) + - FirebaseInstallations (~> 8.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.4) + - GoogleUtilities/MethodSwizzler (~> 7.4) + - GoogleUtilities/Network (~> 7.4) + - "GoogleUtilities/NSData+zlib (~> 7.4)" + - nanopb (~> 2.30908.0) + - FirebaseAnalytics/AdIdSupport (8.3.0): + - FirebaseCore (~> 8.0) + - FirebaseInstallations (~> 8.0) + - GoogleAppMeasurement (= 8.3.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.4) + - GoogleUtilities/MethodSwizzler (~> 7.4) + - GoogleUtilities/Network (~> 7.4) + - "GoogleUtilities/NSData+zlib (~> 7.4)" + - nanopb (~> 2.30908.0) + - FirebaseCore (8.3.0): + - FirebaseCoreDiagnostics (~> 8.0) + - GoogleUtilities/Environment (~> 7.4) + - GoogleUtilities/Logger (~> 7.4) + - FirebaseCoreDiagnostics (8.5.0): + - GoogleDataTransport (~> 9.0) + - GoogleUtilities/Environment (~> 7.4) + - GoogleUtilities/Logger (~> 7.4) + - nanopb (~> 2.30908.0) + - FirebaseInstallations (8.5.0): + - FirebaseCore (~> 8.0) + - GoogleUtilities/Environment (~> 7.4) + - GoogleUtilities/UserDefaults (~> 7.4) + - PromisesObjC (< 3.0, >= 1.2) + - Flutter (1.0.0) + - GoogleAppMeasurement (8.3.0): + - GoogleAppMeasurement/AdIdSupport (= 8.3.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.4) + - GoogleUtilities/MethodSwizzler (~> 7.4) + - GoogleUtilities/Network (~> 7.4) + - "GoogleUtilities/NSData+zlib (~> 7.4)" + - nanopb (~> 2.30908.0) + - GoogleAppMeasurement/AdIdSupport (8.3.0): + - GoogleUtilities/AppDelegateSwizzler (~> 7.4) + - GoogleUtilities/MethodSwizzler (~> 7.4) + - GoogleUtilities/Network (~> 7.4) + - "GoogleUtilities/NSData+zlib (~> 7.4)" + - nanopb (~> 2.30908.0) + - GoogleDataTransport (9.1.0): + - GoogleUtilities/Environment (~> 7.2) + - nanopb (~> 2.30908.0) + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/AppDelegateSwizzler (7.5.0): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Environment (7.5.0): + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/Logger (7.5.0): + - GoogleUtilities/Environment + - GoogleUtilities/MethodSwizzler (7.5.0): + - GoogleUtilities/Logger + - GoogleUtilities/Network (7.5.0): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Reachability + - "GoogleUtilities/NSData+zlib (7.5.0)" + - GoogleUtilities/Reachability (7.5.0): + - GoogleUtilities/Logger + - GoogleUtilities/UserDefaults (7.5.0): + - GoogleUtilities/Logger + - nanopb (2.30908.0): + - nanopb/decode (= 2.30908.0) + - nanopb/encode (= 2.30908.0) + - nanopb/decode (2.30908.0) + - nanopb/encode (2.30908.0) + - package_info (0.0.1): + - Flutter + - path_provider (0.0.1): + - Flutter + - PromisesObjC (2.0.0) + - shared_preferences (0.0.1): + - Flutter + - url_launcher (0.0.1): + - Flutter + - wakelock (0.0.1): + - Flutter + +DEPENDENCIES: + - device_info (from `.symlinks/plugins/device_info/ios`) + - firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`) + - firebase_core (from `.symlinks/plugins/firebase_core/ios`) + - Flutter (from `Flutter`) + - package_info (from `.symlinks/plugins/package_info/ios`) + - path_provider (from `.symlinks/plugins/path_provider/ios`) + - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) + - url_launcher (from `.symlinks/plugins/url_launcher/ios`) + - wakelock (from `.symlinks/plugins/wakelock/ios`) + +SPEC REPOS: + trunk: + - Firebase + - FirebaseAnalytics + - FirebaseCore + - FirebaseCoreDiagnostics + - FirebaseInstallations + - GoogleAppMeasurement + - GoogleDataTransport + - GoogleUtilities + - nanopb + - PromisesObjC + +EXTERNAL SOURCES: + device_info: + :path: ".symlinks/plugins/device_info/ios" + firebase_analytics: + :path: ".symlinks/plugins/firebase_analytics/ios" + firebase_core: + :path: ".symlinks/plugins/firebase_core/ios" + Flutter: + :path: Flutter + package_info: + :path: ".symlinks/plugins/package_info/ios" + path_provider: + :path: ".symlinks/plugins/path_provider/ios" + shared_preferences: + :path: ".symlinks/plugins/shared_preferences/ios" + url_launcher: + :path: ".symlinks/plugins/url_launcher/ios" + wakelock: + :path: ".symlinks/plugins/wakelock/ios" + +SPEC CHECKSUMS: + device_info: d7d233b645a32c40dfdc212de5cf646ca482f175 + Firebase: 817b9171d0d51dccc458b94a5e8edff6b1dd323d + firebase_analytics: 25a03006c041a3be10bbd8e7313f87f59b9857bf + firebase_core: 72374607c8c6f5a0adc0559e5c5bf57deda975e3 + FirebaseAnalytics: 456f2c51599870a15ded4548d1f1f7b2cabf65a7 + FirebaseCore: a6dba751680d7033b9d3831e1cfc95ead0605118 + FirebaseCoreDiagnostics: 7bf55d386f9fc690d971b70a582142321a390eb8 + FirebaseInstallations: f2bc590b291d25fb40a9a05b8281c02a881b5117 + Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c + GoogleAppMeasurement: de70802583dedceb0bca18172b345307e8f410b8 + GoogleDataTransport: 85fd18ff3019bb85d3f2c551d04c481dedf71fc9 + GoogleUtilities: eea970f4a389963963bffe8d8fabe43540678b9c + nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 + package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62 + path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c + PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58 + shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d + url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef + wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f + +PODFILE CHECKSUM: a00077baecbb97321490c14848fceed3893ca92a + +COCOAPODS: 1.10.2 diff --git a/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj b/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj index 0724246e2..253550635 100644 --- a/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj +++ b/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj @@ -3,16 +3,19 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 51; objects = { /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 7E078EE926BAB4720036E738 /* liblibrustdesk.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E078EE826BAB4710036E738 /* liblibrustdesk.a */; }; + 7E078EEC26BADB3D0036E738 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7E078EEB26BADB3D0036E738 /* GoogleService-Info.plist */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + C362FA62593C0A953D788A2B /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D81DD31BD179189F3EA0124A /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -35,6 +38,11 @@ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7B0BE50AED65C219B81E506C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 7E078EE826BAB4710036E738 /* liblibrustdesk.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = liblibrustdesk.a; path = "../../target/aarch64-apple-ios/release/liblibrustdesk.a"; sourceTree = ""; }; + 7E078EEA26BABB100036E738 /* ffi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ffi.h; sourceTree = ""; }; + 7E078EEB26BADB3D0036E738 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 94AF76B3E95A41AD3421FB7B /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -42,6 +50,8 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + C9E47BCA6B42B34FF7A143DA /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + D81DD31BD179189F3EA0124A /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -49,12 +59,24 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 7E078EE926BAB4720036E738 /* liblibrustdesk.a in Frameworks */, + C362FA62593C0A953D788A2B /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 4064E93F6C682BED4ADBDEEA /* Pods */ = { + isa = PBXGroup; + children = ( + 94AF76B3E95A41AD3421FB7B /* Pods-Runner.debug.xcconfig */, + 7B0BE50AED65C219B81E506C /* Pods-Runner.release.xcconfig */, + C9E47BCA6B42B34FF7A143DA /* Pods-Runner.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -72,6 +94,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, + 4064E93F6C682BED4ADBDEEA /* Pods */, + 9F474765C702311B2610E104 /* Frameworks */, ); sourceTree = ""; }; @@ -90,14 +114,25 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C147021CF9000F007C117D /* Info.plist */, + 7E078EEB26BADB3D0036E738 /* GoogleService-Info.plist */, 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 7E078EEA26BABB100036E738 /* ffi.h */, 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, ); path = Runner; sourceTree = ""; }; + 9F474765C702311B2610E104 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 7E078EE826BAB4710036E738 /* liblibrustdesk.a */, + D81DD31BD179189F3EA0124A /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -105,12 +140,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 7E81731B2965F8CA22E67C02 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 3974543C243EF943511C7BDD /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -161,6 +198,7 @@ files = ( 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 7E078EEC26BADB3D0036E738 /* GoogleService-Info.plist in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, ); @@ -169,6 +207,23 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 3974543C243EF943511C7BDD /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -183,6 +238,28 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; + 7E81731B2965F8CA22E67C02 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -288,9 +365,15 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = HZF9JMC8YN; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = "$(PROJECT_DIR)/../../target/aarch64-apple-ios/release/"; PRODUCT_BUNDLE_IDENTIFIER = com.carriez.flutterHbb; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -399,7 +482,8 @@ MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -412,9 +496,15 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = HZF9JMC8YN; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = "$(PROJECT_DIR)/../../target/aarch64-apple-ios/release/"; PRODUCT_BUNDLE_IDENTIFIER = com.carriez.flutterHbb; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -431,9 +521,15 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = HZF9JMC8YN; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = "$(PROJECT_DIR)/../../target/aarch64-apple-ios/release/"; PRODUCT_BUNDLE_IDENTIFIER = com.carriez.flutterHbb; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; diff --git a/flutter_hbb/ios/Runner.xcworkspace/contents.xcworkspacedata b/flutter_hbb/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a16e..21a3cc14c 100644 --- a/flutter_hbb/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/flutter_hbb/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/flutter_hbb/ios/Runner/AppDelegate.swift b/flutter_hbb/ios/Runner/AppDelegate.swift index 70693e4a8..203cfff11 100644 --- a/flutter_hbb/ios/Runner/AppDelegate.swift +++ b/flutter_hbb/ios/Runner/AppDelegate.swift @@ -8,6 +8,14 @@ import Flutter didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) + dummyMethodToEnforceBundling(); return super.application(application, didFinishLaunchingWithOptions: launchOptions) } + + public func dummyMethodToEnforceBundling() { + get_rgba(); + free_rgba(nil); + get_by_name("", ""); + set_by_name("", ""); + } } diff --git a/flutter_hbb/ios/Runner/GoogleService-Info.plist b/flutter_hbb/ios/Runner/GoogleService-Info.plist new file mode 100644 index 000000000..f39288230 --- /dev/null +++ b/flutter_hbb/ios/Runner/GoogleService-Info.plist @@ -0,0 +1,36 @@ + + + + + CLIENT_ID + 768133699366-k1rn3ls1u2n3nklmgd9t4cmpdob0c8bn.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.768133699366-k1rn3ls1u2n3nklmgd9t4cmpdob0c8bn + API_KEY + AIzaSyCf57HjCwSokt91CqFI0Mwf8D--ek0jvfc + GCM_SENDER_ID + 768133699366 + PLIST_VERSION + 1 + BUNDLE_ID + com.carriez.flutterHbb + PROJECT_ID + rustdesk + STORAGE_BUCKET + rustdesk.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:768133699366:ios:c33078a6181b9d507993e7 + DATABASE_URL + https://rustdesk.firebaseio.com + + \ No newline at end of file diff --git a/flutter_hbb/ios/Runner/Runner-Bridging-Header.h b/flutter_hbb/ios/Runner/Runner-Bridging-Header.h index 308a2a560..a8c447418 100644 --- a/flutter_hbb/ios/Runner/Runner-Bridging-Header.h +++ b/flutter_hbb/ios/Runner/Runner-Bridging-Header.h @@ -1 +1,3 @@ #import "GeneratedPluginRegistrant.h" + +#import "ffi.h" diff --git a/flutter_hbb/ios/Runner/ffi.h b/flutter_hbb/ios/Runner/ffi.h new file mode 100644 index 000000000..701ec4b09 --- /dev/null +++ b/flutter_hbb/ios/Runner/ffi.h @@ -0,0 +1,4 @@ +void* get_rgba(); +void free_rgba(void*); +void set_by_name(const char*, const char*); +const char* get_by_name(const char*, const char*); From 7b89016c25d0b91f4234c6b345277dab74d40239 Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 5 Aug 2021 01:38:20 +0800 Subject: [PATCH 166/422] ios works, but icon wierd --- flutter_hbb/lib/model.dart | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 0dbd3182c..6986a28ff 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -49,6 +49,7 @@ class FfiModel with ChangeNotifier { () async { await FFI.init(); _initialized = true; + print("FFI initialized"); notifyListeners(); }(); } @@ -628,6 +629,7 @@ class FFI { final dylib = Platform.isAndroid ? DynamicLibrary.open('librustdesk.so') : DynamicLibrary.process(); + print('initializing FFI'); _getByName = dylib.lookupFunction('get_by_name'); _setByName = dylib.lookupFunction, Pointer), F3>( @@ -636,10 +638,22 @@ class FFI { .lookupFunction), F4>('free_rgba'); _getRgba = dylib.lookupFunction('get_rgba'); _dir = (await getApplicationDocumentsDirectory()).path; - DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); - AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; - final name = '${androidInfo.brand}-${androidInfo.model}'; - final id = androidInfo.id; + String id = 'NA'; + String name = 'Flutter'; + try { + DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); + if (Platform.isAndroid) { + AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; + name = '${androidInfo.brand}-${androidInfo.model}'; + id = androidInfo.id.hashCode.toString(); + } else { + IosDeviceInfo iosInfo = await deviceInfo.iosInfo; + name = iosInfo.utsname.machine; + id = iosInfo.identifierForVendor.hashCode.toString(); + } + } catch (e) { + print(e); + } setByName('info1', id); setByName('info2', name); setByName('init', _dir); @@ -730,7 +744,9 @@ void initializeCursorAndCanvas() async { FFI.canvasModel.update(xCanvas, yCanvas, scale); } -final bool isCn = Platform.localeName.endsWith('CN'); +final locale = Platform.localeName; +final bool isCn = + locale.startsWith('zh') && (locale.endsWith('CN') || locale.endsWith('SG')); final langs = >{ 'cn': { From efcd7fd9f627b347ca05dc8bd446a837eab2c526 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 5 Aug 2021 10:33:26 +0800 Subject: [PATCH 167/422] change flutter icon to no margin --- flutter_hbb/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index 3e7a9e9f6..510cad5ec 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -51,7 +51,7 @@ dev_dependencies: flutter_icons: android: "ic_launcher" ios: true - image_path: "../128x128@2x.png" + image_path: "../360.png" # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec From c6d07ffb5c97dbb50e5e80165a21cecf64b22f30 Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 5 Aug 2021 10:47:15 +0800 Subject: [PATCH 168/422] flutter pub run flutter_launcher_icons:main --- .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 2693 -> 2545 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 1902 -> 1729 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 3510 -> 3390 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 5300 -> 5048 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 7053 -> 6765 bytes .../Icon-App-1024x1024@1x.png | Bin 79746 -> 82293 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 993 -> 691 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 1619 -> 1342 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 2316 -> 2072 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 1283 -> 1042 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 2218 -> 2033 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 3205 -> 3096 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 1619 -> 1342 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 2986 -> 2812 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 4330 -> 4145 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 4330 -> 4145 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 6651 -> 6317 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 2840 -> 2653 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 5545 -> 5290 bytes .../Icon-App-83.5x83.5@2x.png | Bin 6190 -> 5882 bytes flutter_hbb/pubspec.yaml | 1 + 21 files changed, 1 insertion(+) diff --git a/flutter_hbb/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/flutter_hbb/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index ed28303a914d6780d414837087b9860d26190e46..8be536a752fe85973c9443c6556cdec03504bf61 100644 GIT binary patch delta 2536 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 rDBveZ0Y5P)|$$uz#z>m+OVPQcfN^mXn zSL7{AMsCttSngNZ2DN{u`Gs}DGpQ-?>lV`>H05x&KFENG;npK!&TMQ=oQ%sEc|0fr zVDjU#j=-yle}ht0flp94#_rjR)aWP_Wu4KiX%kCNzA=!uLG$#($n=d!4hh$+Iq&|x;8CE$Lg{d_Yz9~uCh zECO!bbJedA+~CV9R4<)(a|1?%0@EUaA;E&|&nJM<%k6W1nE~_<5<%_z6G+JdemVx+ ztCZeW@yAJmoW0hah);n ziPZbNjpun(lh)z>06Mj%6nhnzn+`16?EEdKC zt(ybyZ+~}$kplrd(V3q71atC?1Ao~I^!gp(Y%!%>8}sYl8v+#l4SbWK%1SW+&esjp z?!22A&&w$UemD#)&$ORo+t_y@sZ>9qRNV~>3)jjQql63}Do|mj7{H=< zc7HeV#*APXKteD1*v$1nX_*331KglS_x2{>$3*>cu9tcRFtnTg`O{aaALOV|GzWVk zwiWGL)g61Pi~04^0N7df4%8o8e?pa>Ik@R`%xI~Dy_O7Y28|#M;JiVe_Y4#k z!c~y@B1VBheK4c}jkzi`^)a=d*ul^PtIyhO^LrQuaGV;*F7oa}Lh6ojP$}c79+m)E z7gXpz2qwN{tbCk3l3D&UOcnhta(|0iZ<1E)%;Y_veXoht*tZ9bdxs6YEPa>OzOx3( z*Vnhofp@l=--}@y02eZEcg%iTf1J(M*JFS;Vt}t`1+|8r+|^rk=dtIXLMbzo=Cx~K z{F@(`oRvdu<0+^$YOLQ=eU>_jJu0jO zlmaLtNsaxUT9wlFv)&#p!-cxmCm8a}#cxPegQeewV#+{SF z^}@N%3;lN9YUDG{3i2wO12y50Ei0hL6}gC(fD`5NXM=4mQLeB9{T(gdUtg!TjZzy9 z`%1RHBa?th5tJswfFPZ91AnVgS+(V6MUmkcrOdo7qG37<4uIo!=oxt;F&Q@$cf>K;aGHU>WSMcK;z1D?LwtBg_FJ77djsh6hYaQy-ijNk_!d z;UQ9~j=~ymJ87T8+b3K*Lqo)7z@c-Zo?L<|lxnz^nQ8riTu<;aGk;BD!Sk+&!{sQ> z$<~m!Y=tWVXb_xEP1BH@l!k~ozgfS4HzwRIEkMqa<(lZ8xW1S^bKg~Z=&!GjB(vMDp1%Akvn#>KGDs>>RZ)tP{A`>`P2;rTCvO}62gWAjcOC{}cK`qY07*qo IM6N<$f&o)b&;S4c delta 1888 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 diff --git a/flutter_hbb/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/flutter_hbb/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 062de677b14cddf431c6fa405ee93c1c459bb088..5aa4c46d484afc85612a7f038055bf1001178a5f 100644 GIT binary patch literal 5048 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 P00000NkvXXu0mjfEd}!> literal 7053 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 diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png index 9666e3043f986c9cfda65d54030a25ba33ea4543..81cd1181c6349010de184bd65e68e0b73f7038a5 100644 GIT binary patch literal 82293 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 }NT74B7qwJ6SZ~TmZ?qg_pfK~+!= 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 diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png index daf959dbd2948bc80bc107fc2e30bac43fb4e86f..108a7a0a5d01d47647522b1c7dc6069b78926b99 100644 GIT binary patch delta 668 zcmV;N0%QH*2eSo`BYy&VNklkk z%==A1+blS@Nq@nHf5P!ytM%V$_~2_6+f`W)4A7D9Df5MWFs%(_-P;PDx@(-XX7f!|G&7r?slD z>#jFj0ChQ019?j!DPCFmx(F&BLs>m6nGHv>VXNpP!qxiIwb0oY0@ynM>n_VJ?^SVH zFda(Q!+*m!0?QJMm;2XN%0Z`U1H+*M^+X?g&o8?mwG7T|kb)opsg}NQ3>vPte^oE& zR!bmEXK7L#ol+%C_uADD`BGsD??STFqIcb5;e9w;Ct+{|1t}4hCY83*s=(^E-qfl3 zUtFq%6k%yJ94_$pyfBYy#eX+uL$Nkc;*aB^>EX>4Tx0C=2zkv&MmKpe$iQ?)7;2P=p; zWT;M7L`5963Pq?8YK2xEOfLO`CJjl7i=*ILaPVWX>fqw6tAnc`2!4P#IXWr2NQwVT z3N2ziIPS;0dyl(!fKV+m&1xG5G~G56v8b3zuZn?Jbf6zy@PA`WW|lE0NpZ9sU-$6w z^)AA*TA%xK^r#t&0X~st?1$8Vf-E(<&} zWF`~y#35oa*ThN_v%IMhPZ5VzO{aVz<*~|ni?dcNv-&;x3xgSbWtrRA0S2B7*_2($PgBTd zf%h}|rZmuZ3v{n}z18<|`T!)UtN0CYa0ra#D0{udyMH^Id;9lHy}utmcyf~IB5y4K z00J0EL_t(|0kxD%NR&|&$A96VhEqiOz%T`)qUVemL=StA3&X%Fpt3@GJ_&Dkp<|IrOA%uovqorkj3uu0&-SPI~t zVzb`vcf%4AQJ4cCAXor9iaY@Q8D^cFvSA5H==|xn&H~0J%(`Xe{IE3TY;CUDN5?$? zJAa3ir=Y$7TzUlkqe)6N;MskUyF`Uj^&gIS0Jf{L^=i{?h0s+G4UeI*8;X|0IlYDO zR&(#-eN+9EK&KFu&2aaGfnKOkzp1*lrRu{Ca#CJ4>L-`G=FNx;_`9ZKlWN>1IK9(^ z|5mx`d#|6xp{WNRL~!@t3~?LiObzFu&qc})U1@k?K1X_@Nq{-c%oo{OKxYIwQ^P!T uuX8dkN1oFest3AMY`T@}$7cUmTWdPqv>BiyLnnd`bEUQ%F%jgd!EA-ah6$9&)SG}I-rMt> z*E{aL?>YCLbFKrw+uecp*ZrL5eV^xf-}fr;$u>L+Y3mA*>VGUmf;}BJ#8PP#21j0a z;6|s*g^MLcXxd!{PoI}76t#L}QY@BKmE#e6&L9B=D~b#fheZ1V^^@v!qB3t8dOO^T zYPqEZmpEi_vJ&mts=@YW<|^v6K1gsrF%z*VWX`oi&7v#v2ZbEMKGYn#B4<_Zd8;K& z83!zS3i$6X@PGB0fpX_uAbu3^(;vY3wg>=;jlI}ZDB;G-W&-cc2j(Ty|Mz!))Ritur5w#Q}#Lz>XTS{(oWMuJ=9y7Sfv9O_sBTSPbsl z;(4o2zYQ;kjajtz9|GR^2Jjew3=8lJ(o?&jGI7 z06wUS5OBh1pma4gvdMK`di(w7fiF%8K%Z{(0?%&)(vrwpFA9%VqUHX)CQmj4Kl~m6 z;HR$w4}Xu5%e%UPf>Pi_vwq*E4&d#Bz^+=L{4EMe;{)q?y07UBKD)65yl6ky7DxvYl*aL2!q0bc7CPBwN_ zV0mzQ^p_^!XpMAcA<=89yH5txOP?%FK&`kmW`x+u7)+tM(t7#6e*N~EfIJn!8li|d zc7OT0RW#wPaUY>!sD&E*%+0#T|<#1tEGam(3%$Cb9w24e(3XoM)Cj;{I z@TN6mt9S{c;{OYYC9@|{c?BTGNdy?-Qpd=0rD{Em|Q z_8Rde!Q!~c7T%IAHl8c?iYyT`j4b8^a%P0yh3}ykJ8&U1Tv$NXW_ruXWR2IK(SNRI z>oUy3=>*mYy!-@^`()@bkJB!C{lV~Xh5-9L!a5}d+F`;cwy65l2*m#4V}hehETQ`X z$l}5H6jnpw<+Im-*bopsI;T25t9*Qv4q&y$!2AI@y&Ae*;2VNNjpD$=hao;b`oyO+ zR=Yc}&);qlvbI)hr_eC+D(cN9UwvW&k+4? ibpFzyE;+xSA^r!pg@NmEpq>~20000EX>4Tx0C=2zkv&MmKpe$iQ?)7;2P=p; zWT;M7L`5963Pq?8YK2xEOfLO`CJjl7i=*ILaPVWX>fqw6tAnc`2!4P#IXWr2NQwVT z3N2ziIPS;0dyl(!fKV+m&1xG5G~G56v8b3zuZn?Jbf6zy@PA`WW|lE0NpZ9sU-$6w z^)AA*TA%xK^r#t&0X~st?1$8Vf-E(<&} zWF`~y#35oa*ThN_v%IMhPZ5VzO{aVz<*~|ni?dcNv-&;x3xgSbWtrRA0S2B7*_2($PgBTd zf%h}|rZmuZ3v{n}z18<|`T!)UtN0CYa0ra#D0{udyMH^Id;9lHy}utmcyf~IB5y4K z00fFjL_t(|0qvN7OjcDG$3G|nqJXIMgUZbIE|(!R(Mp{WbFAi4Gqss>nx^&-tvNJZ zma8>cscrq!aA{hhXksE%+M2Rq1qPIeIyN1V$)Lc@#1}O4?K$3;z1$yo&kHX?Ti=bn z_dJJl?|-1I+Schx!IJ?UeZuqKn3d&f&FriY&2Mr%_QRUdpv z&5ja%!11eJU~=|mxf;+zU;)K@l1Q95pM;cGIkIwtwc8G;Ja?<$0tzEFYd!TPho~+3 zv15lFP)+Ntg=<$hQ#P#=z!_$vRmPg@9mRVdw||^}*K zvGD#(X!r}hIt7Q$z~9%k5o2`#T{R>MHarcBAAx@T4BN1112E$On4MzSGN-`lCRnu} z3QlR=I;`7(DR)BQixNxmX8XVh<2NS4Z1*I<=Sv{B9G2%p>$TvX7Zl)QBcN!hcx#Zo zuYaY@_`P_=-WMi*Sug=^mkFHlktDw$AhiKK!{ESjh#P3{t2_(qzBaDEkZI2S0cOcG zM_as^F&ehch50*hn9Obf4v=-0x4;hfZ7r-Wg74}A5B#H5ytNBnDTFME$rVpPM2Dx$ z8z)nK9QK|FA>iz>Fm8m|R#*+sY=`SU9DluW-V19EKt(<5S!hhNlKNenA%NA+781SUNFaOTJ6~B-pANLjV{T327d)?ahM%>2<&P4T+at7f(K) z3WNM>Qh9OuLm>i88wHUa{_EO55(Jf+aDM%E8NR83nPUUC$@l4vbp`a?W^Vs_0e}8f zDMd31+jX+h3^;PA-Z)plm?(3*?J7aTDP3waw}(XPjdKOOVo9olRjTPEa&IQ6HBBO~!(Kf1VW}=95;OO~nP&UL28+km zil?*E?Hn76_29Gtnjill^zgJgK5PyuAvVN%zAWQUDa^z?@{G(st6})3UGU8Cfdi zfJ)oSjbbxLYPwiUUW@VvLA1jiRZ;P=)QB(d3+nqMctEvo@{U1?Oyzsijc)(Hs8)7^O_mdt^Wwq9pXrQtkhkHeJD@tqQD-GxnvibE z$I-XnwHob!>Xa!VWfiGe*^d1^BOfQWW{YzUT05Kpj;!2BeaYb)XFMhG9{K!6N`xNq zsF%w!t;%x_aGe7xsZ%D)K~8VzKYYU4N7Uy5{{=70F2BYy|qNkldbfoCVE(<0%!I{=iyVh5 zso5r>0>WNx>!9ztjd1sV>Or*D1t>H*@KeIl}0$c z!-H0{ARuY zMC}Bok?gq~XSUGNwX_p%RLW!;skefQ5;aHF@`bN?CRjrSH!3Rx7}XB=I3Adu0NlRo zjFviqaDSufz{;UOgKBbF_tJ&0wK|94Sr+)T8_>H6@X})7#0C4?X@Wz9yEOoolgr{! zR`CT3ZE-hW?KM_xyT%joAH+I$1a=edPwR2XKq{b=`a712=xUx=yDDI9zu{#fy#QFB ztX`)j>>|vHWwB1OjQytzUuV#!t#Wzmvev+z6Mr*|T>p8%?`it#HbFPxdR2iHLx9pc z$lf6P`h5~L_F&=rV^#rkdIQaBs+U!cNUt6Sw3q>$zO4S93AzX~$rZF_E4r!4A2|!m z+A8eol^aE$C!PeFPX`*;0EV{#Mg|BCErU0?N{b?ZPIE<=Ll1Njo)AI}^pVS5z79PKb!(-BkDk?jUf-K+pqKOgXmfO`r~RDw-|5-fGA2j~?>wdb zoO_J_VieSm4*v}M?At~sP$rV?R}IF4Yn^k%5iNkEQz8)0 zdf>8^V0=4YVb(ZzTt!FPg=5%zPs`;jp^Gp_H~KE54f*TxT@-_fw}15&zloth`!7hr zm^mo#bp}3oPG5r=8~*}6+^GLOhVIx0T4^3r{xS_$slnwNe}qU2FIyn#~!Zbkx5(+%-*Iy zQ)mqipYhZd9-X1=B#7P0TGB#bK*{ntK*Jy=&TkuZf+v2 z>D?mF%eK1d<3;6;oGp6&Y~8~5vT3WgazF6p_M-AmKt2=ZxPM~>XOza%Z_Wc(A5x3c3{QARx@B|e9Gt+l*Wc~sg;jPKWuRr*Mh~kWlghk9% z7=`%Aap<2I2!HotdUpC6>8VYT3oSmAD=q_RC8@sXCk!G^dJ;RI~VeWWb+;u%DBgdKodHBj6MKnfMehqys!^@uzs~KRx`P q;^mW1k(-f1I?41YUXfFo{{g8kGYv2Wfj|HN002ovP6b4+LSTaWFYr+S delta 2305 zcmV+c3I6t&5R4L#BYy#eX+uL$Nkc;*aB^>EX>4Tx0C=2zkv&MmKpe$iQ?)7;2P=p; zWT;M7L`5963Pq?8YK2xEOfLO`CJjl7i=*ILaPVWX>fqw6tAnc`2!4P#IXWr2NQwVT z3N2ziIPS;0dyl(!fKV+m&1xG5G~G56v8b3zuZn?Jbf6zy@PA`WW|lE0NpZ9sU-$6w z^)AA*TA%xK^r#t&0X~st?1$8Vf-E(<&} zWF`~y#35oa*ThN_v%IMhPZ5VzO{aVz<*~|ni?dcNv-&;x3xgSbWtrRA0S2B7*_2($PgBTd zf%h}|rZmuZ3v{n}z18<|`T!)UtN0CYa0ra#D0{udyMH^Id;9lHy}utmcyf~IB5y4K z00%=!L_t(|0qt4|P*qhJ{zO4Z#3dC3%~5kuG%(jK#U<2wF=$g_i-aiR7Wzb41pAH`CWU+Mdv^ha{AM2SasPAA z`M>j@^M5b*o(A}zp~1aYpr-MFa9zg(!gU?a6ZUN17=7O#i^e`r@joA3_f`+e__>4< z`(fB(HX-!t%^9E@KRepD072@3~xf9_dFs7KvrlkikEp`5VdjcX@7%vdJ4)l9|z@W}R098oN048q) zN`FaIklhdoj0phF<^#Kb2U0SDUHsTf%#5t9yYuwAl?Sc_P8a4 zyR-zpCfUxd9JehfQoi$x9k+Goidi=i@bUyc*b7TYn@Ta#;*rXUUb~o4NlC`J;2(d zzzTZkF+J6{YY)cqX-@!gM~rH1f`9OAE=4!V-KQ=A;c-A-fxbS^aG#FlByFSx$33pz zX3mo&oCYPcF#tn^-CX1fm6X!5@%qW$iTT@ru&#{BD$Bb&!(JdQZOb**w;{p{ z)Mcv=07uUlQ>FsB^x)Aufn_7A%7gMGw*18Fc84mYTmwmDyT<%w$NM0&KH(}q# zjG3P5vO}lzE1-pR$>X^COiv$uSc%Jep__1<<{VJ6lzzLQUjZ$gkc2c5vf6sq5Mj@T za#>N4eg(8}U8BF|k_5#Obbk{r(~!AZU0rK9rJGucXh64$S2bqchszzOjVYprM+ zfdwoEEGgDWDy*BjNYvD~9i)MAbQ2bp%Hb?vSof+jf2MuCSv?)e)_<=^Wprh664F6k zminjhe)YQ<&g!;b)Cmi`%?c`fEzDV&llsPB^=%`t`-JiS4f)k%?$hNX)oWL=4CgaD zirsHqsqq0oWRSY7fZ0`2y0Lx*3=tOT;}<_M?F&_x$8a+Krc+5iuCrrWb|9DW+6nTf zp5n>Lh3h)|y#t0L=YMJ@_cQvcFFT=Z;v^tyD{I%I&h_WUdtT(qh#oGlvxRs5=#Wv3 z6&NOb{2cHxOTm{^l6wL13T5>;-~~=f+(BA~+aO;hhKt%-EaBIN(CK+;kGbIp(@9Z! zn(zFjB^omtShWvWva>u}qkpyD8A_}6XPPK^D1RsR z{{hTQhUt)JDj6&+1xBv}cFX`mJ88G|U=F@$s8Z+Vu)uH4s%uG@bNU}U%c6gc^6bkj zOlQ9ZR3euZKnJ$EGkmQ z&kxh1X8^@zFhf?VyB6WghKCbzm`W!yBYK1-V<_`$Z-0L8&E%dhRho#G2jScXBYFUT zu@KltvS;nEs_?iZ6Uhme*ni;PXH5-E%Vx8jOp=t0$ItVVa>oU$mT!qYO(v2Pw#95h z$B1PRhrVhXY%vKkk({tNyO?K7gjb;I@t^w^#r6!Ouh=FNYkTSKk#AvYW;lOIo;}=m zC_&1Md3q?iB7MK8O;{Y%ObqISVTrM5?k8S+SR5oQM6vxC$<9UQm#JFK9LHgCP&2;E zsN2W+#0fX$(3h`M_f{{I-maTyEN{h3LlH+`}3HRDcHI4^_>pC6~ buIu<0I3et#iqgn000000NkvXXu0mjf8}Dgi diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png index 3844fd0a219a4cba35b0f79d0fe52944d471d6ab..909f1e47febca5edce7e37a075d9ae4498b4fded 100644 GIT binary patch delta 1021 zcmVh+{d&*8w783q$&hyNYWD)+coc)6t5 z=rHO3sZoW6hTn&dloSW)Za95Yw}&M`LT@;KM;zDgaFJMds)2(;Gg&_C<6M|NTqe69 zcNjj6!+pi9@kF~V=f7@h%AUi4e&&%Px1A6D(f~rmJTVw~e`QXSk zD6iJ-O%+fy1qQ^MbCmhFy#>E}viZFMq(ALs0v`?Au(S{kTlbx+xF( z#`qT%@UDfB@kMAK+o17c)=y_!`${X}{U1VqwO362t&E?lmFELF&3^EpzS;^KjyizO z8w>sWnC+SdSbZ=wFjyDv!ulhyCEx5@Ji#F}ZVDr6QF8oiIoB@>{KZVLVn4NC+WejuxRlW#b#M9Mf@1%NeLY;Jldf5|Av%)`dvk&H1SJaqjTucBy0Q6M-HG@{xG3RZ^(W9`jj(Z zzgWmhv-VxS=Ky-V24;>l+eK4hyPV1=4o>jOY=3Q?fK(v4@xVUM2Ho?U+^ty<^Nel} z5%ZS5DZ#ufa{78LT?hSOZLJJF*9N`)mkwck#V?f8t2LRi(M5mZ&DpLW15TYdU{!j_IjXmeVWDS17&jE>4%6~Yb` z04lZl!p{q<>V0K~l=_=VnKlFW3)%l-iSEf$dx@%&&kXtB!pr`tIb@ZrCB2}KKFMjS rOzbg`_z-X7Evn1*3veUN4Nd<5C&m}Aw8A(000000NkvXXu0mjfnn4d- delta 1264 zcmVEX>4Tx0C=2zkv&MmKpe$iQ?)7;2P=p; zWT;M7L`5963Pq?8YK2xEOfLO`CJjl7i=*ILaPVWX>fqw6tAnc`2!4P#IXWr2NQwVT z3N2ziIPS;0dyl(!fKV+m&1xG5G~G56v8b3zuZn?Jbf6zy@PA`WW|lE0NpZ9sU-$6w z^)AA*TA%xK^r#t&0X~st?1$8Vf-E(<&} zWF`~y#35oa*ThN_v%IMhPZ5VzO{aVz<*~|ni?dcNv-&;x3xgSbWtrRA0S2B7*_2($PgBTd zf%h}|rZmuZ3v{n}z18<|`T!)UtN0CYa0ra#D0{udyMH^Id;9lHy}utmcyf~IB5y4K z00TNnL_t(|0p*r|NK;W1$G_@a+X9zTt2Bv3L5&PEStUhjVEqv#=0E)r{WK~hQc5%v ziXtqGX-57bL{LGIN>E9qwX{NLA*n%SsaQ?y2RcpE>1OBdYHzvs-s)-iM_+jC-23j{ z=bdxUJ%8`0Fbz`u$IzI4X;^tO7dpdY8u=@J@+cgz+ge~bTZ9({r5JTcKp8Np^afcz ziecmOfgfh*Bav|W5>z@3p68jwq0Gan08Ug~R<6rqd+(?xl!sJW^q272K-UkTZwQDW z`zzT!gI=I2%^au$34b%b^JAt*~!d!rcfDn|B!$q_C4hEp>zl2#UuNlt z3=9K@D+!dr$Lk^hJ?*1i|Ci9vK*4VL zCJ%Ip!rJ3=@R7cuRPZewJe(Cx zz!eMs#>)914rM(JYcWJr-@z|;#c7+wR*J|uV#9EMmpGKQ6s)J2YBiOWV5r+E4rN^% zYog+^a;U{HOd7^KWWd_YeD~=IdrDzvT^sY5^)!=dLD?R%kh*y&4jmWk+Qyowf1zpV a@P7iT*Y+9Q({&yI0000pt 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 diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png index bcfd804e138836de91fe5c0c42e7c0c0403020a1..b21169255540257d6df62d102ff4f4a3f0d9af62 100644 GIT binary patch delta 3092 zcmV+v4D0iS8JHN5BYz9uNklHa1U9?NH2fz-!U467?(HK6N6>(BfG@n- z(Df?($9{>7^?ygg=t$&dUehY1O|EAzZ-hsG4QKcE*6i^k4mP91$G|(hFLp)^$L=W` z&2K14uJ4#G=zl(U75LPzL|M3phUgF#3->0G*gp0X+{*d&qld}m#Ky9}oNV01M;(sv ziZkn7#+iT{t-N@8W<*EfNo62R*C{uI3Qr` z0-RX09a)KIIk`cR@50GF$j%+BC~!lkS@WWhncjMFdpvhm{@Q|Q+xNLLZT`flD6Px?|xKJ z)Fg84?%i^909rQyf|~%(c>{H70^j@yEJ`8kvLx8;cr5@J$OKw`0g#prBxeHKjsaJ0 z*tqkS;C}#sML>bzL6*EQFEl=?+ZZRI=QA&jT~U9=z`9%l|JZ1d>hw zOZTydvmeO2ZN0whfnCV$3u!|?XnDY?y-piFMRlYPk$Y&L@j4vOebK(Gr;t%z_cyE-NLfV z83~(^O9cIyP2~@}0@bVPFXzC7s~aQfl9?PO;`G9aeZ#+Y=c4DWmB7vu`pc@q>g0OY z0hSK}0vpMduU?J)V@9sCzym6RZ+#$P6fipxn6!b7;sUw;h`_4khO>p{-vgOGU0uG8 zDt`_o-@B)!Lg=ZB&7k+513EIzzHc;eCRe^+dSDfDNeuig6d2!`sacL{brot5sXFRt zA^mXzF!Wm>?uc4lrNJuXe)%$!Z_gN0*VUUP-^*nxmYc$CAkbpDZWu5!23U}+R+l9h zPVS#Vj3f3Oo3__9bk&R30WWv;`pI~i-+z~@Y8$LDf(<3NUmK>}hZ|R4%t7tP{y`j2gJ}_inE6|^ih6tX zYq9oT2cDm2tS)Q_xi#4r{cbS-!YatV#^ql$v#d^BwtOF%wQsP+=mSQ6|F*zDe}CZV zMqDqJ1!)jdd$0!@Ctrs-3?X+W69qmV^5rwHurIcm`vmR?XaeW+fG>9Nt8;ViZwP8E z-w#sL)*S|tPs-O(9LnT|`EnCe2B&j4sf+iht5=B`K>N>uZa%;iHUisAkW6~;k^w-# zY_5^0LWx`w2Mhbj$=O&oLIy1Za(`~A^q?f-j{;kc@=^FnC?`w=Pt@h+@`QEjeNiHp z93#Jzh;&%U`4Drx{+SAJXJHo45rgiuG*7 zj##Pf1UDM14)WRY?O?IlMZSx7p9LmvP-|bB+;Bgjc1^J|qzw#TVH;wLuz&IZQ?Zi8 z*M*@$OgwB>TMMMg?bBN9esZ=9`GygkC)=VtKHR`vjv|A>!<8@As}ISyD@ATKrntYL z)8SnNQnNPxEYq4g$RAAlD=oevpa)`ea=ZG7IZkfgtD*mXQs~J=SRO!nHK~Gc2J0`=Qb3HDLzrB8k|Kyp6K%n|NH``& zZZn1WPhoOfH4wW$$%ebFday@uiZ^;pzAa<L^f+Xc`{9Alia;aesoD#JBVLmb)I* zb1C~COAscvvA~nO#ANNQe9;!H3(^|S%qiZph)yadIWffiW3UJ4}U1;mPW5F;<7}bxBmp{yNpO62$M@100r0S0ZqSVj|1uDNF8&j z26zalS6UH2dLc}17E@zHQZ3swEMMf3`7W{rud>x6Om6ylvHQFD04q{OTd^K!3tj|d zbjU)O-2GW%_xFAZ7!kws70Ls8JtcPk&w_)gIE2X+wax3eaDV6eCOhpy6bA+GiN-S= zIxF9vFu4>RbMi9K}84;V)fwu(QcH~^Ah2-!#nbwQ} z!F zEd{n8SLuN^NWN2^gNJ~FrU*1zDS*X@cs3<+51aw!C-I0tQIJBvZ4@IRg$KMDk?hgD zHrGmM;gk*Gr|_ltboIUvl%3_1;&`T{-fffOpa7HMOr?7*5Lm$$$ml(6Bxi`LKjfvG z5W*u9Rev1xv@P|{f8@)XLz&#XJ3Iz9k&Qx=w_lpUJ6Nz0~U>vwB)68$r+VMNiB8q(yPtNS@+k z6ewvW=74ea5e(Zyqik^68750ebrcaOo{N&Qe}6x-NW3|=O``KSk0 zA%B{wk%jRD1;8}p$zlr0rJs; z<{XmzrO{SXBG35vC7v@(*_O6|)z7%0gVb7m3Vhs???{N6%37gE*KdoFON;E>?Q9lp zww`>|f=y0mB4c1QvwY?z^0d*xf%;BqB7e`BRxsbyTl)M4B&#V@pUxIj*-?-MkxTos z1}zh%gVIH5Czu>Q5zM-Z2rNCAI_0Fc1BLWbdPrIzIhvBmO=)s=+j69up^(U>v^$%g zRet3rPeP%qMO}VeAf4ez>HHK8LvoeG$#@IN&6Mfhron^5-)Lr#PlEE?;tm79*njB+ zyoYcPH%#oGQ*70+aDbonLD4wri4QV<>bvsL)6!yl$)C|}0r!H@uE;M}Y@70`3l7LO zAuII|yxQ1}Tyuyx&@&XGVXThq^kXJmNZ5M+w`M9~EUxU^W5VI+)%SnIr}D=)jK%TT zcoQxjxP;^D=c8HpID7Y}!XWR=UVmh)P2zg)w$YOrxv#LlB)0P^VTJ&TgM)CoKKC#y55tE zUOsI8#*du=I)vXZf_`?&fc=Eu5YDH@Ats_9Zs+`(0b=G`$YBHz`53uZ(|-{>b|HJi zfa54DJPMd7n8`+LWN~5%+K!u)aVv3Z%?@;)HShk9_*9N!ZRDNVha|Rmr7hX3^|58I z*y+S`SRT-aejN#(AwAG2BpB{KEm75#&i{0w-$AfepUWW%$yY!oEwc9=!`1XX$cT++ i>ef!C6EX>4Tx0C=2zkv&MmKpe$iQ?)7;2P=p; zWT;M7L`5963Pq?8YK2xEOfLO`CJjl7i=*ILaPVWX>fqw6tAnc`2!4P#IXWr2NQwVT z3N2ziIPS;0dyl(!fKV+m&1xG5G~G56v8b3zuZn?Jbf6zy@PA`WW|lE0NpZ9sU-$6w z^)AA*TA%xK^r#t&0X~st?1$8Vf-E(<&} zWF`~y#35oa*ThN_v%IMhPZ5VzO{aVz<*~|ni?dcNv-&;x3xgSbWtrRA0S2B7*_2($PgBTd zf%h}|rZmuZ3v{n}z18<|`T!)UtN0CYa0ra#D0{udyMH^Id;9lHy}utmcyf~IB5y4K z01CWGL_t(|0qtA~RF%gS{%`{oGzxmeM589|fO`cJcVk`R4yH{^qB+sTES@0Nwx;K3 zJ+(QB5t>9z%~G|sN7TkdaU*Cnt{4T4OQR@C0#zwuRS?Al=r{Zt9#4G#yq}r(0N;1c zJCEhgKYugdf9KA-$#)Wvk@t=n~|6Kla+^ABe!j2OY~b9 zLsG}`w;Gn7Q8`hQ#5Un~6Mpwbj~OrWlZxb+>3`T2{iU-HbDrGpQNKjLm{t6#k<(?X z9=!NzPlC5lkz(=nj9Jv;BH6;uk!uk91%L8!vXNCCHP9|H8h#JeMO^r+vW4WyEinZp z8hgJHTZ+K2X&AmH8Jj2kO-5Xv+aZoo{rVwbIb%~$&Enu@(L=3z_17ehR(%>q+&;6a^xC*tOs}Cg^uLJkK|r+ zBqnQf+bd{Nb%Gaz3=uRPawH^6ZeYdRQ|-{)OhA@g*Mzi%z$5;^*btykE8v?9VE&G~ z#hTU!-WtYmswQwa6G%7(oXmoVuUrXJPJi5=xFMn850lU!U`S^m2s{_25 zU?b1?Xxqy`@NK#0dmG=!&IA8S20lN|p9RsCVET0*xtwPQwg!GX6d2r=TzHi)HoPOr z-3lWRCA@t~iM2&=bK^5-1dx;t{E20@oGvZr4!DKfW*pYoum+SBFMr|= zNv-@G%h%xZ!rK5FrvW=o0xzuv(ypjzwexT-xg=>EhyO=U0sg*nMTA+fMg7`x<-|xB zM6EwE3;6wdU{MlWuEj1TcRo$P?}psl1e6DR*&>H-``z%R8To)OvsX)C+BzV=(7Eyz zflJ8sAwlnr2d0KQU-rhWvcF&d(SKSSo(rQndH{%64&+*VhO^L5E;Vg&B=AC*wc?jW z3}mCdnR(WmGK6O0^U0hfKLKt@XJxHJJGpNSwN9+Sx??Cq?v0zk3A$aKO5rBWf?SPIB7FZB7^J)2Gc{En5`)1gts;yz>srw z?j(5;ZF`4ia%Fw!(k-EUzkkK@^*IYI zF+g;G!`<_d=vg1Ob0Pzol}9bqDFOB57Rmdvmr3kUYR;jHP@+9~G%NGA0ejPd_$h{V zI)WGLj2=XCk3dTtwumlOfLt-MM2F09m9zbn)g{?RZMaSoF*Od@ z^@h0@F+Rk|ys3^Wl#<(pGj;2x=5nz+fUJC#HJ6WlnVcw3(5?qNmTSv)w_#{}MsfB` zDY>IfIw6o(0Q_Z_%70oNmow?YCZ)p}pGR8&ezs;wB zi@88ElRR*EE8t_rX-Q?|`qlx0BpmoecGjhNRo1nebw-*@w7taYhD}ml1(cE7f|}8w zo?{u2Gf2r$Rih+G)=3F>FR&pe9!tEy2Rr%ZQs0p1*W zO}shL-c(nwGI9gzo6CqCLeUMleD8VIb#obSMsp2nS5k7lCBnyBE?iMuNiF~DGeGDbJ#Emc;+8;<5WaPd`ZuS01L+(6Q>MfzXE>Lv}sKJz6Cnrf7k>JY$vz9 z=*eK(>NkM-TYxnuEY?YKS~mg~jRS@U$rls5dw)LMXR%HRXxVY|4QJkkJAhe( z5lusoiQoHk5ijE;&x+^RVFo0LBzVD2VBhz+i)4`+oJe2!Ws=&BK|(DHN2HeS0XAzs zkbk0Wm!sG>ASSCrQNRNdiF8}k_hwm6ile$vyBN*AZ@Vf>6R?&qU{r|pwjDUf=Wc-Z z^rzPSxoHfCCy?OvQ-QiBzHfOD%SMxWlU$osS+U|VvI4}4!+@i!%g*w|v&K1ub(4*y z6ldXUcT}pAzjcL6{Vy=!Lm=yh#RoVEmwy~M*-ZO6Y$-MPMY)K{7WUDe)uosNe92F8 z6kU>7Pg4cg9F?M~?O^8~ivdW-nGZ11YCL!?bs!t zS1j=7VZf_>u{kFs_?%9e};h znk;tc*pQQx2Yk!Fb!k!hH<6nVs~{^61Mx?JJ?Cy82GmQcsgaQ**SLbONrjtZ4|e{$ z9ANfVeyGi@8UuaXFz{f+7U0MKF@IwF*na0B%S0|Sw%ZG{a^@nipMl1nbZTi4N(t4k zt)_d2ZB4vpuQCZV?}v48K$2+ENw z?vG85c^y4whzn6F!R;|_L~h1XlA9tUD(~m+icW-Iix{+voN2jXRUNq*#}F4jOIF;u zAVS<9E3P>W40{FloBhqrIF7HQhj0|<$`)~s+`P);UQNe nJmhMGhg^;DkgE|May8=rK!>@&*Jt$z00000NkvXXu0mjfh9?Ve diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png index f2413d68ba14fc7f79c44ff7255f7dcef8389dde..e8884f40b24fd1f3859d3d078a3eca505fa2036c 100644 GIT binary patch delta 1324 zcmV+{1=IS|4897GBYy=2Nklt3AMY`T@}$7cUmTWdPqv>BiyLnnd`bEUQ%F%jgd!EA-ah6$9&)SG}I-rMt> z*E{aL?>YCLbFKrw+uecp*ZrL5eV^xf-}fr;$u>L+Y3mA*>VGUmf;}BJ#8PP#21j0a z;6|s*g^MLcXxd!{PoI}76t#L}QY@BKmE#e6&L9B=D~b#fheZ1V^^@v!qB3t8dOO^T zYPqEZmpEi_vJ&mts=@YW<|^v6K1gsrF%z*VWX`oi&7v#v2ZbEMKGYn#B4<_Zd8;K& z83!zS3i$6X@PGB0fpX_uAbu3^(;vY3wg>=;jlI}ZDB;G-W&-cc2j(Ty|Mz!))Ritur5w#Q}#Lz>XTS{(oWMuJ=9y7Sfv9O_sBTSPbsl z;(4o2zYQ;kjajtz9|GR^2Jjew3=8lJ(o?&jGI7 z06wUS5OBh1pma4gvdMK`di(w7fiF%8K%Z{(0?%&)(vrwpFA9%VqUHX)CQmj4Kl~m6 z;HR$w4}Xu5%e%UPf>Pi_vwq*E4&d#Bz^+=L{4EMe;{)q?y07UBKD)65yl6ky7DxvYl*aL2!q0bc7CPBwN_ zV0mzQ^p_^!XpMAcA<=89yH5txOP?%FK&`kmW`x+u7)+tM(t7#6e*N~EfIJn!8li|d zc7OT0RW#wPaUY>!sD&E*%+0#T|<#1tEGam(3%$Cb9w24e(3XoM)Cj;{I z@TN6mt9S{c;{OYYC9@|{c?BTGNdy?-Qpd=0rD{Em|Q z_8Rde!Q!~c7T%IAHl8c?iYyT`j4b8^a%P0yh3}ykJ8&U1Tv$NXW_ruXWR2IK(SNRI z>oUy3=>*mYy!-@^`()@bkJB!C{lV~Xh5-9L!a5}d+F`;cwy65l2*m#4V}hehETQ`X z$l}5H6jnpw<+Im-*bopsI;T25t9*Qv4q&y$!2AI@y&Ae*;2VNNjpD$=hao;b`oyO+ zR=Yc}&);qlvbI)hr_eC+D(cN9UwvW&k+4? ibpFzyE;+xSA^r!pg@NmEpq>~20000EX>4Tx0C=2zkv&MmKpe$iQ?)7;2P=p; zWT;M7L`5963Pq?8YK2xEOfLO`CJjl7i=*ILaPVWX>fqw6tAnc`2!4P#IXWr2NQwVT z3N2ziIPS;0dyl(!fKV+m&1xG5G~G56v8b3zuZn?Jbf6zy@PA`WW|lE0NpZ9sU-$6w z^)AA*TA%xK^r#t&0X~st?1$8Vf-E(<&} zWF`~y#35oa*ThN_v%IMhPZ5VzO{aVz<*~|ni?dcNv-&;x3xgSbWtrRA0S2B7*_2($PgBTd zf%h}|rZmuZ3v{n}z18<|`T!)UtN0CYa0ra#D0{udyMH^Id;9lHy}utmcyf~IB5y4K z00fFjL_t(|0qvN7OjcDG$3G|nqJXIMgUZbIE|(!R(Mp{WbFAi4Gqss>nx^&-tvNJZ zma8>cscrq!aA{hhXksE%+M2Rq1qPIeIyN1V$)Lc@#1}O4?K$3;z1$yo&kHX?Ti=bn z_dJJl?|-1I+Schx!IJ?UeZuqKn3d&f&FriY&2Mr%_QRUdpv z&5ja%!11eJU~=|mxf;+zU;)K@l1Q95pM;cGIkIwtwc8G;Ja?<$0tzEFYd!TPho~+3 zv15lFP)+Ntg=<$hQ#P#=z!_$vRmPg@9mRVdw||^}*K zvGD#(X!r}hIt7Q$z~9%k5o2`#T{R>MHarcBAAx@T4BN1112E$On4MzSGN-`lCRnu} z3QlR=I;`7(DR)BQixNxmX8XVh<2NS4Z1*I<=Sv{B9G2%p>$TvX7Zl)QBcN!hcx#Zo zuYaY@_`P_=-WMi*Sug=^mkFHlktDw$AhiKK!{ESjh#P3{t2_(qzBaDEkZI2S0cOcG zM_as^F&ehch50*hn9Obf4v=-0x4;hfZ7r-Wg74}A5B#H5ytNBnDTFME$rVpPM2Dx$ z8z)nK9QK|FA>iz>Fm8m|R#*+sY=`SU9DluW-V19EKt(<5S!hhNlKNenA%NA+781SUNFaOTJ6~B-pANLjV{T327d)?ahM%>2<&P4T+at7f(K) z3WNM>Qh9OuLm>i88wHUa{_EO55(Jf+aDM%E8NR83nPUUC$@l4vbp`a?W^Vs_0e}8f zDMd31+jX+h3^;PA-Z)plm?(3*?J7aTDP3waw}(XPjdKOOVo9olRjTPEa&IQ6HBBO~!(Kf1VW}=95;OO~nP&UL28+km zil?*E?Hn76_29Gtnjill^zgJgK5PyuAvVN%zAWQUDa^z?@{G(st6})3UGU8Cfdi zfJ)oSjbbxLYPwiUUW@VvLA1jiRZ;P=)QB(d3+nqMctEvo@{U1?Oyzsijc)(Hs8)7^O_mdt^Wwq9pXrQtkhkHeJD@tqQD-GxnvibE z$I-XnwHob!>Xa!VWfiGe*^d1^BOfQWW{YzUT05Kpj;!2BeaYb)XFMhG9{K!6N`xNq zsF%w!t;%x_aGe7xsZ%D)K~8VzKYYU4N7Uy5{{=70F2e@Xs(&VP2kd-gti@3YU(hRWbw6KECHjeo9Z(Qt4#_@O1;4Wz4D zrTwZduG4i1#mK`CncpBcaU~8cUx%v~Zn)}Tb9&b=$cE6kXxQV%!9Tc-Rnp#t;E53k zAK1}2oUd4f%{+>nPv#(d#>co+P+YNtBZ0y8APh`PLY;;^+#&H@51(31(Qf=Kw26sD z+Q>-cryeN(@qg_EHX3Hf(8Vd#sj*vytP1e09fCp2vXC+18SG!Mwe0pS0(s`$Bd>CJ z2i%YT^T{Tj?2NqBJ$y?LSR=?A!_rpXBTou7&}Y$NEU|aO<$_{CpgnFB0vdMruwYdO z0l}f@o-h$>W2V|}x+WN7}_iyT*ycJ~S3>wF;Np!oN?%TV8PRCAt8un=L*f!D)G_Kq9gq&CUp5=`LW_X5hP1hDDZ!@73^@ z@6Tp|?PVmHh_Ok)>hI+H>cA2LS*=oj543+!y|2y5lIG>*c<)YVQU^#LEmV3^3UJM$ zg~oIOLmL4rqJif1%zNhb>w5i|cwVp`3I<;I0w}y{o=jyhmB21dfeo*ysa!=|a`H!B zDSy#_9%C@6bJnZmF3knb|7`486=52IkJHRc{+%)M7zclHym!uoKbkdzReIn;;IhNc zhCWOouqoM_j6VQ%{f$2>ahK!0-QOF3F5^iGTK^eMKIlU8Y@%TTd4*j%(l~kSK+nn~ z`?<&QGmbc3Tq^v?%6vi@JVg%pTjV$XIe*>QbIM?tz?qRi*JgTsd2v`pZpKFxMQl7& zdE3u-imPn{Ahs(I(^21COzsV&{TKN6QN8}wP$c-w2ZVSbS7}2NUd*07$T5opQ?|>@#=GnX|+CRWPfC_ zuuRJkVc*gbA~se*v%1107*%C$0$=JZRyGxE%@gEt0>CWr7c%G*p_WtnI(NifVJvCS zM_Yj(FX@l3MW8>~o`ivFeYPJ1`Y&+q=NSc?gm>1Fk4Ne_d#HTBI<)XjBoh#yqCd74 zfg?Hq^&|rPGW_Qxsn~pL$SaRIS${wy3M1nq8U&A^vYxMSHRu^)lrOLd@`_Og1kNC%#GU! z5ZX8U|WSie&t?OQOw6T3bC%r-&P(<1cua?Z_81Kr;&4tZ14@a zaFaK#YWt~cjS_(zKgxi2!+(ySR};hvu1irlUqgLeciC6KELWN`Mfnh;jv6h5`62Y=-Ls4kG1V4KZV z60*>#?ouL zN+fN3c_ zN95aYKCCy65`jDDTYoAl5ygx$7~Rox1isWoKC<8%>$Us!#!(`Wi*h(c6#1xJdv5nw z1NW!&1ARsPv@EzJ_?TwwuAI_ev|XM+jz~WJEzs>l;Hn#KA=kdB)atCx(H~2hKvqdE z1d{Ow8N3q4_XMV{R~<;QZCQ<18Th7U(3yPC z6)~drMHXCQvd4gl(b^ZjSp`qC%2%>Cx8t(ksL!k;k+&Pt#yBrhcLh!~Of*oHz|bO) zBa&&PlJEAH@58fV`6%F>wZMCsK(U6?>I`gWS!JgUkoOMCkhBNbWw3ZtySOMJ19+jm zTAH6{BG=SB-G3H{{fBYk`4qr{=YVz^rvW(8F?o%#@fq3#a)~UT!$^-+&;RkEo1No< z%tOGk-J;m>dr3Ejo zWjBYsGnawh^MoI37EC$jz}tsUeoXOXtO(tVprI27y}9IeYGvSLaOB^B!)J^?t2|6Q zXv3}z7k^P^#)%plT{!dO{0;80uxt@?f&C^d8WmycQ7;y>(`<3Pb+G1(RGd{)T{Ez^ z!llK}|K0qVR|eA$8?rhNT_nP{Hx-M%8O~Dhy8Fz0ihW$NwXX!Qqz4x(A!1Uh$l_G@(%Zka1f@7( zyZN@-#^G)yup-V#Rs@_ja3sqToXyU$AvZCJ2z=SovQ$arr*9+zm#;_I%zXINYUXKq zsu)UeXk`-FKwA6xJ<09Hzwc>@suYf-e1Cw`*{6gbm_1|e&G#hx)>b@q`zu9pVLmpF zoyBELpE;;sjY*n?>w1srJw}sViK@p`h0{8k3^G*-Uxo& z4)0ox`L&n$CI5FA6rQ2<@(G;VzZZGwo5)pJL94CH%EEX>4Tx0C=2zkv&MmKpe$iQ?)7;2P=p; zWT;M7L`5963Pq?8YK2xEOfLO`CJjl7i=*ILaPVWX>fqw6tAnc`2!4P#IXWr2NQwVT z3N2ziIPS;0dyl(!fKV+m&1xG5G~G56v8b3zuZn?Jbf6zy@PA`WW|lE0NpZ9sU-$6w z^)AA*TA%xK^r#t&0X~st?1$8Vf-E(<&} zWF`~y#35oa*ThN_v%IMhPZ5VzO{aVz<*~|ni?dcNv-&;x3xgSbWtrRA0S2B7*_2($PgBTd zf%h}|rZmuZ3v{n}z18<|`T!)UtN0CYa0ra#D0{udyMH^Id;9lHy}utmcyf~IB5y4K z014qqL_t(|0qtB1P*ukn{vay8Fe;+4g5wh(7|HN3>S*v0$Hu0uM(`19T5agGL9MBc z$uyIwdDS+F$@BVii!4F$#^mTWjzQa$TP52442|s~0;U~~0`~=#BpFo@N zp1_d4K^QpyF}{jp>A}*1B|uZZe1RJ*XIOS)pKCjcv#)vc5bFen#kWUPaxx+kllZQq zH;vXgL?m9tg~R{Efu$~%94l3=5g7gaXvDi#^M6$vOZDnLS|KEIGDa+!h_GRckdgcX z%Fk7)R8%L>ATN!tntI#IdVoM`;~bQi79wNDyDAmb2o%%S(7c9B-hD(RzmCGJ4ai?x zC=*p9Fe-Tl8s4-uLonh~7LmXNnV_7&wtbsYt)JD>t@?!E@NpP8Zz!@C>@*XT6F6x8 zP=7R()z%sU>8Aa_K7qghO&zL#2#}9mg~09_x>8>u9lHvFEi`n*9Cv_zG!oI@0ycgQ zT)tW3dwOJjLV?JKfP;)3@=D>WAhe}SK&2b{!!r^R+6)-o3m6^&3}qnLJ%mOl5Gc9~ zJiZh-cK*KjGciEwB%m>STUiO5qD>z3xaaq4HFpZzeP+Nk1P3%o_~^ zHU8@NVXcAj{eZVJt3M#w<6{l-1O&<5+OiA+rnB6-4g7W+urLECyX~7XxWlIe4(|di zo?uKxGqh^%_J$hQrIlw{LF~)lrk2v&&sqnh9o5iL3-AenLDZVxJP9N}#s&lB@_)^n zxV@nUwrJu}^#2H_R<2J1*5m*`C6QNddEH%0aGbyg8T@UU4h-p`UZ*C(OFO ziUazG0pIx}ZRJ(J7-CBgg{~QKA7$uc}I$Y zza4b@fEu{`6X1J&fTjwz&>iiA=~ps}k6T2WSkt%E1pNfkUM(EM5ZYq=vLhc@xrgK( z0IuEn@^|U<`FgzpOrbyc3OU6W1LZa%hZ~&cOMkNI_fe~|XEQR9Xk!Q?`lNx>_HEv`SsU~b$cSNLtX!;& zF3E(I_Q-R`ImW?HuL5>+#eG1c#gn2*;8wm+m?5grMxfYDi3^v@&1962r`fI8a~g1M z2VNLuE*HTCC6;~6Iis&_Z3O->+K2?qu<;OWA8PP)E@NVYcS5ZUX7e1KNnKRU?_4~~U6o~Fz z#|V_gsh6+mwL50`_+!CO-iSycm8Zo2`W8q^uS~JJUo^qK_|EsS(Ed(AeW{@tC8N(ocqd4?- z(hhPujEk0w?SGf%ovK3%f!mJ*^CU9Q!~!pG0xneOXt8#T=>_zV_@pIQ`Ga%D)Tzyc zou`1aUjQ9~JrP1bMvqo?L z`Nr9VZoI*^<0aV-BE#W5@h=dKW<=YD>XKc+FGg|&G=F(w)5gF*rUMVZ&G7Duu9ksG zk-)E>kjou917y$S=t7SlWrOigEQ3E;xzn`e4RI7#d zY4%0+9V!JF2jdA*ayvAPQ|+VYfXS#^o0 z`gyxf7=Ib3GCbOiQ|d)v;dWpdr~i3IGb%(1kpbaA%9Ft0p6cAsPY2F?q1J{KXwOU# zM~pjW09_Tb288CkK8E(|ao`gYzvd9I?z7rmoyZO}5A-F+vh)wP#={0a^PMczkJOGL z`dOoB-*TP@S}Uff3m0cZAEUfULWjNs9JzS^`+uplt1G6;PyK4co|A^DxM|nkdVnL@ zStmI73Cp>t{KO!~3PKoDkL^bScT|7CflnLk9eGF30^d({jywU!@&iAlua~d_NSl5? zL0lCr-FK7k0DVo)dOe|#i_`Ew(7`XaUe^j7%~0KZgwFGN`iK*(X;94^X*1ulHe9|= zEq_im!ZxSjY>qTZTb4~%g;RRrca7slnTsRvC9)aiT;Zb9#^j9S9>ihNa=KD){tGDZ z0eJ$>XX%rStqk9`x`3GjfmawYgqp<60@rKgNME2vb!lr}CnL|uOxRltpWfWy)BlMq zrZ%qHPYof7(*_x#U2Qn)zTp{Ytn4u!o_~4zJn$-gyp=hItgo-VP$5w3KJq0#?Kx`7 zYn*0NqJi;rQTlhfA5C0jNVp~6tpW-8Ggss%A`Dw7HUvr;M{hf3Bw6k}VeE=KbmlA_ zy9$Ah0(?%byDeCAT7TQB zR zU~%?!TsXW5A(2mO=~n$g9`-FtmkFt@!s14s5lbe)C+R_T0+rb7nt`$+X(qQSfqM}) z%*CnvOWR$m4=BuBiyd>6^7+;Mz<-S7xhO9!LR9i=Xt?&U@-BIqvy)USs1qpek`mV; zi5q?5Q(OoRe+ms}N{h@L+C0}xWX<`|N=56Dh`0_Ze{CWL&KrV3^QXeN7DfCsP9%5K zzw7U+=&I~c2RS;gkng!iFb_JUn^%QqbU8!TklI1lok3 bK%4j!k-wcK(^1=KW6NHg~hGz2PJv@8oz(##qwtz52-PFa~{^Nc2F<+zk(E{zLK zySZeF8<0wAAJn3kAxUe7}!-Fvy0bI$jDF!%R7w%thtT*osDyBoK~26RUO|4h7r-6o#OtJZ9TorFh3u3}WT&sj?v(Y&J$TNa zhJtH3!sni_zCzq>8pZk@%U?Sd-^rZ%Py7WSroCxF~RR z@;C&}x21tF#0V`HMh)i=QsB^zb;Y)OkgeScpbu&p+Hn%HGY(0CyCe@mz=%=2`-W1))sTl-9nV^O@Wwe z#S~P)L^Gg0@b*UF;YGmj=RLP^SsCE9p1`0e@kK9l>3jQ*0iW&wKHd(j+zp)dD+b3mcY$xa&bM}Ih-v?^DEZpBA4pCG-W7tM^D;X) z3C9Xtp)4>W1{mH2s8+%Mzi&|8`}_5)`QK*ytAT|N0N?xoys!dzC)4{f-~=2gaKARd zgggBkv4trYB-iHLU){f7Q{n7z?Nz|sMET_#_5rYVZ}HnQjiUr^R0DV;0f@a;PkU7_ z_bhP5a_a53ssp4B7GiG(Tpt3*eo=2zH;xf_U`Jqb5AnfkbXs2FMZ_!0>Ujot;Qial zjURXJ%XjK&R~NPm%#rkZFCekC{`HUb@HUD} z8ZT~%0MdqtquA@ffuGGi#}{l7xI=wl*(36dYm(gGJimAHS9!)YMbi*q-4kMMo*|D_ zc44`|JWH;46sU5!+2?Td9?kXq{z(tJYn{*{wp#46qo)Gj9W(ztcUUTLliFfhe#E@E zWY1CHH!st0&I#a#Q1kt$TNPM22<4@O74VJ2Xj31-dmgw}UTCJFzsq)f?zN z&zk4uV~N1YJ%Gpt=AXkI>+9*jdEt5f^Qz$1~;$kN;bp%YpG>E8pdtTX?t ze2fcxcQem!IUW2Ygj%!(n6nA^Vjtko@s?}!)b;Ykk(|&Hc%Yp$T9|@1_SJjkEMo;m z1*SotR;t1dbsZc!4!kU;;aOi4Jz9N%(Hwj|y9~HTd?`o8$!S$L<;5pHFA-=v*_uEC zqXJL4Tg_|Z4u;nJeWh>HaB<-A>fNv9tA`MJ(p}Pfq!TU&e~p#@AEq%TFk_wiw9?z= zhVOs}7g%RFU~Jd2oxs?xLXbG`Q6&B{=4RS|7W7l|Q5U2_IpC!`fWGhOY0ncz1g86}nI?AcrNw8dR+@3}=AEv$maur5=jWvXQz_4YW^ zjk*a)GG1)UHBT3c?^>(ac$os%8;lSmJL45q|94#1i@XS_N9o zIOXd`o51&I7`@LgcYdaTJ9w6*L#4dJr^(nfp0EF#qpx2?i@*#|?W*WJ|Mo}Vy{~oj z#(KD}Zdy~4rof-hIY4#Os5jeV$+TU|pp6}2z zudl4yBZCN3zUUk%y3rzVR0FlY-+Vy&cS|yKf>7l9S`@zx=4LS<@r1^Jc|B8fUA4bo z=CFL8;zEanioo=~h1Zl{cwHz9e5Lrt)vK!3#`-t*U@8=q~-BEU;>Hz7?sYR-#%&Mt4}SvcRfq=v4-@ z?Cn6+>7qL^U0LAcr`7(R0<)x1Y1wRrDjsuwR{T5*qb%?bipLyUeQ0FgRtl_HEWa3{ znECL7;xVf*$^vI=@B%Rzt2784QA>T3wO@05w6ehaHIxZrVU~Y=sosv=w9=66;5dPq z>Bp^okV58NbVJo%ID<~tsJF56hM&_P%UT_uu%^le+R1y0nE?9K#t-DnYb zjfR5Qk@aO>(^iX$ljwx;&@A4f%6g{?oOwXz6cwlRem*=8ySf1V7wG7bwe)QR)K)Zb z&_wyUQ&U$M&&+FR?pM48F;V>eODlohM|Jed8aR5N(U9<-CPwd1nypwwTj~lU`&9*I zb%3NEz@4-7^vMcXEvKjpKzzYeqPsSMm+VlVrU@^*>e;+xq_HrUh}Cjb;q<)lEA@8V zp-o^`qhWRe?>ACq#(lEnpOeqUv^%rvj%wN{;XMzgD$#090Ov91iO%X_v@D*M(jSPM z=Dj4aUGUsK_kMllh;`>96-2ARENk$|cJ+*RLbKnTM6s^VcUDR%zq`}L40CUDJ?$|k zdc`h1ZMj2x5f<)(xsK{6g4Vm0T%QIku)YAAJGcp)l^|5FWrJ(3s=c`&tT?-Pi@x^UVWiUC=q14IO@MNmTIWTQnJ!r{ zY?H>k9e@yLg+YsZL;EP`cPThnKiVm2$<)+P7}Jz zI?}pJBJe2{!G!nS0nVuU- zi-Ms^ysgwKjNjQX{xf+Yhp#to7Z=VFcQlooySFw1kEM7&T5(C85ic+0u!^0B#g{3? zy5|w&0&|y0V+A)z;TqRtPjEBDwUcJfXT22oq+`TNahY|<0;ZnsGd6`K*XnS`+x-n7 zZK!pb3_L%36ZnC)*7SpLKhkryJ$D#dcJmp7$!?!1HbsN2$wy*=PmbpE9?qFo+>=CT zOYb!YSZh-q5=-wc+f9zq@+Sk$-($#Zd3ORc$=bwSP6gqWKljd-wXf~OmivR#_sJ_)mkcy}pJ4?LMher; zDV}lfdQ*lI+l_5EfU|T`yUDypggDBYG$S!+GvD1q~##fLX# zGn|DUBZltyTP~l|rZ;E$!Q=j4zsHCY+PSB!F@LF~F8HzK!J$HPr_I|&M|~_!i~ZXA zZhyw!%3;~#WD#D>bo^Uda2yq z^s;tu(RcIGe>{ByFv!jA0|)au1#pO3y=BGP?rJpZn7y^21Jnu6wf;{j~q4A#1) z@L#0BIT?qC=&&apACd(So(k^*j>Ndx?M3P>?TxjU2*i*B_ha zivnk5oWNJfebLgjQXG&=SsnKO8_r$O@NmW-^#U%nL8p#LMa6m}#j$C!Jh6v{fc)#@RuXj;802v#3nzF z(8$Nde<@{mq?B0BTsR7)y2gnku2*Mf(T@JZ8M#;(H4>p6lM$UfRGgLXN8l)B7dgmE zpNqAwsp6dLQ!xwQBRP0*UKBMNmFhl?i1;XkM8*lx+n{p22r*UE6l-;5zuF7njXd$0 vo}@n)ClBlvpM55B(pOm?25QqSw3fy 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 TF3bAg4+CJVZ=qMOizfUJN(KeK diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png index defbb2e964ba9a1d8f7c6dfd7862715693da59d9..34a69a42d897d8f29d4333835130d3da0775f2bf 100644 GIT binary patch literal 4145 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 vo}@n)ClBlvpM55B(pOm?25QqSw3fy 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 TF3bAg4+CJVZ=qMOizfUJN(KeK diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png index 0d277b087942a0ce61d24176f940909ae80bc8ab..4abc4aabed9c15c2a8edc64dc5bdbdfb670c1f5d 100644 GIT binary patch literal 6317 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 diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png index 768042695bcf353deaf629b98fcc96981bf587af..d870fc24812b0c699179af6c2404e0783d88cccf 100644 GIT binary patch literal 5290 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 literal 5545 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 Q$p8QV07*qoM6N<$f}yK^1poj5 literal 6190 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 diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index 510cad5ec..4ba934433 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -48,6 +48,7 @@ dev_dependencies: flutter_test: sdk: flutter +# rerun: flutter pub run flutter_launcher_icons:main flutter_icons: android: "ic_launcher" ios: true From 96a09af487783c677f961d9ad5186c51d7745629 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 5 Aug 2021 11:09:08 +0800 Subject: [PATCH 169/422] new icon --- .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 2545 -> 2561 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 1729 -> 1708 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 3390 -> 3394 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 5048 -> 5152 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 6765 -> 6871 bytes .../ios/Runner.xcodeproj/project.pbxproj | 2 +- .../Icon-App-1024x1024@1x.png | Bin 82293 -> 42405 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 691 -> 713 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 1342 -> 1390 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 2072 -> 2099 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 1042 -> 1039 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 2033 -> 2029 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 3096 -> 3109 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 1342 -> 1390 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 2812 -> 2853 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 4145 -> 4215 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 4145 -> 4215 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 6317 -> 6460 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 2653 -> 2651 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 5290 -> 5385 bytes .../Icon-App-83.5x83.5@2x.png | Bin 5882 -> 5957 bytes flutter_hbb/pubspec.yaml | 192 +++++++++--------- 22 files changed, 97 insertions(+), 97 deletions(-) diff --git a/flutter_hbb/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/flutter_hbb/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index 8be536a752fe85973c9443c6556cdec03504bf61..d021a56748badbe63df6a4b809bb1e439df3fa67 100644 GIT binary patch delta 2551 zcmV*Yvo@)>ha}ZOO9TvdvXNMV3sV(vFp3!Vz0FgVwKIM8Pya5QG5t zKHg=W8D@B&`_4NvtoA!+j?QrJ-249Lz0ZA~=RPwg*u&)Gg?|B)k`b1gik>4Q(Y4QA z;#F_)@UYwG|ATAd(TLj9m8dQ)!SU_eP`)7#jTh@}H8!<*#lzDB!{^RI2xcY$=eJpAQl@uR};|nk#4QcD%a< z;K8LU5t@{Qf`8=6sIR%u`llNLkvLO!7b0L-lv^g*5eOa?i$_2G2)R+?QCEFkBq>BW5j1ich9545*RVU1m8Y= z+&zyDi*Imm+QmA%dR#v-VpKoi=sDm>wf#2HVZe#=z@alDKDg%EIsO;#wxNcT=yeD1 zqY*&LU|@6z(8&|{PB`_`*8>~NEZg1_2<&_s=hmaft3+fT}>_B1`#0~ zhgIU@AL`eByFg#D)V+Y(#v8AN+-bi(iS@G>U~ZHYYT*vx%wo9Ps>v zws(v@?&xeOP*-p5J|^+rvquAyh5_^PrC83uP=A3!{DDm~fRT5r_4Bfth3PhaU%%Ud zwbOvuaA3}6psrE7j|zqf6eDa+?sOo)SG}+G4%O76{vMA#01OQVQr`rQpI7g14?_fs z9|+_p>`_I>a#h`!p3}j4EW1l;BWie{(buq;DJ8^KYa*D7rT`I?doGhij8802*aP+ ztlrlg-2%Oo2;6^{S|3_YuY3Z$A%r<~v48!x2daQ4bA^+c2c!)VeowSq&MN$54Df!r zu#X4T`kA9kAlk;~MXU88Kjk^$uUxQsxMCk#A{)x&gM`XXmv)AOhX?TLBoVfJ4P3mc zHf}4r1p0jfa2xY(jFAlmr?aBtAXn-IFCI^Y=sQ6XH@QM4I$AaPXI6 zm-EqXPi}+L1)3Qt4WAY#zXg`OuYW!nRcI%qXr2tW7K6lxJFuty1f4yU1fs~9!=fE> z8MuNq#liqj0PgnJZc8~SrwKHqmyFi6sJsBIx8eLb3kqy$hg>pIyDcT}G8LB@TOh9O zd264(XxtGt9pDK>jwV?MmGm}SAg(^4cG$$y0{5Tz2d zxZD7IQN3o1EK?a=ZhVYtTtuY3w@*nbbyFl&G)5B0;Lz%OFd zizSGReBfKf_{$t!0@1}ikqhL_R7)R(^^hege@K?)FDpN`f4E48lvF;PK9v7KSVQH- zw#sW%&|UsQe$LVl#L|gV?@RWJj$p1I!|Vc{w<;`%5yrRZDP9)S9^|(@nWMjdD-Knd zEh?0b102f^taa0_iOXEf;=Tyb$MDM=Jv+M~p9Mlf)LwZC9Rpi`zoNab5>Tb=+I_&PPt=Z(b#jdaq9Zq_eCv=d=EV<1 zWWQ7TJb$#)(Z2%&rJbUm6Zx%;LMy5#Oc*3nsdR^tV7rfs;P*@UaI`pFfkqQ*t196e ztPuLCL+Mw_Q@7@-Y1g8llLAOv7fYV{kmb^jQ5D&F9mSW1aOKP?6RJx};G5!9f!d5K zmefBrlIIlE9z5-u`PikVtjvUC+qNMrWty9((tj~IQB-6?MebHaXV$>G8}sPx7NO~C zEh;u{GNJKOJxVi|;=vbJxqD6>iUZkM^b#HvyCpr$Jev^R_84IreM9i%K8RRd7&G}3$t0qgozHO+{($Fl{ zUlz(%yoLc&(h-^%kHCog>4!dWGyP1Ua01EC!O6lRl;><}{mn9K{sXB|)`^N$Pc8re N002ovPDHLkV1o18^=$wE delta 2535 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 R5TpPA002ovPDHLkV1nRw=#T&a literal 6765 zcmV-z8j|ISP)6pJ`T1Ht^ z3tWyC!AsPCB2#txdg|FS>Z`+19PEo#f4`cj{&FK)A`dO|x2S%!1i7J^>Y9rASzpE4 zXlzIyl&(5`AN8oax+?zmoa^+C=r~SY)m1;LjVoQH6vJ@L4UWK;>^;#Mh)w{h z85NNlxLP%;r~2-)XdB~jgh3y!aN|Dd1 z?@o`@xWf?z6;%Iw^tY=;@B=Ig_QUL~^G^jZ4u9y3rol^5 zB`^S?;LG?vJM3TdCV(C@yQ<;mO%%-W9S(aqN%hT_a6>2)V*`DVo4qTXUkYCUJ!bY- z2L7fVm5Q{54u|igpewqE>L?MkS0czM>E+=GphbQFsdzr>#yK1kaO=HCXbhGFTOgE` zU))Pe7C?G_JEY_7m^IGfkfAv$1)l=49xm=h#S5Ujn)al*JHI1NNy9_vpWhH;0=?CA zXj@S)Iko`0s|9b`Z*zW!Bc5rmrb82y2-+$U{95>jMGK(ypqjt$_P7PFBN1qhE}_8~ zmU3U=9~>=!ni(l*8XO6?)#*qiPC!3gGjkfoWllTt>7xbEG}u@D+e)fk4u=pas2&)K zw2b=5$=+A+Sz^ly24fV!qz<8a6mtXCTM;v)6vc+*XBIK*j*)Y57o z=wiAZ4u=sAp$^vo8K&Fea2O#4H7I~;rrY6g7$G0!DS#@b+u?8+AqC|qfC{GD;cytD zZ0xf`CkYL!0v+lB={11*>T%^$fK}fDf13%6UK;)TS11cSav5-W9sS;We+9n!4p_Ju zn6(~QunE|AFnT-5juU`KGHL_QT?I5wJ^E)&(tuGn1LalceSLo9HjXO=eE27z)!Bc1 z{=74Q&KKy%Kkfrw`Zq9SHt_x4$ZaJ%P5>pqQ^Jc?XTPQa(Dg!~-kBx;&I?_E4L<e?ds_1 zh!;*i9vFLzni@R|@-Y8%3DE7L@Lyj_DYQ{FeNhl!ZUg@Bb9F5SHtqCgI~j2T zID#|PT>JKx>PiPvqx6lfa3U~3&EfAH>+SdYG>gve`DX!7UJ3l=3gDwvz{C}5I;FmT z^J8@Ho2WPeD9)**?!1}$j$gJ+?B2Ic3DcOulsf^a9ixbBj$)lI(AR-H;5{`eJUv5= z5Z}h;{S$?x6~J+7>TpA2;3n1S18QMg`ncHJsOd4Y5=WgBAA*v~sQ=r|O@SMm=(WPY zkAP+0#NJLkl9U3tqAoDF6VRZlzjjVJL9ZWn?<@JWq-uG8+r#xjmkaf%FnWoaqJ8Mr z2}vb@8m9q6uTdS~d_kLP6j-)3yw{$3qQ7k^fI{eXF>rN#wMI}Q!b{n}yiGE;Z6j#} zP)p6N7xf1!o+NActa3^KVZJt&>yL2lUDpt}<{~vJz6yN0R@S}^B!vLFs3HHA?(&_V zaAzr8Or6V@Rtx42ZUcf-f#*U%UJ_*oNoWDkjo_({`XI{`r=J}DYfmy5mUN;T4W90- zPnvs=0>0jEt}T)zqyTtF<07|zhMw=5q>P8%rQ<9}OVhd*u=IZ5j1KM5xQ z?xfI_piM2)Ezy+mP+`HAsRwtj-n{{6t3JaACj+~FG2J5nNGJi&8ExXNN(2@;czYQE z4p}0A0{+xOpB>ykQLUlonr@9hB#Z#Klkv{=X6MmG;pJe4Tg0G&OHNoV3M6} zjD~~|z#T1tA>H)xziotj2uAiD@UI=w!u1r-QLY~e>`$OLgt!;Led-VMw=Vv;5X6E# z`@?_j&qtMTvkQ8u4!9HuPAmDf@j_e+U|>7oFC9#`qIkCK4*#`3TRZ|3KmpK?XneS* zdzKTo0vOa@-HFsYsJQ7y|Xm=j)`gQTqLCB<)-d?}+7ww+xHIBd(+;Se;4XA!9@X%z_ zt%;1d5kQ?YfPdYh=Ab?VuJ{3i;@|zu`|t5?`b?aRa0M`$ZtZs9jVLlD8jR2KRw<0{a zlXK;3z|siQlklXKj%YvptP^9!3tSUCe-+Sri0PJi!+HT^o&)r6Yq}LB!{k&2F4 z@b!-9-|x{WICV-ofu17S2>(w1=nl-?=xzJgB-RRm`7qzKaL0%43U4jci`7+v zDMmP@;x%A0aOt_g?JaanuOHbgqJjC4M)Xo`d?h}%My(Y9JrK_3vf7oF3(H_+)_8d+53fz#G|+UVS+wqvCLn1k~g59tB@gc+FMS^zxx39!|& z>}otS;I7g0A)UK)a7UnaCDX0aJkbFdw@e@I*^G4pV0JX7E-?*VexLeOcP=j;ohIO@ zF!l?zURtT$L>_CW{rFA7sIr&3Dq)T=+m4d3P5?~sTFr1o*YaDgkgW`Oa2m+pu*v5cs>^hmTiSNaK=WYSH|Ft|?%W z`vN++-qy73RtbQPZYLWIX(LI9lh+@C3Lmwr2seFO}f4IDTm zZ(kA~qH$dix(hhNL%POT(W{aAtfzRdnBzBA2!N-Wm4*K(EE>^$MEplId4W@q_OEIY zl=oQnDOwms)~~I7J3__;i2l}5$<_G_;BE}JcqjFhPb1Se9T+&(uIv}a z1u*hPeWWi5T^f=h@8z+WvI_X$sk)Y!B#i5zGvC{bWbGD*Q2{W@OWNgO$4|f`Q)F%0 z$kU(dgksSa1M~{3Z#n_|PC?OUNqFZ=q|9cKLf3893LD-_)-F9+dEjZw-@vEytIW zI}s$PN;a68n&F;VSIF4oZjcQa5kT|mGIsI~=@Z|)1o!ia6%&@r3E;vicB})$1;9Pv zi>k`l;gQShfLZPR_|<}`oW?c|7{hAxByz6Ef@5XsD<79f<96_rIi{5 zTArPR0>}_5U&{QP(^kvaN+ia7At!()1`C)MATEG5wPoy#UIH9+bAQh;+1F}y)jiXH zTaDBFw^0(J0^o6bGob}=B9!kj#m8%81VB%KD&>K14CY%B6+pepGV^~{HlMZLe>;f^ zOrIcR$q67f>=Vq}BXx5u3NZnc5-NA<@~HUwDn>F;L$ogH6>hM8wjOdOk{Wykz&(MIa`Gh zbwi@kB?Qn^DECmJLhsBvm1S(PR@m0vGPa6BQ~(=x08Io^qg-4~yI)J~JS7^9Q{`%g znNDFNDgah0mw?CkOv3xI1qB%5kqn{qm1NWsIa@^`DuB&`b3JA>Z~};oM%CnOF$Fwoj`!ligdY7MpXXW8KUq7W6p=UcR&xkPM0ptg35b0L!6;2JGWsCxXetul6s zLsS6t`B=AIramyEjCwQx-dy0ntwe$~e5IIc!C8%XG$s$IqzYu;)WiwE3oKvD&rFKh z7G&@i7r@MQavyTrTEKOUbg>La$y`wns8&J77N^2Tg|k%69BnIO}XxyU^e#Dc5ZDh7q?C3ZIrWB9A*TdrX+ou$=R!LBGBz3T>;T?G)zs{ zvyq%V#sDugTpqOmBLetfC2%lL85hhv@N{Qj@|R}gPi^J#4zdwov(*i7hb8eEMg%~& z=;>?pu7eb3R|FnZYk{nfio;k|#*vF<1waN|28>_sYKRrU zY3Pt{vNrb}uxl!6OaSbN426>%dYPZ}o$G<-FS;C}bt?leT`g~S(n`7b@Nh6D0M5mS zF9hxn$l7gKP5epSG*Ir27(!(d`+;2(2!L1xJ`eB{5>oOf{! z@J154N$|5CeKTtZV6qvXo`Lah){O#+Su|0m7 z8N0<{Q~>1BDSD5-N_knkjC&p43+VB7@@$PZs}4MVg&Eu2`yXr3;)IS5LD&&cHB&<|E z`8MGCk-+=QTX2y0&SRnvTL9)~b^y;-{?6YHx?F~HgA@KAJ zGi~q##`gZTqnh*Ak@fQ_%sY#KwStAWym znxIcrIQ->f*-DdI9DjUhF7Mo240_Ux)tGJr2xL&1N3=M@6VfD5EsSqrFHHcUfjO@JQw))dhO%x zSMF1uRWQfVbq)26L8|C@Bye=(E?T@`r2xp3RlrMX%F(Z_>6RRu6U*o%yu2R1L@naK z*5?_qaQNb)_TIL7dUH_~+32}QVXBi`{;B5w2GgB;#aaQ72R~3B>LDe947=}ogd-KR zDljD@-MqP_8>x>G^BIl41vpEfmO?CfW`@3T#S>NwfLo9EPtsKc2emidn(&|ym|=ly zh7PZLF9OZ1c_01>IK_akel&2WY@o#lUdO^}0TjSEUY-zlZ(zD5;nS`Ta91n6bL535 zuZ+LW&z-w#-!@z5B2wZ)0NhUd^BBGDM>pnpf!?NG1i;awgK(U5Ea0iRO<|5+hzkJ_ z`sVP1e*Ieec7~2cEsqCNbzZbYtO?y7GB(G;Ih+6>=hS@RC=$k$5tqxz+OR&Gii~IFE^qk+uxZY@p zYXOkOxj@EHU`%hI-Fc>4b2KM=*weSwdy>Hf{itCF95^u~~`?s2lD=rEz0{F$EfE&7S(5L^YPwkI}i-v>| z09m^oX!e56cuapB8#vB`0Hk9Jj>Wb;zzrkg`+zH45=sE%=L10B(b_fn<*T(@u}Qp~ zTJPQOUpvBufj)PSOP-XG2`7L8-u+A$;dtm0pm#H!5!nQLJalT4BLIVt=)!Gd`a(ZR zSOE~G-{RZ_C=l1W6%)0=TGa{JdJ=HCA`L%YZsQmujWABvZhLi$q7H z$cD}ao|y^kOu{>UVUkJ!gg!)EA8@fZ;a1=ZA9LliD7Y8=PSegzZJEOo?=&|Wl1=~x z>_4c_4VqTh85jDd$9|!_;RkQ8X98JzTE;r)XX6k0u1|OJUBATkz2hAUfcG84DOAnC z%JK6z>1)t5op$85<~l)cEEdc2HhOzKcZ^0YiOm@XeRnw0F-V=|pzi80J^vvA|+%q278bN*>p2E`i zW};f3R4OJj9v$aask!_L_19avMc?V^x8v|Y3ieO{%;eJ4bX#nNb8?<4hkj3Xb&{Ga zHcrzc126jfv)m{PK(h)W@4ic;?1&W5`A_TOftQ`9>!;BfZNm=z-b-@T^lFpdWpYHt zb_!s!x^6bzl6Zlx7IY8xJ0gm%2#j~IwX{%zu%aJ1{(fidQ{O=s~ipyazaCqn|+WE4+|hSd$0Pz%Sa0ZO|{G65MY;@{`|e* zn?Jfk=Y~c)0XPzxd`wg~`~UaS`lA5mXML>}!xNDLwG|Br=#P_zIxW&>jbebFOSM}2pp->q{vd?X*ap*t}q zb8%5WJa$k#SIT1o?Ue{dsqfnO)jEg6M>b)8u&0{$&p*}+iq8s>o4pk;1lyuV;BmDk zcr514b2$8CGsc82M{d@d;$9R+T9hncOx8g3%WtIC1RZ15I)}qY@-a`{`Ag{hB>cQU zV*|a>Jv2#)phZ+ob2$8Eznb&kh51?Y!g+D{0?5tYu0)W5^w1MX!vpG>QjxaM;qZwK z*cQ4@EqE7&`wQMO!{uflz}S?B&~4^))CllI&Bw%o4oBGJsY%ak>IVIAQCpqG5m{=) z@tL0>Eu#UN2k%2#;Lqxrni0K^!x0_KzB&o>gF`Vd>$6DSAoARxIoZD=Cv&iRG#KfF zS|L67h*}@8YOl*K;qV*!^S`1Nx}bGk)a;fQ=>zg5(H0`YpS_g zIrX@ldTh9Qm%}nRkKe2M2IufQ)Z;bS5?ZY~`v&#>61BKphK<=VPj~tsYQzbBYB@1f P00000NkvXXu0mjfEd}!> diff --git a/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj b/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj index 253550635..e30c05833 100644 --- a/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj +++ b/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj @@ -564,4 +564,4 @@ /* End XCConfigurationList section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; -} +} \ No newline at end of file diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png index 81cd1181c6349010de184bd65e68e0b73f7038a5..0a626efe0087f402a0b6eca1346dcbab4e1b8a3d 100644 GIT binary patch literal 42405 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 M_dU&U{qC240aRBrfB*mh literal 82293 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 BGMWGY diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png index e8884f40b24fd1f3859d3d078a3eca505fa2036c..496c47677d3cbff1f8bd81645216235ee7c96dde 100644 GIT binary patch delta 1371 zcmV-h1*H1E3hoM!B!4wYL_t(|0nM3vP*hbM$G?jgHI-cO{Vk@a%ZII+MH(oWppg@uhV!SG9cA|s{vbB1pm~ddR zp4*%8n4^7VB7YDW0#vmCCocK`NNkcfj^t^A+LQ()Pa_i>nt%QZaPTxRYYI@Z(lNHV z1K3&(e03D)AJF(99dweG@$r$swrn6RUfFlgZTucR*}46JJAf_AfHzZtH!6U=7Hy0k z11_Hlm<&MZL~*~rAHVyl>_`l~z@9wdizC2#0<{kQkAHxx=MY>DJ={-yOm{!OU%RP3 zh8>4j7XT5#bWTg`vBF2dl>5j4UT_a*xeWZ&%s)3=0FJi;NipiT`d;1QhJ%*1LNR5F6p#&$gQxPr_#4memWuSUS{&^OfzrH-L<7 zfcd=omkt+O-T7O9n)N`O3uHW5_$u<5JL|jve0d&OreJH=?eQGWu1no~#jDmi{O0iI zcm=rP5oH?#D>|+X&aUdS!1v?>pPk{{{#Yz<=YJ%iy;lPu+g`T(uKMz*cW`!XuT=sv zzxj9^hxZsz%|-lt+6OxD*vx#Nt25U}X#~7KQrUKsEUop5Z-CuoU>&rTQ7-AplrZ%< zLcov!Wn2F(Ryg=B<^@~QJ#@`MILTGacf=EdTQbkXfrJ>@|0)&k+K$U_UU(S8ceM*Ef0T2bow_ zB+{{S>T`sET@>(Z&hkuVxLBgPkXEtkPwj9IlPk%ct!zKlM!TYQ1({5BNhz<+yBkzw5K+&+!|KJ*H()CgSY8W}EjD3hKQ zt?X;>q3CqPOF-tBU!%lee}OW<&!k+h@h#w!!#p;h6FBg@!+0XFF_o{NNSi9jFS^OV z-2yTl_=7`iNLBWQ2LT@}2Hs!96VIGt;GgS2Abn%`i@*}S*kbKsJLHMMjhOPk{eJ^b z9I_I`{cLqJ9t0NAReFMqh|X=M%2>O8Jp<&IjSS}zpp_y=b`co|6-@?#71|X#w2f-p zX%|Zha%r`1cvvHQRMoLB%Prxq=c7zul1k~xbY104u^9aB0pPPLZ82SvM|=3|!sj() zk&V0vXT?xNh@+2HX-t>9o#%8?7=J4=10Vm!Giyz1)>_Dshhd!=`SO{tZFaN(Rw`_WA}i6`5e! dQOw|2*T0ivhZLY=W|sf}002ovPDHLkV1i#eq?iBz delta 1323 zcmV+`1=RZP3cd=EB!2}-L_t(|0nM3zOjTtV$G=>Tzc4{mB1%IswIck{B}s~93b9$_ z{0)}tUYyx%x|Qq4X8%`PYdYPu8K5IWCxQ-hrM4U~5#*}DY=y;!36#jxn}8wS+w+{) zJMO*jIrpA(t^>c@-GTSl{ha50pXYhs_bTwoHarSx>k5$SEPq6TJsmd0QfU+hM_zc~ zMyJb#izP*9+Fb@upO-5XwR&VyES6N2;}Lt#AOQs{iVPBmMEe5ulj?M$GH)4rJKTzD zxupb`IAn0L67AWl!S-k7D(bX8NN_$e6R{~|&b33$qAT(Tg&e{@)Ev4ZXI1Wbt0het z2P}FD`0p<8^?#Xxa_3wieiZQ2AHeyx2mpzVz1UPJ;l|5m0`JWS<|Whr_j><%O#r1& z0*dno+F$AfcAf^lqEF8q(}+=unbuDylBHG=*krkG;#l!a3Yw2;3BZ?&fz9)QjTJy; zlQ}}efGcMMC1jbH;o9}XY~mTMGarw|0f!vGjvBK5VSnJR_dWs^(wf>$ma~Oe4DQ?F zd8<#q4KIg{S+w>a0^axr@ECv$3;4_DJkAIQNySjmbQs6|he&41J;O&FJu3Dh{Eec8F1M7LZw=bnw{5f#PV*xOQ26Q|p zm*1qdwqPgF*sedQzu3B2JAm3vz@+%VI`(j$?oyWp!1eRAr)ERBaX6paZUS%a15Uj= z5I21)%&-W!YK~lXh8F*~=gh(RRNVp`{T0Yd4Sy`pN)cJ-dant<>16ukc)5JnX=`vk zmDJ0C(Kg}n-~VO;&^}SS@o}9&jOynW;r+qW%o)lQV9I#8tcL<{$G?#QUh5W4Hg;5C zd2o94mnPt7jdW%q(QB%^PX^RWpDay4t++I1gxJX#Org5cdilP7{q~xGJQcwjp@=wk z`G2}qG~umrBjxg|xAgmH0`eYCOD|2OnAKnfP3VOwvV*Xib-s_K33&E0?KEhbZ9zGB zO?qU2tDlw2*sD3dngZH+(?nq9aA8(69|cy-mdh`+iA-Y(kX2MC1M>9n zrZr=$cnPE8wO6)AdskFkAG)tD;6BQapMT`YF>&ey;KvQX;v%5cfWvV63Wo^B!D=VV z71vn=?4y&~rb?h{t-P>j(R4L^2yCks>8qtn?$4?6olF`dOFz!NeED0dhu!9)B7X=gp7E5?hXm8-}@$i$fG&d$9P}>7*$f7bAjw zeXH?9x+G##(CKPGqJ5#+zPvU$qiAK7J!dTf1&i1eTr4TJ0yrolHbuR#PIWpSxLekL zTql@KQE$^L7A?n;V?%j~9w>&Z$Xm{ABFx;r>c)Z0T%@hbSFeNIlrGF{s*>&f$MOfo)`cC002ovPDHLkV1ig)njHWD diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png index 2f7b56b069a6d29a0e3a958dad564999d87f2e8b..5f967891690a3c0836c1a1b4855142fc08c3a999 100644 GIT binary patch delta 2086 zcmV+>2-)|T5VH`FB!BuzL_t(|0p*(sR8&#oRpH>(9-iYO;S>;nhV9&7S*t$#RDV;YZOx$sPm2nWdo=@@ zsi`=-dM$1r`CZ>gTj5=u#%uFj}Tu${Xz!V;6vz}c-@x~o1KxC|Nz$8Nn4KQIV6Coj;4TriDnSP~QK^t^~I z!`jgqp?i`L>wnV?d0BV3V4B9yqgWl*O`R|xdIjQxU)7dwP_$;BcUlL9p_y+NEj-6Z z{s%WGW6;1OVyqTU{Q|1HC(n#letos2e)j)@t&6J`7NtRj*{Ej)baMhu-vsU#RKDK9 zk+}-Cpw6)CPoA2yCk*DNwdRPQ8q@(scmqQ_0e&vb>whuPw;ln#mjYMrmD$bq2R?n7 zEiTrFM}Gq;7lF;EfcVqEUq$Noi)qzp<*meVv;p2?JHmt>K>ga4uSNI(?PlIDo0(bitlU7v~F9Mjq(s+xEC;LBXI4$@%_|clHj2B zz<1#+46i3egAN)cuZ8Tz1V?rU#`WOtNscD0{SBjmfR$9{P=+GGQ+wS18_wu;3Yfe5aIxrtphj@-WgAww(cjzh*9I zGK=bJyJY;41c^7ZMs+;w7w^P&qrEndQWBj`XWtyEei<_ zV@W_G8Taw#Ha@~?5__DtO-YC$TpP2%#2NVYy4XF11dr@2ef?B6@U6nlLzFo?fgv4u zyl#lW9aI!d@oqpnX`9dXDR&Q|&?cC`HnBmThHd;^#qTF8xVw||#z8JqT1sEvf7wjQx?USn&@6OeYx%r@E0h)N(Ny~SshjeRn9B7( zxfh^HiXoisRT50GvlKUth-Zy~;$|tqO$3t=S;124m&iBsbk?DtTIwU$PaP&#JZe3o z626pMB2)`J3S>smK|wxi3UXDIe1Dl4phH&hJ(;SZtAkiWra*~*OIvB1J9%RFk`+ul zq!is-i8W*j9UbMXE20HHS;6OSNndww0<^Yc4a)p5rVt==7*tSLvA{2dtl-0cNMEN+ zW=vP$qhzrLjbT(5>FY-`#p~9xf+?20%6`2zjSSmN>%((Wb3|Me$9HZaZGV$|QT%>F zZs|uBE6?&be7$)CeonL?NxWeZXx&mFUW$?uWwE@F;5A2BzS~zi5ue+iXEijv)MDXa z`HChjxpTM0?M;Jk8kDJ`e?rTo0sPQh(MF9o8&FTpG$twsM@v0)9}KnhFa(&TjtAAz-qvv>nxq zlcxZykMNya)IVCX`ucf=LB{-7L`Pwup^%IInIhq4+6KSSmuOZZXD{6OiW)Q5UWIu+&&*^|4WACx>px`Ieb4sB^|#KdQ{=$Bvi zGY>(>_vDWwc{*0M(@!-o+^H;>9)qCRc-aAU!_-nrP{ITk<$ptCrOMNI8|TX_tBClds_TQxPRa1zO}@xdeN6Z?*4z_7;Ch zK|VQfmD(*!@Zj2IEizMnGKb(ANmkl^EzYi8i%!#K&~u2@-5;}aJ~l>++ec61%Jw+8 z3?5b8{V^+YUHb+gvh+bc8i`2H;4mG?;<`V2%&rS zmi|G+^8|_AIw%+quV&NRvcMzfRfh?7w1(ijh`c zb`G`<9EN7To#7E4ftLP$Xz1b!3+AV5`lePw|B4F8^&ik~OdAxfS$(sA0K<|y6k!1L QWB>pF07*qoM6N<$f_={crT_o{ delta 2059 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 delta 1020 zcmV6FMnQs-}EA~Amz$~9)UIDJ#Mhb2KmZ#aKP9M|q}kyv-CfrCRcSw8FIT$nywCc7Ya z8&q6}ldE7*0)O0Xf}LmJ%M)+MxIUhLnE#T}7lc@k?E!bLh|{Yx~bf4xm^5Fri9s$bJ3#lrvzz zSjbAV_FcZ`0D8OzW{x!5MN?tBoXRH-PVmZXZGWABR3N$Wz&_6g-SeB=tyvKBjBXDR z^On9T!MrST`g$#02mN4etqeWa2EF~44s9O=EwFf^zH`Ng;Y_`CezKg-;v85tNypuQ zR8aZmv_q)UC*_bm#2WCY$@{NG+UI%O<#5T>*zq>JBT=VUr;Pl_3k!CK4=N#{PbFo| z-G3?BWCdiVTE|8w>%*e5$Px+ku1;6vaSP1<4$j^V4GaraYLc(bkqV)7J`}uUP2%eQ zD);&%)wzOpc~z`Xum{dX00yA)r-Tdx%1n!-64?3+tdQy7K1Sk=mV!VVPx zDz*8-&kL*SePxD}`kP6aHUswy+5cjR?#WYoiK>#%4Ef)}%l@f3WR@kq|5O3oxs>}8Za3jqPP5%HV#uu-&!Z-T>00002*GiFW!5;d+xcs z_k6`~=KgWt`QAC-@7{C1_3B^=UIYEnC1N5xgM;Df<&8RZ8h==n`>#Nc3X~V-qwvr{ zWUq}zUgAD;5uM4MTl>}+ymA>l`we|&O4+Qw=<~)EqeH)?a0A8beTBO(Yn|jox(zO@C*ua zv>tYcM?j#C`N=kp*2C^_@ocSQ!GM!J=5EYp4Q>zgXV!gU9yJgn@C?sY_{?k6lAL0aN^d5nX_W^-af($$zd32Zp~?eP8|%*pUVNaunE? z16!kx&4>oK0~U<|Uh%Lz9@Ng1sE?=VxC;w1Lw)#_kq>;FU|Ygg6MeoBu=sW0Eg#w; z%5rWEP4Ay`R+jDE8rU<3rW^Dz^?pdp)l|=h6g*HzDJl)h~o$8t1*AqMx0zI_w zUci73z<-2j;9zcz)vQ8|i4N}!Y?=f#k~C$HDoyVz*u9K}Xw?Kro(8LWb5`{3LL#6|33pX5v6TPBxT>k)qk77T1Ie18gRMHbnN{tz?L&WL~meR zcex|r%-q%b2%y`y!0m@xG}lP4NsJ&;$Tl(~;Db^)t1_!}c@F2YnlzDtf> zm49DDn&?-#C9@bI(Mmn9*C;JURX#5_^>btm+BFl8 z-zb-xN7d@)mWiLLueW`P`l@6yxqqc{^QaQdWahoMm-2X3?dY`JVp>53w85RkGG`^- zn<~-N%fMt`nUkLtCvf`G4+ zke zqSR1|4F?7+wN0a_2TSyqgotIx^_e%WM35r7;S?}`nAp_!ZNt*FNMPI=o;cSO)T4=y z6t^r0Kc%t|RMNE&q=-^H`}t1b*SE!T)E!Iv5D1R}PN@Xr<{%p|H<;Ig>Z?Rtr7Xr? zpwT8s7ssf&N9V@4)n^G^Uw;5H=5YCyX+TP@xa3K1F0=$BvCoh1Wv!sgTb0cwRNqLh}6WoPif z40+|6Ge7v{Fy820c%b_CRiWxu)f}0{K;(LD|1m&w?}Z$67(0V;5r67u$rvxYo8F6} zElbG04h;QSJ#AA$jk;;^nZU^9%o;PYO&Y4p6I_j24fKGtd{jVz!KfA5tgJa|+#gN4 zz_#8JU@LP4?Inzeab8KiQc<*J0Hui3diY?A_5RtajyKg_eHL;b`(B_p5r1f=D4ki+ zZn#v!W9Z+qtIPo5f1?|(q6fC+RfwDt9t#^|eiE>JJWs_a)!K8BxegY)9?r8? z6ARGekq&nX^5N$6sDx0bC}JD znnTo+v30*j5k;w~I%LO0Goq89(UOkAg}C)Pqn)qXU8 z_ff?8`(enMl~`!mBfwz_+5)9)yY6Cd@(D?TX~0mDc@eQa1JQQmAan{1gGXQ>eboEp xn_iVqH5>-&N<2b&Q2|+3GCXu9X}cN={R1T$H5SUT7&-s|002ovPDHLkV1oa4=$HTi delta 2019 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 delta 1323 zcmV+`1=RZP3cd=EB!2}-L_t(|0nM3zOjTtV$G=>Tzc4{mB1%IswIck{B}s~93b9$_ z{0)}tUYyx%x|Qq4X8%`PYdYPu8K5IWCxQ-hrM4U~5#*}DY=y;!36#jxn}8wS+w+{) zJMO*jIrpA(t^>c@-GTSl{ha50pXYhs_bTwoHarSx>k5$SEPq6TJsmd0QfU+hM_zc~ zMyJb#izP*9+Fb@upO-5XwR&VyES6N2;}Lt#AOQs{iVPBmMEe5ulj?M$GH)4rJKTzD zxupb`IAn0L67AWl!S-k7D(bX8NN_$e6R{~|&b33$qAT(Tg&e{@)Ev4ZXI1Wbt0het z2P}FD`0p<8^?#Xxa_3wieiZQ2AHeyx2mpzVz1UPJ;l|5m0`JWS<|Whr_j><%O#r1& z0*dno+F$AfcAf^lqEF8q(}+=unbuDylBHG=*krkG;#l!a3Yw2;3BZ?&fz9)QjTJy; zlQ}}efGcMMC1jbH;o9}XY~mTMGarw|0f!vGjvBK5VSnJR_dWs^(wf>$ma~Oe4DQ?F zd8<#q4KIg{S+w>a0^axr@ECv$3;4_DJkAIQNySjmbQs6|he&41J;O&FJu3Dh{Eec8F1M7LZw=bnw{5f#PV*xOQ26Q|p zm*1qdwqPgF*sedQzu3B2JAm3vz@+%VI`(j$?oyWp!1eRAr)ERBaX6paZUS%a15Uj= z5I21)%&-W!YK~lXh8F*~=gh(RRNVp`{T0Yd4Sy`pN)cJ-dant<>16ukc)5JnX=`vk zmDJ0C(Kg}n-~VO;&^}SS@o}9&jOynW;r+qW%o)lQV9I#8tcL<{$G?#QUh5W4Hg;5C zd2o94mnPt7jdW%q(QB%^PX^RWpDay4t++I1gxJX#Org5cdilP7{q~xGJQcwjp@=wk z`G2}qG~umrBjxg|xAgmH0`eYCOD|2OnAKnfP3VOwvV*Xib-s_K33&E0?KEhbZ9zGB zO?qU2tDlw2*sD3dngZH+(?nq9aA8(69|cy-mdh`+iA-Y(kX2MC1M>9n zrZr=$cnPE8wO6)AdskFkAG)tD;6BQapMT`YF>&ey;KvQX;v%5cfWvV63Wo^B!D=VV z71vn=?4y&~rb?h{t-P>j(R4L^2yCks>8qtn?$4?6olF`dOFz!NeED0dhu!9)B7X=gp7E5?hXm8-}@$i$fG&d$9P}>7*$f7bAjw zeXH?9x+G##(CKPGqJ5#+zPvU$qiAK7J!dTf1&i1eTr4TJ0yrolHbuR#PIWpSxLekL zTql@KQE$^L7A?n;V?%j~9w>&Z$Xm{ABFx;r>c)Z0T%@hbSFeNIlrGF{s*>&f$MOfo)`cC002ovPDHLkV1ig)njHWD diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png index 83381000f5d3b139fadae8d573aa5a98b624d893..405b44be06a64586909ed4462ef0f06439ebf11e 100644 GIT binary patch delta 2846 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 delta 2804 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 N002ovPDHLkV1m*HGF1Qo literal 4145 zcmV-15YF$3P)!k-wcK(^1=KW6NHg~hGz2PJv@8oz(##qwtz52-PFa~{^Nc2F<+zk(E{zLK zySZeF8<0wAAJn3kAxUe7}!-Fvy0bI$jDF!%R7w%thtT*osDyBoK~26RUO|4h7r-6o#OtJZ9TorFh3u3}WT&sj?v(Y&J$TNa zhJtH3!sni_zCzq>8pZk@%U?Sd-^rZ%Py7WSroCxF~RR z@;C&}x21tF#0V`HMh)i=QsB^zb;Y)OkgeScpbu&p+Hn%HGY(0CyCe@mz=%=2`-W1))sTl-9nV^O@Wwe z#S~P)L^Gg0@b*UF;YGmj=RLP^SsCE9p1`0e@kK9l>3jQ*0iW&wKHd(j+zp)dD+b3mcY$xa&bM}Ih-v?^DEZpBA4pCG-W7tM^D;X) z3C9Xtp)4>W1{mH2s8+%Mzi&|8`}_5)`QK*ytAT|N0N?xoys!dzC)4{f-~=2gaKARd zgggBkv4trYB-iHLU){f7Q{n7z?Nz|sMET_#_5rYVZ}HnQjiUr^R0DV;0f@a;PkU7_ z_bhP5a_a53ssp4B7GiG(Tpt3*eo=2zH;xf_U`Jqb5AnfkbXs2FMZ_!0>Ujot;Qial zjURXJ%XjK&R~NPm%#rkZFCekC{`HUb@HUD} z8ZT~%0MdqtquA@ffuGGi#}{l7xI=wl*(36dYm(gGJimAHS9!)YMbi*q-4kMMo*|D_ zc44`|JWH;46sU5!+2?Td9?kXq{z(tJYn{*{wp#46qo)Gj9W(ztcUUTLliFfhe#E@E zWY1CHH!st0&I#a#Q1kt$TNPM22<4@O74VJ2Xj31-dmgw}UTCJFzsq)f?zN z&zk4uV~N1YJ%Gpt=AXkI>+9*jdEt5f^Qz$1~;$kN;bp%YpG>E8pdtTX?t ze2fcxcQem!IUW2Ygj%!(n6nA^Vjtko@s?}!)b;Ykk(|&Hc%Yp$T9|@1_SJjkEMo;m z1*SotR;t1dbsZc!4!kU;;aOi4Jz9N%(Hwj|y9~HTd?`o8$!S$L<;5pHFA-=v*_uEC zqXJL4Tg_|Z4u;nJeWh>HaB<-A>fNv9tA`MJ(p}Pfq!TU&e~p#@AEq%TFk_wiw9?z= zhVOs}7g%RFU~Jd2oxs?xLXbG`Q6&B{=4RS|7W7l|Q5U2_IpC!`fWGhOY0ncz1g86}nI?AcrNw8dR+@3}=AEv$maur5=jWvXQz_4YW^ zjk*a)GG1)UHBT3c?^>(ac$os%8;lSmJL45q|94#1i@XS_N9o zIOXd`o51&I7`@LgcYdaTJ9w6*L#4dJr^(nfp0EF#qpx2?i@*#|?W*WJ|Mo}Vy{~oj z#(KD}Zdy~4rof-hIY4#Os5jeV$+TU|pp6}2z zudl4yBZCN3zUUk%y3rzVR0FlY-+Vy&cS|yKf>7l9S`@zx=4LS<@r1^Jc|B8fUA4bo z=CFL8;zEanioo=~h1Zl{cwHz9e5Lrt)vK!3#`-t*U@8=q~-BEU;>Hz7?sYR-#%&Mt4}SvcRfq=v4-@ z?Cn6+>7qL^U0LAcr`7(R0<)x1Y1wRrDjsuwR{T5*qb%?bipLyUeQ0FgRtl_HEWa3{ znECL7;xVf*$^vI=@B%Rzt2784QA>T3wO@05w6ehaHIxZrVU~Y=sosv=w9=66;5dPq z>Bp^okV58NbVJo%ID<~tsJF56hM&_P%UT_uu%^le+R1y0nE?9K#t-DnYb zjfR5Qk@aO>(^iX$ljwx;&@A4f%6g{?oOwXz6cwlRem*=8ySf1V7wG7bwe)QR)K)Zb z&_wyUQ&U$M&&+FR?pM48F;V>eODlohM|Jed8aR5N(U9<-CPwd1nypwwTj~lU`&9*I zb%3NEz@4-7^vMcXEvKjpKzzYeqPsSMm+VlVrU@^*>e;+xq_HrUh}Cjb;q<)lEA@8V zp-o^`qhWRe?>ACq#(lEnpOeqUv^%rvj%wN{;XMzgD$#090Ov91iO%X_v@D*M(jSPM z=Dj4aUGUsK_kMllh;`>96-2ARENk$|cJ+*RLbKnTM6s^VcUDR%zq`}L40CUDJ?$|k zdc`h1ZMj2x5f<)(xsK{6g4Vm0T%QIku)YAAJGcp)l^|5FWrJ(3s=c`&tT?-Pi@x^UVWiUC=q14IO@MNmTIWTQnJ!r{ zY?H>k9e@yLg+YsZL;EP`cPThnKiVm2$<)+P7}Jz zI?}pJBJe2{!G!nS0nVuU- zi-Ms^ysgwKjNjQX{xf+Yhp#to7Z=VFcQlooySFw1kEM7&T5(C85ic+0u!^0B#g{3? zy5|w&0&|y0V+A)z;TqRtPjEBDwUcJfXT22oq+`TNahY|<0;ZnsGd6`K*XnS`+x-n7 zZK!pb3_L%36ZnC)*7SpLKhkryJ$D#dcJmp7$!?!1HbsN2$wy*=PmbpE9?qFo+>=CT zOYb!YSZh-q5=-wc+f9zq@+Sk$-($#Zd3ORc$=bwSP6gqWKljd-wXf~OmivR#_sJ_)mkcy}pJ4?LMher; zDV}lfdQ*lI+l_5EfU|T`yUDypggDBYG$S!+GvD1q~##fLX# zGn|DUBZltyTP~l|rZ;E$!Q=j4zsHCY+PSB!F@LF~F8HzK!J$HPr_I|&M|~_!i~ZXA zZhyw!%3;~#WD#D>bo^Uda2yq z^s;tu(RcIGe>{ByFv!jA0|)au1#pO3y=BGP?rJpZn7y^21Jnu6wf;{j~q4A#1) z@L#0BIT?qC=&&apACd(So(k^*j>Ndx?M3P>?TxjU2*i*B_ha zivnk5oWNJfebLgjQXG&=SsnKO8_r$O@NmW-^#U%nL8p#LMa6m}#j$C!Jh6v{fc)#@RuXj;802v#3nzF z(8$Nde<@{mq?B0BTsR7)y2gnku2*Mf(T@JZ8M#;(H4>p6lM$UfRGgLXN8l)B7dgmE zpNqAwsp6dLQ!xwQBRP0*UKBMNmFhl?i1;XkM8*lx+n{p22r*UE6l-;5zuF7njXd$0 vo}@n)ClBlvpM55B(pOWxW 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 N002ovPDHLkV1m*HGF1Qo literal 4145 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 W80S=h7BXM}0000=UY{l}xW+1Q_S%G!P%J{?0 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≫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 diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png index e086378e67a20c7f5657bea7c5b9a1b10fcc6526..57167e19e1b69a69499fdf416746c8a0a74cea06 100644 GIT binary patch literal 5957 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 Q$p8QV07*qoM6N<$f}yK^1poj5 diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index 4ba934433..9583b6a64 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -1,96 +1,96 @@ -name: flutter_hbb -description: Your Remote Desktop Software - -# The following line prevents the package from being accidentally published to -# pub.dev using `pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev - -# 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 used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.6+8 - -environment: - sdk: ">=2.7.0 <3.0.0" - -dependencies: - flutter: - sdk: flutter - - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.3 - ffi: ^1.1.2 - path_provider: ^2.0.2 - provider: ^5.0.0 - flutter_easyloading: - git: - url: git://github.com/open-trade/flutter_easyloading - #path: flutter_easyloading - tuple: ^2.0.0 - wakelock: ^0.5.2 - device_info: ^2.0.2 - firebase_analytics: ^8.2.0 - package_info: ^2.0.2 - url_launcher: ^6.0.9 - shared_preferences: ^2.0.6 - -dev_dependencies: - flutter_launcher_icons: ^0.9.1 - flutter_test: - sdk: flutter - -# rerun: flutter pub run flutter_launcher_icons:main -flutter_icons: - android: "ic_launcher" - ios: true - image_path: "../360.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. -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: - - assets/ - - # 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 +name: flutter_hbb +description: Your Remote Desktop Software + +# The following line prevents the package from being accidentally published to +# pub.dev using `pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# 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 used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +version: 1.1.6+8 + +environment: + sdk: ">=2.7.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.3 + ffi: ^1.1.2 + path_provider: ^2.0.2 + provider: ^5.0.0 + flutter_easyloading: + git: + url: git://github.com/open-trade/flutter_easyloading + #path: flutter_easyloading + tuple: ^2.0.0 + wakelock: ^0.5.2 + device_info: ^2.0.2 + firebase_analytics: ^8.2.0 + package_info: ^2.0.2 + url_launcher: ^6.0.9 + shared_preferences: ^2.0.6 + +dev_dependencies: + flutter_launcher_icons: ^0.9.1 + flutter_test: + sdk: flutter + +# rerun: flutter pub run flutter_launcher_icons:main +flutter_icons: + android: "ic_launcher" + ios: true + image_path: "../1024.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. +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: + - assets/ + + # 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 From 79c12a0220b67f48d74050a1c802c86adfc4041c Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 5 Aug 2021 11:15:19 +0800 Subject: [PATCH 170/422] bigger version --- flutter_hbb/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index 9583b6a64..6ecba2348 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.6+8 +version: 1.1.7+9 environment: sdk: ">=2.7.0 <3.0.0" From 799df2d77a18a199923fe3a824c0e617d06df01e Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 6 Aug 2021 11:30:27 +0800 Subject: [PATCH 171/422] fix new line --- flutter_hbb/pubspec.yaml | 192 +++++++++++++++++++-------------------- 1 file changed, 96 insertions(+), 96 deletions(-) diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index 6ecba2348..3d7d08c16 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -1,96 +1,96 @@ -name: flutter_hbb -description: Your Remote Desktop Software - -# The following line prevents the package from being accidentally published to -# pub.dev using `pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev - -# 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 used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.7+9 - -environment: - sdk: ">=2.7.0 <3.0.0" - -dependencies: - flutter: - sdk: flutter - - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.3 - ffi: ^1.1.2 - path_provider: ^2.0.2 - provider: ^5.0.0 - flutter_easyloading: - git: - url: git://github.com/open-trade/flutter_easyloading - #path: flutter_easyloading - tuple: ^2.0.0 - wakelock: ^0.5.2 - device_info: ^2.0.2 - firebase_analytics: ^8.2.0 - package_info: ^2.0.2 - url_launcher: ^6.0.9 - shared_preferences: ^2.0.6 - -dev_dependencies: - flutter_launcher_icons: ^0.9.1 - flutter_test: - sdk: flutter - -# rerun: flutter pub run flutter_launcher_icons:main -flutter_icons: - android: "ic_launcher" - ios: true - image_path: "../1024.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. -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: - - assets/ - - # 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 +name: flutter_hbb +description: Your Remote Desktop Software + +# The following line prevents the package from being accidentally published to +# pub.dev using `pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# 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 used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +version: 1.1.7+9 + +environment: + sdk: ">=2.7.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.3 + ffi: ^1.1.2 + path_provider: ^2.0.2 + provider: ^5.0.0 + flutter_easyloading: + git: + url: git://github.com/open-trade/flutter_easyloading + #path: flutter_easyloading + tuple: ^2.0.0 + wakelock: ^0.5.2 + device_info: ^2.0.2 + firebase_analytics: ^8.2.0 + package_info: ^2.0.2 + url_launcher: ^6.0.9 + shared_preferences: ^2.0.6 + +dev_dependencies: + flutter_launcher_icons: ^0.9.1 + flutter_test: + sdk: flutter + +# rerun: flutter pub run flutter_launcher_icons:main +flutter_icons: + android: "ic_launcher" + ios: true + image_path: "../1024.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. +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: + - assets/ + + # 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 From 0f029545a424dfecca142fce41b167dc1c88cfd1 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 6 Aug 2021 21:18:06 +0800 Subject: [PATCH 172/422] refactor input_os_password --- flutter_hbb/lib/home_page.dart | 3 +++ flutter_hbb/lib/model.dart | 19 +++++++++++++++++++ flutter_hbb/lib/remote_page.dart | 17 ++++++++++++++++- 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index 9fa515fda..86385855f 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -236,6 +236,9 @@ class _HomePageState extends State { ); if (value == 'remove') { setState(() => FFI.setByName('remove', '${p.id}')); + () async { + removePreference(p.id); + }(); } }(); }, diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 6986a28ff..72687ecb4 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -711,6 +711,12 @@ Future> getPreference(String id) async { return m; } +void removePreference(String id) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + prefs.remove('peer' + id); + prefs.remove('peer' + id + '-password'); +} + Future getPassword(String id) async { SharedPreferences prefs = await SharedPreferences.getInstance(); var p = prefs.getString('peer' + id + '-password'); @@ -721,6 +727,19 @@ Future getPassword(String id) async { void savePassword(String id, String password) async { SharedPreferences prefs = await SharedPreferences.getInstance(); prefs.setString('peer' + id + '-password', password); + prefs.setString('peer' + id + '-autologin', password); +} + +Future getAutoLogin(String id) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + var p = prefs.getString('peer' + id + '-autologin'); + if (p == null) return false; + return p != ""; +} + +void saveAutoLogin(String id, bool a) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + prefs.setString('peer' + id + '-autologin', a ? 'Y' : ''); } void initializeCursorAndCanvas() async { diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index ab79968f8..8c7acc503 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -233,6 +233,7 @@ class _RemotePageState extends State { setState(() { _mouseTools = !_mouseTools; resetTool(); + if (_mouseTools) _drag = true; }); }, )), @@ -300,6 +301,7 @@ class _RemotePageState extends State { _drag = false; _scroll = false; _right = false; + _mouseTools = false; }); } else if (_scroll) { var dy = (_yOffset - _yOffset0) / 10; @@ -837,13 +839,26 @@ void showActions(BuildContext context) { void showSetOSPassword(BuildContext context) async { final controller = TextEditingController(); var password = await getPassword(FFI.id); + var autoLogin = await getAutoLogin(FFI.id); controller.text = password; showAlertDialog( context, (setState) => Tuple3( - Text(translate('Password Required')), + Text(translate('OS Password')), Column(mainAxisSize: MainAxisSize.min, children: [ PasswordWidget(controller: controller), + CheckboxListTile( + contentPadding: const EdgeInsets.all(0), + dense: true, + controlAffinity: ListTileControlAffinity.leading, + title: Text( + translate('Auto Login'), + ), + value: autoLogin, + onChanged: (v) { + setState(() => autoLogin = v); + }, + ), ]), [ TextButton( From 46e239dabe864a838f5315abb80d18cf13c51f79 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 6 Aug 2021 22:29:11 +0800 Subject: [PATCH 173/422] refactor --- flutter_hbb/lib/home_page.dart | 17 +++++++++----- flutter_hbb/lib/model.dart | 26 ---------------------- flutter_hbb/lib/remote_page.dart | 38 +++++++++++++++----------------- 3 files changed, 29 insertions(+), 52 deletions(-) diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index 86385855f..a7fe87852 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -257,9 +257,9 @@ class _HomePageState extends State { void showServer(BuildContext context) { final formKey = GlobalKey(); - final id0 = FFI.getByName('custom-rendezvous-server'); - final relay0 = FFI.getByName('relay-server'); - final key0 = FFI.getByName('key'); + final id0 = FFI.getByName('option', 'custom-rendezvous-server'); + final relay0 = FFI.getByName('option', 'relay-server'); + final key0 = FFI.getByName('option', 'key'); var id = ''; var relay = ''; var key = ''; @@ -316,9 +316,14 @@ void showServer(BuildContext context) { if (formKey.currentState.validate()) { formKey.currentState.save(); if (id != id0) - FFI.setByName('custom-rendezvous-server', id); - if (relay != relay0) FFI.setByName('relay-server', relay); - if (key != key0) FFI.setByName('key', key); + FFI.setByName('option', + '{"name": "custom-rendezvous-server", "value": "${id}"}'); + if (relay != relay0) + FFI.setByName('option', + '{"name": "relay-server", "value": "${relay}"}'); + if (key != key0) + FFI.setByName( + 'option', '{"name": "key", "value": "${key}"}'); Navigator.pop(context); } }, diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 72687ecb4..13caa06c1 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -714,32 +714,6 @@ Future> getPreference(String id) async { void removePreference(String id) async { SharedPreferences prefs = await SharedPreferences.getInstance(); prefs.remove('peer' + id); - prefs.remove('peer' + id + '-password'); -} - -Future getPassword(String id) async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - var p = prefs.getString('peer' + id + '-password'); - if (p == null) return ""; - return p; -} - -void savePassword(String id, String password) async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - prefs.setString('peer' + id + '-password', password); - prefs.setString('peer' + id + '-autologin', password); -} - -Future getAutoLogin(String id) async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - var p = prefs.getString('peer' + id + '-autologin'); - if (p == null) return false; - return p != ""; -} - -void saveAutoLogin(String id, bool a) async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - prefs.setString('peer' + id + '-autologin', a ? 'Y' : ''); } void initializeCursorAndCanvas() async { diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 8c7acc503..721ae01f4 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -782,8 +782,8 @@ void showActions(BuildContext context) { TextButton( style: flatButtonStyle, onPressed: () { - showSetOSPassword(context); Navigator.pop(context); + showSetOSPassword(context, false); }, child: Icon(Icons.edit), ) @@ -817,29 +817,20 @@ void showActions(BuildContext context) { } }(); } else if (value == 'enter_os_password') { - () async { - var password = await getPassword(FFI.id); - if (password != "") { - var x = FFI.cursorModel.x; - var y = FFI.cursorModel.y; - FFI.moveMouse(x + 3, y + 3); - await Future.delayed(Duration(milliseconds: 50)); - FFI.moveMouse(x, y); - await Future.delayed(Duration(milliseconds: 50)); - FFI.tap(true); - await Future.delayed(Duration(milliseconds: 300)); - FFI.setByName('input_string', password); - FFI.inputKey('VK_RETURN'); - } - }(); + var password = FFI.getByName('peer_option', "os-password"); + if (password != "") { + FFI.setByName('input_os_password', password); + } else { + showSetOSPassword(context, true); + } } }(); } -void showSetOSPassword(BuildContext context) async { +void showSetOSPassword(BuildContext context, bool login) { final controller = TextEditingController(); - var password = await getPassword(FFI.id); - var autoLogin = await getAutoLogin(FFI.id); + var password = FFI.getByName('peer_option', "os-password"); + var autoLogin = FFI.getByName('peer_option', "auto-login") != ""; controller.text = password; showAlertDialog( context, @@ -872,7 +863,14 @@ void showSetOSPassword(BuildContext context) async { style: flatButtonStyle, onPressed: () { var text = controller.text.trim(); - savePassword(FFI.id, text); + FFI.setByName('peer_option', + '{"name": "os-password", "value": "${text}"}'); + FFI.setByName('peer_option', + '{"name": "auto-login", "value": "${autoLogin ? 'Y' : ''}"}'); + print(text); + if (text != "") { + FFI.setByName('input_os_password', text); + } Navigator.pop(context); }, child: Text(translate('OK')), From 0704cc8ee41d1fb2b52fb9ab03a087f60ec3bb3f Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 6 Aug 2021 22:45:45 +0800 Subject: [PATCH 174/422] ctrl+v --- flutter_hbb/lib/home_page.dart | 6 ++++-- flutter_hbb/lib/remote_page.dart | 11 +++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index a7fe87852..dd9c1f24d 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -49,7 +49,7 @@ class _HomePageState extends State { position: RelativeRect.fromLTRB(3000, 70, 3000, 70), items: [ PopupMenuItem( - child: Text(translate('ID/Relay Server')), + child: Text(translate('ID Server')), value: 'server'), PopupMenuItem( child: Text(translate('About') + ' RustDesk'), @@ -266,7 +266,7 @@ void showServer(BuildContext context) { showAlertDialog( context, (setState) => Tuple3( - Text(translate('ID/Relay Server')), + Text(translate('ID Server')), Form( key: formKey, child: @@ -281,6 +281,7 @@ void showServer(BuildContext context) { id = value.trim(); }, ), + /* TextFormField( initialValue: relay0, decoration: InputDecoration( @@ -291,6 +292,7 @@ void showServer(BuildContext context) { relay = value.trim(); }, ), + */ TextFormField( initialValue: key0, decoration: InputDecoration( diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 721ae01f4..53e23c44c 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -490,6 +490,14 @@ class _RemotePageState extends State { ); FFI.ctrl = old; }), + wrap('Ctrl+V', () { + var old = FFI.ctrl; + FFI.ctrl = true; + FFI.inputKey( + 'VK_V', + ); + FFI.ctrl = old; + }), wrap('Ctrl+S', () { var old = FFI.ctrl; FFI.ctrl = true; @@ -867,8 +875,7 @@ void showSetOSPassword(BuildContext context, bool login) { '{"name": "os-password", "value": "${text}"}'); FFI.setByName('peer_option', '{"name": "auto-login", "value": "${autoLogin ? 'Y' : ''}"}'); - print(text); - if (text != "") { + if (text != "" && login) { FFI.setByName('input_os_password', text); } Navigator.pop(context); From b4cd94c4889ea0105acad9259be14108fb136e74 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 11 Aug 2021 00:22:47 +0800 Subject: [PATCH 175/422] flutter cmd/win for mac/win --- flutter_hbb/lib/remote_page.dart | 46 +++++++++++++++++--------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 53e23c44c..b904bd15d 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -399,6 +399,8 @@ class _RemotePageState extends State { }); }, _right) ]; + final pi = FFI.ffiModel.pi; + final isMac = pi.platform == "Mac OS"; final modifiers = [ wrap('Ctrl', () { setState(() => FFI.ctrl = !FFI.ctrl); @@ -409,7 +411,7 @@ class _RemotePageState extends State { wrap('Shift', () { setState(() => FFI.shift = !FFI.shift); }, FFI.shift), - wrap('Cmd', () { + wrap(isMac ? 'Cmd' : 'Win', () { setState(() => FFI.command = !FFI.command); }, FFI.command), ]; @@ -482,29 +484,14 @@ class _RemotePageState extends State { wrap('', () { FFI.inputKey('VK_RIGHT'); }, false, Icons.keyboard_arrow_right), - wrap('Ctrl+C', () { - var old = FFI.ctrl; - FFI.ctrl = true; - FFI.inputKey( - 'VK_C', - ); - FFI.ctrl = old; + wrap(isMac ? 'Cmd+C' : 'Ctrl+C', () { + sendPrompt(isMac, 'VK_C'); }), - wrap('Ctrl+V', () { - var old = FFI.ctrl; - FFI.ctrl = true; - FFI.inputKey( - 'VK_V', - ); - FFI.ctrl = old; + wrap(isMac ? 'Cmd+V' : 'Ctrl+V', () { + sendPrompt(isMac, 'VK_V'); }), - wrap('Ctrl+S', () { - var old = FFI.ctrl; - FFI.ctrl = true; - FFI.inputKey( - 'VK_S', - ); - FFI.ctrl = old; + wrap(isMac ? 'Cmd+S' : 'Ctrl+S', () { + sendPrompt(isMac, 'VK_S'); }), ]; return Container( @@ -885,3 +872,18 @@ void showSetOSPassword(BuildContext context, bool login) { ], )); } + +void sendPrompt(bool isMac, String key) { + final old = isMac ? FFI.command : FFI.ctrl; + if (isMac) { + FFI.command = true; + } else { + FFI.ctrl = true; + } + FFI.inputKey(key); + if (isMac) { + FFI.command = old; + } else { + FFI.ctrl = old; + } +} From b6f6b094de591f76a8cdd896f7dc0da0e0e6354b Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 11 Aug 2021 01:11:29 +0800 Subject: [PATCH 176/422] refactor msgbox retry --- flutter_hbb/lib/remote_page.dart | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index b904bd15d..fa59cdc16 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -93,21 +93,13 @@ class _RemotePageState extends State { } else if (type == 'input-password') { enterPasswordDialog(id, context); } else { - showMsgBox(type, title, text); + var hasRetry = evt['hasRetry']; + showMsgBox(type, title, text, hasRetry); } } - void showMsgBox(String type, String title, String text) { + void showMsgBox(String type, String title, String text, bool hasRetry) { msgbox(type, title, text, context); - final hasRetry = type == "error" && - title == "Connection Error" && - text.toLowerCase().indexOf("offline") < 0 && - text.toLowerCase().indexOf("exist") < 0 && - text.toLowerCase().indexOf("handshake") < 0 && - text.toLowerCase().indexOf("failed") < 0 && - text.toLowerCase().indexOf("resolve") < 0 && - text.toLowerCase().indexOf("mismatch") < 0 && - text.toLowerCase().indexOf("manually") < 0; if (hasRetry) { _timer?.cancel(); _timer = Timer(Duration(seconds: _reconnects), () { From 3d49dbc60d2e6fedcb558dab8269e6dbe2fc081c Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 11 Aug 2021 01:38:21 +0800 Subject: [PATCH 177/422] fix mobile --- flutter_hbb/lib/remote_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index fa59cdc16..08c07e0d1 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -93,7 +93,7 @@ class _RemotePageState extends State { } else if (type == 'input-password') { enterPasswordDialog(id, context); } else { - var hasRetry = evt['hasRetry']; + var hasRetry = evt['hasRetry'] == 'true'; showMsgBox(type, title, text, hasRetry); } } From c17f48c1288862c34d41bdb14389ccbc5f40a358 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 12 Aug 2021 11:10:29 +0800 Subject: [PATCH 178/422] obfuscate --- flutter_hbb/build_android.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_hbb/build_android.sh b/flutter_hbb/build_android.sh index d0372066d..6ea0eba3b 100755 --- a/flutter_hbb/build_android.sh +++ b/flutter_hbb/build_android.sh @@ -1,2 +1,2 @@ #!/usr/bin/env bash -flutter build apk --target-platform android-arm64 --release +flutter build apk --target-platform android-arm64 --release --obfuscate --split-debug-info ./split-debug-info From f226ede64be1af028b6145dec6ac8e77107e30a5 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sat, 14 Aug 2021 14:14:01 +0800 Subject: [PATCH 179/422] unify peer color --- flutter_hbb/lib/common.dart | 3 ++- flutter_hbb/lib/home_page.dart | 15 +++++++++------ flutter_hbb/lib/model.dart | 1 + 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 26d9826d6..15588f13c 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -174,5 +174,6 @@ Color str2color(String str, [alpha = 0xFF]) { for (var i = 0; i < str.length; i += 1) { hash = str.codeUnitAt(i) + ((hash << 5) - hash); } - return Color((hash & 0xFFFFFF) | (alpha << 24)); + hash = hash % 16777216; + return Color((hash & 0xFF7FFF) | (alpha << 24)); } diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index dd9c1f24d..fb38dd5bd 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -7,6 +7,7 @@ import 'dart:async'; import 'common.dart'; import 'model.dart'; import 'remote_page.dart'; +import 'dart:io'; class HomePage extends StatefulWidget { HomePage({Key key, this.title}) : super(key: key); @@ -24,10 +25,12 @@ class _HomePageState extends State { @override void initState() { super.initState(); - Timer(Duration(seconds: 5), () { - _updateUrl = FFI.getByName('software_update_url'); - if (_updateUrl.isNotEmpty) setState(() {}); - }); + if (Platform.isAndroid) { + Timer(Duration(seconds: 5), () { + _updateUrl = FFI.getByName('software_update_url'); + if (_updateUrl.isNotEmpty) setState(() {}); + }); + } } @override @@ -87,7 +90,7 @@ class _HomePageState extends State { width: double.infinity, color: Colors.pinkAccent, padding: EdgeInsets.symmetric(vertical: 12), - child: Text('Download new version', + child: Text(translate('Download new version'), style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold)))), @@ -248,7 +251,7 @@ class _HomePageState extends State { leading: Container( padding: const EdgeInsets.all(6), child: getPlatformImage('${p.platform}'), - color: str2color('${p.id}${p.platform}', 0x77)), + color: str2color('${p.id}${p.platform}', 0x7f)), ))))); }); return Wrap(children: cards); diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 13caa06c1..78772ee35 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -746,6 +746,7 @@ final langs = >{ 'Remote ID': '远程ID', 'Paste': '粘贴', 'Are you sure to close the connection?': '是否确认关闭连接?', + 'Download new version': '下载新版本', }, 'en': {} }; From d9163077c0c37f3f4bdea0ea6ac4e9cb7803eadf Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sun, 15 Aug 2021 00:25:41 +0800 Subject: [PATCH 180/422] no alpha icon required by iOS --- .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 2561 -> 1889 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 1708 -> 1274 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 3394 -> 2554 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 5152 -> 3822 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 6871 -> 5160 bytes .../Icon-App-1024x1024@1x.png | Bin 42405 -> 32008 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 713 -> 558 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 1390 -> 1034 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 2099 -> 1594 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 1039 -> 790 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 2029 -> 1531 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 3109 -> 2331 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 1390 -> 1034 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 2853 -> 2105 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 4215 -> 3176 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 4215 -> 3176 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 6460 -> 4814 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 2651 -> 1969 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 5385 -> 4010 bytes .../Icon-App-83.5x83.5@2x.png | Bin 5957 -> 4461 bytes flutter_hbb/pubspec.yaml | 2 +- 21 files changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_hbb/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/flutter_hbb/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index d021a56748badbe63df6a4b809bb1e439df3fa67..9a6ce011d32bea90d6d91918a89da549727cb8de 100644 GIT binary patch delta 1885 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 diff --git a/flutter_hbb/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/flutter_hbb/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index f8237ec9913823aa407434805a605d1c40d78a59..b77c65d159089dfca3979ef08629e43f1fde17c7 100644 GIT binary patch literal 2554 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 Q^Z)<=07*qoM6N<$g1?mA00000 literal 3394 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 YAAR}1r@-n%+yDRo07*qoM6N<$f|r$gdH?_b diff --git a/flutter_hbb/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/flutter_hbb/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index e23f2f82bba97a2164a598d80f3597b02b5b05db..638a672f9fd88ce12ccd1eec924d52fd7554a91e 100644 GIT binary patch literal 3822 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 O0000#zR}eBfhVY`xXN!XiIXQRn0z@Z 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 R5TpPA002ovPDHLkV1nRw=#T&a diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png index 0a626efe0087f402a0b6eca1346dcbab4e1b8a3d..eb0bf04fd6b1eab4bc6db023f61f95f121904356 100644 GIT binary patch literal 32008 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

  • {v?hPuWKmS$V$-_uKFy!hK{!^Musbr_5g@ zX)OLx+8smmR%XPXb&c~5GDnNWyrlI0hGtAHh(dkHd-6SQ%U#T(FOdA1`D=Z4tp*C0 zieMc%uE_7~iR&!#C?6O;90Vox2&e8SdnZj|;c*b|KGN-?W!WuhotEm)#D5&xV|xg^ zvS_ZxK&6K5vMqvsPYd68RB|~#iOT6SYsL*OMKS#RZqv|+J7QKFij6X>5|CI(zcG>f z^~*E7n{JCj0`9`$(Bs&sq@MrccE`80cFv*Br6AZFNm9x>I1#3w(soVFb<=6#m6x)U zP#-EBBjWXUrC6n)TF8Zkw#FmfIFT z)aKi^=~oWiV_v19Ktxx1*2>7)Yuhhd)zepl9>1kUa5@(LBO9s#vj+)slB9E({bNWs zt#Re&O0`K@!AU2L)cbD#j2*%lFDWL5?89%J^wys(!#?2|IG;Hv5P@EL^?to(pG{; z@8H>#edNL$(_o)TRLFm}Gm|wLHVQ}Q_FAS!T9(-U`}zE`o9>4yD$KP<%dRAbwHA%L z{S_lXw0jnK58UH(q$%UCyyB(qztL2SGAnu*UG~vm{AzN&6u8y@~T`}JX)>J($faGA_>Eoi$|vw9uT9+Xt^N2hs4vIZ9`_R z8yOy?akypJG7t*HhwHmZw*RYN0}iutvlDW)@oKFK9dn5fBy$StekX1nC|_eHhZiLq zJ(_k{utUpVBvGYKJ5Wx& z7-OQEOJFbV@%Hcw`n&qQ=U1FRXNDoL*<)4S^m>ksx{fH#CpPKV)sYtyHa9tj&K=5S zF{C{*8^X z2hmi&{DD@rj3MnciRC$yd)Q#b$R|n@5&*E9!{8mQXJ?cDi zhWl$~Ki9Y$nln=Cv4f0N;aD%^_Eiql^?p$iUOK6{dobiMT>E@Y{h_+$8IXHu!Ou$bK;ir!KfAN>?KpT7e zK7P2jzBZ!fk^Qv8b!p?`^xn0DG?~&D8ehJmC9gKd_kI$_F0dk)YV&?p@;^(Q%rAbX zQR&Zcz&`T^C;JzbNZqzg@iC~VoK4Gm4qrxioQ*k!hf9WFUi#qqWTvagZ%1Vbt5^P< z*iL?M2#l+~96#Q>v9Ws#>Ay5_oG-HFproghqv4Kez;;Q6$UQpnejLV64W?RdlhBTh z@-*5vy!R>>x94kfQHQsCo6BBG%|i^%(VGmKsjyS-$dhZfJ)&H*;f(7qRs13+*c*M?lXNdh0Lo@}!p}E&JvCqly_pE8#4bD%6SX z3h(mIlyGp6w@ovJF{2knc17|UZJo^V>LX9La$GpQFXv@Ue1PE8*$@1VL`h|d zfKFRU50e7^wKpj*ovL$A^e=CGxc0ZkUe9mb`=ERz>)wEi7WdzOHTxAqc!_ah0$%9R zFnPUDL|Q}ZW}Pnun>sLGT)>M2?FQf+y?$Ow6@9!a;o2<$rfhG5*M(w{;h??zttJBe#p%rE(&Btf@Q~BR zhc_tGnvZ@UP$7DEDwFFErp5w#y!JP2yNai}kXwN&i%Kd>4 z9G5>bG~S@BA$_h1)(XhfwcJ{(_^)tL+=e~N;Y(ZoyjSqt^!ml%78gAI2QN@Yen)Tr zyAIilgxOwQSySaeVA>BcKf3hsk-2z9hrhz_!XK90-sto(cOD4k{v??X#GTL>u$^zU zb{)GjZ>F7V+MVJ|($IdX-`8ujODO%za^yB6oUx0QRoPuLPJqPrJ=Z<;(mAXP27gI5dsj6f1zk(y=Hn4K}COaoQ z+NQS)S2o6RQ^Ai^29MHr&w((aw!hm>w0i8@Bs;UM3}1O9k82Nq(BA#UW!V~jbBqV2 zn3cc2AAtK5nLm%qa0&3q-D>N6&`t;%{(K_&^9;Jzg8Qb)d-RqQne0TK74FpjDx#GV ze~=Yt&9KzL+*_jJOH^+^`F|fiOP2ru literal 42405 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 M_dU&U{qC240aRBrfB*mh diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png index 7f2c4ca48e5981665f20b4298367825f74aa508d..b17866fe40a146d18921e2ba11afbcbc873454d8 100644 GIT binary patch delta 544 zcmV+*0^j|~1+D~;8Gix*0008(idp~w0rg2lK~#7FwUkdt(_t9LKh`W8S(Y`uNF`0A zsntBBQXSGdNTS0sre5-(hYac>cIc2oP>^T@1r<^bI!KqO!xn)SA;t=d1sPO`mF2&o zW46uBwP*fNet*0eefb@}5AXZ=J@50r&-A-G58B?CYTo$g9ji00000_-1jz-E8Gi-<0051N9Sr~g0+2~WK~#7Ft(03x6j2y}zs)I4vUIn!7pWve zQoC7*Sp-=me3IcLuQ&41UhXi?;zsUfq>PKtUbhTJ7^@D>!UfV@;_eGPY< zFg6}WDcWk&!bR*r7F^m1@w3%F#RgjhR=fuG%k#2V-Z$WV|0G3=WMneToQ%eTOf zJU;m5h8|b&PJdQ1JU#;J8{or$QQrVeON5I>3N|tdr`w_a5x50fbx*llUkDphAaM@d zEPmJoRwJP64l_(xGQNDS0hwopiSuPh281+!`E6Jx?b!WQ zT2G4kg1GMNGq##Re6B%FChb)<9L?hT1zmwG_BFZHGo^GlH);CV)+en#9QApokD!03 incUPZ(_uWRI`s!zYs5UB<#Q(h0000plmYQZ^ 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 b3|Me z$9HZaZIgUa{C+}i=|>hT&+<2Xy?FzEPP8CNykQY&-BKZ5ijop#vAmGrHAh&!+gCaf zpWB~jH8j1{V&P!edO&H z?NfGN;Ye$K0ubyDSV%-X&41X`J`e?rTo0sPQq~b2)+|I^8p=zya-7Hleo&a23JX5Y zZvM?7V6v~Y9o3AJrvR&u@SR)KKU%WoGNLp4P6%^z_VV_0E)^Be*RT>4#nQ8P00Z3l ziJ=rU3#hhC#r1tGm)(34h&fhuIhCFO7|q<;80LDM?8N)BIL)}@7Jo&8DPsv+!=~-) zfQ>||ro+;LyA^u98^})z%IB=*p8!z8KDJ!u>{9j}9g4FhIwcsk4%j|HS*FyS#lqoi zBdhOuWl-#_$g3W zEdSU;;S=D4y}&2Q>OALRa>XNq#oE40_)*OKKL!<{w*X$PNM&E z!TC4uB0eMtp?mh0{z1g^1c}``C>Rf~X4BiUz$546MZ|XXLI3C#XcIgXHUBTsw zIFXI)zha4skyc)I4z>;)hGxE<;SnBzmi~Td=;8_s<|?Oa`lePw|B4F8_0Voi8x*Zs beY1Z6!;(7`VF2`G00000NkvXXu0mjf2$=*@ diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png index cd9c118a8efcc9831dda3f4f29ab20b238e229b5..8999d5ed9e5f015fa047d1df173190469e5bbdf4 100644 GIT binary patch delta 778 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 BaasTX literal 3109 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 diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png index 496c47677d3cbff1f8bd81645216235ee7c96dde..70164e1a45fe30b6877b2119b92803db03d4beab 100644 GIT binary patch delta 1023 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 DDVcv; diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png index c72ef5c8dba466e4be39387e24da3fa11a330596..df9cb7d0a538c60fbb5b92f57befdadf1bdf79e7 100644 GIT binary patch literal 3176 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 N002ovPDHLkV1m*HGF1Qo diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png index c72ef5c8dba466e4be39387e24da3fa11a330596..df9cb7d0a538c60fbb5b92f57befdadf1bdf79e7 100644 GIT binary patch literal 3176 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 N002ovPDHLkV1m*HGF1Qo diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png index 2c19116c7c9aa5f83b54d8114930970dead9be76..12ffdab248fbaad8750f7786f73836b6233b1c80 100644 GIT binary patch literal 4814 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 Xk49Q;y(j+WR76JlW_qnU?n(ax0O(Aj literal 6460 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 AVE_OC literal 2651 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 TT4rwED7UoL_0_6WY(oAAreLo; literal 5957 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 nII Date: Sun, 15 Aug 2021 01:52:41 +0800 Subject: [PATCH 181/422] =?UTF-8?q?set=20ITSAppUsesNonExemptEncryption=20t?= =?UTF-8?q?o=20false,=20=E8=BF=99=E4=B8=AA=E5=8A=A0=E5=AF=86=E5=87=BA?= =?UTF-8?q?=E5=8F=A3=E5=90=88=E8=A7=84=E6=98=AF=E4=B8=AA=E5=8E=86=E5=8F=B2?= =?UTF-8?q?=E9=81=97=E7=95=99=E9=97=AE=E9=A2=98=EF=BC=8C=E7=8E=B0=E5=9C=A8?= =?UTF-8?q?=E6=B2=A1=E9=82=A3=E4=B9=88=E4=B8=A5=E6=A0=BC=E4=BA=86=EF=BC=8C?= =?UTF-8?q?=E6=88=91=E4=BB=AClibsodium=E7=AE=97=E6=B3=95=E5=BA=94=E8=AF=A5?= =?UTF-8?q?=E5=9C=A8exempt=E8=8C=83=E5=9B=B4=EF=BC=8C=E6=89=80=E4=BB=A5?= =?UTF-8?q?=E8=BF=99=E9=87=8C=E9=80=89false=EF=BC=8C=E4=BB=A5=E5=90=8E?= =?UTF-8?q?=E6=8F=90=E4=BA=A4=EF=BC=8C=20connect=E5=B0=B1=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E9=97=AE=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- flutter_hbb/ios/Runner.xcodeproj/project.pbxproj | 2 +- flutter_hbb/ios/Runner/Info.plist | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj b/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj index e30c05833..253550635 100644 --- a/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj +++ b/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj @@ -564,4 +564,4 @@ /* End XCConfigurationList section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; -} \ No newline at end of file +} diff --git a/flutter_hbb/ios/Runner/Info.plist b/flutter_hbb/ios/Runner/Info.plist index 647671745..aa93457cd 100644 --- a/flutter_hbb/ios/Runner/Info.plist +++ b/flutter_hbb/ios/Runner/Info.plist @@ -41,5 +41,7 @@ UIViewControllerBasedStatusBarAppearance + ITSAppUsesNonExemptEncryption + From b521175bca2e3fcfc4abe5cb3e6e42a7969e5384 Mon Sep 17 00:00:00 2001 From: open-trade Date: Tue, 17 Aug 2021 15:07:01 +0800 Subject: [PATCH 182/422] fix on iOS --- flutter_hbb/build_ios.sh | 2 + .../ios/Runner.xcodeproj/project.pbxproj | 5 ++ flutter_hbb/ios/Runner/Runner.entitlements | 8 ++++ flutter_hbb/lib/remote_page.dart | 46 ++++++++++++------- flutter_hbb/pubspec.yaml | 2 +- 5 files changed, 45 insertions(+), 18 deletions(-) create mode 100755 flutter_hbb/build_ios.sh create mode 100644 flutter_hbb/ios/Runner/Runner.entitlements diff --git a/flutter_hbb/build_ios.sh b/flutter_hbb/build_ios.sh new file mode 100755 index 000000000..6d0d627ac --- /dev/null +++ b/flutter_hbb/build_ios.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +flutter build ipa --release --obfuscate --split-debug-info=./split-debug-info diff --git a/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj b/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj index 253550635..5bcdd6260 100644 --- a/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj +++ b/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj @@ -42,6 +42,7 @@ 7E078EE826BAB4710036E738 /* liblibrustdesk.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = liblibrustdesk.a; path = "../../target/aarch64-apple-ios/release/liblibrustdesk.a"; sourceTree = ""; }; 7E078EEA26BABB100036E738 /* ffi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ffi.h; sourceTree = ""; }; 7E078EEB26BADB3D0036E738 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 7E0A73A826CAB3C100FF94B3 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; 94AF76B3E95A41AD3421FB7B /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; @@ -110,6 +111,7 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( + 7E0A73A826CAB3C100FF94B3 /* Runner.entitlements */, 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, @@ -364,6 +366,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = HZF9JMC8YN; ENABLE_BITCODE = NO; @@ -495,6 +498,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = HZF9JMC8YN; ENABLE_BITCODE = NO; @@ -520,6 +524,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = HZF9JMC8YN; ENABLE_BITCODE = NO; diff --git a/flutter_hbb/ios/Runner/Runner.entitlements b/flutter_hbb/ios/Runner/Runner.entitlements new file mode 100644 index 000000000..ba21fbdaf --- /dev/null +++ b/flutter_hbb/ios/Runner/Runner.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.developer.networking.wifi-info + + + diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 08c07e0d1..99349a041 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -8,6 +8,7 @@ import 'package:tuple/tuple.dart'; import 'package:wakelock/wakelock.dart'; import 'common.dart'; import 'model.dart'; +import 'dart:io'; final initText = '\1' * 1024; @@ -37,7 +38,7 @@ class _RemotePageState extends State { var _more = true; var _fn = false; final FocusNode _focusNode = FocusNode(); - var _showEdit = true; + var _showEdit = false; var _reconnects = 1; @override @@ -172,6 +173,8 @@ class _RemotePageState extends State { @override Widget build(BuildContext context) { final pi = Provider.of(context).pi; + final hideKeyboard = Platform.isIOS && _showEdit; + final showActionButton = !_showBar || hideKeyboard; EasyLoading.instance.loadingStyle = EasyLoadingStyle.light; return WillPopScope( onWillPop: () async { @@ -179,14 +182,21 @@ class _RemotePageState extends State { return false; }, child: Scaffold( - floatingActionButton: _showBar + floatingActionButton: !showActionButton ? null : FloatingActionButton( - mini: true, - child: Icon(Icons.expand_less), + mini: !hideKeyboard, + child: Icon( + hideKeyboard ? Icons.expand_more : Icons.expand_less), backgroundColor: MyTheme.accent50, onPressed: () { - setState(() => _showBar = !_showBar); + setState(() { + if (hideKeyboard) { + _showEdit = !_showEdit; + } else { + _showBar = !_showBar; + } + }); }), bottomNavigationBar: _showBar && pi.displays != null ? BottomAppBar( @@ -337,18 +347,19 @@ class _RemotePageState extends State { } Widget getHelpTools() { - final keyboard = _bottom >= 100; + final keyboard = _showEdit; if (!_mouseTools && !keyboard) { return SizedBox(); } + final size = MediaQuery.of(context).size; var wrap = (String text, void Function() onPressed, [bool active, IconData icon]) { return TextButton( style: TextButton.styleFrom( minimumSize: Size(0, 0), padding: EdgeInsets.symmetric( - vertical: icon != null ? 3 : 6, - horizontal: 6), //adds padding inside the button + vertical: 10, + horizontal: 9.75), //adds padding inside the button tapTargetSize: MaterialTapTargetSize .shrinkWrap, //limits the touch area to the button area shape: RoundedRectangleBorder( @@ -394,22 +405,22 @@ class _RemotePageState extends State { final pi = FFI.ffiModel.pi; final isMac = pi.platform == "Mac OS"; final modifiers = [ - wrap('Ctrl', () { + wrap('Ctrl ', () { setState(() => FFI.ctrl = !FFI.ctrl); }, FFI.ctrl), - wrap('Alt', () { + wrap(' Alt ', () { setState(() => FFI.alt = !FFI.alt); }, FFI.alt), wrap('Shift', () { setState(() => FFI.shift = !FFI.shift); }, FFI.shift), - wrap(isMac ? 'Cmd' : 'Win', () { + wrap(isMac ? ' Cmd ' : ' Win ', () { setState(() => FFI.command = !FFI.command); }, FFI.command), ]; final keys = [ wrap( - 'Fn', + ' Fn ', () => setState( () { _fn = !_fn; @@ -420,7 +431,7 @@ class _RemotePageState extends State { ), _fn), wrap( - '...', + ' ... ', () => setState( () { _more = !_more; @@ -460,7 +471,7 @@ class _RemotePageState extends State { wrap('PgUp', () { FFI.inputKey('VK_PRIOR'); }), - wrap('PgDown', () { + wrap('PgDn', () { FFI.inputKey('VK_NEXT'); }), SizedBox(width: 9999), @@ -486,13 +497,14 @@ class _RemotePageState extends State { sendPrompt(isMac, 'VK_S'); }), ]; + final space = size.width > 320 ? 4.0 : 2.0; return Container( color: Color(0xAA000000), padding: EdgeInsets.only( - top: keyboard ? 24 : 4, left: 8, right: 8, bottom: 8), + top: keyboard ? 24 : 4, left: 0, right: 0, bottom: 8), child: Wrap( - spacing: 4, - runSpacing: 4, + spacing: space, + runSpacing: space, children: [SizedBox(width: 9999)] + (keyboard ? modifiers + keys + (_fn ? fn : []) + (_more ? more : []) diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index 4ae6a3f11..da3cb00b0 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.7+9 +version: 1.1.7+10 environment: sdk: ">=2.7.0 <3.0.0" From 1414ff5fcbd0c82575bc604f1c7381aad61dd7f8 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 17 Aug 2021 15:20:37 +0800 Subject: [PATCH 183/422] refactor --- flutter_hbb/lib/remote_page.dart | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 99349a041..4d5ec92ab 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -38,7 +38,7 @@ class _RemotePageState extends State { var _more = true; var _fn = false; final FocusNode _focusNode = FocusNode(); - var _showEdit = false; + var _showKeyboard = false; var _reconnects = 1; @override @@ -78,6 +78,7 @@ class _RemotePageState extends State { setState(() { _bottom = v; if (v < 100) { + _showKeyboard = false; SystemChrome.setEnabledSystemUIOverlays([]); } }); @@ -155,12 +156,12 @@ class _RemotePageState extends State { void openKeyboard() { // destroy first, so that our _value trick can work _value = initText; - setState(() => _showEdit = false); + setState(() => _showKeyboard = false); _timer?.cancel(); _timer = Timer(Duration(milliseconds: 30), () { // show now, and sleep a while to requestFocus to // make sure edit ready, so that keyboard wont show/hide/show/hide happen - setState(() => _showEdit = true); + setState(() => _showKeyboard = true); _timer?.cancel(); _timer = Timer(Duration(milliseconds: 30), () { SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values); @@ -173,7 +174,7 @@ class _RemotePageState extends State { @override Widget build(BuildContext context) { final pi = Provider.of(context).pi; - final hideKeyboard = Platform.isIOS && _showEdit; + final hideKeyboard = Platform.isIOS && _showKeyboard; final showActionButton = !_showBar || hideKeyboard; EasyLoading.instance.loadingStyle = EasyLoadingStyle.light; return WillPopScope( @@ -192,7 +193,7 @@ class _RemotePageState extends State { onPressed: () { setState(() { if (hideKeyboard) { - _showEdit = !_showEdit; + _showKeyboard = !_showKeyboard; } else { _showBar = !_showBar; } @@ -222,7 +223,7 @@ class _RemotePageState extends State { color: Colors.white, icon: Icon(Icons.tv), onPressed: () { - setState(() => _showEdit = false); + setState(() => _showKeyboard = false); showOptions(context); }, ), @@ -243,7 +244,7 @@ class _RemotePageState extends State { color: Colors.white, icon: Icon(Icons.more_vert), onPressed: () { - setState(() => _showEdit = false); + setState(() => _showKeyboard = false); showActions(context); }, ), @@ -323,7 +324,7 @@ class _RemotePageState extends State { SizedBox( width: 0, height: 0, - child: !_showEdit + child: !_showKeyboard ? Container() : TextFormField( textInputAction: TextInputAction.newline, @@ -347,7 +348,7 @@ class _RemotePageState extends State { } Widget getHelpTools() { - final keyboard = _showEdit; + final keyboard = _showKeyboard; if (!_mouseTools && !keyboard) { return SizedBox(); } From 4d730586aea20953b2f6ba2a3e48752748de7926 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 17 Aug 2021 15:31:12 +0800 Subject: [PATCH 184/422] resetMouse if show keyboard --- flutter_hbb/lib/remote_page.dart | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 4d5ec92ab..15f1d4d3a 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -156,6 +156,7 @@ class _RemotePageState extends State { void openKeyboard() { // destroy first, so that our _value trick can work _value = initText; + resetMouse(); setState(() => _showKeyboard = false); _timer?.cancel(); _timer = Timer(Duration(milliseconds: 30), () { @@ -171,6 +172,13 @@ class _RemotePageState extends State { }); } + void resetMouse() { + _drag = false; + _scroll = false; + _right = false; + _mouseTools = false; + } + @override Widget build(BuildContext context) { final pi = Provider.of(context).pi; @@ -300,12 +308,7 @@ class _RemotePageState extends State { onScaleEnd: (details) { if (_drag) { FFI.sendMouse('up', 'left'); - setState(() { - _drag = false; - _scroll = false; - _right = false; - _mouseTools = false; - }); + setState(resetMouse); } else if (_scroll) { var dy = (_yOffset - _yOffset0) / 10; if (dy.abs() > 0.1) { From 72adcacce971b6e1e77c277313d4d7a25bce64dd Mon Sep 17 00:00:00 2001 From: open-trade Date: Tue, 17 Aug 2021 22:57:35 +0800 Subject: [PATCH 185/422] ime works fine now on iOS --- flutter_hbb/lib/remote_page.dart | 60 +++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 12 deletions(-) diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 15f1d4d3a..2ad51e599 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -115,17 +115,49 @@ class _RemotePageState extends State { } void handleInput(String newValue) { - if (_value[0] == '\1' && newValue[0] != '\1') { - // clipboard - _value = ''; + var oldValue = _value; + _value = newValue; + if (Platform.isIOS) { + var i = newValue.length - 1; + for (; i >= 0 && newValue[i] != '\1'; --i) {} + var j = oldValue.length - 1; + for (; j >= 0 && oldValue[j] != '\1'; --j) {} + if (i < j) j = i; + newValue = newValue.substring(j + 1); + oldValue = oldValue.substring(j + 1); + var common = 0; + for (; + common < oldValue.length && + common < newValue.length && + newValue[common] == oldValue[common]; + ++common); + for (i = 0; i < oldValue.length - common; ++i) { + FFI.inputKey('VK_BACK'); + } + if (newValue.length > common) { + var s = newValue.substring(common); + if (s.length > 1) { + FFI.setByName('input_string', s); + } else { + inputChar(s); + } + } + return; } - if (newValue.length <= _value.length) { + if (oldValue.length > 0 && + newValue.length > 0 && + oldValue[0] == '\1' && + newValue[0] != '\1') { + // clipboard + oldValue = ''; + } + if (newValue.length <= oldValue.length) { final char = 'VK_BACK'; FFI.inputKey(char); } else { - final content = newValue.substring(_value.length); + final content = newValue.substring(oldValue.length); if (content.length > 1) { - if (_value != '' && + if (oldValue != '' && content.length == 2 && (content == '""' || content == '()' || @@ -143,14 +175,18 @@ class _RemotePageState extends State { } FFI.setByName('input_string', content); } else { - var char = content; - if (char == '\n') { - char = 'VK_RETURN'; - } - FFI.inputKey(char); + inputChar(content); } } - _value = newValue; + } + + void inputChar(String char) { + if (char == '\n') { + char = 'VK_RETURN'; + } else if (char == ' ') { + char = 'VK_SPACE'; + } + FFI.inputKey(char); } void openKeyboard() { From b20bf7d1cb6d43eae50e7c9847561bdf25644c79 Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 18 Aug 2021 00:39:48 +0800 Subject: [PATCH 186/422] cancel button on loading for iOS --- flutter_hbb/lib/common.dart | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 15588f13c..2d43d5957 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'dart:async'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:tuple/tuple.dart'; +import 'dart:io'; typedef F = String Function(String); @@ -33,18 +34,38 @@ void showLoading(String text, BuildContext context) { Navigator.pop(context); } dismissLoading(); - EasyLoading.show(status: text, maskType: EasyLoadingMaskType.black); + if (Platform.isAndroid) { + EasyLoading.show(status: text, maskType: EasyLoadingMaskType.black); + return; + } + EasyLoading.showWidget( + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Center(child: CircularProgressIndicator()), + SizedBox(height: 20), + Center( + child: + Text(Translator.call(text), style: TextStyle(fontSize: 15))), + SizedBox(height: 20), + Center( + child: TextButton( + style: flatButtonStyle, + onPressed: () { + dismissLoading(); + Navigator.pop(context); + }, + child: Text(Translator.call('Cancel'), + style: TextStyle(color: MyTheme.accent)))) + ], + ), + maskType: EasyLoadingMaskType.black); } void dismissLoading() { EasyLoading.dismiss(); } -void showSuccess(String text) { - dismissLoading(); - EasyLoading.showSuccess(text, maskType: EasyLoadingMaskType.black); -} - bool _hasDialog = false; typedef BuildAlertDailog = Tuple3> Function( void Function(void Function())); From 6c18645561df88b4eb653ec0eeb5db80fc603b5a Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 18 Aug 2021 02:01:03 +0800 Subject: [PATCH 187/422] fix input password dialog bug --- flutter_hbb/lib/common.dart | 6 +++++- flutter_hbb/lib/remote_page.dart | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/flutter_hbb/lib/common.dart b/flutter_hbb/lib/common.dart index 2d43d5957..82c3c754a 100644 --- a/flutter_hbb/lib/common.dart +++ b/flutter_hbb/lib/common.dart @@ -29,6 +29,7 @@ final ButtonStyle flatButtonStyle = TextButton.styleFrom( ), ); +void Function() loadingCancelCallback = null; void showLoading(String text, BuildContext context) { if (_hasDialog && context != null) { Navigator.pop(context); @@ -52,7 +53,10 @@ void showLoading(String text, BuildContext context) { child: TextButton( style: flatButtonStyle, onPressed: () { - dismissLoading(); + // with out loadingCancelCallback, we can see unexpected input password + // dialog shown in home, no clue why, so use this as workaround + // why no such issue on android? + if (loadingCancelCallback != null) loadingCancelCallback(); Navigator.pop(context); }, child: Text(Translator.call('Cancel'), diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 2ad51e599..3345d6219 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -52,18 +52,20 @@ class _RemotePageState extends State { Timer.periodic(Duration(milliseconds: 30), (timer) => interval()); }); Wakelock.enable(); + loadingCancelCallback = () => _interval.cancel(); } @override void dispose() { _focusNode.dispose(); - super.dispose(); FFI.close(); + loadingCancelCallback = null; _interval.cancel(); _timer?.cancel(); dismissLoading(); SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values); Wakelock.disable(); + super.dispose(); } void resetTool() { From bcd550ebef1c5a0ca08bda816c1d1d776a86f239 Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 20 Aug 2021 03:50:10 +0800 Subject: [PATCH 188/422] safearea, still no idea why white screen on physical ios with testflight --- flutter_hbb/build_ios.sh | 3 +- flutter_hbb/ios_x64.sh | 2 ++ flutter_hbb/lib/main.dart | 1 + flutter_hbb/lib/remote_page.dart | 48 +++++++++++++++++--------------- flutter_hbb/pubspec.yaml | 2 +- 5 files changed, 32 insertions(+), 24 deletions(-) create mode 100755 flutter_hbb/ios_x64.sh diff --git a/flutter_hbb/build_ios.sh b/flutter_hbb/build_ios.sh index 6d0d627ac..eb8954165 100755 --- a/flutter_hbb/build_ios.sh +++ b/flutter_hbb/build_ios.sh @@ -1,2 +1,3 @@ #!/usr/bin/env bash -flutter build ipa --release --obfuscate --split-debug-info=./split-debug-info +#flutter build ipa --release --obfuscate --split-debug-info=./split-debug-info +flutter build ipa --release diff --git a/flutter_hbb/ios_x64.sh b/flutter_hbb/ios_x64.sh new file mode 100755 index 000000000..455ef4199 --- /dev/null +++ b/flutter_hbb/ios_x64.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +cargo build --release --target x86_64-apple-ios diff --git a/flutter_hbb/lib/main.dart b/flutter_hbb/lib/main.dart index 70da191ff..32af66665 100644 --- a/flutter_hbb/lib/main.dart +++ b/flutter_hbb/lib/main.dart @@ -25,6 +25,7 @@ class App extends StatelessWidget { child: ChangeNotifierProvider.value( value: FFI.canvasModel, child: MaterialApp( + debugShowCheckedModeBanner: false, title: 'RustDesk', theme: ThemeData( primarySwatch: Colors.blue, diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 3345d6219..afff9ab9d 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -357,29 +357,33 @@ class _RemotePageState extends State { } }, child: Container( - color: MyTheme.canvasColor, - child: Stack(children: [ - ImagePaint(), - CursorPaint(), - getHelpTools(), - SizedBox( - width: 0, - height: 0, - child: !_showKeyboard - ? Container() - : TextFormField( - textInputAction: TextInputAction.newline, - autocorrect: false, - enableSuggestions: false, - focusNode: _focusNode, - maxLines: null, - initialValue: - _value, // trick way to make backspace work always - keyboardType: TextInputType.multiline, - onChanged: handleInput, + color: Colors.black, + child: SafeArea( + child: Container( + color: MyTheme.canvasColor, + child: Stack(children: [ + ImagePaint(), + CursorPaint(), + getHelpTools(), + SizedBox( + width: 0, + height: 0, + child: !_showKeyboard + ? Container() + : TextFormField( + textInputAction: + TextInputAction.newline, + autocorrect: false, + enableSuggestions: false, + focusNode: _focusNode, + maxLines: null, + initialValue: + _value, // trick way to make backspace work always + keyboardType: TextInputType.multiline, + onChanged: handleInput, + ), ), - ), - ]))), + ]))))), )), ); } diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index da3cb00b0..7116743ab 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.7+10 +version: 1.1.7+11 environment: sdk: ">=2.7.0 <3.0.0" From 365a422f2dcd38e0cf6ae48e12774358597a40d5 Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 20 Aug 2021 17:16:46 +0800 Subject: [PATCH 189/422] finally got IPA work --- .../android/app/src/main/AndroidManifest.xml | 4 ++- .../ios/Runner.xcodeproj/project.pbxproj | 3 ++ flutter_hbb/lib/model.dart | 29 ++++++++++--------- flutter_hbb/pubspec.yaml | 2 +- 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/flutter_hbb/android/app/src/main/AndroidManifest.xml b/flutter_hbb/android/app/src/main/AndroidManifest.xml index 567c1ebbb..4a977bb00 100644 --- a/flutter_hbb/android/app/src/main/AndroidManifest.xml +++ b/flutter_hbb/android/app/src/main/AndroidManifest.xml @@ -2,7 +2,9 @@ package="com.carriez.flutter_hbb"> + android:icon="@mipmap/ic_launcher" + android:extractNativeLibs="true" + tools:replace="android:extractNativeLibs"> ('get_by_name'); - _setByName = - dylib.lookupFunction, Pointer), F3>( - 'set_by_name'); - _freeRgba = dylib - .lookupFunction), F4>('free_rgba'); - _getRgba = dylib.lookupFunction('get_rgba'); - _dir = (await getApplicationDocumentsDirectory()).path; - String id = 'NA'; - String name = 'Flutter'; try { + _getByName = dylib.lookupFunction('get_by_name'); + _setByName = + dylib.lookupFunction, Pointer), F3>( + 'set_by_name'); + _freeRgba = dylib + .lookupFunction), F4>('free_rgba'); + _getRgba = dylib.lookupFunction('get_rgba'); + _dir = (await getApplicationDocumentsDirectory()).path; + String id = 'NA'; + String name = 'Flutter'; DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); if (Platform.isAndroid) { AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; @@ -651,12 +654,12 @@ class FFI { name = iosInfo.utsname.machine; id = iosInfo.identifierForVendor.hashCode.toString(); } + setByName('info1', id); + setByName('info2', name); + setByName('init', _dir); } catch (e) { print(e); } - setByName('info1', id); - setByName('info2', name); - setByName('init', _dir); } } diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index 7116743ab..1089bfe82 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.7+11 +version: 1.1.7+18 environment: sdk: ">=2.7.0 <3.0.0" From 3310205fd1169985c3d2940b01e0fb0d0c291e26 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sat, 21 Aug 2021 13:26:40 +0800 Subject: [PATCH 190/422] tools:replace="android:extractNativeLibs" invalid --- flutter_hbb/android/app/src/main/AndroidManifest.xml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/flutter_hbb/android/app/src/main/AndroidManifest.xml b/flutter_hbb/android/app/src/main/AndroidManifest.xml index 4a977bb00..567c1ebbb 100644 --- a/flutter_hbb/android/app/src/main/AndroidManifest.xml +++ b/flutter_hbb/android/app/src/main/AndroidManifest.xml @@ -2,9 +2,7 @@ package="com.carriez.flutter_hbb"> + android:icon="@mipmap/ic_launcher"> Date: Sat, 21 Aug 2021 17:18:14 +0800 Subject: [PATCH 191/422] touch mode and reset canvas --- flutter_hbb/lib/model.dart | 37 ++++- flutter_hbb/lib/remote_page.dart | 266 +++++++++++++++++-------------- 2 files changed, 183 insertions(+), 120 deletions(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 01d251070..397b5b3fc 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -299,6 +299,7 @@ class CanvasModel with ChangeNotifier { _x = 0; _y = 0; _scale = 1.0; + notifyListeners(); } } @@ -341,8 +342,40 @@ class CursorModel with ChangeNotifier { return h - thresh; } - void updatePan(double dx, double dy) { + void touch(double x, double y, bool right) { + final scale = FFI.canvasModel.scale; + final xoffset = FFI.canvasModel.x; + final yoffset = FFI.canvasModel.y; + _x = (x - xoffset) / scale + _displayOriginX; + _y = (y - yoffset) / scale + _displayOriginY; + FFI.moveMouse(_x, _y); + FFI.tap(right); + notifyListeners(); + } + + void reset() { + _x = _displayOriginX; + _y = _displayOriginY; + FFI.moveMouse(_x, _y); + FFI.canvasModel.clear(); + notifyListeners(); + } + + void updatePan(double dx, double dy, bool touchMode, bool drag) { if (FFI.imageModel.image == null) return; + if (touchMode) { + if (drag) { + final scale = FFI.canvasModel.scale; + _x += dx / scale; + _y += dy / scale; + FFI.moveMouse(_x, _y); + notifyListeners(); + } else { + FFI.canvasModel.panX(dx); + FFI.canvasModel.panY(dy); + } + return; + } final scale = FFI.canvasModel.scale; dx /= scale; dy /= scale; @@ -750,6 +783,8 @@ final langs = >{ 'Paste': '粘贴', 'Are you sure to close the connection?': '是否确认关闭连接?', 'Download new version': '下载新版本', + 'Touch mode': '触屏模式', + 'Reset canvas': '重置画布', }, 'en': {} }; diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index afff9ab9d..f936a075f 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -40,6 +40,7 @@ class _RemotePageState extends State { final FocusNode _focusNode = FocusNode(); var _showKeyboard = false; var _reconnects = 1; + var _touchMode = false; @override void initState() { @@ -53,6 +54,7 @@ class _RemotePageState extends State { }); Wakelock.enable(); loadingCancelCallback = () => _interval.cancel(); + _touchMode = FFI.getByName('peer_option', "touch-mode") != ''; } @override @@ -306,59 +308,65 @@ class _RemotePageState extends State { ) : null, body: FlutterEasyLoading( - child: GestureDetector( - onLongPress: () { - if (_drag || _scroll) return; - FFI.tap(true); - }, - onTap: () { - if (_drag || _scroll) return; - FFI.tap(_right); - }, - onScaleStart: (details) { - _scale = 1; - _xOffset = details.focalPoint.dx; - _yOffset = _yOffset0 = details.focalPoint.dy; - if (_drag) { - FFI.sendMouse('down', 'left'); - } - }, - onScaleUpdate: (details) { - var scale = details.scale; - if (scale == 1) { - if (!_scroll) { - var x = details.focalPoint.dx; - var y = details.focalPoint.dy; - var dx = x - _xOffset; - var dy = y - _yOffset; - FFI.cursorModel.updatePan(dx, dy); - _xOffset = x; - _yOffset = y; - } else { - _xOffset = details.focalPoint.dx; - _yOffset = details.focalPoint.dy; - } - } else if (!_drag && !_scroll) { - FFI.canvasModel.updateScale(scale / _scale); - _scale = scale; - } - }, - onScaleEnd: (details) { - if (_drag) { - FFI.sendMouse('up', 'left'); - setState(resetMouse); - } else if (_scroll) { - var dy = (_yOffset - _yOffset0) / 10; - if (dy.abs() > 0.1) { - if (dy > 0 && dy < 1) dy = 1; - if (dy < 0 && dy > -1) dy = -1; - FFI.scroll(dy); - } - } - }, - child: Container( - color: Colors.black, - child: SafeArea( + child: Container( + color: Colors.black, + child: SafeArea( + child: GestureDetector( + onLongPress: () { + if (_drag || _scroll) return; + FFI.tap(true); + }, + onTapUp: (details) { + if (_drag || _scroll) return; + if (_touchMode) { + FFI.cursorModel.touch(details.localPosition.dx, + details.localPosition.dy, _right); + } else { + FFI.tap(_right); + } + }, + onScaleStart: (details) { + _scale = 1; + _xOffset = details.focalPoint.dx; + _yOffset = _yOffset0 = details.focalPoint.dy; + if (_drag) { + FFI.sendMouse('down', 'left'); + } + }, + onScaleUpdate: (details) { + var scale = details.scale; + if (scale == 1) { + if (!_scroll) { + var x = details.focalPoint.dx; + var y = details.focalPoint.dy; + var dx = x - _xOffset; + var dy = y - _yOffset; + FFI.cursorModel + .updatePan(dx, dy, _touchMode, _drag); + _xOffset = x; + _yOffset = y; + } else { + _xOffset = details.focalPoint.dx; + _yOffset = details.focalPoint.dy; + } + } else if (!_drag && !_scroll) { + FFI.canvasModel.updateScale(scale / _scale); + _scale = scale; + } + }, + onScaleEnd: (details) { + if (_drag) { + FFI.sendMouse('up', 'left'); + setState(resetMouse); + } else if (_scroll) { + var dy = (_yOffset - _yOffset0) / 10; + if (dy.abs() > 0.1) { + if (dy > 0 && dy < 1) dy = 1; + if (dy < 0 && dy > -1) dy = -1; + FFI.scroll(dy); + } + } + }, child: Container( color: MyTheme.canvasColor, child: Stack(children: [ @@ -388,6 +396,92 @@ class _RemotePageState extends State { ); } + void showActions(BuildContext context) { + final size = MediaQuery.of(context).size; + final x = 120.0; + final y = size.height; + final more = >[]; + if (FFI.ffiModel.pi.version.isNotEmpty) { + more.add(PopupMenuItem( + child: Text(translate('Refresh')), value: 'refresh')); + } + if (FFI.ffiModel.permissions['keyboard'] != false && + FFI.ffiModel.permissions['clipboard'] != false) { + more.add(PopupMenuItem( + child: Text(translate('Paste')), value: 'paste')); + } + more.add(PopupMenuItem( + child: Row( + children: ([ + Container(width: 100.0, child: Text(translate('OS Password'))), + TextButton( + style: flatButtonStyle, + onPressed: () { + Navigator.pop(context); + showSetOSPassword(context, false); + }, + child: Icon(Icons.edit, color: MyTheme.accent), + ) + ])), + value: 'enter_os_password')); + more.add(PopupMenuItem( + child: Row( + children: ([ + Container(width: 100.0, child: Text(translate('Touch mode'))), + Padding(padding: EdgeInsets.symmetric(horizontal: 16.0)), + Icon( + _touchMode + ? Icons.check_box_outlined + : Icons.check_box_outline_blank, + color: MyTheme.accent) + ])), + value: 'touch_mode')); + more.add(PopupMenuItem( + child: Text(translate('Reset canvas')), value: 'reset_canvas')); + () async { + var value = await showMenu( + context: context, + position: RelativeRect.fromLTRB(x, y, x, y), + items: [ + PopupMenuItem( + child: Text(translate('Insert') + ' Ctrl + Alt + Del'), + value: 'cad'), + PopupMenuItem( + child: Text(translate('Insert Lock')), value: 'lock'), + ] + + more, + elevation: 8, + ); + if (value == 'cad') { + FFI.setByName('ctrl_alt_del'); + } else if (value == 'lock') { + FFI.setByName('lock_screen'); + } else if (value == 'refresh') { + FFI.setByName('refresh'); + } else if (value == 'paste') { + () async { + ClipboardData data = await Clipboard.getData(Clipboard.kTextPlain); + if (data.text != null) { + FFI.setByName('input_string', '${data.text}'); + } + }(); + } else if (value == 'enter_os_password') { + var password = FFI.getByName('peer_option', "os-password"); + if (password != "") { + FFI.setByName('input_os_password', password); + } else { + showSetOSPassword(context, true); + } + } else if (value == 'touch_mode') { + _touchMode = !_touchMode; + final v = _touchMode ? 'Y' : ''; + FFI.setByName('peer_option', '{"name": "touch-mode", "value": "${v}"}'); + } else if (value == 'reset_canvas') { + FFI.cursorModel.reset(); + } + }(); + } + void close() { msgbox('', 'Close', 'Are you sure to close the connection?', context); } @@ -806,72 +900,6 @@ void showOptions(BuildContext context) { }, () async => true, true, 0); } -void showActions(BuildContext context) { - final size = MediaQuery.of(context).size; - final x = 120.0; - final y = size.height; - final more = >[]; - if (FFI.ffiModel.pi.version.isNotEmpty) { - more.add(PopupMenuItem( - child: Text(translate('Refresh')), value: 'refresh')); - } - if (FFI.ffiModel.permissions['keyboard'] != false && - FFI.ffiModel.permissions['clipboard'] != false) { - more.add( - PopupMenuItem(child: Text(translate('Paste')), value: 'paste')); - } - more.add(PopupMenuItem( - child: Row( - children: ([ - Text(translate('OS Password')), - TextButton( - style: flatButtonStyle, - onPressed: () { - Navigator.pop(context); - showSetOSPassword(context, false); - }, - child: Icon(Icons.edit), - ) - ])), - value: 'enter_os_password')); - () async { - var value = await showMenu( - context: context, - position: RelativeRect.fromLTRB(x, y, x, y), - items: [ - PopupMenuItem( - child: Text(translate('Insert') + ' Ctrl + Alt + Del'), - value: 'cad'), - PopupMenuItem( - child: Text(translate('Insert Lock')), value: 'lock'), - ] + - more, - elevation: 8, - ); - if (value == 'cad') { - FFI.setByName('ctrl_alt_del'); - } else if (value == 'lock') { - FFI.setByName('lock_screen'); - } else if (value == 'refresh') { - FFI.setByName('refresh'); - } else if (value == 'paste') { - () async { - ClipboardData data = await Clipboard.getData(Clipboard.kTextPlain); - if (data.text != null) { - FFI.setByName('input_string', '${data.text}'); - } - }(); - } else if (value == 'enter_os_password') { - var password = FFI.getByName('peer_option', "os-password"); - if (password != "") { - FFI.setByName('input_os_password', password); - } else { - showSetOSPassword(context, true); - } - } - }(); -} - void showSetOSPassword(BuildContext context, bool login) { final controller = TextEditingController(); var password = FFI.getByName('peer_option', "os-password"); From 86c7013e53f8ef273b10714b7166bc4400d634fa Mon Sep 17 00:00:00 2001 From: open-trade Date: Sun, 22 Aug 2021 07:27:48 +0800 Subject: [PATCH 192/422] 19 build --- flutter_hbb/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index 1089bfe82..e59bbc0fb 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.7+18 +version: 1.1.7+19 environment: sdk: ">=2.7.0 <3.0.0" From 0316c7bcd85cce9332bc65ffa366cb2571e1ca0f Mon Sep 17 00:00:00 2001 From: open-trade Date: Sun, 22 Aug 2021 07:50:12 +0800 Subject: [PATCH 193/422] clear bug --- flutter_hbb/lib/model.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 397b5b3fc..db897443d 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -295,11 +295,11 @@ class CanvasModel with ChangeNotifier { notifyListeners(); } - void clear() { + void clear([bool notify=false]) { _x = 0; _y = 0; _scale = 1.0; - notifyListeners(); + if (notify) notifyListeners(); } } @@ -357,7 +357,7 @@ class CursorModel with ChangeNotifier { _x = _displayOriginX; _y = _displayOriginY; FFI.moveMouse(_x, _y); - FFI.canvasModel.clear(); + FFI.canvasModel.clear(true); notifyListeners(); } From b2588b32bb003a2bc6a4fdd4ff09b668bea6f41e Mon Sep 17 00:00:00 2001 From: open-trade Date: Sun, 22 Aug 2021 08:18:42 +0800 Subject: [PATCH 194/422] have to use 1.1.8 --- flutter_hbb/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_hbb/pubspec.yaml b/flutter_hbb/pubspec.yaml index e59bbc0fb..886a5956a 100644 --- a/flutter_hbb/pubspec.yaml +++ b/flutter_hbb/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.7+19 +version: 1.1.8+19 environment: sdk: ">=2.7.0 <3.0.0" From 9ab5aec229f057927bb350c1aa64bf46d7a44088 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sun, 22 Aug 2021 22:34:09 +0800 Subject: [PATCH 195/422] fix change id --- flutter_hbb/build_android.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/flutter_hbb/build_android.sh b/flutter_hbb/build_android.sh index 6ea0eba3b..113b49867 100755 --- a/flutter_hbb/build_android.sh +++ b/flutter_hbb/build_android.sh @@ -1,2 +1,3 @@ #!/usr/bin/env bash flutter build apk --target-platform android-arm64 --release --obfuscate --split-debug-info ./split-debug-info +flutter build appbundle --target-platform android-arm64 --release --obfuscate --split-debug-info ./split-debug-info From f00abb64cb25466ed97bb5cd417c688c69fb3ddf Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 26 Aug 2021 23:48:45 +0800 Subject: [PATCH 196/422] obfuscate also help reduce file size on ios --- flutter_hbb/build_android.sh | 1 + flutter_hbb/build_ios.sh | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/flutter_hbb/build_android.sh b/flutter_hbb/build_android.sh index 113b49867..b4ba09671 100755 --- a/flutter_hbb/build_android.sh +++ b/flutter_hbb/build_android.sh @@ -1,3 +1,4 @@ #!/usr/bin/env bash +$ANDROID_NDK/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-strip android/app/src/main/jniLibs/arm64-v8a/* flutter build apk --target-platform android-arm64 --release --obfuscate --split-debug-info ./split-debug-info flutter build appbundle --target-platform android-arm64 --release --obfuscate --split-debug-info ./split-debug-info diff --git a/flutter_hbb/build_ios.sh b/flutter_hbb/build_ios.sh index eb8954165..6d0d627ac 100755 --- a/flutter_hbb/build_ios.sh +++ b/flutter_hbb/build_ios.sh @@ -1,3 +1,2 @@ #!/usr/bin/env bash -#flutter build ipa --release --obfuscate --split-debug-info=./split-debug-info -flutter build ipa --release +flutter build ipa --release --obfuscate --split-debug-info=./split-debug-info From 83736732fc35b0e98cc9c6c2be66e3e7aaf3ee4e Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sun, 29 Aug 2021 19:21:16 +0800 Subject: [PATCH 197/422] refactor and remove space from id in mobile --- flutter_hbb/lib/home_page.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/flutter_hbb/lib/home_page.dart b/flutter_hbb/lib/home_page.dart index fb38dd5bd..19ca26339 100644 --- a/flutter_hbb/lib/home_page.dart +++ b/flutter_hbb/lib/home_page.dart @@ -107,6 +107,7 @@ class _HomePageState extends State { void connect(String id) { if (id == '') return; + id = id.replaceAll(' ', ''); () async { await Navigator.push( context, From 903a4ea27f380d1fa0f0906f8dfb00eee3dadce9 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 30 Aug 2021 21:12:31 +0800 Subject: [PATCH 198/422] remove subfolder flutter_hbb --- .gitattributes | 1 + flutter_hbb/.gitignore => .gitignore | 0 flutter_hbb/.metadata => .metadata | 0 {flutter_hbb/android => android}/.gitignore | 0 {flutter_hbb/android => android}/app/build.gradle | 0 .../android => android}/app/google-services.json | 0 .../app/src/debug/AndroidManifest.xml | 0 .../app/src/main/AndroidManifest.xml | 0 .../kotlin/com/carriez/flutter_hbb/MainActivity.kt | 0 .../src/main/res/drawable-v21/launch_background.xml | 0 .../app/src/main/res/drawable/launch_background.xml | 0 .../app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin .../app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin .../app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin .../app/src/main/res/mipmap-xxhdpi/ic_launcher.png | Bin .../app/src/main/res/mipmap-xxxhdpi/ic_launcher.png | Bin .../app/src/main/res/values-night/styles.xml | 0 .../app/src/main/res/values/styles.xml | 0 .../app/src/profile/AndroidManifest.xml | 0 {flutter_hbb/android => android}/build.gradle | 0 .../android => android}/flutter_hbb_android.iml | 0 {flutter_hbb/android => android}/gradle.properties | 0 .../gradle/wrapper/gradle-wrapper.properties | 0 {flutter_hbb/android => android}/key.jks | Bin {flutter_hbb/android => android}/key.properties | 0 {flutter_hbb/android => android}/settings.gradle | 0 {flutter_hbb/assets => assets}/insecure.png | Bin {flutter_hbb/assets => assets}/insecure_relay.png | Bin {flutter_hbb/assets => assets}/linux.png | Bin {flutter_hbb/assets => assets}/mac.png | Bin {flutter_hbb/assets => assets}/secure.png | Bin {flutter_hbb/assets => assets}/secure_relay.png | Bin {flutter_hbb/assets => assets}/win.png | Bin flutter_hbb/build_android.sh => build_android.sh | 0 flutter_hbb/build_ios.sh => build_ios.sh | 0 {flutter_hbb/ios => ios}/.gitignore | 0 .../ios => ios}/Flutter/AppFrameworkInfo.plist | 0 {flutter_hbb/ios => ios}/Flutter/Debug.xcconfig | 0 {flutter_hbb/ios => ios}/Flutter/Release.xcconfig | 0 {flutter_hbb/ios => ios}/Podfile | 0 {flutter_hbb/ios => ios}/Podfile.lock | 0 .../ios => ios}/Runner.xcodeproj/project.pbxproj | 0 .../project.xcworkspace/contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/WorkspaceSettings.xcsettings | 0 .../xcshareddata/xcschemes/Runner.xcscheme | 0 .../Runner.xcworkspace/contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/WorkspaceSettings.xcsettings | 0 {flutter_hbb/ios => ios}/Runner/AppDelegate.swift | 0 .../AppIcon.appiconset/Contents.json | 0 .../AppIcon.appiconset/Icon-App-1024x1024@1x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin .../AppIcon.appiconset/Icon-App-83.5x83.5@2x.png | Bin .../LaunchImage.imageset/Contents.json | 0 .../LaunchImage.imageset/LaunchImage.png | Bin .../LaunchImage.imageset/LaunchImage@2x.png | Bin .../LaunchImage.imageset/LaunchImage@3x.png | Bin .../Assets.xcassets/LaunchImage.imageset/README.md | 0 .../Runner/Base.lproj/LaunchScreen.storyboard | 0 .../ios => ios}/Runner/Base.lproj/Main.storyboard | 0 .../ios => ios}/Runner/GoogleService-Info.plist | 0 {flutter_hbb/ios => ios}/Runner/Info.plist | 0 .../ios => ios}/Runner/Runner-Bridging-Header.h | 0 {flutter_hbb/ios => ios}/Runner/Runner.entitlements | 0 {flutter_hbb/ios => ios}/Runner/ffi.h | 0 flutter_hbb/ios_arm64.sh => ios_arm64.sh | 0 flutter_hbb/ios_x64.sh => ios_x64.sh | 0 {flutter_hbb/lib => lib}/common.dart | 0 {flutter_hbb/lib => lib}/home_page.dart | 0 {flutter_hbb/lib => lib}/main.dart | 0 {flutter_hbb/lib => lib}/model.dart | 0 {flutter_hbb/lib => lib}/remote_page.dart | 0 flutter_hbb/ndk_arm64.sh => ndk_arm64.sh | 0 flutter_hbb/ndk_x64.sh => ndk_x64.sh | 0 flutter_hbb/pubspec.lock => pubspec.lock | 0 flutter_hbb/pubspec.yaml => pubspec.yaml | 0 {flutter_hbb/test => test}/widget_test.dart | 0 90 files changed, 1 insertion(+) create mode 100644 .gitattributes rename flutter_hbb/.gitignore => .gitignore (100%) rename flutter_hbb/.metadata => .metadata (100%) rename {flutter_hbb/android => android}/.gitignore (100%) rename {flutter_hbb/android => android}/app/build.gradle (100%) rename {flutter_hbb/android => android}/app/google-services.json (100%) rename {flutter_hbb/android => android}/app/src/debug/AndroidManifest.xml (100%) rename {flutter_hbb/android => android}/app/src/main/AndroidManifest.xml (100%) rename {flutter_hbb/android => android}/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt (100%) rename {flutter_hbb/android => android}/app/src/main/res/drawable-v21/launch_background.xml (100%) rename {flutter_hbb/android => android}/app/src/main/res/drawable/launch_background.xml (100%) rename {flutter_hbb/android => android}/app/src/main/res/mipmap-hdpi/ic_launcher.png (100%) rename {flutter_hbb/android => android}/app/src/main/res/mipmap-mdpi/ic_launcher.png (100%) rename {flutter_hbb/android => android}/app/src/main/res/mipmap-xhdpi/ic_launcher.png (100%) rename {flutter_hbb/android => android}/app/src/main/res/mipmap-xxhdpi/ic_launcher.png (100%) rename {flutter_hbb/android => android}/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename {flutter_hbb/android => android}/app/src/main/res/values-night/styles.xml (100%) rename {flutter_hbb/android => android}/app/src/main/res/values/styles.xml (100%) rename {flutter_hbb/android => android}/app/src/profile/AndroidManifest.xml (100%) rename {flutter_hbb/android => android}/build.gradle (100%) rename {flutter_hbb/android => android}/flutter_hbb_android.iml (100%) rename {flutter_hbb/android => android}/gradle.properties (100%) rename {flutter_hbb/android => android}/gradle/wrapper/gradle-wrapper.properties (100%) rename {flutter_hbb/android => android}/key.jks (100%) rename {flutter_hbb/android => android}/key.properties (100%) rename {flutter_hbb/android => android}/settings.gradle (100%) rename {flutter_hbb/assets => assets}/insecure.png (100%) rename {flutter_hbb/assets => assets}/insecure_relay.png (100%) rename {flutter_hbb/assets => assets}/linux.png (100%) rename {flutter_hbb/assets => assets}/mac.png (100%) rename {flutter_hbb/assets => assets}/secure.png (100%) rename {flutter_hbb/assets => assets}/secure_relay.png (100%) rename {flutter_hbb/assets => assets}/win.png (100%) rename flutter_hbb/build_android.sh => build_android.sh (100%) rename flutter_hbb/build_ios.sh => build_ios.sh (100%) rename {flutter_hbb/ios => ios}/.gitignore (100%) rename {flutter_hbb/ios => ios}/Flutter/AppFrameworkInfo.plist (100%) rename {flutter_hbb/ios => ios}/Flutter/Debug.xcconfig (100%) rename {flutter_hbb/ios => ios}/Flutter/Release.xcconfig (100%) rename {flutter_hbb/ios => ios}/Podfile (100%) rename {flutter_hbb/ios => ios}/Podfile.lock (100%) rename {flutter_hbb/ios => ios}/Runner.xcodeproj/project.pbxproj (100%) rename {flutter_hbb/ios => ios}/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata (100%) rename {flutter_hbb/ios => ios}/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename {flutter_hbb/ios => ios}/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings (100%) rename {flutter_hbb/ios => ios}/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme (100%) rename {flutter_hbb/ios => ios}/Runner.xcworkspace/contents.xcworkspacedata (100%) rename {flutter_hbb/ios => ios}/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename {flutter_hbb/ios => ios}/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings (100%) rename {flutter_hbb/ios => ios}/Runner/AppDelegate.swift (100%) rename {flutter_hbb/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename {flutter_hbb/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png (100%) rename {flutter_hbb/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png (100%) rename {flutter_hbb/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png (100%) rename {flutter_hbb/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png (100%) rename {flutter_hbb/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png (100%) rename {flutter_hbb/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png (100%) rename {flutter_hbb/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png (100%) rename {flutter_hbb/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png (100%) rename {flutter_hbb/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png (100%) rename {flutter_hbb/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png (100%) rename {flutter_hbb/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png (100%) rename {flutter_hbb/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png (100%) rename {flutter_hbb/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png (100%) rename {flutter_hbb/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png (100%) rename {flutter_hbb/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png (100%) rename {flutter_hbb/ios => ios}/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json (100%) rename {flutter_hbb/ios => ios}/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png (100%) rename {flutter_hbb/ios => ios}/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png (100%) rename {flutter_hbb/ios => ios}/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png (100%) rename {flutter_hbb/ios => ios}/Runner/Assets.xcassets/LaunchImage.imageset/README.md (100%) rename {flutter_hbb/ios => ios}/Runner/Base.lproj/LaunchScreen.storyboard (100%) rename {flutter_hbb/ios => ios}/Runner/Base.lproj/Main.storyboard (100%) rename {flutter_hbb/ios => ios}/Runner/GoogleService-Info.plist (100%) rename {flutter_hbb/ios => ios}/Runner/Info.plist (100%) rename {flutter_hbb/ios => ios}/Runner/Runner-Bridging-Header.h (100%) rename {flutter_hbb/ios => ios}/Runner/Runner.entitlements (100%) rename {flutter_hbb/ios => ios}/Runner/ffi.h (100%) rename flutter_hbb/ios_arm64.sh => ios_arm64.sh (100%) rename flutter_hbb/ios_x64.sh => ios_x64.sh (100%) rename {flutter_hbb/lib => lib}/common.dart (100%) rename {flutter_hbb/lib => lib}/home_page.dart (100%) rename {flutter_hbb/lib => lib}/main.dart (100%) rename {flutter_hbb/lib => lib}/model.dart (100%) rename {flutter_hbb/lib => lib}/remote_page.dart (100%) rename flutter_hbb/ndk_arm64.sh => ndk_arm64.sh (100%) rename flutter_hbb/ndk_x64.sh => ndk_x64.sh (100%) rename flutter_hbb/pubspec.lock => pubspec.lock (100%) rename flutter_hbb/pubspec.yaml => pubspec.yaml (100%) rename {flutter_hbb/test => test}/widget_test.dart (100%) diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..176a458f9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/flutter_hbb/.gitignore b/.gitignore similarity index 100% rename from flutter_hbb/.gitignore rename to .gitignore diff --git a/flutter_hbb/.metadata b/.metadata similarity index 100% rename from flutter_hbb/.metadata rename to .metadata diff --git a/flutter_hbb/android/.gitignore b/android/.gitignore similarity index 100% rename from flutter_hbb/android/.gitignore rename to android/.gitignore diff --git a/flutter_hbb/android/app/build.gradle b/android/app/build.gradle similarity index 100% rename from flutter_hbb/android/app/build.gradle rename to android/app/build.gradle diff --git a/flutter_hbb/android/app/google-services.json b/android/app/google-services.json similarity index 100% rename from flutter_hbb/android/app/google-services.json rename to android/app/google-services.json diff --git a/flutter_hbb/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml similarity index 100% rename from flutter_hbb/android/app/src/debug/AndroidManifest.xml rename to android/app/src/debug/AndroidManifest.xml diff --git a/flutter_hbb/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml similarity index 100% rename from flutter_hbb/android/app/src/main/AndroidManifest.xml rename to android/app/src/main/AndroidManifest.xml diff --git a/flutter_hbb/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt b/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt similarity index 100% rename from flutter_hbb/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt rename to android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt diff --git a/flutter_hbb/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml similarity index 100% rename from flutter_hbb/android/app/src/main/res/drawable-v21/launch_background.xml rename to android/app/src/main/res/drawable-v21/launch_background.xml diff --git a/flutter_hbb/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml similarity index 100% rename from flutter_hbb/android/app/src/main/res/drawable/launch_background.xml rename to android/app/src/main/res/drawable/launch_background.xml diff --git a/flutter_hbb/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from flutter_hbb/android/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to android/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/flutter_hbb/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from flutter_hbb/android/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to android/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/flutter_hbb/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from flutter_hbb/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to android/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/flutter_hbb/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from flutter_hbb/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/flutter_hbb/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from flutter_hbb/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/flutter_hbb/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml similarity index 100% rename from flutter_hbb/android/app/src/main/res/values-night/styles.xml rename to android/app/src/main/res/values-night/styles.xml diff --git a/flutter_hbb/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml similarity index 100% rename from flutter_hbb/android/app/src/main/res/values/styles.xml rename to android/app/src/main/res/values/styles.xml diff --git a/flutter_hbb/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml similarity index 100% rename from flutter_hbb/android/app/src/profile/AndroidManifest.xml rename to android/app/src/profile/AndroidManifest.xml diff --git a/flutter_hbb/android/build.gradle b/android/build.gradle similarity index 100% rename from flutter_hbb/android/build.gradle rename to android/build.gradle diff --git a/flutter_hbb/android/flutter_hbb_android.iml b/android/flutter_hbb_android.iml similarity index 100% rename from flutter_hbb/android/flutter_hbb_android.iml rename to android/flutter_hbb_android.iml diff --git a/flutter_hbb/android/gradle.properties b/android/gradle.properties similarity index 100% rename from flutter_hbb/android/gradle.properties rename to android/gradle.properties diff --git a/flutter_hbb/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from flutter_hbb/android/gradle/wrapper/gradle-wrapper.properties rename to android/gradle/wrapper/gradle-wrapper.properties diff --git a/flutter_hbb/android/key.jks b/android/key.jks similarity index 100% rename from flutter_hbb/android/key.jks rename to android/key.jks diff --git a/flutter_hbb/android/key.properties b/android/key.properties similarity index 100% rename from flutter_hbb/android/key.properties rename to android/key.properties diff --git a/flutter_hbb/android/settings.gradle b/android/settings.gradle similarity index 100% rename from flutter_hbb/android/settings.gradle rename to android/settings.gradle diff --git a/flutter_hbb/assets/insecure.png b/assets/insecure.png similarity index 100% rename from flutter_hbb/assets/insecure.png rename to assets/insecure.png diff --git a/flutter_hbb/assets/insecure_relay.png b/assets/insecure_relay.png similarity index 100% rename from flutter_hbb/assets/insecure_relay.png rename to assets/insecure_relay.png diff --git a/flutter_hbb/assets/linux.png b/assets/linux.png similarity index 100% rename from flutter_hbb/assets/linux.png rename to assets/linux.png diff --git a/flutter_hbb/assets/mac.png b/assets/mac.png similarity index 100% rename from flutter_hbb/assets/mac.png rename to assets/mac.png diff --git a/flutter_hbb/assets/secure.png b/assets/secure.png similarity index 100% rename from flutter_hbb/assets/secure.png rename to assets/secure.png diff --git a/flutter_hbb/assets/secure_relay.png b/assets/secure_relay.png similarity index 100% rename from flutter_hbb/assets/secure_relay.png rename to assets/secure_relay.png diff --git a/flutter_hbb/assets/win.png b/assets/win.png similarity index 100% rename from flutter_hbb/assets/win.png rename to assets/win.png diff --git a/flutter_hbb/build_android.sh b/build_android.sh similarity index 100% rename from flutter_hbb/build_android.sh rename to build_android.sh diff --git a/flutter_hbb/build_ios.sh b/build_ios.sh similarity index 100% rename from flutter_hbb/build_ios.sh rename to build_ios.sh diff --git a/flutter_hbb/ios/.gitignore b/ios/.gitignore similarity index 100% rename from flutter_hbb/ios/.gitignore rename to ios/.gitignore diff --git a/flutter_hbb/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist similarity index 100% rename from flutter_hbb/ios/Flutter/AppFrameworkInfo.plist rename to ios/Flutter/AppFrameworkInfo.plist diff --git a/flutter_hbb/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig similarity index 100% rename from flutter_hbb/ios/Flutter/Debug.xcconfig rename to ios/Flutter/Debug.xcconfig diff --git a/flutter_hbb/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig similarity index 100% rename from flutter_hbb/ios/Flutter/Release.xcconfig rename to ios/Flutter/Release.xcconfig diff --git a/flutter_hbb/ios/Podfile b/ios/Podfile similarity index 100% rename from flutter_hbb/ios/Podfile rename to ios/Podfile diff --git a/flutter_hbb/ios/Podfile.lock b/ios/Podfile.lock similarity index 100% rename from flutter_hbb/ios/Podfile.lock rename to ios/Podfile.lock diff --git a/flutter_hbb/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj similarity index 100% rename from flutter_hbb/ios/Runner.xcodeproj/project.pbxproj rename to ios/Runner.xcodeproj/project.pbxproj diff --git a/flutter_hbb/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from flutter_hbb/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/flutter_hbb/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from flutter_hbb/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/flutter_hbb/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from flutter_hbb/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/flutter_hbb/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 100% rename from flutter_hbb/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme diff --git a/flutter_hbb/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata similarity index 100% rename from flutter_hbb/ios/Runner.xcworkspace/contents.xcworkspacedata rename to ios/Runner.xcworkspace/contents.xcworkspacedata diff --git a/flutter_hbb/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from flutter_hbb/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/flutter_hbb/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from flutter_hbb/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/flutter_hbb/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift similarity index 100% rename from flutter_hbb/ios/Runner/AppDelegate.swift rename to ios/Runner/AppDelegate.swift diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png similarity index 100% rename from flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png similarity index 100% rename from flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png similarity index 100% rename from flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png similarity index 100% rename from flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png similarity index 100% rename from flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png similarity index 100% rename from flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png similarity index 100% rename from flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png similarity index 100% rename from flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png similarity index 100% rename from flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png similarity index 100% rename from flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png similarity index 100% rename from flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png similarity index 100% rename from flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png similarity index 100% rename from flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png similarity index 100% rename from flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png similarity index 100% rename from flutter_hbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json similarity index 100% rename from flutter_hbb/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json rename to ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png similarity index 100% rename from flutter_hbb/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png rename to ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png similarity index 100% rename from flutter_hbb/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png rename to ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png similarity index 100% rename from flutter_hbb/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png rename to ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png diff --git a/flutter_hbb/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md similarity index 100% rename from flutter_hbb/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md rename to ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md diff --git a/flutter_hbb/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from flutter_hbb/ios/Runner/Base.lproj/LaunchScreen.storyboard rename to ios/Runner/Base.lproj/LaunchScreen.storyboard diff --git a/flutter_hbb/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard similarity index 100% rename from flutter_hbb/ios/Runner/Base.lproj/Main.storyboard rename to ios/Runner/Base.lproj/Main.storyboard diff --git a/flutter_hbb/ios/Runner/GoogleService-Info.plist b/ios/Runner/GoogleService-Info.plist similarity index 100% rename from flutter_hbb/ios/Runner/GoogleService-Info.plist rename to ios/Runner/GoogleService-Info.plist diff --git a/flutter_hbb/ios/Runner/Info.plist b/ios/Runner/Info.plist similarity index 100% rename from flutter_hbb/ios/Runner/Info.plist rename to ios/Runner/Info.plist diff --git a/flutter_hbb/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h similarity index 100% rename from flutter_hbb/ios/Runner/Runner-Bridging-Header.h rename to ios/Runner/Runner-Bridging-Header.h diff --git a/flutter_hbb/ios/Runner/Runner.entitlements b/ios/Runner/Runner.entitlements similarity index 100% rename from flutter_hbb/ios/Runner/Runner.entitlements rename to ios/Runner/Runner.entitlements diff --git a/flutter_hbb/ios/Runner/ffi.h b/ios/Runner/ffi.h similarity index 100% rename from flutter_hbb/ios/Runner/ffi.h rename to ios/Runner/ffi.h diff --git a/flutter_hbb/ios_arm64.sh b/ios_arm64.sh similarity index 100% rename from flutter_hbb/ios_arm64.sh rename to ios_arm64.sh diff --git a/flutter_hbb/ios_x64.sh b/ios_x64.sh similarity index 100% rename from flutter_hbb/ios_x64.sh rename to ios_x64.sh diff --git a/flutter_hbb/lib/common.dart b/lib/common.dart similarity index 100% rename from flutter_hbb/lib/common.dart rename to lib/common.dart diff --git a/flutter_hbb/lib/home_page.dart b/lib/home_page.dart similarity index 100% rename from flutter_hbb/lib/home_page.dart rename to lib/home_page.dart diff --git a/flutter_hbb/lib/main.dart b/lib/main.dart similarity index 100% rename from flutter_hbb/lib/main.dart rename to lib/main.dart diff --git a/flutter_hbb/lib/model.dart b/lib/model.dart similarity index 100% rename from flutter_hbb/lib/model.dart rename to lib/model.dart diff --git a/flutter_hbb/lib/remote_page.dart b/lib/remote_page.dart similarity index 100% rename from flutter_hbb/lib/remote_page.dart rename to lib/remote_page.dart diff --git a/flutter_hbb/ndk_arm64.sh b/ndk_arm64.sh similarity index 100% rename from flutter_hbb/ndk_arm64.sh rename to ndk_arm64.sh diff --git a/flutter_hbb/ndk_x64.sh b/ndk_x64.sh similarity index 100% rename from flutter_hbb/ndk_x64.sh rename to ndk_x64.sh diff --git a/flutter_hbb/pubspec.lock b/pubspec.lock similarity index 100% rename from flutter_hbb/pubspec.lock rename to pubspec.lock diff --git a/flutter_hbb/pubspec.yaml b/pubspec.yaml similarity index 100% rename from flutter_hbb/pubspec.yaml rename to pubspec.yaml diff --git a/flutter_hbb/test/widget_test.dart b/test/widget_test.dart similarity index 100% rename from flutter_hbb/test/widget_test.dart rename to test/widget_test.dart From ce3e9ce8d6115755a70d02fbe5dffe26bddd62b8 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 1 Sep 2021 01:35:01 +0800 Subject: [PATCH 199/422] fix keyboard not shown on some android problem because my stupid change, also make long press not only for right click, so that to make show password eye work --- lib/remote_page.dart | 29 +++++++++++++++++++---------- pubspec.yaml | 2 +- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/lib/remote_page.dart b/lib/remote_page.dart index f936a075f..ca728b94b 100644 --- a/lib/remote_page.dart +++ b/lib/remote_page.dart @@ -38,7 +38,7 @@ class _RemotePageState extends State { var _more = true; var _fn = false; final FocusNode _focusNode = FocusNode(); - var _showKeyboard = false; + var _showEdit = false; var _reconnects = 1; var _touchMode = false; @@ -75,6 +75,10 @@ class _RemotePageState extends State { FFI.resetModifiers(); } + bool isKeyboardShown() { + return _bottom >= 100; + } + void interval() { var v = MediaQuery.of(context).viewInsets.bottom; if (v != _bottom) { @@ -82,7 +86,6 @@ class _RemotePageState extends State { setState(() { _bottom = v; if (v < 100) { - _showKeyboard = false; SystemChrome.setEnabledSystemUIOverlays([]); } }); @@ -197,12 +200,12 @@ class _RemotePageState extends State { // destroy first, so that our _value trick can work _value = initText; resetMouse(); - setState(() => _showKeyboard = false); + setState(() => _showEdit = false); _timer?.cancel(); _timer = Timer(Duration(milliseconds: 30), () { // show now, and sleep a while to requestFocus to // make sure edit ready, so that keyboard wont show/hide/show/hide happen - setState(() => _showKeyboard = true); + setState(() => _showEdit = true); _timer?.cancel(); _timer = Timer(Duration(milliseconds: 30), () { SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values); @@ -222,7 +225,7 @@ class _RemotePageState extends State { @override Widget build(BuildContext context) { final pi = Provider.of(context).pi; - final hideKeyboard = Platform.isIOS && _showKeyboard; + final hideKeyboard = isKeyboardShown() && _showEdit; final showActionButton = !_showBar || hideKeyboard; EasyLoading.instance.loadingStyle = EasyLoadingStyle.light; return WillPopScope( @@ -241,7 +244,7 @@ class _RemotePageState extends State { onPressed: () { setState(() { if (hideKeyboard) { - _showKeyboard = !_showKeyboard; + _showEdit = false; } else { _showBar = !_showBar; } @@ -271,7 +274,7 @@ class _RemotePageState extends State { color: Colors.white, icon: Icon(Icons.tv), onPressed: () { - setState(() => _showKeyboard = false); + setState(() => _showEdit = false); showOptions(context); }, ), @@ -292,7 +295,7 @@ class _RemotePageState extends State { color: Colors.white, icon: Icon(Icons.more_vert), onPressed: () { - setState(() => _showKeyboard = false); + setState(() => _showEdit = false); showActions(context); }, ), @@ -314,8 +317,14 @@ class _RemotePageState extends State { child: GestureDetector( onLongPress: () { if (_drag || _scroll) return; + // make right click and real left long click both work + // should add "long press = right click" option? + FFI.sendMouse('down', 'left'); FFI.tap(true); }, + onLongPressUp: () { + FFI.sendMouse('up', 'left'); + }, onTapUp: (details) { if (_drag || _scroll) return; if (_touchMode) { @@ -376,7 +385,7 @@ class _RemotePageState extends State { SizedBox( width: 0, height: 0, - child: !_showKeyboard + child: !_showEdit ? Container() : TextFormField( textInputAction: @@ -487,7 +496,7 @@ class _RemotePageState extends State { } Widget getHelpTools() { - final keyboard = _showKeyboard; + final keyboard = isKeyboardShown(); if (!_mouseTools && !keyboard) { return SizedBox(); } diff --git a/pubspec.yaml b/pubspec.yaml index 886a5956a..a6fa95449 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.8+19 +version: 1.1.9+20 environment: sdk: ">=2.7.0 <3.0.0" From b2c8c247b20e36980c907bcd8e02fc38a54c9110 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 1 Sep 2021 01:48:41 +0800 Subject: [PATCH 200/422] fix abnormal backspace issue reported from google play --- lib/remote_page.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/remote_page.dart b/lib/remote_page.dart index ca728b94b..5fdaac60e 100644 --- a/lib/remote_page.dart +++ b/lib/remote_page.dart @@ -158,7 +158,9 @@ class _RemotePageState extends State { // clipboard oldValue = ''; } - if (newValue.length <= oldValue.length) { + if (newValue.length == oldValue.length) { + // ? + } else if (newValue.length < oldValue.length) { final char = 'VK_BACK'; FFI.inputKey(char); } else { From eeb30aa0d17392f1fb842769c4f5debeff9ac017 Mon Sep 17 00:00:00 2001 From: csf Date: Mon, 10 Jan 2022 10:45:22 +0800 Subject: [PATCH 201/422] create branch --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index eea406451..72307b910 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,5 @@ app.*.symbols # Obfuscation related app.*.map.json jniLibs + +.vscode \ No newline at end of file From 5d4b557e22e59655ee51cf5d5f84142f5df5647f Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 17 Jan 2022 15:45:42 +0800 Subject: [PATCH 202/422] init --- .gitattributes | 1 + .gitignore | 5 ++ favicon.svg | 15 ++++ index.html | 13 +++ package.json | 13 +++ src/main.ts | 8 ++ src/style.css | 8 ++ src/vite-env.d.ts | 1 + tsconfig.json | 18 +++++ yarn.lock | 202 ++++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 284 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 favicon.svg create mode 100644 index.html create mode 100644 package.json create mode 100644 src/main.ts create mode 100644 src/style.css create mode 100644 src/vite-env.d.ts create mode 100644 tsconfig.json create mode 100644 yarn.lock diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..176a458f9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..53f7466ac --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local \ No newline at end of file diff --git a/favicon.svg b/favicon.svg new file mode 100644 index 000000000..de4aeddc1 --- /dev/null +++ b/favicon.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/index.html b/index.html new file mode 100644 index 000000000..867581c51 --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite App + + +

    + + + diff --git a/package.json b/package.json new file mode 100644 index 000000000..ec6b7fac4 --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "name": "web_hbb", + "version": "1.0.0", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "devDependencies": { + "typescript": "^4.4.4", + "vite": "^2.7.2" + } +} diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 000000000..f77db7a8f --- /dev/null +++ b/src/main.ts @@ -0,0 +1,8 @@ +import './style.css' + +const app = document.querySelector('#app')! + +app.innerHTML = ` +

    Hello Vite!

    +
    Documentation +` diff --git a/src/style.css b/src/style.css new file mode 100644 index 000000000..852de7aa2 --- /dev/null +++ b/src/style.css @@ -0,0 +1,8 @@ +#app { + font-family: Avenir, Helvetica, Arial, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-align: center; + color: #2c3e50; + margin-top: 60px; +} diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 000000000..11f02fe2a --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..8cdbb2ac9 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ESNext", "DOM"], + "moduleResolution": "Node", + "strict": true, + "sourceMap": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "noEmit": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true + }, + "include": ["./src"] +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 000000000..2cfb703a5 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,202 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +esbuild-android-arm64@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.13.15.tgz#3fc3ff0bab76fe35dd237476b5d2b32bb20a3d44" + integrity sha512-m602nft/XXeO8YQPUDVoHfjyRVPdPgjyyXOxZ44MK/agewFFkPa8tUo6lAzSWh5Ui5PB4KR9UIFTSBKh/RrCmg== + +esbuild-darwin-64@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.13.15.tgz#8e9169c16baf444eacec60d09b24d11b255a8e72" + integrity sha512-ihOQRGs2yyp7t5bArCwnvn2Atr6X4axqPpEdCFPVp7iUj4cVSdisgvEKdNR7yH3JDjW6aQDw40iQFoTqejqxvQ== + +esbuild-darwin-arm64@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.15.tgz#1b07f893b632114f805e188ddfca41b2b778229a" + integrity sha512-i1FZssTVxUqNlJ6cBTj5YQj4imWy3m49RZRnHhLpefFIh0To05ow9DTrXROTE1urGTQCloFUXTX8QfGJy1P8dQ== + +esbuild-freebsd-64@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.15.tgz#0b8b7eca1690c8ec94c75680c38c07269c1f4a85" + integrity sha512-G3dLBXUI6lC6Z09/x+WtXBXbOYQZ0E8TDBqvn7aMaOCzryJs8LyVXKY4CPnHFXZAbSwkCbqiPuSQ1+HhrNk7EA== + +esbuild-freebsd-arm64@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.15.tgz#2e1a6c696bfdcd20a99578b76350b41db1934e52" + integrity sha512-KJx0fzEDf1uhNOZQStV4ujg30WlnwqUASaGSFPhznLM/bbheu9HhqZ6mJJZM32lkyfGJikw0jg7v3S0oAvtvQQ== + +esbuild-linux-32@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.13.15.tgz#6fd39f36fc66dd45b6b5f515728c7bbebc342a69" + integrity sha512-ZvTBPk0YWCLMCXiFmD5EUtB30zIPvC5Itxz0mdTu/xZBbbHJftQgLWY49wEPSn2T/TxahYCRDWun5smRa0Tu+g== + +esbuild-linux-64@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.13.15.tgz#9cb8e4bcd7574e67946e4ee5f1f1e12386bb6dd3" + integrity sha512-eCKzkNSLywNeQTRBxJRQ0jxRCl2YWdMB3+PkWFo2BBQYC5mISLIVIjThNtn6HUNqua1pnvgP5xX0nHbZbPj5oA== + +esbuild-linux-arm64@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.15.tgz#3891aa3704ec579a1b92d2a586122e5b6a2bfba1" + integrity sha512-bYpuUlN6qYU9slzr/ltyLTR9YTBS7qUDymO8SV7kjeNext61OdmqFAzuVZom+OLW1HPHseBfJ/JfdSlx8oTUoA== + +esbuild-linux-arm@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.13.15.tgz#8a00e99e6a0c6c9a6b7f334841364d8a2b4aecfe" + integrity sha512-wUHttDi/ol0tD8ZgUMDH8Ef7IbDX+/UsWJOXaAyTdkT7Yy9ZBqPg8bgB/Dn3CZ9SBpNieozrPRHm0BGww7W/jA== + +esbuild-linux-mips64le@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.15.tgz#36b07cc47c3d21e48db3bb1f4d9ef8f46aead4f7" + integrity sha512-KlVjIG828uFPyJkO/8gKwy9RbXhCEUeFsCGOJBepUlpa7G8/SeZgncUEz/tOOUJTcWMTmFMtdd3GElGyAtbSWg== + +esbuild-linux-ppc64le@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.15.tgz#f7e6bba40b9a11eb9dcae5b01550ea04670edad2" + integrity sha512-h6gYF+OsaqEuBjeesTBtUPw0bmiDu7eAeuc2OEH9S6mV9/jPhPdhOWzdeshb0BskRZxPhxPOjqZ+/OqLcxQwEQ== + +esbuild-netbsd-64@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.15.tgz#a2fedc549c2b629d580a732d840712b08d440038" + integrity sha512-3+yE9emwoevLMyvu+iR3rsa+Xwhie7ZEHMGDQ6dkqP/ndFzRHkobHUKTe+NCApSqG5ce2z4rFu+NX/UHnxlh3w== + +esbuild-openbsd-64@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.15.tgz#b22c0e5806d3a1fbf0325872037f885306b05cd7" + integrity sha512-wTfvtwYJYAFL1fSs8yHIdf5GEE4NkbtbXtjLWjM3Cw8mmQKqsg8kTiqJ9NJQe5NX/5Qlo7Xd9r1yKMMkHllp5g== + +esbuild-sunos-64@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.13.15.tgz#d0b6454a88375ee8d3964daeff55c85c91c7cef4" + integrity sha512-lbivT9Bx3t1iWWrSnGyBP9ODriEvWDRiweAs69vI+miJoeKwHWOComSRukttbuzjZ8r1q0mQJ8Z7yUsDJ3hKdw== + +esbuild-windows-32@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.13.15.tgz#c96d0b9bbb52f3303322582ef8e4847c5ad375a7" + integrity sha512-fDMEf2g3SsJ599MBr50cY5ve5lP1wyVwTe6aLJsM01KtxyKkB4UT+fc5MXQFn3RLrAIAZOG+tHC+yXObpSn7Nw== + +esbuild-windows-64@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.13.15.tgz#1f79cb9b1e1bb02fb25cd414cb90d4ea2892c294" + integrity sha512-9aMsPRGDWCd3bGjUIKG/ZOJPKsiztlxl/Q3C1XDswO6eNX/Jtwu4M+jb6YDH9hRSUflQWX0XKAfWzgy5Wk54JQ== + +esbuild-windows-arm64@0.13.15: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.15.tgz#482173070810df22a752c686509c370c3be3b3c3" + integrity sha512-zzvyCVVpbwQQATaf3IG8mu1IwGEiDxKkYUdA4FpoCHi1KtPa13jeScYDjlW0Qh+ebWzpKfR2ZwvqAQkSWNcKjA== + +esbuild@^0.13.12: + version "0.13.15" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.13.15.tgz#db56a88166ee373f87dbb2d8798ff449e0450cdf" + integrity sha512-raCxt02HBKv8RJxE8vkTSCXGIyKHdEdGfUmiYb8wnabnaEmHzyW7DCHb5tEN0xU8ryqg5xw54mcwnYkC4x3AIw== + optionalDependencies: + esbuild-android-arm64 "0.13.15" + esbuild-darwin-64 "0.13.15" + esbuild-darwin-arm64 "0.13.15" + esbuild-freebsd-64 "0.13.15" + esbuild-freebsd-arm64 "0.13.15" + esbuild-linux-32 "0.13.15" + esbuild-linux-64 "0.13.15" + esbuild-linux-arm "0.13.15" + esbuild-linux-arm64 "0.13.15" + esbuild-linux-mips64le "0.13.15" + esbuild-linux-ppc64le "0.13.15" + esbuild-netbsd-64 "0.13.15" + esbuild-openbsd-64 "0.13.15" + esbuild-sunos-64 "0.13.15" + esbuild-windows-32 "0.13.15" + esbuild-windows-64 "0.13.15" + esbuild-windows-arm64 "0.13.15" + +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +is-core-module@^2.8.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" + integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== + dependencies: + has "^1.0.3" + +nanoid@^3.1.30: + version "3.2.0" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c" + integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +postcss@^8.4.5: + version "8.4.5" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.5.tgz#bae665764dfd4c6fcc24dc0fdf7e7aa00cc77f95" + integrity sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg== + dependencies: + nanoid "^3.1.30" + picocolors "^1.0.0" + source-map-js "^1.0.1" + +resolve@^1.20.0: + version "1.21.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.21.0.tgz#b51adc97f3472e6a5cf4444d34bc9d6b9037591f" + integrity sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA== + dependencies: + is-core-module "^2.8.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +rollup@^2.59.0: + version "2.64.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.64.0.tgz#f0f59774e21fbb56de438a37d06a2189632b207a" + integrity sha512-+c+lbw1lexBKSMb1yxGDVfJ+vchJH3qLbmavR+awDinTDA2C5Ug9u7lkOzj62SCu0PKUExsW36tpgW7Fmpn3yQ== + optionalDependencies: + fsevents "~2.3.2" + +source-map-js@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.1.tgz#a1741c131e3c77d048252adfa24e23b908670caf" + integrity sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA== + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +typescript@^4.4.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.4.tgz#a17d3a0263bf5c8723b9c52f43c5084edf13c2e8" + integrity sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg== + +vite@^2.7.2: + version "2.7.12" + resolved "https://registry.yarnpkg.com/vite/-/vite-2.7.12.tgz#7784ab19e7ff98f6a192d2d7d877480a8c2b7e7d" + integrity sha512-KvPYToRQWhRfBeVkyhkZ5hASuHQkqZUUdUcE3xyYtq5oYEPIJ0h9LWiWTO6v990glmSac2cEPeYeXzpX5Z6qKQ== + dependencies: + esbuild "^0.13.12" + postcss "^8.4.5" + resolve "^1.20.0" + rollup "^2.59.0" + optionalDependencies: + fsevents "~2.3.2" From 8168745965e5566cfd1ae31eb7e9cdbf5050744d Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 17 Jan 2022 17:51:18 +0800 Subject: [PATCH 203/422] protobuf --- package.json | 3 + src/message.ts | 4969 +++++++++++++++++++++++++++++++++++++++++++++ src/rendezvous.ts | 2252 ++++++++++++++++++++ ts_proto.py | 20 + yarn.lock | 140 ++ 5 files changed, 7384 insertions(+) create mode 100644 src/message.ts create mode 100644 src/rendezvous.ts create mode 100644 ts_proto.py diff --git a/package.json b/package.json index ec6b7fac4..abe5dd151 100644 --- a/package.json +++ b/package.json @@ -9,5 +9,8 @@ "devDependencies": { "typescript": "^4.4.4", "vite": "^2.7.2" + }, + "dependencies": { + "ts-proto": "^1.101.0" } } diff --git a/src/message.ts b/src/message.ts new file mode 100644 index 000000000..9c0532dec --- /dev/null +++ b/src/message.ts @@ -0,0 +1,4969 @@ +/* eslint-disable */ +import { util, configure, Writer, Reader } from "protobufjs/minimal"; +import * as Long from "long"; + +export const protobufPackage = "hbb"; + +export enum ControlKey { + Unknown = 0, + Alt = 1, + Backspace = 2, + CapsLock = 3, + Control = 4, + Delete = 5, + DownArrow = 6, + End = 7, + Escape = 8, + F1 = 9, + F10 = 10, + F11 = 11, + F12 = 12, + F2 = 13, + F3 = 14, + F4 = 15, + F5 = 16, + F6 = 17, + F7 = 18, + F8 = 19, + F9 = 20, + Home = 21, + LeftArrow = 22, + /** Meta - / meta key (also known as "windows"; "super"; and "command") */ + Meta = 23, + /** Option - / option key on macOS (alt key on Linux and Windows) */ + Option = 24, + PageDown = 25, + PageUp = 26, + Return = 27, + RightArrow = 28, + Shift = 29, + Space = 30, + Tab = 31, + UpArrow = 32, + Numpad0 = 33, + Numpad1 = 34, + Numpad2 = 35, + Numpad3 = 36, + Numpad4 = 37, + Numpad5 = 38, + Numpad6 = 39, + Numpad7 = 40, + Numpad8 = 41, + Numpad9 = 42, + Cancel = 43, + Clear = 44, + /** Menu - deprecated, use Alt instead */ + Menu = 45, + Pause = 46, + Kana = 47, + Hangul = 48, + Junja = 49, + Final = 50, + Hanja = 51, + Kanji = 52, + Convert = 53, + Select = 54, + Print = 55, + Execute = 56, + Snapshot = 57, + Insert = 58, + Help = 59, + Sleep = 60, + Separator = 61, + Scroll = 62, + NumLock = 63, + RWin = 64, + Apps = 65, + Multiply = 66, + Add = 67, + Subtract = 68, + Decimal = 69, + Divide = 70, + Equals = 71, + NumpadEnter = 72, + RShift = 73, + RControl = 74, + RAlt = 75, + CtrlAltDel = 100, + LockScreen = 101, + UNRECOGNIZED = -1, +} + +export function controlKeyFromJSON(object: any): ControlKey { + switch (object) { + case 0: + case "Unknown": + return ControlKey.Unknown; + case 1: + case "Alt": + return ControlKey.Alt; + case 2: + case "Backspace": + return ControlKey.Backspace; + case 3: + case "CapsLock": + return ControlKey.CapsLock; + case 4: + case "Control": + return ControlKey.Control; + case 5: + case "Delete": + return ControlKey.Delete; + case 6: + case "DownArrow": + return ControlKey.DownArrow; + case 7: + case "End": + return ControlKey.End; + case 8: + case "Escape": + return ControlKey.Escape; + case 9: + case "F1": + return ControlKey.F1; + case 10: + case "F10": + return ControlKey.F10; + case 11: + case "F11": + return ControlKey.F11; + case 12: + case "F12": + return ControlKey.F12; + case 13: + case "F2": + return ControlKey.F2; + case 14: + case "F3": + return ControlKey.F3; + case 15: + case "F4": + return ControlKey.F4; + case 16: + case "F5": + return ControlKey.F5; + case 17: + case "F6": + return ControlKey.F6; + case 18: + case "F7": + return ControlKey.F7; + case 19: + case "F8": + return ControlKey.F8; + case 20: + case "F9": + return ControlKey.F9; + case 21: + case "Home": + return ControlKey.Home; + case 22: + case "LeftArrow": + return ControlKey.LeftArrow; + case 23: + case "Meta": + return ControlKey.Meta; + case 24: + case "Option": + return ControlKey.Option; + case 25: + case "PageDown": + return ControlKey.PageDown; + case 26: + case "PageUp": + return ControlKey.PageUp; + case 27: + case "Return": + return ControlKey.Return; + case 28: + case "RightArrow": + return ControlKey.RightArrow; + case 29: + case "Shift": + return ControlKey.Shift; + case 30: + case "Space": + return ControlKey.Space; + case 31: + case "Tab": + return ControlKey.Tab; + case 32: + case "UpArrow": + return ControlKey.UpArrow; + case 33: + case "Numpad0": + return ControlKey.Numpad0; + case 34: + case "Numpad1": + return ControlKey.Numpad1; + case 35: + case "Numpad2": + return ControlKey.Numpad2; + case 36: + case "Numpad3": + return ControlKey.Numpad3; + case 37: + case "Numpad4": + return ControlKey.Numpad4; + case 38: + case "Numpad5": + return ControlKey.Numpad5; + case 39: + case "Numpad6": + return ControlKey.Numpad6; + case 40: + case "Numpad7": + return ControlKey.Numpad7; + case 41: + case "Numpad8": + return ControlKey.Numpad8; + case 42: + case "Numpad9": + return ControlKey.Numpad9; + case 43: + case "Cancel": + return ControlKey.Cancel; + case 44: + case "Clear": + return ControlKey.Clear; + case 45: + case "Menu": + return ControlKey.Menu; + case 46: + case "Pause": + return ControlKey.Pause; + case 47: + case "Kana": + return ControlKey.Kana; + case 48: + case "Hangul": + return ControlKey.Hangul; + case 49: + case "Junja": + return ControlKey.Junja; + case 50: + case "Final": + return ControlKey.Final; + case 51: + case "Hanja": + return ControlKey.Hanja; + case 52: + case "Kanji": + return ControlKey.Kanji; + case 53: + case "Convert": + return ControlKey.Convert; + case 54: + case "Select": + return ControlKey.Select; + case 55: + case "Print": + return ControlKey.Print; + case 56: + case "Execute": + return ControlKey.Execute; + case 57: + case "Snapshot": + return ControlKey.Snapshot; + case 58: + case "Insert": + return ControlKey.Insert; + case 59: + case "Help": + return ControlKey.Help; + case 60: + case "Sleep": + return ControlKey.Sleep; + case 61: + case "Separator": + return ControlKey.Separator; + case 62: + case "Scroll": + return ControlKey.Scroll; + case 63: + case "NumLock": + return ControlKey.NumLock; + case 64: + case "RWin": + return ControlKey.RWin; + case 65: + case "Apps": + return ControlKey.Apps; + case 66: + case "Multiply": + return ControlKey.Multiply; + case 67: + case "Add": + return ControlKey.Add; + case 68: + case "Subtract": + return ControlKey.Subtract; + case 69: + case "Decimal": + return ControlKey.Decimal; + case 70: + case "Divide": + return ControlKey.Divide; + case 71: + case "Equals": + return ControlKey.Equals; + case 72: + case "NumpadEnter": + return ControlKey.NumpadEnter; + case 73: + case "RShift": + return ControlKey.RShift; + case 74: + case "RControl": + return ControlKey.RControl; + case 75: + case "RAlt": + return ControlKey.RAlt; + case 100: + case "CtrlAltDel": + return ControlKey.CtrlAltDel; + case 101: + case "LockScreen": + return ControlKey.LockScreen; + case -1: + case "UNRECOGNIZED": + default: + return ControlKey.UNRECOGNIZED; + } +} + +export function controlKeyToJSON(object: ControlKey): string { + switch (object) { + case ControlKey.Unknown: + return "Unknown"; + case ControlKey.Alt: + return "Alt"; + case ControlKey.Backspace: + return "Backspace"; + case ControlKey.CapsLock: + return "CapsLock"; + case ControlKey.Control: + return "Control"; + case ControlKey.Delete: + return "Delete"; + case ControlKey.DownArrow: + return "DownArrow"; + case ControlKey.End: + return "End"; + case ControlKey.Escape: + return "Escape"; + case ControlKey.F1: + return "F1"; + case ControlKey.F10: + return "F10"; + case ControlKey.F11: + return "F11"; + case ControlKey.F12: + return "F12"; + case ControlKey.F2: + return "F2"; + case ControlKey.F3: + return "F3"; + case ControlKey.F4: + return "F4"; + case ControlKey.F5: + return "F5"; + case ControlKey.F6: + return "F6"; + case ControlKey.F7: + return "F7"; + case ControlKey.F8: + return "F8"; + case ControlKey.F9: + return "F9"; + case ControlKey.Home: + return "Home"; + case ControlKey.LeftArrow: + return "LeftArrow"; + case ControlKey.Meta: + return "Meta"; + case ControlKey.Option: + return "Option"; + case ControlKey.PageDown: + return "PageDown"; + case ControlKey.PageUp: + return "PageUp"; + case ControlKey.Return: + return "Return"; + case ControlKey.RightArrow: + return "RightArrow"; + case ControlKey.Shift: + return "Shift"; + case ControlKey.Space: + return "Space"; + case ControlKey.Tab: + return "Tab"; + case ControlKey.UpArrow: + return "UpArrow"; + case ControlKey.Numpad0: + return "Numpad0"; + case ControlKey.Numpad1: + return "Numpad1"; + case ControlKey.Numpad2: + return "Numpad2"; + case ControlKey.Numpad3: + return "Numpad3"; + case ControlKey.Numpad4: + return "Numpad4"; + case ControlKey.Numpad5: + return "Numpad5"; + case ControlKey.Numpad6: + return "Numpad6"; + case ControlKey.Numpad7: + return "Numpad7"; + case ControlKey.Numpad8: + return "Numpad8"; + case ControlKey.Numpad9: + return "Numpad9"; + case ControlKey.Cancel: + return "Cancel"; + case ControlKey.Clear: + return "Clear"; + case ControlKey.Menu: + return "Menu"; + case ControlKey.Pause: + return "Pause"; + case ControlKey.Kana: + return "Kana"; + case ControlKey.Hangul: + return "Hangul"; + case ControlKey.Junja: + return "Junja"; + case ControlKey.Final: + return "Final"; + case ControlKey.Hanja: + return "Hanja"; + case ControlKey.Kanji: + return "Kanji"; + case ControlKey.Convert: + return "Convert"; + case ControlKey.Select: + return "Select"; + case ControlKey.Print: + return "Print"; + case ControlKey.Execute: + return "Execute"; + case ControlKey.Snapshot: + return "Snapshot"; + case ControlKey.Insert: + return "Insert"; + case ControlKey.Help: + return "Help"; + case ControlKey.Sleep: + return "Sleep"; + case ControlKey.Separator: + return "Separator"; + case ControlKey.Scroll: + return "Scroll"; + case ControlKey.NumLock: + return "NumLock"; + case ControlKey.RWin: + return "RWin"; + case ControlKey.Apps: + return "Apps"; + case ControlKey.Multiply: + return "Multiply"; + case ControlKey.Add: + return "Add"; + case ControlKey.Subtract: + return "Subtract"; + case ControlKey.Decimal: + return "Decimal"; + case ControlKey.Divide: + return "Divide"; + case ControlKey.Equals: + return "Equals"; + case ControlKey.NumpadEnter: + return "NumpadEnter"; + case ControlKey.RShift: + return "RShift"; + case ControlKey.RControl: + return "RControl"; + case ControlKey.RAlt: + return "RAlt"; + case ControlKey.CtrlAltDel: + return "CtrlAltDel"; + case ControlKey.LockScreen: + return "LockScreen"; + default: + return "UNKNOWN"; + } +} + +export enum FileType { + UnknownFileType = 0, + Dir = 1, + DirLink = 2, + DirDrive = 3, + File = 4, + FileLink = 5, + UNRECOGNIZED = -1, +} + +export function fileTypeFromJSON(object: any): FileType { + switch (object) { + case 0: + case "UnknownFileType": + return FileType.UnknownFileType; + case 1: + case "Dir": + return FileType.Dir; + case 2: + case "DirLink": + return FileType.DirLink; + case 3: + case "DirDrive": + return FileType.DirDrive; + case 4: + case "File": + return FileType.File; + case 5: + case "FileLink": + return FileType.FileLink; + case -1: + case "UNRECOGNIZED": + default: + return FileType.UNRECOGNIZED; + } +} + +export function fileTypeToJSON(object: FileType): string { + switch (object) { + case FileType.UnknownFileType: + return "UnknownFileType"; + case FileType.Dir: + return "Dir"; + case FileType.DirLink: + return "DirLink"; + case FileType.DirDrive: + return "DirDrive"; + case FileType.File: + return "File"; + case FileType.FileLink: + return "FileLink"; + default: + return "UNKNOWN"; + } +} + +export enum ImageQuality { + NotSet = 0, + Low = 2, + Balanced = 3, + Best = 4, + UNRECOGNIZED = -1, +} + +export function imageQualityFromJSON(object: any): ImageQuality { + switch (object) { + case 0: + case "NotSet": + return ImageQuality.NotSet; + case 2: + case "Low": + return ImageQuality.Low; + case 3: + case "Balanced": + return ImageQuality.Balanced; + case 4: + case "Best": + return ImageQuality.Best; + case -1: + case "UNRECOGNIZED": + default: + return ImageQuality.UNRECOGNIZED; + } +} + +export function imageQualityToJSON(object: ImageQuality): string { + switch (object) { + case ImageQuality.NotSet: + return "NotSet"; + case ImageQuality.Low: + return "Low"; + case ImageQuality.Balanced: + return "Balanced"; + case ImageQuality.Best: + return "Best"; + default: + return "UNKNOWN"; + } +} + +export interface VP9 { + data: Uint8Array; + key: boolean; + pts: number; +} + +export interface VP9s { + frames: VP9[]; +} + +export interface RGB { + compress: boolean; +} + +/** planes data send directly in binary for better use arraybuffer on web */ +export interface YUV { + compress: boolean; + stride: number; +} + +export interface VideoFrame { + vp9s: VP9s | undefined; + rgb: RGB | undefined; + yuv: YUV | undefined; +} + +export interface DisplayInfo { + x: number; + y: number; + width: number; + height: number; + name: string; + online: boolean; +} + +export interface PortForward { + host: string; + port: number; +} + +export interface FileTransfer { + dir: string; + showHidden: boolean; +} + +export interface LoginRequest { + username: string; + password: Uint8Array; + myId: string; + myName: string; + option: OptionMessage | undefined; + fileTransfer: FileTransfer | undefined; + portForward: PortForward | undefined; +} + +export interface ChatMessage { + text: string; +} + +export interface PeerInfo { + username: string; + hostname: string; + platform: string; + displays: DisplayInfo[]; + currentDisplay: number; + sasEnabled: boolean; + version: string; +} + +export interface LoginResponse { + error: string | undefined; + peerInfo: PeerInfo | undefined; +} + +export interface MouseEvent { + mask: number; + x: number; + y: number; + modifiers: ControlKey[]; +} + +export interface KeyEvent { + down: boolean; + press: boolean; + controlKey: ControlKey | undefined; + chr: number | undefined; + unicode: number | undefined; + seq: string | undefined; + modifiers: ControlKey[]; +} + +export interface CursorData { + id: number; + hotx: number; + hoty: number; + width: number; + height: number; + colors: Uint8Array; +} + +export interface CursorPosition { + x: number; + y: number; +} + +export interface Hash { + salt: string; + challenge: string; +} + +export interface Clipboard { + compress: boolean; + content: Uint8Array; +} + +export interface FileEntry { + entryType: FileType; + name: string; + isHidden: boolean; + size: number; + modifiedTime: number; +} + +export interface FileDirectory { + id: number; + path: string; + entries: FileEntry[]; +} + +export interface ReadDir { + path: string; + includeHidden: boolean; +} + +export interface ReadAllFiles { + id: number; + path: string; + includeHidden: boolean; +} + +export interface FileAction { + readDir: ReadDir | undefined; + send: FileTransferSendRequest | undefined; + receive: FileTransferReceiveRequest | undefined; + create: FileDirCreate | undefined; + removeDir: FileRemoveDir | undefined; + removeFile: FileRemoveFile | undefined; + allFiles: ReadAllFiles | undefined; + cancel: FileTransferCancel | undefined; +} + +export interface FileTransferCancel { + id: number; +} + +export interface FileResponse { + dir: FileDirectory | undefined; + block: FileTransferBlock | undefined; + error: FileTransferError | undefined; + done: FileTransferDone | undefined; +} + +export interface FileTransferBlock { + id: number; + fileNum: number; + data: Uint8Array; + compressed: boolean; +} + +export interface FileTransferError { + id: number; + error: string; + fileNum: number; +} + +export interface FileTransferSendRequest { + id: number; + path: string; + includeHidden: boolean; +} + +export interface FileTransferDone { + id: number; + fileNum: number; +} + +export interface FileTransferReceiveRequest { + id: number; + /** path written to */ + path: string; + files: FileEntry[]; +} + +export interface FileRemoveDir { + id: number; + path: string; + recursive: boolean; +} + +export interface FileRemoveFile { + id: number; + path: string; + fileNum: number; +} + +export interface FileDirCreate { + id: number; + path: string; +} + +export interface SwitchDisplay { + display: number; + x: number; + y: number; + width: number; + height: number; +} + +export interface PermissionInfo { + permission: PermissionInfo_Permission; + enabled: boolean; +} + +export enum PermissionInfo_Permission { + Unknown = 0, + Keyboard = 1, + Clipboard = 2, + Audio = 3, + UNRECOGNIZED = -1, +} + +export function permissionInfo_PermissionFromJSON( + object: any +): PermissionInfo_Permission { + switch (object) { + case 0: + case "Unknown": + return PermissionInfo_Permission.Unknown; + case 1: + case "Keyboard": + return PermissionInfo_Permission.Keyboard; + case 2: + case "Clipboard": + return PermissionInfo_Permission.Clipboard; + case 3: + case "Audio": + return PermissionInfo_Permission.Audio; + case -1: + case "UNRECOGNIZED": + default: + return PermissionInfo_Permission.UNRECOGNIZED; + } +} + +export function permissionInfo_PermissionToJSON( + object: PermissionInfo_Permission +): string { + switch (object) { + case PermissionInfo_Permission.Unknown: + return "Unknown"; + case PermissionInfo_Permission.Keyboard: + return "Keyboard"; + case PermissionInfo_Permission.Clipboard: + return "Clipboard"; + case PermissionInfo_Permission.Audio: + return "Audio"; + default: + return "UNKNOWN"; + } +} + +export interface OptionMessage { + imageQuality: ImageQuality; + lockAfterSessionEnd: OptionMessage_BoolOption; + showRemoteCursor: OptionMessage_BoolOption; + privacyMode: OptionMessage_BoolOption; + blockInput: OptionMessage_BoolOption; + customImageQuality: number; + disableAudio: OptionMessage_BoolOption; + disableClipboard: OptionMessage_BoolOption; +} + +export enum OptionMessage_BoolOption { + NotSet = 0, + No = 1, + Yes = 2, + UNRECOGNIZED = -1, +} + +export function optionMessage_BoolOptionFromJSON( + object: any +): OptionMessage_BoolOption { + switch (object) { + case 0: + case "NotSet": + return OptionMessage_BoolOption.NotSet; + case 1: + case "No": + return OptionMessage_BoolOption.No; + case 2: + case "Yes": + return OptionMessage_BoolOption.Yes; + case -1: + case "UNRECOGNIZED": + default: + return OptionMessage_BoolOption.UNRECOGNIZED; + } +} + +export function optionMessage_BoolOptionToJSON( + object: OptionMessage_BoolOption +): string { + switch (object) { + case OptionMessage_BoolOption.NotSet: + return "NotSet"; + case OptionMessage_BoolOption.No: + return "No"; + case OptionMessage_BoolOption.Yes: + return "Yes"; + default: + return "UNKNOWN"; + } +} + +export interface OptionResponse { + opt: OptionMessage | undefined; + error: string; +} + +export interface TestDelay { + time: number; + fromClient: boolean; +} + +export interface PublicKey { + asymmetricValue: Uint8Array; + symmetricValue: Uint8Array; +} + +export interface SignedId { + id: Uint8Array; +} + +export interface AudioFormat { + sampleRate: number; + channels: number; +} + +export interface AudioFrame { + data: Uint8Array; +} + +export interface Misc { + chatMessage: ChatMessage | undefined; + switchDisplay: SwitchDisplay | undefined; + permissionInfo: PermissionInfo | undefined; + option: OptionMessage | undefined; + audioFormat: AudioFormat | undefined; + closeReason: string | undefined; + refreshVideo: boolean | undefined; + optionResponse: OptionResponse | undefined; +} + +export interface Message { + signedId: SignedId | undefined; + publicKey: PublicKey | undefined; + testDelay: TestDelay | undefined; + videoFrame: VideoFrame | undefined; + loginRequest: LoginRequest | undefined; + loginResponse: LoginResponse | undefined; + hash: Hash | undefined; + mouseEvent: MouseEvent | undefined; + audioFrame: AudioFrame | undefined; + cursorData: CursorData | undefined; + cursorPosition: CursorPosition | undefined; + cursorId: number | undefined; + keyEvent: KeyEvent | undefined; + clipboard: Clipboard | undefined; + fileAction: FileAction | undefined; + fileResponse: FileResponse | undefined; + misc: Misc | undefined; +} + +function createBaseVP9(): VP9 { + return { data: new Uint8Array(), key: false, pts: 0 }; +} + +export const VP9 = { + encode(message: VP9, writer: Writer = Writer.create()): Writer { + if (message.data.length !== 0) { + writer.uint32(10).bytes(message.data); + } + if (message.key === true) { + writer.uint32(16).bool(message.key); + } + if (message.pts !== 0) { + writer.uint32(24).int64(message.pts); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): VP9 { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseVP9(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.data = reader.bytes(); + break; + case 2: + message.key = reader.bool(); + break; + case 3: + message.pts = longToNumber(reader.int64() as Long); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): VP9 { + return { + data: isSet(object.data) + ? bytesFromBase64(object.data) + : new Uint8Array(), + key: isSet(object.key) ? Boolean(object.key) : false, + pts: isSet(object.pts) ? Number(object.pts) : 0, + }; + }, + + toJSON(message: VP9): unknown { + const obj: any = {}; + message.data !== undefined && + (obj.data = base64FromBytes( + message.data !== undefined ? message.data : new Uint8Array() + )); + message.key !== undefined && (obj.key = message.key); + message.pts !== undefined && (obj.pts = Math.round(message.pts)); + return obj; + }, + + fromPartial, I>>(object: I): VP9 { + const message = createBaseVP9(); + message.data = object.data ?? new Uint8Array(); + message.key = object.key ?? false; + message.pts = object.pts ?? 0; + return message; + }, +}; + +function createBaseVP9s(): VP9s { + return { frames: [] }; +} + +export const VP9s = { + encode(message: VP9s, writer: Writer = Writer.create()): Writer { + for (const v of message.frames) { + VP9.encode(v!, writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): VP9s { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseVP9s(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.frames.push(VP9.decode(reader, reader.uint32())); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): VP9s { + return { + frames: Array.isArray(object?.frames) + ? object.frames.map((e: any) => VP9.fromJSON(e)) + : [], + }; + }, + + toJSON(message: VP9s): unknown { + const obj: any = {}; + if (message.frames) { + obj.frames = message.frames.map((e) => (e ? VP9.toJSON(e) : undefined)); + } else { + obj.frames = []; + } + return obj; + }, + + fromPartial, I>>(object: I): VP9s { + const message = createBaseVP9s(); + message.frames = object.frames?.map((e) => VP9.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseRGB(): RGB { + return { compress: false }; +} + +export const RGB = { + encode(message: RGB, writer: Writer = Writer.create()): Writer { + if (message.compress === true) { + writer.uint32(8).bool(message.compress); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): RGB { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseRGB(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.compress = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): RGB { + return { + compress: isSet(object.compress) ? Boolean(object.compress) : false, + }; + }, + + toJSON(message: RGB): unknown { + const obj: any = {}; + message.compress !== undefined && (obj.compress = message.compress); + return obj; + }, + + fromPartial, I>>(object: I): RGB { + const message = createBaseRGB(); + message.compress = object.compress ?? false; + return message; + }, +}; + +function createBaseYUV(): YUV { + return { compress: false, stride: 0 }; +} + +export const YUV = { + encode(message: YUV, writer: Writer = Writer.create()): Writer { + if (message.compress === true) { + writer.uint32(8).bool(message.compress); + } + if (message.stride !== 0) { + writer.uint32(16).int32(message.stride); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): YUV { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseYUV(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.compress = reader.bool(); + break; + case 2: + message.stride = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): YUV { + return { + compress: isSet(object.compress) ? Boolean(object.compress) : false, + stride: isSet(object.stride) ? Number(object.stride) : 0, + }; + }, + + toJSON(message: YUV): unknown { + const obj: any = {}; + message.compress !== undefined && (obj.compress = message.compress); + message.stride !== undefined && (obj.stride = Math.round(message.stride)); + return obj; + }, + + fromPartial, I>>(object: I): YUV { + const message = createBaseYUV(); + message.compress = object.compress ?? false; + message.stride = object.stride ?? 0; + return message; + }, +}; + +function createBaseVideoFrame(): VideoFrame { + return { vp9s: undefined, rgb: undefined, yuv: undefined }; +} + +export const VideoFrame = { + encode(message: VideoFrame, writer: Writer = Writer.create()): Writer { + if (message.vp9s !== undefined) { + VP9s.encode(message.vp9s, writer.uint32(50).fork()).ldelim(); + } + if (message.rgb !== undefined) { + RGB.encode(message.rgb, writer.uint32(58).fork()).ldelim(); + } + if (message.yuv !== undefined) { + YUV.encode(message.yuv, writer.uint32(66).fork()).ldelim(); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): VideoFrame { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseVideoFrame(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 6: + message.vp9s = VP9s.decode(reader, reader.uint32()); + break; + case 7: + message.rgb = RGB.decode(reader, reader.uint32()); + break; + case 8: + message.yuv = YUV.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): VideoFrame { + return { + vp9s: isSet(object.vp9s) ? VP9s.fromJSON(object.vp9s) : undefined, + rgb: isSet(object.rgb) ? RGB.fromJSON(object.rgb) : undefined, + yuv: isSet(object.yuv) ? YUV.fromJSON(object.yuv) : undefined, + }; + }, + + toJSON(message: VideoFrame): unknown { + const obj: any = {}; + message.vp9s !== undefined && + (obj.vp9s = message.vp9s ? VP9s.toJSON(message.vp9s) : undefined); + message.rgb !== undefined && + (obj.rgb = message.rgb ? RGB.toJSON(message.rgb) : undefined); + message.yuv !== undefined && + (obj.yuv = message.yuv ? YUV.toJSON(message.yuv) : undefined); + return obj; + }, + + fromPartial, I>>( + object: I + ): VideoFrame { + const message = createBaseVideoFrame(); + message.vp9s = + object.vp9s !== undefined && object.vp9s !== null + ? VP9s.fromPartial(object.vp9s) + : undefined; + message.rgb = + object.rgb !== undefined && object.rgb !== null + ? RGB.fromPartial(object.rgb) + : undefined; + message.yuv = + object.yuv !== undefined && object.yuv !== null + ? YUV.fromPartial(object.yuv) + : undefined; + return message; + }, +}; + +function createBaseDisplayInfo(): DisplayInfo { + return { x: 0, y: 0, width: 0, height: 0, name: "", online: false }; +} + +export const DisplayInfo = { + encode(message: DisplayInfo, writer: Writer = Writer.create()): Writer { + if (message.x !== 0) { + writer.uint32(8).sint32(message.x); + } + if (message.y !== 0) { + writer.uint32(16).sint32(message.y); + } + if (message.width !== 0) { + writer.uint32(24).int32(message.width); + } + if (message.height !== 0) { + writer.uint32(32).int32(message.height); + } + if (message.name !== "") { + writer.uint32(42).string(message.name); + } + if (message.online === true) { + writer.uint32(48).bool(message.online); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): DisplayInfo { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseDisplayInfo(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.x = reader.sint32(); + break; + case 2: + message.y = reader.sint32(); + break; + case 3: + message.width = reader.int32(); + break; + case 4: + message.height = reader.int32(); + break; + case 5: + message.name = reader.string(); + break; + case 6: + message.online = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): DisplayInfo { + return { + x: isSet(object.x) ? Number(object.x) : 0, + y: isSet(object.y) ? Number(object.y) : 0, + width: isSet(object.width) ? Number(object.width) : 0, + height: isSet(object.height) ? Number(object.height) : 0, + name: isSet(object.name) ? String(object.name) : "", + online: isSet(object.online) ? Boolean(object.online) : false, + }; + }, + + toJSON(message: DisplayInfo): unknown { + const obj: any = {}; + message.x !== undefined && (obj.x = Math.round(message.x)); + message.y !== undefined && (obj.y = Math.round(message.y)); + message.width !== undefined && (obj.width = Math.round(message.width)); + message.height !== undefined && (obj.height = Math.round(message.height)); + message.name !== undefined && (obj.name = message.name); + message.online !== undefined && (obj.online = message.online); + return obj; + }, + + fromPartial, I>>( + object: I + ): DisplayInfo { + const message = createBaseDisplayInfo(); + message.x = object.x ?? 0; + message.y = object.y ?? 0; + message.width = object.width ?? 0; + message.height = object.height ?? 0; + message.name = object.name ?? ""; + message.online = object.online ?? false; + return message; + }, +}; + +function createBasePortForward(): PortForward { + return { host: "", port: 0 }; +} + +export const PortForward = { + encode(message: PortForward, writer: Writer = Writer.create()): Writer { + if (message.host !== "") { + writer.uint32(10).string(message.host); + } + if (message.port !== 0) { + writer.uint32(16).int32(message.port); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): PortForward { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBasePortForward(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.host = reader.string(); + break; + case 2: + message.port = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): PortForward { + return { + host: isSet(object.host) ? String(object.host) : "", + port: isSet(object.port) ? Number(object.port) : 0, + }; + }, + + toJSON(message: PortForward): unknown { + const obj: any = {}; + message.host !== undefined && (obj.host = message.host); + message.port !== undefined && (obj.port = Math.round(message.port)); + return obj; + }, + + fromPartial, I>>( + object: I + ): PortForward { + const message = createBasePortForward(); + message.host = object.host ?? ""; + message.port = object.port ?? 0; + return message; + }, +}; + +function createBaseFileTransfer(): FileTransfer { + return { dir: "", showHidden: false }; +} + +export const FileTransfer = { + encode(message: FileTransfer, writer: Writer = Writer.create()): Writer { + if (message.dir !== "") { + writer.uint32(10).string(message.dir); + } + if (message.showHidden === true) { + writer.uint32(16).bool(message.showHidden); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): FileTransfer { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFileTransfer(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.dir = reader.string(); + break; + case 2: + message.showHidden = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): FileTransfer { + return { + dir: isSet(object.dir) ? String(object.dir) : "", + showHidden: isSet(object.showHidden) ? Boolean(object.showHidden) : false, + }; + }, + + toJSON(message: FileTransfer): unknown { + const obj: any = {}; + message.dir !== undefined && (obj.dir = message.dir); + message.showHidden !== undefined && (obj.showHidden = message.showHidden); + return obj; + }, + + fromPartial, I>>( + object: I + ): FileTransfer { + const message = createBaseFileTransfer(); + message.dir = object.dir ?? ""; + message.showHidden = object.showHidden ?? false; + return message; + }, +}; + +function createBaseLoginRequest(): LoginRequest { + return { + username: "", + password: new Uint8Array(), + myId: "", + myName: "", + option: undefined, + fileTransfer: undefined, + portForward: undefined, + }; +} + +export const LoginRequest = { + encode(message: LoginRequest, writer: Writer = Writer.create()): Writer { + if (message.username !== "") { + writer.uint32(10).string(message.username); + } + if (message.password.length !== 0) { + writer.uint32(18).bytes(message.password); + } + if (message.myId !== "") { + writer.uint32(34).string(message.myId); + } + if (message.myName !== "") { + writer.uint32(42).string(message.myName); + } + if (message.option !== undefined) { + OptionMessage.encode(message.option, writer.uint32(50).fork()).ldelim(); + } + if (message.fileTransfer !== undefined) { + FileTransfer.encode( + message.fileTransfer, + writer.uint32(58).fork() + ).ldelim(); + } + if (message.portForward !== undefined) { + PortForward.encode( + message.portForward, + writer.uint32(66).fork() + ).ldelim(); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): LoginRequest { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseLoginRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.username = reader.string(); + break; + case 2: + message.password = reader.bytes(); + break; + case 4: + message.myId = reader.string(); + break; + case 5: + message.myName = reader.string(); + break; + case 6: + message.option = OptionMessage.decode(reader, reader.uint32()); + break; + case 7: + message.fileTransfer = FileTransfer.decode(reader, reader.uint32()); + break; + case 8: + message.portForward = PortForward.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): LoginRequest { + return { + username: isSet(object.username) ? String(object.username) : "", + password: isSet(object.password) + ? bytesFromBase64(object.password) + : new Uint8Array(), + myId: isSet(object.myId) ? String(object.myId) : "", + myName: isSet(object.myName) ? String(object.myName) : "", + option: isSet(object.option) + ? OptionMessage.fromJSON(object.option) + : undefined, + fileTransfer: isSet(object.fileTransfer) + ? FileTransfer.fromJSON(object.fileTransfer) + : undefined, + portForward: isSet(object.portForward) + ? PortForward.fromJSON(object.portForward) + : undefined, + }; + }, + + toJSON(message: LoginRequest): unknown { + const obj: any = {}; + message.username !== undefined && (obj.username = message.username); + message.password !== undefined && + (obj.password = base64FromBytes( + message.password !== undefined ? message.password : new Uint8Array() + )); + message.myId !== undefined && (obj.myId = message.myId); + message.myName !== undefined && (obj.myName = message.myName); + message.option !== undefined && + (obj.option = message.option + ? OptionMessage.toJSON(message.option) + : undefined); + message.fileTransfer !== undefined && + (obj.fileTransfer = message.fileTransfer + ? FileTransfer.toJSON(message.fileTransfer) + : undefined); + message.portForward !== undefined && + (obj.portForward = message.portForward + ? PortForward.toJSON(message.portForward) + : undefined); + return obj; + }, + + fromPartial, I>>( + object: I + ): LoginRequest { + const message = createBaseLoginRequest(); + message.username = object.username ?? ""; + message.password = object.password ?? new Uint8Array(); + message.myId = object.myId ?? ""; + message.myName = object.myName ?? ""; + message.option = + object.option !== undefined && object.option !== null + ? OptionMessage.fromPartial(object.option) + : undefined; + message.fileTransfer = + object.fileTransfer !== undefined && object.fileTransfer !== null + ? FileTransfer.fromPartial(object.fileTransfer) + : undefined; + message.portForward = + object.portForward !== undefined && object.portForward !== null + ? PortForward.fromPartial(object.portForward) + : undefined; + return message; + }, +}; + +function createBaseChatMessage(): ChatMessage { + return { text: "" }; +} + +export const ChatMessage = { + encode(message: ChatMessage, writer: Writer = Writer.create()): Writer { + if (message.text !== "") { + writer.uint32(10).string(message.text); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): ChatMessage { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseChatMessage(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.text = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): ChatMessage { + return { + text: isSet(object.text) ? String(object.text) : "", + }; + }, + + toJSON(message: ChatMessage): unknown { + const obj: any = {}; + message.text !== undefined && (obj.text = message.text); + return obj; + }, + + fromPartial, I>>( + object: I + ): ChatMessage { + const message = createBaseChatMessage(); + message.text = object.text ?? ""; + return message; + }, +}; + +function createBasePeerInfo(): PeerInfo { + return { + username: "", + hostname: "", + platform: "", + displays: [], + currentDisplay: 0, + sasEnabled: false, + version: "", + }; +} + +export const PeerInfo = { + encode(message: PeerInfo, writer: Writer = Writer.create()): Writer { + if (message.username !== "") { + writer.uint32(10).string(message.username); + } + if (message.hostname !== "") { + writer.uint32(18).string(message.hostname); + } + if (message.platform !== "") { + writer.uint32(26).string(message.platform); + } + for (const v of message.displays) { + DisplayInfo.encode(v!, writer.uint32(34).fork()).ldelim(); + } + if (message.currentDisplay !== 0) { + writer.uint32(40).int32(message.currentDisplay); + } + if (message.sasEnabled === true) { + writer.uint32(48).bool(message.sasEnabled); + } + if (message.version !== "") { + writer.uint32(58).string(message.version); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): PeerInfo { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBasePeerInfo(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.username = reader.string(); + break; + case 2: + message.hostname = reader.string(); + break; + case 3: + message.platform = reader.string(); + break; + case 4: + message.displays.push(DisplayInfo.decode(reader, reader.uint32())); + break; + case 5: + message.currentDisplay = reader.int32(); + break; + case 6: + message.sasEnabled = reader.bool(); + break; + case 7: + message.version = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): PeerInfo { + return { + username: isSet(object.username) ? String(object.username) : "", + hostname: isSet(object.hostname) ? String(object.hostname) : "", + platform: isSet(object.platform) ? String(object.platform) : "", + displays: Array.isArray(object?.displays) + ? object.displays.map((e: any) => DisplayInfo.fromJSON(e)) + : [], + currentDisplay: isSet(object.currentDisplay) + ? Number(object.currentDisplay) + : 0, + sasEnabled: isSet(object.sasEnabled) ? Boolean(object.sasEnabled) : false, + version: isSet(object.version) ? String(object.version) : "", + }; + }, + + toJSON(message: PeerInfo): unknown { + const obj: any = {}; + message.username !== undefined && (obj.username = message.username); + message.hostname !== undefined && (obj.hostname = message.hostname); + message.platform !== undefined && (obj.platform = message.platform); + if (message.displays) { + obj.displays = message.displays.map((e) => + e ? DisplayInfo.toJSON(e) : undefined + ); + } else { + obj.displays = []; + } + message.currentDisplay !== undefined && + (obj.currentDisplay = Math.round(message.currentDisplay)); + message.sasEnabled !== undefined && (obj.sasEnabled = message.sasEnabled); + message.version !== undefined && (obj.version = message.version); + return obj; + }, + + fromPartial, I>>(object: I): PeerInfo { + const message = createBasePeerInfo(); + message.username = object.username ?? ""; + message.hostname = object.hostname ?? ""; + message.platform = object.platform ?? ""; + message.displays = + object.displays?.map((e) => DisplayInfo.fromPartial(e)) || []; + message.currentDisplay = object.currentDisplay ?? 0; + message.sasEnabled = object.sasEnabled ?? false; + message.version = object.version ?? ""; + return message; + }, +}; + +function createBaseLoginResponse(): LoginResponse { + return { error: undefined, peerInfo: undefined }; +} + +export const LoginResponse = { + encode(message: LoginResponse, writer: Writer = Writer.create()): Writer { + if (message.error !== undefined) { + writer.uint32(10).string(message.error); + } + if (message.peerInfo !== undefined) { + PeerInfo.encode(message.peerInfo, writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): LoginResponse { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseLoginResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.error = reader.string(); + break; + case 2: + message.peerInfo = PeerInfo.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): LoginResponse { + return { + error: isSet(object.error) ? String(object.error) : undefined, + peerInfo: isSet(object.peerInfo) + ? PeerInfo.fromJSON(object.peerInfo) + : undefined, + }; + }, + + toJSON(message: LoginResponse): unknown { + const obj: any = {}; + message.error !== undefined && (obj.error = message.error); + message.peerInfo !== undefined && + (obj.peerInfo = message.peerInfo + ? PeerInfo.toJSON(message.peerInfo) + : undefined); + return obj; + }, + + fromPartial, I>>( + object: I + ): LoginResponse { + const message = createBaseLoginResponse(); + message.error = object.error ?? undefined; + message.peerInfo = + object.peerInfo !== undefined && object.peerInfo !== null + ? PeerInfo.fromPartial(object.peerInfo) + : undefined; + return message; + }, +}; + +function createBaseMouseEvent(): MouseEvent { + return { mask: 0, x: 0, y: 0, modifiers: [] }; +} + +export const MouseEvent = { + encode(message: MouseEvent, writer: Writer = Writer.create()): Writer { + if (message.mask !== 0) { + writer.uint32(8).int32(message.mask); + } + if (message.x !== 0) { + writer.uint32(16).sint32(message.x); + } + if (message.y !== 0) { + writer.uint32(24).sint32(message.y); + } + writer.uint32(34).fork(); + for (const v of message.modifiers) { + writer.int32(v); + } + writer.ldelim(); + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): MouseEvent { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMouseEvent(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.mask = reader.int32(); + break; + case 2: + message.x = reader.sint32(); + break; + case 3: + message.y = reader.sint32(); + break; + case 4: + if ((tag & 7) === 2) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.modifiers.push(reader.int32() as any); + } + } else { + message.modifiers.push(reader.int32() as any); + } + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): MouseEvent { + return { + mask: isSet(object.mask) ? Number(object.mask) : 0, + x: isSet(object.x) ? Number(object.x) : 0, + y: isSet(object.y) ? Number(object.y) : 0, + modifiers: Array.isArray(object?.modifiers) + ? object.modifiers.map((e: any) => controlKeyFromJSON(e)) + : [], + }; + }, + + toJSON(message: MouseEvent): unknown { + const obj: any = {}; + message.mask !== undefined && (obj.mask = Math.round(message.mask)); + message.x !== undefined && (obj.x = Math.round(message.x)); + message.y !== undefined && (obj.y = Math.round(message.y)); + if (message.modifiers) { + obj.modifiers = message.modifiers.map((e) => controlKeyToJSON(e)); + } else { + obj.modifiers = []; + } + return obj; + }, + + fromPartial, I>>( + object: I + ): MouseEvent { + const message = createBaseMouseEvent(); + message.mask = object.mask ?? 0; + message.x = object.x ?? 0; + message.y = object.y ?? 0; + message.modifiers = object.modifiers?.map((e) => e) || []; + return message; + }, +}; + +function createBaseKeyEvent(): KeyEvent { + return { + down: false, + press: false, + controlKey: undefined, + chr: undefined, + unicode: undefined, + seq: undefined, + modifiers: [], + }; +} + +export const KeyEvent = { + encode(message: KeyEvent, writer: Writer = Writer.create()): Writer { + if (message.down === true) { + writer.uint32(8).bool(message.down); + } + if (message.press === true) { + writer.uint32(16).bool(message.press); + } + if (message.controlKey !== undefined) { + writer.uint32(24).int32(message.controlKey); + } + if (message.chr !== undefined) { + writer.uint32(32).uint32(message.chr); + } + if (message.unicode !== undefined) { + writer.uint32(40).uint32(message.unicode); + } + if (message.seq !== undefined) { + writer.uint32(50).string(message.seq); + } + writer.uint32(66).fork(); + for (const v of message.modifiers) { + writer.int32(v); + } + writer.ldelim(); + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): KeyEvent { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseKeyEvent(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.down = reader.bool(); + break; + case 2: + message.press = reader.bool(); + break; + case 3: + message.controlKey = reader.int32() as any; + break; + case 4: + message.chr = reader.uint32(); + break; + case 5: + message.unicode = reader.uint32(); + break; + case 6: + message.seq = reader.string(); + break; + case 8: + if ((tag & 7) === 2) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.modifiers.push(reader.int32() as any); + } + } else { + message.modifiers.push(reader.int32() as any); + } + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): KeyEvent { + return { + down: isSet(object.down) ? Boolean(object.down) : false, + press: isSet(object.press) ? Boolean(object.press) : false, + controlKey: isSet(object.controlKey) + ? controlKeyFromJSON(object.controlKey) + : undefined, + chr: isSet(object.chr) ? Number(object.chr) : undefined, + unicode: isSet(object.unicode) ? Number(object.unicode) : undefined, + seq: isSet(object.seq) ? String(object.seq) : undefined, + modifiers: Array.isArray(object?.modifiers) + ? object.modifiers.map((e: any) => controlKeyFromJSON(e)) + : [], + }; + }, + + toJSON(message: KeyEvent): unknown { + const obj: any = {}; + message.down !== undefined && (obj.down = message.down); + message.press !== undefined && (obj.press = message.press); + message.controlKey !== undefined && + (obj.controlKey = + message.controlKey !== undefined + ? controlKeyToJSON(message.controlKey) + : undefined); + message.chr !== undefined && (obj.chr = Math.round(message.chr)); + message.unicode !== undefined && + (obj.unicode = Math.round(message.unicode)); + message.seq !== undefined && (obj.seq = message.seq); + if (message.modifiers) { + obj.modifiers = message.modifiers.map((e) => controlKeyToJSON(e)); + } else { + obj.modifiers = []; + } + return obj; + }, + + fromPartial, I>>(object: I): KeyEvent { + const message = createBaseKeyEvent(); + message.down = object.down ?? false; + message.press = object.press ?? false; + message.controlKey = object.controlKey ?? undefined; + message.chr = object.chr ?? undefined; + message.unicode = object.unicode ?? undefined; + message.seq = object.seq ?? undefined; + message.modifiers = object.modifiers?.map((e) => e) || []; + return message; + }, +}; + +function createBaseCursorData(): CursorData { + return { + id: 0, + hotx: 0, + hoty: 0, + width: 0, + height: 0, + colors: new Uint8Array(), + }; +} + +export const CursorData = { + encode(message: CursorData, writer: Writer = Writer.create()): Writer { + if (message.id !== 0) { + writer.uint32(8).uint64(message.id); + } + if (message.hotx !== 0) { + writer.uint32(16).sint32(message.hotx); + } + if (message.hoty !== 0) { + writer.uint32(24).sint32(message.hoty); + } + if (message.width !== 0) { + writer.uint32(32).int32(message.width); + } + if (message.height !== 0) { + writer.uint32(40).int32(message.height); + } + if (message.colors.length !== 0) { + writer.uint32(50).bytes(message.colors); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): CursorData { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseCursorData(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = longToNumber(reader.uint64() as Long); + break; + case 2: + message.hotx = reader.sint32(); + break; + case 3: + message.hoty = reader.sint32(); + break; + case 4: + message.width = reader.int32(); + break; + case 5: + message.height = reader.int32(); + break; + case 6: + message.colors = reader.bytes(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): CursorData { + return { + id: isSet(object.id) ? Number(object.id) : 0, + hotx: isSet(object.hotx) ? Number(object.hotx) : 0, + hoty: isSet(object.hoty) ? Number(object.hoty) : 0, + width: isSet(object.width) ? Number(object.width) : 0, + height: isSet(object.height) ? Number(object.height) : 0, + colors: isSet(object.colors) + ? bytesFromBase64(object.colors) + : new Uint8Array(), + }; + }, + + toJSON(message: CursorData): unknown { + const obj: any = {}; + message.id !== undefined && (obj.id = Math.round(message.id)); + message.hotx !== undefined && (obj.hotx = Math.round(message.hotx)); + message.hoty !== undefined && (obj.hoty = Math.round(message.hoty)); + message.width !== undefined && (obj.width = Math.round(message.width)); + message.height !== undefined && (obj.height = Math.round(message.height)); + message.colors !== undefined && + (obj.colors = base64FromBytes( + message.colors !== undefined ? message.colors : new Uint8Array() + )); + return obj; + }, + + fromPartial, I>>( + object: I + ): CursorData { + const message = createBaseCursorData(); + message.id = object.id ?? 0; + message.hotx = object.hotx ?? 0; + message.hoty = object.hoty ?? 0; + message.width = object.width ?? 0; + message.height = object.height ?? 0; + message.colors = object.colors ?? new Uint8Array(); + return message; + }, +}; + +function createBaseCursorPosition(): CursorPosition { + return { x: 0, y: 0 }; +} + +export const CursorPosition = { + encode(message: CursorPosition, writer: Writer = Writer.create()): Writer { + if (message.x !== 0) { + writer.uint32(8).sint32(message.x); + } + if (message.y !== 0) { + writer.uint32(16).sint32(message.y); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): CursorPosition { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseCursorPosition(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.x = reader.sint32(); + break; + case 2: + message.y = reader.sint32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): CursorPosition { + return { + x: isSet(object.x) ? Number(object.x) : 0, + y: isSet(object.y) ? Number(object.y) : 0, + }; + }, + + toJSON(message: CursorPosition): unknown { + const obj: any = {}; + message.x !== undefined && (obj.x = Math.round(message.x)); + message.y !== undefined && (obj.y = Math.round(message.y)); + return obj; + }, + + fromPartial, I>>( + object: I + ): CursorPosition { + const message = createBaseCursorPosition(); + message.x = object.x ?? 0; + message.y = object.y ?? 0; + return message; + }, +}; + +function createBaseHash(): Hash { + return { salt: "", challenge: "" }; +} + +export const Hash = { + encode(message: Hash, writer: Writer = Writer.create()): Writer { + if (message.salt !== "") { + writer.uint32(10).string(message.salt); + } + if (message.challenge !== "") { + writer.uint32(18).string(message.challenge); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): Hash { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseHash(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.salt = reader.string(); + break; + case 2: + message.challenge = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): Hash { + return { + salt: isSet(object.salt) ? String(object.salt) : "", + challenge: isSet(object.challenge) ? String(object.challenge) : "", + }; + }, + + toJSON(message: Hash): unknown { + const obj: any = {}; + message.salt !== undefined && (obj.salt = message.salt); + message.challenge !== undefined && (obj.challenge = message.challenge); + return obj; + }, + + fromPartial, I>>(object: I): Hash { + const message = createBaseHash(); + message.salt = object.salt ?? ""; + message.challenge = object.challenge ?? ""; + return message; + }, +}; + +function createBaseClipboard(): Clipboard { + return { compress: false, content: new Uint8Array() }; +} + +export const Clipboard = { + encode(message: Clipboard, writer: Writer = Writer.create()): Writer { + if (message.compress === true) { + writer.uint32(8).bool(message.compress); + } + if (message.content.length !== 0) { + writer.uint32(18).bytes(message.content); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): Clipboard { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseClipboard(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.compress = reader.bool(); + break; + case 2: + message.content = reader.bytes(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): Clipboard { + return { + compress: isSet(object.compress) ? Boolean(object.compress) : false, + content: isSet(object.content) + ? bytesFromBase64(object.content) + : new Uint8Array(), + }; + }, + + toJSON(message: Clipboard): unknown { + const obj: any = {}; + message.compress !== undefined && (obj.compress = message.compress); + message.content !== undefined && + (obj.content = base64FromBytes( + message.content !== undefined ? message.content : new Uint8Array() + )); + return obj; + }, + + fromPartial, I>>( + object: I + ): Clipboard { + const message = createBaseClipboard(); + message.compress = object.compress ?? false; + message.content = object.content ?? new Uint8Array(); + return message; + }, +}; + +function createBaseFileEntry(): FileEntry { + return { entryType: 0, name: "", isHidden: false, size: 0, modifiedTime: 0 }; +} + +export const FileEntry = { + encode(message: FileEntry, writer: Writer = Writer.create()): Writer { + if (message.entryType !== 0) { + writer.uint32(8).int32(message.entryType); + } + if (message.name !== "") { + writer.uint32(18).string(message.name); + } + if (message.isHidden === true) { + writer.uint32(24).bool(message.isHidden); + } + if (message.size !== 0) { + writer.uint32(32).uint64(message.size); + } + if (message.modifiedTime !== 0) { + writer.uint32(40).uint64(message.modifiedTime); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): FileEntry { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFileEntry(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.entryType = reader.int32() as any; + break; + case 2: + message.name = reader.string(); + break; + case 3: + message.isHidden = reader.bool(); + break; + case 4: + message.size = longToNumber(reader.uint64() as Long); + break; + case 5: + message.modifiedTime = longToNumber(reader.uint64() as Long); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): FileEntry { + return { + entryType: isSet(object.entryType) + ? fileTypeFromJSON(object.entryType) + : 0, + name: isSet(object.name) ? String(object.name) : "", + isHidden: isSet(object.isHidden) ? Boolean(object.isHidden) : false, + size: isSet(object.size) ? Number(object.size) : 0, + modifiedTime: isSet(object.modifiedTime) + ? Number(object.modifiedTime) + : 0, + }; + }, + + toJSON(message: FileEntry): unknown { + const obj: any = {}; + message.entryType !== undefined && + (obj.entryType = fileTypeToJSON(message.entryType)); + message.name !== undefined && (obj.name = message.name); + message.isHidden !== undefined && (obj.isHidden = message.isHidden); + message.size !== undefined && (obj.size = Math.round(message.size)); + message.modifiedTime !== undefined && + (obj.modifiedTime = Math.round(message.modifiedTime)); + return obj; + }, + + fromPartial, I>>( + object: I + ): FileEntry { + const message = createBaseFileEntry(); + message.entryType = object.entryType ?? 0; + message.name = object.name ?? ""; + message.isHidden = object.isHidden ?? false; + message.size = object.size ?? 0; + message.modifiedTime = object.modifiedTime ?? 0; + return message; + }, +}; + +function createBaseFileDirectory(): FileDirectory { + return { id: 0, path: "", entries: [] }; +} + +export const FileDirectory = { + encode(message: FileDirectory, writer: Writer = Writer.create()): Writer { + if (message.id !== 0) { + writer.uint32(8).int32(message.id); + } + if (message.path !== "") { + writer.uint32(18).string(message.path); + } + for (const v of message.entries) { + FileEntry.encode(v!, writer.uint32(26).fork()).ldelim(); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): FileDirectory { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFileDirectory(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = reader.int32(); + break; + case 2: + message.path = reader.string(); + break; + case 3: + message.entries.push(FileEntry.decode(reader, reader.uint32())); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): FileDirectory { + return { + id: isSet(object.id) ? Number(object.id) : 0, + path: isSet(object.path) ? String(object.path) : "", + entries: Array.isArray(object?.entries) + ? object.entries.map((e: any) => FileEntry.fromJSON(e)) + : [], + }; + }, + + toJSON(message: FileDirectory): unknown { + const obj: any = {}; + message.id !== undefined && (obj.id = Math.round(message.id)); + message.path !== undefined && (obj.path = message.path); + if (message.entries) { + obj.entries = message.entries.map((e) => + e ? FileEntry.toJSON(e) : undefined + ); + } else { + obj.entries = []; + } + return obj; + }, + + fromPartial, I>>( + object: I + ): FileDirectory { + const message = createBaseFileDirectory(); + message.id = object.id ?? 0; + message.path = object.path ?? ""; + message.entries = + object.entries?.map((e) => FileEntry.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseReadDir(): ReadDir { + return { path: "", includeHidden: false }; +} + +export const ReadDir = { + encode(message: ReadDir, writer: Writer = Writer.create()): Writer { + if (message.path !== "") { + writer.uint32(10).string(message.path); + } + if (message.includeHidden === true) { + writer.uint32(16).bool(message.includeHidden); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): ReadDir { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseReadDir(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.path = reader.string(); + break; + case 2: + message.includeHidden = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): ReadDir { + return { + path: isSet(object.path) ? String(object.path) : "", + includeHidden: isSet(object.includeHidden) + ? Boolean(object.includeHidden) + : false, + }; + }, + + toJSON(message: ReadDir): unknown { + const obj: any = {}; + message.path !== undefined && (obj.path = message.path); + message.includeHidden !== undefined && + (obj.includeHidden = message.includeHidden); + return obj; + }, + + fromPartial, I>>(object: I): ReadDir { + const message = createBaseReadDir(); + message.path = object.path ?? ""; + message.includeHidden = object.includeHidden ?? false; + return message; + }, +}; + +function createBaseReadAllFiles(): ReadAllFiles { + return { id: 0, path: "", includeHidden: false }; +} + +export const ReadAllFiles = { + encode(message: ReadAllFiles, writer: Writer = Writer.create()): Writer { + if (message.id !== 0) { + writer.uint32(8).int32(message.id); + } + if (message.path !== "") { + writer.uint32(18).string(message.path); + } + if (message.includeHidden === true) { + writer.uint32(24).bool(message.includeHidden); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): ReadAllFiles { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseReadAllFiles(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = reader.int32(); + break; + case 2: + message.path = reader.string(); + break; + case 3: + message.includeHidden = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): ReadAllFiles { + return { + id: isSet(object.id) ? Number(object.id) : 0, + path: isSet(object.path) ? String(object.path) : "", + includeHidden: isSet(object.includeHidden) + ? Boolean(object.includeHidden) + : false, + }; + }, + + toJSON(message: ReadAllFiles): unknown { + const obj: any = {}; + message.id !== undefined && (obj.id = Math.round(message.id)); + message.path !== undefined && (obj.path = message.path); + message.includeHidden !== undefined && + (obj.includeHidden = message.includeHidden); + return obj; + }, + + fromPartial, I>>( + object: I + ): ReadAllFiles { + const message = createBaseReadAllFiles(); + message.id = object.id ?? 0; + message.path = object.path ?? ""; + message.includeHidden = object.includeHidden ?? false; + return message; + }, +}; + +function createBaseFileAction(): FileAction { + return { + readDir: undefined, + send: undefined, + receive: undefined, + create: undefined, + removeDir: undefined, + removeFile: undefined, + allFiles: undefined, + cancel: undefined, + }; +} + +export const FileAction = { + encode(message: FileAction, writer: Writer = Writer.create()): Writer { + if (message.readDir !== undefined) { + ReadDir.encode(message.readDir, writer.uint32(10).fork()).ldelim(); + } + if (message.send !== undefined) { + FileTransferSendRequest.encode( + message.send, + writer.uint32(18).fork() + ).ldelim(); + } + if (message.receive !== undefined) { + FileTransferReceiveRequest.encode( + message.receive, + writer.uint32(26).fork() + ).ldelim(); + } + if (message.create !== undefined) { + FileDirCreate.encode(message.create, writer.uint32(34).fork()).ldelim(); + } + if (message.removeDir !== undefined) { + FileRemoveDir.encode( + message.removeDir, + writer.uint32(42).fork() + ).ldelim(); + } + if (message.removeFile !== undefined) { + FileRemoveFile.encode( + message.removeFile, + writer.uint32(50).fork() + ).ldelim(); + } + if (message.allFiles !== undefined) { + ReadAllFiles.encode(message.allFiles, writer.uint32(58).fork()).ldelim(); + } + if (message.cancel !== undefined) { + FileTransferCancel.encode( + message.cancel, + writer.uint32(66).fork() + ).ldelim(); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): FileAction { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFileAction(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.readDir = ReadDir.decode(reader, reader.uint32()); + break; + case 2: + message.send = FileTransferSendRequest.decode( + reader, + reader.uint32() + ); + break; + case 3: + message.receive = FileTransferReceiveRequest.decode( + reader, + reader.uint32() + ); + break; + case 4: + message.create = FileDirCreate.decode(reader, reader.uint32()); + break; + case 5: + message.removeDir = FileRemoveDir.decode(reader, reader.uint32()); + break; + case 6: + message.removeFile = FileRemoveFile.decode(reader, reader.uint32()); + break; + case 7: + message.allFiles = ReadAllFiles.decode(reader, reader.uint32()); + break; + case 8: + message.cancel = FileTransferCancel.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): FileAction { + return { + readDir: isSet(object.readDir) + ? ReadDir.fromJSON(object.readDir) + : undefined, + send: isSet(object.send) + ? FileTransferSendRequest.fromJSON(object.send) + : undefined, + receive: isSet(object.receive) + ? FileTransferReceiveRequest.fromJSON(object.receive) + : undefined, + create: isSet(object.create) + ? FileDirCreate.fromJSON(object.create) + : undefined, + removeDir: isSet(object.removeDir) + ? FileRemoveDir.fromJSON(object.removeDir) + : undefined, + removeFile: isSet(object.removeFile) + ? FileRemoveFile.fromJSON(object.removeFile) + : undefined, + allFiles: isSet(object.allFiles) + ? ReadAllFiles.fromJSON(object.allFiles) + : undefined, + cancel: isSet(object.cancel) + ? FileTransferCancel.fromJSON(object.cancel) + : undefined, + }; + }, + + toJSON(message: FileAction): unknown { + const obj: any = {}; + message.readDir !== undefined && + (obj.readDir = message.readDir + ? ReadDir.toJSON(message.readDir) + : undefined); + message.send !== undefined && + (obj.send = message.send + ? FileTransferSendRequest.toJSON(message.send) + : undefined); + message.receive !== undefined && + (obj.receive = message.receive + ? FileTransferReceiveRequest.toJSON(message.receive) + : undefined); + message.create !== undefined && + (obj.create = message.create + ? FileDirCreate.toJSON(message.create) + : undefined); + message.removeDir !== undefined && + (obj.removeDir = message.removeDir + ? FileRemoveDir.toJSON(message.removeDir) + : undefined); + message.removeFile !== undefined && + (obj.removeFile = message.removeFile + ? FileRemoveFile.toJSON(message.removeFile) + : undefined); + message.allFiles !== undefined && + (obj.allFiles = message.allFiles + ? ReadAllFiles.toJSON(message.allFiles) + : undefined); + message.cancel !== undefined && + (obj.cancel = message.cancel + ? FileTransferCancel.toJSON(message.cancel) + : undefined); + return obj; + }, + + fromPartial, I>>( + object: I + ): FileAction { + const message = createBaseFileAction(); + message.readDir = + object.readDir !== undefined && object.readDir !== null + ? ReadDir.fromPartial(object.readDir) + : undefined; + message.send = + object.send !== undefined && object.send !== null + ? FileTransferSendRequest.fromPartial(object.send) + : undefined; + message.receive = + object.receive !== undefined && object.receive !== null + ? FileTransferReceiveRequest.fromPartial(object.receive) + : undefined; + message.create = + object.create !== undefined && object.create !== null + ? FileDirCreate.fromPartial(object.create) + : undefined; + message.removeDir = + object.removeDir !== undefined && object.removeDir !== null + ? FileRemoveDir.fromPartial(object.removeDir) + : undefined; + message.removeFile = + object.removeFile !== undefined && object.removeFile !== null + ? FileRemoveFile.fromPartial(object.removeFile) + : undefined; + message.allFiles = + object.allFiles !== undefined && object.allFiles !== null + ? ReadAllFiles.fromPartial(object.allFiles) + : undefined; + message.cancel = + object.cancel !== undefined && object.cancel !== null + ? FileTransferCancel.fromPartial(object.cancel) + : undefined; + return message; + }, +}; + +function createBaseFileTransferCancel(): FileTransferCancel { + return { id: 0 }; +} + +export const FileTransferCancel = { + encode( + message: FileTransferCancel, + writer: Writer = Writer.create() + ): Writer { + if (message.id !== 0) { + writer.uint32(8).int32(message.id); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): FileTransferCancel { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFileTransferCancel(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): FileTransferCancel { + return { + id: isSet(object.id) ? Number(object.id) : 0, + }; + }, + + toJSON(message: FileTransferCancel): unknown { + const obj: any = {}; + message.id !== undefined && (obj.id = Math.round(message.id)); + return obj; + }, + + fromPartial, I>>( + object: I + ): FileTransferCancel { + const message = createBaseFileTransferCancel(); + message.id = object.id ?? 0; + return message; + }, +}; + +function createBaseFileResponse(): FileResponse { + return { + dir: undefined, + block: undefined, + error: undefined, + done: undefined, + }; +} + +export const FileResponse = { + encode(message: FileResponse, writer: Writer = Writer.create()): Writer { + if (message.dir !== undefined) { + FileDirectory.encode(message.dir, writer.uint32(10).fork()).ldelim(); + } + if (message.block !== undefined) { + FileTransferBlock.encode( + message.block, + writer.uint32(18).fork() + ).ldelim(); + } + if (message.error !== undefined) { + FileTransferError.encode( + message.error, + writer.uint32(26).fork() + ).ldelim(); + } + if (message.done !== undefined) { + FileTransferDone.encode(message.done, writer.uint32(34).fork()).ldelim(); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): FileResponse { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFileResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.dir = FileDirectory.decode(reader, reader.uint32()); + break; + case 2: + message.block = FileTransferBlock.decode(reader, reader.uint32()); + break; + case 3: + message.error = FileTransferError.decode(reader, reader.uint32()); + break; + case 4: + message.done = FileTransferDone.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): FileResponse { + return { + dir: isSet(object.dir) ? FileDirectory.fromJSON(object.dir) : undefined, + block: isSet(object.block) + ? FileTransferBlock.fromJSON(object.block) + : undefined, + error: isSet(object.error) + ? FileTransferError.fromJSON(object.error) + : undefined, + done: isSet(object.done) + ? FileTransferDone.fromJSON(object.done) + : undefined, + }; + }, + + toJSON(message: FileResponse): unknown { + const obj: any = {}; + message.dir !== undefined && + (obj.dir = message.dir ? FileDirectory.toJSON(message.dir) : undefined); + message.block !== undefined && + (obj.block = message.block + ? FileTransferBlock.toJSON(message.block) + : undefined); + message.error !== undefined && + (obj.error = message.error + ? FileTransferError.toJSON(message.error) + : undefined); + message.done !== undefined && + (obj.done = message.done + ? FileTransferDone.toJSON(message.done) + : undefined); + return obj; + }, + + fromPartial, I>>( + object: I + ): FileResponse { + const message = createBaseFileResponse(); + message.dir = + object.dir !== undefined && object.dir !== null + ? FileDirectory.fromPartial(object.dir) + : undefined; + message.block = + object.block !== undefined && object.block !== null + ? FileTransferBlock.fromPartial(object.block) + : undefined; + message.error = + object.error !== undefined && object.error !== null + ? FileTransferError.fromPartial(object.error) + : undefined; + message.done = + object.done !== undefined && object.done !== null + ? FileTransferDone.fromPartial(object.done) + : undefined; + return message; + }, +}; + +function createBaseFileTransferBlock(): FileTransferBlock { + return { id: 0, fileNum: 0, data: new Uint8Array(), compressed: false }; +} + +export const FileTransferBlock = { + encode(message: FileTransferBlock, writer: Writer = Writer.create()): Writer { + if (message.id !== 0) { + writer.uint32(8).int32(message.id); + } + if (message.fileNum !== 0) { + writer.uint32(16).sint32(message.fileNum); + } + if (message.data.length !== 0) { + writer.uint32(26).bytes(message.data); + } + if (message.compressed === true) { + writer.uint32(32).bool(message.compressed); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): FileTransferBlock { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFileTransferBlock(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = reader.int32(); + break; + case 2: + message.fileNum = reader.sint32(); + break; + case 3: + message.data = reader.bytes(); + break; + case 4: + message.compressed = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): FileTransferBlock { + return { + id: isSet(object.id) ? Number(object.id) : 0, + fileNum: isSet(object.fileNum) ? Number(object.fileNum) : 0, + data: isSet(object.data) + ? bytesFromBase64(object.data) + : new Uint8Array(), + compressed: isSet(object.compressed) ? Boolean(object.compressed) : false, + }; + }, + + toJSON(message: FileTransferBlock): unknown { + const obj: any = {}; + message.id !== undefined && (obj.id = Math.round(message.id)); + message.fileNum !== undefined && + (obj.fileNum = Math.round(message.fileNum)); + message.data !== undefined && + (obj.data = base64FromBytes( + message.data !== undefined ? message.data : new Uint8Array() + )); + message.compressed !== undefined && (obj.compressed = message.compressed); + return obj; + }, + + fromPartial, I>>( + object: I + ): FileTransferBlock { + const message = createBaseFileTransferBlock(); + message.id = object.id ?? 0; + message.fileNum = object.fileNum ?? 0; + message.data = object.data ?? new Uint8Array(); + message.compressed = object.compressed ?? false; + return message; + }, +}; + +function createBaseFileTransferError(): FileTransferError { + return { id: 0, error: "", fileNum: 0 }; +} + +export const FileTransferError = { + encode(message: FileTransferError, writer: Writer = Writer.create()): Writer { + if (message.id !== 0) { + writer.uint32(8).int32(message.id); + } + if (message.error !== "") { + writer.uint32(18).string(message.error); + } + if (message.fileNum !== 0) { + writer.uint32(24).sint32(message.fileNum); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): FileTransferError { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFileTransferError(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = reader.int32(); + break; + case 2: + message.error = reader.string(); + break; + case 3: + message.fileNum = reader.sint32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): FileTransferError { + return { + id: isSet(object.id) ? Number(object.id) : 0, + error: isSet(object.error) ? String(object.error) : "", + fileNum: isSet(object.fileNum) ? Number(object.fileNum) : 0, + }; + }, + + toJSON(message: FileTransferError): unknown { + const obj: any = {}; + message.id !== undefined && (obj.id = Math.round(message.id)); + message.error !== undefined && (obj.error = message.error); + message.fileNum !== undefined && + (obj.fileNum = Math.round(message.fileNum)); + return obj; + }, + + fromPartial, I>>( + object: I + ): FileTransferError { + const message = createBaseFileTransferError(); + message.id = object.id ?? 0; + message.error = object.error ?? ""; + message.fileNum = object.fileNum ?? 0; + return message; + }, +}; + +function createBaseFileTransferSendRequest(): FileTransferSendRequest { + return { id: 0, path: "", includeHidden: false }; +} + +export const FileTransferSendRequest = { + encode( + message: FileTransferSendRequest, + writer: Writer = Writer.create() + ): Writer { + if (message.id !== 0) { + writer.uint32(8).int32(message.id); + } + if (message.path !== "") { + writer.uint32(18).string(message.path); + } + if (message.includeHidden === true) { + writer.uint32(24).bool(message.includeHidden); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): FileTransferSendRequest { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFileTransferSendRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = reader.int32(); + break; + case 2: + message.path = reader.string(); + break; + case 3: + message.includeHidden = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): FileTransferSendRequest { + return { + id: isSet(object.id) ? Number(object.id) : 0, + path: isSet(object.path) ? String(object.path) : "", + includeHidden: isSet(object.includeHidden) + ? Boolean(object.includeHidden) + : false, + }; + }, + + toJSON(message: FileTransferSendRequest): unknown { + const obj: any = {}; + message.id !== undefined && (obj.id = Math.round(message.id)); + message.path !== undefined && (obj.path = message.path); + message.includeHidden !== undefined && + (obj.includeHidden = message.includeHidden); + return obj; + }, + + fromPartial, I>>( + object: I + ): FileTransferSendRequest { + const message = createBaseFileTransferSendRequest(); + message.id = object.id ?? 0; + message.path = object.path ?? ""; + message.includeHidden = object.includeHidden ?? false; + return message; + }, +}; + +function createBaseFileTransferDone(): FileTransferDone { + return { id: 0, fileNum: 0 }; +} + +export const FileTransferDone = { + encode(message: FileTransferDone, writer: Writer = Writer.create()): Writer { + if (message.id !== 0) { + writer.uint32(8).int32(message.id); + } + if (message.fileNum !== 0) { + writer.uint32(16).sint32(message.fileNum); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): FileTransferDone { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFileTransferDone(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = reader.int32(); + break; + case 2: + message.fileNum = reader.sint32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): FileTransferDone { + return { + id: isSet(object.id) ? Number(object.id) : 0, + fileNum: isSet(object.fileNum) ? Number(object.fileNum) : 0, + }; + }, + + toJSON(message: FileTransferDone): unknown { + const obj: any = {}; + message.id !== undefined && (obj.id = Math.round(message.id)); + message.fileNum !== undefined && + (obj.fileNum = Math.round(message.fileNum)); + return obj; + }, + + fromPartial, I>>( + object: I + ): FileTransferDone { + const message = createBaseFileTransferDone(); + message.id = object.id ?? 0; + message.fileNum = object.fileNum ?? 0; + return message; + }, +}; + +function createBaseFileTransferReceiveRequest(): FileTransferReceiveRequest { + return { id: 0, path: "", files: [] }; +} + +export const FileTransferReceiveRequest = { + encode( + message: FileTransferReceiveRequest, + writer: Writer = Writer.create() + ): Writer { + if (message.id !== 0) { + writer.uint32(8).int32(message.id); + } + if (message.path !== "") { + writer.uint32(18).string(message.path); + } + for (const v of message.files) { + FileEntry.encode(v!, writer.uint32(26).fork()).ldelim(); + } + return writer; + }, + + decode( + input: Reader | Uint8Array, + length?: number + ): FileTransferReceiveRequest { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFileTransferReceiveRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = reader.int32(); + break; + case 2: + message.path = reader.string(); + break; + case 3: + message.files.push(FileEntry.decode(reader, reader.uint32())); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): FileTransferReceiveRequest { + return { + id: isSet(object.id) ? Number(object.id) : 0, + path: isSet(object.path) ? String(object.path) : "", + files: Array.isArray(object?.files) + ? object.files.map((e: any) => FileEntry.fromJSON(e)) + : [], + }; + }, + + toJSON(message: FileTransferReceiveRequest): unknown { + const obj: any = {}; + message.id !== undefined && (obj.id = Math.round(message.id)); + message.path !== undefined && (obj.path = message.path); + if (message.files) { + obj.files = message.files.map((e) => + e ? FileEntry.toJSON(e) : undefined + ); + } else { + obj.files = []; + } + return obj; + }, + + fromPartial, I>>( + object: I + ): FileTransferReceiveRequest { + const message = createBaseFileTransferReceiveRequest(); + message.id = object.id ?? 0; + message.path = object.path ?? ""; + message.files = object.files?.map((e) => FileEntry.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseFileRemoveDir(): FileRemoveDir { + return { id: 0, path: "", recursive: false }; +} + +export const FileRemoveDir = { + encode(message: FileRemoveDir, writer: Writer = Writer.create()): Writer { + if (message.id !== 0) { + writer.uint32(8).int32(message.id); + } + if (message.path !== "") { + writer.uint32(18).string(message.path); + } + if (message.recursive === true) { + writer.uint32(24).bool(message.recursive); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): FileRemoveDir { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFileRemoveDir(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = reader.int32(); + break; + case 2: + message.path = reader.string(); + break; + case 3: + message.recursive = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): FileRemoveDir { + return { + id: isSet(object.id) ? Number(object.id) : 0, + path: isSet(object.path) ? String(object.path) : "", + recursive: isSet(object.recursive) ? Boolean(object.recursive) : false, + }; + }, + + toJSON(message: FileRemoveDir): unknown { + const obj: any = {}; + message.id !== undefined && (obj.id = Math.round(message.id)); + message.path !== undefined && (obj.path = message.path); + message.recursive !== undefined && (obj.recursive = message.recursive); + return obj; + }, + + fromPartial, I>>( + object: I + ): FileRemoveDir { + const message = createBaseFileRemoveDir(); + message.id = object.id ?? 0; + message.path = object.path ?? ""; + message.recursive = object.recursive ?? false; + return message; + }, +}; + +function createBaseFileRemoveFile(): FileRemoveFile { + return { id: 0, path: "", fileNum: 0 }; +} + +export const FileRemoveFile = { + encode(message: FileRemoveFile, writer: Writer = Writer.create()): Writer { + if (message.id !== 0) { + writer.uint32(8).int32(message.id); + } + if (message.path !== "") { + writer.uint32(18).string(message.path); + } + if (message.fileNum !== 0) { + writer.uint32(24).sint32(message.fileNum); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): FileRemoveFile { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFileRemoveFile(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = reader.int32(); + break; + case 2: + message.path = reader.string(); + break; + case 3: + message.fileNum = reader.sint32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): FileRemoveFile { + return { + id: isSet(object.id) ? Number(object.id) : 0, + path: isSet(object.path) ? String(object.path) : "", + fileNum: isSet(object.fileNum) ? Number(object.fileNum) : 0, + }; + }, + + toJSON(message: FileRemoveFile): unknown { + const obj: any = {}; + message.id !== undefined && (obj.id = Math.round(message.id)); + message.path !== undefined && (obj.path = message.path); + message.fileNum !== undefined && + (obj.fileNum = Math.round(message.fileNum)); + return obj; + }, + + fromPartial, I>>( + object: I + ): FileRemoveFile { + const message = createBaseFileRemoveFile(); + message.id = object.id ?? 0; + message.path = object.path ?? ""; + message.fileNum = object.fileNum ?? 0; + return message; + }, +}; + +function createBaseFileDirCreate(): FileDirCreate { + return { id: 0, path: "" }; +} + +export const FileDirCreate = { + encode(message: FileDirCreate, writer: Writer = Writer.create()): Writer { + if (message.id !== 0) { + writer.uint32(8).int32(message.id); + } + if (message.path !== "") { + writer.uint32(18).string(message.path); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): FileDirCreate { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFileDirCreate(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = reader.int32(); + break; + case 2: + message.path = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): FileDirCreate { + return { + id: isSet(object.id) ? Number(object.id) : 0, + path: isSet(object.path) ? String(object.path) : "", + }; + }, + + toJSON(message: FileDirCreate): unknown { + const obj: any = {}; + message.id !== undefined && (obj.id = Math.round(message.id)); + message.path !== undefined && (obj.path = message.path); + return obj; + }, + + fromPartial, I>>( + object: I + ): FileDirCreate { + const message = createBaseFileDirCreate(); + message.id = object.id ?? 0; + message.path = object.path ?? ""; + return message; + }, +}; + +function createBaseSwitchDisplay(): SwitchDisplay { + return { display: 0, x: 0, y: 0, width: 0, height: 0 }; +} + +export const SwitchDisplay = { + encode(message: SwitchDisplay, writer: Writer = Writer.create()): Writer { + if (message.display !== 0) { + writer.uint32(8).int32(message.display); + } + if (message.x !== 0) { + writer.uint32(16).sint32(message.x); + } + if (message.y !== 0) { + writer.uint32(24).sint32(message.y); + } + if (message.width !== 0) { + writer.uint32(32).int32(message.width); + } + if (message.height !== 0) { + writer.uint32(40).int32(message.height); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): SwitchDisplay { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSwitchDisplay(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.display = reader.int32(); + break; + case 2: + message.x = reader.sint32(); + break; + case 3: + message.y = reader.sint32(); + break; + case 4: + message.width = reader.int32(); + break; + case 5: + message.height = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): SwitchDisplay { + return { + display: isSet(object.display) ? Number(object.display) : 0, + x: isSet(object.x) ? Number(object.x) : 0, + y: isSet(object.y) ? Number(object.y) : 0, + width: isSet(object.width) ? Number(object.width) : 0, + height: isSet(object.height) ? Number(object.height) : 0, + }; + }, + + toJSON(message: SwitchDisplay): unknown { + const obj: any = {}; + message.display !== undefined && + (obj.display = Math.round(message.display)); + message.x !== undefined && (obj.x = Math.round(message.x)); + message.y !== undefined && (obj.y = Math.round(message.y)); + message.width !== undefined && (obj.width = Math.round(message.width)); + message.height !== undefined && (obj.height = Math.round(message.height)); + return obj; + }, + + fromPartial, I>>( + object: I + ): SwitchDisplay { + const message = createBaseSwitchDisplay(); + message.display = object.display ?? 0; + message.x = object.x ?? 0; + message.y = object.y ?? 0; + message.width = object.width ?? 0; + message.height = object.height ?? 0; + return message; + }, +}; + +function createBasePermissionInfo(): PermissionInfo { + return { permission: 0, enabled: false }; +} + +export const PermissionInfo = { + encode(message: PermissionInfo, writer: Writer = Writer.create()): Writer { + if (message.permission !== 0) { + writer.uint32(8).int32(message.permission); + } + if (message.enabled === true) { + writer.uint32(16).bool(message.enabled); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): PermissionInfo { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBasePermissionInfo(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.permission = reader.int32() as any; + break; + case 2: + message.enabled = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): PermissionInfo { + return { + permission: isSet(object.permission) + ? permissionInfo_PermissionFromJSON(object.permission) + : 0, + enabled: isSet(object.enabled) ? Boolean(object.enabled) : false, + }; + }, + + toJSON(message: PermissionInfo): unknown { + const obj: any = {}; + message.permission !== undefined && + (obj.permission = permissionInfo_PermissionToJSON(message.permission)); + message.enabled !== undefined && (obj.enabled = message.enabled); + return obj; + }, + + fromPartial, I>>( + object: I + ): PermissionInfo { + const message = createBasePermissionInfo(); + message.permission = object.permission ?? 0; + message.enabled = object.enabled ?? false; + return message; + }, +}; + +function createBaseOptionMessage(): OptionMessage { + return { + imageQuality: 0, + lockAfterSessionEnd: 0, + showRemoteCursor: 0, + privacyMode: 0, + blockInput: 0, + customImageQuality: 0, + disableAudio: 0, + disableClipboard: 0, + }; +} + +export const OptionMessage = { + encode(message: OptionMessage, writer: Writer = Writer.create()): Writer { + if (message.imageQuality !== 0) { + writer.uint32(8).int32(message.imageQuality); + } + if (message.lockAfterSessionEnd !== 0) { + writer.uint32(16).int32(message.lockAfterSessionEnd); + } + if (message.showRemoteCursor !== 0) { + writer.uint32(24).int32(message.showRemoteCursor); + } + if (message.privacyMode !== 0) { + writer.uint32(32).int32(message.privacyMode); + } + if (message.blockInput !== 0) { + writer.uint32(40).int32(message.blockInput); + } + if (message.customImageQuality !== 0) { + writer.uint32(48).int32(message.customImageQuality); + } + if (message.disableAudio !== 0) { + writer.uint32(56).int32(message.disableAudio); + } + if (message.disableClipboard !== 0) { + writer.uint32(64).int32(message.disableClipboard); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): OptionMessage { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseOptionMessage(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.imageQuality = reader.int32() as any; + break; + case 2: + message.lockAfterSessionEnd = reader.int32() as any; + break; + case 3: + message.showRemoteCursor = reader.int32() as any; + break; + case 4: + message.privacyMode = reader.int32() as any; + break; + case 5: + message.blockInput = reader.int32() as any; + break; + case 6: + message.customImageQuality = reader.int32(); + break; + case 7: + message.disableAudio = reader.int32() as any; + break; + case 8: + message.disableClipboard = reader.int32() as any; + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): OptionMessage { + return { + imageQuality: isSet(object.imageQuality) + ? imageQualityFromJSON(object.imageQuality) + : 0, + lockAfterSessionEnd: isSet(object.lockAfterSessionEnd) + ? optionMessage_BoolOptionFromJSON(object.lockAfterSessionEnd) + : 0, + showRemoteCursor: isSet(object.showRemoteCursor) + ? optionMessage_BoolOptionFromJSON(object.showRemoteCursor) + : 0, + privacyMode: isSet(object.privacyMode) + ? optionMessage_BoolOptionFromJSON(object.privacyMode) + : 0, + blockInput: isSet(object.blockInput) + ? optionMessage_BoolOptionFromJSON(object.blockInput) + : 0, + customImageQuality: isSet(object.customImageQuality) + ? Number(object.customImageQuality) + : 0, + disableAudio: isSet(object.disableAudio) + ? optionMessage_BoolOptionFromJSON(object.disableAudio) + : 0, + disableClipboard: isSet(object.disableClipboard) + ? optionMessage_BoolOptionFromJSON(object.disableClipboard) + : 0, + }; + }, + + toJSON(message: OptionMessage): unknown { + const obj: any = {}; + message.imageQuality !== undefined && + (obj.imageQuality = imageQualityToJSON(message.imageQuality)); + message.lockAfterSessionEnd !== undefined && + (obj.lockAfterSessionEnd = optionMessage_BoolOptionToJSON( + message.lockAfterSessionEnd + )); + message.showRemoteCursor !== undefined && + (obj.showRemoteCursor = optionMessage_BoolOptionToJSON( + message.showRemoteCursor + )); + message.privacyMode !== undefined && + (obj.privacyMode = optionMessage_BoolOptionToJSON(message.privacyMode)); + message.blockInput !== undefined && + (obj.blockInput = optionMessage_BoolOptionToJSON(message.blockInput)); + message.customImageQuality !== undefined && + (obj.customImageQuality = Math.round(message.customImageQuality)); + message.disableAudio !== undefined && + (obj.disableAudio = optionMessage_BoolOptionToJSON(message.disableAudio)); + message.disableClipboard !== undefined && + (obj.disableClipboard = optionMessage_BoolOptionToJSON( + message.disableClipboard + )); + return obj; + }, + + fromPartial, I>>( + object: I + ): OptionMessage { + const message = createBaseOptionMessage(); + message.imageQuality = object.imageQuality ?? 0; + message.lockAfterSessionEnd = object.lockAfterSessionEnd ?? 0; + message.showRemoteCursor = object.showRemoteCursor ?? 0; + message.privacyMode = object.privacyMode ?? 0; + message.blockInput = object.blockInput ?? 0; + message.customImageQuality = object.customImageQuality ?? 0; + message.disableAudio = object.disableAudio ?? 0; + message.disableClipboard = object.disableClipboard ?? 0; + return message; + }, +}; + +function createBaseOptionResponse(): OptionResponse { + return { opt: undefined, error: "" }; +} + +export const OptionResponse = { + encode(message: OptionResponse, writer: Writer = Writer.create()): Writer { + if (message.opt !== undefined) { + OptionMessage.encode(message.opt, writer.uint32(10).fork()).ldelim(); + } + if (message.error !== "") { + writer.uint32(18).string(message.error); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): OptionResponse { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseOptionResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.opt = OptionMessage.decode(reader, reader.uint32()); + break; + case 2: + message.error = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): OptionResponse { + return { + opt: isSet(object.opt) ? OptionMessage.fromJSON(object.opt) : undefined, + error: isSet(object.error) ? String(object.error) : "", + }; + }, + + toJSON(message: OptionResponse): unknown { + const obj: any = {}; + message.opt !== undefined && + (obj.opt = message.opt ? OptionMessage.toJSON(message.opt) : undefined); + message.error !== undefined && (obj.error = message.error); + return obj; + }, + + fromPartial, I>>( + object: I + ): OptionResponse { + const message = createBaseOptionResponse(); + message.opt = + object.opt !== undefined && object.opt !== null + ? OptionMessage.fromPartial(object.opt) + : undefined; + message.error = object.error ?? ""; + return message; + }, +}; + +function createBaseTestDelay(): TestDelay { + return { time: 0, fromClient: false }; +} + +export const TestDelay = { + encode(message: TestDelay, writer: Writer = Writer.create()): Writer { + if (message.time !== 0) { + writer.uint32(8).int64(message.time); + } + if (message.fromClient === true) { + writer.uint32(16).bool(message.fromClient); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): TestDelay { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTestDelay(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.time = longToNumber(reader.int64() as Long); + break; + case 2: + message.fromClient = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): TestDelay { + return { + time: isSet(object.time) ? Number(object.time) : 0, + fromClient: isSet(object.fromClient) ? Boolean(object.fromClient) : false, + }; + }, + + toJSON(message: TestDelay): unknown { + const obj: any = {}; + message.time !== undefined && (obj.time = Math.round(message.time)); + message.fromClient !== undefined && (obj.fromClient = message.fromClient); + return obj; + }, + + fromPartial, I>>( + object: I + ): TestDelay { + const message = createBaseTestDelay(); + message.time = object.time ?? 0; + message.fromClient = object.fromClient ?? false; + return message; + }, +}; + +function createBasePublicKey(): PublicKey { + return { + asymmetricValue: new Uint8Array(), + symmetricValue: new Uint8Array(), + }; +} + +export const PublicKey = { + encode(message: PublicKey, writer: Writer = Writer.create()): Writer { + if (message.asymmetricValue.length !== 0) { + writer.uint32(10).bytes(message.asymmetricValue); + } + if (message.symmetricValue.length !== 0) { + writer.uint32(18).bytes(message.symmetricValue); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): PublicKey { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBasePublicKey(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.asymmetricValue = reader.bytes(); + break; + case 2: + message.symmetricValue = reader.bytes(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): PublicKey { + return { + asymmetricValue: isSet(object.asymmetricValue) + ? bytesFromBase64(object.asymmetricValue) + : new Uint8Array(), + symmetricValue: isSet(object.symmetricValue) + ? bytesFromBase64(object.symmetricValue) + : new Uint8Array(), + }; + }, + + toJSON(message: PublicKey): unknown { + const obj: any = {}; + message.asymmetricValue !== undefined && + (obj.asymmetricValue = base64FromBytes( + message.asymmetricValue !== undefined + ? message.asymmetricValue + : new Uint8Array() + )); + message.symmetricValue !== undefined && + (obj.symmetricValue = base64FromBytes( + message.symmetricValue !== undefined + ? message.symmetricValue + : new Uint8Array() + )); + return obj; + }, + + fromPartial, I>>( + object: I + ): PublicKey { + const message = createBasePublicKey(); + message.asymmetricValue = object.asymmetricValue ?? new Uint8Array(); + message.symmetricValue = object.symmetricValue ?? new Uint8Array(); + return message; + }, +}; + +function createBaseSignedId(): SignedId { + return { id: new Uint8Array() }; +} + +export const SignedId = { + encode(message: SignedId, writer: Writer = Writer.create()): Writer { + if (message.id.length !== 0) { + writer.uint32(10).bytes(message.id); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): SignedId { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSignedId(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = reader.bytes(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): SignedId { + return { + id: isSet(object.id) ? bytesFromBase64(object.id) : new Uint8Array(), + }; + }, + + toJSON(message: SignedId): unknown { + const obj: any = {}; + message.id !== undefined && + (obj.id = base64FromBytes( + message.id !== undefined ? message.id : new Uint8Array() + )); + return obj; + }, + + fromPartial, I>>(object: I): SignedId { + const message = createBaseSignedId(); + message.id = object.id ?? new Uint8Array(); + return message; + }, +}; + +function createBaseAudioFormat(): AudioFormat { + return { sampleRate: 0, channels: 0 }; +} + +export const AudioFormat = { + encode(message: AudioFormat, writer: Writer = Writer.create()): Writer { + if (message.sampleRate !== 0) { + writer.uint32(8).uint32(message.sampleRate); + } + if (message.channels !== 0) { + writer.uint32(16).uint32(message.channels); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): AudioFormat { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAudioFormat(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.sampleRate = reader.uint32(); + break; + case 2: + message.channels = reader.uint32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): AudioFormat { + return { + sampleRate: isSet(object.sampleRate) ? Number(object.sampleRate) : 0, + channels: isSet(object.channels) ? Number(object.channels) : 0, + }; + }, + + toJSON(message: AudioFormat): unknown { + const obj: any = {}; + message.sampleRate !== undefined && + (obj.sampleRate = Math.round(message.sampleRate)); + message.channels !== undefined && + (obj.channels = Math.round(message.channels)); + return obj; + }, + + fromPartial, I>>( + object: I + ): AudioFormat { + const message = createBaseAudioFormat(); + message.sampleRate = object.sampleRate ?? 0; + message.channels = object.channels ?? 0; + return message; + }, +}; + +function createBaseAudioFrame(): AudioFrame { + return { data: new Uint8Array() }; +} + +export const AudioFrame = { + encode(message: AudioFrame, writer: Writer = Writer.create()): Writer { + if (message.data.length !== 0) { + writer.uint32(10).bytes(message.data); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): AudioFrame { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAudioFrame(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.data = reader.bytes(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): AudioFrame { + return { + data: isSet(object.data) + ? bytesFromBase64(object.data) + : new Uint8Array(), + }; + }, + + toJSON(message: AudioFrame): unknown { + const obj: any = {}; + message.data !== undefined && + (obj.data = base64FromBytes( + message.data !== undefined ? message.data : new Uint8Array() + )); + return obj; + }, + + fromPartial, I>>( + object: I + ): AudioFrame { + const message = createBaseAudioFrame(); + message.data = object.data ?? new Uint8Array(); + return message; + }, +}; + +function createBaseMisc(): Misc { + return { + chatMessage: undefined, + switchDisplay: undefined, + permissionInfo: undefined, + option: undefined, + audioFormat: undefined, + closeReason: undefined, + refreshVideo: undefined, + optionResponse: undefined, + }; +} + +export const Misc = { + encode(message: Misc, writer: Writer = Writer.create()): Writer { + if (message.chatMessage !== undefined) { + ChatMessage.encode( + message.chatMessage, + writer.uint32(34).fork() + ).ldelim(); + } + if (message.switchDisplay !== undefined) { + SwitchDisplay.encode( + message.switchDisplay, + writer.uint32(42).fork() + ).ldelim(); + } + if (message.permissionInfo !== undefined) { + PermissionInfo.encode( + message.permissionInfo, + writer.uint32(50).fork() + ).ldelim(); + } + if (message.option !== undefined) { + OptionMessage.encode(message.option, writer.uint32(58).fork()).ldelim(); + } + if (message.audioFormat !== undefined) { + AudioFormat.encode( + message.audioFormat, + writer.uint32(66).fork() + ).ldelim(); + } + if (message.closeReason !== undefined) { + writer.uint32(74).string(message.closeReason); + } + if (message.refreshVideo !== undefined) { + writer.uint32(80).bool(message.refreshVideo); + } + if (message.optionResponse !== undefined) { + OptionResponse.encode( + message.optionResponse, + writer.uint32(90).fork() + ).ldelim(); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): Misc { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMisc(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 4: + message.chatMessage = ChatMessage.decode(reader, reader.uint32()); + break; + case 5: + message.switchDisplay = SwitchDisplay.decode(reader, reader.uint32()); + break; + case 6: + message.permissionInfo = PermissionInfo.decode( + reader, + reader.uint32() + ); + break; + case 7: + message.option = OptionMessage.decode(reader, reader.uint32()); + break; + case 8: + message.audioFormat = AudioFormat.decode(reader, reader.uint32()); + break; + case 9: + message.closeReason = reader.string(); + break; + case 10: + message.refreshVideo = reader.bool(); + break; + case 11: + message.optionResponse = OptionResponse.decode( + reader, + reader.uint32() + ); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): Misc { + return { + chatMessage: isSet(object.chatMessage) + ? ChatMessage.fromJSON(object.chatMessage) + : undefined, + switchDisplay: isSet(object.switchDisplay) + ? SwitchDisplay.fromJSON(object.switchDisplay) + : undefined, + permissionInfo: isSet(object.permissionInfo) + ? PermissionInfo.fromJSON(object.permissionInfo) + : undefined, + option: isSet(object.option) + ? OptionMessage.fromJSON(object.option) + : undefined, + audioFormat: isSet(object.audioFormat) + ? AudioFormat.fromJSON(object.audioFormat) + : undefined, + closeReason: isSet(object.closeReason) + ? String(object.closeReason) + : undefined, + refreshVideo: isSet(object.refreshVideo) + ? Boolean(object.refreshVideo) + : undefined, + optionResponse: isSet(object.optionResponse) + ? OptionResponse.fromJSON(object.optionResponse) + : undefined, + }; + }, + + toJSON(message: Misc): unknown { + const obj: any = {}; + message.chatMessage !== undefined && + (obj.chatMessage = message.chatMessage + ? ChatMessage.toJSON(message.chatMessage) + : undefined); + message.switchDisplay !== undefined && + (obj.switchDisplay = message.switchDisplay + ? SwitchDisplay.toJSON(message.switchDisplay) + : undefined); + message.permissionInfo !== undefined && + (obj.permissionInfo = message.permissionInfo + ? PermissionInfo.toJSON(message.permissionInfo) + : undefined); + message.option !== undefined && + (obj.option = message.option + ? OptionMessage.toJSON(message.option) + : undefined); + message.audioFormat !== undefined && + (obj.audioFormat = message.audioFormat + ? AudioFormat.toJSON(message.audioFormat) + : undefined); + message.closeReason !== undefined && + (obj.closeReason = message.closeReason); + message.refreshVideo !== undefined && + (obj.refreshVideo = message.refreshVideo); + message.optionResponse !== undefined && + (obj.optionResponse = message.optionResponse + ? OptionResponse.toJSON(message.optionResponse) + : undefined); + return obj; + }, + + fromPartial, I>>(object: I): Misc { + const message = createBaseMisc(); + message.chatMessage = + object.chatMessage !== undefined && object.chatMessage !== null + ? ChatMessage.fromPartial(object.chatMessage) + : undefined; + message.switchDisplay = + object.switchDisplay !== undefined && object.switchDisplay !== null + ? SwitchDisplay.fromPartial(object.switchDisplay) + : undefined; + message.permissionInfo = + object.permissionInfo !== undefined && object.permissionInfo !== null + ? PermissionInfo.fromPartial(object.permissionInfo) + : undefined; + message.option = + object.option !== undefined && object.option !== null + ? OptionMessage.fromPartial(object.option) + : undefined; + message.audioFormat = + object.audioFormat !== undefined && object.audioFormat !== null + ? AudioFormat.fromPartial(object.audioFormat) + : undefined; + message.closeReason = object.closeReason ?? undefined; + message.refreshVideo = object.refreshVideo ?? undefined; + message.optionResponse = + object.optionResponse !== undefined && object.optionResponse !== null + ? OptionResponse.fromPartial(object.optionResponse) + : undefined; + return message; + }, +}; + +function createBaseMessage(): Message { + return { + signedId: undefined, + publicKey: undefined, + testDelay: undefined, + videoFrame: undefined, + loginRequest: undefined, + loginResponse: undefined, + hash: undefined, + mouseEvent: undefined, + audioFrame: undefined, + cursorData: undefined, + cursorPosition: undefined, + cursorId: undefined, + keyEvent: undefined, + clipboard: undefined, + fileAction: undefined, + fileResponse: undefined, + misc: undefined, + }; +} + +export const Message = { + encode(message: Message, writer: Writer = Writer.create()): Writer { + if (message.signedId !== undefined) { + SignedId.encode(message.signedId, writer.uint32(26).fork()).ldelim(); + } + if (message.publicKey !== undefined) { + PublicKey.encode(message.publicKey, writer.uint32(34).fork()).ldelim(); + } + if (message.testDelay !== undefined) { + TestDelay.encode(message.testDelay, writer.uint32(42).fork()).ldelim(); + } + if (message.videoFrame !== undefined) { + VideoFrame.encode(message.videoFrame, writer.uint32(50).fork()).ldelim(); + } + if (message.loginRequest !== undefined) { + LoginRequest.encode( + message.loginRequest, + writer.uint32(58).fork() + ).ldelim(); + } + if (message.loginResponse !== undefined) { + LoginResponse.encode( + message.loginResponse, + writer.uint32(66).fork() + ).ldelim(); + } + if (message.hash !== undefined) { + Hash.encode(message.hash, writer.uint32(74).fork()).ldelim(); + } + if (message.mouseEvent !== undefined) { + MouseEvent.encode(message.mouseEvent, writer.uint32(82).fork()).ldelim(); + } + if (message.audioFrame !== undefined) { + AudioFrame.encode(message.audioFrame, writer.uint32(90).fork()).ldelim(); + } + if (message.cursorData !== undefined) { + CursorData.encode(message.cursorData, writer.uint32(98).fork()).ldelim(); + } + if (message.cursorPosition !== undefined) { + CursorPosition.encode( + message.cursorPosition, + writer.uint32(106).fork() + ).ldelim(); + } + if (message.cursorId !== undefined) { + writer.uint32(112).uint64(message.cursorId); + } + if (message.keyEvent !== undefined) { + KeyEvent.encode(message.keyEvent, writer.uint32(122).fork()).ldelim(); + } + if (message.clipboard !== undefined) { + Clipboard.encode(message.clipboard, writer.uint32(130).fork()).ldelim(); + } + if (message.fileAction !== undefined) { + FileAction.encode(message.fileAction, writer.uint32(138).fork()).ldelim(); + } + if (message.fileResponse !== undefined) { + FileResponse.encode( + message.fileResponse, + writer.uint32(146).fork() + ).ldelim(); + } + if (message.misc !== undefined) { + Misc.encode(message.misc, writer.uint32(154).fork()).ldelim(); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): Message { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMessage(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 3: + message.signedId = SignedId.decode(reader, reader.uint32()); + break; + case 4: + message.publicKey = PublicKey.decode(reader, reader.uint32()); + break; + case 5: + message.testDelay = TestDelay.decode(reader, reader.uint32()); + break; + case 6: + message.videoFrame = VideoFrame.decode(reader, reader.uint32()); + break; + case 7: + message.loginRequest = LoginRequest.decode(reader, reader.uint32()); + break; + case 8: + message.loginResponse = LoginResponse.decode(reader, reader.uint32()); + break; + case 9: + message.hash = Hash.decode(reader, reader.uint32()); + break; + case 10: + message.mouseEvent = MouseEvent.decode(reader, reader.uint32()); + break; + case 11: + message.audioFrame = AudioFrame.decode(reader, reader.uint32()); + break; + case 12: + message.cursorData = CursorData.decode(reader, reader.uint32()); + break; + case 13: + message.cursorPosition = CursorPosition.decode( + reader, + reader.uint32() + ); + break; + case 14: + message.cursorId = longToNumber(reader.uint64() as Long); + break; + case 15: + message.keyEvent = KeyEvent.decode(reader, reader.uint32()); + break; + case 16: + message.clipboard = Clipboard.decode(reader, reader.uint32()); + break; + case 17: + message.fileAction = FileAction.decode(reader, reader.uint32()); + break; + case 18: + message.fileResponse = FileResponse.decode(reader, reader.uint32()); + break; + case 19: + message.misc = Misc.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): Message { + return { + signedId: isSet(object.signedId) + ? SignedId.fromJSON(object.signedId) + : undefined, + publicKey: isSet(object.publicKey) + ? PublicKey.fromJSON(object.publicKey) + : undefined, + testDelay: isSet(object.testDelay) + ? TestDelay.fromJSON(object.testDelay) + : undefined, + videoFrame: isSet(object.videoFrame) + ? VideoFrame.fromJSON(object.videoFrame) + : undefined, + loginRequest: isSet(object.loginRequest) + ? LoginRequest.fromJSON(object.loginRequest) + : undefined, + loginResponse: isSet(object.loginResponse) + ? LoginResponse.fromJSON(object.loginResponse) + : undefined, + hash: isSet(object.hash) ? Hash.fromJSON(object.hash) : undefined, + mouseEvent: isSet(object.mouseEvent) + ? MouseEvent.fromJSON(object.mouseEvent) + : undefined, + audioFrame: isSet(object.audioFrame) + ? AudioFrame.fromJSON(object.audioFrame) + : undefined, + cursorData: isSet(object.cursorData) + ? CursorData.fromJSON(object.cursorData) + : undefined, + cursorPosition: isSet(object.cursorPosition) + ? CursorPosition.fromJSON(object.cursorPosition) + : undefined, + cursorId: isSet(object.cursorId) ? Number(object.cursorId) : undefined, + keyEvent: isSet(object.keyEvent) + ? KeyEvent.fromJSON(object.keyEvent) + : undefined, + clipboard: isSet(object.clipboard) + ? Clipboard.fromJSON(object.clipboard) + : undefined, + fileAction: isSet(object.fileAction) + ? FileAction.fromJSON(object.fileAction) + : undefined, + fileResponse: isSet(object.fileResponse) + ? FileResponse.fromJSON(object.fileResponse) + : undefined, + misc: isSet(object.misc) ? Misc.fromJSON(object.misc) : undefined, + }; + }, + + toJSON(message: Message): unknown { + const obj: any = {}; + message.signedId !== undefined && + (obj.signedId = message.signedId + ? SignedId.toJSON(message.signedId) + : undefined); + message.publicKey !== undefined && + (obj.publicKey = message.publicKey + ? PublicKey.toJSON(message.publicKey) + : undefined); + message.testDelay !== undefined && + (obj.testDelay = message.testDelay + ? TestDelay.toJSON(message.testDelay) + : undefined); + message.videoFrame !== undefined && + (obj.videoFrame = message.videoFrame + ? VideoFrame.toJSON(message.videoFrame) + : undefined); + message.loginRequest !== undefined && + (obj.loginRequest = message.loginRequest + ? LoginRequest.toJSON(message.loginRequest) + : undefined); + message.loginResponse !== undefined && + (obj.loginResponse = message.loginResponse + ? LoginResponse.toJSON(message.loginResponse) + : undefined); + message.hash !== undefined && + (obj.hash = message.hash ? Hash.toJSON(message.hash) : undefined); + message.mouseEvent !== undefined && + (obj.mouseEvent = message.mouseEvent + ? MouseEvent.toJSON(message.mouseEvent) + : undefined); + message.audioFrame !== undefined && + (obj.audioFrame = message.audioFrame + ? AudioFrame.toJSON(message.audioFrame) + : undefined); + message.cursorData !== undefined && + (obj.cursorData = message.cursorData + ? CursorData.toJSON(message.cursorData) + : undefined); + message.cursorPosition !== undefined && + (obj.cursorPosition = message.cursorPosition + ? CursorPosition.toJSON(message.cursorPosition) + : undefined); + message.cursorId !== undefined && + (obj.cursorId = Math.round(message.cursorId)); + message.keyEvent !== undefined && + (obj.keyEvent = message.keyEvent + ? KeyEvent.toJSON(message.keyEvent) + : undefined); + message.clipboard !== undefined && + (obj.clipboard = message.clipboard + ? Clipboard.toJSON(message.clipboard) + : undefined); + message.fileAction !== undefined && + (obj.fileAction = message.fileAction + ? FileAction.toJSON(message.fileAction) + : undefined); + message.fileResponse !== undefined && + (obj.fileResponse = message.fileResponse + ? FileResponse.toJSON(message.fileResponse) + : undefined); + message.misc !== undefined && + (obj.misc = message.misc ? Misc.toJSON(message.misc) : undefined); + return obj; + }, + + fromPartial, I>>(object: I): Message { + const message = createBaseMessage(); + message.signedId = + object.signedId !== undefined && object.signedId !== null + ? SignedId.fromPartial(object.signedId) + : undefined; + message.publicKey = + object.publicKey !== undefined && object.publicKey !== null + ? PublicKey.fromPartial(object.publicKey) + : undefined; + message.testDelay = + object.testDelay !== undefined && object.testDelay !== null + ? TestDelay.fromPartial(object.testDelay) + : undefined; + message.videoFrame = + object.videoFrame !== undefined && object.videoFrame !== null + ? VideoFrame.fromPartial(object.videoFrame) + : undefined; + message.loginRequest = + object.loginRequest !== undefined && object.loginRequest !== null + ? LoginRequest.fromPartial(object.loginRequest) + : undefined; + message.loginResponse = + object.loginResponse !== undefined && object.loginResponse !== null + ? LoginResponse.fromPartial(object.loginResponse) + : undefined; + message.hash = + object.hash !== undefined && object.hash !== null + ? Hash.fromPartial(object.hash) + : undefined; + message.mouseEvent = + object.mouseEvent !== undefined && object.mouseEvent !== null + ? MouseEvent.fromPartial(object.mouseEvent) + : undefined; + message.audioFrame = + object.audioFrame !== undefined && object.audioFrame !== null + ? AudioFrame.fromPartial(object.audioFrame) + : undefined; + message.cursorData = + object.cursorData !== undefined && object.cursorData !== null + ? CursorData.fromPartial(object.cursorData) + : undefined; + message.cursorPosition = + object.cursorPosition !== undefined && object.cursorPosition !== null + ? CursorPosition.fromPartial(object.cursorPosition) + : undefined; + message.cursorId = object.cursorId ?? undefined; + message.keyEvent = + object.keyEvent !== undefined && object.keyEvent !== null + ? KeyEvent.fromPartial(object.keyEvent) + : undefined; + message.clipboard = + object.clipboard !== undefined && object.clipboard !== null + ? Clipboard.fromPartial(object.clipboard) + : undefined; + message.fileAction = + object.fileAction !== undefined && object.fileAction !== null + ? FileAction.fromPartial(object.fileAction) + : undefined; + message.fileResponse = + object.fileResponse !== undefined && object.fileResponse !== null + ? FileResponse.fromPartial(object.fileResponse) + : undefined; + message.misc = + object.misc !== undefined && object.misc !== null + ? Misc.fromPartial(object.misc) + : undefined; + return message; + }, +}; + +declare var self: any | undefined; +declare var window: any | undefined; +declare var global: any | undefined; +var globalThis: any = (() => { + if (typeof globalThis !== "undefined") return globalThis; + if (typeof self !== "undefined") return self; + if (typeof window !== "undefined") return window; + if (typeof global !== "undefined") return global; + throw "Unable to locate global object"; +})(); + +const atob: (b64: string) => string = + globalThis.atob || + ((b64) => globalThis.Buffer.from(b64, "base64").toString("binary")); +function bytesFromBase64(b64: string): Uint8Array { + const bin = atob(b64); + const arr = new Uint8Array(bin.length); + for (let i = 0; i < bin.length; ++i) { + arr[i] = bin.charCodeAt(i); + } + return arr; +} + +const btoa: (bin: string) => string = + globalThis.btoa || + ((bin) => globalThis.Buffer.from(bin, "binary").toString("base64")); +function base64FromBytes(arr: Uint8Array): string { + const bin: string[] = []; + for (const byte of arr) { + bin.push(String.fromCharCode(byte)); + } + return btoa(bin.join("")); +} + +type Builtin = + | Date + | Function + | Uint8Array + | string + | number + | boolean + | undefined; + +export type DeepPartial = T extends Builtin + ? T + : T extends Array + ? Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin + ? P + : P & { [K in keyof P]: Exact } & Record< + Exclude>, + never + >; + +function longToNumber(long: Long): number { + if (long.gt(Number.MAX_SAFE_INTEGER)) { + throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER"); + } + return long.toNumber(); +} + +// If you get a compile-error about 'Constructor and ... have no overlap', +// add '--ts_proto_opt=esModuleInterop=true' as a flag when calling 'protoc'. +if (util.Long !== Long) { + util.Long = Long as any; + configure(); +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/src/rendezvous.ts b/src/rendezvous.ts new file mode 100644 index 000000000..9af1a4a44 --- /dev/null +++ b/src/rendezvous.ts @@ -0,0 +1,2252 @@ +/* eslint-disable */ +import { util, configure, Writer, Reader } from "protobufjs/minimal"; +import * as Long from "long"; + +export const protobufPackage = "hbb"; + +export enum ConnType { + DEFAULT_CONN = 0, + FILE_TRANSFER = 1, + PORT_FORWARD = 2, + RDP = 3, + UNRECOGNIZED = -1, +} + +export function connTypeFromJSON(object: any): ConnType { + switch (object) { + case 0: + case "DEFAULT_CONN": + return ConnType.DEFAULT_CONN; + case 1: + case "FILE_TRANSFER": + return ConnType.FILE_TRANSFER; + case 2: + case "PORT_FORWARD": + return ConnType.PORT_FORWARD; + case 3: + case "RDP": + return ConnType.RDP; + case -1: + case "UNRECOGNIZED": + default: + return ConnType.UNRECOGNIZED; + } +} + +export function connTypeToJSON(object: ConnType): string { + switch (object) { + case ConnType.DEFAULT_CONN: + return "DEFAULT_CONN"; + case ConnType.FILE_TRANSFER: + return "FILE_TRANSFER"; + case ConnType.PORT_FORWARD: + return "PORT_FORWARD"; + case ConnType.RDP: + return "RDP"; + default: + return "UNKNOWN"; + } +} + +export enum NatType { + UNKNOWN_NAT = 0, + ASYMMETRIC = 1, + SYMMETRIC = 2, + UNRECOGNIZED = -1, +} + +export function natTypeFromJSON(object: any): NatType { + switch (object) { + case 0: + case "UNKNOWN_NAT": + return NatType.UNKNOWN_NAT; + case 1: + case "ASYMMETRIC": + return NatType.ASYMMETRIC; + case 2: + case "SYMMETRIC": + return NatType.SYMMETRIC; + case -1: + case "UNRECOGNIZED": + default: + return NatType.UNRECOGNIZED; + } +} + +export function natTypeToJSON(object: NatType): string { + switch (object) { + case NatType.UNKNOWN_NAT: + return "UNKNOWN_NAT"; + case NatType.ASYMMETRIC: + return "ASYMMETRIC"; + case NatType.SYMMETRIC: + return "SYMMETRIC"; + default: + return "UNKNOWN"; + } +} + +export interface RegisterPeer { + id: string; + serial: number; +} + +export interface RegisterPeerResponse { + requestPk: boolean; +} + +export interface PunchHoleRequest { + id: string; + natType: NatType; + licenceKey: string; + connType: ConnType; +} + +export interface PunchHole { + socketAddr: Uint8Array; + relayServer: string; + natType: NatType; +} + +export interface TestNatRequest { + serial: number; +} + +/** per my test, uint/int has no difference in encoding, int not good for negative, use sint for negative */ +export interface TestNatResponse { + port: number; + /** for mobile */ + cu: ConfigUpdate | undefined; +} + +export interface PunchHoleSent { + socketAddr: Uint8Array; + id: string; + relayServer: string; + natType: NatType; + version: string; +} + +export interface RegisterPk { + id: string; + uuid: Uint8Array; + pk: Uint8Array; + oldId: string; +} + +export interface RegisterPkResponse { + result: RegisterPkResponse_Result; +} + +export enum RegisterPkResponse_Result { + UNKNOWN = 0, + OK = 1, + UUID_MISMATCH = 2, + ID_EXISTS = 3, + TOO_FREQUENT = 4, + INVALID_ID_FORMAT = 5, + NOT_SUPPORT = 6, + SERVER_ERROR = 7, + UNRECOGNIZED = -1, +} + +export function registerPkResponse_ResultFromJSON( + object: any +): RegisterPkResponse_Result { + switch (object) { + case 0: + case "UNKNOWN": + return RegisterPkResponse_Result.UNKNOWN; + case 1: + case "OK": + return RegisterPkResponse_Result.OK; + case 2: + case "UUID_MISMATCH": + return RegisterPkResponse_Result.UUID_MISMATCH; + case 3: + case "ID_EXISTS": + return RegisterPkResponse_Result.ID_EXISTS; + case 4: + case "TOO_FREQUENT": + return RegisterPkResponse_Result.TOO_FREQUENT; + case 5: + case "INVALID_ID_FORMAT": + return RegisterPkResponse_Result.INVALID_ID_FORMAT; + case 6: + case "NOT_SUPPORT": + return RegisterPkResponse_Result.NOT_SUPPORT; + case 7: + case "SERVER_ERROR": + return RegisterPkResponse_Result.SERVER_ERROR; + case -1: + case "UNRECOGNIZED": + default: + return RegisterPkResponse_Result.UNRECOGNIZED; + } +} + +export function registerPkResponse_ResultToJSON( + object: RegisterPkResponse_Result +): string { + switch (object) { + case RegisterPkResponse_Result.UNKNOWN: + return "UNKNOWN"; + case RegisterPkResponse_Result.OK: + return "OK"; + case RegisterPkResponse_Result.UUID_MISMATCH: + return "UUID_MISMATCH"; + case RegisterPkResponse_Result.ID_EXISTS: + return "ID_EXISTS"; + case RegisterPkResponse_Result.TOO_FREQUENT: + return "TOO_FREQUENT"; + case RegisterPkResponse_Result.INVALID_ID_FORMAT: + return "INVALID_ID_FORMAT"; + case RegisterPkResponse_Result.NOT_SUPPORT: + return "NOT_SUPPORT"; + case RegisterPkResponse_Result.SERVER_ERROR: + return "SERVER_ERROR"; + default: + return "UNKNOWN"; + } +} + +export interface PunchHoleResponse { + socketAddr: Uint8Array; + pk: Uint8Array; + failure: PunchHoleResponse_Failure; + relayServer: string; + natType: NatType | undefined; + isLocal: boolean | undefined; + otherFailure: string; +} + +export enum PunchHoleResponse_Failure { + UNKNOWN = 0, + ID_NOT_EXIST = 1, + OFFLINE = 2, + LICENSE_MISMATCH = 3, + LICENSE_OVERUSE = 4, + UNRECOGNIZED = -1, +} + +export function punchHoleResponse_FailureFromJSON( + object: any +): PunchHoleResponse_Failure { + switch (object) { + case 0: + case "UNKNOWN": + return PunchHoleResponse_Failure.UNKNOWN; + case 1: + case "ID_NOT_EXIST": + return PunchHoleResponse_Failure.ID_NOT_EXIST; + case 2: + case "OFFLINE": + return PunchHoleResponse_Failure.OFFLINE; + case 3: + case "LICENSE_MISMATCH": + return PunchHoleResponse_Failure.LICENSE_MISMATCH; + case 4: + case "LICENSE_OVERUSE": + return PunchHoleResponse_Failure.LICENSE_OVERUSE; + case -1: + case "UNRECOGNIZED": + default: + return PunchHoleResponse_Failure.UNRECOGNIZED; + } +} + +export function punchHoleResponse_FailureToJSON( + object: PunchHoleResponse_Failure +): string { + switch (object) { + case PunchHoleResponse_Failure.UNKNOWN: + return "UNKNOWN"; + case PunchHoleResponse_Failure.ID_NOT_EXIST: + return "ID_NOT_EXIST"; + case PunchHoleResponse_Failure.OFFLINE: + return "OFFLINE"; + case PunchHoleResponse_Failure.LICENSE_MISMATCH: + return "LICENSE_MISMATCH"; + case PunchHoleResponse_Failure.LICENSE_OVERUSE: + return "LICENSE_OVERUSE"; + default: + return "UNKNOWN"; + } +} + +export interface ConfigUpdate { + serial: number; + rendezvousServers: string[]; +} + +export interface RequestRelay { + id: string; + uuid: string; + socketAddr: Uint8Array; + relayServer: string; + secure: boolean; + licenceKey: string; + connType: ConnType; +} + +export interface RelayResponse { + socketAddr: Uint8Array; + uuid: string; + relayServer: string; + id: string | undefined; + pk: Uint8Array | undefined; + refuseReason: string; + version: string; +} + +export interface SoftwareUpdate { + url: string; +} + +/** + * if in same intranet, punch hole won't work both for udp and tcp, + * even some router has below connection error if we connect itself, + * { kind: Other, error: "could not resolve to any address" }, + * so we request local address to connect. + */ +export interface FetchLocalAddr { + socketAddr: Uint8Array; + relayServer: string; +} + +export interface LocalAddr { + socketAddr: Uint8Array; + localAddr: Uint8Array; + relayServer: string; + id: string; + version: string; +} + +export interface PeerDiscovery { + cmd: string; + mac: string; + id: string; + username: string; + hostname: string; + platform: string; + misc: string; +} + +export interface RendezvousMessage { + registerPeer: RegisterPeer | undefined; + registerPeerResponse: RegisterPeerResponse | undefined; + punchHoleRequest: PunchHoleRequest | undefined; + punchHole: PunchHole | undefined; + punchHoleSent: PunchHoleSent | undefined; + punchHoleResponse: PunchHoleResponse | undefined; + fetchLocalAddr: FetchLocalAddr | undefined; + localAddr: LocalAddr | undefined; + configureUpdate: ConfigUpdate | undefined; + registerPk: RegisterPk | undefined; + registerPkResponse: RegisterPkResponse | undefined; + softwareUpdate: SoftwareUpdate | undefined; + requestRelay: RequestRelay | undefined; + relayResponse: RelayResponse | undefined; + testNatRequest: TestNatRequest | undefined; + testNatResponse: TestNatResponse | undefined; + peerDiscovery: PeerDiscovery | undefined; +} + +function createBaseRegisterPeer(): RegisterPeer { + return { id: "", serial: 0 }; +} + +export const RegisterPeer = { + encode(message: RegisterPeer, writer: Writer = Writer.create()): Writer { + if (message.id !== "") { + writer.uint32(10).string(message.id); + } + if (message.serial !== 0) { + writer.uint32(16).int32(message.serial); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): RegisterPeer { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseRegisterPeer(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = reader.string(); + break; + case 2: + message.serial = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): RegisterPeer { + return { + id: isSet(object.id) ? String(object.id) : "", + serial: isSet(object.serial) ? Number(object.serial) : 0, + }; + }, + + toJSON(message: RegisterPeer): unknown { + const obj: any = {}; + message.id !== undefined && (obj.id = message.id); + message.serial !== undefined && (obj.serial = Math.round(message.serial)); + return obj; + }, + + fromPartial, I>>( + object: I + ): RegisterPeer { + const message = createBaseRegisterPeer(); + message.id = object.id ?? ""; + message.serial = object.serial ?? 0; + return message; + }, +}; + +function createBaseRegisterPeerResponse(): RegisterPeerResponse { + return { requestPk: false }; +} + +export const RegisterPeerResponse = { + encode( + message: RegisterPeerResponse, + writer: Writer = Writer.create() + ): Writer { + if (message.requestPk === true) { + writer.uint32(16).bool(message.requestPk); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): RegisterPeerResponse { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseRegisterPeerResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 2: + message.requestPk = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): RegisterPeerResponse { + return { + requestPk: isSet(object.requestPk) ? Boolean(object.requestPk) : false, + }; + }, + + toJSON(message: RegisterPeerResponse): unknown { + const obj: any = {}; + message.requestPk !== undefined && (obj.requestPk = message.requestPk); + return obj; + }, + + fromPartial, I>>( + object: I + ): RegisterPeerResponse { + const message = createBaseRegisterPeerResponse(); + message.requestPk = object.requestPk ?? false; + return message; + }, +}; + +function createBasePunchHoleRequest(): PunchHoleRequest { + return { id: "", natType: 0, licenceKey: "", connType: 0 }; +} + +export const PunchHoleRequest = { + encode(message: PunchHoleRequest, writer: Writer = Writer.create()): Writer { + if (message.id !== "") { + writer.uint32(10).string(message.id); + } + if (message.natType !== 0) { + writer.uint32(16).int32(message.natType); + } + if (message.licenceKey !== "") { + writer.uint32(26).string(message.licenceKey); + } + if (message.connType !== 0) { + writer.uint32(32).int32(message.connType); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): PunchHoleRequest { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBasePunchHoleRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = reader.string(); + break; + case 2: + message.natType = reader.int32() as any; + break; + case 3: + message.licenceKey = reader.string(); + break; + case 4: + message.connType = reader.int32() as any; + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): PunchHoleRequest { + return { + id: isSet(object.id) ? String(object.id) : "", + natType: isSet(object.natType) ? natTypeFromJSON(object.natType) : 0, + licenceKey: isSet(object.licenceKey) ? String(object.licenceKey) : "", + connType: isSet(object.connType) ? connTypeFromJSON(object.connType) : 0, + }; + }, + + toJSON(message: PunchHoleRequest): unknown { + const obj: any = {}; + message.id !== undefined && (obj.id = message.id); + message.natType !== undefined && + (obj.natType = natTypeToJSON(message.natType)); + message.licenceKey !== undefined && (obj.licenceKey = message.licenceKey); + message.connType !== undefined && + (obj.connType = connTypeToJSON(message.connType)); + return obj; + }, + + fromPartial, I>>( + object: I + ): PunchHoleRequest { + const message = createBasePunchHoleRequest(); + message.id = object.id ?? ""; + message.natType = object.natType ?? 0; + message.licenceKey = object.licenceKey ?? ""; + message.connType = object.connType ?? 0; + return message; + }, +}; + +function createBasePunchHole(): PunchHole { + return { socketAddr: new Uint8Array(), relayServer: "", natType: 0 }; +} + +export const PunchHole = { + encode(message: PunchHole, writer: Writer = Writer.create()): Writer { + if (message.socketAddr.length !== 0) { + writer.uint32(10).bytes(message.socketAddr); + } + if (message.relayServer !== "") { + writer.uint32(18).string(message.relayServer); + } + if (message.natType !== 0) { + writer.uint32(24).int32(message.natType); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): PunchHole { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBasePunchHole(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.socketAddr = reader.bytes(); + break; + case 2: + message.relayServer = reader.string(); + break; + case 3: + message.natType = reader.int32() as any; + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): PunchHole { + return { + socketAddr: isSet(object.socketAddr) + ? bytesFromBase64(object.socketAddr) + : new Uint8Array(), + relayServer: isSet(object.relayServer) ? String(object.relayServer) : "", + natType: isSet(object.natType) ? natTypeFromJSON(object.natType) : 0, + }; + }, + + toJSON(message: PunchHole): unknown { + const obj: any = {}; + message.socketAddr !== undefined && + (obj.socketAddr = base64FromBytes( + message.socketAddr !== undefined ? message.socketAddr : new Uint8Array() + )); + message.relayServer !== undefined && + (obj.relayServer = message.relayServer); + message.natType !== undefined && + (obj.natType = natTypeToJSON(message.natType)); + return obj; + }, + + fromPartial, I>>( + object: I + ): PunchHole { + const message = createBasePunchHole(); + message.socketAddr = object.socketAddr ?? new Uint8Array(); + message.relayServer = object.relayServer ?? ""; + message.natType = object.natType ?? 0; + return message; + }, +}; + +function createBaseTestNatRequest(): TestNatRequest { + return { serial: 0 }; +} + +export const TestNatRequest = { + encode(message: TestNatRequest, writer: Writer = Writer.create()): Writer { + if (message.serial !== 0) { + writer.uint32(8).int32(message.serial); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): TestNatRequest { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTestNatRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.serial = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): TestNatRequest { + return { + serial: isSet(object.serial) ? Number(object.serial) : 0, + }; + }, + + toJSON(message: TestNatRequest): unknown { + const obj: any = {}; + message.serial !== undefined && (obj.serial = Math.round(message.serial)); + return obj; + }, + + fromPartial, I>>( + object: I + ): TestNatRequest { + const message = createBaseTestNatRequest(); + message.serial = object.serial ?? 0; + return message; + }, +}; + +function createBaseTestNatResponse(): TestNatResponse { + return { port: 0, cu: undefined }; +} + +export const TestNatResponse = { + encode(message: TestNatResponse, writer: Writer = Writer.create()): Writer { + if (message.port !== 0) { + writer.uint32(8).int32(message.port); + } + if (message.cu !== undefined) { + ConfigUpdate.encode(message.cu, writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): TestNatResponse { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTestNatResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.port = reader.int32(); + break; + case 2: + message.cu = ConfigUpdate.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): TestNatResponse { + return { + port: isSet(object.port) ? Number(object.port) : 0, + cu: isSet(object.cu) ? ConfigUpdate.fromJSON(object.cu) : undefined, + }; + }, + + toJSON(message: TestNatResponse): unknown { + const obj: any = {}; + message.port !== undefined && (obj.port = Math.round(message.port)); + message.cu !== undefined && + (obj.cu = message.cu ? ConfigUpdate.toJSON(message.cu) : undefined); + return obj; + }, + + fromPartial, I>>( + object: I + ): TestNatResponse { + const message = createBaseTestNatResponse(); + message.port = object.port ?? 0; + message.cu = + object.cu !== undefined && object.cu !== null + ? ConfigUpdate.fromPartial(object.cu) + : undefined; + return message; + }, +}; + +function createBasePunchHoleSent(): PunchHoleSent { + return { + socketAddr: new Uint8Array(), + id: "", + relayServer: "", + natType: 0, + version: "", + }; +} + +export const PunchHoleSent = { + encode(message: PunchHoleSent, writer: Writer = Writer.create()): Writer { + if (message.socketAddr.length !== 0) { + writer.uint32(10).bytes(message.socketAddr); + } + if (message.id !== "") { + writer.uint32(18).string(message.id); + } + if (message.relayServer !== "") { + writer.uint32(26).string(message.relayServer); + } + if (message.natType !== 0) { + writer.uint32(32).int32(message.natType); + } + if (message.version !== "") { + writer.uint32(42).string(message.version); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): PunchHoleSent { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBasePunchHoleSent(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.socketAddr = reader.bytes(); + break; + case 2: + message.id = reader.string(); + break; + case 3: + message.relayServer = reader.string(); + break; + case 4: + message.natType = reader.int32() as any; + break; + case 5: + message.version = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): PunchHoleSent { + return { + socketAddr: isSet(object.socketAddr) + ? bytesFromBase64(object.socketAddr) + : new Uint8Array(), + id: isSet(object.id) ? String(object.id) : "", + relayServer: isSet(object.relayServer) ? String(object.relayServer) : "", + natType: isSet(object.natType) ? natTypeFromJSON(object.natType) : 0, + version: isSet(object.version) ? String(object.version) : "", + }; + }, + + toJSON(message: PunchHoleSent): unknown { + const obj: any = {}; + message.socketAddr !== undefined && + (obj.socketAddr = base64FromBytes( + message.socketAddr !== undefined ? message.socketAddr : new Uint8Array() + )); + message.id !== undefined && (obj.id = message.id); + message.relayServer !== undefined && + (obj.relayServer = message.relayServer); + message.natType !== undefined && + (obj.natType = natTypeToJSON(message.natType)); + message.version !== undefined && (obj.version = message.version); + return obj; + }, + + fromPartial, I>>( + object: I + ): PunchHoleSent { + const message = createBasePunchHoleSent(); + message.socketAddr = object.socketAddr ?? new Uint8Array(); + message.id = object.id ?? ""; + message.relayServer = object.relayServer ?? ""; + message.natType = object.natType ?? 0; + message.version = object.version ?? ""; + return message; + }, +}; + +function createBaseRegisterPk(): RegisterPk { + return { id: "", uuid: new Uint8Array(), pk: new Uint8Array(), oldId: "" }; +} + +export const RegisterPk = { + encode(message: RegisterPk, writer: Writer = Writer.create()): Writer { + if (message.id !== "") { + writer.uint32(10).string(message.id); + } + if (message.uuid.length !== 0) { + writer.uint32(18).bytes(message.uuid); + } + if (message.pk.length !== 0) { + writer.uint32(26).bytes(message.pk); + } + if (message.oldId !== "") { + writer.uint32(34).string(message.oldId); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): RegisterPk { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseRegisterPk(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = reader.string(); + break; + case 2: + message.uuid = reader.bytes(); + break; + case 3: + message.pk = reader.bytes(); + break; + case 4: + message.oldId = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): RegisterPk { + return { + id: isSet(object.id) ? String(object.id) : "", + uuid: isSet(object.uuid) + ? bytesFromBase64(object.uuid) + : new Uint8Array(), + pk: isSet(object.pk) ? bytesFromBase64(object.pk) : new Uint8Array(), + oldId: isSet(object.oldId) ? String(object.oldId) : "", + }; + }, + + toJSON(message: RegisterPk): unknown { + const obj: any = {}; + message.id !== undefined && (obj.id = message.id); + message.uuid !== undefined && + (obj.uuid = base64FromBytes( + message.uuid !== undefined ? message.uuid : new Uint8Array() + )); + message.pk !== undefined && + (obj.pk = base64FromBytes( + message.pk !== undefined ? message.pk : new Uint8Array() + )); + message.oldId !== undefined && (obj.oldId = message.oldId); + return obj; + }, + + fromPartial, I>>( + object: I + ): RegisterPk { + const message = createBaseRegisterPk(); + message.id = object.id ?? ""; + message.uuid = object.uuid ?? new Uint8Array(); + message.pk = object.pk ?? new Uint8Array(); + message.oldId = object.oldId ?? ""; + return message; + }, +}; + +function createBaseRegisterPkResponse(): RegisterPkResponse { + return { result: 0 }; +} + +export const RegisterPkResponse = { + encode( + message: RegisterPkResponse, + writer: Writer = Writer.create() + ): Writer { + if (message.result !== 0) { + writer.uint32(8).int32(message.result); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): RegisterPkResponse { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseRegisterPkResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.result = reader.int32() as any; + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): RegisterPkResponse { + return { + result: isSet(object.result) + ? registerPkResponse_ResultFromJSON(object.result) + : 0, + }; + }, + + toJSON(message: RegisterPkResponse): unknown { + const obj: any = {}; + message.result !== undefined && + (obj.result = registerPkResponse_ResultToJSON(message.result)); + return obj; + }, + + fromPartial, I>>( + object: I + ): RegisterPkResponse { + const message = createBaseRegisterPkResponse(); + message.result = object.result ?? 0; + return message; + }, +}; + +function createBasePunchHoleResponse(): PunchHoleResponse { + return { + socketAddr: new Uint8Array(), + pk: new Uint8Array(), + failure: 0, + relayServer: "", + natType: undefined, + isLocal: undefined, + otherFailure: "", + }; +} + +export const PunchHoleResponse = { + encode(message: PunchHoleResponse, writer: Writer = Writer.create()): Writer { + if (message.socketAddr.length !== 0) { + writer.uint32(10).bytes(message.socketAddr); + } + if (message.pk.length !== 0) { + writer.uint32(18).bytes(message.pk); + } + if (message.failure !== 0) { + writer.uint32(24).int32(message.failure); + } + if (message.relayServer !== "") { + writer.uint32(34).string(message.relayServer); + } + if (message.natType !== undefined) { + writer.uint32(40).int32(message.natType); + } + if (message.isLocal !== undefined) { + writer.uint32(48).bool(message.isLocal); + } + if (message.otherFailure !== "") { + writer.uint32(58).string(message.otherFailure); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): PunchHoleResponse { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBasePunchHoleResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.socketAddr = reader.bytes(); + break; + case 2: + message.pk = reader.bytes(); + break; + case 3: + message.failure = reader.int32() as any; + break; + case 4: + message.relayServer = reader.string(); + break; + case 5: + message.natType = reader.int32() as any; + break; + case 6: + message.isLocal = reader.bool(); + break; + case 7: + message.otherFailure = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): PunchHoleResponse { + return { + socketAddr: isSet(object.socketAddr) + ? bytesFromBase64(object.socketAddr) + : new Uint8Array(), + pk: isSet(object.pk) ? bytesFromBase64(object.pk) : new Uint8Array(), + failure: isSet(object.failure) + ? punchHoleResponse_FailureFromJSON(object.failure) + : 0, + relayServer: isSet(object.relayServer) ? String(object.relayServer) : "", + natType: isSet(object.natType) + ? natTypeFromJSON(object.natType) + : undefined, + isLocal: isSet(object.isLocal) ? Boolean(object.isLocal) : undefined, + otherFailure: isSet(object.otherFailure) + ? String(object.otherFailure) + : "", + }; + }, + + toJSON(message: PunchHoleResponse): unknown { + const obj: any = {}; + message.socketAddr !== undefined && + (obj.socketAddr = base64FromBytes( + message.socketAddr !== undefined ? message.socketAddr : new Uint8Array() + )); + message.pk !== undefined && + (obj.pk = base64FromBytes( + message.pk !== undefined ? message.pk : new Uint8Array() + )); + message.failure !== undefined && + (obj.failure = punchHoleResponse_FailureToJSON(message.failure)); + message.relayServer !== undefined && + (obj.relayServer = message.relayServer); + message.natType !== undefined && + (obj.natType = + message.natType !== undefined + ? natTypeToJSON(message.natType) + : undefined); + message.isLocal !== undefined && (obj.isLocal = message.isLocal); + message.otherFailure !== undefined && + (obj.otherFailure = message.otherFailure); + return obj; + }, + + fromPartial, I>>( + object: I + ): PunchHoleResponse { + const message = createBasePunchHoleResponse(); + message.socketAddr = object.socketAddr ?? new Uint8Array(); + message.pk = object.pk ?? new Uint8Array(); + message.failure = object.failure ?? 0; + message.relayServer = object.relayServer ?? ""; + message.natType = object.natType ?? undefined; + message.isLocal = object.isLocal ?? undefined; + message.otherFailure = object.otherFailure ?? ""; + return message; + }, +}; + +function createBaseConfigUpdate(): ConfigUpdate { + return { serial: 0, rendezvousServers: [] }; +} + +export const ConfigUpdate = { + encode(message: ConfigUpdate, writer: Writer = Writer.create()): Writer { + if (message.serial !== 0) { + writer.uint32(8).int32(message.serial); + } + for (const v of message.rendezvousServers) { + writer.uint32(18).string(v!); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): ConfigUpdate { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseConfigUpdate(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.serial = reader.int32(); + break; + case 2: + message.rendezvousServers.push(reader.string()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): ConfigUpdate { + return { + serial: isSet(object.serial) ? Number(object.serial) : 0, + rendezvousServers: Array.isArray(object?.rendezvousServers) + ? object.rendezvousServers.map((e: any) => String(e)) + : [], + }; + }, + + toJSON(message: ConfigUpdate): unknown { + const obj: any = {}; + message.serial !== undefined && (obj.serial = Math.round(message.serial)); + if (message.rendezvousServers) { + obj.rendezvousServers = message.rendezvousServers.map((e) => e); + } else { + obj.rendezvousServers = []; + } + return obj; + }, + + fromPartial, I>>( + object: I + ): ConfigUpdate { + const message = createBaseConfigUpdate(); + message.serial = object.serial ?? 0; + message.rendezvousServers = object.rendezvousServers?.map((e) => e) || []; + return message; + }, +}; + +function createBaseRequestRelay(): RequestRelay { + return { + id: "", + uuid: "", + socketAddr: new Uint8Array(), + relayServer: "", + secure: false, + licenceKey: "", + connType: 0, + }; +} + +export const RequestRelay = { + encode(message: RequestRelay, writer: Writer = Writer.create()): Writer { + if (message.id !== "") { + writer.uint32(10).string(message.id); + } + if (message.uuid !== "") { + writer.uint32(18).string(message.uuid); + } + if (message.socketAddr.length !== 0) { + writer.uint32(26).bytes(message.socketAddr); + } + if (message.relayServer !== "") { + writer.uint32(34).string(message.relayServer); + } + if (message.secure === true) { + writer.uint32(40).bool(message.secure); + } + if (message.licenceKey !== "") { + writer.uint32(50).string(message.licenceKey); + } + if (message.connType !== 0) { + writer.uint32(56).int32(message.connType); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): RequestRelay { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseRequestRelay(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.id = reader.string(); + break; + case 2: + message.uuid = reader.string(); + break; + case 3: + message.socketAddr = reader.bytes(); + break; + case 4: + message.relayServer = reader.string(); + break; + case 5: + message.secure = reader.bool(); + break; + case 6: + message.licenceKey = reader.string(); + break; + case 7: + message.connType = reader.int32() as any; + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): RequestRelay { + return { + id: isSet(object.id) ? String(object.id) : "", + uuid: isSet(object.uuid) ? String(object.uuid) : "", + socketAddr: isSet(object.socketAddr) + ? bytesFromBase64(object.socketAddr) + : new Uint8Array(), + relayServer: isSet(object.relayServer) ? String(object.relayServer) : "", + secure: isSet(object.secure) ? Boolean(object.secure) : false, + licenceKey: isSet(object.licenceKey) ? String(object.licenceKey) : "", + connType: isSet(object.connType) ? connTypeFromJSON(object.connType) : 0, + }; + }, + + toJSON(message: RequestRelay): unknown { + const obj: any = {}; + message.id !== undefined && (obj.id = message.id); + message.uuid !== undefined && (obj.uuid = message.uuid); + message.socketAddr !== undefined && + (obj.socketAddr = base64FromBytes( + message.socketAddr !== undefined ? message.socketAddr : new Uint8Array() + )); + message.relayServer !== undefined && + (obj.relayServer = message.relayServer); + message.secure !== undefined && (obj.secure = message.secure); + message.licenceKey !== undefined && (obj.licenceKey = message.licenceKey); + message.connType !== undefined && + (obj.connType = connTypeToJSON(message.connType)); + return obj; + }, + + fromPartial, I>>( + object: I + ): RequestRelay { + const message = createBaseRequestRelay(); + message.id = object.id ?? ""; + message.uuid = object.uuid ?? ""; + message.socketAddr = object.socketAddr ?? new Uint8Array(); + message.relayServer = object.relayServer ?? ""; + message.secure = object.secure ?? false; + message.licenceKey = object.licenceKey ?? ""; + message.connType = object.connType ?? 0; + return message; + }, +}; + +function createBaseRelayResponse(): RelayResponse { + return { + socketAddr: new Uint8Array(), + uuid: "", + relayServer: "", + id: undefined, + pk: undefined, + refuseReason: "", + version: "", + }; +} + +export const RelayResponse = { + encode(message: RelayResponse, writer: Writer = Writer.create()): Writer { + if (message.socketAddr.length !== 0) { + writer.uint32(10).bytes(message.socketAddr); + } + if (message.uuid !== "") { + writer.uint32(18).string(message.uuid); + } + if (message.relayServer !== "") { + writer.uint32(26).string(message.relayServer); + } + if (message.id !== undefined) { + writer.uint32(34).string(message.id); + } + if (message.pk !== undefined) { + writer.uint32(42).bytes(message.pk); + } + if (message.refuseReason !== "") { + writer.uint32(50).string(message.refuseReason); + } + if (message.version !== "") { + writer.uint32(58).string(message.version); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): RelayResponse { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseRelayResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.socketAddr = reader.bytes(); + break; + case 2: + message.uuid = reader.string(); + break; + case 3: + message.relayServer = reader.string(); + break; + case 4: + message.id = reader.string(); + break; + case 5: + message.pk = reader.bytes(); + break; + case 6: + message.refuseReason = reader.string(); + break; + case 7: + message.version = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): RelayResponse { + return { + socketAddr: isSet(object.socketAddr) + ? bytesFromBase64(object.socketAddr) + : new Uint8Array(), + uuid: isSet(object.uuid) ? String(object.uuid) : "", + relayServer: isSet(object.relayServer) ? String(object.relayServer) : "", + id: isSet(object.id) ? String(object.id) : undefined, + pk: isSet(object.pk) ? bytesFromBase64(object.pk) : undefined, + refuseReason: isSet(object.refuseReason) + ? String(object.refuseReason) + : "", + version: isSet(object.version) ? String(object.version) : "", + }; + }, + + toJSON(message: RelayResponse): unknown { + const obj: any = {}; + message.socketAddr !== undefined && + (obj.socketAddr = base64FromBytes( + message.socketAddr !== undefined ? message.socketAddr : new Uint8Array() + )); + message.uuid !== undefined && (obj.uuid = message.uuid); + message.relayServer !== undefined && + (obj.relayServer = message.relayServer); + message.id !== undefined && (obj.id = message.id); + message.pk !== undefined && + (obj.pk = + message.pk !== undefined ? base64FromBytes(message.pk) : undefined); + message.refuseReason !== undefined && + (obj.refuseReason = message.refuseReason); + message.version !== undefined && (obj.version = message.version); + return obj; + }, + + fromPartial, I>>( + object: I + ): RelayResponse { + const message = createBaseRelayResponse(); + message.socketAddr = object.socketAddr ?? new Uint8Array(); + message.uuid = object.uuid ?? ""; + message.relayServer = object.relayServer ?? ""; + message.id = object.id ?? undefined; + message.pk = object.pk ?? undefined; + message.refuseReason = object.refuseReason ?? ""; + message.version = object.version ?? ""; + return message; + }, +}; + +function createBaseSoftwareUpdate(): SoftwareUpdate { + return { url: "" }; +} + +export const SoftwareUpdate = { + encode(message: SoftwareUpdate, writer: Writer = Writer.create()): Writer { + if (message.url !== "") { + writer.uint32(10).string(message.url); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): SoftwareUpdate { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSoftwareUpdate(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.url = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): SoftwareUpdate { + return { + url: isSet(object.url) ? String(object.url) : "", + }; + }, + + toJSON(message: SoftwareUpdate): unknown { + const obj: any = {}; + message.url !== undefined && (obj.url = message.url); + return obj; + }, + + fromPartial, I>>( + object: I + ): SoftwareUpdate { + const message = createBaseSoftwareUpdate(); + message.url = object.url ?? ""; + return message; + }, +}; + +function createBaseFetchLocalAddr(): FetchLocalAddr { + return { socketAddr: new Uint8Array(), relayServer: "" }; +} + +export const FetchLocalAddr = { + encode(message: FetchLocalAddr, writer: Writer = Writer.create()): Writer { + if (message.socketAddr.length !== 0) { + writer.uint32(10).bytes(message.socketAddr); + } + if (message.relayServer !== "") { + writer.uint32(18).string(message.relayServer); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): FetchLocalAddr { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFetchLocalAddr(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.socketAddr = reader.bytes(); + break; + case 2: + message.relayServer = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): FetchLocalAddr { + return { + socketAddr: isSet(object.socketAddr) + ? bytesFromBase64(object.socketAddr) + : new Uint8Array(), + relayServer: isSet(object.relayServer) ? String(object.relayServer) : "", + }; + }, + + toJSON(message: FetchLocalAddr): unknown { + const obj: any = {}; + message.socketAddr !== undefined && + (obj.socketAddr = base64FromBytes( + message.socketAddr !== undefined ? message.socketAddr : new Uint8Array() + )); + message.relayServer !== undefined && + (obj.relayServer = message.relayServer); + return obj; + }, + + fromPartial, I>>( + object: I + ): FetchLocalAddr { + const message = createBaseFetchLocalAddr(); + message.socketAddr = object.socketAddr ?? new Uint8Array(); + message.relayServer = object.relayServer ?? ""; + return message; + }, +}; + +function createBaseLocalAddr(): LocalAddr { + return { + socketAddr: new Uint8Array(), + localAddr: new Uint8Array(), + relayServer: "", + id: "", + version: "", + }; +} + +export const LocalAddr = { + encode(message: LocalAddr, writer: Writer = Writer.create()): Writer { + if (message.socketAddr.length !== 0) { + writer.uint32(10).bytes(message.socketAddr); + } + if (message.localAddr.length !== 0) { + writer.uint32(18).bytes(message.localAddr); + } + if (message.relayServer !== "") { + writer.uint32(26).string(message.relayServer); + } + if (message.id !== "") { + writer.uint32(34).string(message.id); + } + if (message.version !== "") { + writer.uint32(42).string(message.version); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): LocalAddr { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseLocalAddr(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.socketAddr = reader.bytes(); + break; + case 2: + message.localAddr = reader.bytes(); + break; + case 3: + message.relayServer = reader.string(); + break; + case 4: + message.id = reader.string(); + break; + case 5: + message.version = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): LocalAddr { + return { + socketAddr: isSet(object.socketAddr) + ? bytesFromBase64(object.socketAddr) + : new Uint8Array(), + localAddr: isSet(object.localAddr) + ? bytesFromBase64(object.localAddr) + : new Uint8Array(), + relayServer: isSet(object.relayServer) ? String(object.relayServer) : "", + id: isSet(object.id) ? String(object.id) : "", + version: isSet(object.version) ? String(object.version) : "", + }; + }, + + toJSON(message: LocalAddr): unknown { + const obj: any = {}; + message.socketAddr !== undefined && + (obj.socketAddr = base64FromBytes( + message.socketAddr !== undefined ? message.socketAddr : new Uint8Array() + )); + message.localAddr !== undefined && + (obj.localAddr = base64FromBytes( + message.localAddr !== undefined ? message.localAddr : new Uint8Array() + )); + message.relayServer !== undefined && + (obj.relayServer = message.relayServer); + message.id !== undefined && (obj.id = message.id); + message.version !== undefined && (obj.version = message.version); + return obj; + }, + + fromPartial, I>>( + object: I + ): LocalAddr { + const message = createBaseLocalAddr(); + message.socketAddr = object.socketAddr ?? new Uint8Array(); + message.localAddr = object.localAddr ?? new Uint8Array(); + message.relayServer = object.relayServer ?? ""; + message.id = object.id ?? ""; + message.version = object.version ?? ""; + return message; + }, +}; + +function createBasePeerDiscovery(): PeerDiscovery { + return { + cmd: "", + mac: "", + id: "", + username: "", + hostname: "", + platform: "", + misc: "", + }; +} + +export const PeerDiscovery = { + encode(message: PeerDiscovery, writer: Writer = Writer.create()): Writer { + if (message.cmd !== "") { + writer.uint32(10).string(message.cmd); + } + if (message.mac !== "") { + writer.uint32(18).string(message.mac); + } + if (message.id !== "") { + writer.uint32(26).string(message.id); + } + if (message.username !== "") { + writer.uint32(34).string(message.username); + } + if (message.hostname !== "") { + writer.uint32(42).string(message.hostname); + } + if (message.platform !== "") { + writer.uint32(50).string(message.platform); + } + if (message.misc !== "") { + writer.uint32(58).string(message.misc); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): PeerDiscovery { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBasePeerDiscovery(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.cmd = reader.string(); + break; + case 2: + message.mac = reader.string(); + break; + case 3: + message.id = reader.string(); + break; + case 4: + message.username = reader.string(); + break; + case 5: + message.hostname = reader.string(); + break; + case 6: + message.platform = reader.string(); + break; + case 7: + message.misc = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): PeerDiscovery { + return { + cmd: isSet(object.cmd) ? String(object.cmd) : "", + mac: isSet(object.mac) ? String(object.mac) : "", + id: isSet(object.id) ? String(object.id) : "", + username: isSet(object.username) ? String(object.username) : "", + hostname: isSet(object.hostname) ? String(object.hostname) : "", + platform: isSet(object.platform) ? String(object.platform) : "", + misc: isSet(object.misc) ? String(object.misc) : "", + }; + }, + + toJSON(message: PeerDiscovery): unknown { + const obj: any = {}; + message.cmd !== undefined && (obj.cmd = message.cmd); + message.mac !== undefined && (obj.mac = message.mac); + message.id !== undefined && (obj.id = message.id); + message.username !== undefined && (obj.username = message.username); + message.hostname !== undefined && (obj.hostname = message.hostname); + message.platform !== undefined && (obj.platform = message.platform); + message.misc !== undefined && (obj.misc = message.misc); + return obj; + }, + + fromPartial, I>>( + object: I + ): PeerDiscovery { + const message = createBasePeerDiscovery(); + message.cmd = object.cmd ?? ""; + message.mac = object.mac ?? ""; + message.id = object.id ?? ""; + message.username = object.username ?? ""; + message.hostname = object.hostname ?? ""; + message.platform = object.platform ?? ""; + message.misc = object.misc ?? ""; + return message; + }, +}; + +function createBaseRendezvousMessage(): RendezvousMessage { + return { + registerPeer: undefined, + registerPeerResponse: undefined, + punchHoleRequest: undefined, + punchHole: undefined, + punchHoleSent: undefined, + punchHoleResponse: undefined, + fetchLocalAddr: undefined, + localAddr: undefined, + configureUpdate: undefined, + registerPk: undefined, + registerPkResponse: undefined, + softwareUpdate: undefined, + requestRelay: undefined, + relayResponse: undefined, + testNatRequest: undefined, + testNatResponse: undefined, + peerDiscovery: undefined, + }; +} + +export const RendezvousMessage = { + encode(message: RendezvousMessage, writer: Writer = Writer.create()): Writer { + if (message.registerPeer !== undefined) { + RegisterPeer.encode( + message.registerPeer, + writer.uint32(50).fork() + ).ldelim(); + } + if (message.registerPeerResponse !== undefined) { + RegisterPeerResponse.encode( + message.registerPeerResponse, + writer.uint32(58).fork() + ).ldelim(); + } + if (message.punchHoleRequest !== undefined) { + PunchHoleRequest.encode( + message.punchHoleRequest, + writer.uint32(66).fork() + ).ldelim(); + } + if (message.punchHole !== undefined) { + PunchHole.encode(message.punchHole, writer.uint32(74).fork()).ldelim(); + } + if (message.punchHoleSent !== undefined) { + PunchHoleSent.encode( + message.punchHoleSent, + writer.uint32(82).fork() + ).ldelim(); + } + if (message.punchHoleResponse !== undefined) { + PunchHoleResponse.encode( + message.punchHoleResponse, + writer.uint32(90).fork() + ).ldelim(); + } + if (message.fetchLocalAddr !== undefined) { + FetchLocalAddr.encode( + message.fetchLocalAddr, + writer.uint32(98).fork() + ).ldelim(); + } + if (message.localAddr !== undefined) { + LocalAddr.encode(message.localAddr, writer.uint32(106).fork()).ldelim(); + } + if (message.configureUpdate !== undefined) { + ConfigUpdate.encode( + message.configureUpdate, + writer.uint32(114).fork() + ).ldelim(); + } + if (message.registerPk !== undefined) { + RegisterPk.encode(message.registerPk, writer.uint32(122).fork()).ldelim(); + } + if (message.registerPkResponse !== undefined) { + RegisterPkResponse.encode( + message.registerPkResponse, + writer.uint32(130).fork() + ).ldelim(); + } + if (message.softwareUpdate !== undefined) { + SoftwareUpdate.encode( + message.softwareUpdate, + writer.uint32(138).fork() + ).ldelim(); + } + if (message.requestRelay !== undefined) { + RequestRelay.encode( + message.requestRelay, + writer.uint32(146).fork() + ).ldelim(); + } + if (message.relayResponse !== undefined) { + RelayResponse.encode( + message.relayResponse, + writer.uint32(154).fork() + ).ldelim(); + } + if (message.testNatRequest !== undefined) { + TestNatRequest.encode( + message.testNatRequest, + writer.uint32(162).fork() + ).ldelim(); + } + if (message.testNatResponse !== undefined) { + TestNatResponse.encode( + message.testNatResponse, + writer.uint32(170).fork() + ).ldelim(); + } + if (message.peerDiscovery !== undefined) { + PeerDiscovery.encode( + message.peerDiscovery, + writer.uint32(178).fork() + ).ldelim(); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): RendezvousMessage { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseRendezvousMessage(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 6: + message.registerPeer = RegisterPeer.decode(reader, reader.uint32()); + break; + case 7: + message.registerPeerResponse = RegisterPeerResponse.decode( + reader, + reader.uint32() + ); + break; + case 8: + message.punchHoleRequest = PunchHoleRequest.decode( + reader, + reader.uint32() + ); + break; + case 9: + message.punchHole = PunchHole.decode(reader, reader.uint32()); + break; + case 10: + message.punchHoleSent = PunchHoleSent.decode(reader, reader.uint32()); + break; + case 11: + message.punchHoleResponse = PunchHoleResponse.decode( + reader, + reader.uint32() + ); + break; + case 12: + message.fetchLocalAddr = FetchLocalAddr.decode( + reader, + reader.uint32() + ); + break; + case 13: + message.localAddr = LocalAddr.decode(reader, reader.uint32()); + break; + case 14: + message.configureUpdate = ConfigUpdate.decode( + reader, + reader.uint32() + ); + break; + case 15: + message.registerPk = RegisterPk.decode(reader, reader.uint32()); + break; + case 16: + message.registerPkResponse = RegisterPkResponse.decode( + reader, + reader.uint32() + ); + break; + case 17: + message.softwareUpdate = SoftwareUpdate.decode( + reader, + reader.uint32() + ); + break; + case 18: + message.requestRelay = RequestRelay.decode(reader, reader.uint32()); + break; + case 19: + message.relayResponse = RelayResponse.decode(reader, reader.uint32()); + break; + case 20: + message.testNatRequest = TestNatRequest.decode( + reader, + reader.uint32() + ); + break; + case 21: + message.testNatResponse = TestNatResponse.decode( + reader, + reader.uint32() + ); + break; + case 22: + message.peerDiscovery = PeerDiscovery.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): RendezvousMessage { + return { + registerPeer: isSet(object.registerPeer) + ? RegisterPeer.fromJSON(object.registerPeer) + : undefined, + registerPeerResponse: isSet(object.registerPeerResponse) + ? RegisterPeerResponse.fromJSON(object.registerPeerResponse) + : undefined, + punchHoleRequest: isSet(object.punchHoleRequest) + ? PunchHoleRequest.fromJSON(object.punchHoleRequest) + : undefined, + punchHole: isSet(object.punchHole) + ? PunchHole.fromJSON(object.punchHole) + : undefined, + punchHoleSent: isSet(object.punchHoleSent) + ? PunchHoleSent.fromJSON(object.punchHoleSent) + : undefined, + punchHoleResponse: isSet(object.punchHoleResponse) + ? PunchHoleResponse.fromJSON(object.punchHoleResponse) + : undefined, + fetchLocalAddr: isSet(object.fetchLocalAddr) + ? FetchLocalAddr.fromJSON(object.fetchLocalAddr) + : undefined, + localAddr: isSet(object.localAddr) + ? LocalAddr.fromJSON(object.localAddr) + : undefined, + configureUpdate: isSet(object.configureUpdate) + ? ConfigUpdate.fromJSON(object.configureUpdate) + : undefined, + registerPk: isSet(object.registerPk) + ? RegisterPk.fromJSON(object.registerPk) + : undefined, + registerPkResponse: isSet(object.registerPkResponse) + ? RegisterPkResponse.fromJSON(object.registerPkResponse) + : undefined, + softwareUpdate: isSet(object.softwareUpdate) + ? SoftwareUpdate.fromJSON(object.softwareUpdate) + : undefined, + requestRelay: isSet(object.requestRelay) + ? RequestRelay.fromJSON(object.requestRelay) + : undefined, + relayResponse: isSet(object.relayResponse) + ? RelayResponse.fromJSON(object.relayResponse) + : undefined, + testNatRequest: isSet(object.testNatRequest) + ? TestNatRequest.fromJSON(object.testNatRequest) + : undefined, + testNatResponse: isSet(object.testNatResponse) + ? TestNatResponse.fromJSON(object.testNatResponse) + : undefined, + peerDiscovery: isSet(object.peerDiscovery) + ? PeerDiscovery.fromJSON(object.peerDiscovery) + : undefined, + }; + }, + + toJSON(message: RendezvousMessage): unknown { + const obj: any = {}; + message.registerPeer !== undefined && + (obj.registerPeer = message.registerPeer + ? RegisterPeer.toJSON(message.registerPeer) + : undefined); + message.registerPeerResponse !== undefined && + (obj.registerPeerResponse = message.registerPeerResponse + ? RegisterPeerResponse.toJSON(message.registerPeerResponse) + : undefined); + message.punchHoleRequest !== undefined && + (obj.punchHoleRequest = message.punchHoleRequest + ? PunchHoleRequest.toJSON(message.punchHoleRequest) + : undefined); + message.punchHole !== undefined && + (obj.punchHole = message.punchHole + ? PunchHole.toJSON(message.punchHole) + : undefined); + message.punchHoleSent !== undefined && + (obj.punchHoleSent = message.punchHoleSent + ? PunchHoleSent.toJSON(message.punchHoleSent) + : undefined); + message.punchHoleResponse !== undefined && + (obj.punchHoleResponse = message.punchHoleResponse + ? PunchHoleResponse.toJSON(message.punchHoleResponse) + : undefined); + message.fetchLocalAddr !== undefined && + (obj.fetchLocalAddr = message.fetchLocalAddr + ? FetchLocalAddr.toJSON(message.fetchLocalAddr) + : undefined); + message.localAddr !== undefined && + (obj.localAddr = message.localAddr + ? LocalAddr.toJSON(message.localAddr) + : undefined); + message.configureUpdate !== undefined && + (obj.configureUpdate = message.configureUpdate + ? ConfigUpdate.toJSON(message.configureUpdate) + : undefined); + message.registerPk !== undefined && + (obj.registerPk = message.registerPk + ? RegisterPk.toJSON(message.registerPk) + : undefined); + message.registerPkResponse !== undefined && + (obj.registerPkResponse = message.registerPkResponse + ? RegisterPkResponse.toJSON(message.registerPkResponse) + : undefined); + message.softwareUpdate !== undefined && + (obj.softwareUpdate = message.softwareUpdate + ? SoftwareUpdate.toJSON(message.softwareUpdate) + : undefined); + message.requestRelay !== undefined && + (obj.requestRelay = message.requestRelay + ? RequestRelay.toJSON(message.requestRelay) + : undefined); + message.relayResponse !== undefined && + (obj.relayResponse = message.relayResponse + ? RelayResponse.toJSON(message.relayResponse) + : undefined); + message.testNatRequest !== undefined && + (obj.testNatRequest = message.testNatRequest + ? TestNatRequest.toJSON(message.testNatRequest) + : undefined); + message.testNatResponse !== undefined && + (obj.testNatResponse = message.testNatResponse + ? TestNatResponse.toJSON(message.testNatResponse) + : undefined); + message.peerDiscovery !== undefined && + (obj.peerDiscovery = message.peerDiscovery + ? PeerDiscovery.toJSON(message.peerDiscovery) + : undefined); + return obj; + }, + + fromPartial, I>>( + object: I + ): RendezvousMessage { + const message = createBaseRendezvousMessage(); + message.registerPeer = + object.registerPeer !== undefined && object.registerPeer !== null + ? RegisterPeer.fromPartial(object.registerPeer) + : undefined; + message.registerPeerResponse = + object.registerPeerResponse !== undefined && + object.registerPeerResponse !== null + ? RegisterPeerResponse.fromPartial(object.registerPeerResponse) + : undefined; + message.punchHoleRequest = + object.punchHoleRequest !== undefined && object.punchHoleRequest !== null + ? PunchHoleRequest.fromPartial(object.punchHoleRequest) + : undefined; + message.punchHole = + object.punchHole !== undefined && object.punchHole !== null + ? PunchHole.fromPartial(object.punchHole) + : undefined; + message.punchHoleSent = + object.punchHoleSent !== undefined && object.punchHoleSent !== null + ? PunchHoleSent.fromPartial(object.punchHoleSent) + : undefined; + message.punchHoleResponse = + object.punchHoleResponse !== undefined && + object.punchHoleResponse !== null + ? PunchHoleResponse.fromPartial(object.punchHoleResponse) + : undefined; + message.fetchLocalAddr = + object.fetchLocalAddr !== undefined && object.fetchLocalAddr !== null + ? FetchLocalAddr.fromPartial(object.fetchLocalAddr) + : undefined; + message.localAddr = + object.localAddr !== undefined && object.localAddr !== null + ? LocalAddr.fromPartial(object.localAddr) + : undefined; + message.configureUpdate = + object.configureUpdate !== undefined && object.configureUpdate !== null + ? ConfigUpdate.fromPartial(object.configureUpdate) + : undefined; + message.registerPk = + object.registerPk !== undefined && object.registerPk !== null + ? RegisterPk.fromPartial(object.registerPk) + : undefined; + message.registerPkResponse = + object.registerPkResponse !== undefined && + object.registerPkResponse !== null + ? RegisterPkResponse.fromPartial(object.registerPkResponse) + : undefined; + message.softwareUpdate = + object.softwareUpdate !== undefined && object.softwareUpdate !== null + ? SoftwareUpdate.fromPartial(object.softwareUpdate) + : undefined; + message.requestRelay = + object.requestRelay !== undefined && object.requestRelay !== null + ? RequestRelay.fromPartial(object.requestRelay) + : undefined; + message.relayResponse = + object.relayResponse !== undefined && object.relayResponse !== null + ? RelayResponse.fromPartial(object.relayResponse) + : undefined; + message.testNatRequest = + object.testNatRequest !== undefined && object.testNatRequest !== null + ? TestNatRequest.fromPartial(object.testNatRequest) + : undefined; + message.testNatResponse = + object.testNatResponse !== undefined && object.testNatResponse !== null + ? TestNatResponse.fromPartial(object.testNatResponse) + : undefined; + message.peerDiscovery = + object.peerDiscovery !== undefined && object.peerDiscovery !== null + ? PeerDiscovery.fromPartial(object.peerDiscovery) + : undefined; + return message; + }, +}; + +declare var self: any | undefined; +declare var window: any | undefined; +declare var global: any | undefined; +var globalThis: any = (() => { + if (typeof globalThis !== "undefined") return globalThis; + if (typeof self !== "undefined") return self; + if (typeof window !== "undefined") return window; + if (typeof global !== "undefined") return global; + throw "Unable to locate global object"; +})(); + +const atob: (b64: string) => string = + globalThis.atob || + ((b64) => globalThis.Buffer.from(b64, "base64").toString("binary")); +function bytesFromBase64(b64: string): Uint8Array { + const bin = atob(b64); + const arr = new Uint8Array(bin.length); + for (let i = 0; i < bin.length; ++i) { + arr[i] = bin.charCodeAt(i); + } + return arr; +} + +const btoa: (bin: string) => string = + globalThis.btoa || + ((bin) => globalThis.Buffer.from(bin, "binary").toString("base64")); +function base64FromBytes(arr: Uint8Array): string { + const bin: string[] = []; + for (const byte of arr) { + bin.push(String.fromCharCode(byte)); + } + return btoa(bin.join("")); +} + +type Builtin = + | Date + | Function + | Uint8Array + | string + | number + | boolean + | undefined; + +export type DeepPartial = T extends Builtin + ? T + : T extends Array + ? Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin + ? P + : P & { [K in keyof P]: Exact } & Record< + Exclude>, + never + >; + +// If you get a compile-error about 'Constructor and ... have no overlap', +// add '--ts_proto_opt=esModuleInterop=true' as a flag when calling 'protoc'. +if (util.Long !== Long) { + util.Long = Long as any; + configure(); +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/ts_proto.py b/ts_proto.py new file mode 100644 index 000000000..d1627f51f --- /dev/null +++ b/ts_proto.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +import os + +path = os.path.abspath(os.path.join(os.getcwd(), '..', 'hbb', 'libs', 'hbb_common', 'protos')) + +if os.name == 'nt': + cmd = r'protoc --plugin=protoc-gen-ts_proto=.\node_modules\.bin\protoc-gen-ts_proto.cmd -I "%s" --ts_proto_out=./src/ rendezvous.proto'%path + print(cmd) + os.system(cmd) + cmd = r'protoc --plugin=protoc-gen-ts_proto=.\node_modules\.bin\protoc-gen-ts_proto.cmd -I "%s" --ts_proto_out=./src/ message.proto'%path + print(cmd) + os.system(cmd) +else: + cmd = r'protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto -I "%s" --ts_proto_out=./src/ rendezvous.proto'%path + print(cmd) + os.system(cmd) + cmd = r'protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto -I "%s" --ts_proto_out=./src/ message.proto'%path + print(cmd) + os.system(cmd) \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 2cfb703a5..8672df468 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,79 @@ # yarn lockfile v1 +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + integrity sha1-m4sMxmPWaafY9vXQiToU00jzD78= + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== + +"@protobufjs/eventemitter@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + integrity sha1-NVy8mLr61ZePntCV85diHx0Ga3A= + +"@protobufjs/fetch@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + integrity sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU= + dependencies: + "@protobufjs/aspromise" "^1.1.1" + "@protobufjs/inquire" "^1.1.0" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + integrity sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E= + +"@protobufjs/inquire@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + integrity sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik= + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + integrity sha1-bMKyDFya1q0NzP0hynZz2Nf79o0= + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + integrity sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q= + +"@protobufjs/utf8@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= + +"@types/long@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9" + integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w== + +"@types/node@>=13.7.0": + version "17.0.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.8.tgz#50d680c8a8a78fe30abe6906453b21ad8ab0ad7b" + integrity sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg== + +"@types/object-hash@^1.3.0": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@types/object-hash/-/object-hash-1.3.4.tgz#079ba142be65833293673254831b5e3e847fe58b" + integrity sha512-xFdpkAkikBgqBdG9vIlsqffDV8GpvnPEzs0IUtr1v3BEB97ijsFQ4RXVbUZwjFThhB4MDSTUfvmxUD5PGx0wXA== + +dataloader@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-1.4.0.tgz#bca11d867f5d3f1b9ed9f737bd15970c65dff5c8" + integrity sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw== + esbuild-android-arm64@0.13.15: version "0.13.15" resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.13.15.tgz#3fc3ff0bab76fe35dd237476b5d2b32bb20a3d44" @@ -134,11 +207,26 @@ is-core-module@^2.8.0: dependencies: has "^1.0.3" +lodash@^4.17.15: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +long@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== + nanoid@^3.1.30: version "3.2.0" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c" integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA== +object-hash@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df" + integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA== + path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" @@ -158,6 +246,30 @@ postcss@^8.4.5: picocolors "^1.0.0" source-map-js "^1.0.1" +prettier@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" + integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== + +protobufjs@^6.8.8: + version "6.11.2" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.2.tgz#de39fabd4ed32beaa08e9bb1e30d08544c1edf8b" + integrity sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/long" "^4.0.1" + "@types/node" ">=13.7.0" + long "^4.0.0" + resolve@^1.20.0: version "1.21.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.21.0.tgz#b51adc97f3472e6a5cf4444d34bc9d6b9037591f" @@ -184,6 +296,34 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +ts-poet@^4.5.0: + version "4.10.0" + resolved "https://registry.yarnpkg.com/ts-poet/-/ts-poet-4.10.0.tgz#8732374655e87f8f833e5d110938e346713e8c66" + integrity sha512-V5xzt+LDMVtxWvK12WVwHhGHTA//CeoPdWOqka0mMjlRqq7RPKYSfWEnzJdMmhNbd34BwZuZpip4mm+nqEcbQA== + dependencies: + lodash "^4.17.15" + prettier "^2.5.1" + +ts-proto-descriptors@^1.2.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/ts-proto-descriptors/-/ts-proto-descriptors-1.3.1.tgz#760ebaaa19475b03662f7b358ffea45b9c5348f5" + integrity sha512-Cybb3fqceMwA6JzHdC32dIo8eVGVmXrM6TWhdk1XQVVHT/6OQqk0ioyX1dIdu3rCIBhRmWUhUE4HsyK+olmgMw== + dependencies: + long "^4.0.0" + protobufjs "^6.8.8" + +ts-proto@^1.101.0: + version "1.101.0" + resolved "https://registry.yarnpkg.com/ts-proto/-/ts-proto-1.101.0.tgz#f8ce4523a0cb32ff224ff8a5759c2c046bf96244" + integrity sha512-XUV0WKQ3icHMToOOpUjf0RCKF9md+Lu9TV00LQlZ6ailJhbBqLh0BXQJ1PFUGxEW8YV65Wc/N8vAir152OE2Sg== + dependencies: + "@types/object-hash" "^1.3.0" + dataloader "^1.4.0" + object-hash "^1.3.1" + protobufjs "^6.8.8" + ts-poet "^4.5.0" + ts-proto-descriptors "^1.2.1" + typescript@^4.4.4: version "4.5.4" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.4.tgz#a17d3a0263bf5c8723b9c52f43c5084edf13c2e8" From fa31257cdc4b210445064de8af6346e1bdb1385e Mon Sep 17 00:00:00 2001 From: open-trade Date: Mon, 17 Jan 2022 18:11:14 +0800 Subject: [PATCH 204/422] initialize websock.ts --- src/websock.ts | 116 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 src/websock.ts diff --git a/src/websock.ts b/src/websock.ts new file mode 100644 index 000000000..abc5c6bc8 --- /dev/null +++ b/src/websock.ts @@ -0,0 +1,116 @@ +import * as proto from '../message.js'; + +export default class Websock { + constructor() { + this._websocket = null; // WebSocket object + this._eventHandlers = { + message: (msg) => { }, + open: () => { }, + close: () => { }, + error: () => { } + }; + this._next_yuv = null; + this._next_rgb = null; + } + + send(msg) { + if (msg instanceof Object) msg = proto.encodeMessage(msg); + this._websocket.send(msg); + } + + // Event Handlers + off(evt) { + this._eventHandlers[evt] = () => { }; + } + + on(evt, handler) { + this._eventHandlers[evt] = handler; + } + + init() { + this._websocket = null; + } + + open(uri, protocols) { + this.init(); + + this._websocket = new WebSocket(uri, protocols); + + this._websocket.onmessage = this._recv_message.bind(this); + this._websocket.binaryType = 'arraybuffer'; + this._websocket.onopen = () => { + console.debug('>> WebSock.onopen'); + if (this._websocket.protocol) { + console.info("Server choose sub-protocol: " + this._websocket.protocol); + } + + this._eventHandlers.open(); + console.debug("<< WebSock.onopen"); + }; + this._websocket.onclose = (e) => { + console.debug(">> WebSock.onclose"); + this._eventHandlers.close(e); + console.debug("<< WebSock.onclose"); + }; + this._websocket.onerror = (e) => { + console.debug(">> WebSock.onerror: " + e); + this._eventHandlers.error(e); + console.debug("<< WebSock.onerror: " + e); + }; + } + + close() { + if (this._websocket) { + if ((this._websocket.readyState === WebSocket.OPEN) || + (this._websocket.readyState === WebSocket.CONNECTING)) { + console.info("Closing WebSocket connection"); + this._websocket.close(); + } + + this._websocket.onmessage = () => { }; + } + } + + _recv_message(e) { + if (e.data instanceof window.ArrayBuffer) { + let bytes = new Uint8Array(e.data); + if (this._next_yuv) { + const yuv = this._next_yuv; + const { compress, stride } = yuv.format; + if (compress) { + bytes = window.simple_zstd.decompress(bytes); + } + if (!yuv.y) { + yuv.y = { bytes, stride: stride }; + } else if (!yuv.u) { + yuv.u = { bytes, stride: stride >> 1 }; + } else { + yuv.v = { bytes, stride: stride >> 1 }; + delete yuv.format.stride; + this._eventHandlers.message({ video_frame: { yuv } }); + this._next_yuv = null; + } + } else if (this._next_rgb) { + if (this._next_rgb.format.compress) { + bytes = window.simple_zstd.decompress(bytes); + } + this._eventHandlers.message({ video_frame: { rgb: bytes } }); + this._next_rgb = null; + } else { + const msg = proto.decodeMessage(bytes); + let vf = msg.video_frame; + if (vf) { + const { yuv, rgb } = vf; + if (yuv) { + this._next_yuv = { format: yuv }; + } else if (rgb) { + this._next_rgb = { format: rgb }; + } + return; + } else { + this._eventHandlers.message(msg); + } + } + } + } +} \ No newline at end of file From d4e8a66cf6439239a2f93e72d75294fd30cf7cd3 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 18 Jan 2022 02:02:39 +0800 Subject: [PATCH 205/422] websock init --- src/websock.ts | 185 ++++++++++++++++++++----------------------------- 1 file changed, 74 insertions(+), 111 deletions(-) diff --git a/src/websock.ts b/src/websock.ts index abc5c6bc8..2c6483584 100644 --- a/src/websock.ts +++ b/src/websock.ts @@ -1,116 +1,79 @@ -import * as proto from '../message.js'; +import * as message from "./message.js"; +import * as rendezvous from "./rendezvous.js"; + +type Keys = "message" | "open" | "close" | "error"; export default class Websock { - constructor() { - this._websocket = null; // WebSocket object - this._eventHandlers = { - message: (msg) => { }, - open: () => { }, - close: () => { }, - error: () => { } - }; - this._next_yuv = null; - this._next_rgb = null; + _websocket: WebSocket; + _eventHandlers: { [key in Keys]: Function }; + + send_message(msg: message.Message) { + this._websocket.send(message.Message.encode(msg).finish()); + } + + send_rendezvous(msg: rendezvous.RendezvousMessage) { + this._websocket.send(rendezvous.RendezvousMessage.encode(msg).finish()); + } + + // Event Handlers + off(evt: Keys) { + this._eventHandlers[evt] = () => {}; + } + + on(evt: Keys, handler: Function) { + this._eventHandlers[evt] = handler; + } + + constructor(uri: string, protocols: string) { + this._eventHandlers = { + message: (_: string) => {}, + open: () => {}, + close: () => {}, + error: () => {}, + }; + + this._websocket = new WebSocket(uri, protocols); + + this._websocket.onmessage = this._recv_message.bind(this); + this._websocket.binaryType = "arraybuffer"; + this._websocket.onopen = () => { + console.debug(">> WebSock.onopen"); + if (this._websocket.protocol) { + console.info("Server choose sub-protocol: " + this._websocket.protocol); + } + + this._eventHandlers.open(); + console.debug("<< WebSock.onopen"); + }; + this._websocket.onclose = (e) => { + console.debug(">> WebSock.onclose"); + this._eventHandlers.close(e); + console.debug("<< WebSock.onclose"); + }; + this._websocket.onerror = (e) => { + console.debug(">> WebSock.onerror: " + e); + this._eventHandlers.error(e); + console.debug("<< WebSock.onerror: " + e); + }; + } + + close() { + if (this._websocket) { + if ( + this._websocket.readyState === WebSocket.OPEN || + this._websocket.readyState === WebSocket.CONNECTING + ) { + console.info("Closing WebSocket connection"); + this._websocket.close(); + } + + this._websocket.onmessage = () => {}; } + } - send(msg) { - if (msg instanceof Object) msg = proto.encodeMessage(msg); - this._websocket.send(msg); + _recv_message(e: any) { + if (e.data instanceof window.ArrayBuffer) { + let bytes = new Uint8Array(e.data); } - - // Event Handlers - off(evt) { - this._eventHandlers[evt] = () => { }; - } - - on(evt, handler) { - this._eventHandlers[evt] = handler; - } - - init() { - this._websocket = null; - } - - open(uri, protocols) { - this.init(); - - this._websocket = new WebSocket(uri, protocols); - - this._websocket.onmessage = this._recv_message.bind(this); - this._websocket.binaryType = 'arraybuffer'; - this._websocket.onopen = () => { - console.debug('>> WebSock.onopen'); - if (this._websocket.protocol) { - console.info("Server choose sub-protocol: " + this._websocket.protocol); - } - - this._eventHandlers.open(); - console.debug("<< WebSock.onopen"); - }; - this._websocket.onclose = (e) => { - console.debug(">> WebSock.onclose"); - this._eventHandlers.close(e); - console.debug("<< WebSock.onclose"); - }; - this._websocket.onerror = (e) => { - console.debug(">> WebSock.onerror: " + e); - this._eventHandlers.error(e); - console.debug("<< WebSock.onerror: " + e); - }; - } - - close() { - if (this._websocket) { - if ((this._websocket.readyState === WebSocket.OPEN) || - (this._websocket.readyState === WebSocket.CONNECTING)) { - console.info("Closing WebSocket connection"); - this._websocket.close(); - } - - this._websocket.onmessage = () => { }; - } - } - - _recv_message(e) { - if (e.data instanceof window.ArrayBuffer) { - let bytes = new Uint8Array(e.data); - if (this._next_yuv) { - const yuv = this._next_yuv; - const { compress, stride } = yuv.format; - if (compress) { - bytes = window.simple_zstd.decompress(bytes); - } - if (!yuv.y) { - yuv.y = { bytes, stride: stride }; - } else if (!yuv.u) { - yuv.u = { bytes, stride: stride >> 1 }; - } else { - yuv.v = { bytes, stride: stride >> 1 }; - delete yuv.format.stride; - this._eventHandlers.message({ video_frame: { yuv } }); - this._next_yuv = null; - } - } else if (this._next_rgb) { - if (this._next_rgb.format.compress) { - bytes = window.simple_zstd.decompress(bytes); - } - this._eventHandlers.message({ video_frame: { rgb: bytes } }); - this._next_rgb = null; - } else { - const msg = proto.decodeMessage(bytes); - let vf = msg.video_frame; - if (vf) { - const { yuv, rgb } = vf; - if (yuv) { - this._next_yuv = { format: yuv }; - } else if (rgb) { - this._next_rgb = { format: rgb }; - } - return; - } else { - this._eventHandlers.message(msg); - } - } - } - } -} \ No newline at end of file + } +} From c09f65b6cc25c970daa0989e9fa38ed671b0004f Mon Sep 17 00:00:00 2001 From: open-trade Date: Tue, 18 Jan 2022 17:05:34 +0800 Subject: [PATCH 206/422] try out ogv codec loader --- package.json | 4 +++- src/codec.js | 47 +++++++++++++++++++++++++++++++++++++++++++++++ src/main.ts | 6 +++++- src/vite-env.d.ts | 2 +- tsconfig.json | 12 +++++++++--- yarn.lock | 24 ++++++++++++++++++++++++ 6 files changed, 89 insertions(+), 6 deletions(-) create mode 100644 src/codec.js diff --git a/package.json b/package.json index abe5dd151..17f883055 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "web_hbb", "version": "1.0.0", "scripts": { - "dev": "vite", + "dev": "cp node_modules/ogv/dist/* dist/ && vite", "build": "tsc && vite build", "preview": "vite preview" }, @@ -11,6 +11,8 @@ "vite": "^2.7.2" }, "dependencies": { + "libsodium": "^0.7.9", + "ogv": "^1.8.6", "ts-proto": "^1.101.0" } } diff --git a/src/codec.js b/src/codec.js new file mode 100644 index 000000000..384a5165e --- /dev/null +++ b/src/codec.js @@ -0,0 +1,47 @@ +import { OGVLoader } from "ogv"; + +// example: https://github.com/rgov/js-theora-decoder/blob/main/index.html +// dev: copy decoder files from node/ogv/dist/* to project dir +// dist: .... to dist + +export function loadVp9() { + OGVLoader.loadClass( + "OGVDecoderVideoVP9W", + (videoCodecClass) => { + videoCodecClass().then((decoder) => { + decoder.init(() => { + onVp9Ready(decoder) + }) + }) + }, + { worker: true } + ); +} + +export function loadOpus() { + OGVLoader.loadClass( + "OGVDecoderAudioOpusW", + (audioCodecClass) => { + audioCodecClass().then((decoder) => { + decoder.init(() => { + onOpusReady(decoder) + }) + }) + }, + { worker: true } + ); +} + +async function onVp9Ready(decoder) { + console.log("Vp9 decoder ready"); + + /* + decoder.processFrame(buffer, () => { + player.drawFrame(decoder.frameBuffer) + }) + */ +} + +async function onOpusReady(decoder) { + console.log("Opus decoder ready"); +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index f77db7a8f..00470a0a5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,8 @@ -import './style.css' +import './style.css'; +import { loadVp9, loadOpus } from "./codec"; + +loadVp9(); +loadOpus(); const app = document.querySelector('#app')! diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 11f02fe2a..151aa6856 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -1 +1 @@ -/// +/// \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 8cdbb2ac9..ca949de6a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,11 @@ "target": "ESNext", "useDefineForClassFields": true, "module": "ESNext", - "lib": ["ESNext", "DOM"], + "allowJs": true, + "lib": [ + "ESNext", + "DOM" + ], "moduleResolution": "Node", "strict": true, "sourceMap": true, @@ -14,5 +18,7 @@ "noUnusedParameters": true, "noImplicitReturns": true }, - "include": ["./src"] -} + "include": [ + "./src" + ] +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 8672df468..f1a562057 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,13 @@ # yarn lockfile v1 +"@babel/runtime@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.7.tgz#03ff99f64106588c9c403c6ecb8c3bafbbdff1fa" + integrity sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ== + dependencies: + regenerator-runtime "^0.13.4" + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" @@ -207,6 +214,11 @@ is-core-module@^2.8.0: dependencies: has "^1.0.3" +libsodium@^0.7.9: + version "0.7.9" + resolved "https://registry.yarnpkg.com/libsodium/-/libsodium-0.7.9.tgz#4bb7bcbf662ddd920d8795c227ae25bbbfa3821b" + integrity sha512-gfeADtR4D/CM0oRUviKBViMGXZDgnFdMKMzHsvBdqLBHd9ySi6EtYnmuhHVDDYgYpAO8eU8hEY+F8vIUAPh08A== + lodash@^4.17.15: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -227,6 +239,13 @@ object-hash@^1.3.1: resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df" integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA== +ogv@^1.8.6: + version "1.8.6" + resolved "https://registry.yarnpkg.com/ogv/-/ogv-1.8.6.tgz#0e6fa1c166802e3a405b1b318d8eb27d2544cce9" + integrity sha512-YuvO37U7Hc0mIm/TyAbyn3qVDKgg+4b+ViU73qEGzEKVX8bdxEVH08SoYrGLTJ2b8Z1/IEx8Vdy0hO4CWhRMOA== + dependencies: + "@babel/runtime" "^7.16.7" + path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" @@ -270,6 +289,11 @@ protobufjs@^6.8.8: "@types/node" ">=13.7.0" long "^4.0.0" +regenerator-runtime@^0.13.4: + version "0.13.9" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" + integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== + resolve@^1.20.0: version "1.21.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.21.0.tgz#b51adc97f3472e6a5cf4444d34bc9d6b9037591f" From 9f42fd534ccd2608324c51398200e5f7ce51b2ec Mon Sep 17 00:00:00 2001 From: open-trade Date: Tue, 18 Jan 2022 17:24:36 +0800 Subject: [PATCH 207/422] put ogv.js to html --- .gitignore | 4 +++- index.html | 1 + package.json | 4 ++-- src/codec.js | 23 +++++++++++++++++++---- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 53f7466ac..f2ed50767 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ node_modules .DS_Store dist dist-ssr -*.local \ No newline at end of file +*.local +*log +ogvjs diff --git a/index.html b/index.html index 867581c51..c88142fa2 100644 --- a/index.html +++ b/index.html @@ -4,6 +4,7 @@ + Vite App diff --git a/package.json b/package.json index 17f883055..f68287840 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,8 @@ "name": "web_hbb", "version": "1.0.0", "scripts": { - "dev": "cp node_modules/ogv/dist/* dist/ && vite", - "build": "tsc && vite build", + "dev": "cp -rf node_modules/ogv/dist/* ogvjs/ && vite", + "build": "cp node_modules/ogv/dist/* dist/ogvjs/ && tsc && vite build", "preview": "vite preview" }, "devDependencies": { diff --git a/src/codec.js b/src/codec.js index 384a5165e..6b44321b6 100644 --- a/src/codec.js +++ b/src/codec.js @@ -1,11 +1,26 @@ -import { OGVLoader } from "ogv"; - // example: https://github.com/rgov/js-theora-decoder/blob/main/index.html // dev: copy decoder files from node/ogv/dist/* to project dir // dist: .... to dist +/* + OGVDemuxerOggW: 'ogv-demuxer-ogg-wasm.js', + OGVDemuxerWebMW: 'ogv-demuxer-webm-wasm.js', + OGVDecoderAudioOpusW: 'ogv-decoder-audio-opus-wasm.js', + OGVDecoderAudioVorbisW: 'ogv-decoder-audio-vorbis-wasm.js', + OGVDecoderVideoTheoraW: 'ogv-decoder-video-theora-wasm.js', + OGVDecoderVideoVP8W: 'ogv-decoder-video-vp8-wasm.js', + OGVDecoderVideoVP8MTW: 'ogv-decoder-video-vp8-mt-wasm.js', + OGVDecoderVideoVP9W: 'ogv-decoder-video-vp9-wasm.js', + OGVDecoderVideoVP9SIMDW: 'ogv-decoder-video-vp9-simd-wasm.js', + OGVDecoderVideoVP9MTW: 'ogv-decoder-video-vp9-mt-wasm.js', + OGVDecoderVideoVP9SIMDMTW: 'ogv-decoder-video-vp9-simd-mt-wasm.js', + OGVDecoderVideoAV1W: 'ogv-decoder-video-av1-wasm.js', + OGVDecoderVideoAV1SIMDW: 'ogv-decoder-video-av1-simd-wasm.js', + OGVDecoderVideoAV1MTW: 'ogv-decoder-video-av1-mt-wasm.js', + OGVDecoderVideoAV1SIMDMTW: 'ogv-decoder-video-av1-simd-mt-wasm.js', +*/ export function loadVp9() { - OGVLoader.loadClass( + window.OGVLoader.loadClass( "OGVDecoderVideoVP9W", (videoCodecClass) => { videoCodecClass().then((decoder) => { @@ -19,7 +34,7 @@ export function loadVp9() { } export function loadOpus() { - OGVLoader.loadClass( + window.OGVLoader.loadClass( "OGVDecoderAudioOpusW", (audioCodecClass) => { audioCodecClass().then((decoder) => { From d7d4b46c46e8847c63f7925d5b9bb4f4ae162db4 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 19 Jan 2022 00:57:57 +0800 Subject: [PATCH 208/422] sha256 --- package.json | 1 + src/websock.ts | 7 +++++++ yarn.lock | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/package.json b/package.json index f68287840..ae337fb4b 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "vite": "^2.7.2" }, "dependencies": { + "fast-sha256": "^1.3.0", "libsodium": "^0.7.9", "ogv": "^1.8.6", "ts-proto": "^1.101.0" diff --git a/src/websock.ts b/src/websock.ts index 2c6483584..67af1c139 100644 --- a/src/websock.ts +++ b/src/websock.ts @@ -1,5 +1,6 @@ import * as message from "./message.js"; import * as rendezvous from "./rendezvous.js"; +import * as sha256 from "fast-sha256"; type Keys = "message" | "open" | "close" | "error"; @@ -76,4 +77,10 @@ export default class Websock { let bytes = new Uint8Array(e.data); } } + + hash(datas: [Uint8Array]): Uint8Array { + const hasher = new sha256.Hash(); + datas.forEach((data) => hasher.update(data)); + return hasher.digest(); + } } diff --git a/yarn.lock b/yarn.lock index f1a562057..c71b7d1c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -190,6 +190,11 @@ esbuild@^0.13.12: esbuild-windows-64 "0.13.15" esbuild-windows-arm64 "0.13.15" +fast-sha256@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/fast-sha256/-/fast-sha256-1.3.0.tgz#7916ba2054eeb255982608cccd0f6660c79b7ae6" + integrity sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ== + fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" From a90cacaeb88b1c48189b271cf44e1047e6148ae8 Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 19 Jan 2022 19:13:49 +0800 Subject: [PATCH 209/422] will implement async web sock myself --- src/main.ts | 1 + src/websock.ts | 12 +++ yarn.lock | 266 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 279 insertions(+) diff --git a/src/main.ts b/src/main.ts index 00470a0a5..872e81375 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,6 @@ import './style.css'; import { loadVp9, loadOpus } from "./codec"; +import './websock'; loadVp9(); loadOpus(); diff --git a/src/websock.ts b/src/websock.ts index 67af1c139..04b7fca7c 100644 --- a/src/websock.ts +++ b/src/websock.ts @@ -84,3 +84,15 @@ export default class Websock { return hasher.digest(); } } + +/* +let ws = new Websock('ws://207.148.17.15:21118'); +await ws.open(); +console.log("ws connected"); +// let punchHole = rendezvous.PunchHoleRequest.fromJSON({ id: '' }); +// ws.send_rendezvous(rendezvous.RendezvousMessage.fromJSON({ punchHole })); +let testNatRequest = rendezvous.TestNatRequest.fromJSON({ serial: 0 }); +ws.send_rendezvous(rendezvous.RendezvousMessage.fromJSON({ testNatRequest })); +let msg = await ws.next(); +console.log(msg); +*/ \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index c71b7d1c5..22a489545 100644 --- a/yarn.lock +++ b/yarn.lock @@ -77,11 +77,66 @@ resolved "https://registry.yarnpkg.com/@types/object-hash/-/object-hash-1.3.4.tgz#079ba142be65833293673254831b5e3e847fe58b" integrity sha512-xFdpkAkikBgqBdG9vIlsqffDV8GpvnPEzs0IUtr1v3BEB97ijsFQ4RXVbUZwjFThhB4MDSTUfvmxUD5PGx0wXA== +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +chnl@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/chnl/-/chnl-1.2.0.tgz#d818c95367767a0880508e7cc0b5b3503f58fd4c" + integrity sha512-g5gJb59edwCliFbX2j7G6sBfY4sX9YLy211yctONI2GRaiX0f2zIbKWmBm+sPqFNEpM7Ljzm7IJX/xrjiEbPrw== + dataloader@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-1.4.0.tgz#bca11d867f5d3f1b9ed9f737bd15970c65dff5c8" integrity sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw== +define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +es-abstract@^1.19.1: + version "1.19.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" + integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + get-intrinsic "^1.1.1" + get-symbol-description "^1.0.0" + has "^1.0.3" + has-symbols "^1.0.2" + internal-slot "^1.0.3" + is-callable "^1.2.4" + is-negative-zero "^2.0.1" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.1" + is-string "^1.0.7" + is-weakref "^1.0.1" + object-inspect "^1.11.0" + object-keys "^1.1.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.4" + string.prototype.trimstart "^1.0.4" + unbox-primitive "^1.0.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + esbuild-android-arm64@0.13.15: version "0.13.15" resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.13.15.tgz#3fc3ff0bab76fe35dd237476b5d2b32bb20a3d44" @@ -205,6 +260,40 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +has-bigints@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" + integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== + +has-symbols@^1.0.1, has-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" + integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -212,6 +301,35 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +internal-slot@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" + integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== + dependencies: + get-intrinsic "^1.1.0" + has "^1.0.3" + side-channel "^1.0.4" + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-callable@^1.1.4, is-callable@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" + integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== + is-core-module@^2.8.0: version "2.8.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" @@ -219,6 +337,59 @@ is-core-module@^2.8.0: dependencies: has "^1.0.3" +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-negative-zero@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + +is-number-object@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" + integrity sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g== + dependencies: + has-tostringtag "^1.0.0" + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-shared-array-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" + integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-weakref@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + libsodium@^0.7.9: version "0.7.9" resolved "https://registry.yarnpkg.com/libsodium/-/libsodium-0.7.9.tgz#4bb7bcbf662ddd920d8795c227ae25bbbfa3821b" @@ -244,6 +415,26 @@ object-hash@^1.3.1: resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df" integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA== +object-inspect@^1.11.0, object-inspect@^1.9.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" + integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== + +object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" + object-keys "^1.1.1" + ogv@^1.8.6: version "1.8.6" resolved "https://registry.yarnpkg.com/ogv/-/ogv-1.8.6.tgz#0e6fa1c166802e3a405b1b318d8eb27d2544cce9" @@ -275,6 +466,25 @@ prettier@^2.5.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== +promise-controller@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/promise-controller/-/promise-controller-1.0.0.tgz#81ebea71271aa40ac8f3bebccab3d4158dc4cc02" + integrity sha512-goA0zA9L91tuQbUmiMinSYqlyUtEgg4fxJcjYnLYOQnrktb4o4UqciXDNXiRUPiDBPACmsr1k8jDW4r7UDq9Qw== + +promise.prototype.finally@^3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/promise.prototype.finally/-/promise.prototype.finally-3.1.3.tgz#d3186e58fcf4df1682a150f934ccc27b7893389c" + integrity sha512-EXRF3fC9/0gz4qkt/f5EP5iW4kj9oFpBICNpCNOb/52+8nlHIX07FPLbi/q4qYBQ1xZqivMzTpNQSnArVASolQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + +promised-map@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/promised-map/-/promised-map-1.0.0.tgz#aa3ec59e13724d37b946415f6850273af8e213b5" + integrity sha512-fP9VSMgcml+U2uJ9PBc4/LDQ3ZkJCH4blLNCS6gbH7RHyRZCYs91zxWHqiUy+heFiEMiB2op/qllYoFqmIqdWA== + protobufjs@^6.8.8: version "6.11.2" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.2.tgz#de39fabd4ed32beaa08e9bb1e30d08544c1edf8b" @@ -315,11 +525,36 @@ rollup@^2.59.0: optionalDependencies: fsevents "~2.3.2" +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + source-map-js@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.1.tgz#a1741c131e3c77d048252adfa24e23b908670caf" integrity sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA== +string.prototype.trimend@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" + integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string.prototype.trimstart@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" + integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" @@ -358,6 +593,16 @@ typescript@^4.4.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.4.tgz#a17d3a0263bf5c8723b9c52f43c5084edf13c2e8" integrity sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg== +unbox-primitive@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" + integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== + dependencies: + function-bind "^1.1.1" + has-bigints "^1.0.1" + has-symbols "^1.0.2" + which-boxed-primitive "^1.0.2" + vite@^2.7.2: version "2.7.12" resolved "https://registry.yarnpkg.com/vite/-/vite-2.7.12.tgz#7784ab19e7ff98f6a192d2d7d877480a8c2b7e7d" @@ -369,3 +614,24 @@ vite@^2.7.2: rollup "^2.59.0" optionalDependencies: fsevents "~2.3.2" + +websocket-as-promised@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/websocket-as-promised/-/websocket-as-promised-2.0.1.tgz#d18b7c160bba294585aaf6fa9572f31e05cfb175" + integrity sha512-ePV26D/D37ughXU9j+DjGmwUbelWJrC/vi+6GK++fRlBJmS7aU9T8ABu47KFF0O7r6XN2NAuqJRpegbUwXZxQg== + dependencies: + chnl "^1.2.0" + promise-controller "^1.0.0" + promise.prototype.finally "^3.1.2" + promised-map "^1.0.0" + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" From b18da1fb39d67dae1c3830689735c2cecac75e58 Mon Sep 17 00:00:00 2001 From: open-trade Date: Wed, 19 Jan 2022 19:19:29 +0800 Subject: [PATCH 210/422] nothing --- src/websock.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/websock.ts b/src/websock.ts index 04b7fca7c..052adf9b2 100644 --- a/src/websock.ts +++ b/src/websock.ts @@ -18,7 +18,7 @@ export default class Websock { // Event Handlers off(evt: Keys) { - this._eventHandlers[evt] = () => {}; + this._eventHandlers[evt] = () => { }; } on(evt: Keys, handler: Function) { @@ -27,10 +27,10 @@ export default class Websock { constructor(uri: string, protocols: string) { this._eventHandlers = { - message: (_: string) => {}, - open: () => {}, - close: () => {}, - error: () => {}, + message: (_: any) => { }, + open: () => { }, + close: () => { }, + error: () => { }, }; this._websocket = new WebSocket(uri, protocols); @@ -68,7 +68,7 @@ export default class Websock { this._websocket.close(); } - this._websocket.onmessage = () => {}; + this._websocket.onmessage = () => { }; } } @@ -76,6 +76,7 @@ export default class Websock { if (e.data instanceof window.ArrayBuffer) { let bytes = new Uint8Array(e.data); } + this._eventHandlers.message(e.data); } hash(datas: [Uint8Array]): Uint8Array { From 85ec94e36cf5fbab9318fe006f91788cd7d8e9a6 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 19 Jan 2022 22:26:23 +0800 Subject: [PATCH 211/422] ws async works --- src/websock.ts | 135 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 97 insertions(+), 38 deletions(-) diff --git a/src/websock.ts b/src/websock.ts index 052adf9b2..acd9c3b58 100644 --- a/src/websock.ts +++ b/src/websock.ts @@ -7,55 +7,115 @@ type Keys = "message" | "open" | "close" | "error"; export default class Websock { _websocket: WebSocket; _eventHandlers: { [key in Keys]: Function }; + _buf: Uint8Array[]; + _status: any; - send_message(msg: message.Message) { - this._websocket.send(message.Message.encode(msg).finish()); + constructor(uri: string) { + this._eventHandlers = { + message: (_: any) => {}, + open: () => {}, + close: () => {}, + error: () => {}, + }; + this._status = ""; + this._buf = []; + this._websocket = new WebSocket(uri); + this._websocket.onmessage = this._recv_message.bind(this); + this._websocket.binaryType = "arraybuffer"; } - send_rendezvous(msg: rendezvous.RendezvousMessage) { - this._websocket.send(rendezvous.RendezvousMessage.encode(msg).finish()); + sendMessage(data: any) { + this._websocket.send( + message.Message.encode(message.Message.fromJSON(data)).finish() + ); + } + + sendRendezvous(data: any) { + this._websocket.send( + rendezvous.RendezvousMessage.encode( + rendezvous.RendezvousMessage.fromJSON(data) + ).finish() + ); + } + + parseMessage(data: Uint8Array) { + return message.Message.decode(data); + } + + parseRendezvous(data: Uint8Array) { + return rendezvous.RendezvousMessage.decode(data); } // Event Handlers off(evt: Keys) { - this._eventHandlers[evt] = () => { }; + this._eventHandlers[evt] = () => {}; } on(evt: Keys, handler: Function) { this._eventHandlers[evt] = handler; } - constructor(uri: string, protocols: string) { - this._eventHandlers = { - message: (_: any) => { }, - open: () => { }, - close: () => { }, - error: () => { }, - }; + async open(timeout: number = 12000): Promise { + return new Promise((resolve, reject) => { + setTimeout(() => { + if (this._status != "open") { + reject(this._status || "timeout"); + } + }, timeout); + this._websocket.onopen = () => { + this._status = "open"; + console.debug(">> WebSock.onopen"); + if (this._websocket?.protocol) { + console.info( + "Server choose sub-protocol: " + this._websocket.protocol + ); + } - this._websocket = new WebSocket(uri, protocols); + this._eventHandlers.open(); + console.debug("<< WebSock.onopen"); + resolve(this); + }; + this._websocket.onclose = (e) => { + this._status = e; + console.debug(">> WebSock.onclose"); + this._eventHandlers.close(e); + console.debug("<< WebSock.onclose"); + reject(e); + }; + this._websocket.onerror = (e) => { + this._status = e; + console.debug(">> WebSock.onerror: " + e); + this._eventHandlers.error(e); + console.debug("<< WebSock.onerror: " + e); + reject(e); + }; + }); + } - this._websocket.onmessage = this._recv_message.bind(this); - this._websocket.binaryType = "arraybuffer"; - this._websocket.onopen = () => { - console.debug(">> WebSock.onopen"); - if (this._websocket.protocol) { - console.info("Server choose sub-protocol: " + this._websocket.protocol); + async next(timeout = 12000): Promise { + let func = ( + resolve: (value: Uint8Array) => void, + reject: (reason: any) => void, + tm0: number + ) => { + if (this._buf.length) { + resolve(this._buf[0]); + this._buf.splice(0, 1); + } else { + if (this._status != 'open') { + reject(this._status); + return; + } + if (new Date().getTime() > tm0 + timeout) { + reject("timeout"); + } else { + setTimeout(() => func(resolve, reject, tm0), 1); + } } - - this._eventHandlers.open(); - console.debug("<< WebSock.onopen"); - }; - this._websocket.onclose = (e) => { - console.debug(">> WebSock.onclose"); - this._eventHandlers.close(e); - console.debug("<< WebSock.onclose"); - }; - this._websocket.onerror = (e) => { - console.debug(">> WebSock.onerror: " + e); - this._eventHandlers.error(e); - console.debug("<< WebSock.onerror: " + e); }; + return new Promise((resolve, reject) => { + func(resolve, reject, new Date().getTime()); + }); } close() { @@ -68,13 +128,14 @@ export default class Websock { this._websocket.close(); } - this._websocket.onmessage = () => { }; + this._websocket.onmessage = () => {}; } } _recv_message(e: any) { if (e.data instanceof window.ArrayBuffer) { let bytes = new Uint8Array(e.data); + this._buf.push(bytes); } this._eventHandlers.message(e.data); } @@ -86,14 +147,12 @@ export default class Websock { } } -/* -let ws = new Websock('ws://207.148.17.15:21118'); +let ws = new Websock("ws://207.148.17.15:21118"); await ws.open(); console.log("ws connected"); // let punchHole = rendezvous.PunchHoleRequest.fromJSON({ id: '' }); // ws.send_rendezvous(rendezvous.RendezvousMessage.fromJSON({ punchHole })); let testNatRequest = rendezvous.TestNatRequest.fromJSON({ serial: 0 }); -ws.send_rendezvous(rendezvous.RendezvousMessage.fromJSON({ testNatRequest })); -let msg = await ws.next(); +ws.sendRendezvous({ testNatRequest }); +let msg = ws.parseRendezvous(await ws.next()); console.log(msg); -*/ \ No newline at end of file From 28c096769061ffc9c2c6ac3e64903c35d168f10d Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 20 Jan 2022 01:00:35 +0800 Subject: [PATCH 212/422] can make relay connection now --- src/client.ts | 100 +++++++++++++++++++++++++++++++++++++++++++++++++ src/codec.js | 88 +++++++++++++++++++++---------------------- src/main.ts | 6 +-- src/websock.ts | 31 +++++++-------- 4 files changed, 158 insertions(+), 67 deletions(-) create mode 100644 src/client.ts diff --git a/src/client.ts b/src/client.ts new file mode 100644 index 000000000..bc8170783 --- /dev/null +++ b/src/client.ts @@ -0,0 +1,100 @@ +import Websock from "./websock"; +import * as message from "./message.js"; +import * as rendezvous from "./rendezvous.js"; +import { loadVp9, loadOpus } from "./codec"; + +const URI = "ws://207.148.17.15"; +const PORT = 21118; +const licenceKey = ""; + +loadVp9(); +loadOpus(); + +export default class Client { + _msgs: any[]; + _ws: Websock | undefined; + _interval: any; + _id: string; + + constructor() { + this._msgs = []; + this._id = ''; + this._interval = setInterval(() => { + while (this._msgs.length) { + this._ws?.sendMessage(this._msgs[0]); + this._msgs.splice(0, 1); + } + }, 1); + } + + close() { + clearInterval(this._interval); + this._ws?.close(); + } + + async connect(id: string) { + const ws = new Websock(URI + ":" + PORT); + this._ws = ws; + this._id = id; + await ws.open(); + const connType = rendezvous.ConnType.DEFAULT_CONN; + const natType = rendezvous.NatType.SYMMETRIC; + const punchHoleRequest = rendezvous.PunchHoleRequest.fromJSON({ + id, + licenceKey, + connType, + natType, + }); + ws.sendRendezvous({ punchHoleRequest }); + const msg = ws.parseRendezvous(await ws.next()); + const phr = msg.punchHoleResponse; + const rr = msg.relayResponse; + if (phr) { + if (phr.failure != rendezvous.PunchHoleResponse_Failure.UNKNOWN) { + switch (phr?.failure) { + case rendezvous.PunchHoleResponse_Failure.ID_NOT_EXIST: + break; + } + ws.close(); + } + } else if (rr) { + await this.connectRelay(rr); + } + } + + async connectRelay(rr: rendezvous.RelayResponse) { + const pk = rr.pk; + let uri = rr.relayServer; + if (uri.indexOf(':') > 0) { + const tmp = uri.split(':'); + const port = parseInt(tmp[1]); + uri = tmp[0] + ':' + (port + 2); + } else { + uri += ':' + (PORT + 1); + } + const uuid = rr.uuid; + const ws = new Websock('ws://' + uri); + await ws.open(); + console.log('Connected to relay server') + this._ws = ws; + const requestRelay = rendezvous.RequestRelay.fromJSON({ + licenceKey, + uuid, + }); + ws.sendRendezvous({ requestRelay }); + await this.secure(pk); + } + + async secure(pk: Uint8Array | undefined) { + // + } +} + +async function testDelay() { + const ws = new Websock(URI + ":" + PORT); + await ws.open(); + console.log(ws.latency()); +} + +await testDelay(); +await new Client().connect("124931507"); diff --git a/src/codec.js b/src/codec.js index 6b44321b6..6f1381d96 100644 --- a/src/codec.js +++ b/src/codec.js @@ -2,61 +2,61 @@ // dev: copy decoder files from node/ogv/dist/* to project dir // dist: .... to dist /* - OGVDemuxerOggW: 'ogv-demuxer-ogg-wasm.js', - OGVDemuxerWebMW: 'ogv-demuxer-webm-wasm.js', - OGVDecoderAudioOpusW: 'ogv-decoder-audio-opus-wasm.js', - OGVDecoderAudioVorbisW: 'ogv-decoder-audio-vorbis-wasm.js', - OGVDecoderVideoTheoraW: 'ogv-decoder-video-theora-wasm.js', - OGVDecoderVideoVP8W: 'ogv-decoder-video-vp8-wasm.js', - OGVDecoderVideoVP8MTW: 'ogv-decoder-video-vp8-mt-wasm.js', - OGVDecoderVideoVP9W: 'ogv-decoder-video-vp9-wasm.js', - OGVDecoderVideoVP9SIMDW: 'ogv-decoder-video-vp9-simd-wasm.js', - OGVDecoderVideoVP9MTW: 'ogv-decoder-video-vp9-mt-wasm.js', - OGVDecoderVideoVP9SIMDMTW: 'ogv-decoder-video-vp9-simd-mt-wasm.js', - OGVDecoderVideoAV1W: 'ogv-decoder-video-av1-wasm.js', - OGVDecoderVideoAV1SIMDW: 'ogv-decoder-video-av1-simd-wasm.js', - OGVDecoderVideoAV1MTW: 'ogv-decoder-video-av1-mt-wasm.js', - OGVDecoderVideoAV1SIMDMTW: 'ogv-decoder-video-av1-simd-mt-wasm.js', + OGVDemuxerOggW: 'ogv-demuxer-ogg-wasm.js', + OGVDemuxerWebMW: 'ogv-demuxer-webm-wasm.js', + OGVDecoderAudioOpusW: 'ogv-decoder-audio-opus-wasm.js', + OGVDecoderAudioVorbisW: 'ogv-decoder-audio-vorbis-wasm.js', + OGVDecoderVideoTheoraW: 'ogv-decoder-video-theora-wasm.js', + OGVDecoderVideoVP8W: 'ogv-decoder-video-vp8-wasm.js', + OGVDecoderVideoVP8MTW: 'ogv-decoder-video-vp8-mt-wasm.js', + OGVDecoderVideoVP9W: 'ogv-decoder-video-vp9-wasm.js', + OGVDecoderVideoVP9SIMDW: 'ogv-decoder-video-vp9-simd-wasm.js', + OGVDecoderVideoVP9MTW: 'ogv-decoder-video-vp9-mt-wasm.js', + OGVDecoderVideoVP9SIMDMTW: 'ogv-decoder-video-vp9-simd-mt-wasm.js', + OGVDecoderVideoAV1W: 'ogv-decoder-video-av1-wasm.js', + OGVDecoderVideoAV1SIMDW: 'ogv-decoder-video-av1-simd-wasm.js', + OGVDecoderVideoAV1MTW: 'ogv-decoder-video-av1-mt-wasm.js', + OGVDecoderVideoAV1SIMDMTW: 'ogv-decoder-video-av1-simd-mt-wasm.js', */ export function loadVp9() { - window.OGVLoader.loadClass( - "OGVDecoderVideoVP9W", - (videoCodecClass) => { - videoCodecClass().then((decoder) => { - decoder.init(() => { - onVp9Ready(decoder) - }) - }) - }, - { worker: true } - ); + window.OGVLoader.loadClass( + "OGVDecoderVideoVP9W", + (videoCodecClass) => { + videoCodecClass().then((decoder) => { + decoder.init(() => { + onVp9Ready(decoder) + }) + }) + }, + { worker: true } + ); } export function loadOpus() { - window.OGVLoader.loadClass( - "OGVDecoderAudioOpusW", - (audioCodecClass) => { - audioCodecClass().then((decoder) => { - decoder.init(() => { - onOpusReady(decoder) - }) - }) - }, - { worker: true } - ); + window.OGVLoader.loadClass( + "OGVDecoderAudioOpusW", + (audioCodecClass) => { + audioCodecClass().then((decoder) => { + decoder.init(() => { + onOpusReady(decoder) + }) + }) + }, + { worker: true } + ); } async function onVp9Ready(decoder) { - console.log("Vp9 decoder ready"); + console.log("Vp9 decoder ready"); - /* - decoder.processFrame(buffer, () => { - player.drawFrame(decoder.frameBuffer) - }) - */ + /* + decoder.processFrame(buffer, () => { + player.drawFrame(decoder.frameBuffer) + }) + */ } async function onOpusReady(decoder) { - console.log("Opus decoder ready"); + console.log("Opus decoder ready"); } \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 872e81375..f1c79723d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,9 +1,5 @@ import './style.css'; -import { loadVp9, loadOpus } from "./codec"; -import './websock'; - -loadVp9(); -loadOpus(); +import "./client"; const app = document.querySelector('#app')! diff --git a/src/websock.ts b/src/websock.ts index acd9c3b58..231bfafa8 100644 --- a/src/websock.ts +++ b/src/websock.ts @@ -9,6 +9,7 @@ export default class Websock { _eventHandlers: { [key in Keys]: Function }; _buf: Uint8Array[]; _status: any; + _latency: number; constructor(uri: string) { this._eventHandlers = { @@ -22,6 +23,11 @@ export default class Websock { this._websocket = new WebSocket(uri); this._websocket.onmessage = this._recv_message.bind(this); this._websocket.binaryType = "arraybuffer"; + this._latency = new Date().getTime(); + } + + latency(): number { + return this._latency; } sendMessage(data: any) { @@ -63,6 +69,7 @@ export default class Websock { } }, timeout); this._websocket.onopen = () => { + this._latency = new Date().getTime() - this._latency; this._status = "open"; console.debug(">> WebSock.onopen"); if (this._websocket?.protocol) { @@ -72,28 +79,26 @@ export default class Websock { } this._eventHandlers.open(); - console.debug("<< WebSock.onopen"); + console.info("WebSock.onopen"); resolve(this); }; this._websocket.onclose = (e) => { this._status = e; - console.debug(">> WebSock.onclose"); + console.error("WebSock.onclose: " + e); this._eventHandlers.close(e); - console.debug("<< WebSock.onclose"); reject(e); }; this._websocket.onerror = (e) => { this._status = e; - console.debug(">> WebSock.onerror: " + e); + console.error("WebSock.onerror: " + e); this._eventHandlers.error(e); - console.debug("<< WebSock.onerror: " + e); reject(e); }; }); } async next(timeout = 12000): Promise { - let func = ( + const func = ( resolve: (value: Uint8Array) => void, reject: (reason: any) => void, tm0: number @@ -102,7 +107,7 @@ export default class Websock { resolve(this._buf[0]); this._buf.splice(0, 1); } else { - if (this._status != 'open') { + if (this._status != "open") { reject(this._status); return; } @@ -134,7 +139,7 @@ export default class Websock { _recv_message(e: any) { if (e.data instanceof window.ArrayBuffer) { - let bytes = new Uint8Array(e.data); + const bytes = new Uint8Array(e.data); this._buf.push(bytes); } this._eventHandlers.message(e.data); @@ -146,13 +151,3 @@ export default class Websock { return hasher.digest(); } } - -let ws = new Websock("ws://207.148.17.15:21118"); -await ws.open(); -console.log("ws connected"); -// let punchHole = rendezvous.PunchHoleRequest.fromJSON({ id: '' }); -// ws.send_rendezvous(rendezvous.RendezvousMessage.fromJSON({ punchHole })); -let testNatRequest = rendezvous.TestNatRequest.fromJSON({ serial: 0 }); -ws.sendRendezvous({ testNatRequest }); -let msg = ws.parseRendezvous(await ws.next()); -console.log(msg); From 50c8650c63c574e7a703b8f076fbb3ead7e0f165 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 20 Jan 2022 01:55:57 +0800 Subject: [PATCH 213/422] test ui --- src/client.ts | 4 ++-- src/main.ts | 10 ++-------- src/ui.js | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 10 deletions(-) create mode 100644 src/ui.js diff --git a/src/client.ts b/src/client.ts index bc8170783..0154242cb 100644 --- a/src/client.ts +++ b/src/client.ts @@ -96,5 +96,5 @@ async function testDelay() { console.log(ws.latency()); } -await testDelay(); -await new Client().connect("124931507"); +testDelay(); +new Client().connect("124931507"); diff --git a/src/main.ts b/src/main.ts index f1c79723d..2f573e805 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,9 +1,3 @@ -import './style.css'; +import "./style.css"; import "./client"; - -const app = document.querySelector('#app')! - -app.innerHTML = ` -

    Hello Vite!

    - Documentation -` +import "./ui"; \ No newline at end of file diff --git a/src/ui.js b/src/ui.js new file mode 100644 index 000000000..e718e7488 --- /dev/null +++ b/src/ui.js @@ -0,0 +1,42 @@ +const app = document.querySelector("#app"); + +if (app) { + app.innerHTML = ` +
    + + + +
    Host:
    Id:
    + +`; + + document.body.onload = () => { + const host = document.querySelector('#host'); + host.value = localStorage.getItem('host'); + const id = document.querySelector('#id'); + id.value = localStorage.getItem('id'); + }; + + window.connect = () => { + const host = document.querySelector('#host'); + localStorage.setItem('host', host.value); + const id = document.querySelector('#id'); + localStorage.setItem('id', id.value); + document.querySelector('div#connect').style.display = 'none'; + document.querySelector('div#password').style.display = 'block'; + } + + window.cancel = () => { + document.querySelector('div#connect').style.display = 'block'; + document.querySelector('div#password').style.display = 'none'; + } + + window.confirm = () => { + // + } + +} \ No newline at end of file From 63242a4f3ab8d7e62381d5d9d35dbd99b6235cf3 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 20 Jan 2022 02:27:49 +0800 Subject: [PATCH 214/422] refactor --- src/{client.ts => connection.ts} | 47 +++++++++++++++++++------------- src/globals.js | 9 ++++++ src/main.ts | 3 +- src/ui.js | 8 ++++++ 4 files changed, 46 insertions(+), 21 deletions(-) rename src/{client.ts => connection.ts} (69%) create mode 100644 src/globals.js diff --git a/src/client.ts b/src/connection.ts similarity index 69% rename from src/client.ts rename to src/connection.ts index 0154242cb..22746a8ed 100644 --- a/src/client.ts +++ b/src/connection.ts @@ -3,14 +3,12 @@ import * as message from "./message.js"; import * as rendezvous from "./rendezvous.js"; import { loadVp9, loadOpus } from "./codec"; -const URI = "ws://207.148.17.15"; -const PORT = 21118; +const PORT = 21116; +const HOST = "rs-sg.rustdesk.com"; const licenceKey = ""; +const SCHEMA = "ws://"; -loadVp9(); -loadOpus(); - -export default class Client { +export default class Connection { _msgs: any[]; _ws: Websock | undefined; _interval: any; @@ -18,7 +16,7 @@ export default class Client { constructor() { this._msgs = []; - this._id = ''; + this._id = ""; this._interval = setInterval(() => { while (this._msgs.length) { this._ws?.sendMessage(this._msgs[0]); @@ -32,8 +30,8 @@ export default class Client { this._ws?.close(); } - async connect(id: string) { - const ws = new Websock(URI + ":" + PORT); + async start(id: string) { + const ws = new Websock(getDefaultUri()); this._ws = ws; this._id = id; await ws.open(); @@ -65,17 +63,15 @@ export default class Client { async connectRelay(rr: rendezvous.RelayResponse) { const pk = rr.pk; let uri = rr.relayServer; - if (uri.indexOf(':') > 0) { - const tmp = uri.split(':'); - const port = parseInt(tmp[1]); - uri = tmp[0] + ':' + (port + 2); + if (uri) { + uri = getrUriFromRs(uri); } else { - uri += ':' + (PORT + 1); + uri = getDefaultUri(true); } const uuid = rr.uuid; - const ws = new Websock('ws://' + uri); + const ws = new Websock(uri); await ws.open(); - console.log('Connected to relay server') + console.log("Connected to relay server"); this._ws = ws; const requestRelay = rendezvous.RequestRelay.fromJSON({ licenceKey, @@ -91,10 +87,23 @@ export default class Client { } async function testDelay() { - const ws = new Websock(URI + ":" + PORT); + const ws = new Websock(getDefaultUri(false)); await ws.open(); console.log(ws.latency()); } -testDelay(); -new Client().connect("124931507"); +function getDefaultUri(isRelay: Boolean = false): string { + const host = localStorage.getItem("host"); + return SCHEMA + (host || HOST) + ":" + (PORT + (isRelay ? 3 : 2)); +} + +function getrUriFromRs(uri: string): string { + if (uri.indexOf(":") > 0) { + const tmp = uri.split(":"); + const port = parseInt(tmp[1]); + uri = tmp[0] + ":" + (port + 2); + } else { + uri += ":" + (PORT + 3); + } + return uri; +} diff --git a/src/globals.js b/src/globals.js new file mode 100644 index 000000000..56d4defe2 --- /dev/null +++ b/src/globals.js @@ -0,0 +1,9 @@ +window.currentConnection = undefined; + +export function setConn(conn) { + window.currentConnection = conn; +} + +export function getConn() { + return windows.currentConnection; +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 2f573e805..2be877f58 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,3 +1,2 @@ -import "./style.css"; -import "./client"; +import "./globals"; import "./ui"; \ No newline at end of file diff --git a/src/ui.js b/src/ui.js index e718e7488..570265c57 100644 --- a/src/ui.js +++ b/src/ui.js @@ -1,9 +1,13 @@ +import "./style.css"; +import "./connection"; + const app = document.querySelector("#app"); if (app) { app.innerHTML = `
    +
    Host:
    Key:
    Id:
    @@ -19,6 +23,8 @@ if (app) { host.value = localStorage.getItem('host'); const id = document.querySelector('#id'); id.value = localStorage.getItem('id'); + const key = document.querySelector('#key'); + key.value = localStorage.getItem('key'); }; window.connect = () => { @@ -26,6 +32,8 @@ if (app) { localStorage.setItem('host', host.value); const id = document.querySelector('#id'); localStorage.setItem('id', id.value); + const key = document.querySelector('#key'); + localStorage.setItem('key', key.value); document.querySelector('div#connect').style.display = 'none'; document.querySelector('div#password').style.display = 'block'; } From ce0f1f75de9bd7d7947879b8d03153de131e5021 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 20 Jan 2022 12:49:57 +0800 Subject: [PATCH 215/422] sodium verify works --- package.json | 1 + src/connection.ts | 12 +- src/globals.js | 23 +++- src/ui.js | 4 +- yarn.lock | 271 +--------------------------------------------- 5 files changed, 40 insertions(+), 271 deletions(-) diff --git a/package.json b/package.json index ae337fb4b..eeba1248b 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "dependencies": { "fast-sha256": "^1.3.0", "libsodium": "^0.7.9", + "libsodium-wrappers": "^0.7.9", "ogv": "^1.8.6", "ts-proto": "^1.101.0" } diff --git a/src/connection.ts b/src/connection.ts index 22746a8ed..172f4f673 100644 --- a/src/connection.ts +++ b/src/connection.ts @@ -2,6 +2,7 @@ import Websock from "./websock"; import * as message from "./message.js"; import * as rendezvous from "./rendezvous.js"; import { loadVp9, loadOpus } from "./codec"; +import * as globals from "./globals"; const PORT = 21116; const HOST = "rs-sg.rustdesk.com"; @@ -16,7 +17,7 @@ export default class Connection { constructor() { this._msgs = []; - this._id = ""; + this._id = ''; this._interval = setInterval(() => { while (this._msgs.length) { this._ws?.sendMessage(this._msgs[0]); @@ -82,7 +83,10 @@ export default class Connection { } async secure(pk: Uint8Array | undefined) { - // + if (pk) { + const RS_PK = 'OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw='; + let pk_id = await globals.verify(pk, RS_PK); + } } } @@ -105,5 +109,5 @@ function getrUriFromRs(uri: string): string { } else { uri += ":" + (PORT + 3); } - return uri; -} + return SCHEMA + uri; +} \ No newline at end of file diff --git a/src/globals.js b/src/globals.js index 56d4defe2..38989f85a 100644 --- a/src/globals.js +++ b/src/globals.js @@ -1,3 +1,6 @@ +import Connection from "./connection"; +import _sodium from "libsodium-wrappers"; + window.currentConnection = undefined; export function setConn(conn) { @@ -6,4 +9,22 @@ export function setConn(conn) { export function getConn() { return windows.currentConnection; -} \ No newline at end of file +} + +export async function startConn(id) { + const conn = new Connection(); + setConn(conn); + await conn.start('124931507'); +} + +let sodium; +export async function verify(signed, pk) { + if (!sodium) { + await _sodium.ready; + sodium = _sodium; + } + pk = sodium.from_base64(pk, sodium.base64_variants.ORIGINAL); + return sodium.crypto_sign_open(signed, pk); +} + +window.startConn = startConn; \ No newline at end of file diff --git a/src/ui.js b/src/ui.js index 570265c57..9bae1326c 100644 --- a/src/ui.js +++ b/src/ui.js @@ -1,7 +1,8 @@ import "./style.css"; import "./connection"; +import { startConn } from "./globals"; -const app = document.querySelector("#app"); +const app = document.querySelector('#app'); if (app) { app.innerHTML = ` @@ -36,6 +37,7 @@ if (app) { localStorage.setItem('key', key.value); document.querySelector('div#connect').style.display = 'none'; document.querySelector('div#password').style.display = 'block'; + startConn(id); } window.cancel = () => { diff --git a/yarn.lock b/yarn.lock index 22a489545..2e61c8dbf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -77,66 +77,11 @@ resolved "https://registry.yarnpkg.com/@types/object-hash/-/object-hash-1.3.4.tgz#079ba142be65833293673254831b5e3e847fe58b" integrity sha512-xFdpkAkikBgqBdG9vIlsqffDV8GpvnPEzs0IUtr1v3BEB97ijsFQ4RXVbUZwjFThhB4MDSTUfvmxUD5PGx0wXA== -call-bind@^1.0.0, call-bind@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== - dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" - -chnl@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/chnl/-/chnl-1.2.0.tgz#d818c95367767a0880508e7cc0b5b3503f58fd4c" - integrity sha512-g5gJb59edwCliFbX2j7G6sBfY4sX9YLy211yctONI2GRaiX0f2zIbKWmBm+sPqFNEpM7Ljzm7IJX/xrjiEbPrw== - dataloader@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-1.4.0.tgz#bca11d867f5d3f1b9ed9f737bd15970c65dff5c8" integrity sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw== -define-properties@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== - dependencies: - object-keys "^1.0.12" - -es-abstract@^1.19.1: - version "1.19.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" - integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== - dependencies: - call-bind "^1.0.2" - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - get-intrinsic "^1.1.1" - get-symbol-description "^1.0.0" - has "^1.0.3" - has-symbols "^1.0.2" - internal-slot "^1.0.3" - is-callable "^1.2.4" - is-negative-zero "^2.0.1" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.1" - is-string "^1.0.7" - is-weakref "^1.0.1" - object-inspect "^1.11.0" - object-keys "^1.1.1" - object.assign "^4.1.2" - string.prototype.trimend "^1.0.4" - string.prototype.trimstart "^1.0.4" - unbox-primitive "^1.0.1" - -es-to-primitive@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" - integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" - esbuild-android-arm64@0.13.15: version "0.13.15" resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.13.15.tgz#3fc3ff0bab76fe35dd237476b5d2b32bb20a3d44" @@ -260,40 +205,6 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" - integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - -get-symbol-description@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" - integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" - -has-bigints@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" - integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== - -has-symbols@^1.0.1, has-symbols@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" - integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== - -has-tostringtag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" - integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== - dependencies: - has-symbols "^1.0.2" - has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -301,35 +212,6 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" -internal-slot@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" - integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== - dependencies: - get-intrinsic "^1.1.0" - has "^1.0.3" - side-channel "^1.0.4" - -is-bigint@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" - integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== - dependencies: - has-bigints "^1.0.1" - -is-boolean-object@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" - integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-callable@^1.1.4, is-callable@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" - integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== - is-core-module@^2.8.0: version "2.8.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" @@ -337,60 +219,14 @@ is-core-module@^2.8.0: dependencies: has "^1.0.3" -is-date-object@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" - integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== +libsodium-wrappers@^0.7.9: + version "0.7.9" + resolved "https://registry.yarnpkg.com/libsodium-wrappers/-/libsodium-wrappers-0.7.9.tgz#4ffc2b69b8f7c7c7c5594a93a4803f80f6d0f346" + integrity sha512-9HaAeBGk1nKTRFRHkt7nzxqCvnkWTjn1pdjKgcUnZxj0FyOP4CnhgFhMdrFfgNsukijBGyBLpP2m2uKT1vuWhQ== dependencies: - has-tostringtag "^1.0.0" + libsodium "^0.7.0" -is-negative-zero@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" - integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== - -is-number-object@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" - integrity sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g== - dependencies: - has-tostringtag "^1.0.0" - -is-regex@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" - integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-shared-array-buffer@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" - integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== - -is-string@^1.0.5, is-string@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" - integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== - dependencies: - has-tostringtag "^1.0.0" - -is-symbol@^1.0.2, is-symbol@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" - integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== - dependencies: - has-symbols "^1.0.2" - -is-weakref@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" - integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== - dependencies: - call-bind "^1.0.2" - -libsodium@^0.7.9: +libsodium@^0.7.0, libsodium@^0.7.9: version "0.7.9" resolved "https://registry.yarnpkg.com/libsodium/-/libsodium-0.7.9.tgz#4bb7bcbf662ddd920d8795c227ae25bbbfa3821b" integrity sha512-gfeADtR4D/CM0oRUviKBViMGXZDgnFdMKMzHsvBdqLBHd9ySi6EtYnmuhHVDDYgYpAO8eU8hEY+F8vIUAPh08A== @@ -415,26 +251,6 @@ object-hash@^1.3.1: resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df" integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA== -object-inspect@^1.11.0, object-inspect@^1.9.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" - integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== - -object-keys@^1.0.12, object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object.assign@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" - integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - has-symbols "^1.0.1" - object-keys "^1.1.1" - ogv@^1.8.6: version "1.8.6" resolved "https://registry.yarnpkg.com/ogv/-/ogv-1.8.6.tgz#0e6fa1c166802e3a405b1b318d8eb27d2544cce9" @@ -466,25 +282,6 @@ prettier@^2.5.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== -promise-controller@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/promise-controller/-/promise-controller-1.0.0.tgz#81ebea71271aa40ac8f3bebccab3d4158dc4cc02" - integrity sha512-goA0zA9L91tuQbUmiMinSYqlyUtEgg4fxJcjYnLYOQnrktb4o4UqciXDNXiRUPiDBPACmsr1k8jDW4r7UDq9Qw== - -promise.prototype.finally@^3.1.2: - version "3.1.3" - resolved "https://registry.yarnpkg.com/promise.prototype.finally/-/promise.prototype.finally-3.1.3.tgz#d3186e58fcf4df1682a150f934ccc27b7893389c" - integrity sha512-EXRF3fC9/0gz4qkt/f5EP5iW4kj9oFpBICNpCNOb/52+8nlHIX07FPLbi/q4qYBQ1xZqivMzTpNQSnArVASolQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - -promised-map@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/promised-map/-/promised-map-1.0.0.tgz#aa3ec59e13724d37b946415f6850273af8e213b5" - integrity sha512-fP9VSMgcml+U2uJ9PBc4/LDQ3ZkJCH4blLNCS6gbH7RHyRZCYs91zxWHqiUy+heFiEMiB2op/qllYoFqmIqdWA== - protobufjs@^6.8.8: version "6.11.2" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.2.tgz#de39fabd4ed32beaa08e9bb1e30d08544c1edf8b" @@ -525,36 +322,11 @@ rollup@^2.59.0: optionalDependencies: fsevents "~2.3.2" -side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== - dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" - source-map-js@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.1.tgz#a1741c131e3c77d048252adfa24e23b908670caf" integrity sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA== -string.prototype.trimend@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" - integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - -string.prototype.trimstart@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" - integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" @@ -593,16 +365,6 @@ typescript@^4.4.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.4.tgz#a17d3a0263bf5c8723b9c52f43c5084edf13c2e8" integrity sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg== -unbox-primitive@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" - integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== - dependencies: - function-bind "^1.1.1" - has-bigints "^1.0.1" - has-symbols "^1.0.2" - which-boxed-primitive "^1.0.2" - vite@^2.7.2: version "2.7.12" resolved "https://registry.yarnpkg.com/vite/-/vite-2.7.12.tgz#7784ab19e7ff98f6a192d2d7d877480a8c2b7e7d" @@ -614,24 +376,3 @@ vite@^2.7.2: rollup "^2.59.0" optionalDependencies: fsevents "~2.3.2" - -websocket-as-promised@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/websocket-as-promised/-/websocket-as-promised-2.0.1.tgz#d18b7c160bba294585aaf6fa9572f31e05cfb175" - integrity sha512-ePV26D/D37ughXU9j+DjGmwUbelWJrC/vi+6GK++fRlBJmS7aU9T8ABu47KFF0O7r6XN2NAuqJRpegbUwXZxQg== - dependencies: - chnl "^1.2.0" - promise-controller "^1.0.0" - promise.prototype.finally "^3.1.2" - promised-map "^1.0.0" - -which-boxed-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" - integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== - dependencies: - is-bigint "^1.0.1" - is-boolean-object "^1.1.0" - is-number-object "^1.0.4" - is-string "^1.0.5" - is-symbol "^1.0.3" From 70c213a60acb523428de463d63c2d5a55ab9d5a2 Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 20 Jan 2022 15:41:11 +0800 Subject: [PATCH 216/422] secure connection works --- src/connection.ts | 88 +++++++++++++++++++++++++++++++++++++---------- src/globals.js | 32 ++++++++++++++++- src/websock.ts | 9 +++-- 3 files changed, 108 insertions(+), 21 deletions(-) diff --git a/src/connection.ts b/src/connection.ts index 172f4f673..f0cd100d1 100644 --- a/src/connection.ts +++ b/src/connection.ts @@ -1,13 +1,13 @@ -import Websock from "./websock"; -import * as message from "./message.js"; -import * as rendezvous from "./rendezvous.js"; -import { loadVp9, loadOpus } from "./codec"; -import * as globals from "./globals"; +import Websock from './websock'; +import * as message from './message.js'; +import * as rendezvous from './rendezvous.js'; +import { loadVp9, loadOpus } from './codec'; +import * as globals from './globals'; const PORT = 21116; -const HOST = "rs-sg.rustdesk.com"; -const licenceKey = ""; -const SCHEMA = "ws://"; +const HOST = 'rs-sg.rustdesk.com'; +const licenceKey = ''; +const SCHEMA = 'ws://'; export default class Connection { _msgs: any[]; @@ -38,7 +38,7 @@ export default class Connection { await ws.open(); const connType = rendezvous.ConnType.DEFAULT_CONN; const natType = rendezvous.NatType.SYMMETRIC; - const punchHoleRequest = rendezvous.PunchHoleRequest.fromJSON({ + const punchHoleRequest = rendezvous.PunchHoleRequest.fromPartial({ id, licenceKey, connType, @@ -72,9 +72,9 @@ export default class Connection { const uuid = rr.uuid; const ws = new Websock(uri); await ws.open(); - console.log("Connected to relay server"); + console.log('Connected to relay server'); this._ws = ws; - const requestRelay = rendezvous.RequestRelay.fromJSON({ + const requestRelay = rendezvous.RequestRelay.fromPartial({ licenceKey, uuid, }); @@ -85,8 +85,60 @@ export default class Connection { async secure(pk: Uint8Array | undefined) { if (pk) { const RS_PK = 'OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw='; - let pk_id = await globals.verify(pk, RS_PK); + try { + pk = await globals.verify(pk, RS_PK).catch(); + if (pk?.length != 32) { + pk = undefined; + } + } catch (e) { + console.error(e); + pk = undefined; + } + if (!pk) console.error('Handshake failed: invalid public key from rendezvous server'); } + if (!pk) { + // send an empty message out in case server is setting up secure and waiting for first message + await this._ws?.sendMessage({}); + return; + } + const msg = this._ws?.parseMessage(await this._ws?.next()); + let signedId: any = msg?.signedId; + if (!signedId) { + console.error("Handshake failed: invalid message type"); + await this._ws?.sendMessage({}); + return; + } + try { + signedId = await globals.verify(signedId.id, Uint8Array.from(pk!)); + } catch (e) { + console.error(e); + // fall back to non-secure connection in case pk mismatch + console.error("pk mismatch, fall back to non-secure"); + const publicKey = message.PublicKey.fromPartial({}); + await this._ws?.sendMessage({ publicKey }); + return; + } + signedId = new TextDecoder().decode(signedId!); + const tmp = signedId.split('\0'); + const id = tmp[0]; + let theirPk = tmp[1]; + if (id != this._id!) { + console.error("Handshake failed: sign failure"); + await this._ws?.sendMessage({}); + return; + } + theirPk = globals.decodeBase64(theirPk); + if (theirPk.length != 32) { + console.error("Handshake failed: invalid public box key length from peer"); + await this._ws?.sendMessage({}); + return; + } + const [mySk, asymmetricValue] = globals.genBoxKeyPair(); + const secretKey = globals.genSecretKey(); + const symmetricValue = globals.seal(secretKey, theirPk, mySk); + const publicKey = message.PublicKey.fromPartial({ asymmetricValue, symmetricValue }); + await this._ws?.sendMessage({ publicKey }); + this._ws?.setSecretKey(secretKey) } } @@ -97,17 +149,17 @@ async function testDelay() { } function getDefaultUri(isRelay: Boolean = false): string { - const host = localStorage.getItem("host"); - return SCHEMA + (host || HOST) + ":" + (PORT + (isRelay ? 3 : 2)); + const host = localStorage.getItem('host'); + return SCHEMA + (host || HOST) + ':' + (PORT + (isRelay ? 3 : 2)); } function getrUriFromRs(uri: string): string { - if (uri.indexOf(":") > 0) { - const tmp = uri.split(":"); + if (uri.indexOf(':') > 0) { + const tmp = uri.split(':'); const port = parseInt(tmp[1]); - uri = tmp[0] + ":" + (port + 2); + uri = tmp[0] + ':' + (port + 2); } else { - uri += ":" + (PORT + 3); + uri += ':' + (PORT + 3); } return SCHEMA + uri; } \ No newline at end of file diff --git a/src/globals.js b/src/globals.js index 38989f85a..f804f5278 100644 --- a/src/globals.js +++ b/src/globals.js @@ -23,8 +23,38 @@ export async function verify(signed, pk) { await _sodium.ready; sodium = _sodium; } - pk = sodium.from_base64(pk, sodium.base64_variants.ORIGINAL); + if (typeof pk == 'string') { + pk = decodeBase64(pk); + } return sodium.crypto_sign_open(signed, pk); } +export function decodeBase64(pk) { + return sodium.from_base64(pk, sodium.base64_variants.ORIGINAL); +} + +export function genBoxKeyPair() { + const pair = sodium.crypto_box_keypair(); + const sk = pair.privateKey; + const pk = pair.publicKey; + return [sk, pk]; +} + +export function genSecretKey() { + return sodium.crypto_secretbox_keygen(); +} + +export function seal(unsigned, theirPk, ourSk) { + const nonce = Uint8Array.from(Array(24).fill(0)); + return sodium.crypto_box_easy(unsigned, nonce, theirPk, ourSk); +} + +export function encrypt(unsigned, nonce, key) { + return sodium.crypto_secretbox_easy(unsigned, nonce, key); +} + +export function decrypt(signed, nonce, key) { + return sodium.crypto_secretbox_open_easy(signed, nonce, key); +} + window.startConn = startConn; \ No newline at end of file diff --git a/src/websock.ts b/src/websock.ts index 231bfafa8..f09cc181e 100644 --- a/src/websock.ts +++ b/src/websock.ts @@ -10,6 +10,7 @@ export default class Websock { _buf: Uint8Array[]; _status: any; _latency: number; + _secretKey: any; constructor(uri: string) { this._eventHandlers = { @@ -30,16 +31,20 @@ export default class Websock { return this._latency; } + setSecretKey(key: any) { + this._secretKey = key; + } + sendMessage(data: any) { this._websocket.send( - message.Message.encode(message.Message.fromJSON(data)).finish() + message.Message.encode(message.Message.fromPartial(data)).finish() ); } sendRendezvous(data: any) { this._websocket.send( rendezvous.RendezvousMessage.encode( - rendezvous.RendezvousMessage.fromJSON(data) + rendezvous.RendezvousMessage.fromPartial(data) ).finish() ); } From 668b34c22807b6ea9a7eaec7498a83226ced5e3d Mon Sep 17 00:00:00 2001 From: csf Date: Thu, 20 Jan 2022 15:57:54 +0800 Subject: [PATCH 217/422] video service 0.1 --- android/app/build.gradle | 2 +- android/app/src/main/AndroidManifest.xml | 19 +- .../com/carriez/flutter_hbb/MainActivity.kt | 102 ++++++- .../com/carriez/flutter_hbb/MainService.kt | 248 ++++++++++++++++++ android_doc.md | 151 +++++++++++ build_android.sh | 3 + lib/home_page.dart | 17 ++ pubspec.lock | 16 +- 8 files changed, 542 insertions(+), 16 deletions(-) create mode 100644 android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt create mode 100644 android_doc.md diff --git a/android/app/build.gradle b/android/app/build.gradle index 2eeab0b0a..9aa6a16d0 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -33,7 +33,7 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { compileSdkVersion 30 - + // ndkVersion '22.1.7171670' // * 仅个人使用 存在多版本NDK无法自动选择 需要使用此配置指定NDK版本 [CSF] sourceSets { main.java.srcDirs += 'src/main/kotlin' } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 567c1ebbb..e096196e9 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -28,17 +28,24 @@ android:resource="@drawable/launch_background" /> - - + + + - - - - + + + + + + + diff --git a/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt b/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt index 0f2e8d208..393e96a98 100644 --- a/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt +++ b/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt @@ -1,6 +1,106 @@ package com.carriez.flutter_hbb +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.media.projection.MediaProjectionManager +import android.os.Build +import android.os.Bundle +import android.os.PersistableBundle +import android.util.Log +import androidx.annotation.RequiresApi import io.flutter.embedding.android.FlutterActivity +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import java.nio.ByteBuffer +import kotlin.concurrent.thread -class MainActivity: FlutterActivity() { + +class MainActivity : FlutterActivity() { + private val channelTag = "mChannel" + private var mediaProjectionResultIntent: Intent? = null + private val requestCode = 1 + private val buf = ByteBuffer.allocate(16) + + init { + System.loadLibrary("rustdesk") + } + + override fun configureFlutterEngine(flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) // 必要 否则无法正确初始化flutter + + MethodChannel( + flutterEngine.dartExecutor.binaryMessenger, + channelTag + ).setMethodCallHandler { call, result -> + when (call.method) { + "getPer" -> { + Log.d(channelTag, "event from flutter,getPer") + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getMediaProjection() + } + result.success(true) + } + "startSer" -> { + mStarService() + result.success(true) + } + "stopSer" -> { + mStopService() + result.success(true) + } + else -> {} + } + } + } + + + @RequiresApi(Build.VERSION_CODES.LOLLIPOP) + private fun getMediaProjection() { + val mMediaProjectionManager = + getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager + val mIntent = mMediaProjectionManager.createScreenCaptureIntent() + startActivityForResult(mIntent, requestCode) + } + + private fun mStarService() { + if (mediaProjectionResultIntent == null) { + Log.w(channelTag, "mediaProjectionResultIntent is null") + return + } + Log.d(channelTag, "Start a service") + val serviceIntent = Intent(this, MainService::class.java) + serviceIntent.action = START_SERVICE + serviceIntent.putExtra(EXTRA_MP_DATA, mediaProjectionResultIntent) + + // TEST api < O + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + startForegroundService(serviceIntent) + } else { + startService(serviceIntent) + } + } + + private fun mStopService() { + Log.d(channelTag, "Stop service") + val serviceIntent = Intent(this, MainService::class.java) + + serviceIntent.action = STOP_SERVICE + + // TEST api < O + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + startForegroundService(serviceIntent) + } else { + startService(serviceIntent) + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (resultCode == Activity.RESULT_OK && data != null) { + Log.d(channelTag, "got mediaProjectionResultIntent ok") + mediaProjectionResultIntent = data + } + } } diff --git a/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt b/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt new file mode 100644 index 000000000..77b2f9c9e --- /dev/null +++ b/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt @@ -0,0 +1,248 @@ +package com.carriez.flutter_hbb + +import android.annotation.SuppressLint +import android.app.* +import android.content.Context +import android.content.Intent +import android.graphics.Color +import android.graphics.PixelFormat +import android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC +import android.media.* +import android.media.projection.MediaProjection +import android.media.projection.MediaProjectionManager +import android.os.Build +import android.os.IBinder +import android.util.Log +import android.view.Surface +import android.widget.Toast +import androidx.annotation.RequiresApi +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationCompat.PRIORITY_MIN +import java.nio.ByteBuffer +import java.util.concurrent.Executors + +const val EXTRA_MP_DATA = "mp_intent" +const val START_SERVICE = "start_service" +const val STOP_SERVICE = "stop_service" +const val MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_VP9 + +// 获取手机尺寸 建立连接时发送尺寸和基础信息 +const val FIXED_WIDTH = 500 // 编码器有上限 +const val FIXED_HEIGHT = 1000 +const val M_KEY_BIT_RATE = 1024_000 +const val M_KEY_FRAME_RATE = 30 + +class MainService : Service() { + + fun rustGetRaw():ByteArray{ + return rawByteArray!! + } + + external fun init(ctx:Context) + + init { + System.loadLibrary("rustdesk") + } + + private val logTag = "LOG_SERVICE" + private var mMediaProjection: MediaProjection? = null + private var surface: Surface? = null + private val singleThread = Executors.newSingleThreadExecutor() + private var mEncoder: MediaCodec? = null + private var rawByteArray :ByteArray? = null + + override fun onBind(intent: Intent): IBinder? { + return null + } + + @RequiresApi(Build.VERSION_CODES.LOLLIPOP) + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + Log.d("whichService", "this service:${Thread.currentThread()}") + init(this) // 注册到rust + if (intent?.action == START_SERVICE) { + Log.d(logTag, "service starting:${startId}:${Thread.currentThread()}") + createNotification() + val mMediaProjectionManager = + getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager + mMediaProjection = intent.getParcelableExtra(EXTRA_MP_DATA)?.let { + mMediaProjectionManager.getMediaProjection(Activity.RESULT_OK, it) + } + Log.d(logTag, "获取mMediaProjection成功$mMediaProjection") + if (testSupport()) { + startRecorder() + } else { + Toast.makeText(this, "此设备不支持:$MIME_TYPE", Toast.LENGTH_SHORT).show() + stopSelf(startId) + } + } else if (intent?.action == STOP_SERVICE) { + mEncoder?.let { + try { + Log.d(logTag, "正在释放encoder") + it.signalEndOfInputStream() + it.stop() + it.release() + } catch (e: Exception) { + null + } + } + stopSelf() + } + return super.onStartCommand(intent, flags, startId) + } + + lateinit var mImageReader:ImageReader // * 注意 这里要成为成员变量,防止被回收 https://www.cnblogs.com/yongdaimi/p/11004560.html + @SuppressLint("WrongConstant") + @RequiresApi(Build.VERSION_CODES.LOLLIPOP) + private fun startRecorder() { + Log.d(logTag, "startRecorder") + mMediaProjection?.let { mp -> + // 使用原始数据 + mImageReader = + ImageReader.newInstance(FIXED_WIDTH, FIXED_HEIGHT, PixelFormat.RGBA_8888, 2) // 至少是2 + mImageReader.setOnImageAvailableListener({ imageReader: ImageReader -> + Log.d(logTag, "on image") + try { + imageReader.acquireLatestImage().use { image -> + if (image == null) return@setOnImageAvailableListener + val planes = image.planes + val buffer = planes[0].buffer + buffer.rewind() + // 这里注意 处理不当会引发OOM + if (rawByteArray == null){ + rawByteArray = ByteArray(buffer.capacity()) + buffer.get(rawByteArray!!) + }else{ + buffer.get(rawByteArray!!) + } + } + } catch (ignored: java.lang.Exception) { + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + imageReader.discardFreeBuffers() + } + }, null) + mp.createVirtualDisplay( + "rustdesk test", + FIXED_WIDTH, FIXED_HEIGHT, 200, VIRTUAL_DISPLAY_FLAG_PUBLIC, + mImageReader.surface, null, null + ) + + + // 使用内置编码器 +// createMediaCodec() +// mEncoder?.let { +// surface = it.createInputSurface() +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { +// surface!!.setFrameRate(1F, FRAME_RATE_COMPATIBILITY_DEFAULT) +// } +// it.setCallback(cb) +// it.start() +// mp.createVirtualDisplay( +// "rustdesk test", +// FIXED_WIDTH, FIXED_HEIGHT, 200, VIRTUAL_DISPLAY_FLAG_PUBLIC, +// surface, null, null +// ) +// } + } ?: let { + Log.d(logTag, "startRecorder fail,mMediaProjection is null") + } + } + + private val cb: MediaCodec.Callback = @RequiresApi(Build.VERSION_CODES.LOLLIPOP) + object : MediaCodec.Callback() { + override fun onInputBufferAvailable(codec: MediaCodec, index: Int) {} + override fun onOutputFormatChanged(codec: MediaCodec, format: MediaFormat) {} + + override fun onOutputBufferAvailable( + codec: MediaCodec, + index: Int, + info: MediaCodec.BufferInfo + ) { + codec.getOutputBuffer(index)?.let { buf -> + singleThread.execute { + // TODO 优化内存使用方式 + val byteArray = ByteArray(buf.limit()) + buf.get(byteArray) + sendVp9(byteArray) + codec.releaseOutputBuffer(index, false) + } + } + } + + override fun onError(codec: MediaCodec, e: MediaCodec.CodecException) { + Log.e(logTag, "MediaCodec.Callback error:$e") + } + } + + external fun sendRaw(buf: ByteBuffer) + external fun sendVp9(data: ByteArray) + + @RequiresApi(Build.VERSION_CODES.LOLLIPOP) + private fun testSupport(): Boolean { + val res = MediaCodecList(MediaCodecList.ALL_CODECS) + .findEncoderForFormat( + MediaFormat.createVideoFormat( + MediaFormat.MIMETYPE_VIDEO_VP9, + FIXED_WIDTH, + FIXED_HEIGHT + ) + ) + return res?.let { + true + } ?: let { + false + } + } + + private fun createMediaCodec() { + Log.d(logTag, "MediaFormat.MIMETYPE_VIDEO_VP9 :$MIME_TYPE") + mEncoder = MediaCodec.createEncoderByType(MIME_TYPE) + val mFormat = MediaFormat.createVideoFormat(MIME_TYPE, FIXED_WIDTH, FIXED_HEIGHT) + mFormat.setInteger(MediaFormat.KEY_BIT_RATE, M_KEY_BIT_RATE) + mFormat.setInteger(MediaFormat.KEY_FRAME_RATE, M_KEY_FRAME_RATE) // codec的帧率设置无效 + mFormat.setInteger( + MediaFormat.KEY_COLOR_FORMAT, + MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible + ) + mFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5) + try { + mEncoder!!.configure(mFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE) + } catch (e: Exception) { + Log.e(logTag, "mEncoder.configure fail!") + } + } + + private fun createNotification() { + val channelId = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + createNotificationChannel("my_service", "My Background Service") + } else { + "" + } + val notification: Notification = NotificationCompat.Builder(this, channelId) + .setOngoing(true) + .setContentTitle("Hello") + .setPriority(PRIORITY_MIN) + .setContentText("TEST TEST") + .build() + startForeground(11, notification) + } + + @RequiresApi(Build.VERSION_CODES.O) + private fun createNotificationChannel(channelId: String, channelName: String): String { + val chan = NotificationChannel( + channelId, + channelName, NotificationManager.IMPORTANCE_NONE + ) + chan.lightColor = Color.BLUE + chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE + val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + service.createNotificationChannel(chan) + return channelId + } + + override fun onDestroy() { + Log.d(logTag, "service stop:${Thread.currentThread()}") + Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show() + } +} \ No newline at end of file diff --git a/android_doc.md b/android_doc.md new file mode 100644 index 000000000..a809c8a9d --- /dev/null +++ b/android_doc.md @@ -0,0 +1,151 @@ +# RustDesk 安卓端被控文档记录 + +### 1.获取屏幕录像 + +##### 原理 流程 +MediaProjectionManager -> MediaProjection +-> VirtualDisplay -> Surface -> MediaCodec + +- 获取mediaProjectionResultIntent + - **必须activity** + - activity获取mediaProjectionResultIntent + - 会提示用户 “获取屏幕录制权限” + +- 获取MediaProjection + - **必须service** + - 将mediaProjectionResultIntent 传递到后台服务 + - 通过后台服务获取MediaProjection + +- 创建Surface(理解为一个buf)和Surface消费者 + - MediaCodec生成Surface传入VirtualDisplay的入参中 + - 设定编码等各类参数 + +- 获取VirtualDisplay(Surface 生产者) + - 前台服务 + - MediaProjection createVirtualDisplay方法创建VirtualDisplay + - 创建VirtualDisplay的入参之一是Surface + - 需要设定正确的VirtualDisplay尺寸 + +- 获取编码后的buf + - 通过MediaCodec回调获取到可用的数据 +- 通过jni传入Rust服务 + - 直接通过jni调用rust端的函数,将数据传递给video_service中 + +- 安卓VP9兼容性待测试 + - 目前测试2017年一台安卓7机器不支持vp9硬件加速 + - **安卓内置的编解码器并不一定是硬件解码** + +##### 权限注意 +``` + + + + +``` +- API大于O(26)时需要startForegroundService,且需要正确设置通知栏, + 新特性中使用ForegroundService不会被系统杀掉 + + +##### 资料 +- 关于 FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION 权限 + https://zhuanlan.zhihu.com/p/360356420 + +- 关于Notification 和 NotificationNotification + https://stackoverflow.com/questions/47531742/startforeground-fail-after-upgrade-to-android-8-1 + https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context) + +// TODO 使用 NotificationCompat 的区别 + +
    + +### 2.获取控制 +暂时可行的方案是使用安卓无障碍服务 参考droidVNC项目,但droidVNC的实现并不完善,droidVNC没有实现连续触控。 + +#### 无障碍服务获取权限 +- https://developer.android.com/guide/topics/ui/accessibility/service?hl=zh-cn#manifest +- 清单文件 + ``` + + + + + + + + ``` +- 创建一个单独的xml文件,用于无障碍服务配置 + ``` + // 首先清单文件中增加文件地址 + + ... + + + // 然后在此位置添加xml + // /res/xml/accessibility_service_config.xml + + ``` +- 连续手势 https://developer.android.com/guide/topics/ui/accessibility/service?hl=zh-cn#continued-gestures + +
    + +### 其他 +- Kotlin 与 compose 版本设置问题 + - https://stackoverflow.com/questions/67600344/jetpack-compose-on-kotlin-1-5-0 + - 在根目录的gradle中 设置两个正确对应版本 + +### Rust JVM 互相调用 + +rust端 引入 jni crate +https://docs.rs/jni/0.19.0/jni/index.html + +Kotlin端 +类中通过init{} 引入lib的调用 +```kotlin +class Main{ + init{ + System.loadLibrary("$libname") + } +} +``` + +Rust端 +使用jni规则进行函数命名 +```rust +pub unsafe extern "system" fn Java_com_carriez_flutter_1hbb_MainActivity_init( + env: JNIEnv, + class: JClass, + ctx:JObject, +){ + +} +``` +- 注意,原项目包名flutter_hbb 带有下划线,通过安卓的编译提示获得的命名方式为如上。 + +- 将安卓的对象实例(Context)在init的过程中传入rust端, +context通过env.new_global_ref()变成全局引用 +env.get_java_vm()获取到jvm + - 原理上 Rust端通过类找静态方法也可行,但在kotlin端测试失败,会遇到类名找不到,类静态方法找不到等问题,目前仅使用绑定具体context对象即可。 +- 将jvm和context 固定到全局变量中等待需要时候引用 + +- 使用时,需要确保jvm与当前的线程绑定 +jvm.attach_current_thread_permanently() + +- 然后通过jvm获得env +jvm.get_env() + +- 通过env.call_method()方法传入context.as_obj()使用对象的方法 + +传递数据 +Kotlin 中的 ByteArray 类 会在JVM中编译成为java的byte[] +byte[]通过jni传递到rust端时 +通过jni.rs的方法 +env.convert_byte_array()即可转化为Vec \ No newline at end of file diff --git a/build_android.sh b/build_android.sh index b4ba09671..4f4038593 100755 --- a/build_android.sh +++ b/build_android.sh @@ -2,3 +2,6 @@ $ANDROID_NDK/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-strip android/app/src/main/jniLibs/arm64-v8a/* flutter build apk --target-platform android-arm64 --release --obfuscate --split-debug-info ./split-debug-info flutter build appbundle --target-platform android-arm64 --release --obfuscate --split-debug-info ./split-debug-info + +# build in linux +# $ANDROID_NDK/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-strip android/app/src/main/jniLibs/arm64-v8a/* diff --git a/lib/home_page.dart b/lib/home_page.dart index 19ca26339..70f444703 100644 --- a/lib/home_page.dart +++ b/lib/home_page.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import 'package:tuple/tuple.dart'; import 'package:package_info/package_info.dart'; @@ -21,6 +22,7 @@ class HomePage extends StatefulWidget { class _HomePageState extends State { final _idController = TextEditingController(); var _updateUrl = ''; + static const toAndroidChannel = MethodChannel("mChannel"); @override void initState() { @@ -96,9 +98,24 @@ class _HomePageState extends State { fontWeight: FontWeight.bold)))), getSearchBarUI(), getPeers(), + ElevatedButton(onPressed:_toAndroidGetPer, child: Text("获取权限事件")), + ElevatedButton(onPressed:_toAndroidStartSer, child: Text("开启录屏服务")), + ElevatedButton(onPressed:_toAndroidStopSer, child: Text("停止录屏服务")) ]), )); } + Future _toAndroidGetPer() async{ + bool res = await toAndroidChannel.invokeMethod("getPer"); + debugPrint("_toAndroidGetPer:$res"); + } + Future _toAndroidStartSer() async{ + bool res = await toAndroidChannel.invokeMethod("startSer"); + debugPrint("_toAndroidStartSer:$res"); + } + Future _toAndroidStopSer() async{ + bool res = await toAndroidChannel.invokeMethod("stopSer"); + debugPrint("_toAndroidStopSer:$res"); + } void onConnect() { var id = _idController.text.trim(); diff --git a/pubspec.lock b/pubspec.lock index 7f7f91a5d..4ec2db940 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -21,7 +21,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.6.1" + version: "2.8.2" boolean_selector: dependency: transitive description: @@ -35,14 +35,14 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.1" clock: dependency: transitive description: @@ -227,14 +227,14 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10" + version: "0.12.11" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.7.0" nested: dependency: transitive description: @@ -428,7 +428,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.3.0" + version: "0.4.3" tuple: dependency: "direct main" description: @@ -491,7 +491,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.1" wakelock: dependency: "direct main" description: @@ -556,5 +556,5 @@ packages: source: hosted version: "3.1.0" sdks: - dart: ">=2.13.0 <3.0.0" + dart: ">=2.14.0 <3.0.0" flutter: ">=2.0.0" From b8ff266d76895a23dde358ee030c275d037ba89f Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 20 Jan 2022 18:02:20 +0800 Subject: [PATCH 218/422] msgLoop works --- src/connection.ts | 57 ++++++++++++++++++++++++++++++++++++++++++++--- src/globals.js | 32 +++++++++++++++++++------- src/ui.js | 37 ++++++++++++++++++++++++++---- src/websock.ts | 33 ++++++++++++++------------- 4 files changed, 129 insertions(+), 30 deletions(-) diff --git a/src/connection.ts b/src/connection.ts index f0cd100d1..a2311e123 100644 --- a/src/connection.ts +++ b/src/connection.ts @@ -2,6 +2,7 @@ import Websock from './websock'; import * as message from './message.js'; import * as rendezvous from './rendezvous.js'; import { loadVp9, loadOpus } from './codec'; +import * as sha256 from "fast-sha256"; import * as globals from './globals'; const PORT = 21116; @@ -9,11 +10,15 @@ const HOST = 'rs-sg.rustdesk.com'; const licenceKey = ''; const SCHEMA = 'ws://'; +type MsgboxCallback = (type: string, title: string, text: string) => void; + export default class Connection { _msgs: any[]; _ws: Websock | undefined; _interval: any; _id: string; + _hash: message.Hash | undefined; + _msgbox: MsgboxCallback | undefined; constructor() { this._msgs = []; @@ -31,11 +36,18 @@ export default class Connection { this._ws?.close(); } + setMsgbox(callback: MsgboxCallback) { + this._msgbox = callback; + } + async start(id: string) { - const ws = new Websock(getDefaultUri()); + const uri = getDefaultUri(); + const ws = new Websock(uri); this._ws = ws; this._id = id; + console.log(new Date() + ': Conntecting to rendezvoous server: ' + uri); await ws.open(); + console.log(new Date() + ': Connected to rendezvoous server'); const connType = rendezvous.ConnType.DEFAULT_CONN; const natType = rendezvous.NatType.SYMMETRIC; const punchHoleRequest = rendezvous.PunchHoleRequest.fromPartial({ @@ -46,15 +58,27 @@ export default class Connection { }); ws.sendRendezvous({ punchHoleRequest }); const msg = ws.parseRendezvous(await ws.next()); + ws.close(); + console.log(new Date() + ': Got relay response'); const phr = msg.punchHoleResponse; const rr = msg.relayResponse; if (phr) { if (phr.failure != rendezvous.PunchHoleResponse_Failure.UNKNOWN) { switch (phr?.failure) { case rendezvous.PunchHoleResponse_Failure.ID_NOT_EXIST: + this.msgbox('error', 'Error', 'ID does not exist'); break; + case rendezvous.PunchHoleResponse_Failure.OFFLINE: + this.msgbox('error', 'Error', 'Remote desktop is offline'); + break; + case rendezvous.PunchHoleResponse_Failure.LICENSE_MISMATCH: + this.msgbox('error', 'Error', 'Key mismatch'); + break; + default: + if (phr?.otherFailure) { + this.msgbox('error', 'Error', phr?.otherFailure); + } } - ws.close(); } } else if (rr) { await this.connectRelay(rr); @@ -70,9 +94,10 @@ export default class Connection { uri = getDefaultUri(true); } const uuid = rr.uuid; + console.log(new Date() + ': Connecting to relay server: ' + uri); const ws = new Websock(uri); await ws.open(); - console.log('Connected to relay server'); + console.log(new Date() + ': Connected to relay server'); this._ws = ws; const requestRelay = rendezvous.RequestRelay.fromPartial({ licenceKey, @@ -80,6 +105,7 @@ export default class Connection { }); ws.sendRendezvous({ requestRelay }); await this.secure(pk); + await this.msgLoop(); } async secure(pk: Uint8Array | undefined) { @@ -140,6 +166,25 @@ export default class Connection { await this._ws?.sendMessage({ publicKey }); this._ws?.setSecretKey(secretKey) } + + async msgLoop() { + while (true) { + const msg = this._ws?.parseMessage(await this._ws?.next()); + if (msg?.hash) { + this._hash = msg?.hash; + this.msgbox("input-password", "Password Required", ""); + } else if (msg?.testDelay) { + const testDelay = msg?.testDelay; + if (!testDelay.fromClient) { + await this._ws?.sendMessage({ testDelay }); + } + } + } + } + + msgbox(type_: string, title: string, text: string) { + this._msgbox?.(type_, title, text); + } } async function testDelay() { @@ -162,4 +207,10 @@ function getrUriFromRs(uri: string): string { uri += ':' + (PORT + 3); } return SCHEMA + uri; +} + +function hash(datas: [string]): Uint8Array { + const hasher = new sha256.Hash(); + datas.forEach((data) => hasher.update(new TextEncoder().encode(data))); + return hasher.digest(); } \ No newline at end of file diff --git a/src/globals.js b/src/globals.js index f804f5278..cd3828dec 100644 --- a/src/globals.js +++ b/src/globals.js @@ -8,13 +8,19 @@ export function setConn(conn) { } export function getConn() { - return windows.currentConnection; + return window.currentConnection; } -export async function startConn(id) { +export function close() { + getConn()?.close(); + setConn(undefined); +} + +export function newConn() { + window.currentConnection?.close(); const conn = new Connection(); setConn(conn); - await conn.start('124931507'); + return conn; } let sodium; @@ -49,12 +55,22 @@ export function seal(unsigned, theirPk, ourSk) { return sodium.crypto_box_easy(unsigned, nonce, theirPk, ourSk); } +function makeOnce(value) { + var byteArray = Array(24).fill(0); + + for (var index = 0; index < byteArray.length && value > 0; index++) { + var byte = value & 0xff; + byteArray[index] = byte; + value = (value - byte) / 256; + } + + return Uint8Array.from(byteArray); +}; + export function encrypt(unsigned, nonce, key) { - return sodium.crypto_secretbox_easy(unsigned, nonce, key); + return sodium.crypto_secretbox_easy(unsigned, makeOnce(nonce), key); } export function decrypt(signed, nonce, key) { - return sodium.crypto_secretbox_open_easy(signed, nonce, key); -} - -window.startConn = startConn; \ No newline at end of file + return sodium.crypto_secretbox_open_easy(signed, makeOnce(nonce), key); +} \ No newline at end of file diff --git a/src/ui.js b/src/ui.js index 9bae1326c..e2495d1b3 100644 --- a/src/ui.js +++ b/src/ui.js @@ -1,6 +1,6 @@ import "./style.css"; import "./connection"; -import { startConn } from "./globals"; +import * as globals from "./globals"; const app = document.querySelector('#app'); @@ -17,6 +17,10 @@ if (app) { + `; document.body.onload = () => { @@ -35,14 +39,39 @@ if (app) { localStorage.setItem('id', id.value); const key = document.querySelector('#key'); localStorage.setItem('key', key.value); - document.querySelector('div#connect').style.display = 'none'; - document.querySelector('div#password').style.display = 'block'; - startConn(id); + const func = async () => { + const conn = globals.newConn(); + conn.setMsgbox(msgbox); + document.querySelector('div#status').style.display = 'block'; + document.querySelector('div#connect').style.display = 'none'; + document.querySelector('div#text').innerHTML = 'Connecting ...'; + try { + await conn.start(id.value); + } catch (e) { + msgbox('error', 'Error', e); + } + }; + func(); + } + + function msgbox(type, title, text) { + if (!globals.getConn()) return; + if (type == 'input-password') { + document.querySelector('div#status').style.display = 'none'; + document.querySelector('div#password').style.display = 'block'; + } else if (!type) { + document.querySelector('div#status').style.display = 'none'; + } else { + document.querySelector('div#status').style.display = 'block'; + document.querySelector('div#text').innerHTML = '
    ' + text + '
    '; + } } window.cancel = () => { + globals.close(); document.querySelector('div#connect').style.display = 'block'; document.querySelector('div#password').style.display = 'none'; + document.querySelector('div#status').style.display = 'none'; } window.confirm = () => { diff --git a/src/websock.ts b/src/websock.ts index f09cc181e..1ce2c45e7 100644 --- a/src/websock.ts +++ b/src/websock.ts @@ -1,6 +1,6 @@ import * as message from "./message.js"; import * as rendezvous from "./rendezvous.js"; -import * as sha256 from "fast-sha256"; +import * as globals from "./globals"; type Keys = "message" | "open" | "close" | "error"; @@ -10,7 +10,7 @@ export default class Websock { _buf: Uint8Array[]; _status: any; _latency: number; - _secretKey: any; + _secretKey: [Uint8Array, number, number] | undefined; constructor(uri: string) { this._eventHandlers = { @@ -31,14 +31,18 @@ export default class Websock { return this._latency; } - setSecretKey(key: any) { - this._secretKey = key; + setSecretKey(key: Uint8Array) { + this._secretKey = [key, 0, 0]; } - sendMessage(data: any) { - this._websocket.send( - message.Message.encode(message.Message.fromPartial(data)).finish() - ); + sendMessage(json: any) { + let data = message.Message.encode(message.Message.fromPartial(json)).finish(); + let k = this._secretKey; + if (k) { + k[1] += 1; + data = globals.encrypt(data, k[1], k[0]); + } + this._websocket.send(data); } sendRendezvous(data: any) { @@ -144,15 +148,14 @@ export default class Websock { _recv_message(e: any) { if (e.data instanceof window.ArrayBuffer) { - const bytes = new Uint8Array(e.data); + let bytes = new Uint8Array(e.data); + const k = this._secretKey; + if (k) { + k[2] += 1; + bytes = globals.decrypt(bytes, k[2], k[0]); + } this._buf.push(bytes); } this._eventHandlers.message(e.data); } - - hash(datas: [Uint8Array]): Uint8Array { - const hasher = new sha256.Hash(); - datas.forEach((data) => hasher.update(data)); - return hasher.digest(); - } } From 72083647857cf4786688bd4f8383bc771fc36ec3 Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 20 Jan 2022 18:41:35 +0800 Subject: [PATCH 219/422] login ok --- src/connection.ts | 64 +++++++++++++++++++++++++++++++++++++++-------- src/ui.js | 16 ++++++++---- 2 files changed, 64 insertions(+), 16 deletions(-) diff --git a/src/connection.ts b/src/connection.ts index a2311e123..1e207f6bf 100644 --- a/src/connection.ts +++ b/src/connection.ts @@ -19,6 +19,7 @@ export default class Connection { _id: string; _hash: message.Hash | undefined; _msgbox: MsgboxCallback | undefined; + _peerInfo: message.PeerInfo | undefined; constructor() { this._msgs = []; @@ -31,15 +32,6 @@ export default class Connection { }, 1); } - close() { - clearInterval(this._interval); - this._ws?.close(); - } - - setMsgbox(callback: MsgboxCallback) { - this._msgbox = callback; - } - async start(id: string) { const uri = getDefaultUri(); const ws = new Websock(uri); @@ -172,19 +164,64 @@ export default class Connection { const msg = this._ws?.parseMessage(await this._ws?.next()); if (msg?.hash) { this._hash = msg?.hash; + await this.handleHash(); this.msgbox("input-password", "Password Required", ""); } else if (msg?.testDelay) { const testDelay = msg?.testDelay; if (!testDelay.fromClient) { await this._ws?.sendMessage({ testDelay }); } + } else if (msg?.loginResponse) { + const r = msg?.loginResponse; + if (r.error) { + this.msgbox('error', 'Error', r.error); + } else if (r.peerInfo) { + this._peerInfo = r.peerInfo; + this.msgbox('success', 'Successful', 'Connected, waiting for image...'); + } } } } + async handleHash() { + await this._sendLoginMessage(); + } + msgbox(type_: string, title: string, text: string) { this._msgbox?.(type_, title, text); } + + close() { + clearInterval(this._interval); + this._ws?.close(); + } + + setMsgbox(callback: MsgboxCallback) { + this._msgbox = callback; + } + + async login(password: string) { + this.msgbox('connecting', 'Connecting...', 'Logging in...'); + let salt = this._hash?.salt; + if (salt) { + let p = hash([password, salt]); + let challenge = this._hash?.challenge; + if (challenge) { + p = hash([p, challenge]); + await this._sendLoginMessage(p); + } + } + } + + async _sendLoginMessage(password: Uint8Array | undefined = undefined) { + const loginRequest = message.LoginRequest.fromPartial({ + username: this._id!, + myId: 'web', // to-do + myName: 'web', // to-do + password, + }); + await this._ws?.sendMessage({ loginRequest }); + } } async function testDelay() { @@ -209,8 +246,13 @@ function getrUriFromRs(uri: string): string { return SCHEMA + uri; } -function hash(datas: [string]): Uint8Array { +function hash(datas: (string | Uint8Array)[]): Uint8Array { const hasher = new sha256.Hash(); - datas.forEach((data) => hasher.update(new TextEncoder().encode(data))); + datas.forEach((data) => { + if (typeof data == 'string') { + data = new TextEncoder().encode(data); + } + return hasher.update(data); + }); return hasher.digest(); } \ No newline at end of file diff --git a/src/ui.js b/src/ui.js index e2495d1b3..f3839cb4b 100644 --- a/src/ui.js +++ b/src/ui.js @@ -14,7 +14,7 @@ if (app) { + `; + let player; + document.body.onload = () => { const host = document.querySelector('#host'); host.value = localStorage.getItem('host'); @@ -30,6 +33,7 @@ if (app) { id.value = localStorage.getItem('id'); const key = document.querySelector('#key'); key.value = localStorage.getItem('key'); + player = YUVCanvas.attach(document.getElementById("player")) }; window.connect = () => { @@ -42,6 +46,9 @@ if (app) { const func = async () => { const conn = globals.newConn(); conn.setMsgbox(msgbox); + conn.setDraw((f) => { + player.drawFrame(f); + }); document.querySelector('div#status').style.display = 'block'; document.querySelector('div#connect').style.display = 'none'; document.querySelector('div#text').innerHTML = 'Connecting ...'; diff --git a/ts_proto.py b/ts_proto.py index d1627f51f..5f8932731 100644 --- a/ts_proto.py +++ b/ts_proto.py @@ -5,16 +5,16 @@ import os path = os.path.abspath(os.path.join(os.getcwd(), '..', 'hbb', 'libs', 'hbb_common', 'protos')) if os.name == 'nt': - cmd = r'protoc --plugin=protoc-gen-ts_proto=.\node_modules\.bin\protoc-gen-ts_proto.cmd -I "%s" --ts_proto_out=./src/ rendezvous.proto'%path + cmd = r'protoc --ts_proto_opt=esModuleInterop=true --plugin=protoc-gen-ts_proto=.\node_modules\.bin\protoc-gen-ts_proto.cmd -I "%s" --ts_proto_out=./src/ rendezvous.proto'%path print(cmd) os.system(cmd) - cmd = r'protoc --plugin=protoc-gen-ts_proto=.\node_modules\.bin\protoc-gen-ts_proto.cmd -I "%s" --ts_proto_out=./src/ message.proto'%path + cmd = r'protoc --ts_proto_opt=esModuleInterop=true --plugin=protoc-gen-ts_proto=.\node_modules\.bin\protoc-gen-ts_proto.cmd -I "%s" --ts_proto_out=./src/ message.proto'%path print(cmd) os.system(cmd) else: - cmd = r'protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto -I "%s" --ts_proto_out=./src/ rendezvous.proto'%path + cmd = r'protoc --ts_proto_opt=esModuleInterop=true --plugin=./node_modules/.bin/protoc-gen-ts_proto -I "%s" --ts_proto_out=./src/ rendezvous.proto'%path print(cmd) os.system(cmd) - cmd = r'protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto -I "%s" --ts_proto_out=./src/ message.proto'%path + cmd = r'protoc --ts_proto_opt=esModuleInterop=true --plugin=./node_modules/.bin/protoc-gen-ts_proto -I "%s" --ts_proto_out=./src/ message.proto'%path print(cmd) - os.system(cmd) \ No newline at end of file + os.system(cmd) From e291e4d1c935a273ea972b898b4a38702cf7539f Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 21 Jan 2022 00:41:02 +0800 Subject: [PATCH 222/422] video works --- src/codec.js | 4 ++-- src/connection.ts | 9 +++++---- src/ui.js | 12 ++++++++++-- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/codec.js b/src/codec.js index 213f2e8fc..da4f0cbdb 100644 --- a/src/codec.js +++ b/src/codec.js @@ -24,7 +24,7 @@ export function loadVp9(callback) { "OGVDecoderVideoVP9W", (videoCodecClass) => { window.videoCodecClass = videoCodecClass; - videoCodecClass().then((decoder) => { + videoCodecClass({ videoFormat: {} }).then((decoder) => { decoder.init(() => { callback(decoder); }) @@ -38,7 +38,7 @@ export function loadOpus(callback) { window.OGVLoader.loadClass( "OGVDecoderAudioOpusW", (audioCodecClass) => { - audioCodecClass().then((decoder) => { + audioCodecClass({ audioFormat: {} }).then((decoder) => { decoder.init(() => { callback(decoder); }) diff --git a/src/connection.ts b/src/connection.ts index a67fbd71a..85f919d40 100644 --- a/src/connection.ts +++ b/src/connection.ts @@ -226,6 +226,8 @@ export default class Connection { close() { clearInterval(this._interval); this._ws?.close(); + this._videoDecoder?.close(); + this._audioDecoder?.close(); } setMsgbox(callback: MsgboxCallback) { @@ -266,11 +268,10 @@ export default class Connection { } if (vf.vp9s) { let dec = this._videoDecoder; + // dec.sync(); vf.vp9s.frames.forEach((f) => { - dec.processFrame(f.data.buffer, (ok: any) => { - console.log(ok); - if (dec.frameBuffer) { - console.log(dec.frameBuffer); + dec.processFrame(f.data.slice(0).buffer, (ok: any) => { + if (ok && dec.frameBuffer) { this.draw(dec.frameBuffer); } }); diff --git a/src/ui.js b/src/ui.js index de602904b..c5c55bf5c 100644 --- a/src/ui.js +++ b/src/ui.js @@ -21,7 +21,10 @@ if (app) {
    - + `; let player; @@ -33,7 +36,7 @@ if (app) { id.value = localStorage.getItem('id'); const key = document.querySelector('#key'); key.value = localStorage.getItem('key'); - player = YUVCanvas.attach(document.getElementById("player")) + player = YUVCanvas.attach(document.getElementById('player')) }; window.connect = () => { @@ -67,11 +70,15 @@ if (app) { document.querySelector('div#status').style.display = 'none'; document.querySelector('div#password').style.display = 'block'; } else if (!type) { + document.querySelector('div#canvas').style.display = 'block'; + document.querySelector('div#password').style.display = 'none'; document.querySelector('div#status').style.display = 'none'; } else if (type == 'error') { document.querySelector('div#status').style.display = 'block'; + document.querySelector('div#canvas').style.display = 'none'; document.querySelector('div#text').innerHTML = '
    ' + text + '
    '; } else { + document.querySelector('div#password').style.display = 'none'; document.querySelector('div#status').style.display = 'block'; document.querySelector('div#text').innerHTML = '
    ' + text + '
    '; } @@ -82,6 +89,7 @@ if (app) { document.querySelector('div#connect').style.display = 'block'; document.querySelector('div#password').style.display = 'none'; document.querySelector('div#status').style.display = 'none'; + document.querySelector('div#canvas').style.display = 'none'; } window.confirm = () => { From c1ff8242d9c2c4f4f177f8f779ad5c8d1d08cc94 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 21 Jan 2022 00:42:33 +0800 Subject: [PATCH 223/422] fix grammer --- src/connection.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/connection.ts b/src/connection.ts index 85f919d40..05df1edda 100644 --- a/src/connection.ts +++ b/src/connection.ts @@ -11,7 +11,7 @@ const licenceKey = ""; const SCHEMA = "ws://"; type MsgboxCallback = (type: string, title: string, text: string) => void; -type DrawCallback = (Uint8Array) => void; +type DrawCallback = (data: Uint8Array) => void; export default class Connection { _msgs: any[]; From 28468042347efb6ec8910c1ce9b47907cf1579a1 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 21 Jan 2022 02:12:38 +0800 Subject: [PATCH 224/422] update protobuf --- src/message.ts | 16 ++-------------- src/rendezvous.ts | 16 ++-------------- 2 files changed, 4 insertions(+), 28 deletions(-) diff --git a/src/message.ts b/src/message.ts index b85556fea..44405e14b 100644 --- a/src/message.ts +++ b/src/message.ts @@ -496,8 +496,7 @@ export function controlKeyToJSON(object: ControlKey): string { } export enum FileType { - UnknownFileType = 0, - Dir = 1, + Dir = 0, DirLink = 2, DirDrive = 3, File = 4, @@ -508,9 +507,6 @@ export enum FileType { export function fileTypeFromJSON(object: any): FileType { switch (object) { case 0: - case "UnknownFileType": - return FileType.UnknownFileType; - case 1: case "Dir": return FileType.Dir; case 2: @@ -534,8 +530,6 @@ export function fileTypeFromJSON(object: any): FileType { export function fileTypeToJSON(object: FileType): string { switch (object) { - case FileType.UnknownFileType: - return "UnknownFileType"; case FileType.Dir: return "Dir"; case FileType.DirLink: @@ -819,8 +813,7 @@ export interface PermissionInfo { } export enum PermissionInfo_Permission { - Unknown = 0, - Keyboard = 1, + Keyboard = 0, Clipboard = 2, Audio = 3, UNRECOGNIZED = -1, @@ -831,9 +824,6 @@ export function permissionInfo_PermissionFromJSON( ): PermissionInfo_Permission { switch (object) { case 0: - case "Unknown": - return PermissionInfo_Permission.Unknown; - case 1: case "Keyboard": return PermissionInfo_Permission.Keyboard; case 2: @@ -853,8 +843,6 @@ export function permissionInfo_PermissionToJSON( object: PermissionInfo_Permission ): string { switch (object) { - case PermissionInfo_Permission.Unknown: - return "Unknown"; case PermissionInfo_Permission.Keyboard: return "Keyboard"; case PermissionInfo_Permission.Clipboard: diff --git a/src/rendezvous.ts b/src/rendezvous.ts index 0ac3b1542..33fab950b 100644 --- a/src/rendezvous.ts +++ b/src/rendezvous.ts @@ -139,8 +139,7 @@ export interface RegisterPkResponse { } export enum RegisterPkResponse_Result { - UNKNOWN = 0, - OK = 1, + OK = 0, UUID_MISMATCH = 2, ID_EXISTS = 3, TOO_FREQUENT = 4, @@ -155,9 +154,6 @@ export function registerPkResponse_ResultFromJSON( ): RegisterPkResponse_Result { switch (object) { case 0: - case "UNKNOWN": - return RegisterPkResponse_Result.UNKNOWN; - case 1: case "OK": return RegisterPkResponse_Result.OK; case 2: @@ -189,8 +185,6 @@ export function registerPkResponse_ResultToJSON( object: RegisterPkResponse_Result ): string { switch (object) { - case RegisterPkResponse_Result.UNKNOWN: - return "UNKNOWN"; case RegisterPkResponse_Result.OK: return "OK"; case RegisterPkResponse_Result.UUID_MISMATCH: @@ -221,8 +215,7 @@ export interface PunchHoleResponse { } export enum PunchHoleResponse_Failure { - UNKNOWN = 0, - ID_NOT_EXIST = 1, + ID_NOT_EXIST = 0, OFFLINE = 2, LICENSE_MISMATCH = 3, LICENSE_OVERUSE = 4, @@ -234,9 +227,6 @@ export function punchHoleResponse_FailureFromJSON( ): PunchHoleResponse_Failure { switch (object) { case 0: - case "UNKNOWN": - return PunchHoleResponse_Failure.UNKNOWN; - case 1: case "ID_NOT_EXIST": return PunchHoleResponse_Failure.ID_NOT_EXIST; case 2: @@ -259,8 +249,6 @@ export function punchHoleResponse_FailureToJSON( object: PunchHoleResponse_Failure ): string { switch (object) { - case PunchHoleResponse_Failure.UNKNOWN: - return "UNKNOWN"; case PunchHoleResponse_Failure.ID_NOT_EXIST: return "ID_NOT_EXIST"; case PunchHoleResponse_Failure.OFFLINE: From 1df3df97b6a258a69f2a61a89b9c677c47729d60 Mon Sep 17 00:00:00 2001 From: open-trade Date: Fri, 21 Jan 2022 14:28:16 +0800 Subject: [PATCH 225/422] fix on grammert --- src/connection.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/connection.ts b/src/connection.ts index 05df1edda..95d992a71 100644 --- a/src/connection.ts +++ b/src/connection.ts @@ -69,7 +69,7 @@ export default class Connection { const phr = msg.punchHoleResponse; const rr = msg.relayResponse; if (phr) { - if (phr.failure != rendezvous.PunchHoleResponse_Failure.UNKNOWN) { + if (phr.failure != rendezvous.PunchHoleResponse_Failure.UNRECOGNIZED) { switch (phr?.failure) { case rendezvous.PunchHoleResponse_Failure.ID_NOT_EXIST: this.msgbox("error", "Error", "ID does not exist"); From 288825f007f63b76a72a3d931077b51fa2ff3a0d Mon Sep 17 00:00:00 2001 From: csf Date: Fri, 21 Jan 2022 22:44:16 +0800 Subject: [PATCH 226/422] update input --- android/app/build.gradle | 2 +- android/app/src/main/AndroidManifest.xml | 111 +++++++++++------- .../com/carriez/flutter_hbb/InputService.kt | 106 +++++++++++++++++ .../com/carriez/flutter_hbb/MainActivity.kt | 25 ++++ .../com/carriez/flutter_hbb/MainService.kt | 39 +++--- android/app/src/main/res/values/strings.xml | 4 + .../res/xml/accessibility_service_config.xml | 6 + android/build.gradle | 2 +- lib/home_page.dart | 7 +- 9 files changed, 237 insertions(+), 65 deletions(-) create mode 100644 android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt create mode 100644 android/app/src/main/res/values/strings.xml create mode 100644 android/app/src/main/res/xml/accessibility_service_config.xml diff --git a/android/app/build.gradle b/android/app/build.gradle index 9aa6a16d0..24d927b25 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -33,7 +33,7 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { compileSdkVersion 30 - // ndkVersion '22.1.7171670' // * 仅个人使用 存在多版本NDK无法自动选择 需要使用此配置指定NDK版本 [CSF] + ndkVersion '22.1.7171670' // * 仅个人使用 存在多版本NDK无法自动选择 需要使用此配置指定NDK版本 [CSF] sourceSets { main.java.srcDirs += 'src/main/kotlin' } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index e096196e9..c83ccf780 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,51 +1,76 @@ + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt b/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt new file mode 100644 index 000000000..6b23eaddd --- /dev/null +++ b/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt @@ -0,0 +1,106 @@ +package com.carriez.flutter_hbb + +import android.accessibilityservice.AccessibilityService +import android.accessibilityservice.GestureDescription +import android.content.Context +import android.graphics.Path +import android.os.Build +import android.util.Log +import android.view.accessibility.AccessibilityEvent +import androidx.annotation.RequiresApi +import kotlin.concurrent.thread + +class InputService : AccessibilityService() { +// companion object { +// var inputService:InputService? = null +// } + private val logTag = "input service" + private var leftIsDown = false + private var mPath = Path() + private var mLastGestureStartTime = 0L + private var mouseX = 0 + private var mouseY = 0 + + @RequiresApi(Build.VERSION_CODES.N) + fun mouseInput(mask: Int, _x: Int, _y: Int) { + Log.w(logTag, "got mouse input:x:$_x ,y:$_y ,mask:$mask ") + + // TODO 临时倍数 + // TODO 按键抬起按下时候 x y 都是0 + if ( !(mask == 9 || mask == 10) ) { + mouseX = _x * 2 + mouseY = _y * 2 + } + + // left button down ,was up + if (mask == 9){ + leftIsDown = true + startGesture(mouseX,mouseY) + } + + // left down ,was down + if (mask == 9){ + continueGesture(mouseX,mouseY) + } + + // left up ,was down + if (mask == 10){ + leftIsDown = false + endGesture(mouseX, mouseY) + } + } + + private fun startGesture(x: Int, y: Int) { + mPath = Path() + mPath.moveTo(x.toFloat(), y.toFloat()) + mLastGestureStartTime = System.currentTimeMillis() + } + + private fun continueGesture(x: Int, y: Int) { + mPath.lineTo(x.toFloat(), y.toFloat()) + } + @RequiresApi(Build.VERSION_CODES.N) + private fun endGesture(x: Int, y: Int) { + mPath.lineTo(x.toFloat(), y.toFloat()) + val stroke = GestureDescription.StrokeDescription( + mPath, + 0, + System.currentTimeMillis() - mLastGestureStartTime + ) + val builder = GestureDescription.Builder() + builder.addStroke(stroke) + Log.d(logTag, "end gesture $x $y") + dispatchGesture(builder.build(), object : GestureResultCallback() { + override fun onCompleted(gestureDescription: GestureDescription) { + super.onCompleted(gestureDescription) + Log.d(logTag, "滑动成功") + } + + override fun onCancelled(gestureDescription: GestureDescription) { + super.onCancelled(gestureDescription) + Log.d(logTag, "滑动失败 ") + } + }, null) + } + + external fun init(ctx: Context) + + init { + System.loadLibrary("rustdesk") + } + + private val LOG_TAG = "INPUT_LOG" + @RequiresApi(Build.VERSION_CODES.O) + override fun onServiceConnected() { + super.onServiceConnected() + Log.d(LOG_TAG,"onServiceConnected!") + init(this) + } + override fun onAccessibilityEvent(event: AccessibilityEvent?) { +// TODO("Not yet implemented") + } + + override fun onInterrupt() { +// TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt b/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt index 393e96a98..372bd3cfe 100644 --- a/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt +++ b/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt @@ -1,12 +1,14 @@ package com.carriez.flutter_hbb import android.app.Activity +import android.app.AlertDialog import android.content.Context import android.content.Intent import android.media.projection.MediaProjectionManager import android.os.Build import android.os.Bundle import android.os.PersistableBundle +import android.provider.Settings import android.util.Log import androidx.annotation.RequiresApi import io.flutter.embedding.android.FlutterActivity @@ -50,6 +52,10 @@ class MainActivity : FlutterActivity() { mStopService() result.success(true) } + "checkInput" ->{ + checkInput() + result.success(true) + } else -> {} } } @@ -96,6 +102,25 @@ class MainActivity : FlutterActivity() { } } + private fun checkInput() { + AlertDialog.Builder(this) + .setCancelable(false) + .setTitle("检查Input服务") + .setMessage("请开启相关服务") + .setPositiveButton("Yes") { dialog, which -> + val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS) + if (intent.resolveActivity(packageManager) != null) startActivityForResult( + intent, + 11 + ) else AlertDialog.Builder(this) + .setTitle("错误") + .setMessage("无法启动服务") + .show() + } + .setNegativeButton("No") { dialog, which -> } + .show() + } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (resultCode == Activity.RESULT_OK && data != null) { diff --git a/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt b/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt index 77b2f9c9e..1c6fba716 100644 --- a/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt +++ b/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt @@ -34,11 +34,11 @@ const val M_KEY_FRAME_RATE = 30 class MainService : Service() { - fun rustGetRaw():ByteArray{ + fun rustGetRaw(): ByteArray { return rawByteArray!! } - external fun init(ctx:Context) + external fun init(ctx: Context) init { System.loadLibrary("rustdesk") @@ -49,7 +49,7 @@ class MainService : Service() { private var surface: Surface? = null private val singleThread = Executors.newSingleThreadExecutor() private var mEncoder: MediaCodec? = null - private var rawByteArray :ByteArray? = null + private var rawByteArray: ByteArray? = null override fun onBind(intent: Intent): IBinder? { return null @@ -90,7 +90,8 @@ class MainService : Service() { return super.onStartCommand(intent, flags, startId) } - lateinit var mImageReader:ImageReader // * 注意 这里要成为成员变量,防止被回收 https://www.cnblogs.com/yongdaimi/p/11004560.html + lateinit var mImageReader: ImageReader // * 注意 这里要成为成员变量,防止被回收 https://www.cnblogs.com/yongdaimi/p/11004560.html + @SuppressLint("WrongConstant") @RequiresApi(Build.VERSION_CODES.LOLLIPOP) private fun startRecorder() { @@ -100,23 +101,23 @@ class MainService : Service() { mImageReader = ImageReader.newInstance(FIXED_WIDTH, FIXED_HEIGHT, PixelFormat.RGBA_8888, 2) // 至少是2 mImageReader.setOnImageAvailableListener({ imageReader: ImageReader -> - Log.d(logTag, "on image") - try { - imageReader.acquireLatestImage().use { image -> - if (image == null) return@setOnImageAvailableListener - val planes = image.planes - val buffer = planes[0].buffer - buffer.rewind() - // 这里注意 处理不当会引发OOM - if (rawByteArray == null){ - rawByteArray = ByteArray(buffer.capacity()) - buffer.get(rawByteArray!!) - }else{ - buffer.get(rawByteArray!!) - } +// Log.d(logTag, "on image") + try { + imageReader.acquireLatestImage().use { image -> + if (image == null) return@setOnImageAvailableListener + val planes = image.planes + val buffer = planes[0].buffer + buffer.rewind() + // 这里注意 处理不当会引发OOM + if (rawByteArray == null) { + rawByteArray = ByteArray(buffer.capacity()) + buffer.get(rawByteArray!!) + } else { + buffer.get(rawByteArray!!) } - } catch (ignored: java.lang.Exception) { } + } catch (ignored: java.lang.Exception) { + } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { imageReader.discardFreeBuffers() } diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml new file mode 100644 index 000000000..9acb4104b --- /dev/null +++ b/android/app/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + RustDesk + 测试服务 输入服务 + \ No newline at end of file diff --git a/android/app/src/main/res/xml/accessibility_service_config.xml b/android/app/src/main/res/xml/accessibility_service_config.xml new file mode 100644 index 000000000..fa9407128 --- /dev/null +++ b/android/app/src/main/res/xml/accessibility_service_config.xml @@ -0,0 +1,6 @@ + diff --git a/android/build.gradle b/android/build.gradle index 110174e8d..18592b640 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -7,7 +7,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:4.1.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10" classpath 'com.google.gms:google-services:4.3.3' } } diff --git a/lib/home_page.dart b/lib/home_page.dart index 70f444703..8d5cde352 100644 --- a/lib/home_page.dart +++ b/lib/home_page.dart @@ -100,7 +100,8 @@ class _HomePageState extends State { getPeers(), ElevatedButton(onPressed:_toAndroidGetPer, child: Text("获取权限事件")), ElevatedButton(onPressed:_toAndroidStartSer, child: Text("开启录屏服务")), - ElevatedButton(onPressed:_toAndroidStopSer, child: Text("停止录屏服务")) + ElevatedButton(onPressed:_toAndroidStopSer, child: Text("停止录屏服务")), + ElevatedButton(onPressed:_toAndroidCheckInput, child: Text("检查输入权限")), ]), )); } @@ -116,6 +117,10 @@ class _HomePageState extends State { bool res = await toAndroidChannel.invokeMethod("stopSer"); debugPrint("_toAndroidStopSer:$res"); } + Future _toAndroidCheckInput() async{ + bool res = await toAndroidChannel.invokeMethod("checkInput"); + debugPrint("_toAndroidStopSer:$res"); +} void onConnect() { var id = _idController.text.trim(); From 39f7835df175fc297138f5c52f03025d28f10ad5 Mon Sep 17 00:00:00 2001 From: csf Date: Sun, 23 Jan 2022 13:59:57 +0800 Subject: [PATCH 227/422] use MultiProvider --- android/app/build.gradle | 2 +- lib/main.dart | 40 ++++++++++++++++++++-------------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 24d927b25..cc4209010 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -33,7 +33,7 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { compileSdkVersion 30 - ndkVersion '22.1.7171670' // * 仅个人使用 存在多版本NDK无法自动选择 需要使用此配置指定NDK版本 [CSF] + ndkVersion '22.1.7171670' // * 仅个人使用 存在多版本NDK无法自动选择 需要使用此配置指定NDK版本 [CSF] sourceSets { main.java.srcDirs += 'src/main/kotlin' } diff --git a/lib/main.dart b/lib/main.dart index 32af66665..c16780ca2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -16,25 +16,25 @@ class App extends StatelessWidget { @override Widget build(BuildContext context) { final analytics = FirebaseAnalytics(); - return ChangeNotifierProvider.value( - value: FFI.ffiModel, - child: ChangeNotifierProvider.value( - value: FFI.imageModel, - child: ChangeNotifierProvider.value( - value: FFI.cursorModel, - child: ChangeNotifierProvider.value( - value: FFI.canvasModel, - child: MaterialApp( - debugShowCheckedModeBanner: false, - title: 'RustDesk', - theme: ThemeData( - primarySwatch: Colors.blue, - visualDensity: VisualDensity.adaptivePlatformDensity, - ), - home: HomePage(title: 'RustDesk'), - navigatorObservers: [ - FirebaseAnalyticsObserver(analytics: analytics), - ], - ))))); + return MultiProvider( + providers: [ + ChangeNotifierProvider.value(value: FFI.ffiModel), + ChangeNotifierProvider.value(value: FFI.imageModel), + ChangeNotifierProvider.value(value: FFI.cursorModel), + ChangeNotifierProvider.value(value: FFI.canvasModel), + ], + child: MaterialApp( + debugShowCheckedModeBanner: false, + title: 'RustDesk', + theme: ThemeData( + primarySwatch: Colors.blue, + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: HomePage(title: 'RustDesk'), + navigatorObservers: [ + FirebaseAnalyticsObserver(analytics: analytics), + ], + ), + ); } } From 6ca487e3e076abb80cb6b1ce7c680c6cd59e28ee Mon Sep 17 00:00:00 2001 From: csf Date: Sun, 23 Jan 2022 21:37:19 +0800 Subject: [PATCH 228/422] add server page --- android_doc.md | 9 ++ lib/home_page.dart | 29 ++---- lib/main.dart | 4 + lib/server_page.dart | 218 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 238 insertions(+), 22 deletions(-) create mode 100644 lib/server_page.dart diff --git a/android_doc.md b/android_doc.md index a809c8a9d..96a18f438 100644 --- a/android_doc.md +++ b/android_doc.md @@ -97,6 +97,15 @@ MediaProjectionManager -> MediaProjection
    +### 3.获取音频输入 +https://developer.android.google.cn/guide/topics/media/playback-capture?hl=zh-cn + +目前谷歌只开放了Android10系统同步音频内录功能 +10之前录音的时候会截取原本系统的音频输出 +即 开启内录时候无法在手机上正常使用耳机扬声器输出 + +
    + ### 其他 - Kotlin 与 compose 版本设置问题 - https://stackoverflow.com/questions/67600344/jetpack-compose-on-kotlin-1-5-0 diff --git a/lib/home_page.dart b/lib/home_page.dart index 8d5cde352..dcb858510 100644 --- a/lib/home_page.dart +++ b/lib/home_page.dart @@ -22,7 +22,6 @@ class HomePage extends StatefulWidget { class _HomePageState extends State { final _idController = TextEditingController(); var _updateUrl = ''; - static const toAndroidChannel = MethodChannel("mChannel"); @override void initState() { @@ -55,15 +54,21 @@ class _HomePageState extends State { items: [ PopupMenuItem( child: Text(translate('ID Server')), + value: 'id_server'), + PopupMenuItem( + child: Text(translate('Share My Screen')), value: 'server'), + // TODO only android PopupMenuItem( child: Text(translate('About') + ' RustDesk'), value: 'about'), ], elevation: 8, ); - if (value == 'server') { + if (value == 'id_server') { showServer(context); + } else if (value == 'server') { + Navigator.pushNamed(context, "server_page"); } else if (value == 'about') { showAbout(context); } @@ -98,29 +103,9 @@ class _HomePageState extends State { fontWeight: FontWeight.bold)))), getSearchBarUI(), getPeers(), - ElevatedButton(onPressed:_toAndroidGetPer, child: Text("获取权限事件")), - ElevatedButton(onPressed:_toAndroidStartSer, child: Text("开启录屏服务")), - ElevatedButton(onPressed:_toAndroidStopSer, child: Text("停止录屏服务")), - ElevatedButton(onPressed:_toAndroidCheckInput, child: Text("检查输入权限")), ]), )); } - Future _toAndroidGetPer() async{ - bool res = await toAndroidChannel.invokeMethod("getPer"); - debugPrint("_toAndroidGetPer:$res"); - } - Future _toAndroidStartSer() async{ - bool res = await toAndroidChannel.invokeMethod("startSer"); - debugPrint("_toAndroidStartSer:$res"); - } - Future _toAndroidStopSer() async{ - bool res = await toAndroidChannel.invokeMethod("stopSer"); - debugPrint("_toAndroidStopSer:$res"); - } - Future _toAndroidCheckInput() async{ - bool res = await toAndroidChannel.invokeMethod("checkInput"); - debugPrint("_toAndroidStopSer:$res"); -} void onConnect() { var id = _idController.text.trim(); diff --git a/lib/main.dart b/lib/main.dart index c16780ca2..62abe4b25 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_hbb/server_page.dart'; import 'package:provider/provider.dart'; import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:firebase_analytics/observer.dart'; @@ -31,6 +32,9 @@ class App extends StatelessWidget { visualDensity: VisualDensity.adaptivePlatformDensity, ), home: HomePage(title: 'RustDesk'), + routes: { + "server_page":(context) => ServerPage(), + }, navigatorObservers: [ FirebaseAnalyticsObserver(analytics: analytics), ], diff --git a/lib/server_page.dart b/lib/server_page.dart new file mode 100644 index 000000000..7e76874b8 --- /dev/null +++ b/lib/server_page.dart @@ -0,0 +1,218 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_hbb/model.dart'; + +import 'common.dart'; + +class ServerPage extends StatefulWidget { + @override + _ServerPageState createState() => _ServerPageState(); +} + +class _ServerPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: MyTheme.grayBg, + appBar: AppBar( + centerTitle: true, + title: const Text("Share My Screen"), + ), + body: SingleChildScrollView( + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + ServerInfo(), + PermissionChecker(), + ], + ), + ), + )); + } +} + +class ServerInfo extends StatefulWidget { + @override + _ServerInfoState createState() => _ServerInfoState(); +} + +class _ServerInfoState extends State { + var _passwdShow = true; + + // TODO set ID / PASSWORD + var _serverId = ""; + var _serverPasswd = ""; + + @override + void initState() { + super.initState(); + _serverId = FFI.getByName("server_id"); + _serverPasswd = FFI.getByName("server_password"); + } + + @override + Widget build(BuildContext context) { + return myCard(Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextFormField( + readOnly: true, + style: TextStyle( + fontSize: 25.0, + fontWeight: FontWeight.bold, + color: MyTheme.accent), + initialValue: _serverId, + decoration: InputDecoration( + icon: const Icon(Icons.perm_identity), + labelText: '服务ID', + labelStyle: + TextStyle(fontWeight: FontWeight.bold, color: MyTheme.accent50), + ), + onSaved: (String value) {}, + ), + TextFormField( + readOnly: true, + obscureText: _passwdShow, + style: TextStyle( + fontSize: 25.0, + fontWeight: FontWeight.bold, + color: MyTheme.accent), + initialValue: _serverPasswd, + decoration: InputDecoration( + icon: const Icon(Icons.lock), + labelText: '密码', + labelStyle: TextStyle( + fontWeight: FontWeight.bold, color: MyTheme.accent50), + suffix: IconButton( + icon: Icon(Icons.visibility), + onPressed: () { + debugPrint("icon btn"); + setState(() { + _passwdShow = !_passwdShow; + }); + })), + onSaved: (String value) {}, + ), + ], + )); + } +} + +class PermissionChecker extends StatefulWidget { + @override + _PermissionCheckerState createState() => _PermissionCheckerState(); +} + +class _PermissionCheckerState extends State { + static const toAndroidChannel = MethodChannel("mChannel"); + + var videoOk = false; + var inputOk = false; + var audioOk = false; + + @override + Widget build(BuildContext context) { + return myCard(Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + cardTitle("权限列表"), + PermissionRow("视频权限", videoOk, _toAndroidGetPer), + const Divider(height: 0), + PermissionRow("音频权限", videoOk, () => {debugPrint("获取视频权限")}), + const Divider(height: 0), + PermissionRow("输入权限", inputOk, _toAndroidCheckInput), + const Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + TextButton.icon( + icon: Icon(Icons.play_arrow), + onPressed: _toAndroidStartSer, + label: Text("Start")), + TextButton.icon( + icon: Icon(Icons.stop), + onPressed: _toAndroidStopSer, + label: Text("Stop")), + ], + ) + ], + )); + } + + Future _toAndroidGetPer() async { + bool res = await toAndroidChannel.invokeMethod("getPer"); + debugPrint("_toAndroidGetPer:$res"); + } + + Future _toAndroidStartSer() async { + bool res = await toAndroidChannel.invokeMethod("startSer"); + debugPrint("_toAndroidStartSer:$res"); + } + + Future _toAndroidStopSer() async { + bool res = await toAndroidChannel.invokeMethod("stopSer"); + debugPrint("_toAndroidStopSer:$res"); + } + + Future _toAndroidCheckInput() async { + bool res = await toAndroidChannel.invokeMethod("checkInput"); + debugPrint("_toAndroidStopSer:$res"); + } +} + +class PermissionRow extends StatelessWidget { + PermissionRow(this.name, this.isOk, this.onPressed); + + final String name; + final bool isOk; + final VoidCallback onPressed; + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text.rich(TextSpan(children: [ + TextSpan( + text: name + ":", + style: TextStyle(fontSize: 16.0, color: MyTheme.accent50)), + TextSpan( + text: isOk ? "已开启" : "未开启", + style: TextStyle( + fontSize: 16.0, color: isOk ? Colors.green : Colors.red)), + ])), + TextButton( + onPressed: onPressed, + child: const Text( + "去开启", + style: TextStyle(fontWeight: FontWeight.bold), + )), + ], + ); + } +} + +Widget cardTitle(String text) { + return Padding( + padding: EdgeInsets.symmetric(vertical: 5.0), + child: Text( + text, + style: TextStyle( + fontFamily: 'WorkSans', + fontWeight: FontWeight.bold, + fontSize: 25, + color: Color(0xFF00B6F0), + ), + )); +} + +Widget myCard(Widget child) { + return Card( + margin: EdgeInsets.all(15.0), + child: Padding( + padding: EdgeInsets.symmetric(vertical: 15.0, horizontal: 30.0), + child: child, + ), + ); +} From 2e73f29ce966ae61f4de9caa7985fc041dda0f3b Mon Sep 17 00:00:00 2001 From: csf Date: Mon, 24 Jan 2022 19:06:53 +0800 Subject: [PATCH 229/422] update android --- .../com/carriez/flutter_hbb/InputService.kt | 10 ++-- .../com/carriez/flutter_hbb/MainActivity.kt | 52 +++++++++++++++---- .../com/carriez/flutter_hbb/MainService.kt | 8 +-- android_doc.md | 15 +++++- lib/server_page.dart | 4 +- 5 files changed, 68 insertions(+), 21 deletions(-) diff --git a/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt b/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt index 6b23eaddd..cf9cfb814 100644 --- a/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt +++ b/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt @@ -11,9 +11,7 @@ import androidx.annotation.RequiresApi import kotlin.concurrent.thread class InputService : AccessibilityService() { -// companion object { -// var inputService:InputService? = null -// } + private val logTag = "input service" private var leftIsDown = false private var mPath = Path() @@ -22,14 +20,14 @@ class InputService : AccessibilityService() { private var mouseY = 0 @RequiresApi(Build.VERSION_CODES.N) - fun mouseInput(mask: Int, _x: Int, _y: Int) { + fun rustMouseInput(mask: Int, _x: Int, _y: Int) { Log.w(logTag, "got mouse input:x:$_x ,y:$_y ,mask:$mask ") // TODO 临时倍数 // TODO 按键抬起按下时候 x y 都是0 if ( !(mask == 9 || mask == 10) ) { - mouseX = _x * 2 - mouseY = _y * 2 + mouseX = _x * SCALE + mouseY = _y * SCALE } // left button down ,was up diff --git a/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt b/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt index 372bd3cfe..f9597073f 100644 --- a/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt +++ b/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt @@ -2,36 +2,34 @@ package com.carriez.flutter_hbb import android.app.Activity import android.app.AlertDialog -import android.content.Context import android.content.Intent import android.media.projection.MediaProjectionManager import android.os.Build -import android.os.Bundle -import android.os.PersistableBundle import android.provider.Settings +import android.util.DisplayMetrics import android.util.Log import androidx.annotation.RequiresApi import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine -import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel -import java.nio.ByteBuffer -import kotlin.concurrent.thread +const val MAX_SIZE = 1400 class MainActivity : FlutterActivity() { private val channelTag = "mChannel" + private val logTag = "mMainActivity" private var mediaProjectionResultIntent: Intent? = null private val requestCode = 1 - private val buf = ByteBuffer.allocate(16) init { System.loadLibrary("rustdesk") } + external fun rustSetInfo(username: String, hostname: String, width: Int, height: Int) + override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) // 必要 否则无法正确初始化flutter - + updateMachineInfo() MethodChannel( flutterEngine.dartExecutor.binaryMessenger, channelTag @@ -52,7 +50,7 @@ class MainActivity : FlutterActivity() { mStopService() result.success(true) } - "checkInput" ->{ + "checkInput" -> { checkInput() result.success(true) } @@ -128,4 +126,40 @@ class MainActivity : FlutterActivity() { mediaProjectionResultIntent = data } } + + private fun updateMachineInfo() { + // 屏幕尺寸 控制最长边不超过1400 超过则减半直到1400 并储存缩放比例 实际发送给手机端的尺寸为缩小后的尺寸 + // input控制时再通过缩放比例恢复原始尺寸进行path入参 + val dm = DisplayMetrics() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + display?.getRealMetrics(dm) + } else { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + @Suppress("DEPRECATION") + windowManager.defaultDisplay.getRealMetrics(dm) + } else { + @Suppress("DEPRECATION") + windowManager.defaultDisplay.getMetrics(dm) + } + } + var w = dm.widthPixels + var h = dm.heightPixels + var scale = 1 + if (w != 0 && h != 0) { + if (w > MAX_SIZE || h > MAX_SIZE) { + scale = 2 + w /= scale + h /= scale + } + Log.d(logTag, "Real size - width:$w,height:$h") + + FIXED_WIDTH = 540 + FIXED_HEIGHT = 1140 + SCALE = scale + // TODO username hostname + rustSetInfo("csf", "Android", FIXED_WIDTH, FIXED_HEIGHT) + } else { + Log.e(logTag, "Got Screen Size Fail!") + } + } } diff --git a/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt b/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt index 1c6fba716..e96b6a617 100644 --- a/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt +++ b/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt @@ -12,8 +12,10 @@ import android.media.projection.MediaProjection import android.media.projection.MediaProjectionManager import android.os.Build import android.os.IBinder +import android.util.DisplayMetrics import android.util.Log import android.view.Surface +import android.view.WindowManager import android.widget.Toast import androidx.annotation.RequiresApi import androidx.core.app.NotificationCompat @@ -26,9 +28,9 @@ const val START_SERVICE = "start_service" const val STOP_SERVICE = "stop_service" const val MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_VP9 -// 获取手机尺寸 建立连接时发送尺寸和基础信息 -const val FIXED_WIDTH = 500 // 编码器有上限 -const val FIXED_HEIGHT = 1000 +var FIXED_WIDTH = 0 // 编码器有上限 +var FIXED_HEIGHT = 0 +var SCALE = 1 // RealScreenWidth = screenWidth * scale const val M_KEY_BIT_RATE = 1024_000 const val M_KEY_FRAME_RATE = 30 diff --git a/android_doc.md b/android_doc.md index 96a18f438..ce44a14d6 100644 --- a/android_doc.md +++ b/android_doc.md @@ -139,6 +139,8 @@ pub unsafe extern "system" fn Java_com_carriez_flutter_1hbb_MainActivity_init( ``` - 注意,原项目包名flutter_hbb 带有下划线,通过安卓的编译提示获得的命名方式为如上。 +- 使用jni的时候会出现无输出崩溃的情况 + - 将安卓的对象实例(Context)在init的过程中传入rust端, context通过env.new_global_ref()变成全局引用 env.get_java_vm()获取到jvm @@ -157,4 +159,15 @@ jvm.get_env() Kotlin 中的 ByteArray 类 会在JVM中编译成为java的byte[] byte[]通过jni传递到rust端时 通过jni.rs的方法 -env.convert_byte_array()即可转化为Vec \ No newline at end of file +env.convert_byte_array()即可转化为Vec + +- 关于 sig + - (I)V : 输入为Int 输出为Void + - (I)Ljava/nio/ByteBuffer : 输入为Int 输出为ByteBuffer 对应jni JByteBuffer 且env 有对应解析方法 + - ()[B : 输入为空 输出为byte[](java) == ByteArray(kotlin) == jbytearray(jni env 中有对应解析方法到Vec\) + - 使用JValue枚举定义java变量 + +# BIG TODO +音频 +连续输入 +服务关闭操作 \ No newline at end of file diff --git a/lib/server_page.dart b/lib/server_page.dart index 7e76874b8..755fa7001 100644 --- a/lib/server_page.dart +++ b/lib/server_page.dart @@ -180,7 +180,7 @@ class PermissionRow extends StatelessWidget { TextSpan( text: isOk ? "已开启" : "未开启", style: TextStyle( - fontSize: 16.0, color: isOk ? Colors.green : Colors.red)), + fontSize: 16.0, color: isOk ? Colors.green : Colors.grey)), ])), TextButton( onPressed: onPressed, @@ -202,7 +202,7 @@ Widget cardTitle(String text) { fontFamily: 'WorkSans', fontWeight: FontWeight.bold, fontSize: 25, - color: Color(0xFF00B6F0), + color: MyTheme.accent80, ), )); } From 3fd0982af57fb62e8f994bedfc546310189df0c3 Mon Sep 17 00:00:00 2001 From: open-trade Date: Tue, 25 Jan 2022 18:13:11 +0800 Subject: [PATCH 230/422] try web --- lib/common.dart | 9 +- lib/home_page.dart | 5 +- lib/main.dart | 2 +- lib/model.dart | 8 +- lib/remote_page.dart | 5 +- lib/web_model.dart | 700 ++++++++++++++++++++++++++++++++ pubspec.lock | 16 +- web/favicon.png | Bin 0 -> 917 bytes web/icons/Icon-192.png | Bin 0 -> 5292 bytes 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 web/index.html | 125 ++++++ web/manifest.json | 35 ++ 14 files changed, 884 insertions(+), 21 deletions(-) create mode 100644 lib/web_model.dart create mode 100644 web/favicon.png create mode 100644 web/icons/Icon-192.png create mode 100644 web/icons/Icon-512.png create mode 100644 web/icons/Icon-maskable-192.png create mode 100644 web/icons/Icon-maskable-512.png create mode 100644 web/index.html create mode 100644 web/manifest.json diff --git a/lib/common.dart b/lib/common.dart index 82c3c754a..410e9ca03 100644 --- a/lib/common.dart +++ b/lib/common.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'dart:async'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:tuple/tuple.dart'; -import 'dart:io'; typedef F = String Function(String); @@ -29,13 +28,13 @@ final ButtonStyle flatButtonStyle = TextButton.styleFrom( ), ); -void Function() loadingCancelCallback = null; +void Function() loadingCancelCallback; void showLoading(String text, BuildContext context) { if (_hasDialog && context != null) { Navigator.pop(context); } dismissLoading(); - if (Platform.isAndroid) { + if (isAndroid) { EasyLoading.show(status: text, maskType: EasyLoadingMaskType.black); return; } @@ -202,3 +201,7 @@ Color str2color(String str, [alpha = 0xFF]) { hash = hash % 16777216; return Color((hash & 0xFF7FFF) | (alpha << 24)); } + +bool isAndroid; +bool isIOS; +bool isWeb; diff --git a/lib/home_page.dart b/lib/home_page.dart index 19ca26339..7f4778c88 100644 --- a/lib/home_page.dart +++ b/lib/home_page.dart @@ -5,9 +5,8 @@ import 'package:package_info/package_info.dart'; import 'package:url_launcher/url_launcher.dart'; import 'dart:async'; import 'common.dart'; -import 'model.dart'; +import 'model.dart' if (dart.library.html) 'web_model.dart'; import 'remote_page.dart'; -import 'dart:io'; class HomePage extends StatefulWidget { HomePage({Key key, this.title}) : super(key: key); @@ -25,7 +24,7 @@ class _HomePageState extends State { @override void initState() { super.initState(); - if (Platform.isAndroid) { + if (isAndroid) { Timer(Duration(seconds: 5), () { _updateUrl = FFI.getByName('software_update_url'); if (_updateUrl.isNotEmpty) setState(() {}); diff --git a/lib/main.dart b/lib/main.dart index 32af66665..29d9ad0ed 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,7 +3,7 @@ import 'package:provider/provider.dart'; import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:firebase_analytics/observer.dart'; import 'package:firebase_core/firebase_core.dart'; -import 'model.dart'; +import 'model.dart' if (dart.library.html) 'web_model.dart'; import 'home_page.dart'; Future main() async { diff --git a/lib/model.dart b/lib/model.dart index db897443d..73dc375ec 100644 --- a/lib/model.dart +++ b/lib/model.dart @@ -1,6 +1,5 @@ import 'package:ffi/ffi.dart'; import 'package:flutter/services.dart'; -import 'package:flutter/gestures.dart'; import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:device_info/device_info.dart'; @@ -44,6 +43,9 @@ class FfiModel with ChangeNotifier { get pi => _pi; FfiModel() { + isIOS = Platform.isIOS; + isAndroid = Platform.isAndroid; + isWeb = false; Translator.call = translate; clear(); () async { @@ -295,7 +297,7 @@ class CanvasModel with ChangeNotifier { notifyListeners(); } - void clear([bool notify=false]) { + void clear([bool notify = false]) { _x = 0; _y = 0; _scale = 1.0; @@ -797,7 +799,7 @@ String translate(String name) { final v = tmp[name]; if (v == null) { var a = 'translate'; - var b = '{"locale": "${Platform.localeName}", "text": "${name}"}'; + var b = '{"locale": "${Platform.localeName}", "text": "$name"}'; return FFI.getByName(a, b); } else { return v; diff --git a/lib/remote_page.dart b/lib/remote_page.dart index 5fdaac60e..d36be7155 100644 --- a/lib/remote_page.dart +++ b/lib/remote_page.dart @@ -7,8 +7,7 @@ import 'dart:async'; import 'package:tuple/tuple.dart'; import 'package:wakelock/wakelock.dart'; import 'common.dart'; -import 'model.dart'; -import 'dart:io'; +import 'model.dart' if (dart.library.html) 'web_model.dart'; final initText = '\1' * 1024; @@ -124,7 +123,7 @@ class _RemotePageState extends State { void handleInput(String newValue) { var oldValue = _value; _value = newValue; - if (Platform.isIOS) { + if (isIOS) { var i = newValue.length - 1; for (; i >= 0 && newValue[i] != '\1'; --i) {} var j = oldValue.length - 1; diff --git a/lib/web_model.dart b/lib/web_model.dart new file mode 100644 index 000000000..c84d20059 --- /dev/null +++ b/lib/web_model.dart @@ -0,0 +1,700 @@ +import 'package:path_provider/path_provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:device_info/device_info.dart'; +import 'dart:math'; +import 'dart:convert'; +import 'dart:typed_data'; +import 'dart:ui' as ui; +import 'package:flutter/material.dart'; +import 'package:tuple/tuple.dart'; +import 'dart:async'; +import 'common.dart'; + +class FfiModel with ChangeNotifier { + PeerInfo _pi; + Display _display; + var _decoding = false; + bool _waitForImage; + bool _initialized = false; + final _permissions = Map(); + bool _secure; + bool _direct; + + get permissions => _permissions; + get initialized => _initialized; + get display => _display; + get secure => _secure; + get direct => _direct; + get pi => _pi; + + FfiModel() { + isIOS = false; + isAndroid = false; + isWeb = true; + Translator.call = translate; + clear(); + () async { + await FFI.init(); + _initialized = true; + print("FFI initialized"); + notifyListeners(); + }(); + } + + void updatePermission(Map evt) { + evt.forEach((k, v) { + if (k == 'name') return; + _permissions[k] = v == 'true'; + }); + print('$_permissions'); + } + + bool keyboard() => _permissions['keyboard'] != false; + + void clear() { + _pi = PeerInfo(); + _display = Display(); + _waitForImage = false; + _secure = null; + _direct = null; + clearPermissions(); + } + + void setConnectionType(bool secure, bool direct) { + _secure = secure; + _direct = direct; + } + + Image getConnectionImage() { + String icon; + if (secure == true && direct == true) { + icon = 'secure'; + } else if (secure == false && direct == true) { + icon = 'insecure'; + } else if (secure == false && direct == false) { + icon = 'insecure_relay'; + } else if (secure == true && direct == false) { + icon = 'secure_relay'; + } + return icon == null + ? null + : Image.asset('assets/$icon.png', width: 48, height: 48); + } + + void clearPermissions() { + _permissions.clear(); + } + + void update( + String id, + BuildContext context, + void Function( + Map evt, + String id, + ) + handleMsgbox) { + var pos; + for (;;) { + var evt = FFI.popEvent(); + if (evt == null) break; + var name = evt['name']; + if (name == 'msgbox') { + handleMsgbox(evt, id); + } else if (name == 'peer_info') { + handlePeerInfo(evt, context); + } else if (name == 'connection_ready') { + FFI.ffiModel.setConnectionType( + evt['secure'] == 'true', evt['direct'] == 'true'); + } else if (name == 'switch_display') { + handleSwitchDisplay(evt); + } else if (name == 'cursor_data') { + FFI.cursorModel.updateCursorData(evt); + } else if (name == 'cursor_id') { + FFI.cursorModel.updateCursorId(evt); + } else if (name == 'cursor_position') { + pos = evt; + } else if (name == 'clipboard') { + } else if (name == 'permission') { + FFI.ffiModel.updatePermission(evt); + } + } + if (pos != null) FFI.cursorModel.updateCursorPosition(pos); + if (!_decoding) { + var rgba = FFI.getRgba(); + if (rgba != null) { + if (_waitForImage) { + _waitForImage = false; + dismissLoading(); + } + _decoding = true; + final pid = FFI.id; + ui.decodeImageFromPixels( + rgba, _display.width, _display.height, ui.PixelFormat.bgra8888, + (image) { + FFI.clearRgbaFrame(); + _decoding = false; + if (FFI.id != pid) return; + try { + // my throw exception, because the listener maybe already dispose + FFI.imageModel.update(image); + } catch (e) { + print('update image: $e'); + } + }); + } + } + } + + void handleSwitchDisplay(Map evt) { + var old = _pi.currentDisplay; + _pi.currentDisplay = int.parse(evt['display']); + _display.x = double.parse(evt['x']); + _display.y = double.parse(evt['y']); + _display.width = int.parse(evt['width']); + _display.height = int.parse(evt['height']); + if (old != _pi.currentDisplay) + FFI.cursorModel.updateDisplayOrigin(_display.x, _display.y); + notifyListeners(); + } + + void handlePeerInfo(Map evt, BuildContext context) { + dismissLoading(); + _pi.version = evt['version']; + _pi.username = evt['username']; + _pi.hostname = evt['hostname']; + _pi.platform = evt['platform']; + _pi.sasEnabled = evt['sas_enabled'] == "true"; + _pi.currentDisplay = int.parse(evt['current_display']); + List displays = json.decode(evt['displays']); + _pi.displays = []; + for (int i = 0; i < displays.length; ++i) { + Map d0 = displays[i]; + var d = Display(); + d.x = d0['x'].toDouble(); + d.y = d0['y'].toDouble(); + d.width = d0['width']; + d.height = d0['height']; + _pi.displays.add(d); + } + if (_pi.currentDisplay < _pi.displays.length) { + _display = _pi.displays[_pi.currentDisplay]; + initializeCursorAndCanvas(); + } + if (displays.length > 0) { + showLoading(translate('Connected, waiting for image...'), context); + _waitForImage = true; + } + notifyListeners(); + } +} + +class ImageModel with ChangeNotifier { + ui.Image _image; + + ui.Image get image => _image; + + void update(ui.Image image) { + if (_image == null && image != null) { + final size = MediaQueryData.fromWindow(ui.window).size; + final xscale = size.width / image.width; + final yscale = size.height / image.height; + FFI.canvasModel.scale = max(xscale, yscale); + } + _image = image; + if (image != null) notifyListeners(); + } + + double get maxScale { + if (_image == null) return 1.0; + final size = MediaQueryData.fromWindow(ui.window).size; + final xscale = size.width / _image.width; + final yscale = size.height / _image.height; + return max(1.0, max(xscale, yscale)); + } + + double get minScale { + if (_image == null) return 1.0; + final size = MediaQueryData.fromWindow(ui.window).size; + final xscale = size.width / _image.width; + final yscale = size.height / _image.height; + return min(xscale, yscale); + } +} + +class CanvasModel with ChangeNotifier { + double _x; + double _y; + double _scale; + + CanvasModel() { + clear(); + } + + double get x => _x; + double get y => _y; + double get scale => _scale; + + void update(double x, double y, double scale) { + _x = x; + _y = y; + _scale = scale; + notifyListeners(); + } + + set scale(v) { + _scale = v; + notifyListeners(); + } + + void panX(double dx) { + _x += dx; + notifyListeners(); + } + + void resetOffset() { + _x = 0; + _y = 0; + notifyListeners(); + } + + void panY(double dy) { + _y += dy; + notifyListeners(); + } + + void updateScale(double v) { + if (FFI.imageModel.image == null) return; + final offset = FFI.cursorModel.offset; + var r = FFI.cursorModel.getVisibleRect(); + final px0 = (offset.dx - r.left) * _scale; + final py0 = (offset.dy - r.top) * _scale; + _scale *= v; + final maxs = FFI.imageModel.maxScale; + final mins = FFI.imageModel.minScale; + if (_scale > maxs) _scale = maxs; + if (_scale < mins) _scale = mins; + r = FFI.cursorModel.getVisibleRect(); + final px1 = (offset.dx - r.left) * _scale; + final py1 = (offset.dy - r.top) * _scale; + _x -= px1 - px0; + _y -= py1 - py0; + notifyListeners(); + } + + void clear([bool notify = false]) { + _x = 0; + _y = 0; + _scale = 1.0; + if (notify) notifyListeners(); + } +} + +class CursorModel with ChangeNotifier { + ui.Image _image; + final _images = Map>(); + double _x = -10000; + double _y = -10000; + double _hotx = 0; + double _hoty = 0; + double _displayOriginX = 0; + double _displayOriginY = 0; + + ui.Image get image => _image; + double get x => _x - _displayOriginX; + double get y => _y - _displayOriginY; + Offset get offset => Offset(_x, _y); + double get hotx => _hotx; + double get hoty => _hoty; + + // remote physical display coordinate + Rect getVisibleRect() { + final size = MediaQueryData.fromWindow(ui.window).size; + final xoffset = FFI.canvasModel.x; + final yoffset = FFI.canvasModel.y; + final scale = FFI.canvasModel.scale; + final x0 = _displayOriginX - xoffset / scale; + final y0 = _displayOriginY - yoffset / scale; + return Rect.fromLTWH(x0, y0, size.width / scale, size.height / scale); + } + + double adjustForKeyboard() { + final m = MediaQueryData.fromWindow(ui.window); + var keyboardHeight = m.viewInsets.bottom; + final size = m.size; + if (keyboardHeight < 100) return 0; + final s = FFI.canvasModel.scale; + final thresh = (size.height - keyboardHeight) / 2; + var h = (_y - getVisibleRect().top) * s; // local physical display height + return h - thresh; + } + + void touch(double x, double y, bool right) { + final scale = FFI.canvasModel.scale; + final xoffset = FFI.canvasModel.x; + final yoffset = FFI.canvasModel.y; + _x = (x - xoffset) / scale + _displayOriginX; + _y = (y - yoffset) / scale + _displayOriginY; + FFI.moveMouse(_x, _y); + FFI.tap(right); + notifyListeners(); + } + + void reset() { + _x = _displayOriginX; + _y = _displayOriginY; + FFI.moveMouse(_x, _y); + FFI.canvasModel.clear(true); + notifyListeners(); + } + + void updatePan(double dx, double dy, bool touchMode, bool drag) { + if (FFI.imageModel.image == null) return; + if (touchMode) { + if (drag) { + final scale = FFI.canvasModel.scale; + _x += dx / scale; + _y += dy / scale; + FFI.moveMouse(_x, _y); + notifyListeners(); + } else { + FFI.canvasModel.panX(dx); + FFI.canvasModel.panY(dy); + } + return; + } + final scale = FFI.canvasModel.scale; + dx /= scale; + dy /= scale; + final r = getVisibleRect(); + var cx = r.center.dx; + var cy = r.center.dy; + var tryMoveCanvasX = false; + if (dx > 0) { + final maxCanvasCanMove = + _displayOriginX + FFI.imageModel.image.width - r.right; + tryMoveCanvasX = _x + dx > cx && maxCanvasCanMove > 0; + if (tryMoveCanvasX) { + dx = min(dx, maxCanvasCanMove); + } else { + final maxCursorCanMove = r.right - _x; + dx = min(dx, maxCursorCanMove); + } + } else if (dx < 0) { + final maxCanvasCanMove = _displayOriginX - r.left; + tryMoveCanvasX = _x + dx < cx && maxCanvasCanMove < 0; + if (tryMoveCanvasX) { + dx = max(dx, maxCanvasCanMove); + } else { + final maxCursorCanMove = r.left - _x; + dx = max(dx, maxCursorCanMove); + } + } + var tryMoveCanvasY = false; + if (dy > 0) { + final mayCanvasCanMove = + _displayOriginY + FFI.imageModel.image.height - r.bottom; + tryMoveCanvasY = _y + dy > cy && mayCanvasCanMove > 0; + if (tryMoveCanvasY) { + dy = min(dy, mayCanvasCanMove); + } else { + final mayCursorCanMove = r.bottom - _y; + dy = min(dy, mayCursorCanMove); + } + } else if (dy < 0) { + final mayCanvasCanMove = _displayOriginY - r.top; + tryMoveCanvasY = _y + dy < cy && mayCanvasCanMove < 0; + if (tryMoveCanvasY) { + dy = max(dy, mayCanvasCanMove); + } else { + final mayCursorCanMove = r.top - _y; + dy = max(dy, mayCursorCanMove); + } + } + + if (dx == 0 && dy == 0) return; + _x += dx; + _y += dy; + if (tryMoveCanvasX && dx != 0) { + FFI.canvasModel.panX(-dx); + } + if (tryMoveCanvasY && dy != 0) { + FFI.canvasModel.panY(-dy); + } + + FFI.moveMouse(_x, _y); + notifyListeners(); + } + + void updateCursorData(Map evt) { + var id = int.parse(evt['id']); + _hotx = double.parse(evt['hotx']); + _hoty = double.parse(evt['hoty']); + var width = int.parse(evt['width']); + var height = int.parse(evt['height']); + List colors = json.decode(evt['colors']); + final rgba = Uint8List.fromList(colors.map((s) => s as int).toList()); + var pid = FFI.id; + ui.decodeImageFromPixels(rgba, width, height, ui.PixelFormat.rgba8888, + (image) { + if (FFI.id != pid) return; + _image = image; + _images[id] = Tuple3(image, _hotx, _hoty); + try { + // my throw exception, because the listener maybe already dispose + notifyListeners(); + } catch (e) { + print('notify cursor: $e'); + } + }); + } + + void updateCursorId(Map evt) { + final tmp = _images[int.parse(evt['id'])]; + if (tmp != null) { + _image = tmp.item1; + _hotx = tmp.item2; + _hoty = tmp.item3; + notifyListeners(); + } + } + + void updateCursorPosition(Map evt) { + _x = double.parse(evt['x']); + _y = double.parse(evt['y']); + notifyListeners(); + } + + void updateDisplayOrigin(double x, double y) { + _displayOriginX = x; + _displayOriginY = y; + _x = x + 1; + _y = y + 1; + FFI.moveMouse(x, y); + FFI.canvasModel.resetOffset(); + notifyListeners(); + } + + void updateDisplayOriginWithCursor( + double x, double y, double xCursor, double yCursor) { + _displayOriginX = x; + _displayOriginY = y; + _x = xCursor; + _y = yCursor; + FFI.moveMouse(x, y); + notifyListeners(); + } + + void clear() { + _x = -10000; + _x = -10000; + _image = null; + _images.clear(); + } +} + +class FFI { + static String id = ""; + static String _dir = ''; + static var shift = false; + static var ctrl = false; + static var alt = false; + static var command = false; + static final imageModel = ImageModel(); + static final ffiModel = FfiModel(); + static final cursorModel = CursorModel(); + static final canvasModel = CanvasModel(); + + static String getId() { + return getByName('remote_id'); + } + + static void tap(bool right) { + sendMouse('down', right ? 'right' : 'left'); + sendMouse('up', right ? 'right' : 'left'); + } + + static void scroll(double y) { + var y2 = y.round(); + if (y2 == 0) return; + setByName('send_mouse', + json.encode(modify({'type': 'wheel', 'y': y2.toString()}))); + } + + static void reconnect() { + setByName('reconnect'); + FFI.ffiModel.clearPermissions(); + } + + static void resetModifiers() { + shift = ctrl = alt = command = false; + } + + static Map modify(Map evt) { + if (ctrl) evt['ctrl'] = 'true'; + if (shift) evt['shift'] = 'true'; + if (alt) evt['alt'] = 'true'; + if (command) evt['command'] = 'true'; + return evt; + } + + static void sendMouse(String type, String buttons) { + if (!ffiModel.keyboard()) return; + setByName( + 'send_mouse', json.encode(modify({'type': type, 'buttons': buttons}))); + } + + static void inputKey(String name) { + if (!ffiModel.keyboard()) return; + setByName('input_key', json.encode(modify({'name': name}))); + } + + static void moveMouse(double x, double y) { + if (!ffiModel.keyboard()) return; + var x2 = x.toInt(); + var y2 = y.toInt(); + setByName('send_mouse', json.encode(modify({'x': '$x2', 'y': '$y2'}))); + } + + static List peers() { + try { + List peers = json.decode(getByName('peers')); + return peers + .map((s) => s as List) + .map((s) => + Peer.fromJson(s[0] as String, s[1] as Map)) + .toList(); + } catch (e) { + print('peers(): $e'); + } + return []; + } + + static void connect(String id) { + setByName('connect', id); + FFI.id = id; + } + + static void clearRgbaFrame() {} + + static Uint8List getRgba() {} + + static Map popEvent() { + var s = getByName('event'); + if (s == '') return null; + try { + Map event = json.decode(s); + return event; + } catch (e) { + print('popEvent(): $e'); + } + return null; + } + + static void login(String password, bool remember) { + setByName( + 'login', + json.encode({ + 'password': password, + 'remember': remember ? 'true' : 'false', + })); + } + + static void close() { + savePreference(id, cursorModel.x, cursorModel.y, canvasModel.x, + canvasModel.y, canvasModel.scale, ffiModel.pi.currentDisplay); + id = ""; + setByName('close', ''); + imageModel.update(null); + cursorModel.clear(); + ffiModel.clear(); + canvasModel.clear(); + resetModifiers(); + } + + static void setByName(String name, [String value = '']) {} + + static String getByName(String name, [String arg = '']) {} + + static Future init() async {} +} + +class Peer { + final String id; + final String username; + final String hostname; + final String platform; + + Peer.fromJson(String id, Map json) + : id = id, + username = json['username'], + hostname = json['hostname'], + platform = json['platform']; +} + +class Display { + double x = 0; + double y = 0; + int width = 0; + int height = 0; +} + +class PeerInfo { + String version; + String username; + String hostname; + String platform; + bool sasEnabled; + int currentDisplay; + List displays; +} + +void savePreference(String id, double xCursor, double yCursor, double xCanvas, + double yCanvas, double scale, int currentDisplay) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + final p = Map(); + p['xCursor'] = xCursor; + p['yCursor'] = yCursor; + p['xCanvas'] = xCanvas; + p['yCanvas'] = yCanvas; + p['scale'] = scale; + p['currentDisplay'] = currentDisplay; + prefs.setString('peer' + id, json.encode(p)); +} + +Future> getPreference(String id) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + var p = prefs.getString('peer' + id); + if (p == null) return null; + Map m = json.decode(p); + return m; +} + +void removePreference(String id) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + prefs.remove('peer' + id); +} + +void initializeCursorAndCanvas() async { + var p = await getPreference(FFI.id); + int currentDisplay = 0; + if (p != null) { + currentDisplay = p['currentDisplay']; + } + if (p == null || currentDisplay != FFI.ffiModel.pi.currentDisplay) { + FFI.cursorModel + .updateDisplayOrigin(FFI.ffiModel.display.x, FFI.ffiModel.display.y); + return; + } + double xCursor = p['xCursor']; + double yCursor = p['yCursor']; + double xCanvas = p['xCanvas']; + double yCanvas = p['yCanvas']; + double scale = p['scale']; + FFI.cursorModel.updateDisplayOriginWithCursor( + FFI.ffiModel.display.x, FFI.ffiModel.display.y, xCursor, yCursor); + FFI.canvasModel.update(xCanvas, yCanvas, scale); +} + +String translate(String name) { + return name; +} diff --git a/pubspec.lock b/pubspec.lock index 7f7f91a5d..4ec2db940 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -21,7 +21,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.6.1" + version: "2.8.2" boolean_selector: dependency: transitive description: @@ -35,14 +35,14 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.1" clock: dependency: transitive description: @@ -227,14 +227,14 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10" + version: "0.12.11" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.7.0" nested: dependency: transitive description: @@ -428,7 +428,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.3.0" + version: "0.4.3" tuple: dependency: "direct main" description: @@ -491,7 +491,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.1" wakelock: dependency: "direct main" description: @@ -556,5 +556,5 @@ packages: source: hosted version: "3.1.0" sdks: - dart: ">=2.13.0 <3.0.0" + dart: ">=2.14.0 <3.0.0" flutter: ">=2.0.0" diff --git a/web/favicon.png b/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/web/icons/Icon-192.png b/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/web/icons/Icon-512.png b/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/web/icons/Icon-maskable-192.png b/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/web/icons/Icon-maskable-512.png b/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/web/index.html b/web/index.html new file mode 100644 index 000000000..377445124 --- /dev/null +++ b/web/index.html @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + RustDesk + + + + + + + + + + + diff --git a/web/manifest.json b/web/manifest.json new file mode 100644 index 000000000..9723be242 --- /dev/null +++ b/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "rustdesk", + "short_name": "rustdesk", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "Remote Desktop.", + "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" + } + ] +} From 9ac1a955ba1c78d820426e6c72c33502ec92285c Mon Sep 17 00:00:00 2001 From: open-trade Date: Tue, 25 Jan 2022 19:04:59 +0800 Subject: [PATCH 231/422] smaller search box --- lib/home_page.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/home_page.dart b/lib/home_page.dart index 7f4778c88..09da3d04b 100644 --- a/lib/home_page.dart +++ b/lib/home_page.dart @@ -126,7 +126,7 @@ class _HomePageState extends State { if (!FFI.ffiModel.initialized) { return Container(); } - return Padding( + var w = Padding( padding: const EdgeInsets.fromLTRB(16.0, 8.0, 16.0, 0.0), child: Container( height: 84, @@ -195,6 +195,9 @@ class _HomePageState extends State { ), ), ); + if (!isWeb) return w; + return Center( + child: Container(constraints: BoxConstraints(maxWidth: 600), child: w)); } @override From f4c3198037815eec9ed19708471410b83d038579 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 26 Jan 2022 12:39:44 +0800 Subject: [PATCH 232/422] more api --- package.json | 3 +- src/connection.ts | 112 ++++++++++++++++++++++++++++++++++++---------- src/globals.js | 87 +++++++++++++++++++++++++++++++++++ yarn.lock | 5 +++ 4 files changed, 183 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index a6d708e94..611351fff 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "libsodium": "^0.7.9", "libsodium-wrappers": "^0.7.9", "ogv": "^1.8.6", - "ts-proto": "^1.101.0" + "ts-proto": "^1.101.0", + "zstddec": "^0.0.2" } } diff --git a/src/connection.ts b/src/connection.ts index 05df1edda..6cbba8c12 100644 --- a/src/connection.ts +++ b/src/connection.ts @@ -19,16 +19,22 @@ export default class Connection { _interval: any; _id: string; _hash: message.Hash | undefined; - _msgbox: MsgboxCallback | undefined; - _draw: DrawCallback | undefined; + _msgbox: MsgboxCallback; + _draw: DrawCallback; _peerInfo: message.PeerInfo | undefined; _firstFrame: Boolean | undefined; _videoDecoder: any; _audioDecoder: any; + _password: string | undefined; constructor() { + this._msgbox = globals.msgbox; + this._draw = globals.draw; this._msgs = []; this._id = ""; + } + + async start(id: string) { this._interval = setInterval(() => { while (this._msgs.length) { this._ws?.sendMessage(this._msgs[0]); @@ -44,9 +50,6 @@ export default class Connection { this._audioDecoder = decoder; console.log("opus loaded"); }); - } - - async start(id: string) { const uri = getDefaultUri(); const ws = new Websock(uri); this._ws = ws; @@ -69,7 +72,7 @@ export default class Connection { const phr = msg.punchHoleResponse; const rr = msg.relayResponse; if (phr) { - if (phr.failure != rendezvous.PunchHoleResponse_Failure.UNKNOWN) { + if (phr.failure != rendezvous.PunchHoleResponse_Failure.UNRECOGNIZED) { switch (phr?.failure) { case rendezvous.PunchHoleResponse_Failure.ID_NOT_EXIST: this.msgbox("error", "Error", "ID does not exist"); @@ -110,7 +113,8 @@ export default class Connection { uuid, }); ws.sendRendezvous({ requestRelay }); - await this.secure(pk); + const secure = (await this.secure(pk)) || false; + globals.pushEvent("connection_ready", { secure, direct: false }); await this.msgLoop(); } @@ -179,6 +183,7 @@ export default class Connection { }); await this._ws?.sendMessage({ publicKey }); this._ws?.setSecretKey(secretKey); + return true; } async msgLoop() { @@ -186,7 +191,7 @@ export default class Connection { const msg = this._ws?.parseMessage(await this._ws?.next()); if (msg?.hash) { this._hash = msg?.hash; - await this.handleHash(); + await this.login(this._password); this.msgbox("input-password", "Password Required", ""); } else if (msg?.testDelay) { const testDelay = msg?.testDelay; @@ -198,23 +203,30 @@ export default class Connection { if (r.error) { this.msgbox("error", "Error", r.error); } else if (r.peerInfo) { - this._peerInfo = r.peerInfo; - this.msgbox( - "success", - "Successful", - "Connected, waiting for image..." - ); + this.handlePeerInfo(r.peerInfo); } } else if (msg?.videoFrame) { this.handleVideoFrame(msg?.videoFrame!); + } else if (msg?.clipboard) { + const cb = msg?.clipboard; + if (cb.compress) cb.content = globals.decompress(cb.content); + globals.pushEvent("clipboard", cb); + } else if (msg?.cursorData) { + const cd = msg?.cursorData; + cd.colors = globals.decompress(cd.colors); + globals.pushEvent("cursor_data", cd); + } else if (msg?.cursorId) { + globals.pushEvent("cursor_id", { id: msg?.cursorId }); + } else if (msg?.cursorPosition) { + globals.pushEvent("cursor_position", msg?.cursorPosition); + } else if (msg?.misc) { + this.handleMisc(msg?.misc); + } else if (msg?.audioFrame) { + // } } } - async handleHash() { - await this._sendLoginMessage(); - } - msgbox(type_: string, title: string, text: string) { this._msgbox?.(type_, title, text); } @@ -224,12 +236,20 @@ export default class Connection { } close() { + this._msgs = []; clearInterval(this._interval); this._ws?.close(); this._videoDecoder?.close(); this._audioDecoder?.close(); } + async refresh() { + const misc = message.Misc.fromPartial({ + refreshVideo: true, + }); + await this._ws?.sendMessage({ misc }); + } + setMsgbox(callback: MsgboxCallback) { this._msgbox = callback; } @@ -238,19 +258,27 @@ export default class Connection { this._draw = callback; } - async login(password: string) { + async login(password: string | undefined, _remember: Boolean = false) { + this._password = password; this.msgbox("connecting", "Connecting...", "Logging in..."); - let salt = this._hash?.salt; - if (salt) { + const salt = this._hash?.salt; + if (salt && password) { let p = hash([password, salt]); - let challenge = this._hash?.challenge; + const challenge = this._hash?.challenge; if (challenge) { p = hash([p, challenge]); await this._sendLoginMessage(p); } + } else { + await this._sendLoginMessage(); } } + async reconnect() { + this.close(); + await this.start(this._id); + } + async _sendLoginMessage(password: Uint8Array | undefined = undefined) { const loginRequest = message.LoginRequest.fromPartial({ username: this._id!, @@ -267,7 +295,7 @@ export default class Connection { this._firstFrame = true; } if (vf.vp9s) { - let dec = this._videoDecoder; + const dec = this._videoDecoder; // dec.sync(); vf.vp9s.frames.forEach((f) => { dec.processFrame(f.data.slice(0).buffer, (ok: any) => { @@ -278,6 +306,44 @@ export default class Connection { }); } } + + handlePeerInfo(pi: message.PeerInfo) { + this._peerInfo = pi; + if (pi.displays.length == 0) { + this.msgbox("error", "Remote Error", "No Display"); + return; + } + this.msgbox("success", "Successful", "Connected, waiting for image..."); + globals.pushEvent("peer_info", pi); + } + + handleMisc(misc: message.Misc) { + if (misc.audioFormat) { + // + } else if (misc.permissionInfo) { + const p = misc.permissionInfo; + console.info("Change permission " + p.permission + " -> " + p.enabled); + let name; + switch (p.permission) { + case message.PermissionInfo_Permission.Keyboard: + name = "keyboard"; + break; + case message.PermissionInfo_Permission.Clipboard: + name = "clipboard"; + break; + case message.PermissionInfo_Permission.Audio: + name = "audio"; + break; + default: + return; + } + globals.pushEvent("permission", { [name]: p.enabled }); + } else if (misc.switchDisplay) { + globals.pushEvent("switch_display", misc.switchDisplay); + } else if (misc.closeReason) { + this.msgbox("error", "Connection Error", misc.closeReason); + } + } } // @ts-ignore diff --git a/src/globals.js b/src/globals.js index cd3828dec..43d5a8022 100644 --- a/src/globals.js +++ b/src/globals.js @@ -1,7 +1,39 @@ import Connection from "./connection"; import _sodium from "libsodium-wrappers"; +import { ZSTDecoder } from 'zstddec'; + +const decompressor = new ZSTDDecoder(); +await decompressor.init(); + +var currentFrame = undefined; +var events = []; window.currentConnection = undefined; +window.getRgba = () => currentFrame; +window.getLanguage = () => navigator.language; + +export function msgbox(type, title, text) { + text = text.toLowerCase(); + var hasRetry = msgtype == "error" + && title == "Connection Error" + && !text.indexOf("offline") >= 0 + && !text.indexOf("exist") >= 0 + && !text.indexOf("handshake") >= 0 + && !text.indexOf("failed") >= 0 + && !text.indexOf("resolve") >= 0 + && !text.indexOf("mismatch") >= 0 + && !text.indexOf("manually") >= 0; + events.push({ name: 'msgbox', type, title, text, hasRetry }); +} + +export function pushEvent(name, payload) { + payload.name = name; + events.push(payload); +} + +export function draw(frame) { + currentFrame = frame; +} export function setConn(conn) { window.currentConnection = conn; @@ -14,6 +46,7 @@ export function getConn() { export function close() { getConn()?.close(); setConn(undefined); + currentFrame = undefined; } export function newConn() { @@ -73,4 +106,58 @@ export function encrypt(unsigned, nonce, key) { export function decrypt(signed, nonce, key) { return sodium.crypto_secretbox_open_easy(signed, makeOnce(nonce), key); +} + +export function decompress(compressedArray) { + const MAX = 1024 * 1024 * 64; + const MIN = 1024 * 1024; + let n = 30 * data.length; + if (n > MAX) { + n = MAX; + } + if (n < MIN) { + n = MIN; + } + try { + return decompressor.decode(compressedArray, n); + } catch (e) { + console.error('decompress failed: ' + e); + } +} + +window.setByName = (name, value) => { + switch (name) { + case 'connect': + newConn(); + break; + case 'login': + currentConnection.login(value.password, value.remember); + break; + case 'close': + close(); + break; + case 'refresh': + currentConnection.refresh(); + break; + case 'reconnect': + currentConnection.reconnect(); + break; + default: + break; + } +} + +window.getByName = (name, value) => { + switch (name) { + case 'peers': + return localStorage.getItem('peers'); + break; + case 'event': + if (events.length) { + const e = events[0]; + events.splice(0, 1); + return e; + } + break; + } } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 2e61c8dbf..c8e30b076 100644 --- a/yarn.lock +++ b/yarn.lock @@ -376,3 +376,8 @@ vite@^2.7.2: rollup "^2.59.0" optionalDependencies: fsevents "~2.3.2" + +zstddec@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/zstddec/-/zstddec-0.0.2.tgz#57e2f28dd1ff56b750e07d158a43f0611ad9eeb4" + integrity sha512-DCo0oxvcvOTGP/f5FA6tz2Z6wF+FIcEApSTu0zV5sQgn9hoT5lZ9YRAKUraxt9oP7l4e8TnNdi8IZTCX6WCkwA== From 3d77365edc782ef1bfc4051c21f7c65cb343f083 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 26 Jan 2022 12:48:16 +0800 Subject: [PATCH 233/422] refactor --- lib/home_page.dart | 8 +- lib/main.dart | 2 +- lib/model.dart | 108 +------ lib/native_model.dart | 100 ++++++ lib/remote_page.dart | 6 +- lib/web_model.dart | 698 +----------------------------------------- web/index.html | 1 + 7 files changed, 132 insertions(+), 791 deletions(-) create mode 100644 lib/native_model.dart diff --git a/lib/home_page.dart b/lib/home_page.dart index 09da3d04b..7daa30814 100644 --- a/lib/home_page.dart +++ b/lib/home_page.dart @@ -5,7 +5,7 @@ import 'package:package_info/package_info.dart'; import 'package:url_launcher/url_launcher.dart'; import 'dart:async'; import 'common.dart'; -import 'model.dart' if (dart.library.html) 'web_model.dart'; +import 'model.dart'; import 'remote_page.dart'; class HomePage extends StatefulWidget { @@ -325,13 +325,13 @@ void showServer(BuildContext context) { formKey.currentState.save(); if (id != id0) FFI.setByName('option', - '{"name": "custom-rendezvous-server", "value": "${id}"}'); + '{"name": "custom-rendezvous-server", "value": "$id"}'); if (relay != relay0) FFI.setByName('option', - '{"name": "relay-server", "value": "${relay}"}'); + '{"name": "relay-server", "value": "$relay"}'); if (key != key0) FFI.setByName( - 'option', '{"name": "key", "value": "${key}"}'); + 'option', '{"name": "key", "value": "$key"}'); Navigator.pop(context); } }, diff --git a/lib/main.dart b/lib/main.dart index 29d9ad0ed..32af66665 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,7 +3,7 @@ import 'package:provider/provider.dart'; import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:firebase_analytics/observer.dart'; import 'package:firebase_core/firebase_core.dart'; -import 'model.dart' if (dart.library.html) 'web_model.dart'; +import 'model.dart'; import 'home_page.dart'; Future main() async { diff --git a/lib/model.dart b/lib/model.dart index 73dc375ec..d914e5b0f 100644 --- a/lib/model.dart +++ b/lib/model.dart @@ -1,11 +1,6 @@ -import 'package:ffi/ffi.dart'; import 'package:flutter/services.dart'; -import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'package:device_info/device_info.dart'; -import 'dart:io'; import 'dart:math'; -import 'dart:ffi'; import 'dart:convert'; import 'dart:typed_data'; import 'dart:ui' as ui; @@ -13,17 +8,7 @@ import 'package:flutter/material.dart'; import 'package:tuple/tuple.dart'; import 'dart:async'; import 'common.dart'; - -class RgbaFrame extends Struct { - @Uint32() - int len; - Pointer data; -} - -typedef F2 = Pointer Function(Pointer, Pointer); -typedef F3 = void Function(Pointer, Pointer); -typedef F4 = void Function(Pointer); -typedef F5 = Pointer Function(); +import 'native_model.dart' if (dart.library.html) 'web_model.dart'; class FfiModel with ChangeNotifier { PeerInfo _pi; @@ -43,13 +28,10 @@ class FfiModel with ChangeNotifier { get pi => _pi; FfiModel() { - isIOS = Platform.isIOS; - isAndroid = Platform.isAndroid; - isWeb = false; Translator.call = translate; clear(); () async { - await FFI.init(); + await PlatformFFI.init(); _initialized = true; print("FFI initialized"); notifyListeners(); @@ -136,7 +118,7 @@ class FfiModel with ChangeNotifier { } if (pos != null) FFI.cursorModel.updateCursorPosition(pos); if (!_decoding) { - var rgba = FFI.getRgba(); + var rgba = PlatformFFI.getRgba(); if (rgba != null) { if (_waitForImage) { _waitForImage = false; @@ -147,7 +129,7 @@ class FfiModel with ChangeNotifier { ui.decodeImageFromPixels( rgba, _display.width, _display.height, ui.PixelFormat.bgra8888, (image) { - FFI.clearRgbaFrame(); + PlatformFFI.clearRgbaFrame(); _decoding = false; if (FFI.id != pid) return; try { @@ -510,12 +492,6 @@ class CursorModel with ChangeNotifier { class FFI { static String id = ""; - static String _dir = ''; - static F2 _getByName; - static F3 _setByName; - static F4 _freeRgba; - static F5 _getRgba; - static Pointer _lastRgbaFrame; static var shift = false; static var ctrl = false; static var alt = false; @@ -595,19 +571,6 @@ class FFI { FFI.id = id; } - static void clearRgbaFrame() { - if (_lastRgbaFrame != null && _lastRgbaFrame != nullptr) - _freeRgba(_lastRgbaFrame); - } - - static Uint8List getRgba() { - if (_getRgba == null) return null; - _lastRgbaFrame = _getRgba(); - if (_lastRgbaFrame == null || _lastRgbaFrame == nullptr) return null; - final ref = _lastRgbaFrame.ref; - return Uint8List.sublistView(ref.data.asTypedList(ref.len)); - } - static Map popEvent() { var s = getByName('event'); if (s == '') return null; @@ -641,60 +604,12 @@ class FFI { resetModifiers(); } - static void setByName(String name, [String value = '']) { - if (_setByName == null) return; - var a = name.toNativeUtf8(); - var b = value.toNativeUtf8(); - _setByName(a, b); - calloc.free(a); - calloc.free(b); - } - static String getByName(String name, [String arg = '']) { - if (_getByName == null) return ''; - var a = name.toNativeUtf8(); - var b = arg.toNativeUtf8(); - var p = _getByName(a, b); - assert(p != nullptr && p != null); - var res = p.toDartString(); - calloc.free(p); - calloc.free(a); - calloc.free(b); - return res; + return PlatformFFI.getByName(name, arg); } - static Future init() async { - final dylib = Platform.isAndroid - ? DynamicLibrary.open('librustdesk.so') - : DynamicLibrary.process(); - print('initializing FFI'); - try { - _getByName = dylib.lookupFunction('get_by_name'); - _setByName = - dylib.lookupFunction, Pointer), F3>( - 'set_by_name'); - _freeRgba = dylib - .lookupFunction), F4>('free_rgba'); - _getRgba = dylib.lookupFunction('get_rgba'); - _dir = (await getApplicationDocumentsDirectory()).path; - String id = 'NA'; - String name = 'Flutter'; - DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); - if (Platform.isAndroid) { - AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; - name = '${androidInfo.brand}-${androidInfo.model}'; - id = androidInfo.id.hashCode.toString(); - } else { - IosDeviceInfo iosInfo = await deviceInfo.iosInfo; - name = iosInfo.utsname.machine; - id = iosInfo.identifierForVendor.hashCode.toString(); - } - setByName('info1', id); - setByName('info2', name); - setByName('init', _dir); - } catch (e) { - print(e); - } + static void setByName(String name, [String value = '']) { + PlatformFFI.setByName(name, value); } } @@ -775,10 +690,6 @@ void initializeCursorAndCanvas() async { FFI.canvasModel.update(xCanvas, yCanvas, scale); } -final locale = Platform.localeName; -final bool isCn = - locale.startsWith('zh') && (locale.endsWith('CN') || locale.endsWith('SG')); - final langs = >{ 'cn': { 'Remote ID': '远程ID', @@ -791,6 +702,9 @@ final langs = >{ 'en': {} }; +final bool isCn = localeName.startsWith('zh') && + (localeName.endsWith('CN') || localeName.endsWith('SG')); + String translate(String name) { if (name.startsWith('Failed') && name.contains(':')) { return name.split(': ').map((x) => translate(x)).join(': '); @@ -799,7 +713,7 @@ String translate(String name) { final v = tmp[name]; if (v == null) { var a = 'translate'; - var b = '{"locale": "${Platform.localeName}", "text": "$name"}'; + var b = '{"locale": "$localeName", "text": "$name"}'; return FFI.getByName(a, b); } else { return v; diff --git a/lib/native_model.dart b/lib/native_model.dart new file mode 100644 index 000000000..cee8bd0bb --- /dev/null +++ b/lib/native_model.dart @@ -0,0 +1,100 @@ +import 'dart:io'; +import 'dart:typed_data'; +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:device_info/device_info.dart'; +import "common.dart"; + +class RgbaFrame extends Struct { + @Uint32() + int len; + Pointer data; +} + +typedef F2 = Pointer Function(Pointer, Pointer); +typedef F3 = void Function(Pointer, Pointer); +typedef F4 = void Function(Pointer); +typedef F5 = Pointer Function(); + +class PlatformFFI { + static Pointer _lastRgbaFrame; + static String _dir = ''; + static F2 _getByName; + static F3 _setByName; + static F4 _freeRgba; + static F5 _getRgba; + + static void clearRgbaFrame() { + if (_lastRgbaFrame != null && _lastRgbaFrame != nullptr) + _freeRgba(_lastRgbaFrame); + } + + static Uint8List getRgba() { + if (_getRgba == null) return null; + _lastRgbaFrame = _getRgba(); + if (_lastRgbaFrame == null || _lastRgbaFrame == nullptr) return null; + final ref = _lastRgbaFrame.ref; + return Uint8List.sublistView(ref.data.asTypedList(ref.len)); + } + + static String getByName(String name, [String arg = '']) { + if (_getByName == null) return ''; + var a = name.toNativeUtf8(); + var b = arg.toNativeUtf8(); + var p = _getByName(a, b); + assert(p != nullptr && p != null); + var res = p.toDartString(); + calloc.free(p); + calloc.free(a); + calloc.free(b); + return res; + } + + static void setByName(String name, [String value = '']) { + if (_setByName == null) return; + var a = name.toNativeUtf8(); + var b = value.toNativeUtf8(); + _setByName(a, b); + calloc.free(a); + calloc.free(b); + } + + static Future init() async { + isIOS = Platform.isIOS; + isAndroid = Platform.isAndroid; + final dylib = Platform.isAndroid + ? DynamicLibrary.open('librustdesk.so') + : DynamicLibrary.process(); + print('initializing FFI'); + try { + _getByName = dylib.lookupFunction('get_by_name'); + _setByName = + dylib.lookupFunction, Pointer), F3>( + 'set_by_name'); + _freeRgba = dylib + .lookupFunction), F4>('free_rgba'); + _getRgba = dylib.lookupFunction('get_rgba'); + _dir = (await getApplicationDocumentsDirectory()).path; + String id = 'NA'; + String name = 'Flutter'; + DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); + if (Platform.isAndroid) { + AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; + name = '${androidInfo.brand}-${androidInfo.model}'; + id = androidInfo.id.hashCode.toString(); + } else { + IosDeviceInfo iosInfo = await deviceInfo.iosInfo; + name = iosInfo.utsname.machine; + id = iosInfo.identifierForVendor.hashCode.toString(); + } + setByName('info1', id); + setByName('info2', name); + setByName('init', _dir); + } catch (e) { + print(e); + } + } +} + +final localeName = Platform.localeName; diff --git a/lib/remote_page.dart b/lib/remote_page.dart index d36be7155..c5733c83c 100644 --- a/lib/remote_page.dart +++ b/lib/remote_page.dart @@ -7,7 +7,7 @@ import 'dart:async'; import 'package:tuple/tuple.dart'; import 'package:wakelock/wakelock.dart'; import 'common.dart'; -import 'model.dart' if (dart.library.html) 'web_model.dart'; +import 'model.dart'; final initText = '\1' * 1024; @@ -485,7 +485,7 @@ class _RemotePageState extends State { } else if (value == 'touch_mode') { _touchMode = !_touchMode; final v = _touchMode ? 'Y' : ''; - FFI.setByName('peer_option', '{"name": "touch-mode", "value": "${v}"}'); + FFI.setByName('peer_option', '{"name": "touch-mode", "value": "$v"}'); } else if (value == 'reset_canvas') { FFI.cursorModel.reset(); } @@ -947,7 +947,7 @@ void showSetOSPassword(BuildContext context, bool login) { onPressed: () { var text = controller.text.trim(); FFI.setByName('peer_option', - '{"name": "os-password", "value": "${text}"}'); + '{"name": "os-password", "value": "$text"}'); FFI.setByName('peer_option', '{"name": "auto-login", "value": "${autoLogin ? 'Y' : ''}"}'); if (text != "" && login) { diff --git a/lib/web_model.dart b/lib/web_model.dart index c84d20059..6000401b3 100644 --- a/lib/web_model.dart +++ b/lib/web_model.dart @@ -1,700 +1,26 @@ -import 'package:path_provider/path_provider.dart'; -import 'package:shared_preferences/shared_preferences.dart'; -import 'package:device_info/device_info.dart'; -import 'dart:math'; -import 'dart:convert'; import 'dart:typed_data'; -import 'dart:ui' as ui; -import 'package:flutter/material.dart'; -import 'package:tuple/tuple.dart'; -import 'dart:async'; +import 'dart:js' as js; import 'common.dart'; -class FfiModel with ChangeNotifier { - PeerInfo _pi; - Display _display; - var _decoding = false; - bool _waitForImage; - bool _initialized = false; - final _permissions = Map(); - bool _secure; - bool _direct; - - get permissions => _permissions; - get initialized => _initialized; - get display => _display; - get secure => _secure; - get direct => _direct; - get pi => _pi; - - FfiModel() { - isIOS = false; - isAndroid = false; - isWeb = true; - Translator.call = translate; - clear(); - () async { - await FFI.init(); - _initialized = true; - print("FFI initialized"); - notifyListeners(); - }(); - } - - void updatePermission(Map evt) { - evt.forEach((k, v) { - if (k == 'name') return; - _permissions[k] = v == 'true'; - }); - print('$_permissions'); - } - - bool keyboard() => _permissions['keyboard'] != false; - - void clear() { - _pi = PeerInfo(); - _display = Display(); - _waitForImage = false; - _secure = null; - _direct = null; - clearPermissions(); - } - - void setConnectionType(bool secure, bool direct) { - _secure = secure; - _direct = direct; - } - - Image getConnectionImage() { - String icon; - if (secure == true && direct == true) { - icon = 'secure'; - } else if (secure == false && direct == true) { - icon = 'insecure'; - } else if (secure == false && direct == false) { - icon = 'insecure_relay'; - } else if (secure == true && direct == false) { - icon = 'secure_relay'; - } - return icon == null - ? null - : Image.asset('assets/$icon.png', width: 48, height: 48); - } - - void clearPermissions() { - _permissions.clear(); - } - - void update( - String id, - BuildContext context, - void Function( - Map evt, - String id, - ) - handleMsgbox) { - var pos; - for (;;) { - var evt = FFI.popEvent(); - if (evt == null) break; - var name = evt['name']; - if (name == 'msgbox') { - handleMsgbox(evt, id); - } else if (name == 'peer_info') { - handlePeerInfo(evt, context); - } else if (name == 'connection_ready') { - FFI.ffiModel.setConnectionType( - evt['secure'] == 'true', evt['direct'] == 'true'); - } else if (name == 'switch_display') { - handleSwitchDisplay(evt); - } else if (name == 'cursor_data') { - FFI.cursorModel.updateCursorData(evt); - } else if (name == 'cursor_id') { - FFI.cursorModel.updateCursorId(evt); - } else if (name == 'cursor_position') { - pos = evt; - } else if (name == 'clipboard') { - } else if (name == 'permission') { - FFI.ffiModel.updatePermission(evt); - } - } - if (pos != null) FFI.cursorModel.updateCursorPosition(pos); - if (!_decoding) { - var rgba = FFI.getRgba(); - if (rgba != null) { - if (_waitForImage) { - _waitForImage = false; - dismissLoading(); - } - _decoding = true; - final pid = FFI.id; - ui.decodeImageFromPixels( - rgba, _display.width, _display.height, ui.PixelFormat.bgra8888, - (image) { - FFI.clearRgbaFrame(); - _decoding = false; - if (FFI.id != pid) return; - try { - // my throw exception, because the listener maybe already dispose - FFI.imageModel.update(image); - } catch (e) { - print('update image: $e'); - } - }); - } - } - } - - void handleSwitchDisplay(Map evt) { - var old = _pi.currentDisplay; - _pi.currentDisplay = int.parse(evt['display']); - _display.x = double.parse(evt['x']); - _display.y = double.parse(evt['y']); - _display.width = int.parse(evt['width']); - _display.height = int.parse(evt['height']); - if (old != _pi.currentDisplay) - FFI.cursorModel.updateDisplayOrigin(_display.x, _display.y); - notifyListeners(); - } - - void handlePeerInfo(Map evt, BuildContext context) { - dismissLoading(); - _pi.version = evt['version']; - _pi.username = evt['username']; - _pi.hostname = evt['hostname']; - _pi.platform = evt['platform']; - _pi.sasEnabled = evt['sas_enabled'] == "true"; - _pi.currentDisplay = int.parse(evt['current_display']); - List displays = json.decode(evt['displays']); - _pi.displays = []; - for (int i = 0; i < displays.length; ++i) { - Map d0 = displays[i]; - var d = Display(); - d.x = d0['x'].toDouble(); - d.y = d0['y'].toDouble(); - d.width = d0['width']; - d.height = d0['height']; - _pi.displays.add(d); - } - if (_pi.currentDisplay < _pi.displays.length) { - _display = _pi.displays[_pi.currentDisplay]; - initializeCursorAndCanvas(); - } - if (displays.length > 0) { - showLoading(translate('Connected, waiting for image...'), context); - _waitForImage = true; - } - notifyListeners(); - } -} - -class ImageModel with ChangeNotifier { - ui.Image _image; - - ui.Image get image => _image; - - void update(ui.Image image) { - if (_image == null && image != null) { - final size = MediaQueryData.fromWindow(ui.window).size; - final xscale = size.width / image.width; - final yscale = size.height / image.height; - FFI.canvasModel.scale = max(xscale, yscale); - } - _image = image; - if (image != null) notifyListeners(); - } - - double get maxScale { - if (_image == null) return 1.0; - final size = MediaQueryData.fromWindow(ui.window).size; - final xscale = size.width / _image.width; - final yscale = size.height / _image.height; - return max(1.0, max(xscale, yscale)); - } - - double get minScale { - if (_image == null) return 1.0; - final size = MediaQueryData.fromWindow(ui.window).size; - final xscale = size.width / _image.width; - final yscale = size.height / _image.height; - return min(xscale, yscale); - } -} - -class CanvasModel with ChangeNotifier { - double _x; - double _y; - double _scale; - - CanvasModel() { - clear(); - } - - double get x => _x; - double get y => _y; - double get scale => _scale; - - void update(double x, double y, double scale) { - _x = x; - _y = y; - _scale = scale; - notifyListeners(); - } - - set scale(v) { - _scale = v; - notifyListeners(); - } - - void panX(double dx) { - _x += dx; - notifyListeners(); - } - - void resetOffset() { - _x = 0; - _y = 0; - notifyListeners(); - } - - void panY(double dy) { - _y += dy; - notifyListeners(); - } - - void updateScale(double v) { - if (FFI.imageModel.image == null) return; - final offset = FFI.cursorModel.offset; - var r = FFI.cursorModel.getVisibleRect(); - final px0 = (offset.dx - r.left) * _scale; - final py0 = (offset.dy - r.top) * _scale; - _scale *= v; - final maxs = FFI.imageModel.maxScale; - final mins = FFI.imageModel.minScale; - if (_scale > maxs) _scale = maxs; - if (_scale < mins) _scale = mins; - r = FFI.cursorModel.getVisibleRect(); - final px1 = (offset.dx - r.left) * _scale; - final py1 = (offset.dy - r.top) * _scale; - _x -= px1 - px0; - _y -= py1 - py0; - notifyListeners(); - } - - void clear([bool notify = false]) { - _x = 0; - _y = 0; - _scale = 1.0; - if (notify) notifyListeners(); - } -} - -class CursorModel with ChangeNotifier { - ui.Image _image; - final _images = Map>(); - double _x = -10000; - double _y = -10000; - double _hotx = 0; - double _hoty = 0; - double _displayOriginX = 0; - double _displayOriginY = 0; - - ui.Image get image => _image; - double get x => _x - _displayOriginX; - double get y => _y - _displayOriginY; - Offset get offset => Offset(_x, _y); - double get hotx => _hotx; - double get hoty => _hoty; - - // remote physical display coordinate - Rect getVisibleRect() { - final size = MediaQueryData.fromWindow(ui.window).size; - final xoffset = FFI.canvasModel.x; - final yoffset = FFI.canvasModel.y; - final scale = FFI.canvasModel.scale; - final x0 = _displayOriginX - xoffset / scale; - final y0 = _displayOriginY - yoffset / scale; - return Rect.fromLTWH(x0, y0, size.width / scale, size.height / scale); - } - - double adjustForKeyboard() { - final m = MediaQueryData.fromWindow(ui.window); - var keyboardHeight = m.viewInsets.bottom; - final size = m.size; - if (keyboardHeight < 100) return 0; - final s = FFI.canvasModel.scale; - final thresh = (size.height - keyboardHeight) / 2; - var h = (_y - getVisibleRect().top) * s; // local physical display height - return h - thresh; - } - - void touch(double x, double y, bool right) { - final scale = FFI.canvasModel.scale; - final xoffset = FFI.canvasModel.x; - final yoffset = FFI.canvasModel.y; - _x = (x - xoffset) / scale + _displayOriginX; - _y = (y - yoffset) / scale + _displayOriginY; - FFI.moveMouse(_x, _y); - FFI.tap(right); - notifyListeners(); - } - - void reset() { - _x = _displayOriginX; - _y = _displayOriginY; - FFI.moveMouse(_x, _y); - FFI.canvasModel.clear(true); - notifyListeners(); - } - - void updatePan(double dx, double dy, bool touchMode, bool drag) { - if (FFI.imageModel.image == null) return; - if (touchMode) { - if (drag) { - final scale = FFI.canvasModel.scale; - _x += dx / scale; - _y += dy / scale; - FFI.moveMouse(_x, _y); - notifyListeners(); - } else { - FFI.canvasModel.panX(dx); - FFI.canvasModel.panY(dy); - } - return; - } - final scale = FFI.canvasModel.scale; - dx /= scale; - dy /= scale; - final r = getVisibleRect(); - var cx = r.center.dx; - var cy = r.center.dy; - var tryMoveCanvasX = false; - if (dx > 0) { - final maxCanvasCanMove = - _displayOriginX + FFI.imageModel.image.width - r.right; - tryMoveCanvasX = _x + dx > cx && maxCanvasCanMove > 0; - if (tryMoveCanvasX) { - dx = min(dx, maxCanvasCanMove); - } else { - final maxCursorCanMove = r.right - _x; - dx = min(dx, maxCursorCanMove); - } - } else if (dx < 0) { - final maxCanvasCanMove = _displayOriginX - r.left; - tryMoveCanvasX = _x + dx < cx && maxCanvasCanMove < 0; - if (tryMoveCanvasX) { - dx = max(dx, maxCanvasCanMove); - } else { - final maxCursorCanMove = r.left - _x; - dx = max(dx, maxCursorCanMove); - } - } - var tryMoveCanvasY = false; - if (dy > 0) { - final mayCanvasCanMove = - _displayOriginY + FFI.imageModel.image.height - r.bottom; - tryMoveCanvasY = _y + dy > cy && mayCanvasCanMove > 0; - if (tryMoveCanvasY) { - dy = min(dy, mayCanvasCanMove); - } else { - final mayCursorCanMove = r.bottom - _y; - dy = min(dy, mayCursorCanMove); - } - } else if (dy < 0) { - final mayCanvasCanMove = _displayOriginY - r.top; - tryMoveCanvasY = _y + dy < cy && mayCanvasCanMove < 0; - if (tryMoveCanvasY) { - dy = max(dy, mayCanvasCanMove); - } else { - final mayCursorCanMove = r.top - _y; - dy = max(dy, mayCursorCanMove); - } - } - - if (dx == 0 && dy == 0) return; - _x += dx; - _y += dy; - if (tryMoveCanvasX && dx != 0) { - FFI.canvasModel.panX(-dx); - } - if (tryMoveCanvasY && dy != 0) { - FFI.canvasModel.panY(-dy); - } - - FFI.moveMouse(_x, _y); - notifyListeners(); - } - - void updateCursorData(Map evt) { - var id = int.parse(evt['id']); - _hotx = double.parse(evt['hotx']); - _hoty = double.parse(evt['hoty']); - var width = int.parse(evt['width']); - var height = int.parse(evt['height']); - List colors = json.decode(evt['colors']); - final rgba = Uint8List.fromList(colors.map((s) => s as int).toList()); - var pid = FFI.id; - ui.decodeImageFromPixels(rgba, width, height, ui.PixelFormat.rgba8888, - (image) { - if (FFI.id != pid) return; - _image = image; - _images[id] = Tuple3(image, _hotx, _hoty); - try { - // my throw exception, because the listener maybe already dispose - notifyListeners(); - } catch (e) { - print('notify cursor: $e'); - } - }); - } - - void updateCursorId(Map evt) { - final tmp = _images[int.parse(evt['id'])]; - if (tmp != null) { - _image = tmp.item1; - _hotx = tmp.item2; - _hoty = tmp.item3; - notifyListeners(); - } - } - - void updateCursorPosition(Map evt) { - _x = double.parse(evt['x']); - _y = double.parse(evt['y']); - notifyListeners(); - } - - void updateDisplayOrigin(double x, double y) { - _displayOriginX = x; - _displayOriginY = y; - _x = x + 1; - _y = y + 1; - FFI.moveMouse(x, y); - FFI.canvasModel.resetOffset(); - notifyListeners(); - } - - void updateDisplayOriginWithCursor( - double x, double y, double xCursor, double yCursor) { - _displayOriginX = x; - _displayOriginY = y; - _x = xCursor; - _y = yCursor; - FFI.moveMouse(x, y); - notifyListeners(); - } - - void clear() { - _x = -10000; - _x = -10000; - _image = null; - _images.clear(); - } -} - -class FFI { - static String id = ""; - static String _dir = ''; - static var shift = false; - static var ctrl = false; - static var alt = false; - static var command = false; - static final imageModel = ImageModel(); - static final ffiModel = FfiModel(); - static final cursorModel = CursorModel(); - static final canvasModel = CanvasModel(); - - static String getId() { - return getByName('remote_id'); - } - - static void tap(bool right) { - sendMouse('down', right ? 'right' : 'left'); - sendMouse('up', right ? 'right' : 'left'); - } - - static void scroll(double y) { - var y2 = y.round(); - if (y2 == 0) return; - setByName('send_mouse', - json.encode(modify({'type': 'wheel', 'y': y2.toString()}))); - } - - static void reconnect() { - setByName('reconnect'); - FFI.ffiModel.clearPermissions(); - } - - static void resetModifiers() { - shift = ctrl = alt = command = false; - } - - static Map modify(Map evt) { - if (ctrl) evt['ctrl'] = 'true'; - if (shift) evt['shift'] = 'true'; - if (alt) evt['alt'] = 'true'; - if (command) evt['command'] = 'true'; - return evt; - } - - static void sendMouse(String type, String buttons) { - if (!ffiModel.keyboard()) return; - setByName( - 'send_mouse', json.encode(modify({'type': type, 'buttons': buttons}))); - } - - static void inputKey(String name) { - if (!ffiModel.keyboard()) return; - setByName('input_key', json.encode(modify({'name': name}))); - } - - static void moveMouse(double x, double y) { - if (!ffiModel.keyboard()) return; - var x2 = x.toInt(); - var y2 = y.toInt(); - setByName('send_mouse', json.encode(modify({'x': '$x2', 'y': '$y2'}))); - } - - static List peers() { - try { - List peers = json.decode(getByName('peers')); - return peers - .map((s) => s as List) - .map((s) => - Peer.fromJson(s[0] as String, s[1] as Map)) - .toList(); - } catch (e) { - print('peers(): $e'); - } - return []; - } - - static void connect(String id) { - setByName('connect', id); - FFI.id = id; - } - +class PlatformFFI { static void clearRgbaFrame() {} - static Uint8List getRgba() {} - - static Map popEvent() { - var s = getByName('event'); - if (s == '') return null; - try { - Map event = json.decode(s); - return event; - } catch (e) { - print('popEvent(): $e'); - } - return null; + static Uint8List getRgba() { + return js.context.callMethod(js.context.callMethod('getRgba')); + // return Uint8List.sublistView(ref.data.asTypedList(ref.len)); } - static void login(String password, bool remember) { - setByName( - 'login', - json.encode({ - 'password': password, - 'remember': remember ? 'true' : 'false', - })); + static String getByName(String name, [String arg = '']) { + return js.context.callMethod('getByName', [name, arg]); } - static void close() { - savePreference(id, cursorModel.x, cursorModel.y, canvasModel.x, - canvasModel.y, canvasModel.scale, ffiModel.pi.currentDisplay); - id = ""; - setByName('close', ''); - imageModel.update(null); - cursorModel.clear(); - ffiModel.clear(); - canvasModel.clear(); - resetModifiers(); + static void setByName(String name, [String value = '']) { + js.context.callMethod('setByName', [name, value]); } - static void setByName(String name, [String value = '']) {} - - static String getByName(String name, [String arg = '']) {} - - static Future init() async {} -} - -class Peer { - final String id; - final String username; - final String hostname; - final String platform; - - Peer.fromJson(String id, Map json) - : id = id, - username = json['username'], - hostname = json['hostname'], - platform = json['platform']; -} - -class Display { - double x = 0; - double y = 0; - int width = 0; - int height = 0; -} - -class PeerInfo { - String version; - String username; - String hostname; - String platform; - bool sasEnabled; - int currentDisplay; - List displays; -} - -void savePreference(String id, double xCursor, double yCursor, double xCanvas, - double yCanvas, double scale, int currentDisplay) async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - final p = Map(); - p['xCursor'] = xCursor; - p['yCursor'] = yCursor; - p['xCanvas'] = xCanvas; - p['yCanvas'] = yCanvas; - p['scale'] = scale; - p['currentDisplay'] = currentDisplay; - prefs.setString('peer' + id, json.encode(p)); -} - -Future> getPreference(String id) async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - var p = prefs.getString('peer' + id); - if (p == null) return null; - Map m = json.decode(p); - return m; -} - -void removePreference(String id) async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - prefs.remove('peer' + id); -} - -void initializeCursorAndCanvas() async { - var p = await getPreference(FFI.id); - int currentDisplay = 0; - if (p != null) { - currentDisplay = p['currentDisplay']; + static Future init() async { + isWeb = true; } - if (p == null || currentDisplay != FFI.ffiModel.pi.currentDisplay) { - FFI.cursorModel - .updateDisplayOrigin(FFI.ffiModel.display.x, FFI.ffiModel.display.y); - return; - } - double xCursor = p['xCursor']; - double yCursor = p['yCursor']; - double xCanvas = p['xCanvas']; - double yCanvas = p['yCanvas']; - double scale = p['scale']; - FFI.cursorModel.updateDisplayOriginWithCursor( - FFI.ffiModel.display.x, FFI.ffiModel.display.y, xCursor, yCursor); - FFI.canvasModel.update(xCanvas, yCanvas, scale); } -String translate(String name) { - return name; -} +final localeName = js.context.callMethod('getLanguage') as String; diff --git a/web/index.html b/web/index.html index 377445124..57cef37e2 100644 --- a/web/index.html +++ b/web/index.html @@ -31,6 +31,7 @@ RustDesk + VirtualDisplay -> Surface -> MediaCodec +-> VirtualDisplay -> Surface <- MediaCodec/ImageReader - 获取mediaProjectionResultIntent - **必须activity** @@ -17,7 +17,7 @@ MediaProjectionManager -> MediaProjection - 通过后台服务获取MediaProjection - 创建Surface(理解为一个buf)和Surface消费者 - - MediaCodec生成Surface传入VirtualDisplay的入参中 + - MediaCodec(使用内置编码器)或者ImageReader(捕获原始数据)生成Surface传入VirtualDisplay的入参中 - 设定编码等各类参数 - 获取VirtualDisplay(Surface 生产者) @@ -26,6 +26,13 @@ MediaProjectionManager -> MediaProjection - 创建VirtualDisplay的入参之一是Surface - 需要设定正确的VirtualDisplay尺寸 +#####方案A 捕获原始数据传入rust进行编码 +- 构建ImageReader生成Surface +- **注意**:安卓捕获到的数据是RGBA格式,暂无BRGA的输出,在rust端需要调用libyuv中相应的rgbatoi420方法 +- 捕获到的数据存入一个bytearray,等待rust端调用获取 + +#####方案B 捕获原始数据传入rust进行编码 !等待完善! +- **自带的编码器无法直接控制流量,默认情况输出的帧率比较高,会造成网络堵塞延迟** - 获取编码后的buf - 通过MediaCodec回调获取到可用的数据 - 通过jni传入Rust服务 @@ -56,12 +63,11 @@ MediaProjectionManager -> MediaProjection https://stackoverflow.com/questions/47531742/startforeground-fail-after-upgrade-to-android-8-1 https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context) -// TODO 使用 NotificationCompat 的区别 -
    ### 2.获取控制 -暂时可行的方案是使用安卓无障碍服务 参考droidVNC项目,但droidVNC的实现并不完善,droidVNC没有实现连续触控。 +暂时可行的方案是使用安卓无障碍服务 参考droidVNC项目, +**目前暂无可用连续输入的方案,暂时只能做到控制端鼠标滑动抬起鼠标后才能发送这组控制到安卓端** #### 无障碍服务获取权限 - https://developer.android.com/guide/topics/ui/accessibility/service?hl=zh-cn#manifest @@ -93,24 +99,40 @@ MediaProjectionManager -> MediaProjection android:canPerformGestures="true" // 这里最关键 /> ``` -- 连续手势 https://developer.android.com/guide/topics/ui/accessibility/service?hl=zh-cn#continued-gestures +- ~~连续手势 https://developer.android.com/guide/topics/ui/accessibility/service?hl=zh-cn#continued-gestures~~ + +#### knox.remotecontrol 三星手机 专用控制方式 + +https://docs.samsungknox.com/devref/knox-sdk/reference/com/samsung/android/knox/remotecontrol/package-summary.html +
    ### 3.获取音频输入 https://developer.android.google.cn/guide/topics/media/playback-capture?hl=zh-cn -目前谷歌只开放了Android10系统同步音频内录功能 +**仅安卓10或更高可用** +目前谷歌只开放了Android10及以上系统同步音频内录功能 10之前录音的时候会截取原本系统的音频输出 即 开启内录时候无法在手机上正常使用耳机扬声器输出 +且普通应用的声音默认不会被捕获 + +**安卓10音频输入原理** +- 音频权限相当于是MediaProjection的附属产物 +- 只有在成功获取MediaProjection,开启了ForegroundService才能使用 +- 相比于AudioRecord普通用法使用,将setAudioSource改为setAudioPlaybackCaptureConfig,这里的AudioPlaybackCaptureConfiguration的构建需要使用到之前成功获取的MediaProjection +
    +- **一些注意事项** + - 使用AudioFormat.ENCODING_PCM_FLOAT,数值范围[-1,1]的32位浮点数据,对应了rust端opus编码器的输入格式。 + - libopus库中使用的opus_encode_float,对于输入的音频数据长度有一定要求,安卓端输出的包过大需要分批发送 + - https://stackoverflow.com/questions/46786922/how-to-confirm-opus-encode-buffer-size + - https://docs.rs/audiopus_sys/0.2.2/audiopus_sys/fn.opus_encode_float.html + - > For example, at 48 kHz the permitted values are 120, 240, 480, 960, 1920, and 2880. + - 安卓11自带了opus输出,几年后或许可用 +
    -### 其他 -- Kotlin 与 compose 版本设置问题 - - https://stackoverflow.com/questions/67600344/jetpack-compose-on-kotlin-1-5-0 - - 在根目录的gradle中 设置两个正确对应版本 - ### Rust JVM 互相调用 rust端 引入 jni crate @@ -137,37 +159,106 @@ pub unsafe extern "system" fn Java_com_carriez_flutter_1hbb_MainActivity_init( } ``` -- 注意,原项目包名flutter_hbb 带有下划线,通过安卓的编译提示获得的命名方式为如上。 - -- 使用jni的时候会出现无输出崩溃的情况 +- 注意,原项目包名flutter_hbb 带有下划线,通过安卓的编译提示获得的命名方式为如上的`..._1hbb...`。 +- 使用jni的时候,如果不捕捉错误会出现无输出崩溃的情况 - 将安卓的对象实例(Context)在init的过程中传入rust端, context通过env.new_global_ref()变成全局引用 env.get_java_vm()获取到jvm - 原理上 Rust端通过类找静态方法也可行,但在kotlin端测试失败,会遇到类名找不到,类静态方法找不到等问题,目前仅使用绑定具体context对象即可。 - 将jvm和context 固定到全局变量中等待需要时候引用 - - 使用时,需要确保jvm与当前的线程绑定 jvm.attach_current_thread_permanently() - - 然后通过jvm获得env jvm.get_env() - - 通过env.call_method()方法传入context.as_obj()使用对象的方法 +- ByteArray(Kotlin) + - 会在JVM中编译成为java的byte[] + - rust端通过jbytearray接收,通过jni.rs的方法 +env.convert_byte_array()即可转化为Vec数组 -传递数据 -Kotlin 中的 ByteArray 类 会在JVM中编译成为java的byte[] -byte[]通过jni传递到rust端时 -通过jni.rs的方法 -env.convert_byte_array()即可转化为Vec +- FloatArray(Kotlin) + - JAVA中的float[] + - jni.rs中使用get_float_array_region方法写入到新buf中 -- 关于 sig - - (I)V : 输入为Int 输出为Void - - (I)Ljava/nio/ByteBuffer : 输入为Int 输出为ByteBuffer 对应jni JByteBuffer 且env 有对应解析方法 - - ()[B : 输入为空 输出为byte[](java) == ByteArray(kotlin) == jbytearray(jni env 中有对应解析方法到Vec\) - - 使用JValue枚举定义java变量 +- ByteBuffer(Kotlin/Java) + - +- 关于 sig 一些用例 https://imzy.vip/posts/55704/ + - (I)V : input:Int,output:Void + - (I)Ljava/nio/ByteBuffer : input:Int,output:ByteBuffer. convert method:env.get_direct_buffer_address() + - ()[B : input:void,output:byte[](java) == ByteArray(kotlin) == jbytearray(jni env 中有对应解析方法到Vec\) + - call java的方法时,使用JValue枚举定义java变量 -# BIG TODO -音频 -连续输入 -服务关闭操作 \ No newline at end of file + +
    + +### UI交互相关 +收到无密码登录请求时,1.通知flutterUI页面问询用户2.通知栏通知用户 +- 否 + - 返回给rust端否 java端不做处理 +- 是 + - 返回给rust端是 java端开始采集音视频 + +收到有密码登录请求,rust端可以自动判断 +- 否 + - rust端自动处理返回密码错误 +- 是 + - 通知java端 java端开始采集 同时在通知推送栏中推送消息 + +#### 服务开启与关闭 +1.start listen +安卓端用户手动开始服务监听 开启service +获取视频权限 成功后 通知flutter将图标状态转为已开启 开启rust端的start_all() +然后就可以被其他人请求连接 + +2.login request +验证成功的请求, +安卓端开启视频 音频 输入的采集 +通知rust端logon response + +3.client close +rust端会自动结束 +rust端发送结束指令给安卓端 +安卓端停止各项采集 但服务依然开启 + +4.server close +4-1 +close conn +用户点击断开连接 +安卓端停止各项采集 +发送close指令给rust让rust关闭这个conn + +4-1 +close totally +如果当前有连接则问是否断开 +是则先执行一遍4-1 +然后关闭整个service + + +服务端主动关闭服务 +Config::set_option("stop_service","Y") +服务端再次启动服务 +Config::set_option("stop_service","") + + +### TODO +完善CM 当前连接的状态 控制音频和输入等开关 断开连接等功能 +横屏模式 +首次登录不显示id密码 +安卓前后分离的问题 通过IPC或者广播解耦 + +
    + +### 其他 +- Kotlin 与 compose 版本设置问题 + - https://stackoverflow.com/questions/67600344/jetpack-compose-on-kotlin-1-5-0 + - 在根目录的gradle中 设置两个正确对应版本 +- 如果开发环境中安装了超过一种NDK版本,则会需要在app的build.gradle中指定NDK版本 + ``` + // build.gradle in app + android { + ... + compileSdkVersion 30 + ndkVersion '22.1.7171670' + ... + ``` \ No newline at end of file diff --git a/lib/common.dart b/lib/common.dart index 82c3c754a..9549c8b93 100644 --- a/lib/common.dart +++ b/lib/common.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; import 'dart:async'; import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:flutter_hbb/server_page.dart'; import 'package:tuple/tuple.dart'; import 'dart:io'; +import 'main.dart'; typedef F = String Function(String); @@ -12,6 +14,7 @@ class Translator { class MyTheme { MyTheme._(); + static const Color grayBg = Color(0xFFEEEEEE); static const Color white = Color(0xFFFFFFFF); static const Color accent = Color(0xFF0071FF); @@ -30,6 +33,7 @@ final ButtonStyle flatButtonStyle = TextButton.styleFrom( ); void Function() loadingCancelCallback = null; + void showLoading(String text, BuildContext context) { if (_hasDialog && context != null) { Navigator.pop(context); @@ -71,6 +75,7 @@ void dismissLoading() { } bool _hasDialog = false; + typedef BuildAlertDailog = Tuple3> Function( void Function(void Function())); @@ -107,9 +112,10 @@ void msgbox(String type, String title, String text, BuildContext context, [bool hasCancel]) { var wrap = (String text, void Function() onPressed) => ButtonTheme( padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), - materialTapTargetSize: MaterialTapTargetSize - .shrinkWrap, //limits the touch area to the button area - minWidth: 0, //wraps child's width + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + //limits the touch area to the button area + minWidth: 0, + //wraps child's width height: 0, child: TextButton( style: flatButtonStyle, @@ -165,12 +171,14 @@ class PasswordWidget extends StatefulWidget { class _PasswordWidgetState extends State { bool _passwordVisible = false; + @override Widget build(BuildContext context) { return TextField( autofocus: true, controller: widget.controller, - obscureText: !_passwordVisible, //This will obscure text dynamically + obscureText: !_passwordVisible, + //This will obscure text dynamically keyboardType: TextInputType.visiblePassword, decoration: InputDecoration( labelText: Translator.call('Password'), @@ -202,3 +210,46 @@ Color str2color(String str, [alpha = 0xFF]) { hash = hash % 16777216; return Color((hash & 0xFF7FFF) | (alpha << 24)); } + +toAndroidChannelInit() { + toAndroidChannel.setMethodCallHandler((call) async { + debugPrint("flutter got android msg"); + + try { + switch (call.method) { + case "try_start_without_auth": + { + var peerID = call.arguments["peerID"] as String; + var name = call.arguments["name"] as String; + ServerPage.serverModel.setPeer(false, name: name, id: peerID); + showLoginReqAlert(nowCtx, peerID, name); + debugPrint("from jvm:try_start_without_auth done"); + break; + } + case "start_capture": + { + var peerID = call.arguments["peerID"] as String; + var name = call.arguments["name"] as String; + ServerPage.serverModel.setPeer(true, name: name, id: peerID); + break; + } + case "stop_capture": + { + ServerPage.serverModel.setPeer(false); + break; + } + case "on_permission_changed": + { + var name = call.arguments["name"] as String; + var value = call.arguments["value"] as String == "true"; + debugPrint("from jvm:on_permission_changed,$name:$value"); + ServerPage.serverModel.changeStatue(name, value); + break; + } + } + } catch (e) { + debugPrint("MethodCallHandler err:$e"); + } + return null; + }); +} diff --git a/lib/home_page.dart b/lib/home_page.dart index dcb858510..5a27635b3 100644 --- a/lib/home_page.dart +++ b/lib/home_page.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import 'package:tuple/tuple.dart'; import 'package:package_info/package_info.dart'; import 'package:url_launcher/url_launcher.dart'; import 'dart:async'; import 'common.dart'; +import 'main.dart'; import 'model.dart'; import 'remote_page.dart'; import 'dart:io'; @@ -26,6 +26,7 @@ class _HomePageState extends State { @override void initState() { super.initState(); + nowCtx = context; if (Platform.isAndroid) { Timer(Duration(seconds: 5), () { _updateUrl = FFI.getByName('software_update_url'); @@ -55,10 +56,11 @@ class _HomePageState extends State { PopupMenuItem( child: Text(translate('ID Server')), value: 'id_server'), - PopupMenuItem( - child: Text(translate('Share My Screen')), - value: 'server'), - // TODO only android + Platform.isAndroid + ? PopupMenuItem( + child: Text(translate('Share My Screen')), + value: 'server') + : null, PopupMenuItem( child: Text(translate('About') + ' RustDesk'), value: 'about'), diff --git a/lib/main.dart b/lib/main.dart index 62abe4b25..ba8ffcb27 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,19 +1,26 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_hbb/server_page.dart'; import 'package:provider/provider.dart'; import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:firebase_analytics/observer.dart'; import 'package:firebase_core/firebase_core.dart'; +import 'common.dart'; import 'model.dart'; import 'home_page.dart'; +const toAndroidChannel = MethodChannel("mChannel"); +BuildContext nowCtx; + Future main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); + toAndroidChannelInit(); runApp(App()); } class App extends StatelessWidget { + @override Widget build(BuildContext context) { final analytics = FirebaseAnalytics(); diff --git a/lib/model.dart b/lib/model.dart index db897443d..83ed08238 100644 --- a/lib/model.dart +++ b/lib/model.dart @@ -37,10 +37,15 @@ class FfiModel with ChangeNotifier { bool _direct; get permissions => _permissions; + get initialized => _initialized; + get display => _display; + get secure => _secure; + get direct => _direct; + get pi => _pi; FfiModel() { @@ -245,7 +250,9 @@ class CanvasModel with ChangeNotifier { } double get x => _x; + double get y => _y; + double get scale => _scale; void update(double x, double y, double scale) { @@ -295,7 +302,7 @@ class CanvasModel with ChangeNotifier { notifyListeners(); } - void clear([bool notify=false]) { + void clear([bool notify = false]) { _x = 0; _y = 0; _scale = 1.0; @@ -314,10 +321,15 @@ class CursorModel with ChangeNotifier { double _displayOriginY = 0; ui.Image get image => _image; + double get x => _x - _displayOriginX; + double get y => _y - _displayOriginY; + Offset get offset => Offset(_x, _y); + double get hotx => _hotx; + double get hoty => _hoty; // remote physical display coordinate @@ -506,6 +518,61 @@ class CursorModel with ChangeNotifier { } } +class ServerModel with ChangeNotifier { + bool _mediaOk; + bool _inputOk; + + bool _peerEnabled; + String _peerName; + String _peerID; + + bool get mediaOk => _mediaOk; + + bool get inputOk => _inputOk; + + bool get peerEnabled => _peerEnabled; + + String get peerName => _peerName; + + String get peerID => _peerID; + + ServerModel() { + _mediaOk = false; + _inputOk = false; + _peerEnabled = false; + _peerName = ""; + _peerID = ""; + } + + changeStatue(String name, bool value) { + switch (name) { + case "media": + _mediaOk = value; + break; + case "input": + _inputOk = value; + break; + default: + return; + } + notifyListeners(); + } + + setPeer(bool enabled, {String name = "", String id = ""}) { + _peerEnabled = enabled; + if (name != "") _peerName = name; + if (id != "") _peerID = id; + notifyListeners(); + } + + clearPeer() { + _peerEnabled = false; + _peerName = ""; + _peerID = ""; + notifyListeners(); + } +} + class FFI { static String id = ""; static String _dir = ''; diff --git a/lib/server_page.dart b/lib/server_page.dart index 755fa7001..0e83ca801 100644 --- a/lib/server_page.dart +++ b/lib/server_page.dart @@ -1,34 +1,57 @@ +import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_hbb/model.dart'; +import 'package:provider/provider.dart'; import 'common.dart'; +import 'main.dart'; -class ServerPage extends StatefulWidget { - @override - _ServerPageState createState() => _ServerPageState(); -} +class ServerPage extends StatelessWidget { + static final serverModel = ServerModel(); -class _ServerPageState extends State { @override Widget build(BuildContext context) { - return Scaffold( - backgroundColor: MyTheme.grayBg, - appBar: AppBar( - centerTitle: true, - title: const Text("Share My Screen"), - ), - body: SingleChildScrollView( - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - ServerInfo(), - PermissionChecker(), + // TODO: implement build + return ChangeNotifierProvider.value( + value: serverModel, + child: Scaffold( + backgroundColor: MyTheme.grayBg, + appBar: AppBar( + centerTitle: true, + title: const Text("Share My Screen"), + actions: [ + PopupMenuButton( + itemBuilder: (context) { + return [ + PopupMenuItem( + child: Text("修改服务ID"), + value: "changeID", + enabled: false, + ), + PopupMenuItem( + child: Text("修改服务密码"), + value: "changeID", + enabled: false, + ) + ]; + }, + onSelected: (value) => + debugPrint("PopupMenuItem onSelected:$value")) ], ), - ), - )); + body: SingleChildScrollView( + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + ServerInfo(), + PermissionChecker(), + ConnectionManager(), + SizedBox.fromSize(size: Size(0, 15.0)), // Bottom padding + ], + ), + ), + ))); } } @@ -105,60 +128,62 @@ class PermissionChecker extends StatefulWidget { } class _PermissionCheckerState extends State { - static const toAndroidChannel = MethodChannel("mChannel"); - - var videoOk = false; - var inputOk = false; - var audioOk = false; + @override + void initState() { + super.initState(); + nowCtx = context; + } @override Widget build(BuildContext context) { + final serverModel = Provider.of(context); + return myCard(Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ cardTitle("权限列表"), - PermissionRow("视频权限", videoOk, _toAndroidGetPer), + PermissionRow("媒体权限", serverModel.mediaOk, _toAndroidInitService), const Divider(height: 0), - PermissionRow("音频权限", videoOk, () => {debugPrint("获取视频权限")}), - const Divider(height: 0), - PermissionRow("输入权限", inputOk, _toAndroidCheckInput), + PermissionRow("输入权限", serverModel.inputOk, _toAndroidCheckInput), const Divider(), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - TextButton.icon( - icon: Icon(Icons.play_arrow), - onPressed: _toAndroidStartSer, - label: Text("Start")), - TextButton.icon( + serverModel.mediaOk + ? ElevatedButton.icon( icon: Icon(Icons.stop), - onPressed: _toAndroidStopSer, - label: Text("Stop")), - ], - ) + onPressed: _toAndroidStopService, + label: Text("Stop")) + : ElevatedButton.icon( + icon: Icon(Icons.play_arrow), + onPressed: _toAndroidInitService, + label: Text("Start")), ], )); } +} - Future _toAndroidGetPer() async { - bool res = await toAndroidChannel.invokeMethod("getPer"); - debugPrint("_toAndroidGetPer:$res"); - } - - Future _toAndroidStartSer() async { - bool res = await toAndroidChannel.invokeMethod("startSer"); - debugPrint("_toAndroidStartSer:$res"); - } - - Future _toAndroidStopSer() async { - bool res = await toAndroidChannel.invokeMethod("stopSer"); - debugPrint("_toAndroidStopSer:$res"); - } - - Future _toAndroidCheckInput() async { - bool res = await toAndroidChannel.invokeMethod("checkInput"); - debugPrint("_toAndroidStopSer:$res"); - } +void showLoginReqAlert(BuildContext context, String peerID, String name) { + debugPrint("got try_start_without_auth"); + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text("收到连接请求"), + content: Text("是否同意来自$name:$peerID的控制?"), + actions: [ + TextButton( + child: Text("接受"), + onPressed: () { + FFI.setByName("login_res", "true"); + _toAndroidStartCapture(); + ServerPage.serverModel.setPeer(true); + Navigator.of(context).pop(); + }), + TextButton( + child: Text("不接受"), + onPressed: () { + FFI.setByName("login_res", "false"); + Navigator.of(context).pop(); + }) + ], + )); } class PermissionRow extends StatelessWidget { @@ -183,7 +208,7 @@ class PermissionRow extends StatelessWidget { fontSize: 16.0, color: isOk ? Colors.green : Colors.grey)), ])), TextButton( - onPressed: onPressed, + onPressed: isOk ? null : onPressed, child: const Text( "去开启", style: TextStyle(fontWeight: FontWeight.bold), @@ -193,6 +218,37 @@ class PermissionRow extends StatelessWidget { } } +class ConnectionManager extends StatelessWidget { + @override + Widget build(BuildContext context) { + final serverModel = Provider.of(context); + var info = + "${serverModel.peerName != "" ? serverModel.peerName : "NA"}-${serverModel.peerID != "" ? serverModel.peerID : "NA"}"; + return serverModel.peerEnabled + ? myCard(Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + cardTitle("当前连接"), + Padding( + padding: EdgeInsets.symmetric(vertical: 5.0), + child: Text(info, style: TextStyle(color: Colors.grey)), + ), + ElevatedButton.icon( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.all(Colors.red)), + icon: Icon(Icons.close), + onPressed: () { + FFI.setByName("close_conn"); + _toAndroidStopCapture(); + serverModel.setPeer(false); + }, + label: Text("断开连接")) + ], + )) + : SizedBox.shrink(); + } +} + Widget cardTitle(String text) { return Padding( padding: EdgeInsets.symmetric(vertical: 5.0), @@ -201,18 +257,47 @@ Widget cardTitle(String text) { style: TextStyle( fontFamily: 'WorkSans', fontWeight: FontWeight.bold, - fontSize: 25, + fontSize: 22, color: MyTheme.accent80, ), )); } Widget myCard(Widget child) { - return Card( - margin: EdgeInsets.all(15.0), - child: Padding( - padding: EdgeInsets.symmetric(vertical: 15.0, horizontal: 30.0), - child: child, - ), - ); + return Container( + width: double.maxFinite, + child: Card( + margin: EdgeInsets.fromLTRB(15.0, 15.0, 15.0, 0), + child: Padding( + padding: EdgeInsets.symmetric(vertical: 15.0, horizontal: 30.0), + child: child, + ), + )); +} + +Future _toAndroidInitService() async { + bool res = await toAndroidChannel.invokeMethod("init_service"); + FFI.setByName("start_service"); + debugPrint("_toAndroidInitService:$res"); +} + +Future _toAndroidStartCapture() async { + bool res = await toAndroidChannel.invokeMethod("start_capture"); + debugPrint("_toAndroidStartCapture:$res"); +} + +Future _toAndroidStopCapture() async { + bool res = await toAndroidChannel.invokeMethod("stop_capture"); + debugPrint("_toAndroidStopCapture:$res"); +} + +Future _toAndroidStopService() async { + FFI.setByName("stop_service"); + bool res = await toAndroidChannel.invokeMethod("stop_service"); + debugPrint("_toAndroidStopSer:$res"); +} + +Future _toAndroidCheckInput() async { + bool res = await toAndroidChannel.invokeMethod("check_input"); + debugPrint("_toAndroidStopSer:$res"); } From 47ca02e2399a3c1d33bf475335dfa3f43a81a8ac Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 3 Feb 2022 00:53:59 +0800 Subject: [PATCH 266/422] prepare for mouse --- lib/model.dart | 55 +++++++++++++++++++++++++++---- lib/native_model.dart | 4 +++ lib/remote_page.dart | 75 +++++++++++++++++++++++++------------------ lib/web_model.dart | 43 ++++++++++++++++++++++++- 4 files changed, 139 insertions(+), 38 deletions(-) diff --git a/lib/model.dart b/lib/model.dart index b6eca7642..687b113f3 100644 --- a/lib/model.dart +++ b/lib/model.dart @@ -200,10 +200,14 @@ class ImageModel with ChangeNotifier { void update(ui.Image image) { if (_image == null && image != null) { - final size = MediaQueryData.fromWindow(ui.window).size; - final xscale = size.width / image.width; - final yscale = size.height / image.height; - FFI.canvasModel.scale = max(xscale, yscale); + if (isDesktop) { + FFI.canvasModel.updateViewStyle(); + } else { + final size = MediaQueryData.fromWindow(ui.window).size; + final xscale = size.width / image.width; + final yscale = size.height / image.height; + FFI.canvasModel.scale = max(xscale, yscale); + } initializeCursorAndCanvas(); } _image = image; @@ -240,6 +244,29 @@ class CanvasModel with ChangeNotifier { double get y => _y; double get scale => _scale; + void updateViewStyle() { + final s = FFI.getByName('peer_option', 'view-style'); + final size = MediaQueryData.fromWindow(ui.window).size; + final s1 = size.width / FFI.ffiModel.display.width; + final s2 = size.height / FFI.ffiModel.display.height; + if (s == 'shrink') { + final s = s1 < s2 ? s1 : s2; + if (s < 1) { + _scale = s; + } + } else if (s == 'stretch') { + final s = s1 > s2 ? s1 : s2; + if (s > 1) { + _scale = s; + } + } else { + _scale = 1; + } + _x = (size.width - FFI.ffiModel.display.width * _scale) / 2; + _y = (size.height - FFI.ffiModel.display.height * _scale) / 2; + notifyListeners(); + } + void update(double x, double y, double scale) { _x = x; _y = y; @@ -258,8 +285,12 @@ class CanvasModel with ChangeNotifier { } void resetOffset() { - _x = 0; - _y = 0; + if (isDesktop) { + updateViewStyle(); + } else { + _x = 0; + _y = 0; + } notifyListeners(); } @@ -624,6 +655,18 @@ class FFI { static Future getVersion() async { return await PlatformFFI.getVersion(); } + + static handleMouse(Map evt) { + // + } + + static listenToMouse(bool yesOrNo) { + if (yesOrNo) { + PlatformFFI.startDesktopWebListener(handleMouse); + } else { + PlatformFFI.stopDesktopWebListener(); + } + } } class Peer { diff --git a/lib/native_model.dart b/lib/native_model.dart index 2c9df78b3..42242b340 100644 --- a/lib/native_model.dart +++ b/lib/native_model.dart @@ -101,6 +101,10 @@ class PlatformFFI { print(e); } } + + static void startDesktopWebListener( + Function(Map) handleMouse) {} + static void stopDesktopWebListener() {} } final localeName = Platform.localeName; diff --git a/lib/remote_page.dart b/lib/remote_page.dart index 63cfb92d4..bd632b2ef 100644 --- a/lib/remote_page.dart +++ b/lib/remote_page.dart @@ -242,6 +242,7 @@ class _RemotePageState extends State { return false; }, child: Scaffold( + // resizeToAvoidBottomInset: true, floatingActionButton: !showActionButton ? null : FloatingActionButton( @@ -264,8 +265,8 @@ class _RemotePageState extends State { child: Container( color: Colors.black, child: isDesktop - ? getBodyForDesktop() - : SafeArea(child: getBodyForMobile())), + ? getBodyForDesktopWithListener() + : SafeArea(child: getBodyForMobileWithGuesture())), )), ); } @@ -344,7 +345,7 @@ class _RemotePageState extends State { ); } - Widget getBodyForMobile() { + Widget getBodyForMobileWithGuesture() { return GestureDetector( onLongPress: () { if (_drag || _scroll) return; @@ -406,36 +407,47 @@ class _RemotePageState extends State { } } }, - child: Container( - color: MyTheme.canvasColor, - child: Stack(children: [ - ImagePaint(), - CursorPaint(), - getHelpTools(), - SizedBox( - width: 0, - height: 0, - child: !_showEdit - ? Container() - : TextFormField( - textInputAction: TextInputAction.newline, - autocorrect: false, - enableSuggestions: false, - focusNode: _focusNode, - maxLines: null, - initialValue: - _value, // trick way to make backspace work always - keyboardType: TextInputType.multiline, - onChanged: handleInput, - ), - ), - ]))); + child: getBodyForMobile()); } - Widget getBodyForDesktop() { - return GestureDetector( - onTapDown: (details) => {}, - onTapUp: (details) => {}, + Widget getBodyForMobile() { + return Container( + color: MyTheme.canvasColor, + child: Stack(children: [ + ImagePaint(), + CursorPaint(), + getHelpTools(), + SizedBox( + width: 0, + height: 0, + child: !_showEdit + ? Container() + : TextFormField( + textInputAction: TextInputAction.newline, + autocorrect: false, + enableSuggestions: false, + focusNode: _focusNode, + maxLines: null, + initialValue: + _value, // trick way to make backspace work always + keyboardType: TextInputType.multiline, + onChanged: handleInput, + ), + ), + ])); + } + + Widget getBodyForDesktopWithListener() { + print(FFI.ffiModel.display.width); + return MouseRegion( + onEnter: (event) { + print('enter'); + FFI.listenToMouse(true); + }, + onExit: (event) { + print('exit'); + FFI.listenToMouse(false); + }, child: Container( color: MyTheme.canvasColor, child: Stack(children: [ImagePaint(), CursorPaint()]))); @@ -932,6 +944,7 @@ void showOptions(BuildContext context) { viewStyle = value; FFI.setByName( 'peer_option', '{"name": "view-style", "value": "$value"}'); + FFI.canvasModel.updateViewStyle(); }); }; return Tuple3( diff --git a/lib/web_model.dart b/lib/web_model.dart index 807a42ab9..156196312 100644 --- a/lib/web_model.dart +++ b/lib/web_model.dart @@ -1,6 +1,10 @@ import 'dart:typed_data'; import 'dart:js' as js; import 'common.dart'; +import 'dart:html'; +import 'dart:async'; + +final List> mouselisteners = []; class PlatformFFI { static void clearRgbaFrame() {} @@ -22,10 +26,47 @@ class PlatformFFI { } static Future init() async { + window.document.onContextMenu.listen((evt) => evt.preventDefault()); isWeb = true; isDesktop = !js.context.callMethod('isMobile'); js.context.callMethod('init'); } + + // MouseRegion onHover not work for mouse move when right button down + static void startDesktopWebListener( + Function(Map) handleMouse) { + // document.body.getElementsByTagName('flt-glass-pane')[0].style.cursor = 'none'; + mouselisteners.add(window.document.onMouseMove + .listen((evt) => handleMouse(getEvent(evt)))); + mouselisteners.add(window.document.onMouseDown + .listen((evt) => handleMouse(getEvent(evt)))); + mouselisteners.add( + window.document.onMouseUp.listen((evt) => handleMouse(getEvent(evt)))); + mouselisteners.add(window.document.onMouseWheel.listen((evt) => {})); + } + + static void stopDesktopWebListener() { + mouselisteners.forEach((l) { + l.cancel(); + }); + mouselisteners.clear(); + } } -final localeName = js.context.callMethod('getLanguage') as String; +Map getEvent(MouseEvent evt) { + // https://github.com/novnc/noVNC/blob/679b45fa3b453c7cf32f4b4455f4814818ecf161/core/rfb.js + // https://developer.mozilla.org/zh-CN/docs/Web/API/Element/mousedown_event + final out = {}; + out['type'] = evt.type; + out['x'] = evt.client.x; + out['y'] = evt.client.y; + out['ctrl'] = evt.ctrlKey; + out['shift'] = evt.shiftKey; + out['alt'] = evt.altKey; + out['meta'] = evt.metaKey; + out['buttons'] = evt + .buttons; // left button: 1, right button: 2, middle button: 4, 1 | 2 = 3 (left + right) + return out; +} + +final localeName = window.navigator.language; From 73da1583da22ddb6318417f0bc601faeccc91ead Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 3 Feb 2022 17:19:25 +0800 Subject: [PATCH 267/422] mouse works --- lib/model.dart | 57 ++++++++++++++++++++++++++++++++++++++++---- lib/remote_page.dart | 1 - lib/web_model.dart | 38 +++++++++++++++++++++-------- 3 files changed, 81 insertions(+), 15 deletions(-) diff --git a/lib/model.dart b/lib/model.dart index 687b113f3..29482492c 100644 --- a/lib/model.dart +++ b/lib/model.dart @@ -367,7 +367,6 @@ class CursorModel with ChangeNotifier { void touch(double x, double y, bool right) { final scale = FFI.canvasModel.scale; - print(scale); final xoffset = FFI.canvasModel.x; final yoffset = FFI.canvasModel.y; _x = (x - xoffset) / scale + _displayOriginX; @@ -633,8 +632,10 @@ class FFI { } static void close() { - savePreference(id, cursorModel.x, cursorModel.y, canvasModel.x, - canvasModel.y, canvasModel.scale, ffiModel.pi.currentDisplay); + if (FFI.imageModel.image != null && !isDesktop) { + savePreference(id, cursorModel.x, cursorModel.y, canvasModel.x, + canvasModel.y, canvasModel.scale, ffiModel.pi.currentDisplay); + } id = ""; setByName('close', ''); imageModel.update(null); @@ -657,7 +658,54 @@ class FFI { } static handleMouse(Map evt) { - // + var type = ''; + switch (evt['type']) { + case 'mousedown': + type = 'down'; + break; + case 'mouseup': + type = 'up'; + break; + case 'mousemove': + break; + default: + return; + } + evt['type'] = type; + var x = evt['x']; + var y = evt['y']; + final d = FFI.ffiModel.display; + x -= FFI.canvasModel.x; + y -= FFI.canvasModel.y; + if (x < 0 || x > d.width || y < 0 || y > d.height) { + return; + } + x += d.x; + y += d.y; + if (type != '') { + x = 0; + y = 0; + } + evt['x'] = '$x'; + evt['y'] = '$y'; + var buttons = ''; + switch (evt['buttons']) { + case 1: + buttons = 'left'; + break; + case 2: + buttons = 'right'; + break; + case 4: + buttons = 'wheel'; + break; + } + evt['buttons'] = buttons; + if (evt['ctrl'] != true) evt.remove('ctrl'); + if (evt['shift'] != true) evt.remove('shift'); + if (evt['alt'] != true) evt.remove('alt'); + if (evt['command'] != true) evt.remove('command'); + setByName('send_mouse', json.encode(evt)); } static listenToMouse(bool yesOrNo) { @@ -713,6 +761,7 @@ void savePreference(String id, double xCursor, double yCursor, double xCanvas, } Future> getPreference(String id) async { + if (!isDesktop) return null; SharedPreferences prefs = await SharedPreferences.getInstance(); var p = prefs.getString('peer' + id); if (p == null) return null; diff --git a/lib/remote_page.dart b/lib/remote_page.dart index bd632b2ef..4a2ccbdf5 100644 --- a/lib/remote_page.dart +++ b/lib/remote_page.dart @@ -438,7 +438,6 @@ class _RemotePageState extends State { } Widget getBodyForDesktopWithListener() { - print(FFI.ffiModel.display.width); return MouseRegion( onEnter: (event) { print('enter'); diff --git a/lib/web_model.dart b/lib/web_model.dart index 156196312..eeab1eff8 100644 --- a/lib/web_model.dart +++ b/lib/web_model.dart @@ -4,7 +4,8 @@ import 'common.dart'; import 'dart:html'; import 'dart:async'; -final List> mouselisteners = []; +final List> mouseListeners = []; +int lastMouseDownButtons = 0; class PlatformFFI { static void clearRgbaFrame() {} @@ -26,7 +27,6 @@ class PlatformFFI { } static Future init() async { - window.document.onContextMenu.listen((evt) => evt.preventDefault()); isWeb = true; isDesktop = !js.context.callMethod('isMobile'); js.context.callMethod('init'); @@ -35,37 +35,55 @@ class PlatformFFI { // MouseRegion onHover not work for mouse move when right button down static void startDesktopWebListener( Function(Map) handleMouse) { + lastMouseDownButtons = 0; // document.body.getElementsByTagName('flt-glass-pane')[0].style.cursor = 'none'; - mouselisteners.add(window.document.onMouseMove + mouseListeners.add(window.document.onMouseMove .listen((evt) => handleMouse(getEvent(evt)))); - mouselisteners.add(window.document.onMouseDown + mouseListeners.add(window.document.onMouseDown .listen((evt) => handleMouse(getEvent(evt)))); - mouselisteners.add( + mouseListeners.add( window.document.onMouseUp.listen((evt) => handleMouse(getEvent(evt)))); - mouselisteners.add(window.document.onMouseWheel.listen((evt) => {})); + mouseListeners.add(window.document.onMouseWheel.listen((evt) { + var dx = evt.deltaX; + var dy = evt.deltaY; + if (dx > 0) + dx = -1; + else if (dx < 0) dx = 1; + if (dy > 0) + dy = -1; + else if (dy < 0) dy = 1; + setByName('send_mouse', '{"type": "wheel", "x": "$dx", "y": "$dy"}'); + })); + mouseListeners.add( + window.document.onContextMenu.listen((evt) => evt.preventDefault())); } static void stopDesktopWebListener() { - mouselisteners.forEach((l) { + mouseListeners.forEach((l) { l.cancel(); }); - mouselisteners.clear(); + mouseListeners.clear(); } } Map getEvent(MouseEvent evt) { // https://github.com/novnc/noVNC/blob/679b45fa3b453c7cf32f4b4455f4814818ecf161/core/rfb.js // https://developer.mozilla.org/zh-CN/docs/Web/API/Element/mousedown_event - final out = {}; + final Map out = {}; out['type'] = evt.type; out['x'] = evt.client.x; out['y'] = evt.client.y; out['ctrl'] = evt.ctrlKey; out['shift'] = evt.shiftKey; out['alt'] = evt.altKey; - out['meta'] = evt.metaKey; + out['command'] = evt.metaKey; out['buttons'] = evt .buttons; // left button: 1, right button: 2, middle button: 4, 1 | 2 = 3 (left + right) + if (evt.buttons != 0) { + lastMouseDownButtons = evt.buttons; + } else { + out['buttons'] = lastMouseDownButtons; + } return out; } From 9ff3bd74c9c8f5c8cf6c14a0bebe6c0bcff1ec6f Mon Sep 17 00:00:00 2001 From: open-trade Date: Thu, 3 Feb 2022 21:26:35 +0800 Subject: [PATCH 268/422] modify input key --- src/connection.ts | 17 ++++++++++++++++- src/globals.js | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/connection.ts b/src/connection.ts index 3337d3505..c0e4138ba 100644 --- a/src/connection.ts +++ b/src/connection.ts @@ -450,6 +450,8 @@ export default class Connection { inputKey( name: string, + down: boolean, + press: boolean, alt: Boolean, ctrl: Boolean, shift: Boolean, @@ -457,7 +459,20 @@ export default class Connection { ) { const key_event = mapKey(name); if (!key_event) return; - key_event.press = true; + if (alt && name == 'VK_MENU') { + alt = false; + } + if (ctrl && name == 'VK_CONTROL') { + ctrl = false; + } + if (shift && name == 'VK_SHIFT') { + shift = false; + } + if (command && name == 'Meta') { + command = false; + } + key_event.down = down; + key_event.press = press; key_event.modifiers = this.getMod(alt, ctrl, shift, command); this._ws?.sendMessage({ key_event }); } diff --git a/src/globals.js b/src/globals.js index b0721f18c..f67703cd1 100644 --- a/src/globals.js +++ b/src/globals.js @@ -181,7 +181,7 @@ window.setByName = (name, value) => { break; case 'input_key': value = JSON.parse(value); - curConn.inputKey(value.name, value.alt || false, value.ctrl || false, value.shift || false, value.command || false); + curConn.inputKey(value.name, value.down || false, value.press || false, value.alt || false, value.ctrl || false, value.shift || false, value.command || false); break; case 'input_string': curConn.inputString(value); From bf3fc140492f658d73c06c7866a7c83699973a1a Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 4 Feb 2022 11:52:54 +0800 Subject: [PATCH 269/422] add press --- lib/model.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/model.dart b/lib/model.dart index 29482492c..46a6115ef 100644 --- a/lib/model.dart +++ b/lib/model.dart @@ -581,7 +581,8 @@ class FFI { static void inputKey(String name) { if (!ffiModel.keyboard()) return; - setByName('input_key', json.encode(modify({'name': name}))); + setByName( + 'input_key', json.encode(modify({'name': name, 'press': 'true'}))); } static void moveMouse(double x, double y) { From b348b3fdc8da6b386273f914e3545f99a24d99b7 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sat, 5 Feb 2022 01:55:23 +0800 Subject: [PATCH 270/422] video_ack_required --- gen_js_from_hbb.py | 5 + index.html | 2 +- ogvjs-1.8.6/COPYING | 21 + ogvjs-1.8.6/COPYING-dav1d.txt | 23 ++ ogvjs-1.8.6/COPYING-ogg.txt | 28 ++ ogvjs-1.8.6/COPYING-opus.txt | 44 ++ ogvjs-1.8.6/COPYING-theora.txt | 28 ++ ogvjs-1.8.6/COPYING-vorbis.txt | 28 ++ ogvjs-1.8.6/LICENSE-nestegg.txt | 13 + ogvjs-1.8.6/LICENSE-vpx.txt | 31 ++ ogvjs-1.8.6/PATENTS-vpx.txt | 23 ++ ogvjs-1.8.6/README.md | 391 ++++++++++++++++++ ogvjs-1.8.6/ogv-decoder-audio-opus-wasm.js | 39 ++ ogvjs-1.8.6/ogv-decoder-audio-opus-wasm.wasm | Bin 0 -> 143417 bytes ogvjs-1.8.6/ogv-decoder-audio-vorbis-wasm.js | 40 ++ .../ogv-decoder-audio-vorbis-wasm.wasm | Bin 0 -> 155787 bytes ogvjs-1.8.6/ogv-decoder-video-av1-mt-wasm.js | 21 + .../ogv-decoder-video-av1-mt-wasm.wasm | Bin 0 -> 418335 bytes .../ogv-decoder-video-av1-mt-wasm.worker.js | 1 + .../ogv-decoder-video-av1-simd-mt-wasm.js | 21 + .../ogv-decoder-video-av1-simd-mt-wasm.wasm | Bin 0 -> 495935 bytes ...v-decoder-video-av1-simd-mt-wasm.worker.js | 1 + .../ogv-decoder-video-av1-simd-wasm.js | 43 ++ .../ogv-decoder-video-av1-simd-wasm.wasm | Bin 0 -> 484008 bytes ogvjs-1.8.6/ogv-decoder-video-av1-wasm.js | 43 ++ ogvjs-1.8.6/ogv-decoder-video-av1-wasm.wasm | Bin 0 -> 406454 bytes ogvjs-1.8.6/ogv-decoder-video-theora-wasm.js | 42 ++ .../ogv-decoder-video-theora-wasm.wasm | Bin 0 -> 48923 bytes ogvjs-1.8.6/ogv-decoder-video-vp8-mt-wasm.js | 21 + .../ogv-decoder-video-vp8-mt-wasm.wasm | Bin 0 -> 139143 bytes .../ogv-decoder-video-vp8-mt-wasm.worker.js | 1 + ogvjs-1.8.6/ogv-decoder-video-vp8-wasm.js | 44 ++ ogvjs-1.8.6/ogv-decoder-video-vp8-wasm.wasm | Bin 0 -> 113790 bytes ogvjs-1.8.6/ogv-decoder-video-vp9-mt-wasm.js | 21 + .../ogv-decoder-video-vp9-mt-wasm.wasm | Bin 0 -> 259095 bytes .../ogv-decoder-video-vp9-mt-wasm.worker.js | 1 + .../ogv-decoder-video-vp9-simd-mt-wasm.js | 21 + .../ogv-decoder-video-vp9-simd-mt-wasm.wasm | Bin 0 -> 277701 bytes ...v-decoder-video-vp9-simd-mt-wasm.worker.js | 1 + .../ogv-decoder-video-vp9-simd-wasm.js | 45 ++ .../ogv-decoder-video-vp9-simd-wasm.wasm | Bin 0 -> 260523 bytes ogvjs-1.8.6/ogv-decoder-video-vp9-wasm.js | 45 ++ ogvjs-1.8.6/ogv-decoder-video-vp9-wasm.wasm | Bin 0 -> 241857 bytes ogvjs-1.8.6/ogv-demuxer-ogg-wasm.js | 43 ++ ogvjs-1.8.6/ogv-demuxer-ogg-wasm.wasm | Bin 0 -> 39042 bytes ogvjs-1.8.6/ogv-demuxer-webm-wasm.js | 46 +++ ogvjs-1.8.6/ogv-demuxer-webm-wasm.wasm | Bin 0 -> 43838 bytes ogvjs-1.8.6/ogv-es2017.js | 2 + ogvjs-1.8.6/ogv-support.js | 1 + ogvjs-1.8.6/ogv-version.js | 1 + ogvjs-1.8.6/ogv-worker-audio.js | 1 + ogvjs-1.8.6/ogv-worker-video.js | 1 + ogvjs-1.8.6/ogv.js | 2 + package.json | 1 - src/codec.js | 9 +- src/common.ts | 5 +- src/connection.ts | 58 ++- src/gen_js_from_hbb.ts | 12 +- src/globals.js | 7 +- src/message.ts | 28 ++ src/ui.js | 1 + src/websock.ts | 33 +- yarn.lock | 19 - yuv.js | 4 +- yuv_rgb.c | 3 + 65 files changed, 1307 insertions(+), 58 deletions(-) create mode 100644 ogvjs-1.8.6/COPYING create mode 100644 ogvjs-1.8.6/COPYING-dav1d.txt create mode 100644 ogvjs-1.8.6/COPYING-ogg.txt create mode 100644 ogvjs-1.8.6/COPYING-opus.txt create mode 100644 ogvjs-1.8.6/COPYING-theora.txt create mode 100644 ogvjs-1.8.6/COPYING-vorbis.txt create mode 100644 ogvjs-1.8.6/LICENSE-nestegg.txt create mode 100644 ogvjs-1.8.6/LICENSE-vpx.txt create mode 100644 ogvjs-1.8.6/PATENTS-vpx.txt create mode 100644 ogvjs-1.8.6/README.md create mode 100644 ogvjs-1.8.6/ogv-decoder-audio-opus-wasm.js create mode 100755 ogvjs-1.8.6/ogv-decoder-audio-opus-wasm.wasm create mode 100644 ogvjs-1.8.6/ogv-decoder-audio-vorbis-wasm.js create mode 100755 ogvjs-1.8.6/ogv-decoder-audio-vorbis-wasm.wasm create mode 100644 ogvjs-1.8.6/ogv-decoder-video-av1-mt-wasm.js create mode 100755 ogvjs-1.8.6/ogv-decoder-video-av1-mt-wasm.wasm create mode 100644 ogvjs-1.8.6/ogv-decoder-video-av1-mt-wasm.worker.js create mode 100644 ogvjs-1.8.6/ogv-decoder-video-av1-simd-mt-wasm.js create mode 100755 ogvjs-1.8.6/ogv-decoder-video-av1-simd-mt-wasm.wasm create mode 100644 ogvjs-1.8.6/ogv-decoder-video-av1-simd-mt-wasm.worker.js create mode 100644 ogvjs-1.8.6/ogv-decoder-video-av1-simd-wasm.js create mode 100755 ogvjs-1.8.6/ogv-decoder-video-av1-simd-wasm.wasm create mode 100644 ogvjs-1.8.6/ogv-decoder-video-av1-wasm.js create mode 100755 ogvjs-1.8.6/ogv-decoder-video-av1-wasm.wasm create mode 100644 ogvjs-1.8.6/ogv-decoder-video-theora-wasm.js create mode 100755 ogvjs-1.8.6/ogv-decoder-video-theora-wasm.wasm create mode 100644 ogvjs-1.8.6/ogv-decoder-video-vp8-mt-wasm.js create mode 100755 ogvjs-1.8.6/ogv-decoder-video-vp8-mt-wasm.wasm create mode 100644 ogvjs-1.8.6/ogv-decoder-video-vp8-mt-wasm.worker.js create mode 100644 ogvjs-1.8.6/ogv-decoder-video-vp8-wasm.js create mode 100755 ogvjs-1.8.6/ogv-decoder-video-vp8-wasm.wasm create mode 100644 ogvjs-1.8.6/ogv-decoder-video-vp9-mt-wasm.js create mode 100755 ogvjs-1.8.6/ogv-decoder-video-vp9-mt-wasm.wasm create mode 100644 ogvjs-1.8.6/ogv-decoder-video-vp9-mt-wasm.worker.js create mode 100644 ogvjs-1.8.6/ogv-decoder-video-vp9-simd-mt-wasm.js create mode 100755 ogvjs-1.8.6/ogv-decoder-video-vp9-simd-mt-wasm.wasm create mode 100644 ogvjs-1.8.6/ogv-decoder-video-vp9-simd-mt-wasm.worker.js create mode 100644 ogvjs-1.8.6/ogv-decoder-video-vp9-simd-wasm.js create mode 100755 ogvjs-1.8.6/ogv-decoder-video-vp9-simd-wasm.wasm create mode 100644 ogvjs-1.8.6/ogv-decoder-video-vp9-wasm.js create mode 100755 ogvjs-1.8.6/ogv-decoder-video-vp9-wasm.wasm create mode 100644 ogvjs-1.8.6/ogv-demuxer-ogg-wasm.js create mode 100755 ogvjs-1.8.6/ogv-demuxer-ogg-wasm.wasm create mode 100644 ogvjs-1.8.6/ogv-demuxer-webm-wasm.js create mode 100755 ogvjs-1.8.6/ogv-demuxer-webm-wasm.wasm create mode 100644 ogvjs-1.8.6/ogv-es2017.js create mode 100644 ogvjs-1.8.6/ogv-support.js create mode 100644 ogvjs-1.8.6/ogv-version.js create mode 100644 ogvjs-1.8.6/ogv-worker-audio.js create mode 100644 ogvjs-1.8.6/ogv-worker-video.js create mode 100644 ogvjs-1.8.6/ogv.js diff --git a/gen_js_from_hbb.py b/gen_js_from_hbb.py index 024cef387..0ee6e40c4 100755 --- a/gen_js_from_hbb.py +++ b/gen_js_from_hbb.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +import re import os import glob from tabnanny import check @@ -30,6 +31,7 @@ def main(): check_if_retry[1] = False continue if check_if_retry[1]: + ln = removeComment(ln) check_if_retry[0] += ln + '\n' if 'KEY_MAP' in ln: KEY_MAP[1] = True @@ -38,6 +40,7 @@ def main(): KEY_MAP[1] = False continue if KEY_MAP[1] and ln.startswith('('): + ln = removeComment(ln) toks = ln.split('", Key::') assert(len(toks) == 2) a = toks[0][2:] @@ -56,5 +59,7 @@ def main(): print('export const ' + ln) +def removeComment(ln): + return re.sub('\s+\/\/.*$', '', ln) main() \ No newline at end of file diff --git a/index.html b/index.html index aef6abe0f..93d956b74 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ - + Vite App diff --git a/ogvjs-1.8.6/COPYING b/ogvjs-1.8.6/COPYING new file mode 100644 index 000000000..56c4368fa --- /dev/null +++ b/ogvjs-1.8.6/COPYING @@ -0,0 +1,21 @@ +ogv.js wrapper and player code + +Copyright (c) 2013-2019 Brion Vibber and other contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/ogvjs-1.8.6/COPYING-dav1d.txt b/ogvjs-1.8.6/COPYING-dav1d.txt new file mode 100644 index 000000000..875b138ec --- /dev/null +++ b/ogvjs-1.8.6/COPYING-dav1d.txt @@ -0,0 +1,23 @@ +Copyright © 2018-2019, VideoLAN and dav1d authors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ogvjs-1.8.6/COPYING-ogg.txt b/ogvjs-1.8.6/COPYING-ogg.txt new file mode 100644 index 000000000..6111c6c5a --- /dev/null +++ b/ogvjs-1.8.6/COPYING-ogg.txt @@ -0,0 +1,28 @@ +Copyright (c) 2002, Xiph.org Foundation + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of the Xiph.org Foundation nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ogvjs-1.8.6/COPYING-opus.txt b/ogvjs-1.8.6/COPYING-opus.txt new file mode 100644 index 000000000..9c739c34a --- /dev/null +++ b/ogvjs-1.8.6/COPYING-opus.txt @@ -0,0 +1,44 @@ +Copyright 2001-2011 Xiph.Org, Skype Limited, Octasic, + Jean-Marc Valin, Timothy B. Terriberry, + CSIRO, Gregory Maxwell, Mark Borgerding, + Erik de Castro Lopo + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of Internet Society, IETF or IETF Trust, nor the +names of specific contributors, may be used to endorse or promote +products derived from this software without specific prior written +permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Opus is subject to the royalty-free patent licenses which are +specified at: + +Xiph.Org Foundation: +https://datatracker.ietf.org/ipr/1524/ + +Microsoft Corporation: +https://datatracker.ietf.org/ipr/1914/ + +Broadcom Corporation: +https://datatracker.ietf.org/ipr/1526/ diff --git a/ogvjs-1.8.6/COPYING-theora.txt b/ogvjs-1.8.6/COPYING-theora.txt new file mode 100644 index 000000000..c8ccce4ff --- /dev/null +++ b/ogvjs-1.8.6/COPYING-theora.txt @@ -0,0 +1,28 @@ +Copyright (C) 2002-2009 Xiph.org Foundation + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of the Xiph.org Foundation nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ogvjs-1.8.6/COPYING-vorbis.txt b/ogvjs-1.8.6/COPYING-vorbis.txt new file mode 100644 index 000000000..153b926a1 --- /dev/null +++ b/ogvjs-1.8.6/COPYING-vorbis.txt @@ -0,0 +1,28 @@ +Copyright (c) 2002-2018 Xiph.org Foundation + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of the Xiph.org Foundation nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ogvjs-1.8.6/LICENSE-nestegg.txt b/ogvjs-1.8.6/LICENSE-nestegg.txt new file mode 100644 index 000000000..a67984a61 --- /dev/null +++ b/ogvjs-1.8.6/LICENSE-nestegg.txt @@ -0,0 +1,13 @@ +Copyright © 2010 Mozilla Foundation + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/ogvjs-1.8.6/LICENSE-vpx.txt b/ogvjs-1.8.6/LICENSE-vpx.txt new file mode 100644 index 000000000..1ce44343c --- /dev/null +++ b/ogvjs-1.8.6/LICENSE-vpx.txt @@ -0,0 +1,31 @@ +Copyright (c) 2010, The WebM Project authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Google, nor the WebM Project, nor the names + of its contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/ogvjs-1.8.6/PATENTS-vpx.txt b/ogvjs-1.8.6/PATENTS-vpx.txt new file mode 100644 index 000000000..caedf607e --- /dev/null +++ b/ogvjs-1.8.6/PATENTS-vpx.txt @@ -0,0 +1,23 @@ +Additional IP Rights Grant (Patents) +------------------------------------ + +"These implementations" means the copyrightable works that implement the WebM +codecs distributed by Google as part of the WebM Project. + +Google hereby grants to you a perpetual, worldwide, non-exclusive, no-charge, +royalty-free, irrevocable (except as stated in this section) patent license to +make, have made, use, offer to sell, sell, import, transfer, and otherwise +run, modify and propagate the contents of these implementations of WebM, where +such license applies only to those patent claims, both currently owned by +Google and acquired in the future, licensable by Google that are necessarily +infringed by these implementations of WebM. This grant does not include claims +that would be infringed only as a consequence of further modification of these +implementations. If you or your agent or exclusive licensee institute or order +or agree to the institution of patent litigation or any other patent +enforcement activity against any entity (including a cross-claim or +counterclaim in a lawsuit) alleging that any of these implementations of WebM +or any code incorporated within any of these implementations of WebM +constitute direct or contributory patent infringement, or inducement of +patent infringement, then any patent rights granted to you under this License +for these implementations of WebM shall terminate as of the date such +litigation is filed. diff --git a/ogvjs-1.8.6/README.md b/ogvjs-1.8.6/README.md new file mode 100644 index 000000000..cb7e249d5 --- /dev/null +++ b/ogvjs-1.8.6/README.md @@ -0,0 +1,391 @@ +ogv.js +====== + +Media decoder and player for Ogg Vorbis/Opus/Theora and WebM VP8/VP9/AV1 video. + +Based around libogg, libvorbis, libtheora, libopus, libvpx, libnestegg and dav1d compiled to JavaScript and WebAssembly with Emscripten. + +## Updates + +1.8.6 - 2022-01-12 +* Bump to yuv-canvas +* Fix demo for removal of video-canvas mode + +1.8.5 - 2022-01-11 +* Remove unnecessary user-agent checks +* Remove flaky, obsolete support for faking CSS `object-fit` +* Remove experimental support for streaming `` into `

    xpR@|)~8MkTyftD$c7VelT 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 R#&WV{U>-O_%?as?{txAm!_WW# literal 0 HcmV?d00001 diff --git a/ogvjs-1.8.6/ogv-decoder-audio-vorbis-wasm.js b/ogvjs-1.8.6/ogv-decoder-audio-vorbis-wasm.js new file mode 100644 index 000000000..60c4306e9 --- /dev/null +++ b/ogvjs-1.8.6/ogv-decoder-audio-vorbis-wasm.js @@ -0,0 +1,40 @@ + +var OGVDecoderAudioVorbisW = (() => { + var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; + if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename; + return ( +function(OGVDecoderAudioVorbisW) { + OGVDecoderAudioVorbisW = OGVDecoderAudioVorbisW || {}; + + +var b;b||(b=typeof OGVDecoderAudioVorbisW !== 'undefined' ? OGVDecoderAudioVorbisW : {});var g=Object.assign,h,m;b.ready=new Promise(function(a,c){h=a;m=c});var n=b,p=g({},b),q=(a,c)=>{throw c;},r="object"===typeof window,t="function"===typeof importScripts,u="",v,w,x,fs,y,z; +if("object"===typeof process&&"object"===typeof process.versions&&"string"===typeof process.versions.node)u=t?require("path").dirname(u)+"/":__dirname+"/",z=function(){y||(fs=require("fs"),y=require("path"))},v=function(a,c){z();a=y.normalize(a);return fs.readFileSync(a,c?null:"utf8")},x=function(a){a=v(a,!0);a.buffer||(a=new Uint8Array(a));return a},w=function(a,c,e){z();a=y.normalize(a);fs.readFile(a,function(d,f){d?e(d):c(f.buffer)})},1{if(noExitRuntime||0=T.length&&(T.length=a+1),T[a]=c=K.get(a));return c} +var ha={a:function(a,c,e){I.copyWithin(a,c,c+e)},b:function(a){var c=I.length;a>>>=0;if(2147483648=e;e*=2){var d=c*(1+.2/e);d=Math.min(d,a+100663296);d=Math.max(a,d);0>>16);J();var f=1;break a}catch(k){}f=void 0}if(f)return!0}return!1},c:function(a){if(!(noExitRuntime||0=a)return W;W&&b._free(W);X=a;return W=b._malloc(X)}var Y;"undefined"===typeof performance||"undefined"===typeof performance.now?Y=Date.now:Y=performance.now.bind(performance);function Z(a){var c=Y();a=a();b.cpuTime+=Y()-c;return a}b.loadedMetadata=!!n.audioFormat;b.audioFormat=n.audioFormat||null;b.audioBuffer=null;b.cpuTime=0; +Object.defineProperty(b,"processing",{get:function(){return!1}});b.init=function(a){Z(function(){b._ogv_audio_decoder_init()});a()};b.processHeader=function(a,c){var e=Z(function(){var d=a.byteLength,f=ja(d);(new Uint8Array(F.buffer,f,d)).set(new Uint8Array(a));return b._ogv_audio_decoder_process_header(f,d)});c(e)};b.processAudio=function(a,c){var e=Z(function(){var d=a.byteLength,f=ja(d);(new Uint8Array(F.buffer,f,d)).set(new Uint8Array(a));return b._ogv_audio_decoder_process_audio(f,d)});c(e)}; +b.close=function(){}; + + + return OGVDecoderAudioVorbisW.ready +} +); +})(); +if (typeof exports === 'object' && typeof module === 'object') + module.exports = OGVDecoderAudioVorbisW; +else if (typeof define === 'function' && define['amd']) + define([], function() { return OGVDecoderAudioVorbisW; }); +else if (typeof exports === 'object') + exports["OGVDecoderAudioVorbisW"] = OGVDecoderAudioVorbisW; diff --git a/ogvjs-1.8.6/ogv-decoder-audio-vorbis-wasm.wasm b/ogvjs-1.8.6/ogv-decoder-audio-vorbis-wasm.wasm new file mode 100755 index 0000000000000000000000000000000000000000..5a492b033eae3e8c032e8c2f7e581e0535440664 GIT binary patch literal 155787 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 z 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 TjJDL)%E36P@V_G*SeyR~&oF>@ literal 0 HcmV?d00001 diff --git a/ogvjs-1.8.6/ogv-decoder-video-av1-mt-wasm.js b/ogvjs-1.8.6/ogv-decoder-video-av1-mt-wasm.js new file mode 100644 index 000000000..b0ca206da --- /dev/null +++ b/ogvjs-1.8.6/ogv-decoder-video-av1-mt-wasm.js @@ -0,0 +1,21 @@ + +var OGVDecoderVideoAV1MTW = (() => { + var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; + if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename; + return ( +function(OGVDecoderVideoAV1MTW) { + OGVDecoderVideoAV1MTW = OGVDecoderVideoAV1MTW || {}; + +function GROWABLE_HEAP_U8(){if(wasmMemory.buffer!=buffer){updateGlobalBufferAndViews(wasmMemory.buffer)}return HEAPU8}function GROWABLE_HEAP_I32(){if(wasmMemory.buffer!=buffer){updateGlobalBufferAndViews(wasmMemory.buffer)}return HEAP32}function GROWABLE_HEAP_F64(){if(wasmMemory.buffer!=buffer){updateGlobalBufferAndViews(wasmMemory.buffer)}return HEAPF64}var Module=typeof OGVDecoderVideoAV1MTW!=="undefined"?OGVDecoderVideoAV1MTW:{};var objAssign=Object.assign;var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise(function(resolve,reject){readyPromiseResolve=resolve;readyPromiseReject=reject});var options=Module;var moduleOverrides=objAssign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var ENVIRONMENT_IS_WEB=typeof window==="object";var ENVIRONMENT_IS_WORKER=typeof importScripts==="function";var ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";var ENVIRONMENT_IS_PTHREAD=Module["ENVIRONMENT_IS_PTHREAD"]||false;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;function logExceptionOnExit(e){if(e instanceof ExitStatus)return;let toLog=e;err("exiting due to exception: "+toLog)}var fs;var nodePath;var requireNodeFS;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}requireNodeFS=function(){if(!nodePath){fs=require("fs");nodePath=require("path")}};read_=function shell_read(filename,binary){requireNodeFS();filename=nodePath["normalize"](filename);return fs.readFileSync(filename,binary?null:"utf8")};readBinary=function readBinary(filename){var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}return ret};readAsync=function readAsync(filename,onload,onerror){requireNodeFS();filename=nodePath["normalize"](filename);fs.readFile(filename,function(err,data){if(err)onerror(err);else onload(data.buffer)})};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);process["on"]("unhandledRejection",function(reason){throw reason});quit_=((status,toThrow)=>{if(keepRuntimeAlive()){process["exitCode"]=status;throw toThrow}logExceptionOnExit(toThrow);process["exit"](status)});Module["inspect"]=function(){return"[Emscripten Module object]"};let nodeWorkerThreads;try{nodeWorkerThreads=require("worker_threads")}catch(e){console.error('The "worker_threads" module is not supported in this node.js build - perhaps a newer version is needed?');throw e}global.Worker=nodeWorkerThreads.Worker}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!=="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}else{scriptDirectory=""}if(!ENVIRONMENT_IS_NODE){read_=function(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=(title=>document.title=title)}else{}if(ENVIRONMENT_IS_NODE){if(typeof performance==="undefined"){global.performance=require("perf_hooks").performance}}var defaultPrint=console.log.bind(console);var defaultPrintErr=console.warn.bind(console);if(ENVIRONMENT_IS_NODE){requireNodeFS();defaultPrint=(str=>fs.writeSync(1,str+"\n"));defaultPrintErr=(str=>fs.writeSync(2,str+"\n"))}var out=Module["print"]||defaultPrint;var err=Module["printErr"]||defaultPrintErr;objAssign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];function warnOnce(text){if(!warnOnce.shown)warnOnce.shown={};if(!warnOnce.shown[text]){warnOnce.shown[text]=1;err(text)}}var Atomics_load=Atomics.load;var Atomics_store=Atomics.store;var Atomics_compareExchange=Atomics.compareExchange;var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime=Module["noExitRuntime"]||true;if(typeof WebAssembly!=="object"){abort("no native wasm support detected")}var wasmMemory;var wasmModule;var ABORT=false;var EXITSTATUS;function TextDecoderWrapper(encoding){var textDecoder=new TextDecoder(encoding);this.decode=(data=>{if(data.buffer instanceof SharedArrayBuffer){data=new Uint8Array(data)}return textDecoder.decode.call(textDecoder,data)})}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoderWrapper("utf8"):undefined;function UTF8ArrayToString(heap,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heap[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(GROWABLE_HEAP_U8(),ptr,maxBytesToRead):""}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,GROWABLE_HEAP_U8(),outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoderWrapper("utf-16le"):undefined;function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;if(ENVIRONMENT_IS_PTHREAD){buffer=Module["buffer"]}function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||16777216;if(ENVIRONMENT_IS_PTHREAD){wasmMemory=Module["wasmMemory"];buffer=Module["buffer"]}else{if(Module["wasmMemory"]){wasmMemory=Module["wasmMemory"]}else{wasmMemory=new WebAssembly.Memory({"initial":INITIAL_MEMORY/65536,"maximum":1073741824/65536,"shared":true});if(!(wasmMemory.buffer instanceof SharedArrayBuffer)){err("requested a shared WebAssembly.Memory but the returned buffer is not a SharedArrayBuffer, indicating that while the browser has SharedArrayBuffer it does not have WebAssembly threads support - you may need to set a flag");if(ENVIRONMENT_IS_NODE){console.log("(on node you may need: --experimental-wasm-threads --experimental-wasm-bulk-memory and also use a recent version)")}throw Error("bad memory")}}}if(wasmMemory){buffer=wasmMemory.buffer}INITIAL_MEMORY=buffer.byteLength;updateGlobalBufferAndViews(buffer);var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATEXIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeExited=false;var runtimeKeepaliveCounter=0;function keepRuntimeAlive(){return noExitRuntime||runtimeKeepaliveCounter>0}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(ENVIRONMENT_IS_PTHREAD)return;callRuntimeCallbacks(__ATINIT__)}function exitRuntime(){if(ENVIRONMENT_IS_PTHREAD)return;PThread.terminateAllThreads();runtimeExited=true}function postRun(){if(ENVIRONMENT_IS_PTHREAD)return;if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(ENVIRONMENT_IS_PTHREAD){postMessage({"cmd":"onAbort","arg":what})}else{if(Module["onAbort"]){Module["onAbort"](what)}}what="Aborted("+what+")";err(what);ABORT=true;EXITSTATUS=1;what+=". Build with -s ASSERTIONS=1 for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return filename.startsWith(dataURIPrefix)}function isFileURI(filename){return filename.startsWith("file://")}var wasmBinaryFile;wasmBinaryFile="ogv-decoder-video-av1-mt-wasm.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(file){try{if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)){if(typeof fetch==="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary(wasmBinaryFile)})}else{if(readAsync){return new Promise(function(resolve,reject){readAsync(wasmBinaryFile,function(response){resolve(new Uint8Array(response))},reject)})}}}return Promise.resolve().then(function(){return getBinary(wasmBinaryFile)})}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;registerTlsInit(Module["asm"]["H"]);wasmTable=Module["asm"]["F"];addOnInit(Module["asm"]["y"]);wasmModule=module;if(!ENVIRONMENT_IS_PTHREAD){var numWorkersToLoad=PThread.unusedWorkers.length;PThread.unusedWorkers.forEach(function(w){PThread.loadWasmModuleToWorker(w,function(){if(!--numWorkersToLoad)removeRunDependency("wasm-instantiate")})})}}if(!ENVIRONMENT_IS_PTHREAD){addRunDependency("wasm-instantiate")}function receiveInstantiationResult(result){receiveInstance(result["instance"],result["module"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(function(instance){return instance}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiationResult,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiationResult)})})}else{return instantiateArrayBuffer(receiveInstantiationResult)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync().catch(readyPromiseReject);return{}}var ASM_CONSTS={};function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){getWasmTableEntry(func)()}else{getWasmTableEntry(func)(callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}function withStackSave(f){var stack=stackSave();var ret=f();stackRestore(stack);return ret}function killThread(pthread_ptr){GROWABLE_HEAP_I32()[pthread_ptr>>2]=0;var pthread=PThread.pthreads[pthread_ptr];delete PThread.pthreads[pthread_ptr];pthread.worker.terminate();__emscripten_thread_free_data(pthread_ptr);PThread.runningWorkers.splice(PThread.runningWorkers.indexOf(pthread.worker),1);pthread.worker.pthread=undefined}function cancelThread(pthread_ptr){var pthread=PThread.pthreads[pthread_ptr];pthread.worker.postMessage({"cmd":"cancel"})}function cleanupThread(pthread_ptr){var pthread=PThread.pthreads[pthread_ptr];if(pthread){GROWABLE_HEAP_I32()[pthread_ptr>>2]=0;var worker=pthread.worker;PThread.returnWorkerToPool(worker)}}function _exit(status){exit(status)}function handleException(e){if(e instanceof ExitStatus||e=="unwind"){return EXITSTATUS}quit_(1,e)}var PThread={unusedWorkers:[],runningWorkers:[],tlsInitFunctions:[],initMainThread:function(){var pthreadPoolSize=1;for(var i=0;i>2]=0;try{func()}finally{GROWABLE_HEAP_I32()[__emscripten_allow_main_runtime_queued_calls>>2]=1}},receiveObjectTransfer:function(data){},threadInit:function(){for(var i in PThread.tlsInitFunctions){PThread.tlsInitFunctions[i]()}},loadWasmModuleToWorker:function(worker,onFinishedLoading){worker.onmessage=function(e){var d=e["data"];var cmd=d["cmd"];if(worker.pthread)PThread.currentProxiedOperationCallerThread=worker.pthread.threadInfoStruct;if(d["targetThread"]&&d["targetThread"]!=_pthread_self()){var thread=PThread.pthreads[d.targetThread];if(thread){thread.worker.postMessage(d,d["transferList"])}else{err('Internal error! Worker sent a message "'+cmd+'" to target pthread '+d["targetThread"]+", but that thread no longer exists!")}PThread.currentProxiedOperationCallerThread=undefined;return}if(cmd==="processQueuedMainThreadWork"){_emscripten_main_thread_process_queued_calls()}else if(cmd==="spawnThread"){spawnThread(d)}else if(cmd==="cleanupThread"){cleanupThread(d["thread"])}else if(cmd==="killThread"){killThread(d["thread"])}else if(cmd==="cancelThread"){cancelThread(d["thread"])}else if(cmd==="loaded"){worker.loaded=true;if(onFinishedLoading)onFinishedLoading(worker);if(worker.runPthread){worker.runPthread();delete worker.runPthread}}else if(cmd==="print"){out("Thread "+d["threadId"]+": "+d["text"])}else if(cmd==="printErr"){err("Thread "+d["threadId"]+": "+d["text"])}else if(cmd==="alert"){alert("Thread "+d["threadId"]+": "+d["text"])}else if(cmd==="detachedExit"){PThread.returnWorkerToPool(worker)}else if(d.target==="setimmediate"){worker.postMessage(d)}else if(cmd==="onAbort"){if(Module["onAbort"]){Module["onAbort"](d["arg"])}}else{err("worker sent an unknown command "+cmd)}PThread.currentProxiedOperationCallerThread=undefined};worker.onerror=function(e){var message="worker sent an error!";err(message+" "+e.filename+":"+e.lineno+": "+e.message);throw e};if(ENVIRONMENT_IS_NODE){worker.on("message",function(data){worker.onmessage({data:data})});worker.on("error",function(e){worker.onerror(e)});worker.on("detachedExit",function(){})}worker.postMessage({"cmd":"load","urlOrBlob":Module["mainScriptUrlOrBlob"]||_scriptDir,"wasmMemory":wasmMemory,"wasmModule":wasmModule})},allocateUnusedWorker:function(){var pthreadMainJs=locateFile("ogv-decoder-video-av1-mt-wasm.worker.js");PThread.unusedWorkers.push(new Worker(pthreadMainJs))},getNewWorker:function(){if(PThread.unusedWorkers.length==0){PThread.allocateUnusedWorker();PThread.loadWasmModuleToWorker(PThread.unusedWorkers[0])}return PThread.unusedWorkers.pop()}};function establishStackSpace(){var pthread_ptr=_pthread_self();var stackTop=GROWABLE_HEAP_I32()[pthread_ptr+44>>2];var stackSize=GROWABLE_HEAP_I32()[pthread_ptr+48>>2];var stackMax=stackTop-stackSize;_emscripten_stack_set_limits(stackTop,stackMax);stackRestore(stackTop)}Module["establishStackSpace"]=establishStackSpace;function exitOnMainThread(returnCode){if(ENVIRONMENT_IS_PTHREAD)return _emscripten_proxy_to_main_thread_js(1,0,returnCode);try{_exit(returnCode)}catch(e){handleException(e)}}var wasmTableMirror=[];function getWasmTableEntry(funcPtr){var func=wasmTableMirror[funcPtr];if(!func){if(funcPtr>=wasmTableMirror.length)wasmTableMirror.length=funcPtr+1;wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func}function invokeEntryPoint(ptr,arg){return getWasmTableEntry(ptr)(arg)}Module["invokeEntryPoint"]=invokeEntryPoint;function registerTlsInit(tlsInitFunc,moduleExports,metadata){PThread.tlsInitFunctions.push(tlsInitFunc)}var _emscripten_get_now;if(ENVIRONMENT_IS_NODE){_emscripten_get_now=(()=>{var t=process["hrtime"]();return t[0]*1e3+t[1]/1e6})}else if(ENVIRONMENT_IS_PTHREAD){_emscripten_get_now=(()=>performance.now()-Module["__performance_now_clock_drift"])}else _emscripten_get_now=(()=>performance.now());function ___emscripten_init_main_thread_js(tb){__emscripten_thread_init(tb,!ENVIRONMENT_IS_WORKER,1,!ENVIRONMENT_IS_WEB);PThread.threadInit()}function ___emscripten_thread_cleanup(thread){if(!ENVIRONMENT_IS_PTHREAD)cleanupThread(thread);else postMessage({"cmd":"cleanupThread","thread":thread})}function spawnThread(threadParams){var worker=PThread.getNewWorker();if(!worker){return 6}PThread.runningWorkers.push(worker);var pthread=PThread.pthreads[threadParams.pthread_ptr]={worker:worker,threadInfoStruct:threadParams.pthread_ptr};worker.pthread=pthread;var msg={"cmd":"run","start_routine":threadParams.startRoutine,"arg":threadParams.arg,"threadInfoStruct":threadParams.pthread_ptr};worker.runPthread=function(){msg.time=performance.now();worker.postMessage(msg,threadParams.transferList)};if(worker.loaded){worker.runPthread();delete worker.runPthread}return 0}function ___pthread_create_js(pthread_ptr,attr,start_routine,arg){if(typeof SharedArrayBuffer==="undefined"){err("Current environment does not support SharedArrayBuffer, pthreads are not available!");return 6}var transferList=[];var error=0;if(ENVIRONMENT_IS_PTHREAD&&(transferList.length===0||error)){return _emscripten_sync_run_in_main_thread_4(687865856,pthread_ptr,attr,start_routine,arg)}if(error)return error;var threadParams={startRoutine:start_routine,pthread_ptr:pthread_ptr,arg:arg,transferList:transferList};if(ENVIRONMENT_IS_PTHREAD){threadParams.cmd="spawnThread";postMessage(threadParams,transferList);return 0}return spawnThread(threadParams)}function ___pthread_detached_exit(){postMessage({"cmd":"detachedExit"})}function __emscripten_default_pthread_stack_size(){return 2097152}function __emscripten_futex_wait_non_blocking(addr,val,timeout){var tNow=performance.now();var tEnd=tNow+timeout;var lastAddr=Atomics.exchange(GROWABLE_HEAP_I32(),__emscripten_main_thread_futex>>2,addr);while(1){tNow=performance.now();if(tNow>tEnd){lastAddr=Atomics.exchange(GROWABLE_HEAP_I32(),__emscripten_main_thread_futex>>2,0);return-73}lastAddr=Atomics.exchange(GROWABLE_HEAP_I32(),__emscripten_main_thread_futex>>2,0);if(lastAddr==0){break}_emscripten_main_thread_process_queued_calls();if(Atomics.load(GROWABLE_HEAP_I32(),addr>>2)!=val){return-6}lastAddr=Atomics.exchange(GROWABLE_HEAP_I32(),__emscripten_main_thread_futex>>2,addr)}return 0}function __emscripten_notify_thread_queue(targetThreadId,mainThreadId){if(targetThreadId==mainThreadId){postMessage({"cmd":"processQueuedMainThreadWork"})}else if(ENVIRONMENT_IS_PTHREAD){postMessage({"targetThread":targetThreadId,"cmd":"processThreadQueue"})}else{var pthread=PThread.pthreads[targetThreadId];var worker=pthread&&pthread.worker;if(!worker){return}worker.postMessage({"cmd":"processThreadQueue"})}return 1}function _abort(){abort("")}function _emscripten_check_blocking_allowed(){if(ENVIRONMENT_IS_NODE)return;if(ENVIRONMENT_IS_WORKER)return;warnOnce("Blocking on the main thread is very dangerous, see https://emscripten.org/docs/porting/pthreads.html#blocking-on-the-main-browser-thread")}function _emscripten_memcpy_big(dest,src,num){GROWABLE_HEAP_U8().copyWithin(dest,src,src+num)}function _emscripten_num_logical_cores(){if(ENVIRONMENT_IS_NODE)return require("os").cpus().length;return navigator["hardwareConcurrency"]}function _emscripten_proxy_to_main_thread_js(index,sync){var numCallArgs=arguments.length-2;var outerArgs=arguments;return withStackSave(function(){var serializedNumCallArgs=numCallArgs;var args=stackAlloc(serializedNumCallArgs*8);var b=args>>3;for(var i=0;i>3;for(var i=0;i>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){var oldSize=GROWABLE_HEAP_U8().length;requestedSize=requestedSize>>>0;if(requestedSize<=oldSize){return false}var maxHeapSize=1073741824;if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}var JSEvents={inEventHandler:0,removeAllEventListeners:function(){for(var i=JSEvents.eventHandlers.length-1;i>=0;--i){JSEvents._removeHandler(i)}JSEvents.eventHandlers=[];JSEvents.deferredCalls=[]},registerRemoveEventListeners:function(){if(!JSEvents.removeEventListenersRegistered){__ATEXIT__.push(JSEvents.removeAllEventListeners);JSEvents.removeEventListenersRegistered=true}},deferredCalls:[],deferCall:function(targetFunction,precedence,argsList){function arraysHaveEqualContent(arrA,arrB){if(arrA.length!=arrB.length)return false;for(var i in arrA){if(arrA[i]!=arrB[i])return false}return true}for(var i in JSEvents.deferredCalls){var call=JSEvents.deferredCalls[i];if(call.targetFunction==targetFunction&&arraysHaveEqualContent(call.argsList,argsList)){return}}JSEvents.deferredCalls.push({targetFunction:targetFunction,precedence:precedence,argsList:argsList});JSEvents.deferredCalls.sort(function(x,y){return x.precedence>2]=eventTypeId;GROWABLE_HEAP_I32()[varargs+4>>2]=eventData;GROWABLE_HEAP_I32()[varargs+8>>2]=userData;_emscripten_dispatch_to_thread_(targetThread,637534208,eventHandlerFunc,eventData,varargs)})},getTargetThreadForEventCallback:function(targetThread){switch(targetThread){case 1:return 0;case 2:return PThread.currentProxiedOperationCallerThread;default:return targetThread}},getNodeNameForTarget:function(target){if(!target)return"";if(target==window)return"#window";if(target==screen)return"#screen";return target&&target.nodeName?target.nodeName:""},fullscreenEnabled:function(){return document.fullscreenEnabled||document.webkitFullscreenEnabled}};function stringToNewUTF8(jsString){var length=lengthBytesUTF8(jsString)+1;var cString=_malloc(length);stringToUTF8(jsString,cString,length);return cString}function _emscripten_set_offscreencanvas_size_on_target_thread_js(targetThread,targetCanvas,width,height){withStackSave(function(){var varargs=stackAlloc(12);var targetCanvasPtr=0;if(targetCanvas){targetCanvasPtr=stringToNewUTF8(targetCanvas)}GROWABLE_HEAP_I32()[varargs>>2]=targetCanvasPtr;GROWABLE_HEAP_I32()[varargs+4>>2]=width;GROWABLE_HEAP_I32()[varargs+8>>2]=height;_emscripten_dispatch_to_thread_(targetThread,657457152,0,targetCanvasPtr,varargs)})}function _emscripten_set_offscreencanvas_size_on_target_thread(targetThread,targetCanvas,width,height){targetCanvas=targetCanvas?UTF8ToString(targetCanvas):"";_emscripten_set_offscreencanvas_size_on_target_thread_js(targetThread,targetCanvas,width,height)}function maybeCStringToJsString(cString){return cString>2?UTF8ToString(cString):cString}var specialHTMLTargets=[0,typeof document!=="undefined"?document:0,typeof window!=="undefined"?window:0];function findEventTarget(target){target=maybeCStringToJsString(target);var domElement=specialHTMLTargets[target]||(typeof document!=="undefined"?document.querySelector(target):undefined);return domElement}function findCanvasEventTarget(target){return findEventTarget(target)}function _emscripten_set_canvas_element_size_calling_thread(target,width,height){var canvas=findCanvasEventTarget(target);if(!canvas)return-4;if(canvas.canvasSharedPtr){GROWABLE_HEAP_I32()[canvas.canvasSharedPtr>>2]=width;GROWABLE_HEAP_I32()[canvas.canvasSharedPtr+4>>2]=height}if(canvas.offscreenCanvas||!canvas.controlTransferredOffscreen){if(canvas.offscreenCanvas)canvas=canvas.offscreenCanvas;var autoResizeViewport=false;if(canvas.GLctxObject&&canvas.GLctxObject.GLctx){var prevViewport=canvas.GLctxObject.GLctx.getParameter(2978);autoResizeViewport=prevViewport[0]===0&&prevViewport[1]===0&&prevViewport[2]===canvas.width&&prevViewport[3]===canvas.height}canvas.width=width;canvas.height=height;if(autoResizeViewport){canvas.GLctxObject.GLctx.viewport(0,0,width,height)}}else if(canvas.canvasSharedPtr){var targetThread=GROWABLE_HEAP_I32()[canvas.canvasSharedPtr+8>>2];_emscripten_set_offscreencanvas_size_on_target_thread(targetThread,target,width,height);return 1}else{return-4}return 0}function _emscripten_set_canvas_element_size_main_thread(target,width,height){if(ENVIRONMENT_IS_PTHREAD)return _emscripten_proxy_to_main_thread_js(2,1,target,width,height);return _emscripten_set_canvas_element_size_calling_thread(target,width,height)}function _emscripten_set_canvas_element_size(target,width,height){var canvas=findCanvasEventTarget(target);if(canvas){return _emscripten_set_canvas_element_size_calling_thread(target,width,height)}else{return _emscripten_set_canvas_element_size_main_thread(target,width,height)}}function _emscripten_unwind_to_js_event_loop(){throw"unwind"}function __webgl_enable_ANGLE_instanced_arrays(ctx){var ext=ctx.getExtension("ANGLE_instanced_arrays");if(ext){ctx["vertexAttribDivisor"]=function(index,divisor){ext["vertexAttribDivisorANGLE"](index,divisor)};ctx["drawArraysInstanced"]=function(mode,first,count,primcount){ext["drawArraysInstancedANGLE"](mode,first,count,primcount)};ctx["drawElementsInstanced"]=function(mode,count,type,indices,primcount){ext["drawElementsInstancedANGLE"](mode,count,type,indices,primcount)};return 1}}function __webgl_enable_OES_vertex_array_object(ctx){var ext=ctx.getExtension("OES_vertex_array_object");if(ext){ctx["createVertexArray"]=function(){return ext["createVertexArrayOES"]()};ctx["deleteVertexArray"]=function(vao){ext["deleteVertexArrayOES"](vao)};ctx["bindVertexArray"]=function(vao){ext["bindVertexArrayOES"](vao)};ctx["isVertexArray"]=function(vao){return ext["isVertexArrayOES"](vao)};return 1}}function __webgl_enable_WEBGL_draw_buffers(ctx){var ext=ctx.getExtension("WEBGL_draw_buffers");if(ext){ctx["drawBuffers"]=function(n,bufs){ext["drawBuffersWEBGL"](n,bufs)};return 1}}function __webgl_enable_WEBGL_multi_draw(ctx){return!!(ctx.multiDrawWebgl=ctx.getExtension("WEBGL_multi_draw"))}var GL={counter:1,buffers:[],programs:[],framebuffers:[],renderbuffers:[],textures:[],shaders:[],vaos:[],contexts:{},offscreenCanvases:{},queries:[],stringCache:{},unpackAlignment:4,recordError:function recordError(errorCode){if(!GL.lastError){GL.lastError=errorCode}},getNewId:function(table){var ret=GL.counter++;for(var i=table.length;i>2]:-1;source+=UTF8ToString(GROWABLE_HEAP_I32()[string+i*4>>2],len<0?undefined:len)}return source},createContext:function(canvas,webGLContextAttributes){if(!canvas.getContextSafariWebGL2Fixed){canvas.getContextSafariWebGL2Fixed=canvas.getContext;canvas.getContext=function(ver,attrs){var gl=canvas.getContextSafariWebGL2Fixed(ver,attrs);return ver=="webgl"==gl instanceof WebGLRenderingContext?gl:null}}var ctx=canvas.getContext("webgl",webGLContextAttributes);if(!ctx)return 0;var handle=GL.registerContext(ctx,webGLContextAttributes);return handle},registerContext:function(ctx,webGLContextAttributes){var handle=_malloc(8);GROWABLE_HEAP_I32()[handle+4>>2]=_pthread_self();var context={handle:handle,attributes:webGLContextAttributes,version:webGLContextAttributes.majorVersion,GLctx:ctx};if(ctx.canvas)ctx.canvas.GLctxObject=context;GL.contexts[handle]=context;if(typeof webGLContextAttributes.enableExtensionsByDefault==="undefined"||webGLContextAttributes.enableExtensionsByDefault){GL.initExtensions(context)}return handle},makeContextCurrent:function(contextHandle){GL.currentContext=GL.contexts[contextHandle];Module.ctx=GLctx=GL.currentContext&&GL.currentContext.GLctx;return!(contextHandle&&!GLctx)},getContext:function(contextHandle){return GL.contexts[contextHandle]},deleteContext:function(contextHandle){if(GL.currentContext===GL.contexts[contextHandle])GL.currentContext=null;if(typeof JSEvents==="object")JSEvents.removeAllHandlersOnTarget(GL.contexts[contextHandle].GLctx.canvas);if(GL.contexts[contextHandle]&&GL.contexts[contextHandle].GLctx.canvas)GL.contexts[contextHandle].GLctx.canvas.GLctxObject=undefined;_free(GL.contexts[contextHandle].handle);GL.contexts[contextHandle]=null},initExtensions:function(context){if(!context)context=GL.currentContext;if(context.initExtensionsDone)return;context.initExtensionsDone=true;var GLctx=context.GLctx;__webgl_enable_ANGLE_instanced_arrays(GLctx);__webgl_enable_OES_vertex_array_object(GLctx);__webgl_enable_WEBGL_draw_buffers(GLctx);{GLctx.disjointTimerQueryExt=GLctx.getExtension("EXT_disjoint_timer_query")}__webgl_enable_WEBGL_multi_draw(GLctx);var exts=GLctx.getSupportedExtensions()||[];exts.forEach(function(ext){if(!ext.includes("lose_context")&&!ext.includes("debug")){GLctx.getExtension(ext)}})}};var __emscripten_webgl_power_preferences=["default","low-power","high-performance"];function _emscripten_webgl_do_create_context(target,attributes){var a=attributes>>2;var powerPreference=GROWABLE_HEAP_I32()[a+(24>>2)];var contextAttributes={"alpha":!!GROWABLE_HEAP_I32()[a+(0>>2)],"depth":!!GROWABLE_HEAP_I32()[a+(4>>2)],"stencil":!!GROWABLE_HEAP_I32()[a+(8>>2)],"antialias":!!GROWABLE_HEAP_I32()[a+(12>>2)],"premultipliedAlpha":!!GROWABLE_HEAP_I32()[a+(16>>2)],"preserveDrawingBuffer":!!GROWABLE_HEAP_I32()[a+(20>>2)],"powerPreference":__emscripten_webgl_power_preferences[powerPreference],"failIfMajorPerformanceCaveat":!!GROWABLE_HEAP_I32()[a+(28>>2)],majorVersion:GROWABLE_HEAP_I32()[a+(32>>2)],minorVersion:GROWABLE_HEAP_I32()[a+(36>>2)],enableExtensionsByDefault:GROWABLE_HEAP_I32()[a+(40>>2)],explicitSwapControl:GROWABLE_HEAP_I32()[a+(44>>2)],proxyContextToMainThread:GROWABLE_HEAP_I32()[a+(48>>2)],renderViaOffscreenBackBuffer:GROWABLE_HEAP_I32()[a+(52>>2)]};var canvas=findCanvasEventTarget(target);if(!canvas){return 0}if(contextAttributes.explicitSwapControl){return 0}var contextHandle=GL.createContext(canvas,contextAttributes);return contextHandle}function _emscripten_webgl_create_context(a0,a1){return _emscripten_webgl_do_create_context(a0,a1)}var SYSCALLS={mappings:{},buffers:[null,[],[]],printChar:function(stream,curr){var buffer=SYSCALLS.buffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer,0));buffer.length=0}else{buffer.push(curr)}},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=GROWABLE_HEAP_I32()[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},get64:function(low,high){return low}};function _fd_close(fd){if(ENVIRONMENT_IS_PTHREAD)return _emscripten_proxy_to_main_thread_js(3,1,fd);return 0}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){if(ENVIRONMENT_IS_PTHREAD)return _emscripten_proxy_to_main_thread_js(4,1,fd,offset_low,offset_high,whence,newOffset)}function _fd_write(fd,iov,iovcnt,pnum){if(ENVIRONMENT_IS_PTHREAD)return _emscripten_proxy_to_main_thread_js(5,1,fd,iov,iovcnt,pnum);var num=0;for(var i=0;i>2];var len=GROWABLE_HEAP_I32()[iov+4>>2];iov+=8;for(var j=0;j>2]=num;return 0}function _ogvjs_callback_async_complete(ret,cpuTime){var callback=Module.callbacks.shift();Module["cpuTime"]+=cpuTime;callback(ret);return}function _ogvjs_callback_frame(bufferY,strideY,bufferCb,strideCb,bufferCr,strideCr,width,height,chromaWidth,chromaHeight,picWidth,picHeight,picX,picY,displayWidth,displayHeight){var heap=wasmMemory.buffer;var format=Module["videoFormat"];function copyAndTrim(arr,buffer,stride,height,picX,picY,picWidth,picHeight,fill){arr.set(new Uint8Array(heap,buffer,stride*height));var x,y,ptr;for(ptr=0,y=0;y0){var next=recycled.shift(),format=next["format"];if(format["width"]===width&&format["height"]===height&&format["chromaWidth"]===chromaWidth&&format["chromaHeight"]===chromaHeight&&format["cropLeft"]===picX&&format["cropTop"]===picY&&format["cropWidth"]===picWidth&&format["cropHeight"]===picHeight&&format["displayWidth"]===displayWidth&&format["displayHeight"]===displayHeight&&next["y"]["bytes"].length===lenY&&next["u"]["bytes"].length===lenCb&&next["v"]["bytes"].length===lenCr){frame=next;break}}if(!frame){frame={"format":{"width":width,"height":height,"chromaWidth":chromaWidth,"chromaHeight":chromaHeight,"cropLeft":picX,"cropTop":picY,"cropWidth":picWidth,"cropHeight":picHeight,"displayWidth":displayWidth,"displayHeight":displayHeight},"y":{"bytes":new Uint8Array(lenY),"stride":strideY},"u":{"bytes":new Uint8Array(lenCb),"stride":strideCb},"v":{"bytes":new Uint8Array(lenCr),"stride":strideCr}}}copyAndTrim(frame["y"]["bytes"],bufferY,strideY,height,picX,picY,picWidth,picHeight,0);copyAndTrim(frame["u"]["bytes"],bufferCb,strideCb,chromaHeight,chromaPicX,chromaPicY,chromaPicWidth,chromaPicHeight,128);copyAndTrim(frame["v"]["bytes"],bufferCr,strideCr,chromaHeight,chromaPicX,chromaPicY,chromaPicWidth,chromaPicHeight,128);Module["frameBuffer"]=frame}if(!ENVIRONMENT_IS_PTHREAD)PThread.initMainThread();var GLctx;var proxiedFunctionTable=[null,exitOnMainThread,_emscripten_set_canvas_element_size_main_thread,_fd_close,_fd_seek,_fd_write];var asmLibraryArg={"v":___emscripten_init_main_thread_js,"q":___emscripten_thread_cleanup,"c":___pthread_create_js,"t":___pthread_detached_exit,"u":__emscripten_default_pthread_stack_size,"j":__emscripten_futex_wait_non_blocking,"i":__emscripten_notify_thread_queue,"f":_abort,"e":_emscripten_check_blocking_allowed,"b":_emscripten_get_now,"n":_emscripten_memcpy_big,"x":_emscripten_num_logical_cores,"w":_emscripten_receive_on_main_thread_js,"o":_emscripten_resize_heap,"g":_emscripten_set_canvas_element_size,"r":_emscripten_unwind_to_js_event_loop,"h":_emscripten_webgl_create_context,"s":_exit,"p":_fd_close,"m":_fd_seek,"d":_fd_write,"a":wasmMemory||Module["wasmMemory"],"k":_ogvjs_callback_async_complete,"l":_ogvjs_callback_frame};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["y"]).apply(null,arguments)};var _ogv_video_decoder_init=Module["_ogv_video_decoder_init"]=function(){return(_ogv_video_decoder_init=Module["_ogv_video_decoder_init"]=Module["asm"]["z"]).apply(null,arguments)};var _free=Module["_free"]=function(){return(_free=Module["_free"]=Module["asm"]["A"]).apply(null,arguments)};var _ogv_video_decoder_async=Module["_ogv_video_decoder_async"]=function(){return(_ogv_video_decoder_async=Module["_ogv_video_decoder_async"]=Module["asm"]["B"]).apply(null,arguments)};var _ogv_video_decoder_destroy=Module["_ogv_video_decoder_destroy"]=function(){return(_ogv_video_decoder_destroy=Module["_ogv_video_decoder_destroy"]=Module["asm"]["C"]).apply(null,arguments)};var _ogv_video_decoder_process_header=Module["_ogv_video_decoder_process_header"]=function(){return(_ogv_video_decoder_process_header=Module["_ogv_video_decoder_process_header"]=Module["asm"]["D"]).apply(null,arguments)};var _ogv_video_decoder_process_frame=Module["_ogv_video_decoder_process_frame"]=function(){return(_ogv_video_decoder_process_frame=Module["_ogv_video_decoder_process_frame"]=Module["asm"]["E"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["G"]).apply(null,arguments)};var _emscripten_tls_init=Module["_emscripten_tls_init"]=function(){return(_emscripten_tls_init=Module["_emscripten_tls_init"]=Module["asm"]["H"]).apply(null,arguments)};var __emscripten_thread_init=Module["__emscripten_thread_init"]=function(){return(__emscripten_thread_init=Module["__emscripten_thread_init"]=Module["asm"]["I"]).apply(null,arguments)};var _emscripten_current_thread_process_queued_calls=Module["_emscripten_current_thread_process_queued_calls"]=function(){return(_emscripten_current_thread_process_queued_calls=Module["_emscripten_current_thread_process_queued_calls"]=Module["asm"]["J"]).apply(null,arguments)};var _emscripten_sync_run_in_main_thread_4=Module["_emscripten_sync_run_in_main_thread_4"]=function(){return(_emscripten_sync_run_in_main_thread_4=Module["_emscripten_sync_run_in_main_thread_4"]=Module["asm"]["K"]).apply(null,arguments)};var _emscripten_main_thread_process_queued_calls=Module["_emscripten_main_thread_process_queued_calls"]=function(){return(_emscripten_main_thread_process_queued_calls=Module["_emscripten_main_thread_process_queued_calls"]=Module["asm"]["L"]).apply(null,arguments)};var _emscripten_run_in_main_runtime_thread_js=Module["_emscripten_run_in_main_runtime_thread_js"]=function(){return(_emscripten_run_in_main_runtime_thread_js=Module["_emscripten_run_in_main_runtime_thread_js"]=Module["asm"]["M"]).apply(null,arguments)};var _pthread_self=Module["_pthread_self"]=function(){return(_pthread_self=Module["_pthread_self"]=Module["asm"]["N"]).apply(null,arguments)};var _emscripten_dispatch_to_thread_=Module["_emscripten_dispatch_to_thread_"]=function(){return(_emscripten_dispatch_to_thread_=Module["_emscripten_dispatch_to_thread_"]=Module["asm"]["O"]).apply(null,arguments)};var __emscripten_thread_free_data=Module["__emscripten_thread_free_data"]=function(){return(__emscripten_thread_free_data=Module["__emscripten_thread_free_data"]=Module["asm"]["P"]).apply(null,arguments)};var __emscripten_thread_exit=Module["__emscripten_thread_exit"]=function(){return(__emscripten_thread_exit=Module["__emscripten_thread_exit"]=Module["asm"]["Q"]).apply(null,arguments)};var _emscripten_stack_set_limits=Module["_emscripten_stack_set_limits"]=function(){return(_emscripten_stack_set_limits=Module["_emscripten_stack_set_limits"]=Module["asm"]["R"]).apply(null,arguments)};var stackSave=Module["stackSave"]=function(){return(stackSave=Module["stackSave"]=Module["asm"]["S"]).apply(null,arguments)};var stackRestore=Module["stackRestore"]=function(){return(stackRestore=Module["stackRestore"]=Module["asm"]["T"]).apply(null,arguments)};var stackAlloc=Module["stackAlloc"]=function(){return(stackAlloc=Module["stackAlloc"]=Module["asm"]["U"]).apply(null,arguments)};var __emscripten_main_thread_futex=Module["__emscripten_main_thread_futex"]=375956;var __emscripten_allow_main_runtime_queued_calls=Module["__emscripten_allow_main_runtime_queued_calls"]=108308;Module["keepRuntimeAlive"]=keepRuntimeAlive;Module["PThread"]=PThread;Module["PThread"]=PThread;Module["wasmMemory"]=wasmMemory;Module["ExitStatus"]=ExitStatus;var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;if(runDependencies>0){return}if(ENVIRONMENT_IS_PTHREAD){readyPromiseResolve(Module);initRuntime();postMessage({"cmd":"loaded"});return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;function exit(status,implicit){EXITSTATUS=status;if(!implicit){if(ENVIRONMENT_IS_PTHREAD){exitOnMainThread(status);throw"unwind"}else{}}if(keepRuntimeAlive()){}else{exitRuntime()}procExit(status)}function procExit(code){EXITSTATUS=code;if(!keepRuntimeAlive()){PThread.terminateAllThreads();if(Module["onExit"])Module["onExit"](code);ABORT=true}quit_(code,new ExitStatus(code))}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}if(ENVIRONMENT_IS_PTHREAD){noExitRuntime=false;PThread.initWorker()}run();var inputBuffer,inputBufferSize;function reallocInputBuffer(size){if(inputBuffer&&inputBufferSize>=size){return inputBuffer}if(inputBuffer){Module["_free"](inputBuffer)}inputBufferSize=size;inputBuffer=Module["_malloc"](inputBufferSize);return inputBuffer}var getTimestamp;if(typeof performance==="undefined"||typeof performance.now==="undefined"){getTimestamp=Date.now}else{getTimestamp=performance.now.bind(performance)}function time(func){var start=getTimestamp(),ret;ret=func();Module["cpuTime"]+=getTimestamp()-start;return ret}Module["loadedMetadata"]=!!options["videoFormat"];Module["videoFormat"]=options["videoFormat"]||null;Module["frameBuffer"]=null;Module["cpuTime"]=0;Object.defineProperty(Module,"processing",{get:function getProcessing(){return false}});Module["init"]=function(callback){time(function(){Module["_ogv_video_decoder_init"]()});callback()};Module["processHeader"]=function(data,callback){var ret=time(function(){var len=data.byteLength;var buffer=reallocInputBuffer(len);var dest=new Uint8Array(wasmMemory.buffer,buffer,len);dest.set(new Uint8Array(data));return Module["_ogv_video_decoder_process_header"](buffer,len)});callback(ret)};Module.callbacks=[];Module["processFrame"]=function(data,callback){var isAsync=Module["_ogv_video_decoder_async"]();var len=data.byteLength;var buffer=Module["_malloc"](len);function callbackWrapper(ret){Module["_free"](buffer);callback(ret)}if(isAsync){Module.callbacks.push(callbackWrapper)}var ret=time(function(){var dest=new Uint8Array(wasmMemory.buffer,buffer,len);dest.set(new Uint8Array(data));return Module["_ogv_video_decoder_process_frame"](buffer,len)});if(!isAsync){callbackWrapper(ret)}};Module["close"]=function(){};Module["sync"]=function(){var isAsync=Module["_ogv_video_decoder_async"]();if(isAsync){Module.callbacks.push(function(){});time(function(){Module["_ogv_video_decoder_process_frame"](0,0)})}};Module["recycledFrames"]=[];Module["recycleFrame"]=function(frame){var arr=Module["recycledFrames"];arr.push(frame);if(arr.length>16){arr.shift()}}; + + + return OGVDecoderVideoAV1MTW.ready +} +); +})(); +if (typeof exports === 'object' && typeof module === 'object') + module.exports = OGVDecoderVideoAV1MTW; +else if (typeof define === 'function' && define['amd']) + define([], function() { return OGVDecoderVideoAV1MTW; }); +else if (typeof exports === 'object') + exports["OGVDecoderVideoAV1MTW"] = OGVDecoderVideoAV1MTW; diff --git a/ogvjs-1.8.6/ogv-decoder-video-av1-mt-wasm.wasm b/ogvjs-1.8.6/ogv-decoder-video-av1-mt-wasm.wasm new file mode 100755 index 0000000000000000000000000000000000000000..c0e8abe04c73213c74af35bd7f3788a517e0cd2f GIT binary patch literal 418335 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§@8u+zPYm&6PRe=w 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¥_#0s*;46I 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¬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 W*zeoX`X>=Zv$)W@F5!Pf)Bgu5n*`ec literal 0 HcmV?d00001 diff --git a/ogvjs-1.8.6/ogv-decoder-video-av1-mt-wasm.worker.js b/ogvjs-1.8.6/ogv-decoder-video-av1-mt-wasm.worker.js new file mode 100644 index 000000000..d8c3fd461 --- /dev/null +++ b/ogvjs-1.8.6/ogv-decoder-video-av1-mt-wasm.worker.js @@ -0,0 +1 @@ +"use strict";var Module={};if(typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string"){var nodeWorkerThreads=require("worker_threads");var parentPort=nodeWorkerThreads.parentPort;parentPort.on("message",function(data){onmessage({data:data})});var nodeFS=require("fs");Object.assign(global,{self:global,require:require,Module:Module,location:{href:__filename},Worker:nodeWorkerThreads.Worker,importScripts:function(f){(0,eval)(nodeFS.readFileSync(f,"utf8"))},postMessage:function(msg){parentPort.postMessage(msg)},performance:global.performance||{now:function(){return Date.now()}}})}function threadPrintErr(){var text=Array.prototype.slice.call(arguments).join(" ");console.error(text)}function threadAlert(){var text=Array.prototype.slice.call(arguments).join(" ");postMessage({cmd:"alert",text:text,threadId:Module["_pthread_self"]()})}var err=threadPrintErr;self.alert=threadAlert;Module["instantiateWasm"]=function(info,receiveInstance){var instance=new WebAssembly.Instance(Module["wasmModule"],info);receiveInstance(instance);Module["wasmModule"]=null;return instance.exports};self.onmessage=function(e){try{if(e.data.cmd==="load"){Module["wasmModule"]=e.data.wasmModule;Module["wasmMemory"]=e.data.wasmMemory;Module["buffer"]=Module["wasmMemory"].buffer;Module["ENVIRONMENT_IS_PTHREAD"]=true;if(typeof e.data.urlOrBlob==="string"){importScripts(e.data.urlOrBlob)}else{var objectUrl=URL.createObjectURL(e.data.urlOrBlob);importScripts(objectUrl);URL.revokeObjectURL(objectUrl)}OGVDecoderVideoAV1MTW(Module).then(function(instance){Module=instance})}else if(e.data.cmd==="run"){Module["__performance_now_clock_drift"]=performance.now()-e.data.time;Module["__emscripten_thread_init"](e.data.threadInfoStruct,0,0,1);Module["establishStackSpace"]();Module["PThread"].receiveObjectTransfer(e.data);Module["PThread"].threadInit();try{var result=Module["invokeEntryPoint"](e.data.start_routine,e.data.arg);if(Module["keepRuntimeAlive"]()){Module["PThread"].setExitStatus(result)}else{Module["__emscripten_thread_exit"](result)}}catch(ex){if(ex!="unwind"){if(ex instanceof Module["ExitStatus"]){if(Module["keepRuntimeAlive"]()){}else{Module["__emscripten_thread_exit"](ex.status)}}else{throw ex}}}}else if(e.data.cmd==="cancel"){if(Module["_pthread_self"]()){Module["__emscripten_thread_exit"](-1)}}else if(e.data.target==="setimmediate"){}else if(e.data.cmd==="processThreadQueue"){if(Module["_pthread_self"]()){Module["_emscripten_current_thread_process_queued_calls"]()}}else{err("worker.js received unknown command "+e.data.cmd);err(e.data)}}catch(ex){err("worker.js onmessage() captured an uncaught exception: "+ex);if(ex&&ex.stack)err(ex.stack);throw ex}}; diff --git a/ogvjs-1.8.6/ogv-decoder-video-av1-simd-mt-wasm.js b/ogvjs-1.8.6/ogv-decoder-video-av1-simd-mt-wasm.js new file mode 100644 index 000000000..325a52121 --- /dev/null +++ b/ogvjs-1.8.6/ogv-decoder-video-av1-simd-mt-wasm.js @@ -0,0 +1,21 @@ + +var OGVDecoderVideoAV1SIMDMTW = (() => { + var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; + if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename; + return ( +function(OGVDecoderVideoAV1SIMDMTW) { + OGVDecoderVideoAV1SIMDMTW = OGVDecoderVideoAV1SIMDMTW || {}; + +function GROWABLE_HEAP_U8(){if(wasmMemory.buffer!=buffer){updateGlobalBufferAndViews(wasmMemory.buffer)}return HEAPU8}function GROWABLE_HEAP_I32(){if(wasmMemory.buffer!=buffer){updateGlobalBufferAndViews(wasmMemory.buffer)}return HEAP32}function GROWABLE_HEAP_F64(){if(wasmMemory.buffer!=buffer){updateGlobalBufferAndViews(wasmMemory.buffer)}return HEAPF64}var Module=typeof OGVDecoderVideoAV1SIMDMTW!=="undefined"?OGVDecoderVideoAV1SIMDMTW:{};var objAssign=Object.assign;var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise(function(resolve,reject){readyPromiseResolve=resolve;readyPromiseReject=reject});var options=Module;var moduleOverrides=objAssign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var ENVIRONMENT_IS_WEB=typeof window==="object";var ENVIRONMENT_IS_WORKER=typeof importScripts==="function";var ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";var ENVIRONMENT_IS_PTHREAD=Module["ENVIRONMENT_IS_PTHREAD"]||false;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;function logExceptionOnExit(e){if(e instanceof ExitStatus)return;let toLog=e;err("exiting due to exception: "+toLog)}var fs;var nodePath;var requireNodeFS;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}requireNodeFS=function(){if(!nodePath){fs=require("fs");nodePath=require("path")}};read_=function shell_read(filename,binary){requireNodeFS();filename=nodePath["normalize"](filename);return fs.readFileSync(filename,binary?null:"utf8")};readBinary=function readBinary(filename){var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}return ret};readAsync=function readAsync(filename,onload,onerror){requireNodeFS();filename=nodePath["normalize"](filename);fs.readFile(filename,function(err,data){if(err)onerror(err);else onload(data.buffer)})};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);process["on"]("unhandledRejection",function(reason){throw reason});quit_=((status,toThrow)=>{if(keepRuntimeAlive()){process["exitCode"]=status;throw toThrow}logExceptionOnExit(toThrow);process["exit"](status)});Module["inspect"]=function(){return"[Emscripten Module object]"};let nodeWorkerThreads;try{nodeWorkerThreads=require("worker_threads")}catch(e){console.error('The "worker_threads" module is not supported in this node.js build - perhaps a newer version is needed?');throw e}global.Worker=nodeWorkerThreads.Worker}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!=="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}else{scriptDirectory=""}if(!ENVIRONMENT_IS_NODE){read_=function(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=(title=>document.title=title)}else{}if(ENVIRONMENT_IS_NODE){if(typeof performance==="undefined"){global.performance=require("perf_hooks").performance}}var defaultPrint=console.log.bind(console);var defaultPrintErr=console.warn.bind(console);if(ENVIRONMENT_IS_NODE){requireNodeFS();defaultPrint=(str=>fs.writeSync(1,str+"\n"));defaultPrintErr=(str=>fs.writeSync(2,str+"\n"))}var out=Module["print"]||defaultPrint;var err=Module["printErr"]||defaultPrintErr;objAssign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];function warnOnce(text){if(!warnOnce.shown)warnOnce.shown={};if(!warnOnce.shown[text]){warnOnce.shown[text]=1;err(text)}}var Atomics_load=Atomics.load;var Atomics_store=Atomics.store;var Atomics_compareExchange=Atomics.compareExchange;var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime=Module["noExitRuntime"]||true;if(typeof WebAssembly!=="object"){abort("no native wasm support detected")}var wasmMemory;var wasmModule;var ABORT=false;var EXITSTATUS;function TextDecoderWrapper(encoding){var textDecoder=new TextDecoder(encoding);this.decode=(data=>{if(data.buffer instanceof SharedArrayBuffer){data=new Uint8Array(data)}return textDecoder.decode.call(textDecoder,data)})}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoderWrapper("utf8"):undefined;function UTF8ArrayToString(heap,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heap[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(GROWABLE_HEAP_U8(),ptr,maxBytesToRead):""}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,GROWABLE_HEAP_U8(),outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoderWrapper("utf-16le"):undefined;function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;if(ENVIRONMENT_IS_PTHREAD){buffer=Module["buffer"]}function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||16777216;if(ENVIRONMENT_IS_PTHREAD){wasmMemory=Module["wasmMemory"];buffer=Module["buffer"]}else{if(Module["wasmMemory"]){wasmMemory=Module["wasmMemory"]}else{wasmMemory=new WebAssembly.Memory({"initial":INITIAL_MEMORY/65536,"maximum":1073741824/65536,"shared":true});if(!(wasmMemory.buffer instanceof SharedArrayBuffer)){err("requested a shared WebAssembly.Memory but the returned buffer is not a SharedArrayBuffer, indicating that while the browser has SharedArrayBuffer it does not have WebAssembly threads support - you may need to set a flag");if(ENVIRONMENT_IS_NODE){console.log("(on node you may need: --experimental-wasm-threads --experimental-wasm-bulk-memory and also use a recent version)")}throw Error("bad memory")}}}if(wasmMemory){buffer=wasmMemory.buffer}INITIAL_MEMORY=buffer.byteLength;updateGlobalBufferAndViews(buffer);var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATEXIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeExited=false;var runtimeKeepaliveCounter=0;function keepRuntimeAlive(){return noExitRuntime||runtimeKeepaliveCounter>0}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(ENVIRONMENT_IS_PTHREAD)return;callRuntimeCallbacks(__ATINIT__)}function exitRuntime(){if(ENVIRONMENT_IS_PTHREAD)return;PThread.terminateAllThreads();runtimeExited=true}function postRun(){if(ENVIRONMENT_IS_PTHREAD)return;if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(ENVIRONMENT_IS_PTHREAD){postMessage({"cmd":"onAbort","arg":what})}else{if(Module["onAbort"]){Module["onAbort"](what)}}what="Aborted("+what+")";err(what);ABORT=true;EXITSTATUS=1;what+=". Build with -s ASSERTIONS=1 for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return filename.startsWith(dataURIPrefix)}function isFileURI(filename){return filename.startsWith("file://")}var wasmBinaryFile;wasmBinaryFile="ogv-decoder-video-av1-simd-mt-wasm.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(file){try{if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)){if(typeof fetch==="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary(wasmBinaryFile)})}else{if(readAsync){return new Promise(function(resolve,reject){readAsync(wasmBinaryFile,function(response){resolve(new Uint8Array(response))},reject)})}}}return Promise.resolve().then(function(){return getBinary(wasmBinaryFile)})}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;registerTlsInit(Module["asm"]["H"]);wasmTable=Module["asm"]["F"];addOnInit(Module["asm"]["y"]);wasmModule=module;if(!ENVIRONMENT_IS_PTHREAD){var numWorkersToLoad=PThread.unusedWorkers.length;PThread.unusedWorkers.forEach(function(w){PThread.loadWasmModuleToWorker(w,function(){if(!--numWorkersToLoad)removeRunDependency("wasm-instantiate")})})}}if(!ENVIRONMENT_IS_PTHREAD){addRunDependency("wasm-instantiate")}function receiveInstantiationResult(result){receiveInstance(result["instance"],result["module"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(function(instance){return instance}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiationResult,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiationResult)})})}else{return instantiateArrayBuffer(receiveInstantiationResult)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync().catch(readyPromiseReject);return{}}var ASM_CONSTS={};function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){getWasmTableEntry(func)()}else{getWasmTableEntry(func)(callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}function withStackSave(f){var stack=stackSave();var ret=f();stackRestore(stack);return ret}function killThread(pthread_ptr){GROWABLE_HEAP_I32()[pthread_ptr>>2]=0;var pthread=PThread.pthreads[pthread_ptr];delete PThread.pthreads[pthread_ptr];pthread.worker.terminate();__emscripten_thread_free_data(pthread_ptr);PThread.runningWorkers.splice(PThread.runningWorkers.indexOf(pthread.worker),1);pthread.worker.pthread=undefined}function cancelThread(pthread_ptr){var pthread=PThread.pthreads[pthread_ptr];pthread.worker.postMessage({"cmd":"cancel"})}function cleanupThread(pthread_ptr){var pthread=PThread.pthreads[pthread_ptr];if(pthread){GROWABLE_HEAP_I32()[pthread_ptr>>2]=0;var worker=pthread.worker;PThread.returnWorkerToPool(worker)}}function _exit(status){exit(status)}function handleException(e){if(e instanceof ExitStatus||e=="unwind"){return EXITSTATUS}quit_(1,e)}var PThread={unusedWorkers:[],runningWorkers:[],tlsInitFunctions:[],initMainThread:function(){var pthreadPoolSize=1;for(var i=0;i>2]=0;try{func()}finally{GROWABLE_HEAP_I32()[__emscripten_allow_main_runtime_queued_calls>>2]=1}},receiveObjectTransfer:function(data){},threadInit:function(){for(var i in PThread.tlsInitFunctions){PThread.tlsInitFunctions[i]()}},loadWasmModuleToWorker:function(worker,onFinishedLoading){worker.onmessage=function(e){var d=e["data"];var cmd=d["cmd"];if(worker.pthread)PThread.currentProxiedOperationCallerThread=worker.pthread.threadInfoStruct;if(d["targetThread"]&&d["targetThread"]!=_pthread_self()){var thread=PThread.pthreads[d.targetThread];if(thread){thread.worker.postMessage(d,d["transferList"])}else{err('Internal error! Worker sent a message "'+cmd+'" to target pthread '+d["targetThread"]+", but that thread no longer exists!")}PThread.currentProxiedOperationCallerThread=undefined;return}if(cmd==="processQueuedMainThreadWork"){_emscripten_main_thread_process_queued_calls()}else if(cmd==="spawnThread"){spawnThread(d)}else if(cmd==="cleanupThread"){cleanupThread(d["thread"])}else if(cmd==="killThread"){killThread(d["thread"])}else if(cmd==="cancelThread"){cancelThread(d["thread"])}else if(cmd==="loaded"){worker.loaded=true;if(onFinishedLoading)onFinishedLoading(worker);if(worker.runPthread){worker.runPthread();delete worker.runPthread}}else if(cmd==="print"){out("Thread "+d["threadId"]+": "+d["text"])}else if(cmd==="printErr"){err("Thread "+d["threadId"]+": "+d["text"])}else if(cmd==="alert"){alert("Thread "+d["threadId"]+": "+d["text"])}else if(cmd==="detachedExit"){PThread.returnWorkerToPool(worker)}else if(d.target==="setimmediate"){worker.postMessage(d)}else if(cmd==="onAbort"){if(Module["onAbort"]){Module["onAbort"](d["arg"])}}else{err("worker sent an unknown command "+cmd)}PThread.currentProxiedOperationCallerThread=undefined};worker.onerror=function(e){var message="worker sent an error!";err(message+" "+e.filename+":"+e.lineno+": "+e.message);throw e};if(ENVIRONMENT_IS_NODE){worker.on("message",function(data){worker.onmessage({data:data})});worker.on("error",function(e){worker.onerror(e)});worker.on("detachedExit",function(){})}worker.postMessage({"cmd":"load","urlOrBlob":Module["mainScriptUrlOrBlob"]||_scriptDir,"wasmMemory":wasmMemory,"wasmModule":wasmModule})},allocateUnusedWorker:function(){var pthreadMainJs=locateFile("ogv-decoder-video-av1-simd-mt-wasm.worker.js");PThread.unusedWorkers.push(new Worker(pthreadMainJs))},getNewWorker:function(){if(PThread.unusedWorkers.length==0){PThread.allocateUnusedWorker();PThread.loadWasmModuleToWorker(PThread.unusedWorkers[0])}return PThread.unusedWorkers.pop()}};function establishStackSpace(){var pthread_ptr=_pthread_self();var stackTop=GROWABLE_HEAP_I32()[pthread_ptr+44>>2];var stackSize=GROWABLE_HEAP_I32()[pthread_ptr+48>>2];var stackMax=stackTop-stackSize;_emscripten_stack_set_limits(stackTop,stackMax);stackRestore(stackTop)}Module["establishStackSpace"]=establishStackSpace;function exitOnMainThread(returnCode){if(ENVIRONMENT_IS_PTHREAD)return _emscripten_proxy_to_main_thread_js(1,0,returnCode);try{_exit(returnCode)}catch(e){handleException(e)}}var wasmTableMirror=[];function getWasmTableEntry(funcPtr){var func=wasmTableMirror[funcPtr];if(!func){if(funcPtr>=wasmTableMirror.length)wasmTableMirror.length=funcPtr+1;wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func}function invokeEntryPoint(ptr,arg){return getWasmTableEntry(ptr)(arg)}Module["invokeEntryPoint"]=invokeEntryPoint;function registerTlsInit(tlsInitFunc,moduleExports,metadata){PThread.tlsInitFunctions.push(tlsInitFunc)}var _emscripten_get_now;if(ENVIRONMENT_IS_NODE){_emscripten_get_now=(()=>{var t=process["hrtime"]();return t[0]*1e3+t[1]/1e6})}else if(ENVIRONMENT_IS_PTHREAD){_emscripten_get_now=(()=>performance.now()-Module["__performance_now_clock_drift"])}else _emscripten_get_now=(()=>performance.now());function ___emscripten_init_main_thread_js(tb){__emscripten_thread_init(tb,!ENVIRONMENT_IS_WORKER,1,!ENVIRONMENT_IS_WEB);PThread.threadInit()}function ___emscripten_thread_cleanup(thread){if(!ENVIRONMENT_IS_PTHREAD)cleanupThread(thread);else postMessage({"cmd":"cleanupThread","thread":thread})}function spawnThread(threadParams){var worker=PThread.getNewWorker();if(!worker){return 6}PThread.runningWorkers.push(worker);var pthread=PThread.pthreads[threadParams.pthread_ptr]={worker:worker,threadInfoStruct:threadParams.pthread_ptr};worker.pthread=pthread;var msg={"cmd":"run","start_routine":threadParams.startRoutine,"arg":threadParams.arg,"threadInfoStruct":threadParams.pthread_ptr};worker.runPthread=function(){msg.time=performance.now();worker.postMessage(msg,threadParams.transferList)};if(worker.loaded){worker.runPthread();delete worker.runPthread}return 0}function ___pthread_create_js(pthread_ptr,attr,start_routine,arg){if(typeof SharedArrayBuffer==="undefined"){err("Current environment does not support SharedArrayBuffer, pthreads are not available!");return 6}var transferList=[];var error=0;if(ENVIRONMENT_IS_PTHREAD&&(transferList.length===0||error)){return _emscripten_sync_run_in_main_thread_4(687865856,pthread_ptr,attr,start_routine,arg)}if(error)return error;var threadParams={startRoutine:start_routine,pthread_ptr:pthread_ptr,arg:arg,transferList:transferList};if(ENVIRONMENT_IS_PTHREAD){threadParams.cmd="spawnThread";postMessage(threadParams,transferList);return 0}return spawnThread(threadParams)}function ___pthread_detached_exit(){postMessage({"cmd":"detachedExit"})}function __emscripten_default_pthread_stack_size(){return 2097152}function __emscripten_futex_wait_non_blocking(addr,val,timeout){var tNow=performance.now();var tEnd=tNow+timeout;var lastAddr=Atomics.exchange(GROWABLE_HEAP_I32(),__emscripten_main_thread_futex>>2,addr);while(1){tNow=performance.now();if(tNow>tEnd){lastAddr=Atomics.exchange(GROWABLE_HEAP_I32(),__emscripten_main_thread_futex>>2,0);return-73}lastAddr=Atomics.exchange(GROWABLE_HEAP_I32(),__emscripten_main_thread_futex>>2,0);if(lastAddr==0){break}_emscripten_main_thread_process_queued_calls();if(Atomics.load(GROWABLE_HEAP_I32(),addr>>2)!=val){return-6}lastAddr=Atomics.exchange(GROWABLE_HEAP_I32(),__emscripten_main_thread_futex>>2,addr)}return 0}function __emscripten_notify_thread_queue(targetThreadId,mainThreadId){if(targetThreadId==mainThreadId){postMessage({"cmd":"processQueuedMainThreadWork"})}else if(ENVIRONMENT_IS_PTHREAD){postMessage({"targetThread":targetThreadId,"cmd":"processThreadQueue"})}else{var pthread=PThread.pthreads[targetThreadId];var worker=pthread&&pthread.worker;if(!worker){return}worker.postMessage({"cmd":"processThreadQueue"})}return 1}function _abort(){abort("")}function _emscripten_check_blocking_allowed(){if(ENVIRONMENT_IS_NODE)return;if(ENVIRONMENT_IS_WORKER)return;warnOnce("Blocking on the main thread is very dangerous, see https://emscripten.org/docs/porting/pthreads.html#blocking-on-the-main-browser-thread")}function _emscripten_memcpy_big(dest,src,num){GROWABLE_HEAP_U8().copyWithin(dest,src,src+num)}function _emscripten_num_logical_cores(){if(ENVIRONMENT_IS_NODE)return require("os").cpus().length;return navigator["hardwareConcurrency"]}function _emscripten_proxy_to_main_thread_js(index,sync){var numCallArgs=arguments.length-2;var outerArgs=arguments;return withStackSave(function(){var serializedNumCallArgs=numCallArgs;var args=stackAlloc(serializedNumCallArgs*8);var b=args>>3;for(var i=0;i>3;for(var i=0;i>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){var oldSize=GROWABLE_HEAP_U8().length;requestedSize=requestedSize>>>0;if(requestedSize<=oldSize){return false}var maxHeapSize=1073741824;if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}var JSEvents={inEventHandler:0,removeAllEventListeners:function(){for(var i=JSEvents.eventHandlers.length-1;i>=0;--i){JSEvents._removeHandler(i)}JSEvents.eventHandlers=[];JSEvents.deferredCalls=[]},registerRemoveEventListeners:function(){if(!JSEvents.removeEventListenersRegistered){__ATEXIT__.push(JSEvents.removeAllEventListeners);JSEvents.removeEventListenersRegistered=true}},deferredCalls:[],deferCall:function(targetFunction,precedence,argsList){function arraysHaveEqualContent(arrA,arrB){if(arrA.length!=arrB.length)return false;for(var i in arrA){if(arrA[i]!=arrB[i])return false}return true}for(var i in JSEvents.deferredCalls){var call=JSEvents.deferredCalls[i];if(call.targetFunction==targetFunction&&arraysHaveEqualContent(call.argsList,argsList)){return}}JSEvents.deferredCalls.push({targetFunction:targetFunction,precedence:precedence,argsList:argsList});JSEvents.deferredCalls.sort(function(x,y){return x.precedence>2]=eventTypeId;GROWABLE_HEAP_I32()[varargs+4>>2]=eventData;GROWABLE_HEAP_I32()[varargs+8>>2]=userData;_emscripten_dispatch_to_thread_(targetThread,637534208,eventHandlerFunc,eventData,varargs)})},getTargetThreadForEventCallback:function(targetThread){switch(targetThread){case 1:return 0;case 2:return PThread.currentProxiedOperationCallerThread;default:return targetThread}},getNodeNameForTarget:function(target){if(!target)return"";if(target==window)return"#window";if(target==screen)return"#screen";return target&&target.nodeName?target.nodeName:""},fullscreenEnabled:function(){return document.fullscreenEnabled||document.webkitFullscreenEnabled}};function stringToNewUTF8(jsString){var length=lengthBytesUTF8(jsString)+1;var cString=_malloc(length);stringToUTF8(jsString,cString,length);return cString}function _emscripten_set_offscreencanvas_size_on_target_thread_js(targetThread,targetCanvas,width,height){withStackSave(function(){var varargs=stackAlloc(12);var targetCanvasPtr=0;if(targetCanvas){targetCanvasPtr=stringToNewUTF8(targetCanvas)}GROWABLE_HEAP_I32()[varargs>>2]=targetCanvasPtr;GROWABLE_HEAP_I32()[varargs+4>>2]=width;GROWABLE_HEAP_I32()[varargs+8>>2]=height;_emscripten_dispatch_to_thread_(targetThread,657457152,0,targetCanvasPtr,varargs)})}function _emscripten_set_offscreencanvas_size_on_target_thread(targetThread,targetCanvas,width,height){targetCanvas=targetCanvas?UTF8ToString(targetCanvas):"";_emscripten_set_offscreencanvas_size_on_target_thread_js(targetThread,targetCanvas,width,height)}function maybeCStringToJsString(cString){return cString>2?UTF8ToString(cString):cString}var specialHTMLTargets=[0,typeof document!=="undefined"?document:0,typeof window!=="undefined"?window:0];function findEventTarget(target){target=maybeCStringToJsString(target);var domElement=specialHTMLTargets[target]||(typeof document!=="undefined"?document.querySelector(target):undefined);return domElement}function findCanvasEventTarget(target){return findEventTarget(target)}function _emscripten_set_canvas_element_size_calling_thread(target,width,height){var canvas=findCanvasEventTarget(target);if(!canvas)return-4;if(canvas.canvasSharedPtr){GROWABLE_HEAP_I32()[canvas.canvasSharedPtr>>2]=width;GROWABLE_HEAP_I32()[canvas.canvasSharedPtr+4>>2]=height}if(canvas.offscreenCanvas||!canvas.controlTransferredOffscreen){if(canvas.offscreenCanvas)canvas=canvas.offscreenCanvas;var autoResizeViewport=false;if(canvas.GLctxObject&&canvas.GLctxObject.GLctx){var prevViewport=canvas.GLctxObject.GLctx.getParameter(2978);autoResizeViewport=prevViewport[0]===0&&prevViewport[1]===0&&prevViewport[2]===canvas.width&&prevViewport[3]===canvas.height}canvas.width=width;canvas.height=height;if(autoResizeViewport){canvas.GLctxObject.GLctx.viewport(0,0,width,height)}}else if(canvas.canvasSharedPtr){var targetThread=GROWABLE_HEAP_I32()[canvas.canvasSharedPtr+8>>2];_emscripten_set_offscreencanvas_size_on_target_thread(targetThread,target,width,height);return 1}else{return-4}return 0}function _emscripten_set_canvas_element_size_main_thread(target,width,height){if(ENVIRONMENT_IS_PTHREAD)return _emscripten_proxy_to_main_thread_js(2,1,target,width,height);return _emscripten_set_canvas_element_size_calling_thread(target,width,height)}function _emscripten_set_canvas_element_size(target,width,height){var canvas=findCanvasEventTarget(target);if(canvas){return _emscripten_set_canvas_element_size_calling_thread(target,width,height)}else{return _emscripten_set_canvas_element_size_main_thread(target,width,height)}}function _emscripten_unwind_to_js_event_loop(){throw"unwind"}function __webgl_enable_ANGLE_instanced_arrays(ctx){var ext=ctx.getExtension("ANGLE_instanced_arrays");if(ext){ctx["vertexAttribDivisor"]=function(index,divisor){ext["vertexAttribDivisorANGLE"](index,divisor)};ctx["drawArraysInstanced"]=function(mode,first,count,primcount){ext["drawArraysInstancedANGLE"](mode,first,count,primcount)};ctx["drawElementsInstanced"]=function(mode,count,type,indices,primcount){ext["drawElementsInstancedANGLE"](mode,count,type,indices,primcount)};return 1}}function __webgl_enable_OES_vertex_array_object(ctx){var ext=ctx.getExtension("OES_vertex_array_object");if(ext){ctx["createVertexArray"]=function(){return ext["createVertexArrayOES"]()};ctx["deleteVertexArray"]=function(vao){ext["deleteVertexArrayOES"](vao)};ctx["bindVertexArray"]=function(vao){ext["bindVertexArrayOES"](vao)};ctx["isVertexArray"]=function(vao){return ext["isVertexArrayOES"](vao)};return 1}}function __webgl_enable_WEBGL_draw_buffers(ctx){var ext=ctx.getExtension("WEBGL_draw_buffers");if(ext){ctx["drawBuffers"]=function(n,bufs){ext["drawBuffersWEBGL"](n,bufs)};return 1}}function __webgl_enable_WEBGL_multi_draw(ctx){return!!(ctx.multiDrawWebgl=ctx.getExtension("WEBGL_multi_draw"))}var GL={counter:1,buffers:[],programs:[],framebuffers:[],renderbuffers:[],textures:[],shaders:[],vaos:[],contexts:{},offscreenCanvases:{},queries:[],stringCache:{},unpackAlignment:4,recordError:function recordError(errorCode){if(!GL.lastError){GL.lastError=errorCode}},getNewId:function(table){var ret=GL.counter++;for(var i=table.length;i>2]:-1;source+=UTF8ToString(GROWABLE_HEAP_I32()[string+i*4>>2],len<0?undefined:len)}return source},createContext:function(canvas,webGLContextAttributes){if(!canvas.getContextSafariWebGL2Fixed){canvas.getContextSafariWebGL2Fixed=canvas.getContext;canvas.getContext=function(ver,attrs){var gl=canvas.getContextSafariWebGL2Fixed(ver,attrs);return ver=="webgl"==gl instanceof WebGLRenderingContext?gl:null}}var ctx=canvas.getContext("webgl",webGLContextAttributes);if(!ctx)return 0;var handle=GL.registerContext(ctx,webGLContextAttributes);return handle},registerContext:function(ctx,webGLContextAttributes){var handle=_malloc(8);GROWABLE_HEAP_I32()[handle+4>>2]=_pthread_self();var context={handle:handle,attributes:webGLContextAttributes,version:webGLContextAttributes.majorVersion,GLctx:ctx};if(ctx.canvas)ctx.canvas.GLctxObject=context;GL.contexts[handle]=context;if(typeof webGLContextAttributes.enableExtensionsByDefault==="undefined"||webGLContextAttributes.enableExtensionsByDefault){GL.initExtensions(context)}return handle},makeContextCurrent:function(contextHandle){GL.currentContext=GL.contexts[contextHandle];Module.ctx=GLctx=GL.currentContext&&GL.currentContext.GLctx;return!(contextHandle&&!GLctx)},getContext:function(contextHandle){return GL.contexts[contextHandle]},deleteContext:function(contextHandle){if(GL.currentContext===GL.contexts[contextHandle])GL.currentContext=null;if(typeof JSEvents==="object")JSEvents.removeAllHandlersOnTarget(GL.contexts[contextHandle].GLctx.canvas);if(GL.contexts[contextHandle]&&GL.contexts[contextHandle].GLctx.canvas)GL.contexts[contextHandle].GLctx.canvas.GLctxObject=undefined;_free(GL.contexts[contextHandle].handle);GL.contexts[contextHandle]=null},initExtensions:function(context){if(!context)context=GL.currentContext;if(context.initExtensionsDone)return;context.initExtensionsDone=true;var GLctx=context.GLctx;__webgl_enable_ANGLE_instanced_arrays(GLctx);__webgl_enable_OES_vertex_array_object(GLctx);__webgl_enable_WEBGL_draw_buffers(GLctx);{GLctx.disjointTimerQueryExt=GLctx.getExtension("EXT_disjoint_timer_query")}__webgl_enable_WEBGL_multi_draw(GLctx);var exts=GLctx.getSupportedExtensions()||[];exts.forEach(function(ext){if(!ext.includes("lose_context")&&!ext.includes("debug")){GLctx.getExtension(ext)}})}};var __emscripten_webgl_power_preferences=["default","low-power","high-performance"];function _emscripten_webgl_do_create_context(target,attributes){var a=attributes>>2;var powerPreference=GROWABLE_HEAP_I32()[a+(24>>2)];var contextAttributes={"alpha":!!GROWABLE_HEAP_I32()[a+(0>>2)],"depth":!!GROWABLE_HEAP_I32()[a+(4>>2)],"stencil":!!GROWABLE_HEAP_I32()[a+(8>>2)],"antialias":!!GROWABLE_HEAP_I32()[a+(12>>2)],"premultipliedAlpha":!!GROWABLE_HEAP_I32()[a+(16>>2)],"preserveDrawingBuffer":!!GROWABLE_HEAP_I32()[a+(20>>2)],"powerPreference":__emscripten_webgl_power_preferences[powerPreference],"failIfMajorPerformanceCaveat":!!GROWABLE_HEAP_I32()[a+(28>>2)],majorVersion:GROWABLE_HEAP_I32()[a+(32>>2)],minorVersion:GROWABLE_HEAP_I32()[a+(36>>2)],enableExtensionsByDefault:GROWABLE_HEAP_I32()[a+(40>>2)],explicitSwapControl:GROWABLE_HEAP_I32()[a+(44>>2)],proxyContextToMainThread:GROWABLE_HEAP_I32()[a+(48>>2)],renderViaOffscreenBackBuffer:GROWABLE_HEAP_I32()[a+(52>>2)]};var canvas=findCanvasEventTarget(target);if(!canvas){return 0}if(contextAttributes.explicitSwapControl){return 0}var contextHandle=GL.createContext(canvas,contextAttributes);return contextHandle}function _emscripten_webgl_create_context(a0,a1){return _emscripten_webgl_do_create_context(a0,a1)}var SYSCALLS={mappings:{},buffers:[null,[],[]],printChar:function(stream,curr){var buffer=SYSCALLS.buffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer,0));buffer.length=0}else{buffer.push(curr)}},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=GROWABLE_HEAP_I32()[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},get64:function(low,high){return low}};function _fd_close(fd){if(ENVIRONMENT_IS_PTHREAD)return _emscripten_proxy_to_main_thread_js(3,1,fd);return 0}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){if(ENVIRONMENT_IS_PTHREAD)return _emscripten_proxy_to_main_thread_js(4,1,fd,offset_low,offset_high,whence,newOffset)}function _fd_write(fd,iov,iovcnt,pnum){if(ENVIRONMENT_IS_PTHREAD)return _emscripten_proxy_to_main_thread_js(5,1,fd,iov,iovcnt,pnum);var num=0;for(var i=0;i>2];var len=GROWABLE_HEAP_I32()[iov+4>>2];iov+=8;for(var j=0;j>2]=num;return 0}function _ogvjs_callback_async_complete(ret,cpuTime){var callback=Module.callbacks.shift();Module["cpuTime"]+=cpuTime;callback(ret);return}function _ogvjs_callback_frame(bufferY,strideY,bufferCb,strideCb,bufferCr,strideCr,width,height,chromaWidth,chromaHeight,picWidth,picHeight,picX,picY,displayWidth,displayHeight){var heap=wasmMemory.buffer;var format=Module["videoFormat"];function copyAndTrim(arr,buffer,stride,height,picX,picY,picWidth,picHeight,fill){arr.set(new Uint8Array(heap,buffer,stride*height));var x,y,ptr;for(ptr=0,y=0;y0){var next=recycled.shift(),format=next["format"];if(format["width"]===width&&format["height"]===height&&format["chromaWidth"]===chromaWidth&&format["chromaHeight"]===chromaHeight&&format["cropLeft"]===picX&&format["cropTop"]===picY&&format["cropWidth"]===picWidth&&format["cropHeight"]===picHeight&&format["displayWidth"]===displayWidth&&format["displayHeight"]===displayHeight&&next["y"]["bytes"].length===lenY&&next["u"]["bytes"].length===lenCb&&next["v"]["bytes"].length===lenCr){frame=next;break}}if(!frame){frame={"format":{"width":width,"height":height,"chromaWidth":chromaWidth,"chromaHeight":chromaHeight,"cropLeft":picX,"cropTop":picY,"cropWidth":picWidth,"cropHeight":picHeight,"displayWidth":displayWidth,"displayHeight":displayHeight},"y":{"bytes":new Uint8Array(lenY),"stride":strideY},"u":{"bytes":new Uint8Array(lenCb),"stride":strideCb},"v":{"bytes":new Uint8Array(lenCr),"stride":strideCr}}}copyAndTrim(frame["y"]["bytes"],bufferY,strideY,height,picX,picY,picWidth,picHeight,0);copyAndTrim(frame["u"]["bytes"],bufferCb,strideCb,chromaHeight,chromaPicX,chromaPicY,chromaPicWidth,chromaPicHeight,128);copyAndTrim(frame["v"]["bytes"],bufferCr,strideCr,chromaHeight,chromaPicX,chromaPicY,chromaPicWidth,chromaPicHeight,128);Module["frameBuffer"]=frame}if(!ENVIRONMENT_IS_PTHREAD)PThread.initMainThread();var GLctx;var proxiedFunctionTable=[null,exitOnMainThread,_emscripten_set_canvas_element_size_main_thread,_fd_close,_fd_seek,_fd_write];var asmLibraryArg={"v":___emscripten_init_main_thread_js,"q":___emscripten_thread_cleanup,"d":___pthread_create_js,"t":___pthread_detached_exit,"u":__emscripten_default_pthread_stack_size,"j":__emscripten_futex_wait_non_blocking,"i":__emscripten_notify_thread_queue,"c":_abort,"f":_emscripten_check_blocking_allowed,"b":_emscripten_get_now,"n":_emscripten_memcpy_big,"x":_emscripten_num_logical_cores,"w":_emscripten_receive_on_main_thread_js,"o":_emscripten_resize_heap,"g":_emscripten_set_canvas_element_size,"r":_emscripten_unwind_to_js_event_loop,"h":_emscripten_webgl_create_context,"s":_exit,"p":_fd_close,"m":_fd_seek,"e":_fd_write,"a":wasmMemory||Module["wasmMemory"],"k":_ogvjs_callback_async_complete,"l":_ogvjs_callback_frame};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["y"]).apply(null,arguments)};var _ogv_video_decoder_init=Module["_ogv_video_decoder_init"]=function(){return(_ogv_video_decoder_init=Module["_ogv_video_decoder_init"]=Module["asm"]["z"]).apply(null,arguments)};var _free=Module["_free"]=function(){return(_free=Module["_free"]=Module["asm"]["A"]).apply(null,arguments)};var _ogv_video_decoder_async=Module["_ogv_video_decoder_async"]=function(){return(_ogv_video_decoder_async=Module["_ogv_video_decoder_async"]=Module["asm"]["B"]).apply(null,arguments)};var _ogv_video_decoder_destroy=Module["_ogv_video_decoder_destroy"]=function(){return(_ogv_video_decoder_destroy=Module["_ogv_video_decoder_destroy"]=Module["asm"]["C"]).apply(null,arguments)};var _ogv_video_decoder_process_header=Module["_ogv_video_decoder_process_header"]=function(){return(_ogv_video_decoder_process_header=Module["_ogv_video_decoder_process_header"]=Module["asm"]["D"]).apply(null,arguments)};var _ogv_video_decoder_process_frame=Module["_ogv_video_decoder_process_frame"]=function(){return(_ogv_video_decoder_process_frame=Module["_ogv_video_decoder_process_frame"]=Module["asm"]["E"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["G"]).apply(null,arguments)};var _emscripten_tls_init=Module["_emscripten_tls_init"]=function(){return(_emscripten_tls_init=Module["_emscripten_tls_init"]=Module["asm"]["H"]).apply(null,arguments)};var __emscripten_thread_init=Module["__emscripten_thread_init"]=function(){return(__emscripten_thread_init=Module["__emscripten_thread_init"]=Module["asm"]["I"]).apply(null,arguments)};var _emscripten_current_thread_process_queued_calls=Module["_emscripten_current_thread_process_queued_calls"]=function(){return(_emscripten_current_thread_process_queued_calls=Module["_emscripten_current_thread_process_queued_calls"]=Module["asm"]["J"]).apply(null,arguments)};var _emscripten_sync_run_in_main_thread_4=Module["_emscripten_sync_run_in_main_thread_4"]=function(){return(_emscripten_sync_run_in_main_thread_4=Module["_emscripten_sync_run_in_main_thread_4"]=Module["asm"]["K"]).apply(null,arguments)};var _emscripten_main_thread_process_queued_calls=Module["_emscripten_main_thread_process_queued_calls"]=function(){return(_emscripten_main_thread_process_queued_calls=Module["_emscripten_main_thread_process_queued_calls"]=Module["asm"]["L"]).apply(null,arguments)};var _emscripten_run_in_main_runtime_thread_js=Module["_emscripten_run_in_main_runtime_thread_js"]=function(){return(_emscripten_run_in_main_runtime_thread_js=Module["_emscripten_run_in_main_runtime_thread_js"]=Module["asm"]["M"]).apply(null,arguments)};var _pthread_self=Module["_pthread_self"]=function(){return(_pthread_self=Module["_pthread_self"]=Module["asm"]["N"]).apply(null,arguments)};var _emscripten_dispatch_to_thread_=Module["_emscripten_dispatch_to_thread_"]=function(){return(_emscripten_dispatch_to_thread_=Module["_emscripten_dispatch_to_thread_"]=Module["asm"]["O"]).apply(null,arguments)};var __emscripten_thread_free_data=Module["__emscripten_thread_free_data"]=function(){return(__emscripten_thread_free_data=Module["__emscripten_thread_free_data"]=Module["asm"]["P"]).apply(null,arguments)};var __emscripten_thread_exit=Module["__emscripten_thread_exit"]=function(){return(__emscripten_thread_exit=Module["__emscripten_thread_exit"]=Module["asm"]["Q"]).apply(null,arguments)};var _emscripten_stack_set_limits=Module["_emscripten_stack_set_limits"]=function(){return(_emscripten_stack_set_limits=Module["_emscripten_stack_set_limits"]=Module["asm"]["R"]).apply(null,arguments)};var stackSave=Module["stackSave"]=function(){return(stackSave=Module["stackSave"]=Module["asm"]["S"]).apply(null,arguments)};var stackRestore=Module["stackRestore"]=function(){return(stackRestore=Module["stackRestore"]=Module["asm"]["T"]).apply(null,arguments)};var stackAlloc=Module["stackAlloc"]=function(){return(stackAlloc=Module["stackAlloc"]=Module["asm"]["U"]).apply(null,arguments)};var __emscripten_main_thread_futex=Module["__emscripten_main_thread_futex"]=375988;var __emscripten_allow_main_runtime_queued_calls=Module["__emscripten_allow_main_runtime_queued_calls"]=108356;Module["keepRuntimeAlive"]=keepRuntimeAlive;Module["PThread"]=PThread;Module["PThread"]=PThread;Module["wasmMemory"]=wasmMemory;Module["ExitStatus"]=ExitStatus;var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;if(runDependencies>0){return}if(ENVIRONMENT_IS_PTHREAD){readyPromiseResolve(Module);initRuntime();postMessage({"cmd":"loaded"});return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;function exit(status,implicit){EXITSTATUS=status;if(!implicit){if(ENVIRONMENT_IS_PTHREAD){exitOnMainThread(status);throw"unwind"}else{}}if(keepRuntimeAlive()){}else{exitRuntime()}procExit(status)}function procExit(code){EXITSTATUS=code;if(!keepRuntimeAlive()){PThread.terminateAllThreads();if(Module["onExit"])Module["onExit"](code);ABORT=true}quit_(code,new ExitStatus(code))}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}if(ENVIRONMENT_IS_PTHREAD){noExitRuntime=false;PThread.initWorker()}run();var inputBuffer,inputBufferSize;function reallocInputBuffer(size){if(inputBuffer&&inputBufferSize>=size){return inputBuffer}if(inputBuffer){Module["_free"](inputBuffer)}inputBufferSize=size;inputBuffer=Module["_malloc"](inputBufferSize);return inputBuffer}var getTimestamp;if(typeof performance==="undefined"||typeof performance.now==="undefined"){getTimestamp=Date.now}else{getTimestamp=performance.now.bind(performance)}function time(func){var start=getTimestamp(),ret;ret=func();Module["cpuTime"]+=getTimestamp()-start;return ret}Module["loadedMetadata"]=!!options["videoFormat"];Module["videoFormat"]=options["videoFormat"]||null;Module["frameBuffer"]=null;Module["cpuTime"]=0;Object.defineProperty(Module,"processing",{get:function getProcessing(){return false}});Module["init"]=function(callback){time(function(){Module["_ogv_video_decoder_init"]()});callback()};Module["processHeader"]=function(data,callback){var ret=time(function(){var len=data.byteLength;var buffer=reallocInputBuffer(len);var dest=new Uint8Array(wasmMemory.buffer,buffer,len);dest.set(new Uint8Array(data));return Module["_ogv_video_decoder_process_header"](buffer,len)});callback(ret)};Module.callbacks=[];Module["processFrame"]=function(data,callback){var isAsync=Module["_ogv_video_decoder_async"]();var len=data.byteLength;var buffer=Module["_malloc"](len);function callbackWrapper(ret){Module["_free"](buffer);callback(ret)}if(isAsync){Module.callbacks.push(callbackWrapper)}var ret=time(function(){var dest=new Uint8Array(wasmMemory.buffer,buffer,len);dest.set(new Uint8Array(data));return Module["_ogv_video_decoder_process_frame"](buffer,len)});if(!isAsync){callbackWrapper(ret)}};Module["close"]=function(){};Module["sync"]=function(){var isAsync=Module["_ogv_video_decoder_async"]();if(isAsync){Module.callbacks.push(function(){});time(function(){Module["_ogv_video_decoder_process_frame"](0,0)})}};Module["recycledFrames"]=[];Module["recycleFrame"]=function(frame){var arr=Module["recycledFrames"];arr.push(frame);if(arr.length>16){arr.shift()}}; + + + return OGVDecoderVideoAV1SIMDMTW.ready +} +); +})(); +if (typeof exports === 'object' && typeof module === 'object') + module.exports = OGVDecoderVideoAV1SIMDMTW; +else if (typeof define === 'function' && define['amd']) + define([], function() { return OGVDecoderVideoAV1SIMDMTW; }); +else if (typeof exports === 'object') + exports["OGVDecoderVideoAV1SIMDMTW"] = OGVDecoderVideoAV1SIMDMTW; diff --git a/ogvjs-1.8.6/ogv-decoder-video-av1-simd-mt-wasm.wasm b/ogvjs-1.8.6/ogv-decoder-video-av1-simd-mt-wasm.wasm new file mode 100755 index 0000000000000000000000000000000000000000..be718ad829bc6456f19f40df51d717e5bba18731 GIT binary patch literal 495935 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 G37>+Q4<~#|X2&~4Ge4N{ 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